diff options
author | Mikko Hallamaa <mikko.hallamaa@qt.io> | 2024-02-05 17:30:15 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2024-02-28 19:13:41 +0000 |
commit | eca23f9e967d281b555f6eddfd53fc1771d63ec8 (patch) | |
tree | 154abaa51491a00244d33399fcfc59af2f05c6e2 | |
parent | 85da55a3f636f69e90e1589425928ebfcf1d3153 (diff) |
eglfs: Implement screen capture for Qt Quick applications on EGLFS
Added a dedicated grabber for screen capture on a Qt Quick application
on the EGLFS platform, which calls QQuickWindow::grabWindow().
Task-number: QTBUG-121836
Fixes: QTBUG-121678
Pick-to: 6.6 6.5
Change-Id: Ia2e43e4f5de7357282c8eae91d5341f112d02155
Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
(cherry picked from commit 5d7920086eeb2ee0678e110251e2ec96bc2cdea4)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/plugins/multimedia/ffmpeg/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp | 108 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h | 4 |
3 files changed, 92 insertions, 21 deletions
diff --git a/src/plugins/multimedia/ffmpeg/CMakeLists.txt b/src/plugins/multimedia/ffmpeg/CMakeLists.txt index a8637f133..6132a68a2 100644 --- a/src/plugins/multimedia/ffmpeg/CMakeLists.txt +++ b/src/plugins/multimedia/ffmpeg/CMakeLists.txt @@ -204,6 +204,7 @@ qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_eglfs qeglfsscreencapture.cpp qeglfsscreencapture_p.h LIBRARIES Qt::OpenGLPrivate + Qt::Quick ) set_source_files_properties(qx11surfacecapture.cpp qx11capturablewindows.cpp # X headers diff --git a/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp index 2f9ad1add..6ff02d055 100644 --- a/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp +++ b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp @@ -10,6 +10,8 @@ #include <QtOpenGL/private/qopenglcompositor_p.h> +#include <QtQuick/qquickwindow.h> + QT_BEGIN_NAMESPACE class QEglfsScreenCapture::Grabber : public QFFmpegSurfaceCaptureGrabber @@ -22,13 +24,13 @@ public: connect(this, &Grabber::errorUpdated, &screenCapture, &QEglfsScreenCapture::updateError); // Limit frame rate to 30 fps for performance reasons, to be reviewed at the next optimization round setFrameRate(std::min(screen->refreshRate(), 30.0)); - start(); } ~Grabber() override { stop(); } QVideoFrameFormat format() { return m_format; } +protected: QVideoFrame grabFrame() override { QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); @@ -52,10 +54,46 @@ public: return QVideoFrame(new QImageVideoBuffer(std::move(img)), m_format); } -private: QVideoFrameFormat m_format; }; +class QEglfsScreenCapture::QuickGrabber : public Grabber +{ +public: + QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow) + : Grabber(screenCapture, screen), m_quickWindow(quickWindow) + { + Q_ASSERT(m_quickWindow); + } + +protected: + QVideoFrame grabFrame() override + { + if (!m_quickWindow) { + updateError(Error::InternalError, QLatin1String("Window deleted")); + return {}; + } + + QImage image = m_quickWindow->grabWindow(); + + if (image.isNull()) { + updateError(Error::InternalError, QLatin1String("Image invalid")); + return {}; + } + + if (!m_format.isValid()) { + m_format = { image.size(), + QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) }; + m_format.setFrameRate(frameRate()); + } + + return QVideoFrame(new QImageVideoBuffer(std::move(image)), m_format); + } + +private: + QPointer<QQuickWindow> m_quickWindow; +}; + QEglfsScreenCapture::QEglfsScreenCapture() : QPlatformSurfaceCapture(ScreenSource{}) { } QEglfsScreenCapture::~QEglfsScreenCapture() = default; @@ -72,30 +110,20 @@ bool QEglfsScreenCapture::setActiveInternal(bool active) if (m_grabber) m_grabber.reset(); - else { - auto screen = source<ScreenSource>(); - if (!checkScreenWithError(screen)) - return false; - - QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); - if (!compositor->context()) { - updateError(Error::CaptureFailed, QLatin1String("OpenGL context is not found")); - return false; - } - if (!compositor->targetWindow()) { - updateError(Error::CaptureFailed, QLatin1String("Target window is not set for OpenGL compositor")); - return false; - } + if (!active) + return true; - // TODO Add check to differentiate between uninitialized UI and QML - // If UI not started, wait and try again, and then give error if still not started. - // If QML, give not supported error for now. + m_grabber = createGrabber(); - m_grabber = std::make_unique<Grabber>(*this, screen); + if (!m_grabber) { + // TODO: This could mean that the UI is not started yet, so we should wait and try again, + // and then give error if still not started. Might not be possible here. + return false; } - return static_cast<bool>(m_grabber) == active; + m_grabber->start(); + return true; } bool QEglfsScreenCapture::isSupported() @@ -103,4 +131,42 @@ bool QEglfsScreenCapture::isSupported() return QGuiApplication::platformName() == QLatin1String("eglfs"); } +std::unique_ptr<QEglfsScreenCapture::Grabber> QEglfsScreenCapture::createGrabber() +{ + auto screen = source<ScreenSource>(); + if (!checkScreenWithError(screen)) + return nullptr; + + QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); + + if (compositor->context()) { + // Create OpenGL grabber + if (!compositor->targetWindow()) { + updateError(Error::CaptureFailed, + QLatin1String("Target window is not set for OpenGL compositor")); + return nullptr; + } + + return std::make_unique<Grabber>(*this, screen); + } + + // Check for QQuickWindow + auto windows = QGuiApplication::topLevelWindows(); + auto it = std::find_if(windows.begin(), windows.end(), [screen](QWindow *window) { + auto quickWindow = qobject_cast<QQuickWindow *>(window); + if (!quickWindow) + return false; + + return quickWindow->screen() == screen; + }); + + if (it != windows.end()) { + // Create grabber that calls QQuickWindow::grabWindow + return std::make_unique<QuickGrabber>(*this, screen, qobject_cast<QQuickWindow *>(*it)); + } + + updateError(Error::CaptureFailed, QLatin1String("No existing OpenGL context or QQuickWindow")); + return nullptr; +} + QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h index bbef78428..3bc92c234 100644 --- a/src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h +++ b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h @@ -37,6 +37,10 @@ private: private: class Grabber; + class QuickGrabber; + + std::unique_ptr<Grabber> createGrabber(); + std::unique_ptr<Grabber> m_grabber; }; |