From 0a6f667a5476029ae7dd3544f8750e13678cbf29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= Date: Tue, 9 May 2023 12:44:57 +0200 Subject: Implement QFFmpegScreenCaptureDxgi in terms of common FFMPeg base class The QtMultimedia FFMpeg screen capture plugins all share a common base class 'QFFMpegScreenCaptureBase' that implements the bulk of the screen capture functionality, including the grabber thread and its screen capture loop. The Windows screen capture class 'QFFMpegScreenCaptureDXGI' class is, on the other hand, having its own implementation which makes it look and behave differently from the other implementations. This change refactors the Windows DXGI screen capture class to share base class with the other platform implementations. This reduces code duplication, and makes the class easier to maintain because it follows the same pattern as the other screen capture implementations. No functionality is changed through this refactoring. Task-number: QTBUG-113460 Pick-to: 6.5 Change-Id: Idd157c96e1705a9fe6eb591920177f6afb88cd2a Reviewed-by: Lars Knoll Reviewed-by: Artem Dyomin --- .../ffmpeg/qffmpegscreencapture_dxgi.cpp | 163 +++++++-------------- .../ffmpeg/qffmpegscreencapture_dxgi_p.h | 21 +-- .../ffmpeg/qffmpegscreencapturethread_p.h | 8 +- 3 files changed, 67 insertions(+), 125 deletions(-) diff --git a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp index 3852c8cad..040509bb1 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp @@ -3,6 +3,7 @@ // GPL-3.0-only #include "qffmpegscreencapture_dxgi_p.h" +#include "qffmpegscreencapturethread_p.h" #include #include #include @@ -152,64 +153,40 @@ static QMaybe> getNextFrame(ID3D11Device *dev return texCopy; } -class DxgiScreenGrabberActive : public QThread +class QFFmpegScreenCaptureDxgi::Grabber : public QFFmpegScreenCaptureThread { public: - DxgiScreenGrabberActive(QPlatformScreenCapture &screenCapture, QWindowsIUPointer &device, - QWindowsIUPointer &duplication, qreal maxFrameRate) - : QThread() - , m_screenCapture(screenCapture) + Grabber(QFFmpegScreenCaptureDxgi &screenCapture, QScreen *screen, QWindowsIUPointer &device, + QWindowsIUPointer &duplication) + : QFFmpegScreenCaptureThread() , m_duplication(duplication) , m_device(device) - , m_maxFrameRate(maxFrameRate) - {} - - void run() override + , m_ctxMutex(std::make_shared()) { - // TODO: refactor with QTimer + setFrameRate(screen->refreshRate()); + addFrameCallback(screenCapture, &QFFmpegScreenCaptureDxgi::newVideoFrame); + connect(this, &Grabber::errorUpdated, &screenCapture, &QFFmpegScreenCaptureDxgi::updateError); + } - qCDebug(qLcScreenCaptureDxgi) << "ScreenGrabberActive started"; + ~Grabber() { + stop(); + } + void run() override + { DXGI_OUTDUPL_DESC outputDesc = {}; m_duplication->GetDesc(&outputDesc); - QSize frameSize = { int(outputDesc.ModeDesc.Width), int(outputDesc.ModeDesc.Height) }; - QVideoFrameFormat format(frameSize, QVideoFrameFormat::Format_BGRA8888); + m_frameSize = { int(outputDesc.ModeDesc.Width), int(outputDesc.ModeDesc.Height) }; + QVideoFrameFormat frameFormat(m_frameSize, QVideoFrameFormat::Format_BGRA8888); m_formatMutex.lock(); - m_format = format; - m_format.setFrameRate(int(m_maxFrameRate)); + m_format = frameFormat; + m_format.setFrameRate(int(frameRate())); m_formatMutex.unlock(); m_waitForFormat.wakeAll(); - const microseconds frameTime(quint64(1000000. / m_maxFrameRate)); - time_point frameStopTime = steady_clock::now(); - time_point frameStartTime = frameStopTime - frameTime; - - std::shared_ptr ctxMutex(new QMutex); - while (!isInterruptionRequested()) { - ctxMutex->lock(); - auto maybeTex = getNextFrame(m_device.get(), m_duplication.get()); - ctxMutex->unlock(); - - if (maybeTex) { - auto buffer = new QD3D11TextureVideoBuffer(m_device, ctxMutex, maybeTex.value(), frameSize); - QVideoFrame frame(buffer, format); - frame.setStartTime(duration_cast(frameStartTime.time_since_epoch()).count()); - frame.setEndTime(duration_cast(frameStopTime.time_since_epoch()).count()); - emit m_screenCapture.newVideoFrame(frame); - } else { - emit m_screenCapture.updateError(maybeTex.error().isEmpty() ? QScreenCapture::NoError - : QScreenCapture::CaptureFailed, - maybeTex.error()); - } - - frameStartTime = frameStopTime; - std::this_thread::sleep_until(frameStartTime + frameTime); - frameStopTime = steady_clock::now(); - } - - qCDebug(qLcScreenCaptureDxgi) << "ScreenGrabberActive finished"; + QFFmpegScreenCaptureThread::run(); } QVideoFrameFormat format() { @@ -219,14 +196,30 @@ public: return m_format; } + QVideoFrame grabFrame() override { + m_ctxMutex->lock(); + auto maybeTex = getNextFrame(m_device.get(), m_duplication.get()); + m_ctxMutex->unlock(); + + if (maybeTex) { + auto buffer = new QD3D11TextureVideoBuffer(m_device, m_ctxMutex, maybeTex.value(), m_frameSize); + return QVideoFrame(buffer, format()); + } else { + const auto status = maybeTex.error().isEmpty() ? QScreenCapture::NoError + : QScreenCapture::CaptureFailed; + updateError(status, maybeTex.error()); + } + return {}; + }; + private: - QPlatformScreenCapture &m_screenCapture; QWindowsIUPointer m_duplication; QWindowsIUPointer m_device; - qreal m_maxFrameRate; QWaitCondition m_waitForFormat; QVideoFrameFormat m_format; QMutex m_formatMutex; + std::shared_ptr m_ctxMutex; + QSize m_frameSize; }; static QMaybe> duplicateOutput(ID3D11Device* device, IDXGIOutput *output) @@ -291,94 +284,52 @@ static QMaybe> createD3D11Device(IDXGIAdapter1 * } QFFmpegScreenCaptureDxgi::QFFmpegScreenCaptureDxgi(QScreenCapture *screenCapture) - : QPlatformScreenCapture(screenCapture) -{ -} - -QFFmpegScreenCaptureDxgi::~QFFmpegScreenCaptureDxgi() + : QFFmpegScreenCaptureBase(screenCapture) { - resetGrabber(); } QVideoFrameFormat QFFmpegScreenCaptureDxgi::frameFormat() const { - if (m_active) - return m_active->format(); + if (m_grabber) + return m_grabber->format(); else return {}; } -void QFFmpegScreenCaptureDxgi::setScreen(QScreen *screen) +bool QFFmpegScreenCaptureDxgi::setActiveInternal(bool active) { - QScreen *oldScreen = m_screen; - if (oldScreen == screen) - return; + if (bool(m_grabber) == active) + return true; - bool active = bool(m_active); - if (active) - setActiveInternal(false); - - m_screen = screen; - if (active) - setActiveInternal(true); - - emit screenCapture()->screenChanged(screen); -} - -void QFFmpegScreenCaptureDxgi::setActiveInternal(bool active) -{ - if (bool(m_active) == active) - return; - - if (m_active) { - resetGrabber(); + if (m_grabber) { + m_grabber.reset(); + return true; } else { - QScreen *screen = m_screen ? m_screen : QGuiApplication::primaryScreen(); + QScreen *screen = this->screen() ? this->screen() : QGuiApplication::primaryScreen(); auto maybeDxgiScreen = findDxgiScreen(screen); if (!maybeDxgiScreen) { qCDebug(qLcScreenCaptureDxgi) << maybeDxgiScreen.error(); - emit updateError(QScreenCapture::NotFound, maybeDxgiScreen.error()); - return; + updateError(QScreenCapture::NotFound, maybeDxgiScreen.error()); + return false; } auto maybeDev = createD3D11Device(maybeDxgiScreen.value().adapter.get()); if (!maybeDev) { qCDebug(qLcScreenCaptureDxgi) << maybeDev.error(); - emit updateError(QScreenCapture::InternalError, maybeDev.error()); - return; + updateError(QScreenCapture::InternalError, maybeDev.error()); + return false; } auto maybeDupOutput = duplicateOutput(maybeDev.value().get(), maybeDxgiScreen.value().output.get()); if (!maybeDupOutput) { qCDebug(qLcScreenCaptureDxgi) << maybeDupOutput.error(); - emit updateError(QScreenCapture::InternalError, maybeDupOutput.error()); - return; + updateError(QScreenCapture::InternalError, maybeDupOutput.error()); + return false; } - qreal maxFrameRate = screen->refreshRate(); - m_active.reset(new DxgiScreenGrabberActive(*this, maybeDev.value(), maybeDupOutput.value(), maxFrameRate)); - m_active->start(); - } -} - -void QFFmpegScreenCaptureDxgi::setActive(bool active) -{ - if (bool(m_active) == active) - return; - - emit updateError(QScreenCapture::NoError, {}); - - setActiveInternal(active); - emit screenCapture()->activeChanged(active); -} - -void QFFmpegScreenCaptureDxgi::resetGrabber() -{ - if (m_active) { - m_active->requestInterruption(); - m_active->quit(); - m_active->wait(); - m_active.reset(); + m_grabber.reset(new Grabber(*this, screen, maybeDev.value(), maybeDupOutput.value())); + m_grabber->start(); + return true; } } diff --git a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h index 0bb5079b2..4c9c75a07 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h @@ -17,35 +17,26 @@ // #include "qvideoframeformat.h" +#include "qffmpegscreencapturebase_p.h" #include #include #include QT_BEGIN_NAMESPACE -class DxgiScreenGrabberActive; -class QFFmpegScreenCaptureDxgi : public QPlatformScreenCapture +class QFFmpegScreenCaptureDxgi : public QFFmpegScreenCaptureBase { public: - explicit QFFmpegScreenCaptureDxgi(QScreenCapture *screenCapture); - ~QFFmpegScreenCaptureDxgi(); + QFFmpegScreenCaptureDxgi(QScreenCapture *screenCapture); - void setActive(bool active) override; - bool isActive() const override { return bool(m_active); } QVideoFrameFormat frameFormat() const override; - void setScreen(QScreen *screen) override; - QScreen *screen() const override { return m_screen; } - private: - void setActiveInternal(bool active); - - void resetGrabber(); + bool setActiveInternal(bool active) override; private: - std::unique_ptr m_active; - QScreen *m_screen = nullptr; - QVideoFrameFormat m_format; + class Grabber; + std::unique_ptr m_grabber; }; QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/qffmpegscreencapturethread_p.h b/src/plugins/multimedia/ffmpeg/qffmpegscreencapturethread_p.h index 0715263dd..cb5dcb03f 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegscreencapturethread_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegscreencapturethread_p.h @@ -44,10 +44,6 @@ public: ~QFFmpegScreenCaptureThread() override; - void setFrameRate(qreal rate); - - qreal frameRate() const; - void stop(); template @@ -69,6 +65,10 @@ protected: virtual QVideoFrame grabFrame() = 0; protected: + void setFrameRate(qreal rate); + + qreal frameRate() const; + void updateTimerInterval(); private: -- cgit v1.2.1