FFmpeg Maintainer趙軍:FFmpeg關鍵元件與硬體加速

NO IMAGE

640?wx_fmt=png

本文來自FFmpeg Maintainer趙軍在LiveVideoStackCon 2018熱身分享,並由LiveVideoStack整理而成。在分享中,趙軍介紹了FFmpeg的歷史、關鍵元件,並介紹了英特爾平臺上的多種FFmpeg硬體加速方式。

文 / 趙軍

整理 / LiveVideoStack

大家好!我是趙軍,現就職於英特爾的DCG從事基於FFmpeg的硬體優化工作,兩年多前加入FFmpeg社群,2018年4月成為FFmpeg的其中的一個FFmpeg Maintainer,主要負責FFmpeg的硬體優化工作。

640?wx_fmt=png

概覽:

  • 什麼是FFmpeg

  • FFmpeg元件

  • FFmpeg開發

  • 我們的主要工作

  • 回顧與總結

1、什麼是FFmpeg

640?wx_fmt=png

FFmpeg誕生於十幾年前,最初是作為一個MPlayer播放器的一個子專案出現。因為當時的播放器有需要支援各種各樣解碼的需求, 其中有一位Mplayer的開發者看到了這樣的需求,於是編寫了FFmpeg。

它作為迄今為止最流行的一個開源多媒體框架之一,FFmpeg有兩種基本使用方式——作為庫或者作為工具,其中後者的使用場景更多,同時它也被稱為多媒體開發的“瑞士軍刀”。FFmpeg庫中90%的程式碼以上使用C,同時也有一些組合語言上的優化,還有一些基於GPU的優化。對於彙編優化而言,由於YASM對最新的CPU指令支援效果不好,FFmpeg的彙編現在正在向NASM轉變。FFmpeg本身有一些基本的開發策略,希望所有的Codec整合在內部庫中隨時呼叫;當然它也在必要時可以依賴一些外部第三方庫,例如像眾所周知的X.264。(X.264作為一個Encoder來說已經足夠優秀,我們可以看到大部分的商業產品都以X.264為對標,常會看到某某Codec宣稱比X.264好多少,似乎X.264已經成為業內一個基本對標點)。FFmpeg同樣也是一個跨平臺的產品,主要的License是GNU GPLv2,或GNU LGPLv2.1 的,講到這裡我想說的是,希望大部分的使用者也能夠在專案通過宣告使用了FFmpeg這一點為開源社群帶來正面的反饋。

1.1 FFmpeg的發展歷史

640?wx_fmt=png

這裡需要說明的是FFmpeg與Libav之間的關係, 2011年FFmpeg社群中的一部分開發者因為某些原因脫離了FFmpeg社群並創立了Libav社群,而後來使用Libav的大部分的發行版本又慢慢遷移回了FFmpeg。但是,直到現在仍有幾位脫離FFmpeg社群的主要開發者堅守在Libav,而大部分的開發者與資源都重新遷回了FFmpeg社群。現在FFmpeg的最新穩定版本為2018年4月更新的v4.0.0,從v3.4.2 到最新的v4.0.0,其中最大的改進之一便是硬體加速。

640?wx_fmt=png

1.2 使用場景

FFmpeg有很多使用場景,其中較為典型的有播放器、媒體編輯器、雲轉碼等等。據我所知有上圖中左側的這些公司在以上場景中使用FFmpeg,而右側的公司則是將FFmpeg作為第三方Codec使用。

2、FFmpeg元件

640?wx_fmt=png

關於FFmpeg元件,大家可以在網上查到使用FFmpeg的API編寫播放器的教程文章。FFmpeg元件中應用最多的是FFmpeg,它被用於進行轉碼,而FFprobe則被用於進行碼流分析(這些都是基於命令列的工具);而FFserver的程式碼庫已經被刪除,其最主要的原因是FFmpeg Server的維護狀態並不好,出現很多問題。最近社群又有人在重寫FFmpeg  Server,估計不久之後程式碼庫會得以恢復。但這時的FFserver的實現方式跟之前已經基本沒什麼關係了。在上圖中我們可以看到其中的Libavdevice主要使用硬體Capture或者進行SDI (流化),Libavformat是一些常見容器格式例如Mux或Demux等。我個人的大部分工作是在Libavcodec與Libavfilter,在Libavcodec上進行一些基於英特爾平臺的優化,而Libavfilter主要是針對影象的後處理。我認為在AI盛行的時代Libavfilter會出現非常大的改動,加入更多新功能而非僅僅基於傳統訊號處理方式的影象處理。最近實際上已經有人嘗試在其中整合Super Resolution,但在效能優化上仍有待改進,預計還需要持續一段時間才能真正做到實時與離線。除去作為基礎元件的Libavutil,其他像Libavresample、Libswresample、Libpostproc這幾個庫現在都有逐漸被廢棄的趨勢。 

2.1 基本介紹

640?wx_fmt=png

為什麼FFmpeg會有那麼高的使用量? FFmpeg和Gstreamer究竟是什麼關係?我也在反覆思考這些問題,為什麼我會用FFmpeg而不用Gstreamer?將這個問題引申來看可能會考慮:FFmpeg適合做哪些?不適合做哪些?我想人們熱衷於使用FFmpeg的原因之一是FFmpeg的API非常簡潔。上圖是一個範例,我們可以看到只需五步就可寫出一個基本的Decoder。這個例子非常容易理解,如果能將這個例子從API到底層逐步研習其細節便可對FFmpeg有更深層的認識。

2.1.1 Libavformat

640?wx_fmt=png

Libavformat的主要任務是Demuxer/Muxer功能。我們可以看到FFmpeg的框架設計得十分精煉,基本上如果需要實現一個AVFormat或AVCodec以對應新的Format/Codec;所以即使一位開發者不瞭解FFmpeg框架也可以編寫一個簡單的Format或Codec,需要做的最主要是實現對應的AVFormat/AVCodec。

2.1.2 Libavcodec

640?wx_fmt=png

需要提及一下的,有兩種方案實現對應的Libavcodec,一種是以Native方式實現在FFmpeg內部,另一種是利用整合的第三方庫,我們現在看到的一些Encoder相關的是以整合的第三方庫為基礎。而對於Decoder ,FFmpeg社群的開發者做得非常快。耐人尋味的是,據說FFmpeg內部VP9 的Decoder速度比Google Libvpx Decoder還要快,我們知道Google是VP9 Codec的創立者,但是Google的表現還不如FFmpeg自身的VP9 的Decoder 。

2.1.3 Libavfilter

640?wx_fmt=png

Libavfilter是FFmpeg內部最複雜的部分之一,其程式碼一直在反覆重構。Libavfilter的思想可能借鑑了Windows 的DirectX上一些思想,但程式碼卻有些複雜。其構成並非像圖片展示的那樣是一個簡單的序列關係,實際上它可以構成一個有向無環圖,這意味著只要能夠構成一個DAG這個LibavFilter就能工作。但是Libavfilter的綜合表現並不是特別好,我一直在想嘗試對其進行改進,但因為這一部分的複雜度比較高,總是令我感覺不入其門。

2.1.4 FFmpeg Transcoding

640?wx_fmt=png

FFmpeg的應用場景之一是Transcoding轉碼,涉及Demux/Decoder/Encoder/Muxer,同時我們可以把Demux Mux的流程看作是它的一個特例。另一應用場景是作為Player播放器,上圖展示了Transcoding與Player兩種應用場景的流程。

3、FFmpeg開發

640?wx_fmt=png

FFmpeg中比較重要的API包括如何進行Decoder、Postprocess、Encoder等。如果你對此感興趣,我認為最好的辦法是去認真看一看FFmpeg裡一些很好的例子。最近兩年FFmpeg把過去的API進行了重構,如果我以原來的FFmpeg API為基礎進行解碼,其做法是輸入一個已經壓縮過的Frame資料並希望得到一個解碼的Frame,但實際上此過程存在的限制是需要確保輸入的Frame與解碼出的資料一一對應。後來隨著開發的深入,特別是H.264提供的MVC這種模式以後,有時我們輸入一個Frame後需要分左右解碼兩個幀,此時它的API便無法支援這種場景。因此最近FFmpeg的API被從輸入一個Frame輸出一個Packet修改為兩個API,這樣便可解除它們之間的耦合。當然由於輸入與解碼變成了兩個分離的步驟,導致程式碼中需要大量的While迴圈來判斷此解碼過程是否結束。

4、硬體加速

640?wx_fmt=png

我在英特爾負責FFmpeg硬體加速的工作,因此更關注FFmpeg的硬體加速在英特爾GPU上的表現。我們一直在考慮如何更快地將英特爾的硬體加速方案推薦給客戶使用,讓使用者能夠有機會體驗到硬體加速的強大功能。現在英特爾提出的兩種通過FFmpeg驅動GPU的硬體加速方案,其中一種方案基於MediaSDK,我想如果你用過英特爾的GPU便應該會對其有所瞭解,已經有很多客戶基於MediaSDk進行了部署,其中,MediaSDK的VPP部分作為AVFilter也在FFmpeg內部;另一種更為直接的方案是VA-API,VA-API類似於Windows上提供的DXV2或是MacOS上提供的Video Toolbox等基於OS層面的底層硬體加速API,我現在的大部分的工作專注於此領域。FFmpeg同樣整合了OpenCL的一些加速,它使得你可以藉助GPU進行轉碼工作並在整套流程中不涉及GPU與CPU的資料交換,這個方案方案會帶來明顯的效能提升。我們雖希望從解碼到VPP再到編碼的整條流程都可以在GPU內完成,但GPU的一些功能上的缺失需要其他硬體加速功能來彌補,此時就可考慮使用OpenCL優化。其次是因為OpenCV已經進行了大量的OpenCL加速,所以當面對這種影象後處理的硬體加速需求時可以考慮把OpenCV整合到FFmpeg中,但在OpenCV發展到v3.0後其API從C切換到了C ,而FFmpeg自身對C 的API支援並不友好,這也導致了FFmpeg的官方版本中只支援OpenCV到v2.4。如果你對此感興趣,可以嘗試基於在OpenCV v3.0以上的版本做一個新的C Warper,再考慮整合進FFmpeg。但如果你對效能要求足夠高,直接使用VA-API和OpenCL去做優化,保證整個流程能夠在GPU內部完整執行,達到最好的效能表現。

640?wx_fmt=png

上圖是對GFFmpeg硬體加速的流程概覽圖,大部分人可能對英特爾的兩套方案有比較清晰的認識,最關鍵的點在於QSV方案依賴於MediaSDK,而VA-API則可以理解為將整個MediaSDK做的工作完整的放進了FFmpeg的內部,與FFmpeg融為一體,FFmpeg開發者與社群更推薦後者。現在的OCL方案最近也正不停的在有一些Patch進來,這裡主要是對AVFilter的處理的過程進行硬體加速。需要說明一下,因為社群曾經有嘗試用OpenCL加速X.264使其成為一個更快的Codec,但結果並不是特別好。所以用OpenCL去硬體加速Encoder,其整體效能提升並不是特別明顯。

640?wx_fmt=png

上圖展示了更多細節,我們可以看到每種方案支援的Codec與VPP的功能與對應的Decoder和Encoder。

640?wx_fmt=png

這兩種方案的差異在於實際上是QSV Call第三方的Library,而VA-API直接基於VA-API 的Interface,使用FFmpeg的Native 實現而並不依賴任何第三方外部庫。

VA-API

640?wx_fmt=png

經常會有人提出疑問:VA-API是什麼?它的本質類似於Microsoft在Windows上提出的DXVA2,也就是希望用一套抽象的介面去隱藏底層硬體細節,同時又暴露底層硬體的基本能力。VA-API有多個可用後端驅動,最常見的是原先英特爾OTC提供的VA (i965)的驅動,現如今在Linux發行版本中也存在;而Hybird驅動則更多被用於當硬體的一些功能還沒有準備好的情景,需要先開發一個模擬驅動;等硬體部分準備完成後再使正式驅動。第三個是iHD/Media driver,這部分驅動在去年年底時Intel便已經開源,這一套驅動對比i965驅動其影象質量和效能表現更優但穩定性較差。現在我一直在此領域工作,希望它能夠更好地支援FFmpeg。Mesa’s State-Trackers主要支援AMD的GPU,但是由於現在只有Decoder而Encoder處在試驗階段一直未開放,所以AMD的GPU在FFmpeg上無法進行Encoder加速。

640?wx_fmt=png

上圖是VA-API的一些基本概念,在這裡我就不做過多闡述。

640?wx_fmt=png

這是基於VA-API 一個基本流程。FFmpeg的VA-API也是基於此流程做的。

開放問題

640?wx_fmt=png

FFmpeg的QSV硬體加速方案究竟有什麼優缺點?如果將 FFmpeg與GStreamer比較,什麼情況下選擇FFmmpeg什麼情況下選擇GStreamer,這是我一直在反反覆覆考慮的內容,還有FFmpeg與OpenMAX的差別這些(Android使用了OpenMAX)。對於未來趨勢,我們期待基於FFmpeg與英特爾的GPU構建一個全開源的解決方案,將整個開發流程透明化;在之後我們也考慮OpenCL的加速 ,順帶說一句,作為OpenCL最初的支持者的Apple,在不久前的WWDC上稱要放棄OpenCL,不過從現實來看,如果想在GPU或異構上進行硬體加速開發,OpenCL仍然是最優的選擇。其他方案直到現在還沒有OpenCL的廣泛適應度。實際上OpenCL本身的推出並不是特別的成功,在OpenCL過去的十年發展中並沒有出現殺手級應用;另一個趨勢是,Vulkan作為OpenGL的後繼者開始流行,因此業界也在考慮直接把OpenCL作為Vulkan部分合併在一起。另外,OpenCV有大量的OpenCL優化,如果你不願意重寫OpenCL的優化,可以考慮用FFmpeg與 OpenCV一起加速來構建整個流程。

640?wx_fmt=png

上圖展示了你所見的基於各個OS與硬體廠商的硬體加速狀態。大部分專注於硬體加速的開發者更關心播放器的表現,在Windows上進行硬體編碼的需求並不強。

Q&A

Q1:FFmpeg  Server最近的一些大改動是什麼?

A: FFmpeg  Server的程式碼在最新版本的FFmpeg裡已經不存在了,主要是由於維護者並不積極。現在又有開發者正在重寫FFmpeg Server並且已經Review到第三輪,我相信最快需要一兩個月它又會回到FFmpeg裡面,但和以前的FFmpeg Server完全不一樣。

Q2:FFmpeg 4.0已經有VA-API的方案嗎?

A:VA-API的Encoder從3.3.1開始支援,這部分的程式碼從2016年到2018年一直在進行重構,在4.0.0時VA-API的Encoder都可以支援。屆時是一個開箱即用的狀態。

Q3:安卓平臺現在可以硬體加速嗎?

A: VA-API的方案是英特爾的,由於英特爾的產品生態緣故,安卓的解決方案是基於MediaCodec而非VA-API,其硬體加速就目前而言只有解碼加速沒有編碼加速。

Q4:後臺的多工轉碼伺服器需要用硬體來編碼,那麼可以同時進行多少任務?如果根據硬體的核心數量來決定,那麼超過效能極限是否會導致建立編碼器失敗?


A:如果是基於CPU的編碼方案,那麼編碼的效能與CPU的執行緒數有關,而FFmpeg效能並未和CPU的核心數量構成一個線性關係;如果是基於GPU的編碼方案,包括1對n的轉碼,這需要以官方測試為準。英特爾在官方網站的GPU引數有相關資料,這與硬體平臺有非常大的關係,具有強大效能的硬體平臺可以保證良好的編解碼運算處理能力。

Q5:還有一個跟WebRTC相關的問題,他說這個在WebRTC 在Chrome裡FFmpeg實現硬體加速有哪些,可以替換其他版本的FFmpeg嗎?


A:據我所知在ChromeOS中只有當自身API硬體加速不工作的情況下才會使用FFmpeg,Chrome可以說是把FFmpeg作為一個備選方案,並沒有直接用作硬體加速。

Q6:英特爾 Collabration 的客戶端SDK,支援喂資料給WebRTC的,這裡硬體編碼是用的WebRTC內部,還是自己替換的?


A:英特爾 Collabration的客戶端我不知道這個事情,在Server端採用了三套方案,一套方案是MediaSDK進行硬體加速,第二套方案是VPX以支援VP8,VP9 ,其他還有支援另外格式的方案。我無法準確推斷是否會用FFmpeg進行硬體加速與軟體解碼,之前與內部有過相關的的交流,但最終沒有決定。

Q7:還有個問題,FFmpeg有哪些Filter是使用了硬體加速,有沒有這方面的加速計劃?


A:現在是有這個計劃,以下圖片可以說明

640?wx_fmt=png

現在已經有一些基本的硬體加速,主要是一些ColorSpace的轉換,有一些Scaling,再就是 Deinterlace,FRC現在考慮OpenCL去做,因為 iHD driver這塊支援應當沒有了。預計更多的OpenCL會進行加速,我們希望Decoder Filter Encoder的整個過程都在GPU內部運算完成從而減少CPU的效能損耗,同時也希望OpenCL具有一定的靈活度。

Q8:VA-API在Linux下支援哪些型號CPU?


A:這與驅動有關,總體來說i965支援更多的處理器,iHD支援英特爾Skylake架構以後的處理器

Q9:如何提升硬體編解碼的質量?


A:這是硬體編解碼方面的老大難問題,每一個做硬體編解碼的人都會提出類似的問題。因特爾曾提出了一個被稱為FEI的解決方案,其原理是僅提供GPU中與硬體加速相關的最基本功能,而像影象質量等方面的提升則基於搜尋演算法等非硬體加速功能。這就使得可以讓使用者考慮使用自己的演算法,而與計算量相關的問題則交給GPU處理,但此方案並未出現一個特別成熟的應用。

Q10:基於CPU、GPU設定FFmpeg執行緒數,執行緒數和核心數有什麼對應關係?


A:其實對GPU而言處理速度已經足夠快,執行多程序的轉碼對GPU而言基本沒有什麼影響。根據實測來看,例如執行4程序的轉碼,對CPU和GPU的消耗沒有特別大的區別。但在這種情況下如果是用GPU進行,我的建議是用程序會更好管理。其次是FFmpeg自身1對n的特性使得在價格上比較敏感,這也是我們一直致力改進的重點。相信改進之後會為1對n轉碼帶來一個比較大的提升,但就目前而言仍處於內部設計的初級階段。

640?wx_fmt=jpeg