summaryrefslogtreecommitdiffstats
path: root/media/jni/android_media_MediaPlayer.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch)
tree35051494d2af230dce54d6b31c6af8fc24091316 /media/jni/android_media_MediaPlayer.cpp
downloadframeworks_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.cpp549
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
+