【H.264/AVC視訊編解碼技術詳解】十三、熵編碼演算法(4):H.264使用CAVLC解析巨集塊的殘差資料
《H.264/AVC視訊編解碼技術詳解》視訊教程已經在“CSDN學院”上線,視訊中詳述了H.264的背景、標準協議和實現,並通過一個實戰工程的形式對H.264的標準進行解析和實現,歡迎觀看!
“紙上得來終覺淺,絕知此事要躬行”,只有自己按照標準文件以程式碼的形式操作一遍,才能對視訊壓縮編碼標準的思想和方法有足夠深刻的理解和體會!
GitHub程式碼地址:點選這裡
1. H.264的CAVLC解析巨集塊殘差資料的流程
在H.264的解碼器在解析巨集塊的殘差資料時,其流程類似於上文提到的CAVLC編碼的逆過程。在解析一個巨集塊殘差的時候,首先解析的是殘差矩陣的非零係數以及拖尾係數的個數numCoeff
2. 計算CAVLC解析殘差的上下文引數
CAVLC編解碼過程中的上下文即為當前塊值numberCurrent。該值與當前畫素塊的左側鄰塊和上方鄰塊中非零係數的個數有關。
以尺寸為4×4巨集塊分割方式為例。當前畫素塊同左側和上方鄰塊的相對位置關係如下圖:
對於當前畫素塊,若其上方和左側相鄰塊都不可見(unavailable),那麼當前畫素塊的numberCurrent值為0;若上方或左側,有且僅有一個相鄰塊是可見的,那麼當前畫素塊的numberCurrent值即為這個鄰塊中非零係數的個數numCoeff;若兩個鄰塊都是可見的,那麼當前畫素塊的numberCurrent值為兩個鄰塊numCoeff的四捨五入平均值。
3. 解析非零係數總個數和拖尾係數個數
在CAVLC的解析過程中,非零係數總個數numCoeff和拖尾係數個數trailingOnes兩個值是一起解析出來的。解析這兩個值依據的是標準文件中的表9-5,如下表即是表9-5的部分:
根據之前解析出來的numberCurrent值,在這個表格中選擇一列作為解碼資料的參考。此後,從碼流中讀取相應長度的二進位制碼流,與表格中的值相比較。當碼流與表格中的值匹配時,表格的前兩列作為陣列的下標,其值即等於希望解析出來的numCoeff和trailingOnes的值。
4. 解析拖尾係數的符號
我們知道變換系數矩陣中最高頻的幾個絕對值為1的非零係數稱之為拖尾係數,其個數範圍為0~3個。表示每一個拖尾係數的符號可以一個bit的trailing_ones_sign_flag表示:
- 當trailing_ones_sign_flag為1,拖尾係數符號為-;
- 當trailing_ones_sign_flag為0,拖尾係數符號為+;
5. 解析非零係數的幅值
非拖尾的非零係數的幅值通常表示為levels。Levels的解析相對較為複雜。該部分是從最高頻開始解析到最低頻的非零係數為止。也就是說,levels部分是按頻率倒序解析的。
在解析每一個level的時候,每一個值都會按照字首(prefix)和字尾(suffix)兩部分進行解析。
5.1 解析level_prefix部分:
Level_prefix部分即level的字首部分,該部分的解析較為簡單,以偽程式碼表示如:
leadingZeroBits = −1
for( b = 0; !b; leadingZeroBits++ )
b = read_bits( 1 )
level_prefix = leadingZeroBits
結合標準文件中的表9-6的表述可知,level的字首值即為當前碼流的下一個位元1之前連續的位元0的個數。
5.2 解析level_suffix部分:
Level_suffix部分的解析比prefix部分複雜,總體上可以分為以下幾個步驟:
- 解析過程開始之前,初始化suffixLength的值:當非零係數總數numCoeff大於10且拖尾係數個數trailingOnes等於3時,suffixLength初始化為1,否則初始化為0;
- 確定levelSuffixSize的值:通常情況下,levelSuffixSize的值等於當前的suffixLength,除了下列兩種意外情況:第一,level_prefix的值等於14且suffixLength為0,此時levelSuffixSize設為4;第二,level_prefix大於等於15,此時levelSuffixSize設為level_prefix-3;
- 解析level_suffix的值:根據levelSuffixSize的值作為長度,在碼流中讀取對應的二進位制資料作為level_suffix;若levelSuffixSize為0,則level_suffix的值為0;
5.3 由level_prefix和level_suffix部分組合成為levelCode
在解析完成level_prefix和level_suffix之後,將二者組合生成levelCode。計算方法為:levelCode=(Min(15,level_prefix)<
5.3 由levelCode計算level
根據計算得到的levelCode的奇偶性,判斷level的符號:
- 若levelCode是偶數,返回level值為(levelCode + 2)>>1;
- 若levelCode為奇數,返回level值為(−levelCode−1)>>1;
5.4 更新suffixLength的值
在解析過程中更新suffixLength體現了上下文自適應的思想。
- 當suffixLength = 0時,suffixLength更新為1;
- 當suffixLength小於6,且剛剛解析出來的level值大於閾值threshold時,suffixLength自增1;閾值threshold定義為( 3 << ( suffixLength − 1 ) );
6. 解析零係數資訊
變換系數矩陣中的零係數也是重要的資訊。CAVLC解析的零係數資訊主要分兩類:
- totalZeros:每個矩陣一個值,表示最高頻非零係數前零係數的總個數;
- runBefore:每個非零係數一個值,表示該非零係數前連續0的總個數;
解析totalZeros的過程與解析numCoeff和trailingOnes類似,都是從一個二維表格中查詢某列表格,在從碼流中查詢與表格中匹配的值,然後索引便是所求的totalZeros值。解析totalZeros的表格為標準文件中的表9-7。下圖是表9-7的區域性:
在解析totalZeros的過程中,選擇表格的索引值等於當前矩陣塊的非零係數個數numCoeff。
解析每個非零係數的runBefore時,也是按照從高頻到低頻逆序處理的。每次解析的runBefore也是按照類似上述的解析方法,從碼流中讀取相應長度的碼流並與表格中的值比對,匹配後返回索引值作為解析的值。解析runBefore參考標準文件的表9-10:
每次解析出一個runBefore後,totalZeros都要減去該值,然後進行下一次處理。若有n個非零係數,則總共需要解析n-1個runBefore。最低頻率的非零係數前的runBefore不需要寫在碼流中,因為可以通過上述資訊推算出。
以上就是解析一個巨集塊的4×4殘差係數矩陣相應語法元素的主要思想和過程。當然實際的解析過程比此要複雜得多,更詳細的情況可到CSDN學院的課程:H.264/AVC視訊編解碼技術詳解中觀看。