summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJøger Hansegård <joger.hansegard@qt.io>2023-05-09 12:44:57 +0200
committerJøger Hansegård <joger.hansegard@qt.io>2023-05-11 14:41:03 +0200
commit0a6f667a5476029ae7dd3544f8750e13678cbf29 (patch)
tree59f5f9d2833894e23beaf4bab82789dfc95a0682
parent54464a09a49593ce70065d8481bb32f499348728 (diff)
downloadqtmultimedia-0a6f667a5476029ae7dd3544f8750e13678cbf29.tar.gz
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 <lars@knoll.priv.no> Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp163
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h21
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegscreencapturethread_p.h8
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 <private/qabstractvideobuffer_p.h>
#include <private/qmultimediautils_p.h>
#include <private/qwindowsmultimediautils_p.h>
@@ -152,64 +153,40 @@ static QMaybe<QWindowsIUPointer<ID3D11Texture2D>> getNextFrame(ID3D11Device *dev
return texCopy;
}
-class DxgiScreenGrabberActive : public QThread
+class QFFmpegScreenCaptureDxgi::Grabber : public QFFmpegScreenCaptureThread
{
public:
- DxgiScreenGrabberActive(QPlatformScreenCapture &screenCapture, QWindowsIUPointer<ID3D11Device> &device,
- QWindowsIUPointer<IDXGIOutputDuplication> &duplication, qreal maxFrameRate)
- : QThread()
- , m_screenCapture(screenCapture)
+ Grabber(QFFmpegScreenCaptureDxgi &screenCapture, QScreen *screen, QWindowsIUPointer<ID3D11Device> &device,
+ QWindowsIUPointer<IDXGIOutputDuplication> &duplication)
+ : QFFmpegScreenCaptureThread()
, m_duplication(duplication)
, m_device(device)
- , m_maxFrameRate(maxFrameRate)
- {}
-
- void run() override
+ , m_ctxMutex(std::make_shared<QMutex>())
{
- // 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<QMutex> 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<microseconds>(frameStartTime.time_since_epoch()).count());
- frame.setEndTime(duration_cast<microseconds>(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<IDXGIOutputDuplication> m_duplication;
QWindowsIUPointer<ID3D11Device> m_device;
- qreal m_maxFrameRate;
QWaitCondition m_waitForFormat;
QVideoFrameFormat m_format;
QMutex m_formatMutex;
+ std::shared_ptr<QMutex> m_ctxMutex;
+ QSize m_frameSize;
};
static QMaybe<QWindowsIUPointer<IDXGIOutputDuplication>> duplicateOutput(ID3D11Device* device, IDXGIOutput *output)
@@ -291,94 +284,52 @@ static QMaybe<QWindowsIUPointer<ID3D11Device>> 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 <private/qwindowsiupointer_p.h>
#include <private/qplatformscreencapture_p.h>
#include <memory>
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<DxgiScreenGrabberActive> m_active;
- QScreen *m_screen = nullptr;
- QVideoFrameFormat m_format;
+ class Grabber;
+ std::unique_ptr<Grabber> 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<typename Object, typename Method>
@@ -69,6 +65,10 @@ protected:
virtual QVideoFrame grabFrame() = 0;
protected:
+ void setFrameRate(qreal rate);
+
+ qreal frameRate() const;
+
void updateTimerInterval();
private: