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