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)圖
到此完成了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)建視頻文件讀取線程