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

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