IjkPlayer 源码阅读一 初始化

作者: nothingwxq | 来源:发表于2017-07-24 17:17 被阅读731次

    1 背景

    公司的底层播放器实际上是ffplayer作为基础修改的,当然需要好好学习研究。 记录下来,作为以后备忘。(发觉人老了,就容易忘事)。ps 入坑需谨慎,很容易入门到放弃。

    2 分析入口

    分析的连接https://github.com/Bilibili/ijkplayer
    版本号: 0.8.1.2 android版
    基本上IjkPlayer的接口和MediaPlayer接口一致。http://www.jianshu.com/p/55fbfd5b564a
    每次我们发觉分析源码总找不到下口的地方,实际上就是没找到分析的入口。那在哪里呢?当然看源码demo了。https://github.com/Bilibili/ijkplayer/blob/5b73bf7eae66a3942e2689ecbe6e51b679abe161/android/ijkplayer/ijkplayer-example/src/main/java/tv/danmaku/ijk/media/example/activities/VideoActivity.java
    这个类里面的onCreate 方法里面:

    image.png

    实际上初始化,作者已注释了 init player。我们来看看初始化里做了什么?

    3 IjkMediaPlayer.loadLibrariesOnce(null);

    这个很简单load了几个需要的lib(ijkffmpeg,ijksdl,ijkplayer)

    image.png

    事实上这里还有隐藏逻辑,看其中源码,你会发现,卧槽,我都找不到定义jni的方法名,熟悉jni的同学知道,jni方法名是Java+ 全类名 命名,可是你会发觉比如你想找到_start方法,你是找不到的。 这里使用的一个trick , 事实上我觉得这对写代码的人,
    提高了写作效率,但可读性没那么高。在jni加载lib的时候,会首先查找JNI_OnLoad() 方法,同理卸载的时候JNI_OnUnload()。我们看下他都干了些啥?
    a. 这个是ijkplayer_jni做的

    static JNINativeMethod g_methods[] = {
        {
            "_setDataSource",
            "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
            (void *) IjkMediaPlayer_setDataSourceAndHeaders
        },
        { "_setDataSourceFd",       "(I)V",     (void *) IjkMediaPlayer_setDataSourceFd },
        { "_setDataSource",         "(Ltv/danmaku/ijk/media/player/misc/IMediaDataSource;)V", (void *)IjkMediaPlayer_setDataSourceCallback },
        { "_setAndroidIOCallback",  "(Ltv/danmaku/ijk/media/player/misc/IAndroidIO;)V", (void *)IjkMediaPlayer_setAndroidIOCallback },
    
        { "_setVideoSurface",       "(Landroid/view/Surface;)V", (void *) IjkMediaPlayer_setVideoSurface },
        { "_prepareAsync",          "()V",      (void *) IjkMediaPlayer_prepareAsync },
        { "_start",                 "()V",      (void *) IjkMediaPlayer_start },
        { "_stop",                  "()V",      (void *) IjkMediaPlayer_stop },
        { "seekTo",                 "(J)V",     (void *) IjkMediaPlayer_seekTo },
        { "_pause",                 "()V",      (void *) IjkMediaPlayer_pause },
        { "isPlaying",              "()Z",      (void *) IjkMediaPlayer_isPlaying },
        { "getCurrentPosition",     "()J",      (void *) IjkMediaPlayer_getCurrentPosition },
        { "getDuration",            "()J",      (void *) IjkMediaPlayer_getDuration },
        { "_release",               "()V",      (void *) IjkMediaPlayer_release },
        { "_reset",                 "()V",      (void *) IjkMediaPlayer_reset },
        { "setVolume",              "(FF)V",    (void *) IjkMediaPlayer_setVolume },
        { "getAudioSessionId",      "()I",      (void *) IjkMediaPlayer_getAudioSessionId },
        { "native_init",            "()V",      (void *) IjkMediaPlayer_native_init },
        { "native_setup",           "(Ljava/lang/Object;)V", (void *) IjkMediaPlayer_native_setup },
        { "native_finalize",        "()V",      (void *) IjkMediaPlayer_native_finalize },
    
        { "_setOption",             "(ILjava/lang/String;Ljava/lang/String;)V", (void *) IjkMediaPlayer_setOption },
        { "_setOption",             "(ILjava/lang/String;J)V",                  (void *) IjkMediaPlayer_setOptionLong },
    
        { "_getColorFormatName",    "(I)Ljava/lang/String;",    (void *) IjkMediaPlayer_getColorFormatName },
        { "_getVideoCodecInfo",     "()Ljava/lang/String;",     (void *) IjkMediaPlayer_getVideoCodecInfo },
        { "_getAudioCodecInfo",     "()Ljava/lang/String;",     (void *) IjkMediaPlayer_getAudioCodecInfo },
        { "_getMediaMeta",          "()Landroid/os/Bundle;",    (void *) IjkMediaPlayer_getMediaMeta },
        { "_setLoopCount",          "(I)V",                     (void *) IjkMediaPlayer_setLoopCount },
        { "_getLoopCount",          "()I",                      (void *) IjkMediaPlayer_getLoopCount },
        { "_getPropertyFloat",      "(IF)F",                    (void *) ijkMediaPlayer_getPropertyFloat },
        { "_setPropertyFloat",      "(IF)V",                    (void *) ijkMediaPlayer_setPropertyFloat },
        { "_getPropertyLong",       "(IJ)J",                    (void *) ijkMediaPlayer_getPropertyLong },
        { "_setPropertyLong",       "(IJ)V",                    (void *) ijkMediaPlayer_setPropertyLong },
        { "_setStreamSelected",     "(IZ)V",                    (void *) ijkMediaPlayer_setStreamSelected },
    
        { "native_profileBegin",    "(Ljava/lang/String;)V",    (void *) IjkMediaPlayer_native_profileBegin },
        { "native_profileEnd",      "()V",                      (void *) IjkMediaPlayer_native_profileEnd },
    
        { "native_setLogLevel",     "(I)V",                     (void *) IjkMediaPlayer_native_setLogLevel },
        { "_injectCacheNode",       "(IJJJJ)V",                 (void *) IjkMediaPlayer_injectCacheNode },
    };
    
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
    {
        JNIEnv* env = NULL;
    
        g_jvm = vm;
        if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
            return -1;
        }
        assert(env != NULL);
    
        pthread_mutex_init(&g_clazz.mutex, NULL );
    
        // FindClass returns LocalReference
        IJK_FIND_JAVA_CLASS(env, g_clazz.clazz, JNI_CLASS_IJKPLAYER);
        (*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods) );
    
        ijkmp_global_init();
        ijkmp_global_set_inject_callback(inject_callback);
    
        FFmpegApi_global_init(env);
    
        return JNI_VERSION_1_4;
    }
    

    来依次看

    #define IJK_FIND_JAVA_CLASS(env__, var__, classsign__) \
        do { \
            jclass clazz = (*env__)->FindClass(env__, classsign__); \
            if (J4A_ExceptionCheck__catchAll(env) || !(clazz)) { \
                ALOGE("FindClass failed: %s", classsign__); \
                return -1; \
            } \
            var__ = (*env__)->NewGlobalRef(env__, clazz); \
            if (J4A_ExceptionCheck__catchAll(env) || !(var__)) { \
                ALOGE("FindClass::NewGlobalRef failed: %s", classsign__); \
                (*env__)->DeleteLocalRef(env__, clazz); \
                return -1; \
            } \
            (*env__)->DeleteLocalRef(env__, clazz); \
        } while(0);
    

    这里宏定义的代码块,在ijkplayer非常常见,忍不住吐槽下, do while(0)貌似没其他作用,仅仅为了装逼? 去掉的话不好么?整个源码里面非常多的 do while(0) 定义的块。这个函数的作用,就是把java class 传递给c。
    (*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods) ); 这句就是动态注册各个方法了。当然c语言和c++注册的代码稍微有点区别,c++没有env参数。

    ijkmp_global_init();

    ijkmedia/ijkplayer/ijkplayer.c

    void ijkmp_global_init()
     {
             ffp_global_init();
    }
    

    哈哈,作为java程序猿,源码缩进格式也看着别扭。继续看ijkmedia/ijkplayer/ff_ffplay.c

    void ffp_global_init()
    {
        if (g_ffmpeg_global_inited)
            return;
    
        /* register all codecs, demux and protocols */
        avcodec_register_all();
    #if CONFIG_AVDEVICE
        avdevice_register_all();
    #endif
    #if CONFIG_AVFILTER
        avfilter_register_all();
    #endif
        av_register_all();
    
        ijkav_register_all();
    
        avformat_network_init();
    
        av_lockmgr_register(lockmgr);
        av_log_set_callback(ffp_log_callback_brief);
    
        av_init_packet(&flush_pkt);
        flush_pkt.data = (uint8_t *)&flush_pkt;
    
        g_ffmpeg_global_inited = true;
    }
    

    看到这里你会说,啊,原来ffmpeg的api 初始化都在这里呀,哇咔咔~~ 当然如果想了解细节可以继续跟源码读下去,不想的话,我继续出发了。

    ijkmp_global_set_inject_callback(inject_callback)
    ijkmedia/ijkplayer/ijkplayer.c

    void ijkmp_global_set_inject_callback(ijk_inject_callback cb)
    {
        ffp_global_set_inject_callback(cb);
    }
    

    别紧张就是一个回调接口。

    FFmpegApi_global_init
    ijkmedia/ijkplayer/android/ffmpeg_api_jni.c

    int FFmpegApi_global_init(JNIEnv *env)
    {
        int ret = 0;
    
        IJK_FIND_JAVA_CLASS(env, g_clazz.clazz, JNI_CLASS_FFMPEG_API);
        (*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods));
    
        return ret;
    }
    

    native_profileBegin

    这个跟进源码看里面没什么有效的代码,略。

    代码位于 https://github.com/Bilibili/ijkplayer/blob/eb265a564f471e91778158156ab49d46bb00197d/android/ijkplayer/ijkplayer-example/src/main/java/tv/danmaku/ijk/media/example/widget/media/IjkVideoView.java

    ## setVideoURI(Uri.parse(path));
     public void setVideoPath(String path) {
             setVideoURI(Uri.parse(path));
     }
    

    本质上也是调用setVideoURI.

    private void setVideoURI(Uri uri, Map<String, String> headers) {
            mUri = uri;
            mHeaders = headers;
            mSeekWhenPrepared = 0;
            openVideo();
            requestLayout();
            invalidate();
        }
    

    其他的初始化参数略, headers是视频的一些信息,这里可以用来添加防盗链参数,主要看openVideo()做啥的。

    @TargetApi(Build.VERSION_CODES.M)
        private void openVideo() {
            if (mUri == null || mSurfaceHolder == null) {
                // not ready for playback just yet, will try again later
                return;
            }
            // we shouldn't clear the target state, because somebody might have
            // called start() previously
            release(false);
    
            AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
            am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    
            try {
                mMediaPlayer = createPlayer(mSettings.getPlayer());
    
               ....
             
                String scheme = mUri.getScheme();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                        mSettings.getUsingMediaDataSource() &&
                        (TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {
                    IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));
                    mMediaPlayer.setDataSource(dataSource);
                }  else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);
                } else {
                    mMediaPlayer.setDataSource(mUri.toString());
                }
                bindSurfaceHolder(mMediaPlayer, mSurfaceHolder);
                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mMediaPlayer.setScreenOnWhilePlaying(true);
                mPrepareStartTime = System.currentTimeMillis();
                mMediaPlayer.prepareAsync();
                if (mHudViewHolder != null)
                    mHudViewHolder.setMediaPlayer(mMediaPlayer);
    
                // REMOVED: mPendingSubtitleTracks
    
                // we don't set the target state here either, but preserve the
                // target state that was there before.
                mCurrentState = STATE_PREPARING;
                attachMediaController();
            } catch (IOException ex) {
                ...
            } catch (IllegalArgumentException ex) {
              ...
            } finally {
              ...
            }
        }
    

    可以看到一系列参数的初始化。
    createPlayer(mSettings.getPlayer()) 这里根据设置,选取播放器。 兼容了系统,ExoPlayer,及IjkPlayer(默认)。

     public IMediaPlayer createPlayer(int playerType) {
            IMediaPlayer mediaPlayer = null;
    
            switch (playerType) {
                case Settings.PV_PLAYER__IjkExoMediaPlayer: {
                    IjkExoMediaPlayer IjkExoMediaPlayer = new IjkExoMediaPlayer(mAppContext);
                    mediaPlayer = IjkExoMediaPlayer;
                }
                break;
                case Settings.PV_PLAYER__AndroidMediaPlayer: {
                    AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer();
                    mediaPlayer = androidMediaPlayer;
                }
                break;
                case Settings.PV_PLAYER__IjkMediaPlayer:
                default: {
                    IjkMediaPlayer ijkMediaPlayer = null;
                    if (mUri != null) {
                        ijkMediaPlayer = new IjkMediaPlayer();
                      ....
    
                    ...
                }
                break;
            }
    
            if (mSettings.getEnableDetachedSurfaceTextureView()) {
                mediaPlayer = new TextureMediaPlayer(mediaPlayer);
            }
    
            return mediaPlayer;
        }
    

    https://github.com/Bilibili/ijkplayer/blob/master/android/ijkplayer/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java

    
        /**
         * Default constructor. Consider using one of the create() methods for
         * synchronously instantiating a IjkMediaPlayer from a Uri or resource.
         * <p>
         * When done with the IjkMediaPlayer, you should call {@link #release()}, to
         * free the resources. If not released, too many IjkMediaPlayer instances
         * may result in an exception.
         * </p>
         */
        public IjkMediaPlayer() {
            this(sLocalLibLoader);
        }
    
        /**
         * do not loadLibaray
         * @param libLoader
         *              custom library loader, can be null.
         */
        public IjkMediaPlayer(IjkLibLoader libLoader) {
            initPlayer(libLoader);
        }
    
        private void initPlayer(IjkLibLoader libLoader) {
            loadLibrariesOnce(libLoader);
            initNativeOnce();
    
            Looper looper;
            if ((looper = Looper.myLooper()) != null) {
                mEventHandler = new EventHandler(this, looper);
            } else if ((looper = Looper.getMainLooper()) != null) {
                mEventHandler = new EventHandler(this, looper);
            } else {
                mEventHandler = null;
            }
    
            /*
             * Native setup requires a weak reference to our object. It's easier to
             * create it here than in C++.
             */
            native_setup(new WeakReference<IjkMediaPlayer>(this));
        }
    
    

    loadLibrariesOnce 这里再次load下,确保lib被加载。initNativeOnce只是打印。 初始化了EventHandler,及native_setup()。

    static void
    IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
    {
        MPTRACE("%s\n", __func__);
        IjkMediaPlayer *mp = ijkmp_android_create(message_loop);
        JNI_CHECK_GOTO(mp, env, "java/lang/OutOfMemoryError", "mpjni: native_setup: ijkmp_create() failed", LABEL_RETURN);
    
        jni_set_media_player(env, thiz, mp);
        ijkmp_set_weak_thiz(mp, (*env)->NewGlobalRef(env, weak_this));
        ijkmp_set_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
        ijkmp_set_ijkio_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
        ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));
    
    LABEL_RETURN:
        ijkmp_dec_ref_p(&mp);
    }
    

    代码的层级每次调用都比较深,由于jni的缘故,很容易就走神了。先看ijkmp_android_create().参数是函数指针,函数 message_loop

    static int message_loop(void *arg)
    {
        MPTRACE("%s\n", __func__);
    
        JNIEnv *env = NULL;
        if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) {
            ALOGE("%s: SetupThreadEnv failed\n", __func__);
            return -1;
        }
    
        IjkMediaPlayer *mp = (IjkMediaPlayer*) arg;
        JNI_CHECK_GOTO(mp, env, NULL, "mpjni: native_message_loop: null mp", LABEL_RETURN);
    
        message_loop_n(env, mp);
    
    LABEL_RETURN:
        ijkmp_dec_ref_p(&mp);
    
        MPTRACE("message_loop exit");
        return 0;
    }
    

    message_loop_n 都是做什么的呢?

    static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp)
    {
        jobject weak_thiz = (jobject) ijkmp_get_weak_thiz(mp);
        JNI_CHECK_GOTO(weak_thiz, env, NULL, "mpjni: message_loop_n: null weak_thiz", LABEL_RETURN);
    
        while (1) {
            AVMessage msg;
    
            int retval = ijkmp_get_msg(mp, &msg, 1);
            if (retval < 0)
                break;
    
            // block-get should never return 0
            assert(retval > 0);
    
            switch (msg.what) {
            case FFP_MSG_FLUSH:
                MPTRACE("FFP_MSG_FLUSH:\n");
                post_event(env, weak_thiz, MEDIA_NOP, 0, 0);
                break;
            case FFP_MSG_ERROR:
                MPTRACE("FFP_MSG_ERROR: %d\n", msg.arg1);
                post_event(env, weak_thiz, MEDIA_ERROR, MEDIA_ERROR_IJK_PLAYER, msg.arg1);
                break;
            case FFP_MSG_PREPARED:
                MPTRACE("FFP_MSG_PREPARED:\n");
                post_event(env, weak_thiz, MEDIA_PREPARED, 0, 0);
                break;
            case FFP_MSG_COMPLETED:
            ...
    }
    

    我们发觉这个其实是消息队列形式,看下post_event函数。直接调用了J4AC_IjkMediaPlayer__postEventFromNative(env, weak_this, what, arg1, arg2, NULL) 函数。

     void J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__postEventFromNative(JNIEnv *env, jobject weakThiz, jint what, jint arg1, jint arg2, jobject obj)
    {
        (*env)->CallStaticVoidMethod(env, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative, weakThiz, what, arg1, arg2, obj);
    }
    

    其中,class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative
    class_id = class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id;
    name = "postEventFromNative";
    sign = "(Ljava/lang/Object;IIILjava/lang/Object;)V"; class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative = J4A_GetStaticMethodID__catchAll(env, class_id, name, sign);

    所以class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative 本质是调用postEventFromNative android/ijkplayer/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java

       @CalledByNative
        private static void postEventFromNative(Object weakThiz, int what,
                int arg1, int arg2, Object obj) {
            if (weakThiz == null)
                return;
    
            @SuppressWarnings("rawtypes")
            IjkMediaPlayer mp = (IjkMediaPlayer) ((WeakReference) weakThiz).get();
            if (mp == null) {
                return;
            }
    
            if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
                // this acquires the wakelock if needed, and sets the client side
                // state
                mp.start();
            }
            if (mp.mEventHandler != null) {
                Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
                mp.mEventHandler.sendMessage(m);
            }
        }
    

    具体是初始化了IjkMediaPlayer 这个结构体,留个坑,单独写个章节介绍其c层数据结构。
    位于https://github.com/Bilibili/ijkplayer/blob/eb265a564f471e91778158156ab49d46bb00197d/ijkmedia/ijkplayer/android/ijkplayer_android.c

    IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*))
    {
        IjkMediaPlayer *mp = ijkmp_create(msg_loop);
        if (!mp)
            goto fail;
    
        mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();
        if (!mp->ffplayer->vout)
            goto fail;
    
        mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer);
        if (!mp->ffplayer->pipeline)
            goto fail;
    
        ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout);
    
        return mp;
    
    fail:
        ijkmp_dec_ref_p(&mp);
        return NULL;
    }
    

    ijkmp_create 初始化了FFPlayer结构的参数初始化。初始化msg_queue_init 消息队列。af_mutex, vf_mutex 音视频互斥锁。这里涉及的FFPlayer初始化参数很多。

    
    FFPlayer *ffp_create()
    {
     
       ...
        FFPlayer* ffp = (FFPlayer*) av_mallocz(sizeof(FFPlayer));
        if (!ffp)
            return NULL;
    
        msg_queue_init(&ffp->msg_queue);
        ffp->af_mutex = SDL_CreateMutex();
        ffp->vf_mutex = SDL_CreateMutex();
    
        ffp_reset_internal(ffp);
        ffp->av_class = &ffp_context_class;
        ffp->meta = ijkmeta_create();
    
        av_opt_set_defaults(ffp);
    
        return ffp;
    }
    

    回到mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface(); 实际是调用 SDL_VoutAndroid_CreateForANativeWindow();
    mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer); 初始化了IJKFF_Pipeline 结构体。回到IjkMediaPlayer_native_setup 方法。 jni_set_media_player(env, thiz, mp) 其实是调用java的方法set

     void J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__mNativeMediaPlayer__set(JNIEnv *env, jobject thiz, jlong value)
    {
        (*env)->SetLongField(env, thiz, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.field_mNativeMediaPlayer, value);
    }
    

    与postEvent类似,找到mNativeMediaPlayer变量(android/ijkplayer/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java)。

    回到openVedio函数内mMediaPlayer.setDataSource(), 到c层的ijkmedia/ijkplayer/android/ijkplayer_jni.c IjkMediaPlayer_setDataSourceAndHeaders函数。

    static void
    IjkMediaPlayer_setDataSourceAndHeaders(
        JNIEnv *env, jobject thiz, jstring path,
        jobjectArray keys, jobjectArray values)
    {
        MPTRACE("%s\n", __func__);
        int retval = 0;
        const char *c_path = NULL;
        IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
        JNI_CHECK_GOTO(path, env, "java/lang/IllegalArgumentException", "mpjni: setDataSource: null path", LABEL_RETURN);
        JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: setDataSource: null mp", LABEL_RETURN);
    
        c_path = (*env)->GetStringUTFChars(env, path, NULL );
        JNI_CHECK_GOTO(c_path, env, "java/lang/OutOfMemoryError", "mpjni: setDataSource: path.string oom", LABEL_RETURN);
    
        ALOGV("setDataSource: path %s", c_path);
        retval = ijkmp_set_data_source(mp, c_path);
        (*env)->ReleaseStringUTFChars(env, path, c_path);
    
        IJK_CHECK_MPRET_GOTO(retval, env, LABEL_RETURN);
    
    LABEL_RETURN:
        ijkmp_dec_ref_p(&mp);
    
    

    ijkmp_set_data_source(mp, c_path) 函数主要代码ijkmp_change_state_l(mp, MP_STATE_INITIALIZED);

    void ijkmp_change_state_l(IjkMediaPlayer *mp, int new_state)
    {
        mp->mp_state = new_state;
        ffp_notify_msg1(mp->ffplayer, FFP_MSG_PLAYBACK_STATE_CHANGED);
    }
    

    发了个FFP_MSG_PLAYBACK_STATE_CHANGED消息,但是我们看message_loop_n里面是没有回调java的。
    先分析到这里,有空的话把时序图和其他的类图的坑填上。ps:纠正下java 函数称 method, c,c++叫函数,写作中没转过来。

    相关文章

      网友评论

      本文标题:IjkPlayer 源码阅读一 初始化

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