android_media_MediaPlayer.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/* //device/libs/android_runtime/android_media_MediaPlayer.cpp 2** 3** Copyright 2007, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18//#define LOG_NDEBUG 0 19#define LOG_TAG "MediaPlayer-JNI" 20#include "utils/Log.h" 21 22#include <media/mediaplayer.h> 23#include <stdio.h> 24#include <assert.h> 25#include <limits.h> 26#include <unistd.h> 27#include <fcntl.h> 28#include <utils/threads.h> 29#include "jni.h" 30#include "JNIHelp.h" 31#include "android_runtime/AndroidRuntime.h" 32 33 34// ---------------------------------------------------------------------------- 35 36using namespace android; 37 38// ---------------------------------------------------------------------------- 39 40struct fields_t { 41 jfieldID context; 42 jfieldID surface; 43 /* actually in android.view.Surface XXX */ 44 jfieldID surface_native; 45 46 jmethodID post_event; 47}; 48static fields_t fields; 49 50static Mutex sLock; 51 52// ---------------------------------------------------------------------------- 53// ref-counted object for callbacks 54class JNIMediaPlayerListener: public MediaPlayerListener 55{ 56public: 57 JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz); 58 ~JNIMediaPlayerListener(); 59 void notify(int msg, int ext1, int ext2); 60private: 61 JNIMediaPlayerListener(); 62 jclass mClass; // Reference to MediaPlayer class 63 jobject mObject; // Weak ref to MediaPlayer Java object to call on 64}; 65 66JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz) 67{ 68 69 // Hold onto the MediaPlayer class for use in calling the static method 70 // that posts events to the application thread. 71 jclass clazz = env->GetObjectClass(thiz); 72 if (clazz == NULL) { 73 LOGE("Can't find android/media/MediaPlayer"); 74 jniThrowException(env, "java/lang/Exception", NULL); 75 return; 76 } 77 mClass = (jclass)env->NewGlobalRef(clazz); 78 79 // We use a weak reference so the MediaPlayer object can be garbage collected. 80 // The reference is only used as a proxy for callbacks. 81 mObject = env->NewGlobalRef(weak_thiz); 82} 83 84JNIMediaPlayerListener::~JNIMediaPlayerListener() 85{ 86 // remove global references 87 JNIEnv *env = AndroidRuntime::getJNIEnv(); 88 env->DeleteGlobalRef(mObject); 89 env->DeleteGlobalRef(mClass); 90} 91 92void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2) 93{ 94 JNIEnv *env = AndroidRuntime::getJNIEnv(); 95 env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0); 96} 97 98// ---------------------------------------------------------------------------- 99 100static sp<Surface> get_surface(JNIEnv* env, jobject clazz) 101{ 102 Surface* const p = (Surface*)env->GetIntField(clazz, fields.surface_native); 103 return sp<Surface>(p); 104} 105 106static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz) 107{ 108 Mutex::Autolock l(sLock); 109 MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context); 110 return sp<MediaPlayer>(p); 111} 112 113static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player) 114{ 115 Mutex::Autolock l(sLock); 116 sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context); 117 if (player.get()) { 118 player->incStrong(thiz); 119 } 120 if (old != 0) { 121 old->decStrong(thiz); 122 } 123 env->SetIntField(thiz, fields.context, (int)player.get()); 124 return old; 125} 126 127// If exception is NULL and opStatus is not OK, this method sends an error 128// event to the client application; otherwise, if exception is not NULL and 129// opStatus is not OK, this method throws the given exception to the client 130// application. 131static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message) 132{ 133 if (exception == NULL) { // Don't throw exception. Instead, send an event. 134 if (opStatus != (status_t) OK) { 135 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 136 if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0); 137 } 138 } else { // Throw exception! 139 if ( opStatus == (status_t) INVALID_OPERATION ) { 140 jniThrowException(env, "java/lang/IllegalStateException", NULL); 141 } else if ( opStatus != (status_t) OK ) { 142 if (strlen(message) > 230) { 143 // if the message is too long, don't bother displaying the status code 144 jniThrowException( env, exception, message); 145 } else { 146 char msg[256]; 147 // append the status code to the message 148 sprintf(msg, "%s: status=0x%X", message, opStatus); 149 jniThrowException( env, exception, msg); 150 } 151 } 152 } 153} 154 155static void 156android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path) 157{ 158 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 159 if (mp == NULL ) { 160 jniThrowException(env, "java/lang/IllegalStateException", NULL); 161 return; 162 } 163 164 if (path == NULL) { 165 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 166 return; 167 } 168 169 const char *pathStr = env->GetStringUTFChars(path, NULL); 170 if (pathStr == NULL) { // Out of memory 171 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 172 return; 173 } 174 status_t opStatus = mp->setDataSource(pathStr); 175 176 // Make sure that local ref is released before a potential exception 177 env->ReleaseStringUTFChars(path, pathStr); 178 process_media_player_call( env, thiz, opStatus, "java/io/IOException", "setDataSource failed." ); 179} 180 181static void 182android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) 183{ 184 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 185 if (mp == NULL ) { 186 jniThrowException(env, "java/lang/IllegalStateException", NULL); 187 return; 188 } 189 190 if (fileDescriptor == NULL) { 191 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 192 return; 193 } 194 int fd = getParcelFileDescriptorFD(env, fileDescriptor); 195 process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); 196} 197 198 199static void 200android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) 201{ 202 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 203 if (mp == NULL ) { 204 jniThrowException(env, "java/lang/IllegalStateException", NULL); 205 return; 206 } 207 jobject surface = env->GetObjectField(thiz, fields.surface); 208 if (surface != NULL) { 209 const sp<Surface>& native_surface = get_surface(env, surface); 210 //LOGI("prepare: surface=%p (id=%d)", 211 // native_surface.get(), native_surface->ID()); 212 mp->setVideoSurface(native_surface); 213 } 214 process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); 215} 216 217static void 218android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz) 219{ 220 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 221 if (mp == NULL ) { 222 jniThrowException(env, "java/lang/IllegalStateException", NULL); 223 return; 224 } 225 jobject surface = env->GetObjectField(thiz, fields.surface); 226 if (surface != NULL) { 227 const sp<Surface>& native_surface = get_surface(env, surface); 228 LOGI("prepareAsync: surface=%p (id=%d)", 229 native_surface.get(), native_surface->ID()); 230 mp->setVideoSurface(native_surface); 231 } 232 process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." ); 233} 234 235static void 236android_media_MediaPlayer_start(JNIEnv *env, jobject thiz) 237{ 238 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 239 if (mp == NULL ) { 240 jniThrowException(env, "java/lang/IllegalStateException", NULL); 241 return; 242 } 243 process_media_player_call( env, thiz, mp->start(), NULL, NULL ); 244} 245 246static void 247android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz) 248{ 249 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 250 if (mp == NULL ) { 251 jniThrowException(env, "java/lang/IllegalStateException", NULL); 252 return; 253 } 254 process_media_player_call( env, thiz, mp->stop(), NULL, NULL ); 255} 256 257static void 258android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz) 259{ 260 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 261 if (mp == NULL ) { 262 jniThrowException(env, "java/lang/IllegalStateException", NULL); 263 return; 264 } 265 process_media_player_call( env, thiz, mp->pause(), NULL, NULL ); 266} 267 268static jboolean 269android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz) 270{ 271 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 272 if (mp == NULL ) { 273 jniThrowException(env, "java/lang/IllegalStateException", NULL); 274 return false; 275 } 276 return mp->isPlaying(); 277} 278 279static void 280android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec) 281{ 282 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 283 if (mp == NULL ) { 284 jniThrowException(env, "java/lang/IllegalStateException", NULL); 285 return; 286 } 287 process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL ); 288} 289 290static int 291android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz) 292{ 293 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 294 if (mp == NULL ) { 295 jniThrowException(env, "java/lang/IllegalStateException", NULL); 296 return 0; 297 } 298 int w; 299 if (0 == mp->getVideoWidth(&w)) 300 return w; 301 return 0; 302} 303 304static int 305android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz) 306{ 307 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 308 if (mp == NULL ) { 309 jniThrowException(env, "java/lang/IllegalStateException", NULL); 310 return 0; 311 } 312 int h; 313 if (0 == mp->getVideoHeight(&h)) 314 return h; 315 return 0; 316} 317 318 319static int 320android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz) 321{ 322 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 323 if (mp == NULL ) { 324 jniThrowException(env, "java/lang/IllegalStateException", NULL); 325 return 0; 326 } 327 int msec; 328 process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL ); 329 return msec; 330} 331 332static int 333android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz) 334{ 335 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 336 if (mp == NULL ) { 337 jniThrowException(env, "java/lang/IllegalStateException", NULL); 338 return 0; 339 } 340 int msec; 341 process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL ); 342 return msec; 343} 344 345static void 346android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) 347{ 348 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 349 if (mp == NULL ) { 350 jniThrowException(env, "java/lang/IllegalStateException", NULL); 351 return; 352 } 353 process_media_player_call( env, thiz, mp->reset(), NULL, NULL ); 354} 355 356static void 357android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype) 358{ 359 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 360 if (mp == NULL ) { 361 jniThrowException(env, "java/lang/IllegalStateException", NULL); 362 return; 363 } 364 process_media_player_call( env, thiz, mp->setAudioStreamType(streamtype) , NULL, NULL ); 365} 366 367static void 368android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping) 369{ 370 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 371 if (mp == NULL ) { 372 jniThrowException(env, "java/lang/IllegalStateException", NULL); 373 return; 374 } 375 process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL ); 376} 377 378static void 379android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume) 380{ 381 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 382 if (mp == NULL ) { 383 jniThrowException(env, "java/lang/IllegalStateException", NULL); 384 return; 385 } 386 process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL ); 387} 388 389// FIXME: deprecated 390static jobject 391android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec) 392{ 393 return NULL; 394} 395 396static void 397android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) 398{ 399 LOGV("native_setup"); 400 sp<MediaPlayer> mp = new MediaPlayer(); 401 if (mp == NULL) { 402 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 403 return; 404 } 405 406 // create new listener and give it to MediaPlayer 407 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); 408 mp->setListener(listener); 409 410 // Stow our new C++ MediaPlayer in an opaque field in the Java object. 411 setMediaPlayer(env, thiz, mp); 412} 413 414static void 415android_media_MediaPlayer_release(JNIEnv *env, jobject thiz) 416{ 417 LOGV("release"); 418 sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0); 419 if (mp != NULL) { 420 // this prevents native callbacks after the object is released 421 mp->setListener(0); 422 mp->disconnect(); 423 } 424} 425 426static void 427android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz) 428{ 429 LOGV("native_finalize"); 430 android_media_MediaPlayer_release(env, thiz); 431} 432 433// ---------------------------------------------------------------------------- 434 435static JNINativeMethod gMethods[] = { 436 {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, 437 {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, 438 {"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, 439 {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, 440 {"_start", "()V", (void *)android_media_MediaPlayer_start}, 441 {"_stop", "()V", (void *)android_media_MediaPlayer_stop}, 442 {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth}, 443 {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight}, 444 {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo}, 445 {"_pause", "()V", (void *)android_media_MediaPlayer_pause}, 446 {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying}, 447 {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition}, 448 {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration}, 449 {"_release", "()V", (void *)android_media_MediaPlayer_release}, 450 {"_reset", "()V", (void *)android_media_MediaPlayer_reset}, 451 {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType}, 452 {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping}, 453 {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume}, 454 {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt}, 455 {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, 456 {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, 457}; 458 459static const char* const kClassPathName = "android/media/MediaPlayer"; 460 461static int register_android_media_MediaPlayer(JNIEnv *env) 462{ 463 jclass clazz; 464 465 clazz = env->FindClass("android/media/MediaPlayer"); 466 if (clazz == NULL) { 467 LOGE("Can't find android/media/MediaPlayer"); 468 return -1; 469 } 470 471 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 472 if (fields.context == NULL) { 473 LOGE("Can't find MediaPlayer.mNativeContext"); 474 return -1; 475 } 476 477 fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", 478 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 479 if (fields.post_event == NULL) { 480 LOGE("Can't find MediaPlayer.postEventFromNative"); 481 return -1; 482 } 483 484 fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;"); 485 if (fields.surface == NULL) { 486 LOGE("Can't find MediaPlayer.mSurface"); 487 return -1; 488 } 489 490 jclass surface = env->FindClass("android/view/Surface"); 491 if (surface == NULL) { 492 LOGE("Can't find android/view/Surface"); 493 return -1; 494 } 495 496 fields.surface_native = env->GetFieldID(surface, "mSurface", "I"); 497 if (fields.surface_native == NULL) { 498 LOGE("Can't find Surface fields"); 499 return -1; 500 } 501 502 return AndroidRuntime::registerNativeMethods(env, 503 "android/media/MediaPlayer", gMethods, NELEM(gMethods)); 504} 505 506extern int register_android_media_MediaRecorder(JNIEnv *env); 507extern int register_android_media_MediaScanner(JNIEnv *env); 508extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); 509 510jint JNI_OnLoad(JavaVM* vm, void* reserved) 511{ 512 JNIEnv* env = NULL; 513 jint result = -1; 514 515 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 516 LOGE("ERROR: GetEnv failed\n"); 517 goto bail; 518 } 519 assert(env != NULL); 520 521 if (register_android_media_MediaPlayer(env) < 0) { 522 LOGE("ERROR: MediaPlayer native registration failed\n"); 523 goto bail; 524 } 525 526 if (register_android_media_MediaRecorder(env) < 0) { 527 LOGE("ERROR: MediaRecorder native registration failed\n"); 528 goto bail; 529 } 530 531 if (register_android_media_MediaScanner(env) < 0) { 532 LOGE("ERROR: MediaScanner native registration failed\n"); 533 goto bail; 534 } 535 536 if (register_android_media_MediaMetadataRetriever(env) < 0) { 537 LOGE("ERROR: MediaMetadataRetriever native registration failed\n"); 538 goto bail; 539 } 540 541 /* success -- return valid version number */ 542 result = JNI_VERSION_1_4; 543 544bail: 545 return result; 546} 547 548// KTHXBYE 549 550