GoPlay 原理詳解

dKingbin發表於2019-05-09

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的濾鏡類比如下表。

GOPlay GPUImage
輸入 FFMovie GPUImageMovie
濾鏡 FFFilter GPUImageFilter
輸出顯示 FFView GPUImageView

執行流程

基本流程

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一起討論。

相關文章