android_media_MediaRecorder.cpp revision 83995063178674f1cd18f32d1e7c37046680d8bd
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_pause(JNIEnv *env, jobject thiz)
403{
404    ALOGV("pause");
405    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
406    process_media_recorder_call(env, mr->pause(), "java/lang/RuntimeException", "pause failed.");
407}
408
409static void
410android_media_MediaRecorder_resume(JNIEnv *env, jobject thiz)
411{
412    ALOGV("resume");
413    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
414    process_media_recorder_call(env, mr->resume(), "java/lang/RuntimeException", "resume failed.");
415}
416
417static void
418android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz)
419{
420    ALOGV("native_reset");
421    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
422    process_media_recorder_call(env, mr->reset(), "java/lang/RuntimeException", "native_reset failed.");
423}
424
425static void
426android_media_MediaRecorder_release(JNIEnv *env, jobject thiz)
427{
428    ALOGV("release");
429    sp<MediaRecorder> mr = setMediaRecorder(env, thiz, 0);
430    if (mr != NULL) {
431        mr->setListener(NULL);
432        mr->release();
433    }
434}
435
436// This function gets some field IDs, which in turn causes class initialization.
437// It is called from a static block in MediaRecorder, which won't run until the
438// first time an instance of this class is used.
439static void
440android_media_MediaRecorder_native_init(JNIEnv *env)
441{
442    jclass clazz;
443
444    clazz = env->FindClass("android/media/MediaRecorder");
445    if (clazz == NULL) {
446        return;
447    }
448
449    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
450    if (fields.context == NULL) {
451        return;
452    }
453
454    fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
455    if (fields.surface == NULL) {
456        return;
457    }
458
459    jclass surface = env->FindClass("android/view/Surface");
460    if (surface == NULL) {
461        return;
462    }
463
464    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
465                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
466    if (fields.post_event == NULL) {
467        return;
468    }
469}
470
471
472static void
473android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
474                                         jstring packageName, jstring opPackageName)
475{
476    ALOGV("setup");
477
478    ScopedUtfChars opPackageNameStr(env, opPackageName);
479
480    sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));
481    if (mr == NULL) {
482        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
483        return;
484    }
485    if (mr->initCheck() != NO_ERROR) {
486        jniThrowException(env, "java/lang/RuntimeException", "Unable to initialize media recorder");
487        return;
488    }
489
490    // create new listener and give it to MediaRecorder
491    sp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);
492    mr->setListener(listener);
493
494    // Convert client name jstring to String16
495    const char16_t *rawClientName = reinterpret_cast<const char16_t*>(
496        env->GetStringChars(packageName, NULL));
497    jsize rawClientNameLen = env->GetStringLength(packageName);
498    String16 clientName(rawClientName, rawClientNameLen);
499    env->ReleaseStringChars(packageName,
500                            reinterpret_cast<const jchar*>(rawClientName));
501
502    // pass client package name for permissions tracking
503    mr->setClientName(clientName);
504
505    setMediaRecorder(env, thiz, mr);
506}
507
508static void
509android_media_MediaRecorder_native_finalize(JNIEnv *env, jobject thiz)
510{
511    ALOGV("finalize");
512    android_media_MediaRecorder_release(env, thiz);
513}
514
515void android_media_MediaRecorder_setInputSurface(
516        JNIEnv* env, jobject thiz, jobject object) {
517    ALOGV("android_media_MediaRecorder_setInputSurface");
518
519    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
520
521    sp<PersistentSurface> persistentSurface = get_persistentSurface(env, object);
522
523    process_media_recorder_call(env, mr->setInputSurface(persistentSurface),
524            "java/lang/IllegalArgumentException", "native_setInputSurface failed.");
525}
526
527// ----------------------------------------------------------------------------
528
529static const JNINativeMethod gMethods[] = {
530    {"setCamera",            "(Landroid/hardware/Camera;)V",    (void *)android_media_MediaRecorder_setCamera},
531    {"setVideoSource",       "(I)V",                            (void *)android_media_MediaRecorder_setVideoSource},
532    {"setAudioSource",       "(I)V",                            (void *)android_media_MediaRecorder_setAudioSource},
533    {"setOutputFormat",      "(I)V",                            (void *)android_media_MediaRecorder_setOutputFormat},
534    {"setVideoEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setVideoEncoder},
535    {"setAudioEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setAudioEncoder},
536    {"setParameter",         "(Ljava/lang/String;)V",           (void *)android_media_MediaRecorder_setParameter},
537    {"_setOutputFile",       "(Ljava/io/FileDescriptor;JJ)V",   (void *)android_media_MediaRecorder_setOutputFileFD},
538    {"setVideoSize",         "(II)V",                           (void *)android_media_MediaRecorder_setVideoSize},
539    {"setVideoFrameRate",    "(I)V",                            (void *)android_media_MediaRecorder_setVideoFrameRate},
540    {"setMaxDuration",       "(I)V",                            (void *)android_media_MediaRecorder_setMaxDuration},
541    {"setMaxFileSize",       "(J)V",                            (void *)android_media_MediaRecorder_setMaxFileSize},
542    {"_prepare",             "()V",                             (void *)android_media_MediaRecorder_prepare},
543    {"getSurface",           "()Landroid/view/Surface;",        (void *)android_media_MediaRecorder_getSurface},
544    {"getMaxAmplitude",      "()I",                             (void *)android_media_MediaRecorder_native_getMaxAmplitude},
545    {"start",                "()V",                             (void *)android_media_MediaRecorder_start},
546    {"stop",                 "()V",                             (void *)android_media_MediaRecorder_stop},
547    {"pause",                "()V",                             (void *)android_media_MediaRecorder_pause},
548    {"resume",               "()V",                             (void *)android_media_MediaRecorder_resume},
549    {"native_reset",         "()V",                             (void *)android_media_MediaRecorder_native_reset},
550    {"release",              "()V",                             (void *)android_media_MediaRecorder_release},
551    {"native_init",          "()V",                             (void *)android_media_MediaRecorder_native_init},
552    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
553                                                                (void *)android_media_MediaRecorder_native_setup},
554    {"native_finalize",      "()V",                             (void *)android_media_MediaRecorder_native_finalize},
555    {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
556};
557
558// This function only registers the native methods, and is called from
559// JNI_OnLoad in android_media_MediaPlayer.cpp
560int register_android_media_MediaRecorder(JNIEnv *env)
561{
562    return AndroidRuntime::registerNativeMethods(env,
563                "android/media/MediaRecorder", gMethods, NELEM(gMethods));
564}
565