GoPlay原理詳解

NO IMAGE

GoPlay是一款基於FFmpeg/OpenGL ES 2.0 的iOS播放器。支持FFmpeg內嵌的所有格式。而且可以自定義各種濾鏡, 包括VR、水印等。

前沿

關於iOS視頻播放,蘋果提供的AVPlayer性能是非常出色的,但是有個缺點,就是支持播放的格式並不多,僅僅支持mp4/mov/ts等有限的幾種格式。顯然業界中比較知名的jikplayer確實彌補了這種缺陷,然而ijkplayer是在FFmpeg/ffplay的基礎上進行開發的,最終是通過SDL2.0進行顯示。在當前大環境下,VR、水印、貼圖、九宮格等濾鏡盛行,在ijkplayer中默認是支持avfilter濾鏡的,但是並沒有支持GPU濾鏡;那麼有沒有一種辦法可以播放AVPlayer不支持格式的視頻,又能夠在視頻上無限制的加濾鏡,例如GPUImage那麼方便那麼絲滑的做法呢?上面兩個痛點也就是GoPlay解決的問題。

原理

關於格式支持

關於格式支持,採用了業界比較出名的FFmpeg解封裝不同的視頻格式;在解碼階段,如果開啟了VideoToolBox硬解碼,那麼就採用iOS的硬解碼方式,否則自動切換到FFmpeg的軟解碼方式。

關於濾鏡支持

為了方便濾鏡的接入,濾鏡包括濾鏡鏈的實現都採用了GPUImage類似的做法,如果使用過GPUImage,那麼就可以無縫的切換到GoPlay,同時可以根據GPUImage的已有濾鏡自定義濾鏡,無限擴展自己的濾鏡庫。GoPlay和GPUImage的濾鏡類比如下表。

GOPlayGPUImage
輸入FFMovieGPUImageMovie
濾鏡FFFilterGPUImageFilter
輸出顯示FFViewGPUImageView

運行流程

基本流程

GoPlay主要有5個線程(包括主線程),其中OpenGL ES渲染、濾鏡都是在一個統一的異步線程中處理,在這方面與GPUImage的處理稍有不同。異步線程可以防止阻塞UI界面,串行可以防止線程間加鎖從而導致的性能損耗。線程模型如下。

  • 解封裝線程 — FFmpeg解封裝,讀取packet,分發到視頻解碼線程和音頻解碼線程

  • 視頻解碼線程 — 將packet解碼成frame,並保存到隊列緩存中

  • 音頻解碼線程 — 將packet解碼成frame,並保存到隊列緩存中

  • OpenGL ES渲染、濾鏡處理線程

    • 從video緩存隊列中取出數據幀,並且在GPU中從YUV轉換成RGB,然後傳遞給下一級濾鏡鏈,並最終顯示

關於音視頻同步

在業界中,普遍沒有認識到音頻視頻兩者的同步算法是控制學的問題,而僅僅停留在誰快誰慢的問題上。在現實中,音頻和視頻的PTS的誤差是客觀存在的,我們需要通過一種控制學算法實現音頻和視頻的相對同步,需要考慮到累積誤差的存在,在相對範圍內,同步算法是具有自我調節能力的,當超出某個範圍了,那麼就需要丟幀了,否則會影響觀感。在這麼多開源項目中,FFmpeg/ffplay實現了這種思想。

關於丟幀算法

如果真正理解了音視頻同步算法,那麼丟幀的做法就很簡單了,當超出了音視頻同步算法的調節範圍,而且是視頻幀慢於音頻幀很多,那麼此時就需要丟調視頻幀。

關於全景圖像顯示

全景圖像是將一張平面圖片映射到一個球面上。本質上也是一種濾鏡處理,即要處理好頂點座標和紋理座標的映射關係。

關於ArcBall控制

ArcBall本質上是將二維平面上的滑動轉換成三維立體球的轉動,具體的做法以屏幕中心為球心,畫一個球。在屏幕中滑動時,就將滑動的點映射到球面上,如果滑動的範圍超出了球的範圍,那麼就映射到最靠近球面的點上。根據起始點的四元數和終點的四元數的差值(求逆),就可以得到旋轉的角度。

關於濾鏡鏈

濾鏡鏈的思路來源於GPUImage,但多路濾鏡的處理情況並沒有沿襲GPUImageTwoInput之流的做法。在多路濾鏡的處理上,水印濾鏡中進行了一種嘗試。

總結

關於GoPlay的相關原理基本上到這裡結束了。感興趣的可以在GoPlay中找到相關的實現,當然也可以提BUG一起討論。

相關文章

手把手帶你實現一個符合promiseA+規範的promise類庫

對於元素是非基本類型的數組去重

UC面試總結

iOS動態化熱修復方案