IjkPlayer系列之播放器創(chuàng)建流程
?
?
今天介紹下 IjkPlayer 的播放器創(chuàng)建流程,本文開(kāi)始將正式開(kāi)始 IjkPlayer 的源碼閱讀之旅,閱讀之前可以先看前面同系列文章:主要內(nèi)容如下:
初始化so
Java層播放器創(chuàng)建
IjkMediaPlayer結(jié)構(gòu)體
Native層播放器創(chuàng)建
調(diào)用流程圖
初始化so
從 IjkPlayer 源碼中VideoActivity查看 IjkPlayer 的初始化,關(guān)鍵代碼如下:1//?加載so庫(kù)查看
2IjkMediaPlayer.loadLibrariesOnce(null);
3//?android-ndk-profiler性能分析
4IjkMediaPlayer.native_profileBegin("libijkplayer.so");
loadLibrariesOnce源碼如下:1private?static?volatile?boolean?mIsLibLoaded?=?false;
2public?static?void?loadLibrariesOnce(IjkLibLoader?libLoader)?{
3????synchronized?(IjkMediaPlayer.class)?{
4????????//?保證只加載一次
5????????if?(!mIsLibLoaded)?{
6????????????if?(libLoader?==?null)
7????????????????//?sLocalLibLoader為默認(rèn)的IjkLibLoader
8????????????????libLoader?=?sLocalLibLoader;
9????????????libLoader.loadLibrary("ijkffmpeg");
10????????????libLoader.loadLibrary("ijksdl");
11????????????libLoader.loadLibrary("ijkplayer");
12????????????mIsLibLoaded?=?true;
13????????}
14????}
15}
loadLibrariesOnce 加載相應(yīng)的 so 庫(kù),native_profileBegin 則是 android-ndk-profiler 工具進(jìn)行性能分析的函數(shù),對(duì)應(yīng)的還有native_profileEnd,其定義如下:1public?static?native?void?native_profileBegin(String?libName);上述方法分別調(diào)用了函數(shù)
2public?static?native?void?native_profileEnd();
monstartup 和 moncleanup,分別在程序運(yùn)行開(kāi)始和結(jié)束調(diào)用,關(guān)于 android-ndk-profiler 的這里不做過(guò)多介紹。在 IjkPlayer系列之JNI基礎(chǔ)及源碼目錄介紹 這篇文章中介紹了 JNI 的注冊(cè)方式,其中 IjkPlayer 使用的就是其動(dòng)態(tài)注冊(cè)方式,即當(dāng)調(diào)用 System.loadLibrary 加載庫(kù)的時(shí)候會(huì)去查找 JNI_OnLoad 這個(gè)函數(shù),然后在該函數(shù)回調(diào)中進(jìn)行注冊(cè),下面看下 ijkplayer 中的 JNI_OnLoad 實(shí)現(xiàn),如下:1JNIEXPORT?jint?JNI_OnLoad(JavaVM?*vm,?void?*reserved)其中數(shù)組
2{
3????JNIEnv*?env?=?NULL;
4????//?保存全局的JavaVM
5????g_jvm?=?vm;
6????if?((*vm)->GetEnv(vm,?(void**)?&env,?JNI_VERSION_1_4)?!=?JNI_OK)?{
7????????return?-1;
8????}
9????assert(env?!=?NULL);
10????//?互斥鎖的初始化
11????pthread_mutex_init(&g_clazz.mutex,?NULL?);
12????//?將IjkMediaPlayer轉(zhuǎn)換為一個(gè)全局引用
13????IJK_FIND_JAVA_CLASS(env,?g_clazz.clazz,?JNI_CLASS_IJKPLAYER);
14????//?注冊(cè)Native方法與Java方法相對(duì)應(yīng)
15????(*env)->RegisterNatives(env,?g_clazz.clazz,?g_methods,?NELEM(g_methods)?);
16????//?初始化:注冊(cè)編解碼器、解復(fù)用器、協(xié)議
17????ijkmp_global_init();
18????//?設(shè)置回調(diào),對(duì)應(yīng)Java層的onNativeInvoke回調(diào)函數(shù)
19????ijkmp_global_set_inject_callback(inject_callback);
20????//?注冊(cè)av_base64_encode與FFmpegApi_av_base64_encode的對(duì)應(yīng)關(guān)系
21????FFmpegApi_global_init(env);
22????return?JNI_VERSION_1_4;
23}
g_methods 定義了 Native 函數(shù)與 Java 方法的一一對(duì)應(yīng)關(guān)系,如下:1static?JNINativeMethod?g_methods[]?=?{上述代碼主要工作如下:
2????{
3????????"_setDataSource",
4????????"(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
5????????(void?*)?IjkMediaPlayer_setDataSourceAndHeaders
6????},
7????{?"_setDataSourceFd",???????"(I)V",?????(void?*)?IjkMediaPlayer_setDataSourceFd?},
8????{?"_setDataSource",?????????"(Ltv/danmaku/ijk/media/player/misc/IMediaDataSource;)V",?(void?*)IjkMediaPlayer_setDataSourceCallback?},
9????{?"_setAndroidIOCallback",??"(Ltv/danmaku/ijk/media/player/misc/IAndroidIO;)V",?(void?*)IjkMediaPlayer_setAndroidIOCallback?},
10
11????{?"_setVideoSurface",???????"(Landroid/view/Surface;)V",?(void?*)?IjkMediaPlayer_setVideoSurface?},
12????{?"_prepareAsync",??????????"()V",??????(void?*)?IjkMediaPlayer_prepareAsync?},
13????{?"_start",?????????????????"()V",??????(void?*)?IjkMediaPlayer_start?},
14????{?"_stop",??????????????????"()V",??????(void?*)?IjkMediaPlayer_stop?},
15????{?"seekTo",?????????????????"(J)V",?????(void?*)?IjkMediaPlayer_seekTo?},
16????{?"_pause",?????????????????"()V",??????(void?*)?IjkMediaPlayer_pause?},
17????{?"isPlaying",??????????????"()Z",??????(void?*)?IjkMediaPlayer_isPlaying?},
18????{?"getCurrentPosition",?????"()J",??????(void?*)?IjkMediaPlayer_getCurrentPosition?},
19????{?"getDuration",????????????"()J",??????(void?*)?IjkMediaPlayer_getDuration?},
20????{?"_release",???????????????"()V",??????(void?*)?IjkMediaPlayer_release?},
21????{?"_reset",?????????????????"()V",??????(void?*)?IjkMediaPlayer_reset?},
22????{?"setVolume",??????????????"(FF)V",????(void?*)?IjkMediaPlayer_setVolume?},
23????{?"getAudioSessionId",??????"()I",??????(void?*)?IjkMediaPlayer_getAudioSessionId?},
24????{?"native_init",????????????"()V",??????(void?*)?IjkMediaPlayer_native_init?},
25????{?"native_setup",???????????"(Ljava/lang/Object;)V",?(void?*)?IjkMediaPlayer_native_setup?},
26????{?"native_finalize",????????"()V",??????(void?*)?IjkMediaPlayer_native_finalize?},
27
28????{?"_setOption",?????????????"(ILjava/lang/String;Ljava/lang/String;)V",?(void?*)?IjkMediaPlayer_setOption?},
29????{?"_setOption",?????????????"(ILjava/lang/String;J)V",??????????????????(void?*)?IjkMediaPlayer_setOptionLong?},
30
31????{?"_getColorFormatName",????"(I)Ljava/lang/String;",????(void?*)?IjkMediaPlayer_getColorFormatName?},
32????{?"_getVideoCodecInfo",?????"()Ljava/lang/String;",?????(void?*)?IjkMediaPlayer_getVideoCodecInfo?},
33????{?"_getAudioCodecInfo",?????"()Ljava/lang/String;",?????(void?*)?IjkMediaPlayer_getAudioCodecInfo?},
34????{?"_getMediaMeta",??????????"()Landroid/os/Bundle;",????(void?*)?IjkMediaPlayer_getMediaMeta?},
35????{?"_setLoopCount",??????????"(I)V",?????????????????????(void?*)?IjkMediaPlayer_setLoopCount?},
36????{?"_getLoopCount",??????????"()I",??????????????????????(void?*)?IjkMediaPlayer_getLoopCount?},
37????{?"_getPropertyFloat",??????"(IF)F",????????????????????(void?*)?ijkMediaPlayer_getPropertyFloat?},
38????{?"_setPropertyFloat",??????"(IF)V",????????????????????(void?*)?ijkMediaPlayer_setPropertyFloat?},
39????{?"_getPropertyLong",???????"(IJ)J",????????????????????(void?*)?ijkMediaPlayer_getPropertyLong?},
40????{?"_setPropertyLong",???????"(IJ)V",????????????????????(void?*)?ijkMediaPlayer_setPropertyLong?},
41????{?"_setStreamSelected",?????"(IZ)V",????????????????????(void?*)?ijkMediaPlayer_setStreamSelected?},
42
43????{?"native_profileBegin",????"(Ljava/lang/String;)V",????(void?*)?IjkMediaPlayer_native_profileBegin?},
44????{?"native_profileEnd",??????"()V",??????????????????????(void?*)?IjkMediaPlayer_native_profileEnd?},
45
46????{?"native_setLogLevel",?????"(I)V",?????????????????????(void?*)?IjkMediaPlayer_native_setLogLevel?},
47????{?"_setFrameAtTime",????????"(Ljava/lang/String;JJII)V",?(void?*)?IjkMediaPlayer_setFrameAtTime?},
48};
注冊(cè) Native 方法與 Java 方法之間的對(duì)應(yīng)關(guān)系。
注冊(cè)編解碼器、解復(fù)用器、協(xié)議等。
設(shè)置
onNativeInvoke回調(diào)供 Native 層調(diào)用,主要關(guān)心其返回值,比如斷網(wǎng)重連等。
Java層播放器創(chuàng)建
看下 Java 層IjkMediaPlayer 的創(chuàng)建1IjkMediaPlayer?ijkMediaPlayer?=?new?IjkMediaPlayer();查看構(gòu)造方法如下:
1public?IjkMediaPlayer()?{可見(jiàn)初始化的時(shí)候加載了 ijkffmpeg、ijksdl、ijkplayer 庫(kù),創(chuàng)建了
2????//?sLocalLibLoader為默認(rèn)的IjkLibLoader
3????this(sLocalLibLoader);
4}
5
6public?IjkMediaPlayer(IjkLibLoader?libLoader)?{
7????initPlayer(libLoader);
8}
9
10private?void?initPlayer(IjkLibLoader?libLoader)?{
11????//?嘗試加載庫(kù),避免之前從未加載過(guò)so
12????loadLibrariesOnce(libLoader);
13????//?對(duì)應(yīng)c層IjkMediaPlayer_native_init方法,暫時(shí)為空實(shí)現(xiàn)
14????initNativeOnce();
15
16????//?用來(lái)處理Iik底層傳遞上來(lái)的消息
17????Looper?looper;
18????if?((looper?=?Looper.myLooper())?!=?null)?{
19????????mEventHandler?=?new?EventHandler(this,?looper);
20????}?else?if?((looper?=?Looper.getMainLooper())?!=?null)?{
21????????mEventHandler?=?new?EventHandler(this,?looper);
22????}?else?{
23????????mEventHandler?=?null;
24????}
25
26????//?IjkMediaPlayer包裝成弱引用傳遞到native層
27????native_setup(new?WeakReference(this));
28}
mEventHandler 處理 Native 拋上來(lái)的播放事件,比如播放開(kāi)始、播放完成、播放出錯(cuò)等事件,具體由 Native 層調(diào)用 Java 層的函數(shù) postEventFromNative 來(lái)發(fā)送對(duì)應(yīng)的事件,在閱讀 C 層代碼之前,先了解一下 IjkMediaPlayer 結(jié)構(gòu)體。IjkMediaPlayer結(jié)構(gòu)體
IjkPlayer 對(duì)應(yīng)IjkMediaPlayer 結(jié)構(gòu)體,其初始化主要是對(duì)該結(jié)構(gòu)體進(jìn)行初始化,其定義如下:1struct?IjkMediaPlayer?{整個(gè)播放流程中涉及到的參數(shù)基本都是
2????/*?IjkMediaPlayer創(chuàng)建一次則ref_count計(jì)數(shù)加一次?*/
3????volatile?int?ref_count;
4????/*?保護(hù)接口調(diào)用的鎖*/
5????pthread_mutex_t?mutex;
6????/* FFPlayer是原ffplayer里的結(jié)構(gòu)體,有被ijk擴(kuò)展?*/
7????FFPlayer?*ffplayer;
8????/*?用于ijkPlayer回調(diào)給應(yīng)用層的一個(gè)消息循環(huán)函數(shù)?*/
9????int?(*msg_loop)(void*);
10????/*?消息線程?*/
11????SDL_Thread?*msg_thread;
12????SDL_Thread?_msg_thread;
13????/*?播放器狀態(tài)?*/
14????int?mp_state;
15????/*?播放地址?*/
16????char?*data_source;
17????/*?Java層IjkMediaPlayer對(duì)應(yīng)弱引用對(duì)象?*/
18????void?*weak_thiz;
19????/*?是否重新播放?*/
20????int?restart;
21????/*?restart是否從頭開(kāi)始?*/
22????int?restart_from_beginning;
23????/*?標(biāo)識(shí)用戶是不是seek了進(jìn)度條?*/
24????int?seek_req;
25????/*?seek的毫秒值?*/
26????long?seek_msec;
27};
IjkMediaPlayer 結(jié)構(gòu)體中的成員變量,后面 Native 層 IjkPlayer 的創(chuàng)建都是在初始化上述結(jié)構(gòu)體中的成員變量。Native層播放器創(chuàng)建
Native 層播放器的創(chuàng)建主要是結(jié)構(gòu)體IjkMediaPlayer 的創(chuàng)建及初始化,這里接著上文繼續(xù)查看 native_setup 方法的具體實(shí)現(xiàn)如下:1static?void上述代碼主要是開(kāi)始創(chuàng)建 C 層的
2IjkMediaPlayer_native_setup(JNIEnv?*env,?jobject?thiz,?jobject?weak_this)
3{
4????MPTRACE("%s\n",?__func__);
5????//?創(chuàng)建C層對(duì)應(yīng)的IjkMediaPlayer
6????IjkMediaPlayer?*mp?=?ijkmp_android_create(message_loop);
7????JNI_CHECK_GOTO(mp,?env,?"java/lang/OutOfMemoryError",?"mpjni:?native_setup:?ijkmp_create()?failed",?LABEL_RETURN);
8????//?Java層mNativeMediaPlayer初始化
9????jni_set_media_player(env,?thiz,?mp);
10????//?初始化mp->weak_thiz
11????ijkmp_set_weak_thiz(mp,?(*env)->NewGlobalRef(env,?weak_this));
12????//?初始化ffp->inject_opaque等
13????ijkmp_set_inject_opaque(mp,?ijkmp_get_weak_thiz(mp));
14????//?初始化ffp->ijkio_inject_opaque等
15????ijkmp_set_ijkio_inject_opaque(mp,?ijkmp_get_weak_thiz(mp));
16????//?設(shè)置解碼器選擇回調(diào)
17????ijkmp_android_set_mediacodec_select_callback(mp,?mediacodec_select_callback,?ijkmp_get_weak_thiz(mp));
18
19LABEL_RETURN:
20????ijkmp_dec_ref_p(&mp);
21}
IjkMediaPlayer 、其對(duì)應(yīng)結(jié)構(gòu)體部分屬性的初始化及解碼器回調(diào),mediacodec_select_callback 會(huì)通過(guò)調(diào)用 Java 層的函數(shù) onSelectCodec 獲取合適的解碼器信息,可在 IjkMediaCodecInfo 中進(jìn)行解碼器的適配。繼續(xù)查看 ijkmp_android_create 函數(shù)如下:1IjkMediaPlayer?*ijkmp_android_create(int(*msg_loop)(void*))其中
2{
3????//?填充IjkMediaPlayer結(jié)構(gòu)體
4????IjkMediaPlayer?*mp?=?ijkmp_create(msg_loop);
5????if?(!mp)
6????????goto?fail;
7????//?初始化SDL_Vout,表示IJK中的顯示上下文
8????mp->ffplayer->vout?=?SDL_VoutAndroid_CreateForAndroidSurface();
9????if?(!mp->ffplayer->vout)
10????????goto?fail;
11????//?初始化IJKFF_Pipeline,解碼器、音頻輸出
12????mp->ffplayer->pipeline?=?ffpipeline_create_from_android(mp->ffplayer);
13????if?(!mp->ffplayer->pipeline)
14????????goto?fail;
15????//?綁定SDL_Vout到IJKFF_Pipeline_Opaque
16????ffpipeline_set_vout(mp->ffplayer->pipeline,?mp->ffplayer->vout);
17????return?mp;
18fail:
19????ijkmp_dec_ref_p(&mp);
20????return?NULL;
21}
ijkmp_create 主要是創(chuàng)建了 FFPlayer,并將 msg_loop 進(jìn)行賦值,msg_loop 事件循環(huán)相關(guān)調(diào)用將在后續(xù)文章中介紹,繼續(xù)看下后面幾個(gè)函數(shù):SDL_VoutAndroid_CreateForAndroidSurface:初始化IjkPlayer的 顯示上下文SDL_Vout。ffpipeline_create_from_android:初始化IJKFF_Pipeline,解碼器、音頻輸出。ffpipeline_set_vout:綁定SDL_Vout到IJKFF_Pipeline_Opaque。
調(diào)用流程圖
IjkPlayer 創(chuàng)建主要函數(shù)調(diào)用流程如下:
其他細(xì)節(jié)會(huì)在后續(xù)的文章中介紹,下一篇將介紹 IjkPlayer 中消息循環(huán)機(jī)制。推薦閱讀:??
評(píng)論
圖片
表情
