android_media_MediaPlayer.cpp revision 8b0b174198793cabb2b3fcc015f9bfdc9d5082b5
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 201static void setVideoSurface(const sp<MediaPlayer>& mp, JNIEnv *env, jobject thiz) 202{ 203 jobject surface = env->GetObjectField(thiz, fields.surface); 204 if (surface != NULL) { 205 const sp<Surface>& native_surface = get_surface(env, surface); 206 LOGV("prepare: surface=%p (id=%d)", 207 native_surface.get(), native_surface->ID()); 208 mp->setVideoSurface(native_surface); 209 } 210} 211 212static void 213android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz) 214{ 215 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 216 if (mp == NULL ) { 217 jniThrowException(env, "java/lang/IllegalStateException", NULL); 218 return; 219 } 220 setVideoSurface(mp, env, thiz); 221} 222 223static void 224android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) 225{ 226 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 227 if (mp == NULL ) { 228 jniThrowException(env, "java/lang/IllegalStateException", NULL); 229 return; 230 } 231 setVideoSurface(mp, env, thiz); 232 process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); 233} 234 235static void 236android_media_MediaPlayer_prepareAsync(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 jobject surface = env->GetObjectField(thiz, fields.surface); 244 if (surface != NULL) { 245 const sp<Surface>& native_surface = get_surface(env, surface); 246 LOGV("prepareAsync: surface=%p (id=%d)", 247 native_surface.get(), native_surface->ID()); 248 mp->setVideoSurface(native_surface); 249 } 250 process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." ); 251} 252 253static void 254android_media_MediaPlayer_start(JNIEnv *env, jobject thiz) 255{ 256 LOGV("start"); 257 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 258 if (mp == NULL ) { 259 jniThrowException(env, "java/lang/IllegalStateException", NULL); 260 return; 261 } 262 process_media_player_call( env, thiz, mp->start(), NULL, NULL ); 263} 264 265static void 266android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz) 267{ 268 LOGV("stop"); 269 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 270 if (mp == NULL ) { 271 jniThrowException(env, "java/lang/IllegalStateException", NULL); 272 return; 273 } 274 process_media_player_call( env, thiz, mp->stop(), NULL, NULL ); 275} 276 277static void 278android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz) 279{ 280 LOGV("pause"); 281 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 282 if (mp == NULL ) { 283 jniThrowException(env, "java/lang/IllegalStateException", NULL); 284 return; 285 } 286 process_media_player_call( env, thiz, mp->pause(), NULL, NULL ); 287} 288 289static jboolean 290android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz) 291{ 292 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 293 if (mp == NULL ) { 294 jniThrowException(env, "java/lang/IllegalStateException", NULL); 295 return false; 296 } 297 const jboolean is_playing = mp->isPlaying(); 298 299 LOGV("isPlaying: %d", is_playing); 300 return is_playing; 301} 302 303static void 304android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec) 305{ 306 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 307 if (mp == NULL ) { 308 jniThrowException(env, "java/lang/IllegalStateException", NULL); 309 return; 310 } 311 LOGV("seekTo: %d(msec)", msec); 312 process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL ); 313} 314 315static int 316android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz) 317{ 318 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 319 if (mp == NULL ) { 320 jniThrowException(env, "java/lang/IllegalStateException", NULL); 321 return 0; 322 } 323 int w; 324 if (0 != mp->getVideoWidth(&w)) { 325 LOGE("getVideoWidth failed"); 326 w = 0; 327 } 328 LOGV("getVideoWidth: %d", w); 329 return w; 330} 331 332static int 333android_media_MediaPlayer_getVideoHeight(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 h; 341 if (0 != mp->getVideoHeight(&h)) { 342 LOGE("getVideoHeight failed"); 343 h = 0; 344 } 345 LOGV("getVideoHeight: %d", h); 346 return h; 347} 348 349 350static int 351android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz) 352{ 353 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 354 if (mp == NULL ) { 355 jniThrowException(env, "java/lang/IllegalStateException", NULL); 356 return 0; 357 } 358 int msec; 359 process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL ); 360 LOGV("getCurrentPosition: %d (msec)", msec); 361 return msec; 362} 363 364static int 365android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz) 366{ 367 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 368 if (mp == NULL ) { 369 jniThrowException(env, "java/lang/IllegalStateException", NULL); 370 return 0; 371 } 372 int msec; 373 process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL ); 374 LOGV("getDuration: %d (msec)", msec); 375 return msec; 376} 377 378static void 379android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) 380{ 381 LOGV("reset"); 382 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 383 if (mp == NULL ) { 384 jniThrowException(env, "java/lang/IllegalStateException", NULL); 385 return; 386 } 387 process_media_player_call( env, thiz, mp->reset(), NULL, NULL ); 388} 389 390static void 391android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype) 392{ 393 LOGV("setAudioStreamType: %d", streamtype); 394 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 395 if (mp == NULL ) { 396 jniThrowException(env, "java/lang/IllegalStateException", NULL); 397 return; 398 } 399 process_media_player_call( env, thiz, mp->setAudioStreamType(streamtype) , NULL, NULL ); 400} 401 402static void 403android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping) 404{ 405 LOGV("setLooping: %d", looping); 406 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 407 if (mp == NULL ) { 408 jniThrowException(env, "java/lang/IllegalStateException", NULL); 409 return; 410 } 411 process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL ); 412} 413 414static jboolean 415android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz) 416{ 417 LOGV("isLooping"); 418 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 419 if (mp == NULL ) { 420 jniThrowException(env, "java/lang/IllegalStateException", NULL); 421 return false; 422 } 423 return mp->isLooping(); 424} 425 426static void 427android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume) 428{ 429 LOGV("setVolume: left %f right %f", leftVolume, rightVolume); 430 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); 431 if (mp == NULL ) { 432 jniThrowException(env, "java/lang/IllegalStateException", NULL); 433 return; 434 } 435 process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL ); 436} 437 438// FIXME: deprecated 439static jobject 440android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec) 441{ 442 return NULL; 443} 444 445static void 446android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) 447{ 448 LOGV("native_setup"); 449 sp<MediaPlayer> mp = new MediaPlayer(); 450 if (mp == NULL) { 451 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 452 return; 453 } 454 455 // create new listener and give it to MediaPlayer 456 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); 457 mp->setListener(listener); 458 459 // Stow our new C++ MediaPlayer in an opaque field in the Java object. 460 setMediaPlayer(env, thiz, mp); 461} 462 463static void 464android_media_MediaPlayer_release(JNIEnv *env, jobject thiz) 465{ 466 LOGV("release"); 467 sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0); 468 if (mp != NULL) { 469 // this prevents native callbacks after the object is released 470 mp->setListener(0); 471 mp->disconnect(); 472 } 473} 474 475static void 476android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz) 477{ 478 LOGV("native_finalize"); 479 android_media_MediaPlayer_release(env, thiz); 480} 481 482// ---------------------------------------------------------------------------- 483 484static JNINativeMethod gMethods[] = { 485 {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, 486 {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, 487 {"_setVideoSurface", "()V", (void *)android_media_MediaPlayer_setVideoSurface}, 488 {"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, 489 {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, 490 {"_start", "()V", (void *)android_media_MediaPlayer_start}, 491 {"_stop", "()V", (void *)android_media_MediaPlayer_stop}, 492 {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth}, 493 {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight}, 494 {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo}, 495 {"_pause", "()V", (void *)android_media_MediaPlayer_pause}, 496 {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying}, 497 {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition}, 498 {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration}, 499 {"_release", "()V", (void *)android_media_MediaPlayer_release}, 500 {"_reset", "()V", (void *)android_media_MediaPlayer_reset}, 501 {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType}, 502 {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping}, 503 {"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping}, 504 {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume}, 505 {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt}, 506 {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, 507 {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, 508}; 509 510static const char* const kClassPathName = "android/media/MediaPlayer"; 511 512static int register_android_media_MediaPlayer(JNIEnv *env) 513{ 514 jclass clazz; 515 516 clazz = env->FindClass("android/media/MediaPlayer"); 517 if (clazz == NULL) { 518 LOGE("Can't find android/media/MediaPlayer"); 519 return -1; 520 } 521 522 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 523 if (fields.context == NULL) { 524 LOGE("Can't find MediaPlayer.mNativeContext"); 525 return -1; 526 } 527 528 fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", 529 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 530 if (fields.post_event == NULL) { 531 LOGE("Can't find MediaPlayer.postEventFromNative"); 532 return -1; 533 } 534 535 fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;"); 536 if (fields.surface == NULL) { 537 LOGE("Can't find MediaPlayer.mSurface"); 538 return -1; 539 } 540 541 jclass surface = env->FindClass("android/view/Surface"); 542 if (surface == NULL) { 543 LOGE("Can't find android/view/Surface"); 544 return -1; 545 } 546 547 fields.surface_native = env->GetFieldID(surface, "mSurface", "I"); 548 if (fields.surface_native == NULL) { 549 LOGE("Can't find Surface fields"); 550 return -1; 551 } 552 553 return AndroidRuntime::registerNativeMethods(env, 554 "android/media/MediaPlayer", gMethods, NELEM(gMethods)); 555} 556 557extern int register_android_media_MediaRecorder(JNIEnv *env); 558extern int register_android_media_MediaScanner(JNIEnv *env); 559extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); 560extern int register_android_media_AmrInputStream(JNIEnv *env); 561extern int register_android_media_ResampleInputStream(JNIEnv *env); 562 563jint JNI_OnLoad(JavaVM* vm, void* reserved) 564{ 565 JNIEnv* env = NULL; 566 jint result = -1; 567 568 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 569 LOGE("ERROR: GetEnv failed\n"); 570 goto bail; 571 } 572 assert(env != NULL); 573 574 if (register_android_media_MediaPlayer(env) < 0) { 575 LOGE("ERROR: MediaPlayer native registration failed\n"); 576 goto bail; 577 } 578 579 if (register_android_media_MediaRecorder(env) < 0) { 580 LOGE("ERROR: MediaRecorder native registration failed\n"); 581 goto bail; 582 } 583 584 if (register_android_media_MediaScanner(env) < 0) { 585 LOGE("ERROR: MediaScanner native registration failed\n"); 586 goto bail; 587 } 588 589 if (register_android_media_MediaMetadataRetriever(env) < 0) { 590 LOGE("ERROR: MediaMetadataRetriever native registration failed\n"); 591 goto bail; 592 } 593 594 if (register_android_media_AmrInputStream(env) < 0) { 595 LOGE("ERROR: AmrInputStream native registration failed\n"); 596 goto bail; 597 } 598 599 if (register_android_media_ResampleInputStream(env) < 0) { 600 LOGE("ERROR: ResampleInputStream native registration failed\n"); 601 goto bail; 602 } 603 604 /* success -- return valid version number */ 605 result = JNI_VERSION_1_4; 606 607bail: 608 return result; 609} 610 611// KTHXBYE 612