FFmpeg 在 Intel GPU 上的硬體加速與優化
英特爾提供了一套基於VA-API/Media SDK的硬體加速方案,通過在FFmpeg中整合Intel GPU的媒體硬體加速能力,為使用者提供更多的收益。本文來自英特爾資深軟體開發工程師趙軍在LiveVideoStackCon 2017大會上的分享,並由LiveVideoStack整理而成。
文 / 趙軍
整理 / LiveVideoStack
大家好,今天與大家分享的主題是FFmpeg在 Intel GPU上的硬體加速與優化。
1、Media pipeline review
上圖展示的是典型的Media Pipeline。我們知道,FFmpeg對輸入格式支援非常的全面,可以是檔案、網路流等,也可以使用Device的Caputer作為輸入;輸入的音視訊經過Splitter後一般會分為兩種常見場景:Play Back與Transcoder。上圖的右上半部分實際是一個Transcoder的基本流程,解複用之後的Video、Audio的ES流,再經過Video、Audio的Filter,大部分情況下,Video有可能在AVFilter執行一些Scale、Frc、Crop操作(也可以在AVFiter中抓取有價值的資訊);隨後音視訊資料會被轉碼成為使用者指定的格式,轉碼時候多伴隨著位元速率轉換、指定IPB幀型別等;Audio也會經過類似的處理流程。上圖的右下半部可以視為播放器處理流程也就是Playback,Playback流程與Transcoder處理流程的主要差異在於對解碼的資料是否進行再次編碼或者直接顯示。另外,眾所周知,Encoder與Decoder的複雜程度存在一個數量級的差異,計算複雜度大概為10:1,且一般情況下Encoder每十年進化為一代,從MPEG2發展到H264大概用了十年時間,而從H264發展到HEVC將近十年時間(實際不到十年),其計算複雜度提升均為上一代的十倍左右,但壓縮率提升大概只有40%到50%,其背後是對計算量的大幅渴求,CPU的計算能力有時不能實時跟上計算量的需求或在高轉碼密度條件下不能提供較好的價效比,在此背景下,Intel提出了基於Intel GPU的媒體硬體加速解決方案。
2、何謂FFmpeg/VA-API?
作為最為流行的開源媒體解決方案,FFmpeg有兩種使用方式:直接使用它自帶Tools,或者把FFmpeg作為Library呼叫它的API而實現自己的邏輯。其中的Tools包含我們經常看到的轉碼工具FFmpeg;輕量媒體播放器FFPlayer;進行格式的探測分析的FFProbe ;輕量級流媒體測試的伺服器FFServer等。另外,FFmpeg的內部實現基本以C語言為主,輔助以部分彙編優化;同時它支援Linux、MacOSX、Android、Windows等不同OS,有著良好的跨平臺相容性。這裡另外強調一點的是FFmpeg自身的License問題,也許國內的廠商不會特別在意License,但在實際使用場景中,所使用軟體或者庫的License即版權是不能不考慮的問題。最近幾年FFmpeg已經將License的問題澄清得比較清楚,目前它的大多數內部實現程式碼使用GPL2.1版本的License。
3、Linux Video API
接下來我將介紹Linux平臺上Video加速API的進化歷史。我們知道,每一個突破性創新都是從細微之處開始慢慢演化,最後才可能成為舉世矚目的創造;另外很多技術的進化過程中,都是工程與演算法科學相互交織,Linux上的硬體加速API的進化流程也遵循了這一點。最初的Linux Video API被稱為Xv,基本只能藉助硬體加速實現Scaling與Color Space Conversion兩個功能,明顯無法滿足行業需求;隨後經過擴充套件,使得在那個MPEG-2稱霸的時代實現了對MPEG-2 Decoding硬體加速API的支援, 也就是Xv/XvMC,不過這一部分在當時還停留在比較初級的階段,iDCT、XvMC-VLD等還未實現被API所標準化;隨後社群便開始嘗試實現Slice層加速API標準化,以避免之前包括不支援解碼所有階段硬體加速且依賴於X-Protocol協議等在內的諸多問題,演化到現在,最終的結果就是VA-API。
4、VA-API
當時的英特爾開始涉足硬體加速領域,於是在1999年左右英特爾提出VA-API介面。這是一套在Linux上的標準介面,從上層來看大家可以將其理解為一個OS層面的Video加速Spec,且與硬體無直接關聯。這套通用介面,同時需要特定的後端實現支援。與大多數開源專案相似,VA-API並沒有一個特別好的Document進行說明,需要自己仔細的去讀它的標頭檔案以瞭解其設計思想和細節。另外,既然這是一個Spec,其設計上自然想剝離與特定硬體的強關聯,所以雖然今天我的分享主要圍繞Intel GPU實踐進行,但實際上VA-API這套Spec並不只限於英特爾的GPU。
5、VA-API可用的後端驅動
VA-API可用的後端驅動非常多:Intel VA(i965)Driver是Intel OTC Team開發的一套全開源驅動,隨後也出現了Intel Hybird Driver、Intel iHD Driver等;在後端實現中還有Mesa‘S state-trackers包括Radeon、Nouveau、Freedreno等的支援,另外,還有些公司開發了一些API Bridge,包括Vdpau-va Bridge、Powervr-va的bridge以提供VA-API的支援,但這些bridge大部分由於種種原因慢慢轉為封閉而逐漸被廢棄;與此同時,英特爾的態度則更為開放,它希望大部分的開發者有能力在現有成熟平臺上進行更深層次的定製與探索,開放出更多的硬體能力以及驅動程式碼,這也是英特爾作為一個開源大廠的風範吧。
6、Intel GPU
Intel GPU從Gen 3的Pinetrail發展到Gen 9.5的Kabylake,每一代GPU的功能都在增強,在Media上的能力也在增強。關於GPU效能我們比較關注以下三部分指標:第一部分是3D渲染能力,這一部分的標準化程度較好,可以使用標準介面包括OpenGL或Vulkan,當然也有Windows上可與OpenGL與Vulkan適配的DirectX等;第二部分是Media;第三部分則為通用計算,其中包括NVIDIA的CUDA與AMD、ARM等公司採用的OpenLL。附帶說一句,有人會混淆GPU的通用計算能力與Media處理能力,以為通用計算能力很強,則Media能力就很強,這並不正確,實際使用中,需要把這三個指標分開來根據具體的使用場景來分析與比較,以挑選最合適的硬體方案。
6.1 Intel GPU Media 硬體程式設計模型
從FFmpeg到具體的GPU,是如何進行一些Media處理的?首先FFmpeg會通過VA-API介面,調到對應的Driver例如i965或iHD,之後資料經過OS Scheduler進入OS KMD,接下來經過一系列硬體程式設計抽象和GPU&CPU資料交換,生成Command streamer並傳輸給EU(也就是Intel GPU中的一個計算執行單元)或者特定的IP以執行相關的Media任務。注意,GPU的Media部分有時也會使用EU這些通用計算資源,而像Sampler、VDBOX、VEBOX、SFC等都是基於一些特定的Fix Function硬體實現相應功能。所以,可以這樣理解,英特爾的GPU實際上是將媒體的大部分功能通過Fix Function方式實現,同時必要時候使用EU作為一個通用的計算資源這樣協同工作的。
6.2 FFmpeg & Intel GPU加速方案
大部分客戶偏向於使用FFmpeg的同時,也希望其具備出色的硬體加速能力,我們現在致力於在FFmpeg中整合Intel GPU諸多的媒體硬體加速能力,使使用者可通過FFmpeg的介面使能呼叫英特爾的GPU的各種能力從而帶來更多收益。這裡的整合方式主要有兩種:1)直接實現為與FFmpeg融為一體的Native Decode/Encode。FFmpeg的大部分Decode如H.264、H.265、VP8、VP9等都使用Native Decoder的方式,2)Warpper第三方庫的,如在FFmpeg中整合Libx264的方式;現在部分Encode都以第三方庫的形式整合進FFmpeg的。根據上圖我們可以看到在Intel GPU中集成了兩個Plugin到FFmpeg中:第一個是QSV Plugin,其類似於libx265的做法,其Codec實現的底層與MediaSDK相關;但FFmpeg社群更傾向於基於libva/vaapi的方式,即直接在FFmpeg中進行整合,不warpper第三方的庫,一是因為此方案相對而言更加輕量,二是因為此方案更加開放;這樣做意味著將全部的硬體Codec部分的程式碼都整合在FFmpeg中,與FFmpeg融為一體,如果客戶希望進行定製或改變,那麼直接在FFmpeg內部程式碼中修改即可實現。除了解決基本的解碼/編碼硬體加速問題,我們也在考慮整合OpenCL、OpenCV等以適應客戶的一些其他需求。
6.3 Intel GPU 支援
1)解碼支援
上圖展示的是Intel GPU Decode部分的的支援狀況。一般情況下我們可以將Decode分為8Bit與10Bit,以HEVC為例,有一些資料顯示10bit的壓縮率要高於8bit,感興趣的同學可以思考一下其原因。從表也可以看到,英特爾的各代GPU逐漸進化,從開始只支援MPEG-2、H.264、VC-1解碼的Sandy Bridge到增加了MJPEG解碼支援的Ivy Bridge再到多用於嵌入式平臺的Bay Trail乃至之後的Haswell,從Broadwell開始對VP8的支援與Cherry Trail/ Braswell對HEVC的支援再到Skylake之後的Apollo lake與 Kaby lake對VP9解碼與10bit HEVC&VP9的解碼支援,其解碼能力穩步增加。
2)編碼支援
編碼方面,Intel GPU很早開始就支援了H.264編碼,到了Broadwell增加了對VP8的支援;而Skylake則增加HEVC和MJPEG,到了Kaby Lake時我們增加了對VP9和10Bit HEVC的編碼支援。關於VP9我想強調一點,據我所知,現在量產的SoC/GPU/CPU中可能只有英特爾的Kaby Lake及其後續的產品與三星的SoC支援VP9的編碼硬體加速。
6.4 使用案例
上圖展示的這些Use Cases可基本涵蓋大部分使用者的使用場景。解碼部分主要是使用hwaccel vaapi進行硬體解碼,由於一款裝置上可能存在多款GPU,因此我們需要是hwaccel_device選擇不同的硬體裝置。對比硬體編碼與硬體解碼我們不難發現,在解碼部分我們使用hwaccel_device而編碼部分則使用vaapi_device。這裡的vaapi_device是一個Group Option,因為FFmpeg中存在Group Option與Per-Stream Option,解碼部分的hwaccel_device是Per-Stream Option,而編碼部分的vaapi_device是全域性的並且Decoder和Encoder只需指定一次。從上面看來,轉碼的例子更為複雜,首先進行硬體解碼,而後在GPU中進行de-interlace與Scall和HEVC編碼,實際上整個過程是一個硬體解碼結合GPU中的Deinterlace/Scale和隨後的HEVC硬編的過程,這裡需要注意一些關於碼控設定的問題,對此問題有興趣的同學可以直接閱讀FFmpeg的文件或者程式碼。
7、FFmpeg硬體加速全覽
上圖展示的是FFmpeg硬體加速全覽,我想這一部分對探索基於FFmpeg實現跨平臺的開發者來說非常有幫助。開發者經常需要面對不同的硬體廠商:Intel、NVIDIA、AMD等等,他們希望僅僅與FFmpeg經過一次整合,就可在不同硬體上實現同樣的功能。而現實情況,即是存在OS層面可以進行硬體優化的API諸如Windows上的Dxva或MacOS上的VideotoolBox、Linux的Vaapi等,其實現可能還是非常分散,而FFmpeg在支援各種硬體加速介面之後,則幫助解決了上面的這個問題。另外,對於上表,Decoder部分只列舉了是否支援硬體surface的輸出。除此之外還有一些附加功能例如Filter,作為FFmpeg中非常重要的一部分,它主要是為了進行Video 後處理等;上表中的Hardware Context是指基於FFmpeg內部的硬體加速介面的實現,Useable from FFmpeg CLI是指FFmpeg的命令列是否直接可用硬體加速(它的典型使用場景是,在Server端將FFmpeg直接作為工具使用,通過PHP在後端直接呼叫FFmpeg的Tools)。另外,如果開發者需要呼叫FFmpeg API進行解碼,此時需要關注Hwaccel的支援情況。最後我想強調一下圖中Decoder部分裡的Internal和Standalone。它實際上是一個歷史遺產,在FFmpeg中,很早便實現了H.264的軟解碼,在此基礎上,如果想使能GPU的解碼能力則需要面臨以下兩個選擇:可以選擇重新實現有別於軟解碼的另一套基於GPU解碼實現,可以考慮為需要完整實現一個類似h264_vaapi的解碼其;也可將解碼相關的一些硬體加速工作直接Hook在已有的軟解碼Codec中,當時的開發者選擇了後者,所以大部分基於OS的硬體加速解碼方案都基於後者的方案也就是Internal AVHWaccel;但諸如NVIDIA等提供NVDEC,NVENC的方案,因為自身已經是一個完整的硬體解碼器,所以在FFmpeg內只需實現成一個簡單的wrappe人即可,不需要藉助FFmpe已有的軟解碼Codec的任何功能。
8、FFmpeg VA-API的細節資訊
上圖展示的是FFmpeg VAAPI的一些細節資訊,之前我已經對HWAcceled的解碼與Native的解碼進行了說明。提及編碼,硬體加速的編碼帶來的最大好處是速度優勢:我曾經基於Skylake-U這樣雙核四執行緒的低電壓CPU上測試1080P的轉碼,基本可實現240FPS的實時轉碼;同時,在大規模部署時不能不考慮功耗比與價效比,也就是單路的編碼或轉碼需要消耗多少電能以及單路轉碼的成本。現在集成了GPU的英特爾PC處理器,其功耗在40~65w,如果是面向伺服器工作站的Xeon E3系列,可在一個65w的處理器上實現14到18路的1080P轉碼,而能達到相同效能的NVIDIA GPU所需的能耗大約在300w左右。另外,對於硬體編碼,有一些客戶可能在影象質量上有更高的需求,現在英特爾的GPU在低位元速率上處理效果還有提升空間,但在處理中高位元速率檔案時,其評測結果與X264相比並無明顯的差距。如果客戶期望藉助自己的一些高階演算法通過更深度的定製實現更強大的功能,Intel也開放了被稱為Flexible Encoder Interface (FEI)的底層介面。此介面可詳細全面展示Intel GPU的全部硬體編碼能力,並讓使用者擁有足夠的靈活度去Tunning各種演算法;如果說FFmpeg代表的是一個可以直接呼叫的成熟平臺,那麼FEI則是可定製Codec演算法的通用介面。與此同時,FEI對客戶的能力要求也更高,如果有高階深層次定製化的編碼需求,可以考慮FEI。最後,附帶一句,我們同樣在AVFilter中集成了GPU的VPP以實現硬體加速的Scaling與Deinterlace等操作,後續也會支援Overlay、CSC等。
9、其他問題
9.1 CPU與GPU的資料交換
當我們在處理一些異構計算時,始終需要面對此問題:CPU與GPU、DSP之間的資料交換。資料從CPU拷貝到GPU與從GPU拷貝到CPU並不是一個對等關係,一般而言,資料從CPU到GPU進行拷貝的速度很快且不存在效能瓶頸;而如果是GPU到CPU的拷貝交換有可能面臨效能瓶頸,其原因是兩者使用了不同的快取策略。如果我們通過mmap GPU的memory到CPU側,之後不進行任何優化而是直接使用諸如memcpy函式將資料拷貝到CPU側,會發現效能可能不如預期。關於這個問題,英特爾也提供了一些優化辦法,例如使用SSE4/AVX等指令集中提供的bypass Cache的特定指令,也就是所謂的Faster Copy背後的特定指令;另外,也可考慮直接用GPU進行拷貝而非使用CPU,或者考慮從OpenCL層面進行優化。
9.2 FFmpeg中的硬體加速
FFmpeg提供了一些Filter用於實現硬體加速pipeline的建立,分別為Hwupload、Hwdownload、Hwmap、Hwunmap,使得在組成硬體的Pipeline時儘量避免大量的資料交換,所有操作儘量在GPU內部直接完成以提升效能。
9.3 硬體或驅動不支援
如果完成了編解碼的部署,需要AVFilter相關的優化但硬體或者驅動層面卻不支援,面對這種情況,我們可考慮OpenCL。因為OpenCL現在可與FFmpeg Video的編解碼進行Buffer Sharing,這相當於是一個GPU內部零拷貝的過程;只需要依靠Hwmap和Hwunmap實現的map就能直接用OpenCL對現有的AVFilter進行優化,從而幫助開發者解決此類由於CPU/GPU的資料交換導致的效能問題,與此同時,把OpenCL作為對GPU通用計算的標準介面,來優化我們的各種視訊或影象的處理;另外,我們可以將此思路放得更寬一點,如果客戶不希望直接使用來OpenCL來手動優化AVFilter,也可考慮把OpenCV作為一個已經被OpenCL優化好的演算法集合再整合進FFmpeg中。我們現在也在考慮此類方式並在其上進行嘗試。
10、To Do List
上圖展示的是我們正在實踐與探索的技術點,期待通過以上優化為音視訊行業帶來技術進步與行業發展。