入口方法
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_init、ijkmp_global_set_inject_callback、FFmpegApi_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中 - FFmpegApi_global_init
到这里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中,是一个结构体,由于属性较多,所以就不贴代码了,用到哪个属性再分析哪个属性 嗯,也是通过mallocz为FFPlayer分配内存,然后去注册内部的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值),后续确定,这个方法对ffplayer的msgqueue进行了初始化
接下来也是通过SDL创建ffplayer中的互斥锁和互斥条件,然后调用ffp_reset_internal方法- ffp_reset_internal
该方法定义在了ff_ffplay_def.h中,也是一个内联方法,由于函数体太长,所以就不贴代码了,这个方法中对ffplayer中的一些属性进行了初始化
- ffp_reset_internal
接下来是为
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_defaults将ffp设置默认属性,ffp_create方法阅读完毕,我们跳出该方法,回到ijkplayer.c中的ijkmp_create方法 - msg_queue_init
之后设置
IjkMediaPlayer中的msg_loop接下来是ijkmp_inc_ref方法 - ijkmp_inc_ref 这个方法的作用是将IjkMediaPlayer中的引用计数+1,这个方法定义在了ijkplayer.c中void 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就阅读完毕。在该方法中,重设了ffplayer的inject_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; }
- ffpipeline_set_mediacodec_select_callback
该方法定义在ijkmedia/ijkplayer/android/pipeline/ffpipeline_android.c中
当我们进行到这里,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