summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMikko Hallamaa <mikko.hallamaa@qt.io>2023-12-07 18:17:26 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-12-14 22:37:07 +0000
commitcc4dd87764b4c9bd897138f891f42063cc218a75 (patch)
treea59f62d2355c1551be14ecc39c302782f3b98241 /src
parent312c718a8b7c65e16a266a71d8d39efd8ff1a589 (diff)
Use composition to run screen capture on main thread if needed
EGLFS screen capture on embedded Linux platform crashes because of calling QScreen::grabWindow on a separate thread. Need to refactor FFmpeg surface capture to allow choosing whether to run on a separate thread or not. Pick-to: 6.6 6.5 Task-number: QTBUG-117746 Task-number: QTBUG-117878 Change-Id: I30629a3806220f02ca2cc3dc8c2749bd337914de Reviewed-by: Artem Dyomin <artem.dyomin@qt.io> (cherry picked from commit e5d614f599a25e9a7280d16f6a6fe18f73120c56) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp27
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread.cpp143
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread_p.h23
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp16
-rw-r--r--src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp2
5 files changed, 139 insertions, 72 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp
index 06c83f78f..e29c2ab03 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp
@@ -21,6 +21,8 @@
#include <thread>
#include <chrono>
+#include <mutex> // std::scoped_lock
+
QT_BEGIN_NAMESPACE
static Q_LOGGING_CATEGORY(qLcScreenCaptureDxgi, "qt.multimedia.ffmpeg.screencapturedxgi")
@@ -367,18 +369,6 @@ public:
stop();
}
- void run() override
- {
- m_duplication = DxgiDuplication();
- const ComStatus status = m_duplication.initialize(m_screen);
- if (!status) {
- updateError(CaptureFailed, status.str());
- return;
- }
-
- QFFmpegSurfaceCaptureThread::run();
- }
-
QVideoFrameFormat format() {
return m_format;
}
@@ -425,6 +415,19 @@ public:
return frame;
}
+ protected:
+ void initializeGrabbingContext() override
+ {
+ m_duplication = DxgiDuplication();
+ const ComStatus status = m_duplication.initialize(m_screen);
+ if (!status) {
+ updateError(CaptureFailed, status.str());
+ return;
+ }
+
+ QFFmpegSurfaceCaptureThread::initializeGrabbingContext();
+ }
+
private:
const QScreen *m_screen = nullptr;
QVideoFrameFormat m_format;
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread.cpp b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread.cpp
index d503d9af2..b53d393ee 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread.cpp
@@ -3,8 +3,10 @@
#include "qffmpegsurfacecapturethread_p.h"
-#include <qtimer.h>
+#include <qelapsedtimer.h>
#include <qloggingcategory.h>
+#include <qthread.h>
+#include <qtimer.h>
QT_BEGIN_NAMESPACE
@@ -48,9 +50,53 @@ private:
} // namespace
-QFFmpegSurfaceCaptureThread::QFFmpegSurfaceCaptureThread()
+struct QFFmpegSurfaceCaptureThread::GrabbingContext
+{
+ GrabbingProfiler profiler;
+ QTimer timer;
+ QElapsedTimer elapsedTimer;
+ qint64 lastFrameTime = 0;
+};
+
+class QFFmpegSurfaceCaptureThread::GrabbingThread : public QThread
+{
+public:
+ GrabbingThread(QFFmpegSurfaceCaptureThread& grabber)
+ : m_grabber(grabber)
+ {}
+
+protected:
+ void run() override
+ {
+ m_grabber.initializeGrabbingContext();
+
+ if (!m_grabber.isGrabbingContextInitialized())
+ return;
+
+ exec();
+ m_grabber.finalizeGrabbingContext();
+ }
+
+private:
+ QFFmpegSurfaceCaptureThread& m_grabber;
+};
+
+QFFmpegSurfaceCaptureThread::QFFmpegSurfaceCaptureThread(bool runInThread)
{
setFrameRate(DefaultScreenCaptureFrameRate);
+
+ if (!runInThread)
+ return;
+
+ m_thread = std::make_unique<GrabbingThread>(*this);
+}
+
+void QFFmpegSurfaceCaptureThread::start()
+{
+ if (m_thread)
+ m_thread->start();
+ else if (!isGrabbingContextInitialized())
+ initializeGrabbingContext();
}
QFFmpegSurfaceCaptureThread::~QFFmpegSurfaceCaptureThread() = default;
@@ -72,36 +118,60 @@ qreal QFFmpegSurfaceCaptureThread::frameRate() const
void QFFmpegSurfaceCaptureThread::stop()
{
- quit();
- wait();
+ if (m_thread)
+ {
+ m_thread->quit();
+ m_thread->wait();
+ }
+ else if (isGrabbingContextInitialized())
+ {
+ finalizeGrabbingContext();
+ }
}
-void QFFmpegSurfaceCaptureThread::run()
+void QFFmpegSurfaceCaptureThread::updateError(QPlatformSurfaceCapture::Error error,
+ const QString &description)
{
- qCDebug(qLcScreenCaptureThread) << "start screen capture thread";
+ const auto prevError = std::exchange(m_prevError, error);
+
+ if (error != QPlatformSurfaceCapture::NoError
+ || prevError != QPlatformSurfaceCapture::NoError) {
+ emit errorUpdated(error, description);
+ }
- m_timer = std::make_unique<QTimer>();
- // should be deleted in this thread
- auto deleter = qScopeGuard([this]() { m_timer.reset(); });
- m_timer->setTimerType(Qt::PreciseTimer);
updateTimerInterval();
+}
- QElapsedTimer elapsedTimer;
- elapsedTimer.start();
+void QFFmpegSurfaceCaptureThread::updateTimerInterval()
+{
+ const qreal rate = m_prevError && *m_prevError != QPlatformSurfaceCapture::NoError
+ ? MinScreenCaptureFrameRate
+ : m_rate;
+ const int interval = static_cast<int>(1000 / rate);
+ if (m_context && m_context->timer.interval() != interval)
+ m_context->timer.setInterval(interval);
+}
- qint64 lastFrameTime = 0;
+void QFFmpegSurfaceCaptureThread::initializeGrabbingContext()
+{
+ Q_ASSERT(!isGrabbingContextInitialized());
+ qCDebug(qLcScreenCaptureThread) << "screen capture started";
- GrabbingProfiler profiler;
+ m_context = std::make_unique<GrabbingContext>();
+ m_context->timer.setTimerType(Qt::PreciseTimer);
+ updateTimerInterval();
+
+ m_context->elapsedTimer.start();
- auto doGrab = [&]() {
- auto measure = profiler.measure();
+ auto doGrab = [this]() {
+ auto measure = m_context->profiler.measure();
auto frame = grabFrame();
if (frame.isValid()) {
- frame.setStartTime(lastFrameTime);
- frame.setEndTime(elapsedTimer.nsecsElapsed() / 1000);
- lastFrameTime = frame.endTime();
+ frame.setStartTime(m_context->lastFrameTime);
+ frame.setEndTime(m_context->elapsedTimer.nsecsElapsed() / 1000);
+ m_context->lastFrameTime = frame.endTime();
updateError(QPlatformSurfaceCapture::NoError);
@@ -111,37 +181,22 @@ void QFFmpegSurfaceCaptureThread::run()
doGrab();
- m_timer->callOnTimeout(m_timer.get(), doGrab);
- m_timer->start();
-
- exec();
-
- qCDebug(qLcScreenCaptureThread)
- << "end screen capture thread; avg grabbing time:" << profiler.avgTime()
- << "ms, grabbings number:" << profiler.number();
+ m_context->timer.callOnTimeout(&m_context->timer, doGrab);
+ m_context->timer.start();
}
-void QFFmpegSurfaceCaptureThread::updateError(QPlatformSurfaceCapture::Error error,
- const QString &description)
+void QFFmpegSurfaceCaptureThread::finalizeGrabbingContext()
{
- const auto prevError = std::exchange(m_prevError, error);
-
- if (error != QPlatformSurfaceCapture::NoError
- || prevError != QPlatformSurfaceCapture::NoError) {
- emit errorUpdated(error, description);
- }
-
- updateTimerInterval();
+ Q_ASSERT(isGrabbingContextInitialized());
+ qCDebug(qLcScreenCaptureThread)
+ << "end screen capture thread; avg grabbing time:" << m_context->profiler.avgTime()
+ << "ms, grabbings number:" << m_context->profiler.number();
+ m_context.reset();
}
-void QFFmpegSurfaceCaptureThread::updateTimerInterval()
+bool QFFmpegSurfaceCaptureThread::isGrabbingContextInitialized() const
{
- const qreal rate = m_prevError && *m_prevError != QPlatformSurfaceCapture::NoError
- ? MinScreenCaptureFrameRate
- : m_rate;
- const int interval = static_cast<int>(1000 / rate);
- if (m_timer && m_timer->interval() != interval)
- m_timer->setInterval(interval);
+ return m_context != nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread_p.h b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread_p.h
index e64e261bd..9c8fb91f5 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturethread_p.h
@@ -15,8 +15,6 @@
// We mean it.
//
-#include <qthread.h>
-
#include "qvideoframe.h"
#include "private/qplatformsurfacecapture_p.h"
@@ -25,7 +23,7 @@
QT_BEGIN_NAMESPACE
-class QTimer;
+class QThread;
static constexpr qreal DefaultScreenCaptureFrameRate = 60.;
@@ -36,14 +34,15 @@ static constexpr qreal DefaultScreenCaptureFrameRate = 60.;
static constexpr qreal MaxScreenCaptureFrameRate = 60.;
static constexpr qreal MinScreenCaptureFrameRate = 1.;
-class QFFmpegSurfaceCaptureThread : public QThread
+class QFFmpegSurfaceCaptureThread : public QObject
{
Q_OBJECT
public:
- QFFmpegSurfaceCaptureThread();
+ QFFmpegSurfaceCaptureThread(bool runInThread = true);
~QFFmpegSurfaceCaptureThread() override;
+ void start();
void stop();
template<typename Object, typename Method>
@@ -58,23 +57,29 @@ signals:
void errorUpdated(QPlatformSurfaceCapture::Error error, const QString &description);
protected:
- void run() override;
-
void updateError(QPlatformSurfaceCapture::Error error, const QString &description = {});
virtual QVideoFrame grabFrame() = 0;
-protected:
void setFrameRate(qreal rate);
qreal frameRate() const;
void updateTimerInterval();
+ virtual void initializeGrabbingContext();
+ virtual void finalizeGrabbingContext();
+
+ bool isGrabbingContextInitialized() const;
+
private:
+ struct GrabbingContext;
+ class GrabbingThread;
+
+ std::unique_ptr<GrabbingContext> m_context;
qreal m_rate = 0;
- std::unique_ptr<QTimer> m_timer;
std::optional<QPlatformSurfaceCapture::Error> m_prevError;
+ std::unique_ptr<QThread> m_thread;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp
index b2d62306e..a5f1c76b2 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp
@@ -30,6 +30,7 @@ auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout);
#include "qvideoframe.h"
#include <qwindow.h>
+#include <qthread.h>
#include <qloggingcategory.h>
#include <qguiapplication.h>
#include <private/qmultimediautils_p.h>
@@ -280,6 +281,7 @@ private:
return texture.as<IDXGISurface>();
}
+ MultithreadedApartment m_comApartment{};
HWND m_captureWindow{};
winrt::Windows::Graphics::SizeInt32 m_frameSize{};
com_ptr<ID3D11Device> m_device;
@@ -317,19 +319,15 @@ public:
protected:
- void run() override
+ void initializeGrabbingContext() override
{
if (!m_adapter || !IsWindow(m_hwnd))
return; // Error already logged
try {
- MultithreadedApartment comApartment;
-
m_windowGrabber = std::make_unique<WindowGrabber>(m_adapter.get(), m_hwnd);
- QFFmpegSurfaceCaptureThread::run();
-
- m_windowGrabber = nullptr;
+ QFFmpegSurfaceCaptureThread::initializeGrabbingContext();
} catch (const winrt::hresult_error &err) {
const QString message = QLatin1String("Unable to capture window: ")
@@ -339,6 +337,12 @@ protected:
}
}
+ void finalizeGrabbingContext() override
+ {
+ QFFmpegSurfaceCaptureThread::finalizeGrabbingContext();
+ m_windowGrabber = nullptr;
+ }
+
QVideoFrame grabFrame() override
{
try {
diff --git a/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp b/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp
index 4e49f0f7d..034dc5813 100644
--- a/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp
+++ b/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp
@@ -91,7 +91,7 @@ public:
private:
Grabber(QGrabWindowSurfaceCapture &capture, QScreen *screen, WindowUPtr window)
- : m_capture(capture), m_screen(screen), m_window(std::move(window))
+ : QFFmpegSurfaceCaptureThread(QGuiApplication::platformName() != QLatin1String("eglfs")), m_capture(capture), m_screen(screen), m_window(std::move(window))
{
connect(qApp, &QGuiApplication::screenRemoved, this, &Grabber::onScreenRemoved);
addFrameCallback(m_capture, &QGrabWindowSurfaceCapture::newVideoFrame);