| /* |
| ** |
| ** 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 "android_runtime/android_view_Surface.h" |
| #include "utils/Errors.h" // for status_t |
| #include "utils/KeyedVector.h" |
| #include "utils/String8.h" |
| #include "android_media_Utils.h" |
| |
| #include "android_util_Binder.h" |
| #include <binder/Parcel.h> |
| #include <gui/ISurfaceTexture.h> |
| #include <surfaceflinger/Surface.h> |
| #include <binder/IPCThreadState.h> |
| #include <binder/IServiceManager.h> |
| |
| // ---------------------------------------------------------------------------- |
| |
| using namespace android; |
| |
| // ---------------------------------------------------------------------------- |
| |
| struct fields_t { |
| jfieldID context; |
| jfieldID surface_texture; |
| |
| 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(); |
| virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL); |
| 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, const Parcel *obj) |
| { |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| if (obj && obj->dataSize() > 0) { |
| jbyteArray jArray = env->NewByteArray(obj->dataSize()); |
| if (jArray != NULL) { |
| jbyte *nArray = env->GetByteArrayElements(jArray, NULL); |
| memcpy(nArray, obj->data(), obj->dataSize()); |
| env->ReleaseByteArrayElements(jArray, nArray, 0); |
| env->CallStaticVoidMethod(mClass, fields.post_event, mObject, |
| msg, ext1, ext2, jArray); |
| env->DeleteLocalRef(jArray); |
| } |
| } else { |
| env->CallStaticVoidMethod(mClass, fields.post_event, mObject, |
| msg, ext1, ext2, NULL); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| 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) PERMISSION_DENIED ) { |
| jniThrowException(env, "java/lang/SecurityException", 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, |
| jobjectArray keys, jobjectArray values) { |
| |
| 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 *tmp = env->GetStringUTFChars(path, NULL); |
| if (tmp == NULL) { // Out of memory |
| return; |
| } |
| LOGV("setDataSource: path %s", tmp); |
| |
| String8 pathStr(tmp); |
| env->ReleaseStringUTFChars(path, tmp); |
| tmp = NULL; |
| |
| // We build a KeyedVector out of the key and val arrays |
| KeyedVector<String8, String8> headersVector; |
| if (!ConvertKeyValueArraysToKeyedVector( |
| env, keys, values, &headersVector)) { |
| return; |
| } |
| |
| status_t opStatus = |
| mp->setDataSource( |
| pathStr, |
| headersVector.size() > 0? &headersVector : NULL); |
| |
| 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, NULL, NULL); |
| } |
| |
| 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 = jniGetFDFromFileDescriptor(env, fileDescriptor); |
| LOGV("setDataSourceFD: fd %d", fd); |
| process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); |
| } |
| |
| static sp<ISurfaceTexture> |
| getVideoSurfaceTexture(JNIEnv* env, jobject thiz) { |
| ISurfaceTexture * const p = (ISurfaceTexture*)env->GetIntField(thiz, fields.surface_texture); |
| return sp<ISurfaceTexture>(p); |
| } |
| |
| static void |
| decVideoSurfaceRef(JNIEnv *env, jobject thiz) |
| { |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL) { |
| return; |
| } |
| |
| sp<ISurfaceTexture> old_st = getVideoSurfaceTexture(env, thiz); |
| if (old_st != NULL) { |
| old_st->decStrong(thiz); |
| } |
| } |
| |
| static void |
| setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive) |
| { |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL) { |
| if (mediaPlayerMustBeAlive) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| } |
| return; |
| } |
| |
| decVideoSurfaceRef(env, thiz); |
| |
| sp<ISurfaceTexture> new_st; |
| if (jsurface) { |
| sp<Surface> surface(Surface_getSurface(env, jsurface)); |
| if (surface != NULL) { |
| new_st = surface->getSurfaceTexture(); |
| new_st->incStrong(thiz); |
| } else { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "The surface has been released"); |
| return; |
| } |
| } |
| |
| env->SetIntField(thiz, fields.surface_texture, (int)new_st.get()); |
| |
| // This will fail if the media player has not been initialized yet. This |
| // can be the case if setDisplay() on MediaPlayer.java has been called |
| // before setDataSource(). The redundant call to setVideoSurfaceTexture() |
| // in prepare/prepareAsync covers for this case. |
| mp->setVideoSurfaceTexture(new_st); |
| } |
| |
| static void |
| android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface) |
| { |
| setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */); |
| } |
| |
| 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; |
| } |
| |
| // Handle the case where the display surface was set before the mp was |
| // initialized. We try again to make it stick. |
| sp<ISurfaceTexture> st = getVideoSurfaceTexture(env, thiz); |
| mp->setVideoSurfaceTexture(st); |
| |
| 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; |
| } |
| |
| // Handle the case where the display surface was set before the mp was |
| // initialized. We try again to make it stick. |
| sp<ISurfaceTexture> st = getVideoSurfaceTexture(env, thiz); |
| mp->setVideoSurfaceTexture(st); |
| |
| 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) { |
| return; |
| } |
| |
| fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); |
| if (fields.context == NULL) { |
| return; |
| } |
| |
| fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", |
| "(Ljava/lang/Object;IIILjava/lang/Object;)V"); |
| if (fields.post_event == NULL) { |
| return; |
| } |
| |
| fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "I"); |
| if (fields.surface_texture == NULL) { |
| 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"); |
| decVideoSurfaceRef(env, thiz); |
| 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"); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp != NULL) { |
| LOGW("MediaPlayer finalized without being released"); |
| } |
| android_media_MediaPlayer_release(env, thiz); |
| } |
| |
| static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env, jobject thiz, jint sessionId) { |
| LOGV("set_session_id(): %d", sessionId); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return; |
| } |
| process_media_player_call( env, thiz, mp->setAudioSessionId(sessionId), NULL, NULL ); |
| } |
| |
| static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env, jobject thiz) { |
| LOGV("get_session_id()"); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return 0; |
| } |
| |
| return mp->getAudioSessionId(); |
| } |
| |
| static void |
| android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level) |
| { |
| LOGV("setAuxEffectSendLevel: level %f", level); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return; |
| } |
| process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL ); |
| } |
| |
| static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env, jobject thiz, jint effectId) { |
| LOGV("attachAuxEffect(): %d", effectId); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return; |
| } |
| process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL ); |
| } |
| |
| static jint |
| android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject java_reply) |
| { |
| sp<IBinder> binder = defaultServiceManager()->getService(String16("media.player")); |
| sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); |
| if (service.get() == NULL) { |
| jniThrowException(env, "java/lang/RuntimeException", "cannot get MediaPlayerService"); |
| return UNKNOWN_ERROR; |
| } |
| |
| Parcel *reply = parcelForJavaObject(env, java_reply); |
| |
| return service->pullBatteryData(reply); |
| } |
| |
| static jboolean |
| android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request) |
| { |
| LOGV("setParameter: key %d", key); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return false; |
| } |
| |
| Parcel *request = parcelForJavaObject(env, java_request); |
| status_t err = mp->setParameter(key, *request); |
| if (err == OK) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static void |
| android_media_MediaPlayer_getParameter(JNIEnv *env, jobject thiz, jint key, jobject java_reply) |
| { |
| LOGV("getParameter: key %d", key); |
| sp<MediaPlayer> mp = getMediaPlayer(env, thiz); |
| if (mp == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return; |
| } |
| |
| Parcel *reply = parcelForJavaObject(env, java_reply); |
| process_media_player_call(env, thiz, mp->getParameter(key, reply), NULL, NULL ); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| static JNINativeMethod gMethods[] = { |
| {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, |
| |
| { |
| "_setDataSource", |
| "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", |
| (void *)android_media_MediaPlayer_setDataSourceAndHeaders |
| }, |
| |
| {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, |
| {"_setVideoSurface", "(Landroid/view/Surface;)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}, |
| {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, |
| {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, |
| {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel}, |
| {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect}, |
| {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, |
| {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter}, |
| {"getParameter", "(ILandroid/os/Parcel;)V", (void *)android_media_MediaPlayer_getParameter}, |
| }; |
| |
| 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); |
| extern int register_android_media_AmrInputStream(JNIEnv *env); |
| extern int register_android_mtp_MtpDatabase(JNIEnv *env); |
| extern int register_android_mtp_MtpDevice(JNIEnv *env); |
| extern int register_android_mtp_MtpServer(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; |
| } |
| |
| if (register_android_media_AmrInputStream(env) < 0) { |
| LOGE("ERROR: AmrInputStream native registration failed\n"); |
| goto bail; |
| } |
| |
| 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; |
| } |
| |
| if (register_android_mtp_MtpDatabase(env) < 0) { |
| LOGE("ERROR: MtpDatabase native registration failed"); |
| goto bail; |
| } |
| |
| if (register_android_mtp_MtpDevice(env) < 0) { |
| LOGE("ERROR: MtpDevice native registration failed"); |
| goto bail; |
| } |
| |
| if (register_android_mtp_MtpServer(env) < 0) { |
| LOGE("ERROR: MtpServer native registration failed"); |
| goto bail; |
| } |
| |
| /* success -- return valid version number */ |
| result = JNI_VERSION_1_4; |
| |
| bail: |
| return result; |
| } |
| |
| // KTHXBYE |