From 16ff5f963cdd12f3e6f2001799eb355562d0c6a7 Mon Sep 17 00:00:00 2001 From: Silas Della Contrada <s.develop@4-dc.de> Date: Wed, 27 Jan 2021 10:14:11 +0100 Subject: [PATCH] Feature complete: Automatic encoder selection based on gpu - Don't use the nVidia GPU on optimus devices for the application if you have an GeForce MX GPU, because they don't have any video de-/ encoders and the application selects the encoder based on the rendering GPU --- VanadiumCast/VanadiumCast.pro | 3 +- VanadiumCast/res/gui/fragments/PageMedia.qml | 2 +- VanadiumCast/src/main.cpp | 5 +- backend/backend.pro | 3 + backend/src/MediaProcessing/OGLUtil.cpp | 61 +++++++++++++++++++ backend/src/MediaProcessing/OGLUtil.h | 31 ++++++++++ .../src/MediaProcessing/VideoTranscoder.cpp | 10 ++- backend/src/MediaProcessing/VideoTranscoder.h | 50 +++++++++++++-- 8 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 backend/src/MediaProcessing/OGLUtil.cpp create mode 100644 backend/src/MediaProcessing/OGLUtil.h diff --git a/VanadiumCast/VanadiumCast.pro b/VanadiumCast/VanadiumCast.pro index 9be353e..02657b9 100644 --- a/VanadiumCast/VanadiumCast.pro +++ b/VanadiumCast/VanadiumCast.pro @@ -1,4 +1,4 @@ -QT += core quick quickcontrols2 gui widgets multimedia network concurrent +QT += core quick quickcontrols2 gui widgets multimedia network concurrent opengl CONFIG += qtquickcompiler @@ -23,6 +23,7 @@ defineTest(copyToDestDir) { win32 { QMAKE_CXXFLAGS_RELEASE += /O2 /Oy + LIBS += -lOpenGL32 CONFIG(release, debug|release) { message("release") # LIBS += -LE:/Dev/QtAV/build-release/lib_win_x86_64 -lQtAV1 -lQtAVWidgets1 diff --git a/VanadiumCast/res/gui/fragments/PageMedia.qml b/VanadiumCast/res/gui/fragments/PageMedia.qml index 317075e..81326ac 100644 --- a/VanadiumCast/res/gui/fragments/PageMedia.qml +++ b/VanadiumCast/res/gui/fragments/PageMedia.qml @@ -114,7 +114,7 @@ Page { anchors.topMargin: 8 height: 1280 width: 720 - videoCodecPriority: ["QSV", "DXVA", "MMAL", "CUDA", "FFMPEG"] + videoCodecPriority: ["DXVA", "QSV", "MMAL", "CUDA", "VAAPI", "FFMPEG"] audioBackends: ["OpenAL", "XAudio2", "null"] Component.onCompleted: console.log(previewVideo.audioBackends) smooth: true diff --git a/VanadiumCast/src/main.cpp b/VanadiumCast/src/main.cpp index cd7b1cb..a05602c 100644 --- a/VanadiumCast/src/main.cpp +++ b/VanadiumCast/src/main.cpp @@ -7,6 +7,8 @@ #include <QtNetwork> #include <csignal> #include "API/NetworkAPI.h" +#include <QtOpenGL> +#include <QtOpenGL/QGLContext> QApplication *app; @@ -25,8 +27,9 @@ int main(int argc, char *argv[]) app = new QApplication(argc, argv); NetworkAPI api; - api.init(); QtAV::Widgets::registerRenderers(); + qDebug() << QtAV::VideoEncoder::supportedCodecs(); + api.init(); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("deviceDirectory", api.getDeviceDirectory()); diff --git a/backend/backend.pro b/backend/backend.pro index ca40f0b..65f2a7a 100644 --- a/backend/backend.pro +++ b/backend/backend.pro @@ -4,6 +4,7 @@ QT += core gui widgets network multimedia quick concurrent win32 { QMAKE_CXXFLAGS_RELEASE += /O2 /Oy + LIBS += -lOpenGL32 CONFIG(release, debug|release) { message("release") message("$$QT.core.libs") @@ -33,6 +34,7 @@ SOURCES += src/util.cpp \ src/GUI/WindowCloseEventFilter.cpp \ src/MediaProcessing/CachedLocalStream.cpp \ src/MediaProcessing/InputFile.cpp \ + src/MediaProcessing/OGLUtil.cpp \ src/MediaProcessing/VideoTranscoder.cpp \ src/Networking/NetworkDevice.cpp \ src/Networking/NetworkDeviceDirectory.cpp \ @@ -48,6 +50,7 @@ HEADERS += src/util.h \ src/GUI/WindowCloseEventFilter.h \ src/MediaProcessing/CachedLocalStream.h \ src/MediaProcessing/InputFile.h \ + src/MediaProcessing/OGLUtil.h \ src/MediaProcessing/VideoTranscoder.h \ src/Networking/NetworkDevice.h \ src/Networking/NetworkDeviceDirectory.h \ diff --git a/backend/src/MediaProcessing/OGLUtil.cpp b/backend/src/MediaProcessing/OGLUtil.cpp new file mode 100644 index 0000000..8fb7735 --- /dev/null +++ b/backend/src/MediaProcessing/OGLUtil.cpp @@ -0,0 +1,61 @@ +#include "OGLUtil.h" +#include <QtOpenGL/QtOpenGL> + +OGLUtil::OGLUtil(QObject *parent) : QObject(parent) +{ + +} + +QVariant OGLUtil::getResult() { + if (done) { + return result; + } else { + return QVariant(); + } +} + +bool OGLUtil::waitForFinished(qint64 msecs) +{ + auto t1 = std::chrono::system_clock::now(); + while (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - t1).count() <= msecs && !done) { + QCoreApplication::processEvents(); + } + return done; +} + +bool OGLUtil::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + switch (action) { + case Action::GET_OGL_VENDOR: { + QOpenGLContext *context = new QOpenGLContext(); + QOffscreenSurface *surface = new QOffscreenSurface(); + context->create(); + surface->create(); + context->makeCurrent(surface); + QMutexLocker locker(&resultLock); + result.setValue(QString::fromLocal8Bit(reinterpret_cast<const char*>(glGetString(GL_VENDOR)))); + locker.unlock(); + surface->destroy(); + delete context; + delete surface; + done = true; + break; + } + } + return true; + } + return QObject::event(event); +} + +bool OGLUtil::triggerAction(OGLUtil::Action action) +{ + if (done) { + done = false; + this->action = action; + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + return true; + } else { + return false; + } +} diff --git a/backend/src/MediaProcessing/OGLUtil.h b/backend/src/MediaProcessing/OGLUtil.h new file mode 100644 index 0000000..b800412 --- /dev/null +++ b/backend/src/MediaProcessing/OGLUtil.h @@ -0,0 +1,31 @@ +#ifndef OGLUTIL_H +#define OGLUTIL_H + +#include <QtCore> + +class OGLUtil : public QObject +{ + Q_OBJECT +public: + enum class Action { + GET_OGL_VENDOR + }; + explicit OGLUtil(QObject *parent = nullptr); + + bool waitForFinished(qint64 msecs); + + QVariant getResult(); + +public slots: + bool event(QEvent *event) override; + + bool triggerAction(Action action); + +private: + QMutex resultLock; + QVariant result; + Action action; + bool done = true; +}; + +#endif // OGLUTIL_H diff --git a/backend/src/MediaProcessing/VideoTranscoder.cpp b/backend/src/MediaProcessing/VideoTranscoder.cpp index 299d697..306e096 100644 --- a/backend/src/MediaProcessing/VideoTranscoder.cpp +++ b/backend/src/MediaProcessing/VideoTranscoder.cpp @@ -22,7 +22,15 @@ VideoTranscoder::VideoTranscoder(std::string inputFilePath, End *outputDevice, E avPlayer->setAudioStream(-1); // } avPlayer->setFrameRate(10000.0); - avPlayer->setVideoDecoderPriority(QStringList() << "QSV" << "DXVA" << "VAAPI" << "MMAL" << "VideoToolbox" << "CUDA" << "FFmpeg"); +#ifdef _WIN32 + avPlayer->setVideoDecoderPriority(QStringList() << "DXVA" << "QSV" << "CUDA" << "FFmpeg"); +#endif +#ifdef __linux__ + avPlayer->setVideoDecoderPriority(QStringList() << "QSV" << "CUDA" << "VAAPI" << "FFmpeg"); +#endif +#ifdef __APPLE__ + avPlayer->setVideoDecoderPriority(QStringList() << "VideoToolbox" << "FFmpeg"); +#endif bufferCon1 = connect(outputDevice, &End::outputUnderrun, [&]() { if (!isPausedByUser && avPlayer->isPaused()) { // qDebug() << "[VideoTranscoder] Resuming"; diff --git a/backend/src/MediaProcessing/VideoTranscoder.h b/backend/src/MediaProcessing/VideoTranscoder.h index 2b3589d..91e1934 100644 --- a/backend/src/MediaProcessing/VideoTranscoder.h +++ b/backend/src/MediaProcessing/VideoTranscoder.h @@ -2,12 +2,15 @@ #define VIDEOTRANSCODER_H #include <QObject> +#include <QOffscreenSurface> #include <QtAV/QtAV> #include <QtAV/AVTranscoder.h> #include <QtAVWidgets/QtAVWidgets> #include "PlayerStateSlots.h" #include "EncodingProfile.h" #include "CachedLocalStream.h" +#include <gl/GL.h> +#include "OGLUtil.h" class VideoTranscoder : public QObject { Q_OBJECT @@ -47,9 +50,45 @@ private: static void initializeProfiles() { static bool encodingProfilesInitialized = false; if (!encodingProfilesInitialized) { + OGLUtil *oglutil = new OGLUtil; + oglutil->moveToThread(qApp->thread()); + oglutil->triggerAction(OGLUtil::Action::GET_OGL_VENDOR); + oglutil->waitForFinished(10000); + QString vendor = oglutil->getResult().toString(); + QString videoCodecSQ = ""; + QString videoCodecHQ = ""; +// qDebug() << "[VideoTranscoder] Video card vendor:"; + +#ifdef __APPLE__ + videoCodecSQ = "h264_videotoolbox"; + videoCodecHQ = "hevc_videotoolbox"; +#else + qDebug() << "[VideoTranscoder] OpenGL Renderer:" << vendor; + if (vendor.compare("Intel", Qt::CaseInsensitive) == 0) { + qDebug() << "[VideoTranscoder] Intel QSV encoder selected"; + videoCodecSQ = "h264_qsv"; + videoCodecHQ = "h264_qsv"; + } else if (vendor.compare("NVIDIA Corporation", Qt::CaseInsensitive) == 0) { + qDebug() << "[VideoTranscoder] nVidia NVENC encoder selected"; + videoCodecSQ = "h264_nvenc"; + videoCodecHQ = "hevc_nvenc"; + } else if (vendor.compare("AMD", Qt::CaseInsensitive) == 0) { +#ifdef _WIN32 + qDebug() << "[VideoTranscoder] AMD AMF encoder selected"; + videoCodecSQ = "h264_amf"; + videoCodecHQ = "hevc_amf"; +#endif +#ifdef __linux__ + qDebug() << "[VideoTranscoder] AMD VAAPI encoder selected"; + videoCodec1 = "h264_vaapi"; + videoCodec2 = "hevc_vaapi"; +#endif + } +#endif + // Low profile LOW.audioCodecName = "aac"; - LOW.videoCodecName = "h264_qsv"; + LOW.videoCodecName = videoCodecSQ; LOW.width = 1280; LOW.height = 720; LOW.rate = 1000000; @@ -58,7 +97,7 @@ private: // Standard profile MEDIUM.audioCodecName = "aac"; - MEDIUM.videoCodecName = "h264_qsv"; + MEDIUM.videoCodecName = videoCodecSQ; MEDIUM.width = 1920; MEDIUM.height = 1080; MEDIUM.rate = 5000000; @@ -67,7 +106,7 @@ private: // High profile HIGH.audioCodecName = "aac"; - HIGH.videoCodecName = "h264_qsv"; + HIGH.videoCodecName = videoCodecSQ; HIGH.width = 1920; HIGH.height = 1080; HIGH.rate = 10000000; @@ -76,7 +115,7 @@ private: // Ultra profile ULTRA.audioCodecName = "aac"; - ULTRA.videoCodecName = "hevc_qsv"; + ULTRA.videoCodecName = videoCodecHQ; ULTRA.width = 2560; ULTRA.height = 1440; ULTRA.rate = 15000000; @@ -85,7 +124,7 @@ private: // Extreme profile EXTREME.audioCodecName = "aac"; - EXTREME.videoCodecName = "hevc_qsv"; + EXTREME.videoCodecName = videoCodecHQ; EXTREME.width = 3840; EXTREME.height = 2160; EXTREME.rate = 30000000; @@ -103,6 +142,7 @@ private: QMetaObject::Connection bufferCon1, bufferCon2, posCon1, posCon2; bool isPausedByUser = false; qint64 duration = 0; + OGLUtil *oglutil; }; #endif // VIDEOTRANSCODER_H -- GitLab