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