1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "androidmediarecorder.h"
41
42 #include "androidcamera.h"
43 #include "androidsurfacetexture.h"
44 #include "androidsurfaceview.h"
45 #include "qandroidglobal.h"
46 #include "qandroidmultimediautils.h"
47 #include <QtCore/private/qjni_p.h>
48 #include <qmap.h>
49
50 QT_BEGIN_NAMESPACE
51
52 typedef QMap<QString, QJNIObjectPrivate> CamcorderProfiles;
Q_GLOBAL_STATIC(CamcorderProfiles,g_camcorderProfiles)53 Q_GLOBAL_STATIC(CamcorderProfiles, g_camcorderProfiles)
54
55 static QString profileKey()
56 {
57 return QStringLiteral("%1-%2");
58 }
59
hasProfile(jint cameraId,Quality quality)60 bool AndroidCamcorderProfile::hasProfile(jint cameraId, Quality quality)
61 {
62 if (g_camcorderProfiles->contains(profileKey().arg(cameraId).arg(quality)))
63 return true;
64
65 return QJNIObjectPrivate::callStaticMethod<jboolean>("android/media/CamcorderProfile",
66 "hasProfile",
67 "(II)Z",
68 cameraId,
69 quality);
70 }
71
get(jint cameraId,Quality quality)72 AndroidCamcorderProfile AndroidCamcorderProfile::get(jint cameraId, Quality quality)
73 {
74 const QString key = profileKey().arg(cameraId).arg(quality);
75 QMap<QString, QJNIObjectPrivate>::const_iterator it = g_camcorderProfiles->constFind(key);
76
77 if (it != g_camcorderProfiles->constEnd())
78 return AndroidCamcorderProfile(*it);
79
80 QJNIObjectPrivate camProfile = QJNIObjectPrivate::callStaticObjectMethod("android/media/CamcorderProfile",
81 "get",
82 "(II)Landroid/media/CamcorderProfile;",
83 cameraId,
84 quality);
85
86 return AndroidCamcorderProfile((*g_camcorderProfiles)[key] = camProfile);
87 }
88
getValue(AndroidCamcorderProfile::Field field) const89 int AndroidCamcorderProfile::getValue(AndroidCamcorderProfile::Field field) const
90 {
91 switch (field) {
92 case audioBitRate:
93 return m_camcorderProfile.getField<jint>("audioBitRate");
94 case audioChannels:
95 return m_camcorderProfile.getField<jint>("audioChannels");
96 case audioCodec:
97 return m_camcorderProfile.getField<jint>("audioCodec");
98 case audioSampleRate:
99 return m_camcorderProfile.getField<jint>("audioSampleRate");
100 case duration:
101 return m_camcorderProfile.getField<jint>("duration");
102 case fileFormat:
103 return m_camcorderProfile.getField<jint>("fileFormat");
104 case quality:
105 return m_camcorderProfile.getField<jint>("quality");
106 case videoBitRate:
107 return m_camcorderProfile.getField<jint>("videoBitRate");
108 case videoCodec:
109 return m_camcorderProfile.getField<jint>("videoCodec");
110 case videoFrameHeight:
111 return m_camcorderProfile.getField<jint>("videoFrameHeight");
112 case videoFrameRate:
113 return m_camcorderProfile.getField<jint>("videoFrameRate");
114 case videoFrameWidth:
115 return m_camcorderProfile.getField<jint>("videoFrameWidth");
116 }
117
118 return 0;
119 }
120
AndroidCamcorderProfile(const QJNIObjectPrivate & camcorderProfile)121 AndroidCamcorderProfile::AndroidCamcorderProfile(const QJNIObjectPrivate &camcorderProfile)
122 {
123 m_camcorderProfile = camcorderProfile;
124 }
125
126 static const char QtMediaRecorderListenerClassName[] = "org/qtproject/qt5/android/multimedia/QtMediaRecorderListener";
127 typedef QMap<jlong, AndroidMediaRecorder*> MediaRecorderMap;
Q_GLOBAL_STATIC(MediaRecorderMap,mediaRecorders)128 Q_GLOBAL_STATIC(MediaRecorderMap, mediaRecorders)
129
130 static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra)
131 {
132 AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
133 if (obj)
134 emit obj->error(what, extra);
135 }
136
notifyInfo(JNIEnv *,jobject,jlong id,jint what,jint extra)137 static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra)
138 {
139 AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
140 if (obj)
141 emit obj->info(what, extra);
142 }
143
AndroidMediaRecorder()144 AndroidMediaRecorder::AndroidMediaRecorder()
145 : QObject()
146 , m_id(reinterpret_cast<jlong>(this))
147 {
148 m_mediaRecorder = QJNIObjectPrivate("android/media/MediaRecorder");
149 if (m_mediaRecorder.isValid()) {
150 QJNIObjectPrivate listener(QtMediaRecorderListenerClassName, "(J)V", m_id);
151 m_mediaRecorder.callMethod<void>("setOnErrorListener",
152 "(Landroid/media/MediaRecorder$OnErrorListener;)V",
153 listener.object());
154 m_mediaRecorder.callMethod<void>("setOnInfoListener",
155 "(Landroid/media/MediaRecorder$OnInfoListener;)V",
156 listener.object());
157 mediaRecorders->insert(m_id, this);
158 }
159 }
160
~AndroidMediaRecorder()161 AndroidMediaRecorder::~AndroidMediaRecorder()
162 {
163 mediaRecorders->remove(m_id);
164 }
165
release()166 void AndroidMediaRecorder::release()
167 {
168 m_mediaRecorder.callMethod<void>("release");
169 }
170
prepare()171 bool AndroidMediaRecorder::prepare()
172 {
173 QJNIEnvironmentPrivate env;
174 m_mediaRecorder.callMethod<void>("prepare");
175 if (env->ExceptionCheck()) {
176 #ifdef QT_DEBUG
177 env->ExceptionDescribe();
178 #endif
179 env->ExceptionClear();
180 return false;
181 }
182 return true;
183 }
184
reset()185 void AndroidMediaRecorder::reset()
186 {
187 m_mediaRecorder.callMethod<void>("reset");
188 }
189
start()190 bool AndroidMediaRecorder::start()
191 {
192 QJNIEnvironmentPrivate env;
193 m_mediaRecorder.callMethod<void>("start");
194 if (env->ExceptionCheck()) {
195 #ifdef QT_DEBUG
196 env->ExceptionDescribe();
197 #endif
198 env->ExceptionClear();
199 return false;
200 }
201 return true;
202 }
203
stop()204 void AndroidMediaRecorder::stop()
205 {
206 QJNIEnvironmentPrivate env;
207 m_mediaRecorder.callMethod<void>("stop");
208 if (env->ExceptionCheck()) {
209 #ifdef QT_DEBUG
210 env->ExceptionDescribe();
211 #endif
212 env->ExceptionClear();
213 }
214 }
215
setAudioChannels(int numChannels)216 void AndroidMediaRecorder::setAudioChannels(int numChannels)
217 {
218 m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels);
219 }
220
setAudioEncoder(AudioEncoder encoder)221 void AndroidMediaRecorder::setAudioEncoder(AudioEncoder encoder)
222 {
223 QJNIEnvironmentPrivate env;
224 m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder));
225 if (env->ExceptionCheck()) {
226 #ifdef QT_DEBUG
227 env->ExceptionDescribe();
228 #endif
229 env->ExceptionClear();
230 }
231 }
232
setAudioEncodingBitRate(int bitRate)233 void AndroidMediaRecorder::setAudioEncodingBitRate(int bitRate)
234 {
235 m_mediaRecorder.callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate);
236 }
237
setAudioSamplingRate(int samplingRate)238 void AndroidMediaRecorder::setAudioSamplingRate(int samplingRate)
239 {
240 m_mediaRecorder.callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate);
241 }
242
setAudioSource(AudioSource source)243 void AndroidMediaRecorder::setAudioSource(AudioSource source)
244 {
245 QJNIEnvironmentPrivate env;
246 m_mediaRecorder.callMethod<void>("setAudioSource", "(I)V", int(source));
247 if (env->ExceptionCheck()) {
248 #ifdef QT_DEBUG
249 env->ExceptionDescribe();
250 #endif
251 env->ExceptionClear();
252 }
253 }
254
setCamera(AndroidCamera * camera)255 void AndroidMediaRecorder::setCamera(AndroidCamera *camera)
256 {
257 QJNIObjectPrivate cam = camera->getCameraObject();
258 m_mediaRecorder.callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object());
259 }
260
setVideoEncoder(VideoEncoder encoder)261 void AndroidMediaRecorder::setVideoEncoder(VideoEncoder encoder)
262 {
263 QJNIEnvironmentPrivate env;
264 m_mediaRecorder.callMethod<void>("setVideoEncoder", "(I)V", int(encoder));
265 if (env->ExceptionCheck()) {
266 #ifdef QT_DEBUG
267 env->ExceptionDescribe();
268 #endif
269 env->ExceptionClear();
270 }
271 }
272
setVideoEncodingBitRate(int bitRate)273 void AndroidMediaRecorder::setVideoEncodingBitRate(int bitRate)
274 {
275 m_mediaRecorder.callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate);
276 }
277
setVideoFrameRate(int rate)278 void AndroidMediaRecorder::setVideoFrameRate(int rate)
279 {
280 QJNIEnvironmentPrivate env;
281 m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate);
282 if (env->ExceptionCheck()) {
283 #ifdef QT_DEBUG
284 env->ExceptionDescribe();
285 #endif
286 env->ExceptionClear();
287 }
288 }
289
setVideoSize(const QSize & size)290 void AndroidMediaRecorder::setVideoSize(const QSize &size)
291 {
292 QJNIEnvironmentPrivate env;
293 m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height());
294 if (env->ExceptionCheck()) {
295 #ifdef QT_DEBUG
296 env->ExceptionDescribe();
297 #endif
298 env->ExceptionClear();
299 }
300 }
301
setVideoSource(VideoSource source)302 void AndroidMediaRecorder::setVideoSource(VideoSource source)
303 {
304 QJNIEnvironmentPrivate env;
305 m_mediaRecorder.callMethod<void>("setVideoSource", "(I)V", int(source));
306 if (env->ExceptionCheck()) {
307 #ifdef QT_DEBUG
308 env->ExceptionDescribe();
309 #endif
310 env->ExceptionClear();
311 }
312 }
313
setOrientationHint(int degrees)314 void AndroidMediaRecorder::setOrientationHint(int degrees)
315 {
316 QJNIEnvironmentPrivate env;
317 m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees);
318 if (env->ExceptionCheck()) {
319 #ifdef QT_DEBUG
320 env->ExceptionDescribe();
321 #endif
322 env->ExceptionClear();
323 }
324 }
325
setOutputFormat(OutputFormat format)326 void AndroidMediaRecorder::setOutputFormat(OutputFormat format)
327 {
328 QJNIEnvironmentPrivate env;
329 m_mediaRecorder.callMethod<void>("setOutputFormat", "(I)V", int(format));
330 if (env->ExceptionCheck()) {
331 #ifdef QT_DEBUG
332 env->ExceptionDescribe();
333 #endif
334 env->ExceptionClear();
335 }
336 }
337
setOutputFile(const QString & path)338 void AndroidMediaRecorder::setOutputFile(const QString &path)
339 {
340 QJNIEnvironmentPrivate env;
341 m_mediaRecorder.callMethod<void>("setOutputFile",
342 "(Ljava/lang/String;)V",
343 QJNIObjectPrivate::fromString(path).object());
344 if (env->ExceptionCheck()) {
345 #ifdef QT_DEBUG
346 env->ExceptionDescribe();
347 #endif
348 env->ExceptionClear();
349 }
350 }
351
setSurfaceTexture(AndroidSurfaceTexture * texture)352 void AndroidMediaRecorder::setSurfaceTexture(AndroidSurfaceTexture *texture)
353 {
354 QJNIEnvironmentPrivate env;
355 m_mediaRecorder.callMethod<void>("setPreviewDisplay",
356 "(Landroid/view/Surface;)V",
357 texture->surface());
358 if (env->ExceptionCheck()) {
359 #ifdef QT_DEBUG
360 env->ExceptionDescribe();
361 #endif
362 env->ExceptionClear();
363 }
364 }
365
setSurfaceHolder(AndroidSurfaceHolder * holder)366 void AndroidMediaRecorder::setSurfaceHolder(AndroidSurfaceHolder *holder)
367 {
368 QJNIEnvironmentPrivate env;
369 QJNIObjectPrivate surfaceHolder(holder->surfaceHolder());
370 QJNIObjectPrivate surface = surfaceHolder.callObjectMethod("getSurface",
371 "()Landroid/view/Surface;");
372 if (!surface.isValid())
373 return;
374
375 m_mediaRecorder.callMethod<void>("setPreviewDisplay",
376 "(Landroid/view/Surface;)V",
377 surface.object());
378 if (env->ExceptionCheck()) {
379 #ifdef QT_DEBUG
380 env->ExceptionDescribe();
381 #endif
382 env->ExceptionClear();
383 }
384 }
385
initJNI(JNIEnv * env)386 bool AndroidMediaRecorder::initJNI(JNIEnv *env)
387 {
388 jclass clazz = QJNIEnvironmentPrivate::findClass(QtMediaRecorderListenerClassName,
389 env);
390
391 static const JNINativeMethod methods[] = {
392 {"notifyError", "(JII)V", (void *)notifyError},
393 {"notifyInfo", "(JII)V", (void *)notifyInfo}
394 };
395
396 if (clazz && env->RegisterNatives(clazz,
397 methods,
398 sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
399 return false;
400 }
401
402 return true;
403 }
404
405 QT_END_NAMESPACE
406