ijkplayer源碼分析之surface與opengl關(guān)聯(lián)初始化(一)

ijkplayer源碼分析之opengl與surface關(guān)聯(lián)初始化

=====================================================

ijkplayer源碼分析系列文章列表:

ijkplayer源碼分析之surface與opengl es關(guān)聯(lián)初始化(一)

ijkplayer源碼分析之a(chǎn)udio與opensl es初始化(二)

======================================================

上層java代碼

  • IjkMediaPlayer.java

構(gòu)造方法

step 1: 
IjkMediaPlayer()
    |--this(sLocalLibLoader);
        |--initPlayer(libLoader);
            |--loadLibrariesOnce(libLoader); //加載所需要的庫
            |   |--libLoader.loadLibrary("ijkffmpeg");
            |   |--libLoader.loadLibrary("ijksdl");
            |   |--libLoader.loadLibrary("ijkplayer"); // step 2: 
            |--initNativeOnce(); 
            |   |--native_init(); //初始化 native 
            |
            | 
            |--native_setup(new WeakReference<IjkMediaPlayer>(this)); //step 3:


底層c代碼

IjkMediaPlayer.java --> ijkplayer_jni.c

step 2.5:
native_init() --> IjkMediaPlayer_native_init
//底層是個空實現(xiàn)
IjkMediaPlayer_native_init(JNIEnv *env)
{
    MPTRACE("%s\n", __func__);
}

step 3:
native_setup --> IjkMediaPlayer_native_setup

IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{

    IjkMediaPlayer *mp = ijkmp_android_create(message_loop); //創(chuàng)建一個 player 結(jié)構(gòu)體

    jni_set_media_player(env, thiz, mp); //保存當(dāng)前player結(jié)構(gòu)體
    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);
}

加載class的宏定義

j4a_allclasses.loader.h  //頭文件中包含加載class的宏定義

J4A_LOAD_CLASS(java_nio_Buffer);
J4A_LOAD_CLASS(java_nio_ByteBuffer);
J4A_LOAD_CLASS(java_util_ArrayList);
J4A_LOAD_CLASS(android_media_AudioTrack);
J4A_LOAD_CLASS(android_media_MediaCodec);
J4A_LOAD_CLASS(android_media_MediaFormat);
J4A_LOAD_CLASS(android_media_PlaybackParams);
J4A_LOAD_CLASS(android_os_Build);
J4A_LOAD_CLASS(android_os_Bundle);
J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_misc_IMediaDataSource);
J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_misc_IIjkIOHttp);
J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_IjkMediaPlayer);

#define J4A_LOAD_CLASS(class__)  --> J4A_loadClass__J4AC_##class__(env)
--> J4A_loadClass__J4AC_IjkMediaPlayer
or 
--> J4A_loadClass__J4AC_MediaCodec

class初始化流程

step 2:
Ijksdl_android_jni.c
JNI_OnLoad(JavaVM *vm, void *reserved)
    |--J4A_LoadAll__catchAll(env);
J4a_allclasses.c
J4A_LoadAll__catchAll(JNIEnv *env)  //調(diào)用頭文件中的宏定義函數(shù)
        |--#include "j4a/j4a_allclasses.loader.h"
            |--J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_IjkMediaPlayer);
                |--J4A_loadClass__J4AC_##class__(env);
                    |--J4A_loadClass__J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer(env)
                        |--class_id = class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id;
                        |--name     = "mNativeMediaPlayer";
                        |--sign     = "J";
                        |--class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.field_mNativeMediaPlayer = J4A_GetFieldID__catchAll(env, class_id, name, sign);

IjkMediaPlayer結(jié)構(gòu)體

struct IjkMediaPlayer {
    volatile int ref_count;
    pthread_mutex_t mutex;
    FFPlayer *ffplayer;

    int (*msg_loop)(void*);
    SDL_Thread *msg_thread;
    SDL_Thread _msg_thread;

    int mp_state;
    char *data_source;
    void *weak_thiz;

    int restart;
    int restart_from_beginning;
    int seek_req;
    long seek_msec;
};

step 3的創(chuàng)建player過程

SDL_Class結(jié)構(gòu)體變量

typedef struct SDL_Class {
    const char *name;
} SDL_Class;

static SDL_Class g_class = {
    .name = "EGL",
};

static SDL_Class g_nativewindow_class = {
    .name = "ANativeWindow_Vout",
};

static SDL_Class g_pipeline_class = {
    .name = "ffpipeline_ffplay",
};

SDL_Vout結(jié)構(gòu)體

struct SDL_Vout {
    SDL_mutex *mutex;

    SDL_Class       *opaque_class;
    SDL_Vout_Opaque *opaque;
    SDL_VoutOverlay *(*create_overlay)(int width, int height, int frame_format, SDL_Vout *vout);
    void (*free_l)(SDL_Vout *vout);
    int (*display_overlay)(SDL_Vout *vout, SDL_VoutOverlay *overlay);

    Uint32 overlay_format;
};

SDL_Vout_Opaque結(jié)構(gòu)體

typedef struct SDL_Vout_Opaque {
    ANativeWindow   *native_window;
    SDL_AMediaCodec *acodec;
    int              null_native_window_warned; // reduce log for null window
    int              next_buffer_id;

    ISDL_Array       overlay_manager;
    ISDL_Array       overlay_pool;

    IJK_EGL         *egl;
} SDL_Vout_Opaque;

IJKFF_Pipeline結(jié)構(gòu)體

struct IJKFF_Pipeline {
    SDL_Class             *opaque_class;
    IJKFF_Pipeline_Opaque *opaque;

    void            (*func_destroy)             (IJKFF_Pipeline *pipeline);
    IJKFF_Pipenode *(*func_open_video_decoder)  (IJKFF_Pipeline *pipeline, FFPlayer *ffp);
    SDL_Aout       *(*func_open_audio_output)   (IJKFF_Pipeline *pipeline, FFPlayer *ffp);
};
IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
    |--IjkMediaPlayer *mp = ijkmp_android_create(message_loop);
        |--IjkMediaPlayer *mp = ijkmp_create(msg_loop);
        |--mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface(); //創(chuàng)建surface用于視頻輸出
        |   |--SDL_VoutAndroid_CreateForANativeWindow();
        |       |--SDL_Vout_CreateInternal(size_t opaque_size)
        |       |   |--SDL_Vout *vout = (SDL_Vout*) calloc(1, sizeof(SDL_Vout));
        |       |
        |       |--SDL_Vout_Opaque *opaque = vout->opaque;
        |       |--opaque->native_window = NULL;
        |       |--opaque->egl = IJK_EGL_create();創(chuàng)建egl結(jié)構(gòu)體
        |       |       |--IJK_EGL *egl = (IJK_EGL*) mallocz(sizeof(IJK_EGL));
        |       |       |--egl->opaque_class = &g_class;
        |       |       |--egl->opaque = mallocz(sizeof(IJK_EGL_Opaque));
        |       |
        |       |--vout->opaque_class    = &g_nativewindow_class;
        |       |--vout->create_overlay  = func_create_overlay;
        |       |--vout->free_l          = func_free_l;
        |       |--vout->display_overlay = func_display_overlay
        |       
        |
        |--mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer); //創(chuàng)建ffplay管線
        |   |--IJKFF_Pipeline *pipeline = ffpipeline_alloc(&g_pipeline_class, sizeof(IJKFF_Pipeline_Opaque));
        |   |--IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
        |   |--opaque->ffp                   = ffp;
        |   |--opaque->surface_mutex         = SDL_CreateMutex();
        |   |--opaque->left_volume           = 1.0f;
        |   |--opaque->right_volume          = 1.0f;
        |   |--pipeline->func_destroy            = func_destroy;
        |   |--pipeline->func_open_video_decoder = func_open_video_decoder;
        |   |--pipeline->func_open_audio_output  = func_open_audio_output;
        |
        |
        |--ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout); //將管線和視頻輸出關(guān)聯(lián)
            |--IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
            |--opaque->weak_vout = vout;

初始化總結(jié)

step 1中進(jìn)行IjkMediaPlayer實例的構(gòu)造,構(gòu)造過程中完成的step 2加載so并在加載過程中將底層的一些信息保存到IjkMediaPlayer實例中
step 3將IjkMediaPlayer實例對象設(shè)置到底層IjkMediaPlayer結(jié)構(gòu)體中,創(chuàng)建底層IjkMediaPlayer結(jié)構(gòu)體罢绽。在創(chuàng)建結(jié)構(gòu)體的同時設(shè)置message_loop回調(diào)函數(shù)實現(xiàn)將底層消息傳遞到上層的作用

初始化后個結(jié)構(gòu)體的關(guān)聯(lián)圖

結(jié)構(gòu)體關(guān)聯(lián)圖.jpg

到此完成了IJKPlayer的初始化

IjkMediaPlayer使用前的設(shè)置

IjkMediaPlayer被封裝到了IjkVideoView中馅巷,在IjkVideoView實例化的過程中會完成上面的初始化過程亡蓉。同時在構(gòu)造函數(shù)中會將surface設(shè)置到底層,下面是初始化圖像渲染環(huán)境的代碼分析

以設(shè)置surfaceView為例:

step 1:
initVideoView()
    |--initRenders();
        |--setRender(mCurrentRender);
            |--renderView = new SurfaceRenderView(getContext());
            |   |--getHolder().addCallback(mSurfaceCallback); //當(dāng)SurfaceRenderView被添加到屏幕顯示時會回調(diào) mSurfaceCallback 中的 surfaceCreated 方法,在 surfaceCreated 方法中又會調(diào)用注冊到 renderView 中的 mSHCallback 實例的 onSurfaceCreated 方法
            |--setRenderView(renderView);
                |--mRenderView.addRenderCallback(mSHCallback);

mSHCallback = new IRenderView.IRenderCallback() //在 mSHCallback 的 onSurfaceCreated 中將 mSurfaceHolder 賦值
    |--onSurfaceCreated()
        |--mSurfaceHolder = holder;                 

step 2:

setVideoPath()
    |--setVideoURI()
        |--openVideo();
            |--mMediaPlayer = createPlayer(mSettings.getPlayer());
            |--bindSurfaceHolder(mMediaPlayer, mSurfaceHolder); //完成了 mSurfaceHolder 賦值后就將它和 mMediaPlayer 進(jìn)行綁定
                |--holder.bindToMediaPlayer(mp); 即 mSurfaceHolder.bindToMediaPlayer(mMediaPlayer)
                    |--InternalSurfaceHolder.bindToMediaPlayer()
                        |--mp.setDisplay(mSurfaceHolder); //即 mMediaPlayer.setDisplay(mSurfaceHolder)
                            |--surface = sh.getSurface();
                            |-- _setVideoSurface(surface); //native 方法設(shè)置surface到native

go to native _setVideoSurface

ijkplayer_jni.c

IjkMediaPlayer_setVideoSurface
    |--ijkmp_android_set_surface //go to Ijkplayer_android.c

Ijkplayer_android.c

ijkmp_android_set_surface(JNIEnv *env, jobject thiz, jobject jsurface)
    |--ijkmp_android_set_surface_l(env, mp, android_surface);
        |--SDL_VoutAndroid_SetAndroidSurface(env, mp->ffplayer->vout, android_surface); //Ijksdl_vout_android_surface.c
        |--ffpipeline_set_surface(env, mp->ffplayer->pipeline, android_surface);

Ijksdl_vout_android_surface.c

SDL_VoutAndroid_SetAndroidSurface(env, mp->ffplayer->vout, android_surface)
    |--native_window = ANativeWindow_fromSurface(env, android_surface);
    |--SDL_VoutAndroid_SetNativeWindow(vout, native_window); //Ijksdl_vout_android_nativewindow.c
        |--SDL_VoutAndroid_SetNativeWindow_l(vout, native_window);
            |--IJK_EGL_terminate(opaque->egl);
            |--SDL_VoutAndroid_invalidateAllBuffers_l(vout);
            |--if (opaque->native_window)
            |       |--ANativeWindow_release(opaque->native_window);
            |--if (native_window)
            |       |--ANativeWindow_acquire(native_window);
            |--opaque->native_window = native_window;

在上面的初始化的過程中就已經(jīng)通過IjkMediaPlayer_native_setup函數(shù)對SDL_Vout結(jié)構(gòu)中的回調(diào)函數(shù)役首,和egl信息進(jìn)行了初始化笨枯。而IjkMediaPlayer_setVideoSurface則將native_window的地址保存到SDL_Vout->opaque->native_window中瘟滨。到目前為止所有關(guān)于顯示的信息都保存的了SDL_Vout結(jié)構(gòu)體中.以便在ff_player.c文件中進(jìn)行調(diào)用

下面展示了調(diào)用過程:

IjkMediaPlayer_prepareAsync //Ijkplayer_jni.c
    |--ijkmp_prepare_async
        |--ijkmp_prepare_async_l(IjkMediaPlayer *mp) //Ijkplayer.c
            |--ffp_prepare_async_l(FFPlayer *ffp, const char *file_name) //Ff_ffplay.c
            |--stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat)
                |--SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, "ff_vout");
                |   |--video_refresh_thread(void *arg)
                |       |--video_refresh(FFPlayer *opaque, double *remaining_time
                |           |--video_display2(FFPlayer *ffp)
                |               |--video_image_display2(FFPlayer *ffp)
                |                   |--SDL_VoutDisplayYUVOverlay(ffp->vout, vp->bmp);
                |                       |--vout->display_overlay(vout, overlay);  // vout->display_overlay = func_display_overlay;在SDL_VoutAndroid_CreateForANativeWindow中設(shè)置了回調(diào)函數(shù)
                |                           |--func_display_overlay
                |                               |--func_display_overlay_l(vout, overlay);
                |                                   |--IJK_EGL_display(opaque->egl, native_window, overlay);
                |                                   |   |--IJK_EGL_makeCurrent(egl, window)
                |                                   |   |   |--eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context)
                |                                   |   |--IJK_EGL_display_internal(egl, window, overlay);
                |                                   |   |   |--IJK_EGL_prepareRenderer(egl, overlay)
                |                                   |   |   |--IJK_GLES2_Renderer_renderOverlay(opaque->renderer, overlay)
                |                                   |   |   |--eglSwapBuffers(egl->display, egl->surface);
                |                                   |   |
                |                                   |   |--eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
                |                                   |--eglReleaseThread(); // FIXME: call at thread exit
                |--SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read"); //創(chuàng)建視頻文件讀取線程

流程圖

流程圖.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市为牍,隨后出現(xiàn)的幾起案子哼绑,更是在濱河造成了極大的恐慌,老刑警劉巖碉咆,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抖韩,死亡現(xiàn)場離奇詭異,居然都是意外死亡疫铜,警方通過查閱死者的電腦和手機茂浮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人席揽,你說我怎么就攤上這事顽馋。” “怎么了幌羞?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵寸谜,是天一觀的道長。 經(jīng)常有香客問我属桦,道長熊痴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任聂宾,我火速辦了婚禮果善,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亏吝。我一直安慰自己岭埠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布蔚鸥。 她就那樣靜靜地躺著惜论,像睡著了一般。 火紅的嫁衣襯著肌膚如雪止喷。 梳的紋絲不亂的頭發(fā)上馆类,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音弹谁,去河邊找鬼乾巧。 笑死,一個胖子當(dāng)著我的面吹牛预愤,可吹牛的內(nèi)容都是我干的沟于。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼植康,長吁一口氣:“原來是場噩夢啊……” “哼旷太!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起销睁,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤供璧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后冻记,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睡毒,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年冗栗,在試婚紗的時候發(fā)現(xiàn)自己被綠了演顾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片供搀。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖偶房,靈堂內(nèi)的尸體忽然破棺而出趁曼,到底是詐尸還是另有隱情,我是刑警寧澤棕洋,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布挡闰,位于F島的核電站,受9級特大地震影響掰盘,放射性物質(zhì)發(fā)生泄漏摄悯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一愧捕、第九天 我趴在偏房一處隱蔽的房頂上張望奢驯。 院中可真熱鬧,春花似錦次绘、人聲如沸瘪阁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至豁跑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間艇拍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工宠纯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卸夕,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓快集,卻偏偏與公主長得像勃救,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子治力,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容