| /* //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 <media/MediaPlayerInterface.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" |
| #include "utils/Errors.h" // for status_t |
| #include "utils/KeyedVector.h" |
| #include "utils/String8.h" |
| #include "android_util_Binder.h" |
| #include <binder/Parcel.h> |
| #include <surfaceflinger/Surface.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 Surface* get_surface(JNIEnv* env, jobject clazz) |
| { |
| return (Surface*)env->GetIntField(clazz, fields.surface_native); |
| } |
| |
| 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_setDataSourceAndHeaders( |
| JNIEnv *env, jobject thiz, jstring path, jobject headers) { |
| 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; |
| } |
| |
| // headers is a Map<String, String>. |
| // We build a similar KeyedVector out of it. |
| KeyedVector<String8, String8> headersVector; |
| if (headers) { |
| // Get the Map's entry Set. |
| jclass mapClass = env->FindClass("java/util/Map"); |
| |
| jmethodID entrySet = |
| env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); |
| |
| jobject set = env->CallObjectMethod(headers, entrySet); |
| // Obtain an iterator over the Set |
| jclass setClass = env->FindClass("java/util/Set"); |
| |
| jmethodID iterator = |
| env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); |
| |
| jobject iter = env->CallObjectMethod(set, iterator); |
| // Get the Iterator method IDs |
| jclass iteratorClass = env->FindClass("java/util/Iterator"); |
| jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); |
| |
| jmethodID next = |
| env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); |
| |
| // Get the Entry class method IDs |
| jclass entryClass = env->FindClass("java/util/Map$Entry"); |
| |
| jmethodID getKey = |
| env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); |
| |
| jmethodID getValue = |
| env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); |
| |
| // Iterate over the entry Set |
| while (env->CallBooleanMethod(iter, hasNext)) { |
| jobject entry = env->CallObjectMethod(iter, next); |
| jstring key = (jstring) env->CallObjectMethod(entry, getKey); |
| jstring value = (jstring) env->CallObjectMethod(entry, getValue); |
| |
| const char* keyStr = env->GetStringUTFChars(key, NULL); |
| if (!keyStr) { // Out of memory |
| jniThrowException( |
| env, "java/lang/RuntimeException", "Out of memory"); |
| return; |
| } |
| |
| const char* valueStr = env->GetStringUTFChars(value, NULL); |
| if (!valueStr) { // Out of memory |
| jniThrowException( |
| env, "java/lang/RuntimeException", "Out of memory"); |
| return; |
| } |
| |
| headersVector.add(String8(keyStr), String8(valueStr)); |
| |
| env->DeleteLocalRef(entry); |
| env->ReleaseStringUTFChars(key, keyStr); |
| env->DeleteLocalRef(key); |
| env->ReleaseStringUTFChars(value, valueStr); |
| env->DeleteLocalRef(value); |
| } |
| |
| env->DeleteLocalRef(entryClass); |
| env->DeleteLocalRef(iteratorClass); |
| env->DeleteLocalRef(iter); |
| env->DeleteLocalRef(setClass); |
| env->DeleteLocalRef(set); |
| env->DeleteLocalRef(mapClass); |
| } |
| |
| LOGV("setDataSource: path %s", pathStr); |
| status_t opStatus = |
| mp->setDataSource( |
| String8(pathStr), |
| headers ? &headersVector : NULL); |
| |
| // 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_setDataSource(JNIEnv *env, jobject thiz, jstring path) |
| { |
| android_media_MediaPlayer_setDataSourceAndHeaders(env, thiz, path, 0); |
| } |
| |
| 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); |
| LOGV("setDataSourceFD: fd %d", fd); |
| process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); |
| } |
| |
| static void setVideoSurface(const sp<MediaPlayer>& mp, JNIEnv *env, jobject thiz) |
| { |
| jobject surface = env->GetObjectField(thiz, fields.surface); |
| if (surface != NULL) { |
| const sp<Surface> native_surface = get_surface(env, surface); |
| LOGV("prepare: surface=%p (id=%d)", |
| native_surface.get(), native_surface->ID()); |
| mp->setVideoSurface(native_surface); |
| } |
| } |
| |
| static void |
| android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz) |
| { |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return; |
| } |
| setVideoSurface(mp, env, thiz); |
| } |
| |
| 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; |
| } |
| setVideoSurface(mp, env, thiz); |
| 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); |
| LOGV("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) |
| { |
| LOGV("start"); |
| 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) |
| { |
| LOGV("stop"); |
| 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) |
| { |
| LOGV("pause"); |
| 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; |
| } |
| const jboolean is_playing = mp->isPlaying(); |
| |
| LOGV("isPlaying: %d", is_playing); |
| return is_playing; |
| } |
| |
| 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; |
| } |
| LOGV("seekTo: %d(msec)", msec); |
| 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)) { |
| LOGE("getVideoWidth failed"); |
| w = 0; |
| } |
| LOGV("getVideoWidth: %d", w); |
| return w; |
| } |
| |
| 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)) { |
| LOGE("getVideoHeight failed"); |
| h = 0; |
| } |
| LOGV("getVideoHeight: %d", h); |
| return h; |
| } |
| |
| |
| 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 ); |
| LOGV("getCurrentPosition: %d (msec)", msec); |
| 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 ); |
| LOGV("getDuration: %d (msec)", msec); |
| return msec; |
| } |
| |
| static void |
| android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) |
| { |
| LOGV("reset"); |
| 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) |
| { |
| LOGV("setAudioStreamType: %d", 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) |
| { |
| LOGV("setLooping: %d", 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 jboolean |
| android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz) |
| { |
| LOGV("isLooping"); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return false; |
| } |
| return mp->isLooping(); |
| } |
| |
| static void |
| android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume) |
| { |
| LOGV("setVolume: left %f right %f", leftVolume, 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; |
| } |
| |
| |
| // Sends the request and reply parcels to the media player via the |
| // binder interface. |
| static jint |
| android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz, |
| jobject java_request, jobject java_reply) |
| { |
| sp<MediaPlayer> media_player = getMediaPlayer(env, thiz); |
| if (media_player == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return UNKNOWN_ERROR; |
| } |
| |
| |
| Parcel *request = parcelForJavaObject(env, java_request); |
| Parcel *reply = parcelForJavaObject(env, java_reply); |
| |
| // Don't use process_media_player_call which use the async loop to |
| // report errors, instead returns the status. |
| return media_player->invoke(*request, reply); |
| } |
| |
| // Sends the new filter to the client. |
| static jint |
| android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request) |
| { |
| sp<MediaPlayer> media_player = getMediaPlayer(env, thiz); |
| if (media_player == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return UNKNOWN_ERROR; |
| } |
| |
| Parcel *filter = parcelForJavaObject(env, request); |
| |
| if (filter == NULL ) { |
| jniThrowException(env, "java/lang/RuntimeException", "Filter is null"); |
| return UNKNOWN_ERROR; |
| } |
| |
| return media_player->setMetadataFilter(*filter); |
| } |
| |
| static jboolean |
| android_media_MediaPlayer_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only, |
| jboolean apply_filter, jobject reply) |
| { |
| sp<MediaPlayer> media_player = getMediaPlayer(env, thiz); |
| if (media_player == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return false; |
| } |
| |
| Parcel *metadata = parcelForJavaObject(env, reply); |
| |
| if (metadata == NULL ) { |
| jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null"); |
| return false; |
| } |
| |
| metadata->freeData(); |
| // On return metadata is positioned at the beginning of the |
| // metadata. Note however that the parcel actually starts with the |
| // return code so you should not rewind the parcel using |
| // setDataPosition(0). |
| return media_player->getMetadata(update_only, apply_filter, metadata) == OK; |
| } |
| |
| // This function gets some field IDs, which in turn causes class initialization. |
| // It is called from a static block in MediaPlayer, which won't run until the |
| // first time an instance of this class is used. |
| static void |
| android_media_MediaPlayer_native_init(JNIEnv *env) |
| { |
| jclass clazz; |
| |
| clazz = env->FindClass("android/media/MediaPlayer"); |
| if (clazz == NULL) { |
| jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaPlayer"); |
| return; |
| } |
| |
| fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); |
| if (fields.context == NULL) { |
| jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mNativeContext"); |
| return; |
| } |
| |
| fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", |
| "(Ljava/lang/Object;IIILjava/lang/Object;)V"); |
| if (fields.post_event == NULL) { |
| jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.postEventFromNative"); |
| return; |
| } |
| |
| fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;"); |
| if (fields.surface == NULL) { |
| jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mSurface"); |
| return; |
| } |
| |
| jclass surface = env->FindClass("android/view/Surface"); |
| if (surface == NULL) { |
| jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface"); |
| return; |
| } |
| |
| fields.surface_native = env->GetFieldID(surface, "mSurface", "I"); |
| if (fields.surface_native == NULL) { |
| jniThrowException(env, "java/lang/RuntimeException", "Can't find Surface.mSurface"); |
| return; |
| } |
| } |
| |
| 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 jint |
| android_media_MediaPlayer_snoop(JNIEnv* env, jobject thiz, jobject data, jint kind) { |
| jshort* ar = (jshort*)env->GetPrimitiveArrayCritical((jarray)data, 0); |
| jsize len = env->GetArrayLength((jarray)data); |
| int ret = 0; |
| if (ar) { |
| ret = MediaPlayer::snoop(ar, len, kind); |
| env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0); |
| } |
| return ret; |
| } |
| |
| static jint |
| android_media_MediaPlayer_native_suspend_resume( |
| JNIEnv *env, jobject thiz, jboolean isSuspend) { |
| LOGV("suspend_resume(%d)", isSuspend); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return UNKNOWN_ERROR; |
| } |
| |
| return isSuspend ? mp->suspend() : mp->resume(); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| static JNINativeMethod gMethods[] = { |
| {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, |
| {"setDataSource", "(Ljava/lang/String;Ljava/util/Map;)V",(void *)android_media_MediaPlayer_setDataSourceAndHeaders}, |
| {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, |
| {"_setVideoSurface", "()V", (void *)android_media_MediaPlayer_setVideoSurface}, |
| {"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}, |
| {"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping}, |
| {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume}, |
| {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt}, |
| {"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke}, |
| {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter}, |
| {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata}, |
| {"native_init", "()V", (void *)android_media_MediaPlayer_native_init}, |
| {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, |
| {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, |
| {"snoop", "([SI)I", (void *)android_media_MediaPlayer_snoop}, |
| {"native_suspend_resume", "(Z)I", (void *)android_media_MediaPlayer_native_suspend_resume}, |
| }; |
| |
| static const char* const kClassPathName = "android/media/MediaPlayer"; |
| |
| // This function only registers the native methods |
| static int register_android_media_MediaPlayer(JNIEnv *env) |
| { |
| return AndroidRuntime::registerNativeMethods(env, |
| "android/media/MediaPlayer", gMethods, NELEM(gMethods)); |
| } |
| |
| extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); |
| extern int register_android_media_MediaRecorder(JNIEnv *env); |
| extern int register_android_media_MediaScanner(JNIEnv *env); |
| extern int register_android_media_ResampleInputStream(JNIEnv *env); |
| extern int register_android_media_MediaProfiles(JNIEnv *env); |
| |
| #ifndef NO_OPENCORE |
| extern int register_android_media_AmrInputStream(JNIEnv *env); |
| #endif |
| |
| 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; |
| } |
| |
| #ifndef NO_OPENCORE |
| if (register_android_media_AmrInputStream(env) < 0) { |
| LOGE("ERROR: AmrInputStream native registration failed\n"); |
| goto bail; |
| } |
| #endif |
| |
| if (register_android_media_ResampleInputStream(env) < 0) { |
| LOGE("ERROR: ResampleInputStream native registration failed\n"); |
| goto bail; |
| } |
| |
| if (register_android_media_MediaProfiles(env) < 0) { |
| LOGE("ERROR: MediaProfiles native registration failed"); |
| goto bail; |
| } |
| |
| /* success -- return valid version number */ |
| result = JNI_VERSION_1_4; |
| |
| bail: |
| return result; |
| } |
| |
| // KTHXBYE |