1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <fcntl.h>
19#include <inttypes.h>
20#include <limits.h>
21#include <stdio.h>
22#include <unistd.h>
23
24//#define LOG_NDEBUG 0
25#define LOG_TAG "MediaRecorderJNI"
26#include <utils/Log.h>
27
28#include <gui/Surface.h>
29#include <camera/ICameraService.h>
30#include <camera/Camera.h>
31#include <media/mediarecorder.h>
32#include <media/stagefright/PersistentSurface.h>
33#include <utils/threads.h>
34
35#include <ScopedUtfChars.h>
36
37#include "jni.h"
38#include "JNIHelp.h"
39#include "android_runtime/AndroidRuntime.h"
40
41#include <system/audio.h>
42#include <android_runtime/android_view_Surface.h>
43
44// ----------------------------------------------------------------------------
45
46using namespace android;
47
48// ----------------------------------------------------------------------------
49
50// helper function to extract a native Camera object from a Camera Java object
51extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context);
52extern sp<PersistentSurface>
53android_media_MediaCodec_getPersistentInputSurface(JNIEnv* env, jobject object);
54
55struct fields_t {
56    jfieldID    context;
57    jfieldID    surface;
58
59    jmethodID   post_event;
60};
61static fields_t fields;
62
63static Mutex sLock;
64
65// ----------------------------------------------------------------------------
66// ref-counted object for callbacks
67class JNIMediaRecorderListener: public MediaRecorderListener
68{
69public:
70    JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
71    ~JNIMediaRecorderListener();
72    void notify(int msg, int ext1, int ext2);
73private:
74    JNIMediaRecorderListener();
75    jclass      mClass;     // Reference to MediaRecorder class
76    jobject     mObject;    // Weak ref to MediaRecorder Java object to call on
77};
78
79JNIMediaRecorderListener::JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
80{
81
82    // Hold onto the MediaRecorder class for use in calling the static method
83    // that posts events to the application thread.
84    jclass clazz = env->GetObjectClass(thiz);
85    if (clazz == NULL) {
86        ALOGE("Can't find android/media/MediaRecorder");
87        jniThrowException(env, "java/lang/Exception", NULL);
88        return;
89    }
90    mClass = (jclass)env->NewGlobalRef(clazz);
91
92    // We use a weak reference so the MediaRecorder object can be garbage collected.
93    // The reference is only used as a proxy for callbacks.
94    mObject  = env->NewGlobalRef(weak_thiz);
95}
96
97JNIMediaRecorderListener::~JNIMediaRecorderListener()
98{
99    // remove global references
100    JNIEnv *env = AndroidRuntime::getJNIEnv();
101    env->DeleteGlobalRef(mObject);
102    env->DeleteGlobalRef(mClass);
103}
104
105void JNIMediaRecorderListener::notify(int msg, int ext1, int ext2)
106{
107    ALOGV("JNIMediaRecorderListener::notify");
108
109    JNIEnv *env = AndroidRuntime::getJNIEnv();
110    env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, NULL);
111}
112
113// ----------------------------------------------------------------------------
114
115static sp<Surface> get_surface(JNIEnv* env, jobject clazz)
116{
117    ALOGV("get_surface");
118    return android_view_Surface_getSurface(env, clazz);
119}
120
121static sp<PersistentSurface> get_persistentSurface(JNIEnv* env, jobject object)
122{
123    ALOGV("get_persistentSurface");
124    return android_media_MediaCodec_getPersistentInputSurface(env, object);
125}
126
127// Returns true if it throws an exception.
128static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message)
129{
130    ALOGV("process_media_recorder_call");
131    if (opStatus == (status_t)INVALID_OPERATION) {
132        jniThrowException(env, "java/lang/IllegalStateException", NULL);
133        return true;
134    } else if (opStatus != (status_t)OK) {
135        jniThrowException(env, exception, message);
136        return true;
137    }
138    return false;
139}
140
141static sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject thiz)
142{
143    Mutex::Autolock l(sLock);
144    MediaRecorder* const p = (MediaRecorder*)env->GetLongField(thiz, fields.context);
145    return sp<MediaRecorder>(p);
146}
147
148static sp<MediaRecorder> setMediaRecorder(JNIEnv* env, jobject thiz, const sp<MediaRecorder>& recorder)
149{
150    Mutex::Autolock l(sLock);
151    sp<MediaRecorder> old = (MediaRecorder*)env->GetLongField(thiz, fields.context);
152    if (recorder.get()) {
153        recorder->incStrong(thiz);
154    }
155    if (old != 0) {
156        old->decStrong(thiz);
157    }
158    env->SetLongField(thiz, fields.context, (jlong)recorder.get());
159    return old;
160}
161
162
163static void android_media_MediaRecorder_setCamera(JNIEnv* env, jobject thiz, jobject camera)
164{
165    // we should not pass a null camera to get_native_camera() call.
166    if (camera == NULL) {
167        jniThrowNullPointerException(env, "camera object is a NULL pointer");
168        return;
169    }
170    sp<Camera> c = get_native_camera(env, camera, NULL);
171    if (c == NULL) {
172        // get_native_camera will throw an exception in this case
173        return;
174    }
175    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
176    process_media_recorder_call(env, mr->setCamera(c->remote(), c->getRecordingProxy()),
177            "java/lang/RuntimeException", "setCamera failed.");
178}
179
180static void
181android_media_MediaRecorder_setVideoSource(JNIEnv *env, jobject thiz, jint vs)
182{
183    ALOGV("setVideoSource(%d)", vs);
184    if (vs < VIDEO_SOURCE_DEFAULT || vs >= VIDEO_SOURCE_LIST_END) {
185        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video source");
186        return;
187    }
188    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
189    process_media_recorder_call(env, mr->setVideoSource(vs), "java/lang/RuntimeException", "setVideoSource failed.");
190}
191
192static void
193android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as)
194{
195    ALOGV("setAudioSource(%d)", as);
196    if (as < AUDIO_SOURCE_DEFAULT ||
197        (as >= AUDIO_SOURCE_CNT && as != AUDIO_SOURCE_FM_TUNER)) {
198        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio source");
199        return;
200    }
201
202    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
203    process_media_recorder_call(env, mr->setAudioSource(as), "java/lang/RuntimeException", "setAudioSource failed.");
204}
205
206static void
207android_media_MediaRecorder_setOutputFormat(JNIEnv *env, jobject thiz, jint of)
208{
209    ALOGV("setOutputFormat(%d)", of);
210    if (of < OUTPUT_FORMAT_DEFAULT || of >= OUTPUT_FORMAT_LIST_END) {
211        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid output format");
212        return;
213    }
214    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
215    process_media_recorder_call(env, mr->setOutputFormat(of), "java/lang/RuntimeException", "setOutputFormat failed.");
216}
217
218static void
219android_media_MediaRecorder_setVideoEncoder(JNIEnv *env, jobject thiz, jint ve)
220{
221    ALOGV("setVideoEncoder(%d)", ve);
222    if (ve < VIDEO_ENCODER_DEFAULT || ve >= VIDEO_ENCODER_LIST_END) {
223        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video encoder");
224        return;
225    }
226    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
227    process_media_recorder_call(env, mr->setVideoEncoder(ve), "java/lang/RuntimeException", "setVideoEncoder failed.");
228}
229
230static void
231android_media_MediaRecorder_setAudioEncoder(JNIEnv *env, jobject thiz, jint ae)
232{
233    ALOGV("setAudioEncoder(%d)", ae);
234    if (ae < AUDIO_ENCODER_DEFAULT || ae >= AUDIO_ENCODER_LIST_END) {
235        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio encoder");
236        return;
237    }
238    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
239    process_media_recorder_call(env, mr->setAudioEncoder(ae), "java/lang/RuntimeException", "setAudioEncoder failed.");
240}
241
242static void
243android_media_MediaRecorder_setParameter(JNIEnv *env, jobject thiz, jstring params)
244{
245    ALOGV("setParameter()");
246    if (params == NULL)
247    {
248        ALOGE("Invalid or empty params string.  This parameter will be ignored.");
249        return;
250    }
251
252    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
253
254    const char* params8 = env->GetStringUTFChars(params, NULL);
255    if (params8 == NULL)
256    {
257        ALOGE("Failed to covert jstring to String8.  This parameter will be ignored.");
258        return;
259    }
260
261    process_media_recorder_call(env, mr->setParameters(String8(params8)), "java/lang/RuntimeException", "setParameter failed.");
262    env->ReleaseStringUTFChars(params,params8);
263}
264
265static void
266android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
267{
268    ALOGV("setOutputFile");
269    if (fileDescriptor == NULL) {
270        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
271        return;
272    }
273    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
274    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
275    status_t opStatus = mr->setOutputFile(fd, offset, length);
276    process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
277}
278
279static void
280android_media_MediaRecorder_setVideoSize(JNIEnv *env, jobject thiz, jint width, jint height)
281{
282    ALOGV("setVideoSize(%d, %d)", width, height);
283    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
284
285    if (width <= 0 || height <= 0) {
286        jniThrowException(env, "java/lang/IllegalArgumentException", "invalid video size");
287        return;
288    }
289    process_media_recorder_call(env, mr->setVideoSize(width, height), "java/lang/RuntimeException", "setVideoSize failed.");
290}
291
292static void
293android_media_MediaRecorder_setVideoFrameRate(JNIEnv *env, jobject thiz, jint rate)
294{
295    ALOGV("setVideoFrameRate(%d)", rate);
296    if (rate <= 0) {
297        jniThrowException(env, "java/lang/IllegalArgumentException", "invalid frame rate");
298        return;
299    }
300    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
301    process_media_recorder_call(env, mr->setVideoFrameRate(rate), "java/lang/RuntimeException", "setVideoFrameRate failed.");
302}
303
304static void
305android_media_MediaRecorder_setMaxDuration(JNIEnv *env, jobject thiz, jint max_duration_ms)
306{
307    ALOGV("setMaxDuration(%d)", max_duration_ms);
308    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
309
310    char params[64];
311    sprintf(params, "max-duration=%d", max_duration_ms);
312
313    process_media_recorder_call(env, mr->setParameters(String8(params)), "java/lang/RuntimeException", "setMaxDuration failed.");
314}
315
316static void
317android_media_MediaRecorder_setMaxFileSize(
318        JNIEnv *env, jobject thiz, jlong max_filesize_bytes)
319{
320    ALOGV("setMaxFileSize(%lld)", (long long)max_filesize_bytes);
321    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
322
323    char params[64];
324    sprintf(params, "max-filesize=%" PRId64, max_filesize_bytes);
325
326    process_media_recorder_call(env, mr->setParameters(String8(params)), "java/lang/RuntimeException", "setMaxFileSize failed.");
327}
328
329static void
330android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)
331{
332    ALOGV("prepare");
333    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
334
335    jobject surface = env->GetObjectField(thiz, fields.surface);
336    if (surface != NULL) {
337        const sp<Surface> native_surface = get_surface(env, surface);
338
339        // The application may misbehave and
340        // the preview surface becomes unavailable
341        if (native_surface.get() == 0) {
342            ALOGE("Application lost the surface");
343            jniThrowException(env, "java/io/IOException", "invalid preview surface");
344            return;
345        }
346
347        ALOGI("prepare: surface=%p", native_surface.get());
348        if (process_media_recorder_call(env, mr->setPreviewSurface(native_surface->getIGraphicBufferProducer()), "java/lang/RuntimeException", "setPreviewSurface failed.")) {
349            return;
350        }
351    }
352    process_media_recorder_call(env, mr->prepare(), "java/io/IOException", "prepare failed.");
353}
354
355static jint
356android_media_MediaRecorder_native_getMaxAmplitude(JNIEnv *env, jobject thiz)
357{
358    ALOGV("getMaxAmplitude");
359    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
360    int result = 0;
361    process_media_recorder_call(env, mr->getMaxAmplitude(&result), "java/lang/RuntimeException", "getMaxAmplitude failed.");
362    return (jint) result;
363}
364
365static jobject
366android_media_MediaRecorder_getSurface(JNIEnv *env, jobject thiz)
367{
368    ALOGV("getSurface");
369    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
370
371    sp<IGraphicBufferProducer> bufferProducer = mr->querySurfaceMediaSourceFromMediaServer();
372    if (bufferProducer == NULL) {
373        jniThrowException(
374                env,
375                "java/lang/IllegalStateException",
376                "failed to get surface");
377        return NULL;
378    }
379
380    // Wrap the IGBP in a Java-language Surface.
381    return android_view_Surface_createFromIGraphicBufferProducer(env,
382            bufferProducer);
383}
384
385static void
386android_media_MediaRecorder_start(JNIEnv *env, jobject thiz)
387{
388    ALOGV("start");
389    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
390    process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed.");
391}
392
393static void
394android_media_MediaRecorder_stop(JNIEnv *env, jobject thiz)
395{
396    ALOGV("stop");
397    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
398    process_media_recorder_call(env, mr->stop(), "java/lang/RuntimeException", "stop failed.");
399}
400
401static void
402android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz)
403{
404    ALOGV("native_reset");
405    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
406    process_media_recorder_call(env, mr->reset(), "java/lang/RuntimeException", "native_reset failed.");
407}
408
409static void
410android_media_MediaRecorder_release(JNIEnv *env, jobject thiz)
411{
412    ALOGV("release");
413    sp<MediaRecorder> mr = setMediaRecorder(env, thiz, 0);
414    if (mr != NULL) {
415        mr->setListener(NULL);
416        mr->release();
417    }
418}
419
420// This function gets some field IDs, which in turn causes class initialization.
421// It is called from a static block in MediaRecorder, which won't run until the
422// first time an instance of this class is used.
423static void
424android_media_MediaRecorder_native_init(JNIEnv *env)
425{
426    jclass clazz;
427
428    clazz = env->FindClass("android/media/MediaRecorder");
429    if (clazz == NULL) {
430        return;
431    }
432
433    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
434    if (fields.context == NULL) {
435        return;
436    }
437
438    fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
439    if (fields.surface == NULL) {
440        return;
441    }
442
443    jclass surface = env->FindClass("android/view/Surface");
444    if (surface == NULL) {
445        return;
446    }
447
448    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
449                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
450    if (fields.post_event == NULL) {
451        return;
452    }
453}
454
455
456static void
457android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
458                                         jstring packageName, jstring opPackageName)
459{
460    ALOGV("setup");
461
462    ScopedUtfChars opPackageNameStr(env, opPackageName);
463
464    sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));
465    if (mr == NULL) {
466        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
467        return;
468    }
469    if (mr->initCheck() != NO_ERROR) {
470        jniThrowException(env, "java/lang/RuntimeException", "Unable to initialize media recorder");
471        return;
472    }
473
474    // create new listener and give it to MediaRecorder
475    sp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);
476    mr->setListener(listener);
477
478    // Convert client name jstring to String16
479    const char16_t *rawClientName = reinterpret_cast<const char16_t*>(
480        env->GetStringChars(packageName, NULL));
481    jsize rawClientNameLen = env->GetStringLength(packageName);
482    String16 clientName(rawClientName, rawClientNameLen);
483    env->ReleaseStringChars(packageName,
484                            reinterpret_cast<const jchar*>(rawClientName));
485
486    // pass client package name for permissions tracking
487    mr->setClientName(clientName);
488
489    setMediaRecorder(env, thiz, mr);
490}
491
492static void
493android_media_MediaRecorder_native_finalize(JNIEnv *env, jobject thiz)
494{
495    ALOGV("finalize");
496    android_media_MediaRecorder_release(env, thiz);
497}
498
499void android_media_MediaRecorder_setInputSurface(
500        JNIEnv* env, jobject thiz, jobject object) {
501    ALOGV("android_media_MediaRecorder_setInputSurface");
502
503    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
504
505    sp<PersistentSurface> persistentSurface = get_persistentSurface(env, object);
506
507    process_media_recorder_call(env, mr->setInputSurface(persistentSurface),
508            "java/lang/IllegalArgumentException", "native_setInputSurface failed.");
509}
510
511// ----------------------------------------------------------------------------
512
513static JNINativeMethod gMethods[] = {
514    {"setCamera",            "(Landroid/hardware/Camera;)V",    (void *)android_media_MediaRecorder_setCamera},
515    {"setVideoSource",       "(I)V",                            (void *)android_media_MediaRecorder_setVideoSource},
516    {"setAudioSource",       "(I)V",                            (void *)android_media_MediaRecorder_setAudioSource},
517    {"setOutputFormat",      "(I)V",                            (void *)android_media_MediaRecorder_setOutputFormat},
518    {"setVideoEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setVideoEncoder},
519    {"setAudioEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setAudioEncoder},
520    {"setParameter",         "(Ljava/lang/String;)V",           (void *)android_media_MediaRecorder_setParameter},
521    {"_setOutputFile",       "(Ljava/io/FileDescriptor;JJ)V",   (void *)android_media_MediaRecorder_setOutputFileFD},
522    {"setVideoSize",         "(II)V",                           (void *)android_media_MediaRecorder_setVideoSize},
523    {"setVideoFrameRate",    "(I)V",                            (void *)android_media_MediaRecorder_setVideoFrameRate},
524    {"setMaxDuration",       "(I)V",                            (void *)android_media_MediaRecorder_setMaxDuration},
525    {"setMaxFileSize",       "(J)V",                            (void *)android_media_MediaRecorder_setMaxFileSize},
526    {"_prepare",             "()V",                             (void *)android_media_MediaRecorder_prepare},
527    {"getSurface",           "()Landroid/view/Surface;",        (void *)android_media_MediaRecorder_getSurface},
528    {"getMaxAmplitude",      "()I",                             (void *)android_media_MediaRecorder_native_getMaxAmplitude},
529    {"start",                "()V",                             (void *)android_media_MediaRecorder_start},
530    {"stop",                 "()V",                             (void *)android_media_MediaRecorder_stop},
531    {"native_reset",         "()V",                             (void *)android_media_MediaRecorder_native_reset},
532    {"release",              "()V",                             (void *)android_media_MediaRecorder_release},
533    {"native_init",          "()V",                             (void *)android_media_MediaRecorder_native_init},
534    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
535                                                                (void *)android_media_MediaRecorder_native_setup},
536    {"native_finalize",      "()V",                             (void *)android_media_MediaRecorder_native_finalize},
537    {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
538};
539
540// This function only registers the native methods, and is called from
541// JNI_OnLoad in android_media_MediaPlayer.cpp
542int register_android_media_MediaRecorder(JNIEnv *env)
543{
544    return AndroidRuntime::registerNativeMethods(env,
545                "android/media/MediaRecorder", gMethods, NELEM(gMethods));
546}
547