diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 6700efe6022a04a6dc6c560a437730065837fd95..0000000000000000000000000000000000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -/codestream.xml -/statistic.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ -/discord.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 0eb1189d82a596e5dbb3b91416da0e55dd9bfb1c..0000000000000000000000000000000000000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -LibAVQt \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index ba0f7e5ea16a2606db664148aefdeaa4437feca7..0000000000000000000000000000000000000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,8 +0,0 @@ -<component name="InspectionProjectProfileManager"> - <profile version="1.0"> - <option name="myName" value="Project Default" /> - <inspection_tool class="Clazy" enabled="true" level="WARNING" enabled_by_default="true"> - <option name="clazyChecks" value="level0,no-qt-macros" /> - </inspection_tool> - </profile> -</component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 30681992d74a91f527acc5761a4f61338e84fb71..c2cdd87df10cd9fbc360e3e46c0dbae0a7afdc1b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,12 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> - <component name="CidrRootsConfiguration"> - <excludeRoots> - <file path="$PROJECT_DIR$/build-qt59" /> - </excludeRoots> - </component> - <component name="ProjectPlainTextFileTypeManager"> - <file url="file://$PROJECT_DIR$/AVQt/mainpage.dox" /> + <component name="DiscordProjectSettings"> + <option name="show" value="ASK" /> + <option name="description" value="" /> </component> </project> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 98c3ec3c4cf4388d48645f157f8ce3d7ad61b17a..407bd37b4d284a7a2073e032d004ba3afa31e6e9 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ <project version="4"> <component name="ProjectModuleManager"> <modules> - <module fileurl="file://$PROJECT_DIR$/.idea/libAVQt.iml" filepath="$PROJECT_DIR$/.idea/libAVQt.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/LibAVQt.iml" filepath="$PROJECT_DIR$/.idea/LibAVQt.iml" /> </modules> </component> </project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4cb416c083d265558da75d457237d671..02f8a3e58b8ada6b0074ddb759d0ca33fa83230b 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ <project version="4"> <component name="VcsDirectoryMappings"> <mapping directory="$PROJECT_DIR$" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/mfx_dispatch" vcs="Git" /> </component> </project> \ No newline at end of file diff --git a/AVQt/CMakeLists.txt b/AVQt/CMakeLists.txt index efdc106480f4508a01593a53b8ef66f937818a35..b55b5242f8a3335070286ada300c55b8d70dbd23 100644 --- a/AVQt/CMakeLists.txt +++ b/AVQt/CMakeLists.txt @@ -9,7 +9,16 @@ set(CMAKE_AUTOUIC on) set(CMAKE_INCLUDE_CURRENT_DIR on) -include_directories(F:\\Dev\\mfx_dispatch-master) +if (WIN32) + string(FIND "${CMAKE_SYSTEM_VERSION}" "." FIRST_DOT) + string(FIND "${CMAKE_SYSTEM_VERSION}" "." LAST_DOT REVERSE) + math(EXPR LAST_DOT "${LAST_DOT}+1") # We don't want the dot in the build number + + string(SUBSTRING "${CMAKE_SYSTEM_VERSION}" 0 ${FIRST_DOT} WINDOWS_VERSION) + string(SUBSTRING "${CMAKE_SYSTEM_VERSION}" ${LAST_DOT} -1 WINDOWS_BUILD) + message("Windows ${WINDOWS_VERSION} with build number: ${WINDOWS_BUILD}") + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS on) +endif() #find_package(Qt6 COMPONENTS Core Gui Concurrent Widgets OpenGL OpenGLWidgets) find_package(Qt5 COMPONENTS Core Gui Concurrent Widgets OpenGL) @@ -91,7 +100,8 @@ set(SOURCES_WINDOWS filter/DecoderDXVA2.cpp ) -if (WIN32) +if (WIN32 AND ${WINDOWS_VERSION} GREATER_EQUAL 10 AND ${WINDOWS_BUILD} GREATER_EQUAL 17763) # LibAVQt only supports Windows 10 1809 and above +# add_compile_options(/EHa) add_library(AVQt SHARED ${SOURCES} ${SOURCES_WINDOWS}) add_library(AVQtStatic STATIC ${SOURCES} ${SOURCES_WINDOWS}) elseif (UNIX) @@ -200,7 +210,7 @@ target_link_libraries(AVQt Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5:: target_link_libraries(AVQtStatic Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample) if (WIN32) - target_link_libraries(AVQt opengl32 OpenAL32 d3d9 windowsapp user32 kernel32 Dwmapi psapi d3d9 dxguid) + target_link_libraries(AVQt opengl32 OpenAL32 windowsapp user32 kernel32 Dwmapi psapi d3d9 dxguid) target_link_libraries(AVQtStatic opengl32 OpenAL32 windowsapp user32 kernel32 Dwmapi psapi d3d9 dxguid) else () target_link_libraries(AVQt GL openal EGL GLU va) diff --git a/AVQt/filter/DecoderQSV.cpp b/AVQt/filter/DecoderQSV.cpp index 1107b8dc68164fc134d02cf3f4e076ca55797792..a3fa0cd676e8d969b737901e78311fd5b0e5c117 100644 --- a/AVQt/filter/DecoderQSV.cpp +++ b/AVQt/filter/DecoderQSV.cpp @@ -369,7 +369,7 @@ namespace AVQt { frames_ctx->height = FFALIGN(pCodecCtx->coded_height, 32); frames_ctx->initial_pool_size = 32; - frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; + frames_hwctx->frame_type = 0x00100; // 0x00100 is equivalent to MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET, but additional headers would be required to use this constant ret = av_hwframe_ctx_init(pCodecCtx->hw_frames_ctx); if (ret < 0) @@ -385,4 +385,4 @@ namespace AVQt { return AV_PIX_FMT_NONE; } -} \ No newline at end of file +} diff --git a/AVQt/filter/EncoderQSV.cpp b/AVQt/filter/EncoderQSV.cpp index 888521b59d24d394bb21b906bfea00bb0bd1fa6f..eb9e8e6c294067ca30bd6cabdb799a7d53b51780 100644 --- a/AVQt/filter/EncoderQSV.cpp +++ b/AVQt/filter/EncoderQSV.cpp @@ -272,6 +272,7 @@ namespace AVQt { qDebug("Referencing frame"); av_frame_ref(queueFrame.first, frame); break; + case AV_PIX_FMT_DXVA2_VLD: case AV_PIX_FMT_DRM_PRIME: case AV_PIX_FMT_VAAPI: if (!d->m_pDeviceCtx) { @@ -295,11 +296,11 @@ namespace AVQt { } av_frame_ref(queueFrame.first, frame); break; - case AV_PIX_FMT_DXVA2_VLD: - qDebug("Transferring frame from GPU to CPU"); - av_hwframe_transfer_data(queueFrame.first, frame, 0); - queueFrame.first->pts = frame->pts; - break; +// case AV_PIX_FMT_DXVA2_VLD: +// qDebug("Transferring frame from GPU to CPU"); +// av_hwframe_transfer_data(queueFrame.first, frame, 0); +// queueFrame.first->pts = frame->pts; +// break; default: qDebug("Referencing frame"); av_frame_ref(queueFrame.first, frame); @@ -400,7 +401,8 @@ namespace AVQt { for (const auto &cb: d->m_cbList) { AVCodecParameters *parameters = avcodec_parameters_alloc(); avcodec_parameters_from_context(parameters, d->m_pCodecCtx); - cb->init(this, d->m_framerate, d->m_pCodecCtx->time_base, 0, parameters, nullptr, nullptr); + parameters->bit_rate = d->m_bitrate; + cb->init(this, d->m_framerate, d->m_pCodecCtx->time_base, d->m_duration, parameters, nullptr, nullptr); cb->start(this); } } @@ -412,14 +414,19 @@ namespace AVQt { } if (frame.first->hw_frames_ctx) { AVFrame *outFrame; + std::chrono::time_point<std::chrono::high_resolution_clock> t1; switch (frame.first->format) { case AV_PIX_FMT_VAAPI: case AV_PIX_FMT_DRM_PRIME: - qDebug("[AVQt::EncoderQSV] Mapping VAAPI/DRM_PRIME frame to QSV"); + case AV_PIX_FMT_DXVA2_VLD: + qDebug("[AVQt::EncoderQSV] Mapping VAAPI/DRM_PRIME/DXVA2 frame to QSV"); outFrame = av_frame_alloc(); outFrame->hw_frames_ctx = av_buffer_ref(d->m_pFramesCtx); outFrame->format = AV_PIX_FMT_QSV; + t1 = std::chrono::high_resolution_clock::now(); av_hwframe_map(outFrame, frame.first, AV_HWFRAME_MAP_DIRECT | AV_HWFRAME_MAP_READ); + qDebug("Mapping time: %ld us", std::chrono::duration_cast<std::chrono::microseconds>( + std::chrono::high_resolution_clock::now() - t1).count()); break; case AV_PIX_FMT_QSV: outFrame = av_frame_clone(frame.first); diff --git a/AVQt/filter/EncoderVAAPI.cpp b/AVQt/filter/EncoderVAAPI.cpp index e001d48eaff91df8b2e1a28db4335d69d1943dfa..18d0f85fc9affa9c0c3f7a100a1035aab715ec6b 100644 --- a/AVQt/filter/EncoderVAAPI.cpp +++ b/AVQt/filter/EncoderVAAPI.cpp @@ -332,7 +332,9 @@ namespace AVQt { for (const auto &cb: d->m_cbList) { AVCodecParameters *parameters = avcodec_parameters_alloc(); avcodec_parameters_from_context(parameters, d->m_pCodecCtx); - cb->init(this, av_make_q(0, 1), d->m_pCodecCtx->time_base, 0, parameters, nullptr, nullptr); + parameters->bit_rate = d->m_bitrate; + //TODO: Pass framerate + cb->init(this, av_make_q(0, 1), d->m_pCodecCtx->time_base, d->m_duration, parameters, nullptr, nullptr); cb->start(this); } } diff --git a/AVQt/input/Demuxer.h b/AVQt/input/Demuxer.h index 313c9bed4e5e1c22748c00c5fec91687b457c30e..6cc5a6a9ddac818dc0cb1884289b676c4794a57b 100644 --- a/AVQt/input/Demuxer.h +++ b/AVQt/input/Demuxer.h @@ -107,7 +107,7 @@ namespace AVQt { void paused(bool pause) Q_DECL_OVERRIDE; protected: - explicit Demuxer(DemuxerPrivate &p); + [[maybe_unused]] explicit Demuxer(DemuxerPrivate &p); DemuxerPrivate *d_ptr; }; diff --git a/AVQt/input/IAudioSource.h b/AVQt/input/IAudioSource.h index 9d563e134a105deaf3531e306bad3f71ec8989eb..40e69af81b32ef1131e3c25234bc78ba47a5f958 100644 --- a/AVQt/input/IAudioSource.h +++ b/AVQt/input/IAudioSource.h @@ -18,9 +18,9 @@ namespace AVQt { virtual bool isPaused() = 0; - Q_INVOKABLE virtual qint64 registerCallback(IAudioSink *callback) = 0; + Q_INVOKABLE virtual qint64 registerCallback(AVQt::IAudioSink *callback) = 0; - Q_INVOKABLE virtual qint64 unregisterCallback(IAudioSink *callback) = 0; + Q_INVOKABLE virtual qint64 unregisterCallback(AVQt::IAudioSink *callback) = 0; public slots: Q_INVOKABLE virtual int init() = 0; diff --git a/AVQt/input/IFrameSource.h b/AVQt/input/IFrameSource.h index 2e3c88a44dfe3b0534b003ec3df72cbb972dac7f..39cc0292d38aaabbf96235431677040eda4e4795 100644 --- a/AVQt/input/IFrameSource.h +++ b/AVQt/input/IFrameSource.h @@ -18,32 +18,32 @@ namespace AVQt { */ class IFrameSource { public: - /*! - * \enum CB_TYPE - * \brief Used to define accepted callback types of a frame source or filter. - * - * Can be linked with bitwise or to set multiple types - */ - enum CB_TYPE : int8_t { - /*! - * \private - */ - CB_NONE = -1, - - /*! - * \brief Callback type: QImage. When registered with this type, the onFrame(QImage, ...) method will be called - */ - CB_QIMAGE = 0b00000001, - - /*! - * \brief Callback type: AVFrame. When registered with this type, the onFrame(AVFrame, ...) method will be called. - * - * ***Important:*** The AVFrame can be every possible software pixel format, so the frame sink/filter has to be able to - * convert the pixel format if necessary. It has to be freed after usage. - * If the source processes frames on the GPU, it has to be downloaded to CPU memory before passing to callbacks. - */ - CB_AVFRAME = 0b00000010 - }; +// /*! +// * \enum CB_TYPE +// * \brief Used to define accepted callback types of a frame source or filter. +// * +// * Can be linked with bitwise or to set multiple types +// */ +// enum CB_TYPE : int8_t { +// /*! +// * \private +// */ +// CB_NONE = -1, +// +// /*! +// * \brief Callback type: QImage. When registered with this type, the onFrame(QImage, ...) method will be called +// */ +// CB_QIMAGE = 0b00000001, +// +// /*! +// * \brief Callback type: AVFrame. When registered with this type, the onFrame(AVFrame, ...) method will be called. +// * +// * ***Important:*** The AVFrame can be every possible software pixel format, so the frame sink/filter has to be able to +// * convert the pixel format if necessary. It has to be freed after usage. +// * If the source processes frames on the GPU, it has to be downloaded to CPU memory before passing to callbacks. +// */ +// CB_AVFRAME = 0b00000010 +// }; /*! * \private @@ -63,14 +63,14 @@ namespace AVQt { * @param type Callback type, can be linked with bitwise or to set multiple options * @return */ - Q_INVOKABLE virtual qint64 registerCallback(IFrameSink *frameSink) = 0; + Q_INVOKABLE virtual qint64 registerCallback(AVQt::IFrameSink *frameSink) = 0; /*! * \brief Removes frame callback \c frameSink from registry * @param frameSink Frame sink/filter to be removed * @return Previous position of the item, is -1 when not in registry */ - Q_INVOKABLE virtual qint64 unregisterCallback(IFrameSink *frameSink) = 0; + Q_INVOKABLE virtual qint64 unregisterCallback(AVQt::IFrameSink *frameSink) = 0; public slots: /*! diff --git a/AVQt/output/Muxer.cpp b/AVQt/output/Muxer.cpp index d612c3110cfd16c10484e4d25035ca6cfa347cb1..a940eab1cb64bfc402a30f81c5d3b35003ae705e 100644 --- a/AVQt/output/Muxer.cpp +++ b/AVQt/output/Muxer.cpp @@ -92,7 +92,7 @@ namespace AVQt { d->m_pFormatContext = avformat_alloc_context(); d->m_pFormatContext->oformat = av_guess_format(outputFormat.toLocal8Bit().data(), "", nullptr); d->m_pIOBuffer = static_cast<uint8_t *>(av_malloc(MuxerPrivate::IOBUF_SIZE)); - d->m_pIOContext = avio_alloc_context(d->m_pIOBuffer, MuxerPrivate::IOBUF_SIZE, 1, d->m_outputDevice, nullptr, + d->m_pIOContext = avio_alloc_context(d->m_pIOBuffer, MuxerPrivate::IOBUF_SIZE, 1, d, nullptr, &MuxerPrivate::writeToIO, &MuxerPrivate::seekIO); d->m_pIOContext->seekable = !d->m_outputDevice->isSequential(); d->m_pFormatContext->pb = d->m_pIOContext; @@ -121,12 +121,6 @@ namespace AVQt { subtitleStream->time_base = timebase; d->m_sourceStreamMap[source].insert(subtitleStream, timebase); } - int ret = avformat_write_header(d->m_pFormatContext, nullptr); - if (ret != 0) { - constexpr auto strBufSize = 32; - char strBuf[strBufSize]; - qWarning("[AVQt::Muxer] %d: Couldn't init AVFormatContext: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); - } qDebug("[AVQt::Muxer] Initialized"); } @@ -250,6 +244,13 @@ namespace AVQt { void Muxer::run() { Q_D(AVQt::Muxer); + int ret = avformat_write_header(d->m_pFormatContext, nullptr); + if (ret != 0) { + constexpr auto strBufSize = 32; + char strBuf[strBufSize]; + qWarning("[AVQt::Muxer] %d: Couldn't init AVFormatContext: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); + } + while (d->m_running.load()) { if (!d->m_paused.load() && d->m_inputQueue.size() > 5) { QPair<AVPacket *, AVStream *> packet; @@ -271,37 +272,39 @@ namespace AVQt { } int MuxerPrivate::writeToIO(void *opaque, uint8_t *buf, int buf_size) { - auto *outputDevice = reinterpret_cast<QIODevice *>(opaque); + auto *d = reinterpret_cast<MuxerPrivate *>(opaque); + QMutexLocker lock(&d->m_ioMutex); - auto bytesWritten = outputDevice->write(reinterpret_cast<const char *>(buf), buf_size); + auto bytesWritten = d->m_outputDevice->write(reinterpret_cast<const char *>(buf), buf_size); return bytesWritten == 0 ? AVERROR_UNKNOWN : static_cast<int>(bytesWritten); } int64_t MuxerPrivate::seekIO(void *opaque, int64_t offset, int whence) { - auto *outputDevice = reinterpret_cast<QIODevice *>(opaque); + auto *d = reinterpret_cast<MuxerPrivate *>(opaque); + QMutexLocker lock(&d->m_ioMutex); - if (outputDevice->isSequential()) { + if (d->m_outputDevice->isSequential()) { return AVERROR_UNKNOWN; } bool result; switch (whence) { case SEEK_SET: - result = outputDevice->seek(offset); + result = d->m_outputDevice->seek(offset); break; case SEEK_CUR: - result = outputDevice->seek(outputDevice->pos() + offset); + result = d->m_outputDevice->seek(d->m_outputDevice->pos() + offset); break; case SEEK_END: - result = outputDevice->seek(outputDevice->size() - offset); + result = d->m_outputDevice->seek(d->m_outputDevice->size() - offset); break; case AVSEEK_SIZE: - return outputDevice->size(); + return d->m_outputDevice->size(); default: return AVERROR_UNKNOWN; } - return result ? outputDevice->pos() : AVERROR_UNKNOWN; + return result ? d->m_outputDevice->pos() : AVERROR_UNKNOWN; } bool MuxerPrivate::packetQueueCompare(const QPair<AVPacket *, AVStream *> &packet1, const QPair<AVPacket *, AVStream *> &packet2) { diff --git a/AVQt/output/OpenGLRenderer.cpp b/AVQt/output/OpenGLRenderer.cpp index 8129ee2e23262d57263c41dceb608b9429ae5798..c9e5a8f80475c13b51e3d85fa9ba98b6c7d39105 100644 --- a/AVQt/output/OpenGLRenderer.cpp +++ b/AVQt/output/OpenGLRenderer.cpp @@ -5,21 +5,20 @@ #include "private/OpenGLRenderer_p.h" #include "OpenGLRenderer.h" -#include <QtGui> -#include <QtConcurrent> -#include <QImage> - #ifdef Q_OS_LINUX #include <va/va.h> #include <va/va_drmcommon.h> #include <libdrm/drm_fourcc.h> #include <unistd.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <EGL/eglplatform.h> +#include <GL/glu.h> #elif defined(Q_OS_WINDOWS) -#include <Windows.h> -#include <gl/GL.h> -#include <d3d9helper.h> +#include <d3d9.h> #define WGL_LOOKUP_FUNCTION(type, func) \ type func = (type) wglGetProcAddress(#func); \ @@ -45,11 +44,16 @@ typedef BOOL (WINAPI *PFNWGLDXOBJECTACCESSNVPROC)(HANDLE hObject, GLenum access) #define WGL_ACCESS_READ_WRITE_NV 0x0001 #define WGL_ACCESS_WRITE_DISCARD_NV 0x0002 +#undef ASSERT +#define ASSERT(expr) if (!expr) { \ + qFatal("Assertion" #expr "failed"); \ +} + std::string GetLastErrorAsString() { //Get the error message ID, if any. DWORD errorMessageID = ::GetLastError(); if (errorMessageID == 0) { - return std::string(); //No error message has been recorded + return {}; //No error message has been recorded } LPSTR messageBuffer = nullptr; @@ -57,7 +61,7 @@ std::string GetLastErrorAsString() { //Ask Win32 to give us the string version of that message ID. //The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be). size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, NULL); + nullptr, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, nullptr); //Copy the error message into a std::string. std::string message(messageBuffer, size); @@ -68,49 +72,89 @@ std::string GetLastErrorAsString() { return message; } -QImage SurfaceToQImage(IDirect3DSurface9 *pSurface, int height) { +[[maybe_unused]] QImage SurfaceToQImage(IDirect3DSurface9 *pSurface/*, IDirect3DDevice9 *pDevice*/, int height) { D3DSURFACE_DESC surfaceDesc; pSurface->GetDesc(&surfaceDesc); +// IDirect3DSurface9 *pRGBASurface; +// HANDLE hRGBASurface; +// ASSERT(SUCCEEDED( +// pDevice->CreateOffscreenPlainSurface(surfaceDesc.Width, surfaceDesc.Height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pRGBASurface, +// &hRGBASurface))); +// ASSERT(SUCCEEDED(pDevice->StretchRect(pSurface, nullptr, pRGBASurface, nullptr, D3DTEXF_LINEAR))); +// BYTE *dataPtr = reinterpret_cast<BYTE *>(locked.pBits); + +// if (locked.Pitch == 0) { +// return {}; +// } +// QThread::sleep(1); + + + QImage::Format imageFormat; + uint bpp; + switch (surfaceDesc.Format) { +// case D3DFMT_A16B16G16R16: +// imageFormat = QImage::Format_RGBA64; +// bpp = 64; +// break; +// case D3DFMT_A8R8G8B8: +// imageFormat = QImage::Format_ARGB32; +// bpp = 32; +// break; + default: + imageFormat = QImage::Format_Grayscale8; + bpp = 8; + break; + } + bpp /= 8; + + QImage result(surfaceDesc.Width, surfaceDesc.Height, imageFormat); + D3DLOCKED_RECT locked; - assert(SUCCEEDED(pSurface->LockRect(&locked, nullptr, D3DLOCK_READONLY))); - BYTE *dataPtr = reinterpret_cast<BYTE *>(locked.pBits); + ASSERT(SUCCEEDED(pSurface->LockRect(&locked, nullptr, D3DLOCK_READONLY))); + qDebug("Locked pitch: %d", locked.Pitch); +// locked.Pitch = 2048; + qDebug() << locked.pBits << locked.Pitch << surfaceDesc.Width; + for (uint y = 0; y < surfaceDesc.Height; ++y) { + qDebug() << y << ": From" << ((uint8_t *) locked.pBits) + y * locked.Pitch << ", To:" + << result.scanLine(y) << ", Size:" << surfaceDesc.Width * bpp; + memcpy(result.scanLine(y), ((uint8_t *) locked.pBits) + y * locked.Pitch, + surfaceDesc.Width * bpp); + } -// uint8_t *data = new uint8_t[surfaceDesc.Width * surfaceDesc.Height * 4]; -// for (uint y = 0; y < surfaceDesc.Height; ++y) { -// memcpy(data + y * surfaceDesc.Width * 4, dataPtr + y * locked.Pitch * 4, surfaceDesc.Width * 4); +// for (uint i = 0; i < 10; ++i) { +// qDebug() << i << ":" << locked.pBits << locked.Pitch << surfaceDesc.Width; +// QThread::msleep(20); // } - QImage result = QImage(dataPtr, surfaceDesc.Width, height, locked.Pitch, - (surfaceDesc.Format == D3DFMT_A8R8G8B8 ? QImage::Format_ARGB32 : QImage::Format_Grayscale8)) - .copy(); - - qDebug() << result.size() << "using" << result.sizeInBytes() << "Byte"; +// QImage result = QImage((uint8_t*)locked.pBits, locked.Pitch, surfaceDesc.Height, locked.Pitch, QImage::Format_Grayscale8); +// QFile out("output2.bmp"); +// out.open(QIODevice::Truncate | QIODevice::ReadWrite); +// result.save(&out, "BMP"); +// out.flush(); +// out.close(); +// qDebug() << result.size() << "using" << result.sizeInBytes() << "Byte"; // delete[] data; + ASSERT(SUCCEEDED(pSurface->UnlockRect())); return result; } #endif -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <EGL/eglplatform.h> -#include <GL/glu.h> - #include <iostream> #pragma clang diagnostic push #pragma ide diagnostic ignored "HidingNonVirtualFunction" extern "C" { -#include <libavutil/frame.h> + #include <libavcodec/avcodec.h> #include <libavutil/imgutils.h> -#include <libavutil/hwcontext.h> + #ifdef Q_OS_LINUX #include <libavutil/hwcontext_vaapi.h> -#elif defined(Q_OS_WINDOWS) -#include <libavutil/hwcontext_dxva2.h> +//#elif defined(Q_OS_WINDOWS) +//#include <libavutil/hwcontext_dxva2.h> #endif } @@ -128,6 +172,9 @@ static int closefd(int fd) { } #endif +#include <QtGui> +#include <QtConcurrent> + std::string eglErrorString(EGLint error) { switch (error) { case EGL_SUCCESS: @@ -366,9 +413,24 @@ namespace AVQt { bool shouldBe = true; if (d->m_firstFrame.compare_exchange_strong(shouldBe, false)) { if (input->format == AV_PIX_FMT_DXVA2_VLD) { +// AVFrame *sw_frame = av_frame_alloc(); +// if (av_hwframe_transfer_data(sw_frame, input, 0)) { +// qFatal("Could not map frame"); +// } +// QImage result(sw_frame->data[0], sw_frame->width, sw_frame->height, sw_frame->linesize[0], QImage::Format_Grayscale8); +// qDebug() << "Dimensions:" << result.size() << ", Size:" << result.sizeInBytes(); +// result.save("output3.bmp"); +// exit(0); d->m_pDXVAContext = static_cast<AVDXVA2DeviceContext *>(reinterpret_cast<AVHWDeviceContext *>(pDeviceCtx->data)->hwctx); d->m_pD3DManager = d->m_pDXVAContext->devmgr; d->m_pD3DManager->AddRef(); +// HANDLE hDevice; +// IDirect3DDevice9 *pDevice; +// d->m_pD3DManager->OpenDeviceHandle(&hDevice); +// d->m_pD3DManager->LockDevice(hDevice, &pDevice, TRUE); +// SurfaceToQImage(reinterpret_cast<LPDIRECT3DSURFACE9>(input->data[3])/*, pDevice*/, input->height); +// d->m_pD3DManager->UnlockDevice(hDevice, TRUE); +// d->m_pD3DManager->CloseDeviceHandle(hDevice); } else if (input->format == AV_PIX_FMT_D3D11) { d->m_pD3D11VAContext = static_cast<AVD3D11VADeviceContext *>(reinterpret_cast<AVHWDeviceContext *>(pDeviceCtx->data)->hwctx); } @@ -495,15 +557,15 @@ namespace AVQt { LOOKUP_FUNCTION(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR) LOOKUP_FUNCTION(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC, glEGLImageTargetTexture2DOES) #elif defined(Q_OS_WINDOWS) - WGL_LOOKUP_FUNCTION(PFNWGLDXOPENDEVICENVPROC, wglDXOpenDeviceNV); - WGL_LOOKUP_FUNCTION(PFNWGLDXCLOSEDEVICENVPROC, wglDXCloseDeviceNV); - WGL_LOOKUP_FUNCTION(PFNWGLDXREGISTEROBJECTNVPROC, wglDXRegisterObjectNV); - WGL_LOOKUP_FUNCTION(PFNWGLDXUNREGISTEROBJECTNVPROC, wglDXUnregisterObjectNV); - WGL_LOOKUP_FUNCTION(PFNWGLDXLOCKOBJECTSNVPROC, wglDXLockObjectsNV); - WGL_LOOKUP_FUNCTION(PFNWGLDXUNLOCKOBJECTSNVPROC, wglDXUnlockObjectsNV); - WGL_LOOKUP_FUNCTION(PFNWGLDXOBJECTACCESSNVPROC, wglDXObjectAccessNV); - WGL_LOOKUP_FUNCTION(PFNWGLDXSETRESOURCESHAREHANDLENVPROC, wglDXSetResourceShareHandleNV); - WGL_LOOKUP_FUNCTION(PFNGLACTIVETEXTUREPROC, glActiveTexture); + WGL_LOOKUP_FUNCTION(PFNWGLDXOPENDEVICENVPROC, wglDXOpenDeviceNV) + WGL_LOOKUP_FUNCTION(PFNWGLDXCLOSEDEVICENVPROC, wglDXCloseDeviceNV) + WGL_LOOKUP_FUNCTION(PFNWGLDXREGISTEROBJECTNVPROC, wglDXRegisterObjectNV) + WGL_LOOKUP_FUNCTION(PFNWGLDXUNREGISTEROBJECTNVPROC, wglDXUnregisterObjectNV) + WGL_LOOKUP_FUNCTION(PFNWGLDXLOCKOBJECTSNVPROC, wglDXLockObjectsNV) + WGL_LOOKUP_FUNCTION(PFNWGLDXUNLOCKOBJECTSNVPROC, wglDXUnlockObjectsNV) + WGL_LOOKUP_FUNCTION(PFNWGLDXOBJECTACCESSNVPROC, wglDXObjectAccessNV) + WGL_LOOKUP_FUNCTION(PFNWGLDXSETRESOURCESHAREHANDLENVPROC, wglDXSetResourceShareHandleNV) + WGL_LOOKUP_FUNCTION(PFNGLACTIVETEXTUREPROC, glActiveTexture) #endif if (d->m_currentFrame) { @@ -691,9 +753,11 @@ namespace AVQt { d->m_pD3DManager->OpenDeviceHandle(&hDevice); d->m_pD3DManager->LockDevice(hDevice, &pDevice, TRUE); - assert(SUCCEEDED(pDevice->CreateOffscreenPlainSurface(surfaceDesc.Width, surfaceDesc.Height, D3DFMT_A8R8G8B8, - D3DPOOL_DEFAULT, &d->m_pSharedSurface, - &d->m_hSharedSurface))); + qWarning("Creating surface with %dx%d pixels", surfaceDesc.Width, surfaceDesc.Height); + + pDevice->CreateOffscreenPlainSurface(surfaceDesc.Width, surfaceDesc.Height, D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, &d->m_pSharedSurface, + &d->m_hSharedSurface); d->m_hDXDevice = wglDXOpenDeviceNV(pDevice); if (d->m_hDXDevice) { @@ -705,6 +769,7 @@ namespace AVQt { pDevice->Release(); d->m_pD3DManager->UnlockDevice(hDevice, TRUE); } else if (d->m_currentFrame->format == AV_PIX_FMT_D3D11) { + qFatal("D3D11VA -> OpenGL interop is being worked on"); glGenTextures(1, d->m_textures); glBindTexture(GL_TEXTURE_2D, d->m_textures[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -715,10 +780,8 @@ namespace AVQt { d->m_program->setUniformValue("inputFormat", 0); d->m_program->release(); - ID3D11Texture2D *texture = reinterpret_cast<ID3D11Texture2D*>(d->m_currentFrame->data[0]); d->m_hDXDevice = wglDXOpenDeviceNV(d->m_pD3D11VAContext->device); - qFatal("D3D11VA -> OpenGL interop is in progress"); //TODO: D3D11 render to texture for NV12/P010 -> RGB conversion, direct NV12/P010 OpenGL mapping not possible, because both planes are in one memory block } else #endif @@ -912,10 +975,19 @@ namespace AVQt { d->m_pD3DManager->LockDevice(hDevice, &pDevice, true); IDirect3D9 *d3d9; pDevice->GetDirect3D(&d3d9); - assert(SUCCEEDED(d3d9->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, surfaceDesc.Format, + ASSERT(SUCCEEDED(d3d9->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, surfaceDesc.Format, D3DFMT_A8R8G8B8))); - assert(SUCCEEDED(pDevice->StretchRect(surface, nullptr, d->m_pSharedSurface, nullptr, D3DTEXF_LINEAR))); + if (0 != pDevice->StretchRect(surface, nullptr, d->m_pSharedSurface, nullptr, D3DTEXF_LINEAR)) { + qFatal("Could not map NV12/P010 surface to RGB"); + } + +// auto image = SurfaceToQImage(surface/*, pDevice*/, d->m_currentFrame->height); +// if (!image.isNull()) { +// image.save("output.bmp"); +// } + d->m_pD3DManager->UnlockDevice(hDevice, TRUE); + d->m_pD3DManager->CloseDeviceHandle(hDevice); // auto image = SurfaceToQImage(surface, frame->height); // qDebug() << "Decoded surface is using" << image.sizeInBytes() << "Byte of memory at" << image.size(); // image.save("decoded.bmp"); @@ -1167,6 +1239,6 @@ namespace AVQt { int s = static_cast<int>((ts / 1000) % 60); int m = static_cast<int>((ts / 1000 / 60) % 60); int h = static_cast<int>(ts / 1000 / 60 / 60); - return QTime(h, m, s, ms); + return {h, m, s, ms}; } } \ No newline at end of file diff --git a/AVQt/output/private/Muxer_p.h b/AVQt/output/private/Muxer_p.h index 7aefc087e4a29b00bbe2f80c28231fd7e86529c9..6a097444d6674d88855972c15080e71936763156 100644 --- a/AVQt/output/private/Muxer_p.h +++ b/AVQt/output/private/Muxer_p.h @@ -30,7 +30,7 @@ namespace AVQt { Muxer::FORMAT m_format{Muxer::FORMAT::INVALID}; - QMutex m_initMutex{}; + QMutex m_initMutex{}, m_ioMutex{}; static constexpr int64_t IOBUF_SIZE{4 * 1024}; // 4 KB uint8_t *m_pIOBuffer{nullptr}; AVIOContext *m_pIOContext{nullptr}; diff --git a/CMakeLists.txt b/CMakeLists.txt index ad04765f27265ee05af97c7128d92a21e2d5ead6..04d51c2cf8973d73e4d959670b721a49c37b67ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,21 @@ project(LibAVQt) set(CMAKE_CXX_STANDARD 20) if (WIN32) - include_directories(${OPENAL_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIR}) - link_directories(${OPENAL_LIBRARY} ${FFMPEG_LIBRARY}) - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS on) -endif () + if (NOT DEFINED ${OPENAL_INCLUDE_DIR}) + set(OPENAL_INCLUDE_DIR "$ENV{OPENAL_INCLUDE_DIR}") + endif() + if (NOT DEFINED ${OPENAL_LIBRARY}) + set(OPENAL_LIBRARY $ENV{OPENAL_LIBRARY}) + endif() -if (WIN32) + if (NOT DEFINED ${FFMPEG_INCLUDE_DIR}) + set(FFMPEG_INCLUDE_DIR $ENV{FFMPEG_INCLUDE_DIR}) + endif() + if (NOT DEFINED ${FFMPEG_LIBRARY}) + set(FFMPEG_LIBRARY $ENV{FFMPEG_LIBRARY}) + endif() + include_directories(${OPENAL_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIR} mfx_dispatch/) + link_directories(${OPENAL_LIBRARY} ${FFMPEG_LIBRARY}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) endif () @@ -20,4 +29,4 @@ set(CMAKE_AUTOUIC on) add_subdirectory(AVQt) include_directories(AVQt) -add_subdirectory(Player) \ No newline at end of file +add_subdirectory(Player) diff --git a/Player/CMakeLists.txt b/Player/CMakeLists.txt index bdfae7fec8c76e2e732e415fc2fd71376f1069db..c5d7fb964aae328d031cd49de327be83a6e6e97b 100644 --- a/Player/CMakeLists.txt +++ b/Player/CMakeLists.txt @@ -58,9 +58,8 @@ endif () target_link_libraries(Player avformat avfilter avutil avcodec avdevice swscale swresample AVQt) -find_package(OpenAL) - if (LINUX) + find_package(OpenAL) target_link_libraries(Player openal GL EGL GLU va) elseif (WIN32) target_link_libraries(Player OpenAL32 opengl32 dxguid d3d9) diff --git a/Player/main.cpp b/Player/main.cpp index 0fd139c018352129d10e41a327995907b4a325dd..65bffabbd1384fed22a943a3e23c6a9c5ac317a4 100644 --- a/Player/main.cpp +++ b/Player/main.cpp @@ -83,9 +83,9 @@ int main(int argc, char *argv[]) { AVQt::OpenGLRenderer renderer; demuxer.registerCallback(videoDecoder, AVQt::IPacketSource::CB_VIDEO); -#ifdef ENABLE_QSV_ENCODE -// videoDecoder->registerCallback(videoEncoder); -#endif +//#ifdef ENABLE_QSV_ENCODE + videoDecoder->registerCallback(videoEncoder); +//#endif QFile outputFile("output.mp4"); outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate); outputFile.seek(0);