SGPlayer 原理詳解 - 支持 VR、RTMP 的視頻播放框架 頂 原 薦

SGPlayer 原理詳解

SGPlayer 是一款基於 AVPlayer、FFmpeg 的媒體資源播放器框架。支持全景視頻,RTMP、RTSP 等直播流;同時支持 iOS、macOS、tvOS 三個平臺。本文將採用圖解+說明的方式把關鍵模塊的實現原理介紹給大家。

發起原因

關於視頻播放,蘋果提供的 AVPlayer 在性能上有着十分出色的表現,在無特需求且資源可控的時,首選一定是它。但隨着 VR 和直播的興起,僅使用 AVPlayer 很多時候已經無法滿足需求。出於性能考慮,又不能完全拋棄 AVPlayer,畢竟在點播時有着明顯的優勢。而在現有的開源項目中,普遍定位比較單一,並不能兼顧 AVPlayer、直播、VR。這樣一來,需同時使用3款播放器才能滿足需求,即點播使用 AVPlayer,直播使用一個獨立的播放器,VR 使用一個獨立的播放器。這樣處理3套不同的接口和回調事件,着實很讓人崩潰!SGPlayer 的出現大大簡化了這一過程。

組成結構 和 播放流程

SGPlayer

上圖展示了 SGPlayer 的播放流程和主要組件,下面簡單介紹圖中各組件的分工

SGPlayer

SGPlayer是一個抽象的播放器外殼,它本身並不具備播放功能。僅作爲和外界交互的載體。真正的播放由內部的 SGAVPlayer 和 SGFFPlayer 完成。而畫面繪製由內部的 SGDisplayView 完成。

SGPlayerDecoder

SGPlayerDecoder 是播放內核的選擇器,根據資源類型動態選擇使用 SGAVPlayer 或 SGFFPlayer 進行播放,可通過更改其配置參數,來自定義播放內核的選擇策略。

SGAVPlayer

SGAVPlayer 是基於 AVPlayer 封裝而成,視頻畫面輸出至 SGDsiplayView,並根據視頻類型(全景或平面)進行展示。音頻由系統處理無需額外操作。

SGFFPlayer

SGFFPlayer 是基於 FFmpeg 封裝而成,支持近所有的主流視頻格式。視頻畫面同樣輸出至 SGDsiplayView。音頻則輸出至 SGAudioManager,再由 SGAudioManager 使用 Audio Unit 進行播放。

SGDisplayView

SGDisplayView 負責視頻畫面的繪製。它本身不會繪製視頻畫面,僅作爲繪製層的父視圖使用,真正的繪製由內部的 AVPlayerLayer 和 SGGLViewController 完成,選擇規則如下表所示。

平面全景
SGAVPlayerAVPlayerLayerSGGLViewController
SGFFPlayerSGGLViewControllerSGGLViewController

SGAudioManager

SGAudioManager 負責聲音的播放和音頻事件的處理。內部使用 AUGraph 做了一層混音,通過混音可以設置聲音的輸出音量大小等操作。

小結

瞭解了各組件的功能,重新梳理一下完整的播放過程

  • SGPlayer 收到播放請求。
  • 由 SGPlayerDecoder 根據資源類型分發給 SGAVPlayer 或 SGFFPlayer 進行播放。
  • 如果使用 SGAVPlayer 播放,根據視頻類型將畫面輸出給 SGDisplayView 中的 AVPlayerLayer 或 SGGLViewController。
  • 如果使用 SGFFPlayer 播放,將視頻畫面輸出給 SGDisplayView,音頻輸出至 SGAudioManager。

通過抽象的 SGPlayer 將真正負責播放的 SGAVPlayer 和 SGFFPlayer 屏蔽起來,這樣可以保證無論資源是何種類型,對外僅暴露一套統一的接口和回調,將播放內核間的差異內部消化,儘可能降低使用成本。

全景圖像原理

全景圖像與平面圖像本質都是一張 2D 圖片,區別在於展示時的載體。對於平面圖而言,用於展示的模型是一個矩形,僅需將圖像上的像素一一對應在矩形上即可;而全景圖像展示的模型是一個球,需要將圖像上的每一個像素都對應到球面相應位置上。在繪製流程上二者的差別並不大,僅在貼圖規則和呈現方式上略有區別。

貼圖規則

image

把平面圖片貼到球面上的過程和地球儀很相似。以上圖爲例,左側圖片中的每一個像素,都可以在右側球面上找到對應的位置。下面列舉一個關鍵的對應關係。

  • 直線AB 上所有的點都與 點J 對應,同理 直線CD 上所有的點都與 點K 對應。
  • 直線MN 上的點與 赤道 上的點一一對應。
  • 直線AC/BD 上的點與綠 色經線前半面 上的點一一對應。
  • 直線EF 上的點與 綠色經線後半面 上的點一一對應。

呈現方式

ball

上圖展示了全景圖像的呈現方式,不同於平面,全景圖像需將觀景點放在球心,站在球心觀看球面上的圖像。最終將 曲面ABCD 在 平面ABCD 上的投影顯示到屏幕上。

小結

這部分內容在實現上涉及到很多 OpenGL 的內容,需要具備一些 OpenGL 的基礎。在雙眼模式下還需要做 畸變校正 和 色散校正 來保證畫面被真實的還原。具體實現可以查看 SGGLViewController。

SGFFPlayer 運作流程

SGFFPlayer

上圖展示了 SGFFPlayer 的協作流程圖,下面簡單介紹圖中各組件

線程模型

SGFFPlayer 中共有4個線程。與圖中4個藍色圓圈對應。

  • 數據讀取 - Read Packet Loop
  • 視頻解碼 - Video Decode Loop
  • 視頻繪製 - Video Display Loop
  • 音頻播放 - Audio Playback Loop

圖中隱藏掉了線程的控制條件。在4個線程的協作下完成整個播放過程。

SGVideoDecoder

SGVideoDecoder 是視頻解碼器,初始化時可配置同步、異步解碼,以及是否開啓硬解。上圖中採用的是異步解碼,默認的解碼線程對應關係如下表所示。

平面全景
軟件解碼異步同步
硬件解碼異步異步
  • 同步解碼在收到視頻包後立即解碼,並存入視頻幀隊列。
  • 異步解碼在收到視頻包後僅存入音頻包隊列,當獨立的解碼線程取出音頻包並完成解碼後,再存入視頻幀隊列。

SGAudioDecoder

SGAudioDecoder 是音頻解碼器,採用同步解碼,收到音頻包後立即解碼,並存入音頻幀隊列。

數據隊列 SGFFPacketQueue、SGFFFrameQueue

  • SGFFPacketQueue 是包隊列,用於管理解碼前的數據包(AVPacket)。
  • SGFFFrameQueue 是幀隊列,用於管理解碼後的幀(SGFFVideoFrame 或 SGFFAudioFrame)。

它們都支持數據的同步獲取和異步獲取,同步獲取是通過條件變量(NSCondition)實現。當隊列中沒有足夠數據時,會阻塞當前線程,直到向隊列中添加新元素時,線程纔會被喚醒。

幀複用池 SGFFFramePool

該部分並沒有在上圖中體現,但能避免一些不必要的性能開銷。由於音頻幀和視頻幀的數量很大,1分鐘的視頻就包含幾千幀的數據。如果每一幀都新創建的話會造成不必要的資源浪費。通過 SGFFFramePool 創建的 SGFFFrame 在使用完成後不會立即釋放,而是被複用池回收,以供下次使用,達到僅創建最小數量的幀對象的目的。

音視頻同步

常用的同步當時有3種

  1. 音頻時鐘
  2. 視頻時鐘
  3. 自制時鐘

在 SGFFPlayer 中,優先使用音頻時鐘,當視頻中沒有音軌時,會使用視頻時鐘進行同步。

小結

瞭解了各組件的功能,以視頻異步解碼爲例,重新梳理一下整個流程

  • 數據讀取線程讀取到數據包,根據數據包類型分發給音頻解碼器或視頻解碼器。
  • 如果爲音頻包,音頻解碼器收到音頻包的同時進行解碼,並將解碼後的音頻幀存入音頻幀隊列。
  • 如果爲視頻包,由於視頻解碼器是異步解碼,僅將視頻包放入視頻包隊列,等待視頻解碼線程來隊列中取視頻包。
  • 視頻解碼線程循環從視頻包隊列中取出視頻包,同時解碼,並將解碼後的視頻幀存入視頻幀隊列。
  • 音頻播放線程循環在音頻幀隊列中取出音頻幀並播放。
  • 視頻展示線程循環在視頻幀隊列中取出視頻幀並繪製。

到這裏SGFFPlayer的運作流程已經很清晰了,只需在各個環節中加入對應的條件控制,就可以完成播放功能了。

總結

關於 SGPlayer 的原理就闡述到這裏,由於本文以理論爲主,所以並沒有貼代碼。感興趣的同學可以在 OSChina 上找到全部的代碼實現。希望對大家能有所幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章