Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidscreencapture.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 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 <QtFFmpegMediaPluginImpl/private/qandroidscreencapture_p.h>
5
6#include <QtCore/private/qjnihelpers_p.h>
7#include <QtCore/qreadwritelock.h>
8
9#include <QtFFmpegMediaPluginImpl/private/qandroidvideoframefactory_p.h>
10#include <QtFFmpegMediaPluginImpl/private/qffmpegvideobuffer_p.h>
11#include <QtFFmpegMediaPluginImpl/private/qffmpegsurfacecapturegrabber_p.h>
12
14
15Q_DECLARE_JNI_CLASS(QtScreenGrabber,
16 "org/qtproject/qt/android/multimedia/qffmpeg/QtScreenGrabber")
17Q_DECLARE_JNI_CLASS(QtScreenCaptureService,
18 "org/qtproject/qt/android/multimedia/qffmpeg/QtScreenCaptureService")
19Q_DECLARE_JNI_CLASS(Size, "android/util/Size")
20
21
22namespace {
24constexpr int REQUEST_CODE_MEDIA_PROJECTION = 24680; // Arbitrary
25constexpr int RESULT_CANCEL = 0;
26constexpr int RESULT_OK = -1;
27
28}
29
30class QAndroidScreenCapture::Grabber : public QtAndroidPrivate::ActivityResultListener,
31 public QFFmpegSurfaceCaptureGrabber
32{
33public:
34 Grabber(QAndroidScreenCapture & screenCapture)
35 : m_activityRequestCode(REQUEST_CODE_MEDIA_PROJECTION + idCounter.fetchAndAddRelaxed(1))
36 , m_screenCapture(screenCapture)
37 {
38 injectContextToGrabbingThread(&m_resourceContext);
39
40 addFrameCallback(&screenCapture, &QAndroidScreenCapture::newVideoFrame);
41
42 using namespace QtJniTypes;
43 const auto sizeObj = QtScreenGrabber::callStaticMethod<Size>(
44 "getScreenCaptureSize", QtAndroidPrivate::activity());
45 const QSize size = QSize(sizeObj.callMethod<int>("getWidth"),
46 sizeObj.callMethod<int>("getHeight"));
47 m_format = QVideoFrameFormat(size, QVideoFrameFormat::Format_RGBA8888);
48
49 if (m_format.frameHeight() > 0 && m_format.frameWidth() > 0) {
50 QtAndroidPrivate::registerActivityResultListener(this);
51 m_jniGrabber = QtScreenGrabber(QtAndroidPrivate::activity(), m_activityRequestCode);
52 } else {
53 updateError(QStringLiteral("Invalid Screen size: %1x%2. Screen capture not started")
54 .arg(m_format.frameHeight())
55 .arg(m_format.frameWidth()));
56 }
57
58 setFrameRate(screenCapture.frameRate().value_or(DefaultScreenCaptureFrameRate));
59 m_format.setStreamFrameRate(frameRate());
60 }
61
62 QVideoFrame grabFrame() override
63 {
64 return m_resourceContext.latestFrame();
65 }
66
67 bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override
68 {
69 if (requestCode != m_activityRequestCode || m_jniGrabber == nullptr)
70 return false;
71
72 if (resultCode == RESULT_OK) {
73 const QtJniTypes::Intent intent(data);
74 const bool screenCaptureServiceStarted = m_jniGrabber.callMethod<bool>(
75 "startScreenCaptureService",
76 resultCode,
77 reinterpret_cast<jlong>(&m_screenCapture),
78 m_format.frameWidth(),
79 m_format.frameHeight(),
80 intent);
81 if (!screenCaptureServiceStarted)
82 updateError(QStringLiteral("Cannot start screen capture service"));
83 } else if (resultCode == RESULT_CANCEL) {
84 updateError(QStringLiteral("Screen capture canceled"));
85 }
86 return true;
87 }
88
89 ~Grabber() override
90 {
91 stop();
92 QtAndroidPrivate::unregisterActivityResultListener(this);
93 m_jniGrabber.callMethod<bool>("stopScreenCaptureService");
94 }
95
96 QVideoFrameFormat format() const { return m_format; }
97
98 void onNewFrameReceived(QtJniTypes::Image image)
99 {
100 QMetaObject::invokeMethod(&m_resourceContext, &ResourceContext::updateLatestImageRef,
101 image);
102 }
103
104private:
105 void updateError(const QString &errorString)
106 {
107 QMetaObject::invokeMethod(&m_screenCapture,
108 &QPlatformSurfaceCapture::updateError,
109 Qt::QueuedConnection,
110 QPlatformSurfaceCapture::Error::InternalError,
111 errorString);
112 }
113
114 QtJniTypes::QtScreenGrabber m_jniGrabber;
115 const int m_activityRequestCode;
116 QAndroidScreenCapture & m_screenCapture;
117 QVideoFrameFormat m_format;
118
119 class ResourceContext : public QObject
120 {
121 public:
122 ResourceContext() : m_frameFactory(QAndroidVideoFrameFactory::create()) { }
123 ~ResourceContext() override { updateLatestImageRef({ }); }
124 Q_DISABLE_COPY_MOVE(ResourceContext)
125
126 void updateLatestImageRef(QJniObject newImage)
127 {
128 auto oldImage = std::exchange(m_latestImage, newImage);
129 if (oldImage.isValid())
130 oldImage.callMethod<void>("close");
131 }
132
133 QVideoFrame latestFrame()
134 {
135 Q_ASSERT(m_frameFactory);
136 return m_latestImage.isValid()
137 ? m_frameFactory->createVideoFrame(std::move(m_latestImage))
138 : QVideoFrame();
139 }
140
141 private:
142 QJniObject m_latestImage;
143 std::shared_ptr<QAndroidVideoFrameFactory> m_frameFactory;
144 } m_resourceContext;
145};
146
147QAndroidScreenCapture::QAndroidScreenCapture()
148 : QPlatformSurfaceCapture(ScreenSource{})
149{
150}
151
152QAndroidScreenCapture::~QAndroidScreenCapture()
153{
154}
155
156QVideoFrameFormat QAndroidScreenCapture::frameFormat() const
157{
158 return m_grabber ? m_grabber->format() : QVideoFrameFormat();
159}
160
161bool QAndroidScreenCapture::setActiveInternal(bool active)
162{
163 if (active == static_cast<bool>(m_grabber))
164 return true;
165
166 if (m_grabber) {
167 m_grabber.reset();
168 } else {
169 m_grabber = std::make_unique<Grabber>(*this);
170 m_grabber->start();
171 }
172
173 return bool(m_grabber) == active;
174}
175
176void QAndroidScreenCapture::onNewFrameReceived(QtJniTypes::Image image)
177{
178 if (m_grabber)
179 m_grabber->onNewFrameReceived(image);
180 else if (image.isValid())
181 image.callMethod<void>("close");
182}
183
184static void onScreenFrameAvailable(JNIEnv *env, jobject obj, QtJniTypes::Image image, jlong id)
185{
186 Q_UNUSED(env);
187 Q_UNUSED(obj);
188 auto *cppObj = reinterpret_cast<QAndroidScreenCapture*>(id);
189 cppObj->onNewFrameReceived(image);
190}
191Q_DECLARE_JNI_NATIVE_METHOD(onScreenFrameAvailable)
192
193static void onErrorUpdate(JNIEnv *env, jobject obj, QString errorString, jlong id)
194{
195 Q_UNUSED(env);
196 Q_UNUSED(obj);
197 auto cppObj = reinterpret_cast<QAndroidScreenCapture*>(id);
198 QMetaObject::invokeMethod(cppObj,
199 &QPlatformSurfaceCapture::updateError,
200 Qt::QueuedConnection,
201 QPlatformSurfaceCapture::Error::InternalError,
202 errorString);
203}
204Q_DECLARE_JNI_NATIVE_METHOD(onErrorUpdate)
205
206
207bool QAndroidScreenCapture::registerNativeMethods()
208{
209 using namespace QtJniTypes;
210 static const bool registered = []() {
211 return QtScreenCaptureService::registerNativeMethods(
212 { Q_JNI_NATIVE_METHOD(onScreenFrameAvailable),
213 Q_JNI_NATIVE_METHOD(onErrorUpdate)});
214 }();
215 return registered;
216}
217
218QT_END_NAMESPACE
constexpr int REQUEST_CODE_MEDIA_PROJECTION
QAtomicInteger< int > idCounter
static void onScreenFrameAvailable(JNIEnv *env, jobject obj, QtJniTypes::Image image, jlong id)
static void onErrorUpdate(JNIEnv *env, jobject obj, QString errorString, jlong id)
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")