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