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