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