美文网首页
MediaPlayer与native层的交互流程

MediaPlayer与native层的交互流程

作者: smallest_one | 来源:发表于2019-04-29 21:06 被阅读0次

    目录

    1. 参考
    2. 概述
    3. MediaPlayer与native层的交互流程

    1. 参考

    2. 概述

    MediaPlayer是Android播放器的组件,先回顾一下音视频播放的流程:

    数据获取->数据解析->音/视频解码->同步控制->渲染
    
    image.png

    android media的框架图[2]


    image.png
    • 播放服务是通过在独立进程的MediaPlayerService提供的。
    • MediaPlayerService的实现在native层。

    本文主要分析MediaPlayer是怎么与native层交互的,不涉及播放流程。

    3. MediaPlayer与native的交互流程

    MediaPlayer_native_workflow.png

    交互流程

    1. MediaPlayer类加载的时机
      (1) 加载media_jni.so
      (2) Java和native的方法进行绑定。
    //MediaPlayer.java
        static {
            System.loadLibrary("media_jni");
            native_init();
        }
    
    //android_media_MediaPlayer.cpp
    static const JNINativeMethod gMethods[] = {
        {
            "nativeSetDataSource",
            "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
            "[Ljava/lang/String;)V",
            (void *)android_media_MediaPlayer_setDataSourceAndHeaders
        },
    ...
    }
    
    static int register_android_media_MediaPlayer(JNIEnv *env)
    {
        return AndroidRuntime::registerNativeMethods(env,
                    "android/media/MediaPlayer", gMethods, NELEM(gMethods));
    }
    
    jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
    {
        JNIEnv* env = NULL;
        jint result = -1;
    
        if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
            ALOGE("ERROR: GetEnv failed\n");
            goto bail;
        }
        assert(env != NULL);
    ...
        if (register_android_media_MediaPlayer(env) < 0) {
            ALOGE("ERROR: MediaPlayer native registration failed\n");
            goto bail;
        }
    ...
        /* success -- return valid version number */
        result = JNI_VERSION_1_4;
    
    bail:
        return result;
    }
    
    //android_media_MediaPlayer.cpp
    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", "J");
        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;
        }
    ...
    }
    
    1. MediaPlayer创建的时机
      (1) 创建C++的MediaPlayer对象,设置listener。
      (2) C++保存Java MediaPlayer对象的一个弱引用,调用MediaPlayer的static postEventFromNative方法时使用。
      (3) Java层的MediaPlayer增加对新创建的C++的MediaPlayer的强引用sp。
    //MediaPlayer.java
        public MediaPlayer() { 
    ...
            native_setup(new WeakReference<MediaPlayer>(this));
    ...
        }
    
    //android_media_MediaPlayer.cpp
    static void
    android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
    {
        sp<MediaPlayer> mp = new MediaPlayer();
        if (mp == NULL) {
            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
            return;
        }
    
        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 sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
    {
        Mutex::Autolock l(sLock);
        sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
        if (player.get()) {
            player->incStrong((void*)setMediaPlayer);
        }
        if (old != 0) {
            old->decStrong((void*)setMediaPlayer);
        }
        env->SetLongField(thiz, fields.context, (jlong)player.get());
        return old;
    }
    
    • Java层的MediaPlayer的private long mNativeContext; // accessed by native methods成员保存了native层c++的MediaPlayer对象的指针。
    1. Native层通知事件给Java层
    //android_media_MediaPlayer.cpp
    void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
    {
        JNIEnv *env = AndroidRuntime::getJNIEnv();
        if (obj && obj->dataSize() > 0) {
            jobject jParcel = createJavaParcelObject(env);
            if (jParcel != NULL) {
                Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
                nativeParcel->setData(obj->data(), obj->dataSize());
                env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                        msg, ext1, ext2, jParcel);
                env->DeleteLocalRef(jParcel);
            }
        } else {
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, NULL);
        }
        if (env->ExceptionCheck()) {
            ALOGW("An exception occurred while notifying an event.");
            LOGW_EX(env);
            env->ExceptionClear();
        }
    }
    
    //MediaPlayer.java
        private static void postEventFromNative(Object mediaplayer_ref,
                                                int what, int arg1, int arg2, Object obj)
        {
            final MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
            if (mp == null) {
                return;
            }
    
            switch (what) {
            case MEDIA_INFO:
                if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            // this acquires the wakelock if needed, and sets the client side state
                            mp.start();
                        }
                    }).start();
                    Thread.yield();
                }
                break;
    ...
    }
    
    1. 其他一些native方法的调用
      比如Java MediaPlayer的nativeSetDataSource方法绑定到了native的android_media_MediaPlayer_setDataSourceAndHeaders方法。
      (1) 获取Java的MediaPlayer持有的C++ MediaPlayer的引用。
      (2) JNI数据结构和native数据结构的转换。
      (3) 调用C++ MediaPlayer的对应方法。
      (4) 异常抛给Java层。
    //android_media_MediaPlayer.cpp
    static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
    {
        Mutex::Autolock l(sLock);
        MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
        return sp<MediaPlayer>(p);
    }
    
    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) BAD_VALUE ) {
                jniThrowException(env, "java/lang/IllegalArgumentException", 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, jobject httpServiceBinderObj, 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;
        }
        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;
        }
    
        sp<IMediaHTTPService> httpService;
        if (httpServiceBinderObj != NULL) {
            sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
            httpService = interface_cast<IMediaHTTPService>(binder);
        }
    
        status_t opStatus =
            mp->setDataSource(
                    httpService,
                    pathStr,
                    headersVector.size() > 0? &headersVector : NULL);
    
        process_media_player_call(
                env, thiz, opStatus, "java/io/IOException",
                "setDataSource failed." );
    }
    

    相关文章

      网友评论

          本文标题:MediaPlayer与native层的交互流程

          本文链接:https://www.haomeiwen.com/subject/wveunqtx.html