diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /media/jni/android_media_MediaPlayer.cpp | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'media/jni/android_media_MediaPlayer.cpp')
-rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp new file mode 100644 index 0000000..f9f3646 --- /dev/null +++ b/media/jni/android_media_MediaPlayer.cpp @@ -0,0 +1,549 @@ +/* //device/libs/android_runtime/android_media_MediaPlayer.cpp +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MediaPlayer-JNI" +#include "utils/Log.h" + +#include <media/mediaplayer.h> +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <utils/threads.h> +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + + +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- + +struct fields_t { + jfieldID context; + jfieldID surface; + /* actually in android.view.Surface XXX */ + jfieldID surface_native; + + jmethodID post_event; +}; +static fields_t fields; + +static Mutex sLock; + +// ---------------------------------------------------------------------------- +// ref-counted object for callbacks +class JNIMediaPlayerListener: public MediaPlayerListener +{ +public: + JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz); + ~JNIMediaPlayerListener(); + void notify(int msg, int ext1, int ext2); +private: + JNIMediaPlayerListener(); + jclass mClass; // Reference to MediaPlayer class + jobject mObject; // Weak ref to MediaPlayer Java object to call on +}; + +JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz) +{ + + // Hold onto the MediaPlayer class for use in calling the static method + // that posts events to the application thread. + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + LOGE("Can't find android/media/MediaPlayer"); + jniThrowException(env, "java/lang/Exception", NULL); + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the MediaPlayer object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); +} + +JNIMediaPlayerListener::~JNIMediaPlayerListener() +{ + // remove global references + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0); +} + +// ---------------------------------------------------------------------------- + +static sp<Surface> get_surface(JNIEnv* env, jobject clazz) +{ + Surface* const p = (Surface*)env->GetIntField(clazz, fields.surface_native); + return sp<Surface>(p); +} + +static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz) +{ + Mutex::Autolock l(sLock); + MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context); + return sp<MediaPlayer>(p); +} + +static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player) +{ + Mutex::Autolock l(sLock); + sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context); + if (player.get()) { + player->incStrong(thiz); + } + if (old != 0) { + old->decStrong(thiz); + } + env->SetIntField(thiz, fields.context, (int)player.get()); + return old; +} + +// If exception is NULL and opStatus is not OK, this method sends an error +// event to the client application; otherwise, if exception is not NULL and +// opStatus is not OK, this method throws the given exception to the client +// application. +static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message) +{ + if (exception == NULL) { // Don't throw exception. Instead, send an event. + if (opStatus != (status_t) OK) { + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0); + } + } else { // Throw exception! + if ( opStatus == (status_t) INVALID_OPERATION ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + } else if ( opStatus != (status_t) OK ) { + if (strlen(message) > 230) { + // if the message is too long, don't bother displaying the status code + jniThrowException( env, exception, message); + } else { + char msg[256]; + // append the status code to the message + sprintf(msg, "%s: status=0x%X", message, opStatus); + jniThrowException( env, exception, msg); + } + } + } +} + +static void +android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + if (path == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + const char *pathStr = env->GetStringUTFChars(path, NULL); + if (pathStr == NULL) { // Out of memory + jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + return; + } + status_t opStatus = mp->setDataSource(pathStr); + + // Make sure that local ref is released before a potential exception + env->ReleaseStringUTFChars(path, pathStr); + process_media_player_call( env, thiz, opStatus, "java/io/IOException", "setDataSource failed." ); +} + +static void +android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + int fd = getParcelFileDescriptorFD(env, fileDescriptor); + process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); +} + + +static void +android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + jobject surface = env->GetObjectField(thiz, fields.surface); + if (surface != NULL) { + const sp<Surface>& native_surface = get_surface(env, surface); + //LOGI("prepare: surface=%p (id=%d)", + // native_surface.get(), native_surface->ID()); + mp->setVideoSurface(native_surface); + } + process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); +} + +static void +android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + jobject surface = env->GetObjectField(thiz, fields.surface); + if (surface != NULL) { + const sp<Surface>& native_surface = get_surface(env, surface); + LOGI("prepareAsync: surface=%p (id=%d)", + native_surface.get(), native_surface->ID()); + mp->setVideoSurface(native_surface); + } + process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." ); +} + +static void +android_media_MediaPlayer_start(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->start(), NULL, NULL ); +} + +static void +android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->stop(), NULL, NULL ); +} + +static void +android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->pause(), NULL, NULL ); +} + +static jboolean +android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return false; + } + return mp->isPlaying(); +} + +static void +android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL ); +} + +static int +android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return 0; + } + int w; + if (0 == mp->getVideoWidth(&w)) + return w; + return 0; +} + +static int +android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return 0; + } + int h; + if (0 == mp->getVideoHeight(&h)) + return h; + return 0; +} + + +static int +android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return 0; + } + int msec; + process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL ); + return msec; +} + +static int +android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return 0; + } + int msec; + process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL ); + return msec; +} + +static void +android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->reset(), NULL, NULL ); +} + +static void +android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->setAudioStreamType(streamtype) , NULL, NULL ); +} + +static void +android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL ); +} + +static void +android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume) +{ + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL ); +} + +// FIXME: deprecated +static jobject +android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec) +{ + return NULL; +} + +static void +android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) +{ + LOGV("native_setup"); + sp<MediaPlayer> mp = new MediaPlayer(); + if (mp == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + return; + } + + // create new listener and give it to MediaPlayer + sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); + mp->setListener(listener); + + // Stow our new C++ MediaPlayer in an opaque field in the Java object. + setMediaPlayer(env, thiz, mp); +} + +static void +android_media_MediaPlayer_release(JNIEnv *env, jobject thiz) +{ + LOGV("release"); + sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0); + if (mp != NULL) { + // this prevents native callbacks after the object is released + mp->setListener(0); + mp->disconnect(); + } +} + +static void +android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz) +{ + LOGV("native_finalize"); + android_media_MediaPlayer_release(env, thiz); +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, + {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, + {"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, + {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, + {"_start", "()V", (void *)android_media_MediaPlayer_start}, + {"_stop", "()V", (void *)android_media_MediaPlayer_stop}, + {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth}, + {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight}, + {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo}, + {"_pause", "()V", (void *)android_media_MediaPlayer_pause}, + {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying}, + {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition}, + {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration}, + {"_release", "()V", (void *)android_media_MediaPlayer_release}, + {"_reset", "()V", (void *)android_media_MediaPlayer_reset}, + {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType}, + {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping}, + {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume}, + {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt}, + {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, + {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, +}; + +static const char* const kClassPathName = "android/media/MediaPlayer"; + +static int register_android_media_MediaPlayer(JNIEnv *env) +{ + jclass clazz; + + clazz = env->FindClass("android/media/MediaPlayer"); + if (clazz == NULL) { + LOGE("Can't find android/media/MediaPlayer"); + return -1; + } + + fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (fields.context == NULL) { + LOGE("Can't find MediaPlayer.mNativeContext"); + return -1; + } + + fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", + "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + if (fields.post_event == NULL) { + LOGE("Can't find MediaPlayer.postEventFromNative"); + return -1; + } + + fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;"); + if (fields.surface == NULL) { + LOGE("Can't find MediaPlayer.mSurface"); + return -1; + } + + jclass surface = env->FindClass("android/view/Surface"); + if (surface == NULL) { + LOGE("Can't find android/view/Surface"); + return -1; + } + + fields.surface_native = env->GetFieldID(surface, "mSurface", "I"); + if (fields.surface_native == NULL) { + LOGE("Can't find Surface fields"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/media/MediaPlayer", gMethods, NELEM(gMethods)); +} + +extern int register_android_media_MediaRecorder(JNIEnv *env); +extern int register_android_media_MediaScanner(JNIEnv *env); +extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); + +jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + LOGE("ERROR: GetEnv failed\n"); + goto bail; + } + assert(env != NULL); + + if (register_android_media_MediaPlayer(env) < 0) { + LOGE("ERROR: MediaPlayer native registration failed\n"); + goto bail; + } + + if (register_android_media_MediaRecorder(env) < 0) { + LOGE("ERROR: MediaRecorder native registration failed\n"); + goto bail; + } + + if (register_android_media_MediaScanner(env) < 0) { + LOGE("ERROR: MediaScanner native registration failed\n"); + goto bail; + } + + if (register_android_media_MediaMetadataRetriever(env) < 0) { + LOGE("ERROR: MediaMetadataRetriever native registration failed\n"); + goto bail; + } + + /* success -- return valid version number */ + result = JNI_VERSION_1_4; + +bail: + return result; +} + +// KTHXBYE + |