從 Github 上拉取最新的源碼,目錄結構大體以下:javascript
H:\MPV ├─.github ├─audio │ ├─decode │ ├─filter │ └─out ├─ci ├─common ├─demux ├─DOCS │ └─man ├─etc ├─filters ├─input ├─libmpv ├─misc ├─options ├─osdep │ ├─android │ ├─ar │ ├─macos │ └─win32 │ └─include ├─player │ ├─javascript │ └─lua ├─stream ├─sub ├─ta ├─test │ └─ref ├─TOOLS │ ├─lua │ ├─mpv-osd-symbols.sfdir │ └─osxbundle │ └─mpv.app │ └─Contents │ ├─MacOS │ │ └─lib │ └─Resources ├─video │ ├─decode │ ├─filter │ └─out │ ├─cocoa │ ├─cocoa-cb │ ├─d3d11 │ ├─gpu │ ├─hwdec │ ├─opengl │ ├─placebo │ ├─vulkan │ └─win32 └─waftools ├─checks ├─detections ├─fragments └─generators
libmpv/mpv.def
裏面。可是這個 .def 文件不是標準的導出文件。若是是啓動播放器進行播放,則首先會進行一個內部狀態的初始化,主要是初始化了MPContext
這個結構體。這個結構體是一個大雜燴,全部播放相關的參數、動態變化的屬性都綁定到這上面。而後內核進入 idle 狀態,等待播放視頻。java
打開第一個媒體文件的時候,會開始進行視頻/音頻播放鏈路(video_output_chain)初始化,其中就包括初始化解碼和渲染模塊。渲染模塊由結構體 vo_driver
定義,(mpv 內部使用結構體來定義接口),例如 vo_gpu 的定義以下:android
const struct vo_driver video_out_gpu = { .description = "Shader-based GPU Renderer", .name = "gpu", .caps = VO_CAP_ROTATE90, .preinit = preinit, .query_format = query_format, .reconfig = reconfig, .control = control, .get_image = get_image, .draw_frame = draw_frame, .flip_page = flip_page, .get_vsync = get_vsync, .wait_events = wait_events, .wakeup = wakeup, .uninit = uninit, .priv_size = sizeof(struct gpu_priv), .options = options, };
接下來咱們都以這個 Windows 下最經常使用的 vo 驅動器——vo_gpu 爲例。在 /video/out/vo.c 中,你能夠看到全部支持的 vo_driver:git
const struct vo_driver *const video_out_drivers[] = { &video_out_libmpv, #if HAVE_ANDROID &video_out_mediacodec_embed, #endif &video_out_gpu, #if HAVE_VDPAU &video_out_vdpau, #endif ...省略多個driver
Mpv 會根據系統、編譯狀況、傳入參數決定使用哪一個具體的視頻輸出驅動。以後,調用該驅動的preinit
方法。對於 vo_gpu 來講,它的下層還依賴於不一樣的 render_context
,對應了在不一樣系統環境上的渲染接口。這也是 Mpv 跨平臺兼容的關鍵。全部 gpu 支持的渲染接口定義在 video/out/gpu/context.cgithub
static const struct ra_ctx_fns *contexts[] = { #if HAVE_D3D11 &ra_ctx_d3d11, #endif // OpenGL contexts: #if HAVE_EGL_ANDROID &ra_ctx_android, #endif #if HAVE_RPI &ra_ctx_rpi, #endif #if HAVE_GL_COCOA &ra_ctx_cocoa, #endif #if HAVE_EGL_ANGLE_WIN32 &ra_ctx_angle, #endif #if HAVE_GL_WIN32 &ra_ctx_wgl, #endif ...省略大量接口 };
每一個底層接口都由結構體 ra_ctx_fns 定義。這個結構體暴露了一組用於配置的具體方法:macos
const struct ra_ctx_fns ra_ctx_d3d11 = { .type = "d3d11", .name = "d3d11", .reconfig = d3d11_reconfig, .control = d3d11_control, .init = d3d11_init, .uninit = d3d11_uninit, };
所以在 gpu 渲染驅動的 preinit
函數中一大任務就是調用具體渲染接口的 init
方法。app
視頻、音頻播放驅動初始化完畢後,就開始視頻播放。整個播放的流程(render loop)以下僞代碼:ide
for video in Videos { while(1) { render_frame(video); wait for next frame; } }
對的,就是這麼簡單粗暴。這裏有意忽略了時間同步、音視頻同步等具體細節,實際上 Mpv 內部大量依賴於鎖和信號量進行線程間同步。函數
總結一下,一個初始化的流程涉及以下接口的調用:oop
下一篇文章,咱們順着官方播放器的具體代碼,看看 Mpv 具體初始化了哪些東西,並試圖捋清楚 libmpv 又是如何進行初始化的。