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