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
13
14Q_DECLARE_JNI_CLASS(QtScreenGrabber,
15 "org/qtproject/qt/android/multimedia/QtScreenGrabber")
16Q_DECLARE_JNI_CLASS(QtScreenCaptureService,
17 "org/qtproject/qt/android/multimedia/QtScreenCaptureService")
18Q_DECLARE_JNI_CLASS(Size, "android/util/Size")
19
20
21namespace {
23constexpr int REQUEST_CODE_MEDIA_PROJECTION = 24680; // Arbitrary
24constexpr int RESULT_CANCEL = 0;
25constexpr int RESULT_OK = -1;
26}
27
28class QAndroidScreenCapture::Grabber : public QtAndroidPrivate::ActivityResultListener
29{
30public:
31 Grabber(QAndroidScreenCapture & screenCapture)
32 : m_activityRequestCode(REQUEST_CODE_MEDIA_PROJECTION + idCounter.fetchAndAddRelaxed(1))
33 , m_screenCapture(screenCapture)
34 {
35 using namespace QtJniTypes;
36 const auto sizeObj = QtScreenGrabber::callStaticMethod<Size>(
37 "getScreenCaptureSize", QtAndroidPrivate::activity());
38 const QSize size = QSize(sizeObj.callMethod<int>("getWidth"),
39 sizeObj.callMethod<int>("getHeight"));
40 m_format = QVideoFrameFormat(size, QVideoFrameFormat::Format_RGBA8888);
41
42 if (m_format.frameHeight() > 0 && m_format.frameWidth() > 0) {
43 QtAndroidPrivate::registerActivityResultListener(this);
44 m_jniGrabber = QtScreenGrabber(QtAndroidPrivate::activity(), m_activityRequestCode);
45 } else {
46 updateError(QStringLiteral("Invalid Screen size: %1x%2. Screen capture not started")
47 .arg(m_format.frameHeight())
48 .arg(m_format.frameWidth()));
49 }
50 }
51
52 bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override
53 {
54 if (requestCode != m_activityRequestCode || m_jniGrabber == nullptr)
55 return false;
56
57 if (resultCode == RESULT_OK) {
58 const QtJniTypes::Intent intent(data);
59 const bool screenCaptureServiceStarted = m_jniGrabber.callMethod<bool>(
60 "startScreenCaptureService",
61 resultCode,
62 reinterpret_cast<jlong>(&m_screenCapture),
63 m_format.frameWidth(),
64 m_format.frameHeight(),
65 intent);
66 if (!screenCaptureServiceStarted)
67 updateError(QStringLiteral("Cannot start screen capture service"));
68 } else if (resultCode == RESULT_CANCEL) {
69 updateError(QStringLiteral("Screen capture canceled"));
70 }
71 return true;
72 }
73
74 ~Grabber()
75 {
76 QtAndroidPrivate::unregisterActivityResultListener(this);
77 m_jniGrabber.callMethod<bool>("stopScreenCaptureService");
78 }
79
80 QVideoFrameFormat format() const { return m_format; }
81
82private:
83 void updateError(const QString &errorString)
84 {
85 QMetaObject::invokeMethod(&m_screenCapture,
86 &QPlatformSurfaceCapture::updateError,
87 Qt::QueuedConnection,
88 QPlatformSurfaceCapture::InternalError,
89 errorString);
90 }
91
92 QtJniTypes::QtScreenGrabber m_jniGrabber;
93 const int m_activityRequestCode;
94 QAndroidScreenCapture & m_screenCapture;
95 QVideoFrameFormat m_format;
96};
97
98QAndroidScreenCapture::QAndroidScreenCapture()
99 : QPlatformSurfaceCapture(ScreenSource{})
100{
101}
102
103QAndroidScreenCapture::~QAndroidScreenCapture()
104{
105}
106
107QVideoFrameFormat QAndroidScreenCapture::frameFormat() const
108{
109 return m_grabber ? m_grabber->format() : QVideoFrameFormat();
110}
111
112bool QAndroidScreenCapture::setActiveInternal(bool active)
113{
114 if (active == static_cast<bool>(m_grabber))
115 return true;
116
117 if (m_grabber) {
118 m_grabber.reset();
119 m_frameFactory.reset();
120 } else {
121 m_grabber = std::make_unique<Grabber>(*this);
122 m_frameFactory = QAndroidVideoFrameFactory::create();
123 }
124
125 return static_cast<bool>(m_grabber) == active;
126}
127
128void QAndroidScreenCapture::onNewFrameReceived(QtJniTypes::Image image)
129{
130 if (!isActive() || m_frameFactory == nullptr) {
131 if (image.isValid())
132 image.callMethod<void>("close");
133 return;
134 }
135
136 QVideoFrame videoFrame = m_frameFactory->createVideoFrame(image);
137 if (videoFrame.isValid())
138 emit newVideoFrame(videoFrame);
139}
140
141static void onScreenFrameAvailable(JNIEnv *env, jobject obj, QtJniTypes::Image image, jlong id)
142{
143 Q_UNUSED(env);
144 Q_UNUSED(obj);
145 auto cppObj = reinterpret_cast<QAndroidScreenCapture*>(id);
146 cppObj->onNewFrameReceived(image);
147}
148Q_DECLARE_JNI_NATIVE_METHOD(onScreenFrameAvailable)
149
150static void onErrorUpdate(JNIEnv *env, jobject obj, QString errorString, jlong id)
151{
152 Q_UNUSED(env);
153 Q_UNUSED(obj);
154 auto cppObj = reinterpret_cast<QAndroidScreenCapture*>(id);
155 QMetaObject::invokeMethod(cppObj,
156 &QPlatformSurfaceCapture::updateError,
157 Qt::QueuedConnection,
158 QPlatformSurfaceCapture::InternalError,
159 errorString);
160}
161Q_DECLARE_JNI_NATIVE_METHOD(onErrorUpdate)
162
163
164bool QAndroidScreenCapture::registerNativeMethods()
165{
166 using namespace QtJniTypes;
167 static const bool registered = []() {
168 return QtScreenCaptureService::registerNativeMethods(
169 { Q_JNI_NATIVE_METHOD(onScreenFrameAvailable),
170 Q_JNI_NATIVE_METHOD(onErrorUpdate)});
171 }();
172 return registered;
173}
174
175QT_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")