1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qeglfsscreencapture_p.h"
5
6#include "qffmpegsurfacecapturegrabber_p.h"
7#include "qguiapplication.h"
8#include "qopenglvideobuffer_p.h"
9#include "private/qimagevideobuffer_p.h"
10#include "private/qvideoframe_p.h"
11
12#include <QtOpenGL/private/qopenglcompositor_p.h>
13#include <QtOpenGL/private/qopenglframebufferobject_p.h>
14
15#include <QtQuick/qquickwindow.h>
16
17QT_BEGIN_NAMESPACE
18
19class QEglfsScreenCapture::Grabber : public QFFmpegSurfaceCaptureGrabber
20{
21public:
22 Grabber(QEglfsScreenCapture &screenCapture, QScreen *screen)
23 : QFFmpegSurfaceCaptureGrabber(QFFmpegSurfaceCaptureGrabber::UseCurrentThread)
24 {
25 addFrameCallback(object&: screenCapture, method: &QEglfsScreenCapture::newVideoFrame);
26 connect(sender: this, signal: &Grabber::errorUpdated, context: &screenCapture, slot: &QEglfsScreenCapture::updateError);
27 // Limit frame rate to 30 fps for performance reasons,
28 // to be reviewed at the next optimization round
29 setFrameRate(std::min(a: screen->refreshRate(), b: qreal(30.0)));
30 }
31
32 ~Grabber() override { stop(); }
33
34 QVideoFrameFormat format() { return m_format; }
35
36protected:
37 QVideoFrame grabFrame() override
38 {
39 auto nativeSize = QOpenGLCompositor::instance()->nativeTargetGeometry().size();
40 auto fbo = std::make_unique<QOpenGLFramebufferObject>(args&: nativeSize);
41
42 if (!QOpenGLCompositor::instance()->grabToFrameBufferObject(
43 fbo: fbo.get(), orientation: QOpenGLCompositor::NotFlipped)) {
44 updateError(error: Error::InternalError, description: QLatin1String("Couldn't grab to framebuffer object"));
45 return {};
46 }
47
48 if (!fbo->isValid()) {
49 updateError(error: Error::InternalError, description: QLatin1String("Framebuffer object invalid"));
50 return {};
51 }
52
53 auto videoBuffer = std::make_unique<QOpenGLVideoBuffer>(args: std::move(fbo));
54
55 if (!m_format.isValid()) {
56 auto image = videoBuffer->ensureImageBuffer().underlyingImage();
57 m_format = { image.size(), QVideoFrameFormat::pixelFormatFromImageFormat(format: image.format()) };
58 m_format.setStreamFrameRate(frameRate());
59 }
60
61 return QVideoFramePrivate::createFrame(buffer: std::move(videoBuffer), format: m_format);
62 }
63
64 QVideoFrameFormat m_format;
65};
66
67class QEglfsScreenCapture::QuickGrabber : public Grabber
68{
69public:
70 QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow)
71 : Grabber(screenCapture, screen), m_quickWindow(quickWindow)
72 {
73 Q_ASSERT(m_quickWindow);
74 }
75
76protected:
77 QVideoFrame grabFrame() override
78 {
79 if (!m_quickWindow) {
80 updateError(error: Error::InternalError, description: QLatin1String("Window deleted"));
81 return {};
82 }
83
84 QImage image = m_quickWindow->grabWindow();
85
86 if (image.isNull()) {
87 updateError(error: Error::InternalError, description: QLatin1String("Image invalid"));
88 return {};
89 }
90
91 if (!m_format.isValid()) {
92 m_format = { image.size(),
93 QVideoFrameFormat::pixelFormatFromImageFormat(format: image.format()) };
94 m_format.setStreamFrameRate(frameRate());
95 }
96
97 return QVideoFramePrivate::createFrame(
98 buffer: std::make_unique<QImageVideoBuffer>(args: std::move(image)), format: m_format);
99 }
100
101private:
102 QPointer<QQuickWindow> m_quickWindow;
103};
104
105QEglfsScreenCapture::QEglfsScreenCapture() : QPlatformSurfaceCapture(ScreenSource{}) { }
106
107QEglfsScreenCapture::~QEglfsScreenCapture() = default;
108
109QVideoFrameFormat QEglfsScreenCapture::frameFormat() const
110{
111 return m_grabber ? m_grabber->format() : QVideoFrameFormat();
112}
113
114bool QEglfsScreenCapture::setActiveInternal(bool active)
115{
116 if (static_cast<bool>(m_grabber) == active)
117 return true;
118
119 if (m_grabber)
120 m_grabber.reset();
121
122 if (!active)
123 return true;
124
125 m_grabber = createGrabber();
126
127 if (!m_grabber) {
128 // TODO: This could mean that the UI is not started yet, so we should wait and try again,
129 // and then give error if still not started. Might not be possible here.
130 return false;
131 }
132
133 m_grabber->start();
134 return true;
135}
136
137bool QEglfsScreenCapture::isSupported()
138{
139 return QGuiApplication::platformName() == QLatin1String("eglfs");
140}
141
142std::unique_ptr<QEglfsScreenCapture::Grabber> QEglfsScreenCapture::createGrabber()
143{
144 auto screen = source<ScreenSource>();
145 if (!checkScreenWithError(screen))
146 return nullptr;
147
148 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
149
150 if (compositor->context()) {
151 // Create OpenGL grabber
152 if (!compositor->targetWindow()) {
153 updateError(error: Error::CaptureFailed,
154 errorString: QLatin1String("Target window is not set for OpenGL compositor"));
155 return nullptr;
156 }
157
158 return std::make_unique<Grabber>(args&: *this, args&: screen);
159 }
160
161 // Check for QQuickWindow
162 auto windows = QGuiApplication::topLevelWindows();
163 auto it = std::find_if(first: windows.begin(), last: windows.end(), pred: [screen](QWindow *window) {
164 auto quickWindow = qobject_cast<QQuickWindow *>(object: window);
165 if (!quickWindow)
166 return false;
167
168 return quickWindow->screen() == screen;
169 });
170
171 if (it != windows.end()) {
172 // Create grabber that calls QQuickWindow::grabWindow
173 return std::make_unique<QuickGrabber>(args&: *this, args&: screen, args: qobject_cast<QQuickWindow *>(object: *it));
174 }
175
176 updateError(error: Error::CaptureFailed, errorString: QLatin1String("No existing OpenGL context or QQuickWindow"));
177 return nullptr;
178}
179
180QT_END_NAMESPACE
181

source code of qtmultimedia/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp