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