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
androidmediarecorder.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
10
11#include <qmap.h>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/qlogging.h>
14#include <QtCore/qurl.h>
15
17
18Q_STATIC_LOGGING_CATEGORY(lcMediaRecorder, "qt.multimedia.mediarecorder.android");
19
20Q_DECLARE_JNI_CLASS(QtAudioDeviceManager,
21 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager");
22Q_DECLARE_JNI_CLASS(AudioDeviceInfo, "android/media/AudioDeviceInfo");
23
26
28{
29 return QStringLiteral("%1-%2");
30}
31
32bool AndroidCamcorderProfile::hasProfile(jint cameraId, Quality quality)
33{
34 if (g_camcorderProfiles->contains(profileKey().arg(cameraId).arg(quality)))
35 return true;
36
37 return QJniObject::callStaticMethod<jboolean>("android/media/CamcorderProfile",
38 "hasProfile",
39 "(II)Z",
40 cameraId,
41 quality);
42}
43
45{
46 const QString key = profileKey().arg(cameraId).arg(quality);
47 QMap<QString, QJniObject>::const_iterator it = g_camcorderProfiles->constFind(key);
48
49 if (it != g_camcorderProfiles->constEnd())
50 return AndroidCamcorderProfile(*it);
51
52 QJniObject camProfile = QJniObject::callStaticObjectMethod("android/media/CamcorderProfile",
53 "get",
54 "(II)Landroid/media/CamcorderProfile;",
55 cameraId,
56 quality);
57
58 return AndroidCamcorderProfile((*g_camcorderProfiles)[key] = camProfile);
59}
60
62{
63 switch (field) {
64 case audioBitRate:
65 return m_camcorderProfile.getField<jint>("audioBitRate");
66 case audioChannels:
67 return m_camcorderProfile.getField<jint>("audioChannels");
68 case audioCodec:
69 return m_camcorderProfile.getField<jint>("audioCodec");
70 case audioSampleRate:
71 return m_camcorderProfile.getField<jint>("audioSampleRate");
72 case duration:
73 return m_camcorderProfile.getField<jint>("duration");
74 case fileFormat:
75 return m_camcorderProfile.getField<jint>("fileFormat");
76 case quality:
77 return m_camcorderProfile.getField<jint>("quality");
78 case videoBitRate:
79 return m_camcorderProfile.getField<jint>("videoBitRate");
80 case videoCodec:
81 return m_camcorderProfile.getField<jint>("videoCodec");
82 case videoFrameHeight:
83 return m_camcorderProfile.getField<jint>("videoFrameHeight");
84 case videoFrameRate:
85 return m_camcorderProfile.getField<jint>("videoFrameRate");
86 case videoFrameWidth:
87 return m_camcorderProfile.getField<jint>("videoFrameWidth");
88 }
89
90 return 0;
91}
92
93AndroidCamcorderProfile::AndroidCamcorderProfile(const QJniObject &camcorderProfile)
94{
95 m_camcorderProfile = camcorderProfile;
96}
97
99 "org/qtproject/qt/android/multimedia/QtMediaRecorderListener";
102
103static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra)
104{
105 AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
106 if (obj)
107 emit obj->error(what, extra);
108}
109
110static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra)
111{
112 AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
113 if (obj)
114 emit obj->info(what, extra);
115}
116
118 : QObject()
119 , m_id(reinterpret_cast<jlong>(this))
120{
121 m_mediaRecorder = QJniObject("android/media/MediaRecorder");
122 if (m_mediaRecorder.isValid()) {
123 QJniObject listener(QtMediaRecorderListenerClassName, "(J)V", m_id);
124 m_mediaRecorder.callMethod<void>("setOnErrorListener",
125 "(Landroid/media/MediaRecorder$OnErrorListener;)V",
126 listener.object());
127 m_mediaRecorder.callMethod<void>("setOnInfoListener",
128 "(Landroid/media/MediaRecorder$OnInfoListener;)V",
129 listener.object());
130 mediaRecorders->insert(m_id, this);
131 }
132}
133
135{
136 if (m_isVideoSourceSet || m_isAudioSourceSet)
137 reset();
138
139 release();
140 mediaRecorders->remove(m_id);
141}
142
144{
145 m_mediaRecorder.callMethod<void>("release");
146}
147
149{
150 QJniEnvironment env;
151 auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "prepare", "()V");
152 env->CallVoidMethod(m_mediaRecorder.object(), methodId);
153
154 if (env.checkAndClearExceptions())
155 return false;
156 return true;
157}
158
160{
161 m_mediaRecorder.callMethod<void>("reset");
162 m_isAudioSourceSet = false; // Now setAudioSource can be used again.
163 m_isVideoSourceSet = false;
164}
165
167{
168 QJniEnvironment env;
169 auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "start", "()V");
170 env->CallVoidMethod(m_mediaRecorder.object(), methodId);
171
172 if (env.checkAndClearExceptions())
173 return false;
174 return true;
175}
176
178{
179 m_mediaRecorder.callMethod<void>("stop");
180}
181
183{
184 m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels);
185}
186
187void AndroidMediaRecorder::setAudioEncoder(AudioEncoder encoder)
188{
189 QJniEnvironment env;
190 m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder));
191}
192
194{
195 m_mediaRecorder.callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate);
196}
197
199{
200 m_mediaRecorder.callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate);
201}
202
204{
205 if (!m_isAudioSourceSet) {
206 QJniEnvironment env;
207 auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setAudioSource", "(I)V");
208 env->CallVoidMethod(m_mediaRecorder.object(), methodId, source);
209 if (!env.checkAndClearExceptions())
210 m_isAudioSourceSet = true;
211 } else {
212 qCWarning(lcMediaRecorder) << "Audio source already set. Not setting a new source.";
213 }
214}
215
217{
218 return m_isAudioSourceSet;
219}
220
221bool AndroidMediaRecorder::setAudioInput(const QByteArray &id)
222{
223 using namespace QtJniTypes;
224
225 AudioDeviceInfo deviceInfo = QtAudioDeviceManager::callStaticMethod<AudioDeviceInfo>(
226 "getAudioInputDeviceInfo", id.toInt());
227
228 if (!m_mediaRecorder.callMethod<jboolean>("setPreferredDevice", deviceInfo))
229 return false;
230
231 const bool ret =
232 QtAudioDeviceManager::callStaticMethod<jboolean>("prepareAudioInput", id.toInt());
233
234 if (!ret)
235 qCWarning(lcMediaRecorder) << "No default input device was set.";
236
237 return ret;
238}
239
241{
242 QJniObject cam = camera->getCameraObject();
243 m_mediaRecorder.callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object());
244}
245
247{
248 m_mediaRecorder.callMethod<void>("setVideoEncoder", "(I)V", int(encoder));
249}
250
252{
253 m_mediaRecorder.callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate);
254}
255
257{
258 m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate);
259}
260
261void AndroidMediaRecorder::setVideoSize(const QSize &size)
262{
263 m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height());
264}
265
267{
268 QJniEnvironment env;
269
270 auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setVideoSource", "(I)V");
271 env->CallVoidMethod(m_mediaRecorder.object(), methodId, source);
272
273 if (!env.checkAndClearExceptions())
274 m_isVideoSourceSet = true;
275}
276
278{
279 m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees);
280}
281
283{
284 QJniEnvironment env;
285 auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setOutputFormat", "(I)V");
286 env->CallVoidMethod(m_mediaRecorder.object(), methodId, format);
287 // setAudioSource cannot be set after outputFormat is set.
288 if (!env.checkAndClearExceptions())
289 m_isAudioSourceSet = true;
290}
291
292void AndroidMediaRecorder::setOutputFile(const QString &path)
293{
294 if (QUrl(path).scheme() == QLatin1String("content")) {
295 const QJniObject fileDescriptor = QJniObject::callStaticObjectMethod(
296 "org/qtproject/qt/android/QtNative",
297 "openFdObjectForContentUrl",
298 "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Ljava/io/FileDescriptor;",
299 QNativeInterface::QAndroidApplication::context().object(),
300 QJniObject::fromString(path).object(),
301 QJniObject::fromString(QLatin1String("rw")).object());
302
303 m_mediaRecorder.callMethod<void>("setOutputFile",
304 "(Ljava/io/FileDescriptor;)V",
305 fileDescriptor.object());
306 } else {
307 m_mediaRecorder.callMethod<void>("setOutputFile",
308 "(Ljava/lang/String;)V",
309 QJniObject::fromString(path).object());
310 }
311}
312
314{
315 m_mediaRecorder.callMethod<void>("setPreviewDisplay",
316 "(Landroid/view/Surface;)V",
317 texture->surface());
318}
319
321{
322 QJniObject surfaceHolder(holder->surfaceHolder());
323 QJniObject surface = surfaceHolder.callObjectMethod("getSurface",
324 "()Landroid/view/Surface;");
325 if (!surface.isValid())
326 return;
327
328 m_mediaRecorder.callMethod<void>("setPreviewDisplay",
329 "(Landroid/view/Surface;)V",
330 surface.object());
331}
332
334{
335 static const JNINativeMethod methods[] = {
336 {"notifyError", "(JII)V", (void *)notifyError},
337 {"notifyInfo", "(JII)V", (void *)notifyInfo}
338 };
339
340 const int size = std::size(methods);
341 return QJniEnvironment().registerNativeMethods(QtMediaRecorderListenerClassName, methods, size);
342}
343
344QT_END_NAMESPACE
345
346#include "moc_androidmediarecorder_p.cpp"
static void notifyError(JNIEnv *, jobject, jlong id, jint what, jint extra)
static const char QtMediaRecorderListenerClassName[]
static void notifyInfo(JNIEnv *, jobject, jlong id, jint what, jint extra)
QMap< QString, QJniObject > CamcorderProfiles
QMap< jlong, AndroidMediaRecorder * > MediaRecorderMap
static QString profileKey()
int getValue(Field field) const
bool setAudioInput(const QByteArray &id)
void setCamera(AndroidCamera *camera)
void setAudioEncodingBitRate(int bitRate)
void setVideoEncoder(VideoEncoder encoder)
void setAudioChannels(int numChannels)
void setOutputFormat(OutputFormat format)
void setOrientationHint(int degrees)
void setAudioSamplingRate(int samplingRate)
void setAudioEncoder(AudioEncoder encoder)
void setVideoEncodingBitRate(int bitRate)
void setOutputFile(const QString &path)
void setVideoSize(const QSize &size)
void setVideoSource(VideoSource source)
void setSurfaceTexture(AndroidSurfaceTexture *texture)
void setSurfaceHolder(AndroidSurfaceHolder *holder)
void setAudioSource(AudioSource source)
Combined button and popup list for selecting options.
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")