ETH官方钱包

前往
大廳
主題

【程式】顯卡少女的第二個能力——視訊編解碼

Shark | 2021-09-02 22:57:43 | 巴幣 672 | 人氣 1832

介紹GPU在3D以外的另一個能力。
筆者也沒碰過DXVA之類的底層API,因為以下介紹的多媒體framework幫我們包裝好了,只要電腦有支援硬體加速就會自動啟用,所以只能介紹如何查詢資訊,以及怎麼設定軟體。

2022/04/23:增加Firefox的設定方式



視訊的資料量很大,如果不用一些演算法編碼,那硬碟動不動就爆滿,網路動不動就塞車。

來算算看一個影片壓縮率是多少。
拿Youtube 720p的影片做例子,尺寸1280×720,30fps,如果原始資料是24 bit RGB,那一秒的資料量是:
一個畫面的像素數:1280×720
一秒的像素數:1280×720×30
一秒的資料量:1280×720×30×24 = 663552000 bits = 663552 kbits
事實上幾乎所有影片都不用RGB色彩格式,而是YUV420,平均一個像素12 bits,但這樣資料量也有331776 kbits。

拿這個的720p版來算算看。

下載後用軟體查看,視訊部分平均位元率是1597 kbit/s,壓縮比是1597/331776=0.48%,是很高的壓縮率。
(拿畫面有在動的影片測試才有代表性。如果是畫面是靜態圖,純欣賞音樂的影片,視訊位元率會比較低,因為視訊編碼其中一個技術是不記錄整個畫面,只記錄與前一個frame的差異)

但為了達到高壓縮率,需要用一些計算量龐大的演算法,如iDCT(逆離散餘弦轉換)、Motion compensation,而且隨著解析度需求越來越高,計算量也更大。

(2K變4K長寬變為兩倍,面積變成4倍,所以原始資料量是4倍)
shader的輸入與輸出」有提過,GPU最擅長SIMD和多個thread平行處理。
不過目前做得到的是把解碼過程的一部分拿到GPU計算,其他部分還是需要CPU處理。

相對地音訊與靜態圖的壓縮演算法比較不複雜,視訊有畫面二維+時間一維+YUV三個分量的變化,音訊只有時間一個維度,靜態圖只有一個frame,或許因此很少人在討論音訊與圖片硬體解碼。



底層API
生產GPU的廠商很多,PC有N牌、A牌、I牌,智慧型手機還有ARM、PowerVR等等更加多樣,假如每家廠商各自開規格呢?

……很遺憾,這次沒有像Direct3D、OpenGL那樣通用的規格,而是隨作業系統和GPU而異,可能因為有人開發出後述的多媒體framework覆蓋掉底層的差異,所以大家認為通用規格沒有急迫性。
(上圖中的函式名稱是DXVA、 VDPAU、VA-API實際有的函式)

以下介紹Windows和Linux桌機版的情況,因為筆者熟悉的是這兩個系統,其他系統要另請高明了。

Windows的底層API是DXVA (DirectX Video Acceleration)。

類Unix(包括Linux與BSD)有以下四種
XvMC
XvBA
VA-API
VDPAU
前兩個比較舊,現在都是用後兩個。
VA-API是Intel開發的,VDPAU是nVidia開發的,這兩家晶片只支援自家的API,AMD則是兩種都支援。
雖然有人做了轉換層,把其中一個的函式轉換成另一個的底層,但是多了一層,效能當然不及原生支援。

nVidia另外有發佈NVDEC和NVENC,當然只能在nVidia的晶片使用,但不限作業系統。

此外維護OpenGL和OpenAL的組織:Khronos Group有開發出一個規格OpenMAX,但廠商接受度很低,目前好像只有Android在使用。

多媒體framework
多媒體編碼格式多如牛毛,格式從副檔名判斷不準,必須檢查檔案內容才能判斷。辨認檔案是哪個格式,然後從電腦裡有安裝的解碼器找出適當的來用是複雜的工作,所以很多人寫了framework做這些事。
這些framework除了辨認格式以外也會檢查目前系統,只要電腦有支援就會自動使用硬體加速,所以寫程式很少需要直接用底層API,除非你要開發這些framework的擴充。
常用的有如下

Windows
DirectShow,比較舊
Media Foundation,Windows Vista以後新增的
跨平臺
GStreamer
libVLC
xine-lib



再來介紹兩個平臺上如何確認硬體加速
不是所有編碼都可以硬體加速,播影片測試時要看一下影片的編碼,MPEG2和H264大部分平臺都有支援,其他格式就不一定。

Windows
Windows 7以後安裝好就有內建WMV和H264硬體加速,但內建的媒體播放器:Windows Media Player和Movies & TV不能查看現在用的是哪個解碼器。

筆者找到一個軟體:DXVA Checker可以查看這臺電腦上的DXVA資訊
https://bluesky-soft.com/en/DXVAChecker.html
Decoder Device頁面顯示晶片支援哪些codec和哪些演算法。
左邊的名稱是什麼意思要查官方的DXVA文件。
MSDN: IDirectXVideoDecoderService::GetDecoderDeviceGuids method

看起來即使同樣的演算法,廠商也要為不同codec各寫一份驅動程式,例如iDCT要寫MPEG2的、H264的、VC1的各一份。

DS/MF Decoder頁面可以選擇一種格式或一個檔案,看解碼的哪些部分可以用DXVA處理。但此頁顯示的資訊好像不太準,例如筆者在這裡看MPEG2沒有顯示任何東西,但播放MPEG2的時候用GPU Engine Usage查是有把GPU拿來用的。
(DS=DirectShow,MF=Media Foundation)


GPU Engine Usage頁面可以看GPU的使用情況,但是要Windows 10以上才有這功能。先用一個軟體播放影片,在這裡按Start查看,如果有VideoDecode之類的就表示有GPU加速,不同廠牌的晶片可能會顯示不一樣的欄位。


另一個軟體:GPU-Z雖然有「DXVA 2.0 Hardware Decode」的項目,但裡面什麼也沒顯示,不知道這是幹嘛的。


Media Player Classic是自己帶一個解碼器:LAV Filter,不是用Windows內建的解碼器。它可以查看硬體加速。
選Play → Filters → LAV Video Decoder。


查看Active Hardware Accelerator這一項,如果不是None就表示有了。


Linux,安裝驅動程式
Linux剛灌好後不一定有硬體加速,即使晶片有功能,驅動程式和framework也必須安裝擴充才能使用,要自己檢查一下。
詳細說明可以看這篇,雖然是ArchLinux的文件,其他發行版也可以參考。
Hardware video acceleration - ArchWiki

首先看晶片種類確定支援的是VA-API還是VDPAU,然後照以下步驟。套件名稱隨發行版而異,以下是LinuxMint的名稱,其他發行版請自己找。

安裝vainfo和vdpauinfo,兩者分別可列出VA-API和VDPAU的資訊。
Mint的套件名稱就叫vainfo和vdpauinfo。

在命令列打指令,如果跳出類似下面的error訊息,表示驅動程式沒有安裝。
vainfo
libva info: VA-API version 0.39.0
libva info: va_getDriverName() returns 0
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/i965_drv_video.so
libva info: va_openDriver() returns -1
vaInitialize failed with error code -1 (unknown libva error),exit
vdpauinfo
display: :0   screen: 0
Failed to open VDPAU backend libvdpau_va_gl.so: cannot open shared object file: No such file or directory
Error creating VDPAU device: 1
反之如果顯示驅動程式的資訊和支援的codec,那代表驅動程式已經裝好了。

沒安裝的話按照晶片種類,自己找套件安裝。
例如筆者的Linux開發機有Intel和nVidia兩個晶片,用這個工具看一下才知道Intel的並沒有安裝,於是安裝這個:i965-va-driver。
nVidia則是裝閉源驅動程式才有,不過自從換了Mint 19.3和Fedora 33之後裝閉源驅動程式就不能開啟GUI,就決定不裝了,放棄使用nVidia晶片。

Linux,framework與軟體如何啟用硬體加速
各發行版預先安裝的軟體不同,且各軟體使用的framework不一樣,要先確定軟體用的是哪個framework再動手。
舉四個軟體做例子,其他軟體可以看上面那篇ArchLinux的文件,Configuring applications的部分。

GStreamer:
GNOME的totem,以及KDE的Dragon player屬於這種。
VA-API要安裝這個:gstreamer1.0-vaapi,然後照這篇的方法打指令確認。
GStreamer - ArchWiki
VDPAU似乎要裝nVidia閉源驅動程式才能用。

libmpv:
Mint 19以後內建的Celluloid屬於這種。
如果用命令列執行mpv,照這篇加「--hwdec=auto-safe」的參數。
https://wiki.archlinux.org/index.php/Mpv#Hardware_video_acceleration
Celluloid使用mpv的函式庫,照這篇在「偏好設定→額外的MPV選項」裡加一個參數。
How to enable HW acceleration in Celluloid video player in Linux Mint 20?

但是上篇是Mint 20的,筆者在Mint 19.3試發現情況不一樣,即使加了參數,用Celluloid播影片CPU佔用率還是到大約16,而且不能按i查看詳細資訊。
要照下圖的方式填,且修改設定後要把Celluloid關掉重開才會生效,這樣CPU佔用率降到2~3了。

libmpv各版本能用的參數不一樣,筆者是裝了mpv然後打「mpv --list-options」和「mpv --vo=help」查看參數才試出來。

VLC:
不一定會自動使用,要自己檢查一下。
播影片的時候點選「工具 → 訊息 → 模組樹」(Tools → Messages → Modules Tree)。
如果像下圖,decoder的部分有vaapi或vdpau的項目,表示有開啟。

如果沒有那就要手動啟用,進入「工具 → 偏好設定 → 輸入 / 編解碼器」,修改「硬體加速解碼」這一項。

修改後要把VLC關掉重開才有效。

Firefox:
多數發行版將Firefox作為預設瀏覽器,但它相當耗效能,筆者每次開Firefox電腦就變燙,尤其是網頁含有廣告的時候,原因之一是Firefox預設不會啟用視訊解碼加速。
(其他瀏覽器也好不到哪裡去,有試一下Falkon,效能也沒有很好,也預設不開視訊解碼加速。
我覺得很多網頁做得太吃效能也是問題。)
參考:
Firefox - ArchWiki
Fedora wiki: Firefox Hardware acceleration
UbuntuHandbook: Enable Hardware Video Acceleration (VA-API) For Firefox in Ubuntu 20.04 / 18.04 & Higher
Firefox依版本不同方法也不一樣,我自己試過86版和100版。

Firefox用的視窗protocol與OpenGL API有wayland+EGL、X11+EGL、X11+GLX三種組合,其中X11+GLX不能支援視訊硬體加速。而且Firefox只支援VA-API不支援VDPAU。

在網址列打「about:support」,查看「WebGL 1 驅動程式 WSI 資訊」和「WebGL 2 驅動程式 WSI 資訊」,必須顯示EGL才能使用。

如果顯示的是GLX,下面說明如何改。

參考上面ArchWiki那篇,在網址列打「about:config」修改設定值。
95版以前要四個都改,100版只要改media.ffmpeg.vaapi.enabled一項。
media.ffmpeg.vaapi.enabled true
media.ffvpx.enabled false
media.navigator.mediadatadecoder_vpx_enabled true
media.ffmpeg.vaapi.enabled false

如果WebGL驅動程式是GLX就再改一項:將gfx.x11-egl.force-enabled設成true。

還要裝一個擴充:enhanced-h264ify,這是在看Youtube的時候強制使用H264編碼而不用VP8/VP9,因為很多硬體有支援H264硬體解碼但不支援VP8/VP9。
enhanced-h264ify
把Firefox關掉重開才會生效。

100版還要設一個環境變數。
如果自己帳號的家目錄有.bash_profile、.bash_login、.profile其中一個存在就打開它,沒有就自己建立一個文字檔.profile,在裡面加入這一行。
export MOZ_DISABLE_RDD_SANDBOX=1
重新登入才會生效。

這樣看Youtube時有效,CPU使用率有降低,但看含有廣告影片的網頁就還是一樣發燙,必須特地把影片按暫停。



關於插圖:這次不用色塊上色,而是MyPaint筆刷的水彩型塗抹(「上色法測試」的5A)。
不要一直用同一種,目前開發的幾種上色法都要找機會用一下,才不會生疏。

以前有查過MyPaint的程式碼了解參數怎麼調(GIMP與MyPaint演算法調查,筆刷篇),MyPaint的筆刷半徑單位不是像素,而是「e的x次方」,e是自然對數的底2.71828……,所以使用時有時候要算Exp和Log。
這次查了一下PowerShell要怎麼用數學函數,在Windows PowerShell比較容易叫出來,能用這個就不用請出比較費事的明緹(Python語言)了。
#想得到1.5 pixel要把數值設成多少?
[math]::Log(1.5)  # = 0.405465108108164
#數值填0.5是幾個pixel?

[math]::Exp(0.5)  # = 1.64872127070013

畫圖遇到難畫的部分有時候會想開遊戲玩一下,就不知不覺拖了一段時間,應該要硬撐下去研究解法,現在吃一點苦但以後畫同樣的東西就不會有困難了。
現在多數遊戲並沒有防沉迷系統,只能在自己腦子裡做一個。

創作回應

更多創作