1. 程式人生 > >姜健:VP9可適性視訊編碼(SVC)新特性

姜健:VP9可適性視訊編碼(SVC)新特性

640?wx_fmt=jpeg


與VP8相比,VP9進行了大量的設計改進以儘可能的獲得更高的視訊編碼質量。Google軟體工程師 姜健詳細介紹了VP9可適性視訊編碼(SVC)中多種新功能的實現與相應API。本文來自姜健在LiveVideoStack 線上交流分享,並由LiveVideoStack整理而成。


文 / 姜健

整理 / LiveVideoStack

直播回放

https://www2.tutormeetplus.com/v2/render/playback?mode=playback&token=e9d457fedba34b69844f3cba29345704


大家好,我是來自Google的姜健,今天主要想給大家分享一下我們在VP9 SVC裡新加的一些功能,以及一些相應API的設定,整體上會比較偏向技術一點。

 

640?wx_fmt=png


分享的主要內容包括以下幾個方面:


1、 介紹VP9 SVC;

2、 對比SVC和VP8的一些引數;

3、 SVC中去噪功能的實現;


一、SVC (Scalable Video Coding) in VP9


首先向大家提出一個問題,我們為什麼要用SVC?常用的視訊編碼難道不可以嗎?當我們進行視訊會議時,可能會有多方的參與者。如果其中一方參與者的網路狀況不是很好,在不採用SVC編碼時,則只有一個解析度,包括空間和時間解析度。這時,需要根據網路情況不太好的參與者來進行丟包,或者降低空間解析度來適應其網路狀況。又因為每個參與者收到的包都是一樣的,這樣其他網路情況好的參與者也就會同時受到影響。但是在採用SVC編碼的情況下,我們就可以很好的解決這個問題。SVC可以編碼不同的解析度,伺服器在分發的時候,它可以根據不同接收者的網路情況對應分發高解析度或低解析度的幀,當有參與者的網路情況不好時,就接受低解析度的幀。這樣一來,其他網路情況好的參與者可以不受影響的接收高解析度的視訊幀。


640?wx_fmt=png


目前VP9 SVC仍在WebRTC中不斷的改進,特別是對於螢幕分享的一些引數。Google正在進行Dogfood,意思就是指大規模的內測。因為Google本身有九萬多名員工,我們依靠自己員工的力量來進行內測,員工們如果有什麼問題,也可以及時向我們提交一些反饋,以便於進行修改。當內測進行幾個月之後,就會把它開放給公眾,後面會介紹一下我們在Dogfood中接收到的一些反饋和問題。

 

640?wx_fmt=png


今天會主要介紹VP9 SVC的幾種Feature。首先會給大家介紹一下SVC的參考幀預測,因為SVC包含空間和時間上的不同解析度,所以在參考幀的預測上會有很大的區別。我們還添加了一些特殊的Feature,比如幀內編碼幀,但是這個幀不是關鍵幀。另外,SVC的預測模式固定之後是可以更改的,即可以在編碼的過程中,隨時修改預測模式。還有一個就是我們最近加入的長時間尺度的編碼幀預測,最後則是去噪部分。


1、SVC Superframe

 

640?wx_fmt=png


在這裡給大家介紹一下SVC中是如何實現將不同解析度的幀放在一起的。上圖的例子中包含三個不同空間解析度的幀,在SVC裡面,不同的層有不同的解析度,這個例子中有三個不同的層即三個不同的解析度。假如相機捕捉到的是一個高清720P的幀,首先我們把最上層720P的幀4×4 Scale Down到180P,然後對其進行編碼。把中間層720P的幀2×2 Scale Down到VGA 360P,再對它進行編碼。最後,我們再編碼HD 720P的幀,編碼結束之後,再把編碼後的三個幀放在一起,我們稱之為超級幀(Superframe)。


2、SVC Patterns - 2 Spatial Layers, 3 Temporal Layers

 

640?wx_fmt=png


上圖是一個預測的例子,模式會稍微複雜一點,它把空間解析度分為兩層(SL0、SL1),時間解析度分為三層(TL0、TL1、TL2),不同的時間解析度相當於不同的幀率。在視訊流裡,編碼速率是60幀/秒,時間解析度TL2佔了一半,即30 FPS,時間解析度TL1佔了1/4,即15FPS,最後一層TL0也是15FPS。在這個預測模式中,灰色的箭頭代表LAST FRAME,它表示當前編碼幀的前一幀;金色的箭頭代表GOLDEN FRAME,在非SVC的定義為很早之前的一個幀,可能是第10幀、第20幀、第30幀,然後從它開始預測。但是,在SVC裡面,我們改變了它的定義,GOLDEN FRAME表示是從更低解析度的那一層去預測。在後面的預測過程中,我們會一直重複這樣的一個模式。對比非SVC,在整個預測過程中,都是採用的離得最近的幀,包括LAST FRAME為當前編碼幀的前一幀,在GOLDEN FRAME我們採用從更低解析度的那一層去預測,而沒有采用長時間的幀來預測,為了解決這一問題,我們後面又引入了一個新的Feature。


3、SVC Reference frame buffer and refresh

 

640?wx_fmt=png


由於我們用了LAST FRAME和GOLDEN FRAME這些參考幀,所以在更新參考幀的Buffer時,SVC會比較複雜一些。在前面我們瞭解到它的Pattern是非常複雜的,在進行幀的預測時,可能會被多個其它的幀用做預測,因此,這一幀會一直放在參考幀的Buffer裡面,留著以後才能用到。在Buffer的更新上面,我們還要用到另外一個概念叫ALTREF,ALTREF在非SVC裡指的是未來的某一幀,我們會從未來的某一幀來進行預測,這個是在做兩遍的壓縮時會用到的。但是,實時視訊編碼是不可能用到未來幀的,所以在SVC中,我們將ALTREF當成一個Marker,用來指定更新哪一個參考幀的哪一個Buffer。在VP9裡面一共有8個Buffer可以當成參考幀來用,大家可以看到,在這裡我們現在只是用了4個而已。為了解決在SVC裡沒有從長時間的某一幀來預測的問題,後面我們還會再用到另外一個Buffer。


4、SVC Interlayer Prediction

 

640?wx_fmt=png


接下來介紹一下我們在不同解析度幀之間進行預測新增的控制,如果你是使用者,就可以通過API來控制不同幀的不同解析度之間的預測是否開啟。還有就是我們可以控制在非關鍵幀上關閉預測,在關鍵幀上開啟不同解析度幀之間的預測,這個在WebRTC裡是用control prediction來設定的。這個API比較簡單,下面這張圖就是它的一些定義的型別。

 

640?wx_fmt=png


5、SVC Frame Dropping

 

640?wx_fmt=png


下面我來講一下SVC的丟幀過程。在非SVC裡,某一幀丟掉就丟掉了,但是在SVC裡由於一個超級幀裡面有三個不同解析度的幀,在丟幀時,則要考慮是丟整個幀,或者只丟掉其中的某些層。就像在前面第一個Feature講的一樣,我們因為控制不同層之間的預測,如果不同的層之間沒有相互依賴的關係,當丟掉最高解析度的層,也可以進行解碼。如果不同幀互相之間是有依賴的,那麼在丟最低解析度層的時候,必須把最高解析度層一起丟掉,否則就解不出來。我們現在有三個不同的設定,第一個設定就是更低解析度的幀丟掉之後,更高解析度的幀也就直接丟掉,這種情況適用於不同空間解析度的幀之間互相有依賴的情況。第二個設定是任何一個空間解析度的幀都可以隨便丟,這種情況是不同解析度的幀之間沒有相互依賴的關係。第三個設定就是最低解析度的幀可以丟,在最低解析度的幀丟掉之後,整個超級幀都一起丟掉。這個跟第一個有點類似,它們有交叉的情況。下面給出的就是一個控制丟幀的API。

 

640?wx_fmt=png


這個API稍微複雜一點,因為丟幀依賴於一些在壓縮過程中的引數。第一個是閾值,閾值是針對於每一個空間解析度的層來定義的。這個閾值是指在編碼時,要預測它的大小,然後會有一個Buffer去裝它,當這一幀編完之後,可以定義體積比閾值大多少的情況下,我們才把這個幀給丟掉,而且可以根據不同的層去定義不同的閾值。第二個是三種丟幀的模式。第三個是指最多連續可以丟多少幀。比如說,網路情況特別不好的情況下,有可能連續丟好多幀,但是如果已經達到了連續最多丟幀的數目,就會強制把丟幀的Feature關掉,這樣就會強制送一幀過去,視訊會議的參與者也就不會什麼都看不到。


6、Intra-only Frame

 

640?wx_fmt=png


下面一個Feature是幀內編碼的幀,但是它是非關鍵幀。舉例說明,如果沒有這個Feature,當我們現在有五個人用視訊會議開會時,突然有第六個人加入進來,他沒有任何關於當前視訊的資訊。如果想開始的話,所有人的視訊幀都必須要插入一個關鍵幀,他才能看到所有人。但是,插入關鍵幀會有一個問題,關鍵幀會更新所有的關鍵幀的Buffer,這個會導致所有人重新開始,也就是這個視訊流會重新開始一遍。然後我們想到,可以用幀內編碼的幀,並且它不是關鍵幀,這樣一來就不會去更新所有的參考幀的Buffer。

 

640?wx_fmt=png


如此當新使用者進來之後,其他所有人都只需要插入一個幀內編碼的幀即可。對於以前有的使用者,會接收更高解析度的,如上圖圈出的SL1,因為幀內編碼的幀不會去更新所有的參考幀,它還是可以從以前的視訊流裡進行預測,它接收高解析度的使用者,然後非常平滑的過渡到新使用者進來,如果沒有幀內編碼,關鍵幀插入進來,所有的人的視訊都中斷一下,這樣就非常不平滑。但是在VP9裡,這應該是比較老的一些設計,必須是no show frame,意思是指不能被顯示出來,解碼器在解碼的時候,是看不到這一幀的。

 

640?wx_fmt=png


為了解決這個問題,我們在VP9中使用了另外一個Feature,用來顯示已經存在的一個幀,稱為show existing frame。在一個超級幀中,編碼完幀內編碼的幀後,把它的幀頭部copy到另外一個幀裡,然後把copy後的幀加上以前顯示幀內編碼的幀的標記。但是,因為我們只複製它的頭部,它的編碼資料還是來自於幀內編碼的幀,整個超級幀的大小也基本上不會變化。通過這個我們很好的解決了新使用者加入到視訊會議後,所有人的視訊流都要重啟的問題。


7、Long Term Temporal Prediction

 

640?wx_fmt=png


前面曾提到過,SVC中所有的預測模式,有的是低解析度的預測即同一幀同一幅畫面,有的是離它很近的,可能是前一幀或者前兩幀預測的情況。VP9支援8個參考幀的Buffer,但我們只用到了4個,那麼空閒的一些參考幀的Buffer可以用來做長時間參考幀的預測。但是因為多加入了一個參考幀,編碼的時間會增加,且編碼速度也要損失一些。目前我們只是把它們用在了最高解析度那一層,因為在測試中發現,由於網速很快且裝置效能較強,所以絕大多數人都是隻接收最高解析度的那一幀。如果要更新參考幀的Buffer,只需更新在最下面的時間解析度層。此外,為了簡單,我們的實現比較暴力一點,只用到了8個參考幀中最後一個參考幀的一個Buffer。由於多了一個參考幀,對壓縮效率及質量的提升很大,在我們自己的實時測試編碼資料集上,有4%的提高,但是在一些運動範圍比較大、運動比較多的視訊上測試,有10%到12%的提高,因為在運動比較多的時候,更有可能從長時間的預測上面得到比較有用的資訊。下圖是一個採用了長時間的參考幀的示意圖,它是從很早之前的某一個幀去進行預測的。

 

640?wx_fmt=png


8、Scale partitioning in SVC

 

640?wx_fmt=png


還有一個Feature是用來加速用的,在VP9裡支援最大的塊是64×64,最小的塊在關鍵幀上是4×4,非關鍵幀上是8×8,而且這是一個遞迴結構,非常浪費時間,所以需要優化分塊的演算法。在優化分塊的演算法中,由於在SVC裡面先編碼低解析度的,再編碼高解析度的,所以我們在編碼HD時, VGA這層的編碼已經有了,我們就想,在某些特定的情況下如SAD比較低,我們就把VGA裡64×64的塊直接放大到HD層進行使用,這樣我們就完全跳過分塊部分的演算法。在上圖的例子中,VGA是一個64×64的塊,最小的是8×8。在把VGA放大到HD層時,由於VGA是64×64的一個大塊,在 HD這邊就單獨拿左上角這塊與之對應。這樣一來,我們就完全跳過了分塊的演算法,在我們的RTC測試集上面,平均速度提高5%左右,在有些視訊上面,提高10%左右。


9、Motion Vector Reuse

 

640?wx_fmt=png


最後是一個很簡單的Feature,我們會重複利用低解析度的運動向量,把低解析度下計算的運動向量表示成MV的兩個方向,在更高解析度的情況下,我們會利用已有的資訊來進行運動向量的搜尋。在我們需要尋找到新的運動向量時,就將搜尋範圍進一步縮小,從而更快的進行運動向量的搜尋,這是加速的Feature,沒有API可以控制。


二、VP9 SVC v.s. VP8 Simulcast

 

640?wx_fmt=png


這一部分給大家看一下VP9 SVC與VP8的質量對比,上圖中藍色代表的是VP9,紅色代表的是VP8,因為VP9比VP8有30%~40%壓縮效率上的提高,出現上圖的結果也不奇怪的,相同的位元速率VP9有更高的PSNR,可以節省更多的位元速率。左側是我們用RTC做的一些測試,最後從PSNR來看,相對VP8來講,還是有很大提高的,大約有40.5%。


640?wx_fmt=png


上圖展示的是VP9 SVC的編碼速度,資料中的最近時間是2018年2月份,已經相對比較老了。現在SVC的編碼速度又相對提高了一些,有超過45%的提升,以前每編碼一個超級幀大概需要25毫秒,現在大概是10在15毫秒之間。這個測試是在Mac裝置上進行的,膝上型電腦也完全可以達到實時性的要求。並且,根據我們在Google內部的預測,沒有接收過速度太慢之類的反饋,目前從速度和效能上來說還是不錯的。


三、VP9 Temporal Denoiser

 

640?wx_fmt=png


接下來介紹一下去噪的過程,之前我們在測試Denoiser時,錄製下來的視訊噪音還是比較大,因為噪音都是隨機產生的,隨機訊號是根本無法壓縮的,所以我們需要儘量減少噪音。此外,去噪其實是要耗費很多時間的,因此在去噪前我們會做一個噪聲的水平估計。大約每20到 30幀做一次估計,如果噪音水平很低,就不再進行去噪了;如果噪音水平很高我們會通過進行運動補償來決定是否去噪。對當前編碼塊做運動補償,從參考幀上做運動補償過來再進行比較。可能會返回兩種結果,一種是從視訊源直接將編碼塊複製過來,則不再進行去噪;另一種就是對當前塊去噪。

 

640?wx_fmt=png


在非SVC情況下,我們可以把INTRA看成Source,就是攝像頭直接拍下來的內容,LAST、GOLDEN、ALTREF是參考幀。在去噪的過程中我們會做運動補償,比如說,上圖中綠色的塊,它的參考幀是LAST,我們對LAST進行運動補償,並把它放到一個新的Buffer裡面,上圖中黃色的塊,它的參考幀是GOLDEN,我們對GOLDEN塊進行運動補償並放到新的Buffer裡面,然後對運動補償過的幀進行去噪,並且只針對3個通道Y、U、V 中的Y通道做去噪。 

 

640?wx_fmt=png


在SVC情況下,去噪相對比較複雜,因為每一個超級幀上都有兩個,三個甚至更多不同的解析度,我們就需要更多的 Buffer在Denoiser裡面放置參考幀,所以最後參考幀的數量就是用參考幀的數目乘以需要去噪音的不同解析度層的數量。比如,上述例子中解析度有一個HD層,以及VGA層,如果這兩層都決定去噪,那麼就需要8個Buffer。 

 

640?wx_fmt=png


我們將去噪的控制權交給了使用者,讓使用者來決定選擇HD層還是VGA層進行去噪。使用者可以通過一個Denoiser的API設定,如果將API設成1,去噪就只有最高解析度的這一層,在這種情況下,當在Denoiser分配Buffer的時候,只用四個Buffer就好了。如果將API設成2,也就是現在要對兩個解析度層進行去噪。它會針對HD和VGA兩層進行去噪,在Denoiser上就需要8個Buffer。


四、總結

 

640?wx_fmt=png


總結一下,首先VP9已經開始在Google進行內測,我們也正在不停地改進,新增一些新的Feature進去,預計不久之後應該會將VP9 SVC加入到WebRTC中開放給大家。此外,VP9 SVC質量相比VP8來說還是非常好的,並且我們也在不停提高編碼速度,這樣一來,無論是使用膝上型電腦或桌上型電腦,VP9 SVC也完全可以滿足實時性要求。


精品文章推薦




技術乾貨: