From 1e2605e99d692d99ab9b0b56183d34769af8f4be Mon Sep 17 00:00:00 2001
From: Silas Della Contrada <s.develop@4-dc.de>
Date: Thu, 7 Apr 2022 11:17:52 +0200
Subject: [PATCH] feat(codec): Implemented QSV decoder

fix(common): Changed QSV native format selector to:
  One luminance plane and one chrominance plane
fix(render): Now moving opengl context back to main thread after FallbackFrameMapper stops
---
 AVQt/src/common/PixelFormat.cpp               |  2 +-
 AVQt/src/decoder/QSVDecoderImpl.cpp           | 20 ++++++++++++++---
 AVQt/src/decoder/QSVDecoderImpl.hpp           |  3 ++-
 AVQt/src/decoder/private/QSVDecoderImpl_p.hpp |  2 ++
 AVQt/src/renderers/FallbackFrameMapper.cpp    |  4 ++++
 .../private/FallbackFrameMapper_p.hpp         |  2 ++
 CMakeLists.txt                                |  2 +-
 Player/OpenGlWidgetRenderer.cpp               |  4 ++--
 Player/main.cpp                               | 22 +++++++++----------
 9 files changed, 42 insertions(+), 19 deletions(-)

diff --git a/AVQt/src/common/PixelFormat.cpp b/AVQt/src/common/PixelFormat.cpp
index 95a712f..d4972f3 100644
--- a/AVQt/src/common/PixelFormat.cpp
+++ b/AVQt/src/common/PixelFormat.cpp
@@ -80,7 +80,7 @@ namespace AVQt::common {
 
             AVQt::common::PixelFormat::registerGPUFormat({.format = AV_PIX_FMT_MMAL, .nativeFormat = nullptr});
             AVQt::common::PixelFormat::registerGPUFormat({.format = AV_PIX_FMT_MEDIACODEC, .nativeFormat = nullptr});
-            AVQt::common::PixelFormat::registerGPUFormat({.format = AV_PIX_FMT_QSV, .nativeFormat = &FullFormatPixelFormatOf});
+            AVQt::common::PixelFormat::registerGPUFormat({.format = AV_PIX_FMT_QSV, .nativeFormat = &OneInterleavedPlanePixelFormatOf});
             AVQt::common::PixelFormat::registerGPUFormat({.format = AV_PIX_FMT_VDPAU, .nativeFormat = nullptr});
             AVQt::common::PixelFormat::registerGPUFormat({.format = AV_PIX_FMT_VIDEOTOOLBOX, .nativeFormat = nullptr});
 
diff --git a/AVQt/src/decoder/QSVDecoderImpl.cpp b/AVQt/src/decoder/QSVDecoderImpl.cpp
index 1df1640..14a71c9 100644
--- a/AVQt/src/decoder/QSVDecoderImpl.cpp
+++ b/AVQt/src/decoder/QSVDecoderImpl.cpp
@@ -6,13 +6,14 @@
 #include "private/QSVDecoderImpl_p.hpp"
 
 #include "common/PixelFormat.hpp"
+#include "decoder/VideoDecoderFactory.hpp"
 
 namespace AVQt {
     const api::VideoDecoderInfo &QSVDecoderImpl::info() {
         static const api::VideoDecoderInfo info = {
                 .metaObject = QSVDecoderImpl::staticMetaObject,
-                .name = "VAAPI",
-                .platforms = {common::Platform::Linux_X11, common::Platform::Linux_Wayland},
+                .name = "QSV",
+                .platforms = {common::Platform::Linux_X11, common::Platform::Linux_Wayland, common::Platform::Windows},
                 .supportedInputPixelFormats = {
                         {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE},
                         {AV_PIX_FMT_YUV420P10, AV_PIX_FMT_NONE},
@@ -102,7 +103,7 @@ namespace AVQt {
 
             auto *framesContext = reinterpret_cast<AVHWFramesContext *>(framesCtxRef->data);
             framesContext->format = AV_PIX_FMT_QSV;
-            framesContext->sw_format = static_cast<AVPixelFormat>(d->codecParams->format);
+            framesContext->sw_format = common::PixelFormat{static_cast<AVPixelFormat>(d->codecParams->format), AV_PIX_FMT_QSV}.toNativeFormat().getCPUFormat();
             framesContext->width = d->codecParams->width;
             framesContext->height = d->codecParams->height;
             framesContext->initial_pool_size = 32;
@@ -128,6 +129,13 @@ namespace AVQt {
                 goto fail;
             }
 
+            d->frameFetcher = std::make_unique<QSVDecoderImplPrivate::FrameFetcher>(d);
+            d->frameFetcher->start();
+            if (!d->frameFetcher->isRunning()) {
+                qWarning() << "[AVQt::QSVDecoderImpl] Failed to start frame fetcher";
+                goto fail;
+            }
+
             return true;
         } else {
             qWarning("[AVQt::QSVDecoderImpl] Already opened");
@@ -135,6 +143,7 @@ namespace AVQt {
         }
     fail:
         d->open = false;
+        d->frameFetcher.reset();
         d->codecContext.reset();
         d->hwDeviceContext.reset();
         d->hwFramesContext.reset();
@@ -146,6 +155,7 @@ namespace AVQt {
 
         bool shouldBe = false;
         if (d->open.compare_exchange_strong(shouldBe, false)) {
+            d->frameFetcher.reset();
             d->codecContext.reset();
             d->hwDeviceContext.reset();
             d->hwFramesContext.reset();
@@ -308,3 +318,7 @@ namespace AVQt {
         qDebug() << "FrameFetcher stopped";
     }
 }// namespace AVQt
+
+static_block {
+    AVQt::VideoDecoderFactory::getInstance().registerDecoder(AVQt::QSVDecoderImpl::info());
+}
diff --git a/AVQt/src/decoder/QSVDecoderImpl.hpp b/AVQt/src/decoder/QSVDecoderImpl.hpp
index e26fff2..3915d73 100644
--- a/AVQt/src/decoder/QSVDecoderImpl.hpp
+++ b/AVQt/src/decoder/QSVDecoderImpl.hpp
@@ -14,10 +14,11 @@ namespace AVQt {
     class QSVDecoderImpl : public QObject, public api::IVideoDecoderImpl {
         Q_OBJECT
         Q_DECLARE_PRIVATE(QSVDecoderImpl)
+        Q_INTERFACES(AVQt::api::IVideoDecoderImpl)
     public:
         static const api::VideoDecoderInfo &info();
 
-        explicit QSVDecoderImpl(AVCodecID codecId, QObject *parent = nullptr);
+        Q_INVOKABLE explicit QSVDecoderImpl(AVCodecID codecId, QObject *parent = nullptr);
         ~QSVDecoderImpl() override;
         bool open(std::shared_ptr<AVCodecParameters> codecParams) override;
         void close() override;
diff --git a/AVQt/src/decoder/private/QSVDecoderImpl_p.hpp b/AVQt/src/decoder/private/QSVDecoderImpl_p.hpp
index b047224..f159008 100644
--- a/AVQt/src/decoder/private/QSVDecoderImpl_p.hpp
+++ b/AVQt/src/decoder/private/QSVDecoderImpl_p.hpp
@@ -50,6 +50,8 @@ namespace AVQt {
         std::shared_ptr<AVBufferRef> hwFramesContext{nullptr, &destroyAVBufferRef};
 
         std::atomic_bool open{false}, firstFrame{true};
+
+        std::unique_ptr<FrameFetcher> frameFetcher{};
     };
 }// namespace AVQt
 
diff --git a/AVQt/src/renderers/FallbackFrameMapper.cpp b/AVQt/src/renderers/FallbackFrameMapper.cpp
index ed4157b..860b4f9 100644
--- a/AVQt/src/renderers/FallbackFrameMapper.cpp
+++ b/AVQt/src/renderers/FallbackFrameMapper.cpp
@@ -209,6 +209,7 @@ namespace AVQt {
         QMutexLocker lock(&d->renderMutex);
         bool shouldBe = true;
         if (d->running.compare_exchange_strong(shouldBe, false)) {
+            d->afterStopThread = QThread::currentThread();
             lock.unlock();
             QThread::quit();
             QThread::wait();
@@ -382,6 +383,9 @@ namespace AVQt {
             emit frameReady(frame->pts, fbo);
             qDebug("Frame ready");
         }
+        if (d->afterStopThread) {
+            d->context->moveToThread(d->afterStopThread);
+        }
     }
 
     void FallbackFrameMapperPrivate::bindResources() {
diff --git a/AVQt/src/renderers/private/FallbackFrameMapper_p.hpp b/AVQt/src/renderers/private/FallbackFrameMapper_p.hpp
index 97f1f75..d86791b 100644
--- a/AVQt/src/renderers/private/FallbackFrameMapper_p.hpp
+++ b/AVQt/src/renderers/private/FallbackFrameMapper_p.hpp
@@ -73,6 +73,8 @@ namespace AVQt {
         QOpenGLBuffer vbo{};
         QOpenGLBuffer ibo{};
 
+        QThread *afterStopThread{nullptr};
+
         std::unique_ptr<SwsContext, decltype(&destroySwsContext)> pSwsContext{nullptr, &destroySwsContext};
 
         std::atomic_bool paused{false}, firstFrame{true}, running{false};
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b6fb8c..5296a9e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@ if (NOT QT_VERSION)
 endif ()
 set(PGRAPH_QT_VERSION "${QT_VERSION}" CACHE INTERNAL "PGRAPH_QT_VERSION")
 set(SC_QT_VERSION "${QT_VERSION}" CACHE INTERNAL "SC_QT_VERSION")
-#set(CMAKE_PREFIX_PATH "/home/silas/ffmpeg_build/" CACHE INTERNAL "CMAKE_PREFIX_PATH")
+set(CMAKE_PREFIX_PATH "/home/silas/ffmpeg_build/" CACHE INTERNAL "CMAKE_PREFIX_PATH")
 
 add_subdirectory(libpgraph_network)
 
diff --git a/Player/OpenGlWidgetRenderer.cpp b/Player/OpenGlWidgetRenderer.cpp
index 9fe9315..7e637cd 100644
--- a/Player/OpenGlWidgetRenderer.cpp
+++ b/Player/OpenGlWidgetRenderer.cpp
@@ -246,8 +246,8 @@ bool OpenGLWidgetRenderer::isPaused() const {
 bool OpenGLWidgetRenderer::open() {
     Q_D(OpenGLWidgetRenderer);
 
-    d->mapper = AVQt::OpenGLFrameMapperFactory::getInstance().create({d->params.isHWAccel ? d->params.swPixelFormat : d->params.pixelFormat, d->params.isHWAccel ? d->params.pixelFormat : AV_PIX_FMT_NONE},
-                                                                     QStringList() << "VAAPI");
+    d->mapper = AVQt::OpenGLFrameMapperFactory::getInstance()
+                        .create({d->params.isHWAccel ? d->params.swPixelFormat : d->params.pixelFormat, d->params.isHWAccel ? d->params.pixelFormat : AV_PIX_FMT_NONE});
     // clang-format off
     // Preserve normalized form of Qt SIGNAL/SLOT macro
     connect(std::dynamic_pointer_cast<QObject>(d->mapper).get(), SIGNAL(frameReady(qint64,std::shared_ptr<QOpenGLFramebufferObject>)),
diff --git a/Player/main.cpp b/Player/main.cpp
index ae94d15..4046b4d 100644
--- a/Player/main.cpp
+++ b/Player/main.cpp
@@ -53,9 +53,9 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
         logFile.open(QIODevice::WriteOnly);
     }
 
-    //#ifndef QT_DEBUG
+#ifndef QT_DEBUG
     if (type > QtMsgType::QtDebugMsg)
-    //#endif
+#endif
     {
         QString output;
         QTextStream os(&output);
@@ -144,7 +144,7 @@ int main(int argc, char *argv[]) {
     auto muxer = std::make_shared<AVQt::Muxer>(std::move(muxerConfig), registry);
 
     AVQt::VideoDecoder::Config videoDecoderConfig{};
-    videoDecoderConfig.decoderPriority << "VAAPI";
+    videoDecoderConfig.decoderPriority << "QSV";
     auto decoder1 = std::make_shared<AVQt::VideoDecoder>(videoDecoderConfig, registry);
     auto decoder2 = std::make_shared<AVQt::VideoDecoder>(videoDecoderConfig, registry);
     //    auto decoder3 = std::make_shared<AVQt::VideoDecoder>("VAAPI", registry);
@@ -220,9 +220,9 @@ int main(int argc, char *argv[]) {
         //    encoderInPad->link(decoder1OutPad);
         //    decoder2InPad->link(encoderOutPad);
         decoder1InPad->link(demuxerOutPad);
-        //    ccInPad->link(decoder1OutPad);
-        encoder1InPad->link(decoder1OutPad);
-        encoder2InPad->link(decoder1OutPad);
+        ccInPad->link(decoder1OutPad);
+        //        encoder1InPad->link(decoder1OutPad);
+        //        encoder2InPad->link(decoder1OutPad);
         //    yuvrgbconverterInPad->link(decoder1OutPad);
         //    renderer2InPad->link(decoder1OutPad);
         renderer1InPad->link(decoder1OutPad);
@@ -231,8 +231,8 @@ int main(int argc, char *argv[]) {
         //    renderer2InPad->link(decoder2OutPad);
         //    yuvrgbconverterOutPad->link(frameSaverInPad);
 
-        muxerInPad1->link(encoder1OutPad);
-        muxerInPad2->link(encoder2OutPad);
+        //        muxerInPad1->link(encoder1OutPad);
+        //        muxerInPad2->link(encoder2OutPad);
 
         demuxer->open();
 
@@ -241,9 +241,9 @@ int main(int argc, char *argv[]) {
 
         demuxer->start();
 
-        QTimer::singleShot(5000, [demuxer]() {
-            QApplication::quit();
-        });
+        //        QTimer::singleShot(5000, [demuxer]() {
+        //            QApplication::quit();
+        //        });
 
         //    QTimer::singleShot(4000, [demuxer]{
         //        demuxer->pause(true);
-- 
GitLab