IjkPlayer系列之播放器创建流程
今天介绍下 IjkPlayer 的播放器创建流程,本文开始将正式开始 IjkPlayer 的源码阅读之旅,主要内容如下:
- 初始化so
- Java层播放器创建
- IjkMediaPlayer结构体
- Native层播放器创建
- 调用流程图
阅读之前可以先看前面几篇文章:
初始化so
从 IjkPlayer 源码中VideoActivity查看 IjkPlayer 的初始化,关键代码如下:
1 | // 加载so库 |
查看loadLibrariesOnce源码如下:
1 | private static volatile boolean mIsLibLoaded = false; |
loadLibrariesOnce 加载相应的 so 库,native_profileBegin 则是 android-ndk-profiler 工具进行性能分析的函数,对应的还有native_profileEnd,其定义如下:
1 | public static native void native_profileBegin(String libName); |
上述方法分别调用了函数 monstartup 和 moncleanup,分别在程序运行开始和结束调用,关于 android-ndk-profiler 的这里不做过多介绍。
在 IjkPlayer系列之JNI基础及源码目录介绍 这篇文章中介绍了 JNI 的注册方式,其中 IjkPlayer 使用的就是其动态注册方式,即当调用 System.loadLibrary 加载库的时候会去查找 JNI_OnLoad 这个函数,然后在该函数回调中进行注册,下面看下 ijkplayer 中的 JNI_OnLoad 实现,如下:
1 | JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) |
其中数组 g_methods 定义了 Native 函数与 Java 方法的一一对应关系,如下:
1 | static JNINativeMethod g_methods[] = { |
上述代码主要工作如下:
- 注册 Native 方法与 Java 方法之间的对应关系。
- 注册编解码器、解复用器、协议等。
- 设置
onNativeInvoke回调供 Native 层调用,主要关心其返回值,比如断网重连等。
Java层播放器创建
看下 Java 层 IjkMediaPlayer 的创建
1 | IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer(); |
查看构造方法如下:
1 | public IjkMediaPlayer() { |
可见初始化的时候加载了 ijkffmpeg、ijksdl、ijkplayer 库,创建了 mEventHandler 处理 Native 拋上来的播放事件,比如播放开始、播放完成、播放出错等事件,具体由 Native 层调用 Java 层的函数 postEventFromNative 来发送对应的事件,在阅读 C 层代码之前,先了解一下 IjkMediaPlayer 结构体。
IjkMediaPlayer结构体
IjkPlayer 对应 IjkMediaPlayer 结构体,其初始化主要是对该结构体进行初始化,其定义如下:
struct IjkMediaPlayer {
/* IjkMediaPlayer创建一次则ref_count计数加一次 */
volatile int ref_count;
/* 保护接口调用的锁*/
pthread_mutex_t mutex;
/* FFPlayer是原ffplayer里的结构体,有被ijk扩展 */
FFPlayer *ffplayer;
/* 用于ijkPlayer回调给应用层的一个消息循环函数 */
int (*msg_loop)(void*);
/* 消息线程 */
SDL_Thread *msg_thread;
SDL_Thread _msg_thread;
/* 播放器状态 */
int mp_state;
/* 播放地址 */
char *data_source;
/* Java层IjkMediaPlayer对应弱引用对象 */
void *weak_thiz;
/* 是否重新播放 */
int restart;
/* restart是否从头开始 */
int restart_from_beginning;
/* 标识用户是不是seek了进度条 */
int seek_req;
/* seek的毫秒值 */
long seek_msec;
};
整个播放流程中涉及到的参数基本都是 IjkMediaPlayer 结构体中的成员变量,后面 Native 层 IjkPlayer 的创建都是在初始化上述结构体中的成员变量。
Native层播放器创建
Native 层播放器的创建主要是结构体 IjkMediaPlayer 的创建及初始化,这里接着上文继续查看 native_setup 方法的具体实现如下:
1 | static void |
上述代码主要是开始创建 C 层的 IjkMediaPlayer 、其对应结构体部分属性的初始化及解码器回调,mediacodec_select_callback 会通过调用 Java 层的函数 onSelectCodec 获取合适的解码器信息,可在 IjkMediaCodecInfo 中进行解码器的适配。
继续查看 ijkmp_android_create 函数如下:
1 | IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*)) |
其中 ijkmp_create 主要是创建了 FFPlayer,并将 msg_loop 进行赋值,msg_loop 事件循环相关调用将在后续文章中介绍,继续看下后面几个函数:
SDL_VoutAndroid_CreateForAndroidSurface:初始化IjkPlayer的 显示上下文SDL_Vout。ffpipeline_create_from_android:初始化IJKFF_Pipeline,解码器、音频输出。ffpipeline_set_vout:绑定SDL_Vout到IJKFF_Pipeline_Opaque。
调用流程图
IjkPlayer 创建主要函数调用流程如下:
sequenceDiagram
IjkMediaPlayer.java->>ijkplayer_jni.c: IjkMediaPlayer_native_setup
ijkplayer_jni.c->>ijkplayer_android.c: ijkmp_android_create
ijkplayer_jni.c->>ijkplayer_android.c: ijkmp_xxx
ijkplayer_android.c->>ijkplayer.c:ijkmp_create
ijkplayer_android.c->>ijkplayer.c:SDL_VoutAndroid_CreateForAndroidSurface
ijkplayer_android.c->>ijkplayer.c:ffpipeline_create_from_android
ijkplayer_android.c->>ijkplayer.c:ffpipeline_set_vout
ijkplayer.c->>ff_ffplay.c:ffp_create
其他细节会在后续的文章中介绍,下一篇将介绍 IjkPlayer 中消息循环机制。