IJKPlayer阅读笔记(一)初始化

2,137 阅读11分钟

入口方法

ijkMediaPlayer = new IjkMediaPlayer();

分析

Java

  • 当我们通过new初始化之后会调用他的无参构造方法
public IjkMediaPlayer() {
    //无参的构造方法会调用一个有参(loadlib)的构造方法
    this(sLocalLibLoader);
}

sLocalLibLoader是一个接口,通过回调加载so

  • 之后查看调用的这个构造方法
public IjkMediaPlayer(IjkLibLoader libLoader) {
    //调用init方法,并传递libload接口
    initPlayer(libLoader);
}
  • 接下来调用他的initPlayer方法
private void initPlayer(IjkLibLoader libLoader) {
    //调用静态方法,将接口传递给它,用于加载c++库
    loadLibrariesOnce(libLoader);
    //用于初始化native方法
    initNativeOnce();
    //声明一个loop
    Looper looper;
    //通过各种方法设置这个loop
    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方法,用于初始化ijkplayer
    native_setup(new WeakReference<IjkMediaPlayer>(this));
}

这个方法时正式的初始化方法,在其中加载了so库,设置了looper,调用了native的初始化与setup方法

  • loadLibrariesOnce方法用于加载so库
public static void loadLibrariesOnce(IjkLibLoader libLoader) {
    synchronized (IjkMediaPlayer.class) {
        //通过这里判断c++库是否已加载
        if (!mIsLibLoaded) {
            //接口为null时重设下接口
            if (libLoader == null)
                libLoader = sLocalLibLoader;
            //通过接口回调去加载这三个so
            libLoader.loadLibrary("ijkffmpeg");
            libLoader.loadLibrary("ijksdl");
            libLoader.loadLibrary("ijkplayer");
            //加载完毕
            mIsLibLoaded = true;
        }
    }
}

由于initNativeOnce方法最终调用的native方法是一个空方法,而设置looper这段代码没什么难度,所以我们直接分析native_setup方法

  • native_setup
    这个方法给native层传了一个ijkplayer的弱引用(跟MediaPlayer学的)

Native

  • JNI_OnLoad 该方法会在java层调用System.loadLibrary时调用
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();1
    ijkmp_global_set_inject_callback(inject_callback);2
    FFmpegApi_global_init(env);3
    return JNI_VERSION_1_4;
}

我们看到了其中执行了三个方法,ijkmp_global_initijkmp_global_set_inject_callbackFFmpegApi_global_init

  • ijkmp_global_init
    这个方法位于ijkmedia/ijkplayer/ijkplayer.c
void ijkmp_global_init() {
    ffp_global_init();
}
  • ffp_global_init
    这个方法在ijkmedia/ijkplayer/ff_ffplay.c
void ffp_global_init(){
    if (g_ffmpeg_global_inited)
        return;
    ALOGD("ijkmediaplayer version :%s"ijkmp_version()) ;
    /* register all codecs, demux andprotocols */
    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中的组件外,还ijkav_register_all方法

  • ijkav_register_all
    这个方法定ijkplayer/ijkavformat/allformats.c中,
void ijkav_register_all(void){
    static int initialized
    if (initialized)
        return;
    initialized = 1
    av_register_all()
    /* protocols */
    av_log(NULL, AV_LOG_INFO, "===== customodules begin =====\n");
    #ifdef __ANDROID__
        IJK_REGISTER_PROTOC(ijkmediadatasource);
    #endif
    IJK_REGISTER_PROTOCOL(ijkio);
    IJK_REGISTER_PROTOCOL(async);
    IJK_REGISTER_PROTOCOL(ijklongurl);
    IJK_REGISTER_PROTOCOL(ijktcphook);
    IJK_REGISTER_PROTOCOL(ijkhttphook);
    IJK_REGISTER_PROTOCOL(ijksegment);
    /* demuxers */
    IJK_REGISTER_DEMUXER(ijklivehook);
    av_log(NULL, AV_LOG_INFO, "===== customodules end =====\n");
}

在这里发现如果是Android的话,会调IJK_REGISTER_PROTOCOL这个宏定义

#define IJK_REGISTER_PROTOCOL(x)                                      \
    {                                                                 \
        extern URLProtocoijkimp_ff_##x##_protocol;                      \
        int ijkav_register_##x##_protoc(URLProtocol *protocol, inprotocol_size);\
        ijkav_register_##x##_protocolijkimp_ff_##x##_protocol, size(URLProtocol));  \
    }

走到这里似乎走不通了,因为通过export可以来ijkplayer应该是修改了ffmpeg中的URLProtocol类,反向思考下,我们IJK_REGISTER_PROTOCOL(ijkmediadatasource)可以确认是是用到了ijkmediadatasource这(c文件),我们在同目录下ijkmediadatasource.c发现

URLProtocoijkimp_ff_ijkmediadatasource_protocol = {
    .name                "ijkmediadatasource",
    .url_open2           = ijkmds_open,
    .url_read            = ijkmds_read,
    .url_seek            = ijkmds_seek,
    .url_close           = ijkmds_close,
    .priv_data_size      = sizeof(Context),
    .priv_data_class     =ijkmediadatasource_context_class,
};

这里是将URLProtocol中的属性初始化为对应数指针,包括打开方式,读取方式,seek,关闭,等等(因下两个我也不知道是干啥的)
好了,到了这里,我们跳出allformat.c文件,回到ff_ffplay.c中的ffp_global_init方法
然后我们知道了ijkav_register_all是将URLProtocol中属性初始化为ijkplayer中相对应的函数指针,接下来我们跳出ffp_global_init方法,然后我们回到中间商ijkplayer.c中的ijkmp_global_init方法,然后我们在回到ijkplayer_jni.c中的JNI_OnLoad方法发现ijkmp_global_init我们已经阅读完毕,接下来阅读ijkmp_global_set_inject_callback方法,

  • ijkmp_global_set_inject_callback
    这个函数从名字上可以猜测出是用来设置回调的,就是不知道是c内部回调还是从c回调到java的,
ijkmp_global_set_inject_callback(inject_callback);

这这里有一个参数,这个参数是一个函数指针,函数声明为

static int inject_callback(void *opaque, int type, void *data, size_t data_size);

由于未使用,暂时忽略掉函数定义 接下来我们继续跟ijkmp_global_set_inject_callback方法,该方法义在ijkmedia/ijkplayer/ijkplayer.c

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

继续跳转到ff_ffplay.c

void ffp_global_set_inject_callbac(ijk_inject_callback cb)
{
    s_inject_callback = cb;
}

这里我们将ijkplayer_jni中的函数指针赋予ff_ffplayer.c中的s_inject_callback 到了这里,ijkmp_global_set_inject_callback的用就是将函数指针从ijkplayer_jni传递ff_ffplayer.c

  • FFmpegApi_global_init
    这个函数是JNI_OnLoad中的最后一个执行的方法,首跟踪到ijkmedia/ijkplayer/ffmpeg_api_jni.c中,这文件只有一个方法
    • FFmpegApi_global_init
      static JNINativeMethod g_methods[] = {
          {"av_base64_encode", "([BLjava/lang/String;", (void *)FFmpegApi_av_base64_encode},
      };
      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, NELE(g_methods));
          return ret;
      }
      
    这里注册了一个FFmpegApi_av_base64_encode的方法,这个方法通过java层的av_base64_encode调用,在java层的ffmpeg/FFmpegApi.java

到这里JNI_OnLoad的流程已经走完,总结下OnLoad中进行了什么操作,首先,初始化了ffmpeg中的设置并为URLProtocol中的属性赋予对应的函数指针,然后将jni中的函数指针传递到了ff_ffplay.c中,最后注册了一个jni方法

  • set_up
    我们通过jni映射发现set_up对应的jni方法为

    • IjkMediaPlayer_native_setup
    static void IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) {
        //初始化,该方法中初始化了ijkplayer
        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);
    }
    
    

    首先通过ijkmp_android_create创建了一个IjkMediaPlayer

    • ijkmp_android_create 首先,我们先跟踪这个方法到ijkmedia/ijkplayer/android/ijkplayer_android.c

      IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*)){
          //这里继续初始化,ijkmp_create初始完成之后会返回IjkMediaPlayer指针
          IjkMediaPlayer *mp = ijkmp_create(msg_loop);
          if (!mp)
              goto fail;
          //创建ffplay的显示surface
          mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();
          if (!mp->ffplayer->vout)
              goto fail;
          //在这里初始化了一些pipeline的设置
          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;
      }
      

      然后我们看到ijkplayer继续通过ijkmp_create进行初始化,我们继续跟踪到ijkplayer.c

      • ijkmp_create
      IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void *)) {
          //通过mallocz分配ijkplayer
          IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
          if (!mp)
              //失败的话会回到goto去销毁
              goto fail;
      
          mp->ffplayer = ffp_create();
          if (!mp->ffplayer)
              goto fail;
          //将参数中的函数指针传递给了ijkplayer的loop属性
          mp->msg_loop = msg_loop;
          //为ijkplayer中的引用计数+1
          ijkmp_inc_ref(mp);
          pthread_mutex_init(&mp->mutex, NULL);
      
          return mp;
      
          fail:
          ijkmp_destroy_p(&mp);
          return NULL;
      }
      

      这里我们发现ijkplayer通过mallocz分配了内存,这个mallocz我没有找到关于它的定义,不过在ffmpeg中有av_mallocz,它是分配内存并将内存的值设为0,估计认为他们是一样的,接下来就是初始化IjkMediaPlayer中对应的属性

      • ffp_create
        ffp_create定义在了ff_ffplay.c

        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;
        }       
        

        跟踪FFPlayer这个结构体,发现它定义在ijkmedia/ijkplayer/ff_ffplay_def.h中,是一个结构体,由于属性较多,所以就不贴代码了,用到哪个属性再分析哪个属性 嗯,也是通过malloczFFPlayer分配内存,然后去注册内部的msgqueue

        • msg_queue_init
          继续跟踪到ijkmedia/ijkplayer/ff_ffmsg_queue.h
          inline static void msg_queue_init(MessageQueue *q){
              memset(q, 0, sizeof(MessageQueue));
              q->mutex = SDL_CreateMutex();
              q->cond = SDL_CreateCond();
              q->abort_request = 1;
          }
          
          嗯~这是一个内联函数,初始化了MessageQueue,然后通过SDL方法创建了互斥锁与互斥条件,最后设置了队列的abort_request(应该翻译为中止请求)为1(可能为bool值),后续确定,这个方法对ffplayermsgqueue进行了初始化
          接下来也是通过SDL创建ffplayer中的互斥锁和互斥条件,然后调用ffp_reset_internal方法
          • ffp_reset_internal
            该方法定义在了ff_ffplay_def.h中,也是一个内联方法,由于函数体太长,所以就不贴代码了,这个方法中对ffplayer中的一些属性进行了初始化

        接下来是为av_class赋值,ffp_context_class是一个AVClass结构体常量,

        const AVClass ffp_context_class = {
            .class_name       = "FFPlayer",
            .item_name        = ffp_context_to_name,
            .option           = ffp_context_options,
            .version          = LIBAVUTIL_VERSION_INT,
            .child_next       = ffp_context_child_next,
            .child_class_next = ffp_context_child_class_next,
        };
        

        接下来是为meta赋值

        • ijkmeta_create
          该方法定义在ijkmedia/ijkplayer/ijkmate.c
          IjkMediaMeta *ijkmeta_create(){
              IjkMediaMeta *meta = (IjkMediaMeta *)calloc(1, sizeof(IjkMediaMeta));
              if (!meta)
                  return NULL;
          
              meta->mutex = SDL_CreateMutex();
              if (!meta->mutex)
                  goto fail;
          
              return meta;
          fail:
              ijkmeta_destroy(meta);
              return NULL;
          }
          
          使用calloc初始化IjkMediaMeta,然后初始化互斥锁,然后我们看一下IjkMediaMeta结构体
          struct IjkMediaMeta {
          SDL_mutex *mutex;
          
          AVDictionary *dict;
          
          size_t children_count;
          size_t children_capacity;
          IjkMediaMeta **children;
          };
          

        最后通过ffmpeg中的AVOption中的av_opt_set_defaultsffp设置默认属性,ffp_create方法阅读完毕,我们跳出该方法,回到ijkplayer.c中的ijkmp_create方法

      之后设置IjkMediaPlayer中的msg_loop接下来是ijkmp_inc_ref方法 - ijkmp_inc_ref 这个方法的作用是将IjkMediaPlayer中的引用计数+1,这个方法定义在了ijkplayer.cvoid ijkmp_inc_ref(IjkMediaPlayer *mp) { assert(mp); //为ijkplayer中的引用计数+1 __sync_fetch_and_add(&mp->ref_count, 1); } 通过原子化操作,将IjkMediaPlayer中的引用计数+1 到了这里ijkmp_create方法阅读完毕,我们跳出回到ijkplayer_android.c中继续阅读

      接下来通过了SDL_VoutAndroid_CreateForAndroidSurface创建了ffplayer的视频输出设备(surface不是display),然后通过ffpipeline_create_from_android创建pipeline

      • ffpipeline_create_from_android
        该方法定义在ijkmedia/ijkplayer/android/pipeline/ffpipeline_android.c
        IJKFF_Pipeline*ffpipeline_create_from_androi(FFPlayer *ffp){
            IJKFF_Pipeline *pipeline =ffpipeline_alloc(&g_pipeline_class sizeof(IJKFF_Pipeline_Opaque));
            if (!pipeline)
                return pipeline;
            IJKFF_Pipeline_Opaque *opaque =pipeline->opaque;
            //设置ffplayer
            opaque->ffp                   =ffp;
            //设置surface互斥锁
            opaque->surface_mutex         =SDL_CreateMutex();
            //设置左声道音量
            opaque->left_volume           =1.0f;
            //设置右声道音量
            opaque->right_volume          =1.0f;
            if (!opaque->surface_mutex) {
                ALOG("ffpipeline-android:createSDL_CreateMutex failed\n");
                goto fail;
            }
            //销毁
            pipeline->func_destroy             = func_destroy;
            //视频解码器
            pipeline->func_open_video_decoder  = func_open_video_decoder;
            //音频输出
            pipeline->func_open_audio_output   = func_open_audio_output;
            //初始化视频解码器
            pipeline->func_init_video_decoder  = func_init_video_decoder;
            //配置视频解码器
            pipeline->func_config_video_decode = func_config_video_decoder;
            return pipeline;
        fail:
            ffpipeline_free_p(&pipeline);
            return NULL;
        }       
        

      俄罗斯套娃,第一步通过ffpipeline_alloc初始化IJKFF_Pipeline

      • ffpipeline_alloc
        该方法定义ijkmedia/ijkplayer/ff_ffpipeline.c
      IJKFF_Pipeline *ffpipeline_alloc(SDL_Class*opaque_class, size_t opaque_size){
          IJKFF_Pipeline *pipeline =(IJKFF_Pipeline*) calloc(1, sizeo(IJKFF_Pipeline));
          if (!pipeline)
              return NULL;
          pipeline->opaque_class = opaque_class;
          pipeline->opaque       = calloc(1,opaque_size);
          if (!pipeline->opaque) {
              free(pipeline);
              return NULL;
          }
          return pipeline;
      }
      

      该方法通过calloc方法初始化IJKFF_Pipeline,之后设置opaque_class属性,并通过calloc方法初始化opaque属性,最后返回初始化的IJKFF_Pipeline
      pipeline初始化之后,开始初始化之前calloc方法实例化的IJKFF_Pipeline_Opaque
      之后开始设置它的方法函数指针,销毁,打开视频解码器,打开音频输出,初始化视频解码器,配置视频解码器,最后返回 设置完成后的pipeline
      之后再ijkplayer_android.c中的ijkmp_android_create调用了ffpipeline_set_vout方法

      • ffpipeline_set_vout
        该方法也在ffpipeline_android.c
          void ffpipeline_set_vou(IJKFF_Pipeline* pipeline, SDL_Vout*vout)
          {
              if (!check_ffpipeline(pipeline,__func__))
                  return;
              IJKFF_Pipeline_Opaque *opaque =pipeline->opaque;
              opaque->weak_vout = vout;
          }
      

      这段代码先是判断了ffpipeline是否成功初始化,然后将SDL_Vout视频输出设备与pipeline中的weak_vout关联起来 跳出ffpipeline_set_vout方法回到ijkmp_android_create方法,该方法最后返回了设置成功的IjkMediaPlayer
      接下来在ijkplayer_jni.c中的IjkMediaPlayer_native_setup方法内,去执行jni_set_media_player方法

      static IjkMediaPlayer *jni_set_media_player(JNIEnv *env, jobject thiz, IjkMediaPlayer *mp) {
          pthread_mutex_lock(&g_clazz.mutex);
          //获取java层的ijkmediaplayer
          IjkMediaPlayer *old = (IjkMediaPlayer *) (intptr_t) J4AC_IjkMediaPlayer__mNativeMediaPlayer__get__catchAll(
                  env, thiz);
          //如果mp存在,则其引用计数+1
          if (mp) {
              ijkmp_inc_ref(mp);
          }
          //这个方法应该是设置java层的mediaplayer为native层的mp
          J4AC_IjkMediaPlayer__mNativeMediaPlayer__set__catchAll(env, thiz, (intptr_t) mp);
      
          pthread_mutex_unlock(&g_clazz.mutex);
      
          // NOTE: ijkmp_dec_ref may block thread
          if (old != NULL) {
              //销毁ijkplayer
              ijkmp_dec_ref_p(&old);
          }
          //返回的必定是一个nullptr
          return old;
      }
      

      这个方法首先会去获取java层的ijkmediaplayer,然后将新的ijkmediaplayer传递到java层,销毁旧的ijkplayer,最后返回了一个空指针,接下来调用ijkmp_set_weak_thiz方法

      • ijkmp_set_weak_thiz 这个方法定义在ijkplayer.c
      void *ijkmp_set_weak_thiz(IjkMediaPlayer *mp, void *weak_thiz) {
          void *prev_weak_thiz = mp->weak_thiz;
      
          mp->weak_thiz = weak_thiz;
      
          return prev_weak_thiz;
      }
      

      作用很简单,就是将传递进来的弱引用与mp中的弱引用关联起来,最后返回之前的弱引用 最后回到IjkMediaPlayer_native_setup方法,进入ijkmp_set_inject_opaque方法,先看这个方法的第二个参数ijkmp_get_weak_thiz(mp),从函数名猜测是获取mediaplayer中的弱引用,我们跟踪下看看是否猜测正确,该方法定义在ijkplayer.c

      void *ijkmp_get_weak_thiz(IjkMediaPlayer *mp) {
          return mp->weak_thiz;
      }
      

      果然正确,接下来我们查看ijkmp_set_inject_opaque方法

    • ijkmp_set_inject_opaque 该方法定义在ijkplayer.c

    void *ijkmp_set_inject_opaque(IjkMediaPlayer *mp, void *opaque) {
        assert(mp);
    
        MPTRACE("%s(%p)\n", __func__, opaque);
        void *prev_weak_thiz = ffp_set_inject_opaque(mp->ffplayer, opaque);
        MPTRACE("%s()=void\n", __func__);
        return prev_weak_thiz;
    }
    

    调用了ffplayer中的ffp_set_inject_opaque方法,继续查看

    • ffp_set_inject_opaque
        void *ffp_set_inject_opaque(FFPlayer *ffp, void *opaque) {
            if (!ffp)
                return NULL;
            void *prev_weak_thiz = ffp->inject_opaque;
            ffp->inject_opaque = opaque;
    
            av_application_closep(&ffp->app_ctx);
            av_application_open(&ffp->app_ctx, ffp);
            ffp_set_option_int(ffp, FFP_OPT_CATEGORY_FORMAT, "ijkapplication", (int64_t)(intptr_t)
            ffp->app_ctx);
    
            ffp->app_ctx->func_on_app_event = app_func_event;
            return prev_weak_thiz;
        }
    

    仍然是设置新的inject_opaque,然后返回旧的,除掉ffmpeg中的方法(可能)后,该方法调用了ffp_set_option_int方法

    • ffp_set_option_int
          void ffp_set_option_int(FFPlayer *ffp, int opt_category, const char *name, int64_t value) {
              if (!ffp)
                  return;
      
              AVDictionary **dict = ffp_get_opt_dict(ffp, opt_category);
              av_dict_set_int(dict, name, value, 0);
          }
      

    这个方法设置了下ffmpeg中的字典,key是传递进来的"ijkapplication",value是ffplay中的app_ctx,对应的字典是通过ffp_get_opt_dict获取到的

    • ffp_get_opt_dict
    static AVDictionary **ffp_get_opt_dict(FFPlayer *ffp, int opt_category) {
        Assert(ffp);
        switch (opt_category) {
            case FFP_OPT_CATEGORY_FORMAT:
                return &ffp->format_opts;
            case FFP_OPT_CATEGORY_CODEC:
                return &ffp->codec_opts;
            case FFP_OPT_CATEGORY_SWS:
                return &ffp->sws_dict;
            case FFP_OPT_CATEGORY_PLAYER:
                return &ffp->player_opts;
            case FFP_OPT_CATEGORY_SWR:
                return &ffp->swr_opts;
            default:
                av_log(ffp, AV_LOG_ERROR, "unknown option category %d\n", opt_category);
                return NULL;
           }
    }   
    

    第二个参数是字典类型,值为FFP_OPT_CATEGORY_FORMAT通过该方法返回的是format_opts,也就是格式字典。 ffp_set_option_int这个方法是向ffplayer中的format_opts存入了app_ctx,最后是初始化了ffplayer中的appctx中的func_on_app_event,这个属性看起来是一个函数指针。

到这里ffp_set_inject_opaque就阅读完毕。在该方法中,重设了ffplayerinject_opaque属性,并且关闭了就得appctx,打开了新的app_ctx,并把appctx加入到了ffplayer中的format_opts字典中,最后设置了下func_on_app_event函数指针。
接下来ijkmp_set_inject_opaque也执行完毕。
我们回到ijkplayer_jni.c中的IjkMediaPlayer_native_setup方法,去执行ijkmp_set_ijkio_inject_opaque方法。从名字上来看,这里应该是去执行io流相关操作。果然和上个方法一样,字典没变,key变成了ijkiomanager,value变成了ijkio_manager_ctx,就不具体看了。
最后是ijkmp_android_set_mediacodec_select_callback,看起来是硬解相关的回调,进入看一下。

  • ijkmp_android_set_mediacodec_select_callback 该方法定义在ijkmedia/ijkplayer/android/ijkplayer_android.c
    void ijkmp_android_set_mediacodec_select_callback(IjkMediaPlayer *mp, bool (*callback)(void *opaque, ijkmp_mediacodecinfo_context *mcc), void *opaque){
        if (mp && mp->ffplayer && mp->ffplayer->pipeline) {
            ffpipeline_set_mediacodec_select_callback(mp->ffplayer->pipeline, callback, opaque);
        }
    }       
    
    省掉了一些无用代码,去看一下ffpipeline_set_mediacodec_select_callback
    • ffpipeline_set_mediacodec_select_callback 该方法定义在ijkmedia/ijkplayer/android/pipeline/ffpipeline_android.c
      void ffpipeline_set_mediacodec_select_callback(IJKFF_Pipeline* pipeline, bool (*callback)(void *opaque, ijkmp_mediacodecinfo_context *mcc), void *opaque){
          pipeline->opaque->mediacodec_select_callback        = callback;
          pipeline->opaque->mediacodec_select_callback_opaque = opaque;
      }
      
    这里仍然是对硬件相关进行了初始化,猜到了,跳出。

当我们进行到这里,setup就已经执行完毕,这也表示了ijkplayer初始化方法阅读完毕,

总结

IjkMediaPlayer的初始化方法中,先去加载了so库,然后初始化了ffmpeg,与ijkplayer自定义的一些接口字典属性等信息。

调用流程

IjkMediaPlayer.java/IjkMediaPlayer
IjkMediaPlayer.java/IjkMediaPlayer(IjkLibLoader libLoader)
--->initPlayer
------->loadLibrariesOnce
------->ijkmedia/ijkplayer/android/ijkplayer_jni.c/JNI_OnLoad
----------->ijkmedia/ijkplayer/ijkplayer.c/ijkmp_global_init
--------------->ijkmedia/ijkplayer/ff_ffplay.c/ffp_global_init
----------->ijkmedia/ijkplayer/ijkplayer.c/ijkmp_global_set_inject_callback
--------------->ijkmedia/ijkplayer/ijkplayer.c/ijkmp_global_set_inject_callback
------------------->ijkmedia/ijkplayer/ff_ffplay.c/ffp_global_set_inject_callback
----------->FFmpegApi_global_init
--------------->jkmedia/ijkplayer/android/ffmpeg_api_jni.c/FFmpegApi_global_init
--->initNativeOnce
------->native_init(空方法)
--->native_setup
------->ijkmedia/ijkplayer/android/ijkplayer_jni.c/IjkMediaPlayer_native_setup
----------->ijkmedia/ijkplayer/android/ijkplayer_android.c/ijkmp_android_create
--------------->ijkmedia/ijkplayer/ijkplayer.c/ijkmp_create
--------------->ijkmedia/ijkplayer/android/pipeline/fffpipeline_android.c/ffpipeline_create_from_android
------------------->ijkmedia/ijkplayer/ffpipeline.c/ffpipeline_alloc
--------------->ijkmedia/ijkplayer/android/pipeline/fffpipeline_android.c/ffpipeline_set_vout
----------->ijkmedia/ijkplayer/android/ijkplayer_jni.c/jni_set_media_player
--------------->j4a/class/tv/danmaku/ijk/media/player/IjkMediaPlayer.c/通过#define定义的方法
----------->ijkmedia/ijkplayer/ijkplayer.c/ijkmp_set_weak_thiz
----------->ijkmedia/ijkplayer/ijkplayer.c/ijkmp_set_inject_opaque
--------------->ijkmedia/ijkplayer/ff_ffplay.c/ffp_set_inject_opaque
------------------->ijkmedia/ijkplayer/ff_ffplay.c/ffp_set_option_int
----------------------->ijkmedia/ijkplayer/ff_ffplay.c/ffp_get_opt_dict
----------->ijkmedia/ijkplayer/ijkplayer.c/ijkmp_set_ijkio_inject_opaque
--------------->ijkmedia/ijkplayer/ff_ffplay.c/ffp_set_ijkio_inject_opaque
------------------->ijkmedia/ijkplayer/ijkavformat/ijkio_manager_destroyp
------------------->ijkmedia/ijkplayer/ijkavformat/ijkio_manager_create
------------------->ijkmedia/ijkplayer/ijkavformat/ijkio_manager_set_callback
------------------->ijkmedia/ijkplayer/ff_ffplay.c/ffp_set_option_int
----------->ijkmedia/ijkplayer/android/ijkplayer_android.c/ijkmp_android_set_mediacodec_select_callback
--------------->ijkmedia/ijkplayer/android/pipeline/ffpipeline_androidc/ffpipeline_set_mediacodec_select_callback