首先,還是要弄清楚編解碼的流程和 H.264 的關鍵技術,看白皮書就知道了,另外 H.264 綜述類的文章和別人的學位論文一般也會講到;
其次,弄清楚代碼的各個函數實現的功能,這個可以看看 JM 代碼里各個函數前面的函數說明;
最后,弄清楚標準各個章節講的什么內容:這里只說重要的。第三章是名詞解釋,第四章是縮略語,第五章是一些計算方式和運算符號的說明,第六章是與 H.264 相關的一些視頻基礎知識和 H.264 中用到的一些過程推導,第七章是 NALU 及其以下語法結構的語法和語義(如果要知道碼流結構就要看這一章了),第八章是詳細說明解碼過程中某一個模塊的功能怎么完成,第九章是熵編碼,附錄 A 是關于 profile 和 level 的具體規定,附錄 B 是關于如何從字節流中解析 NALU(標準沒有說明如何在 RTP 流中解析 NALU)。
有了上面的基本知識,下面我們結合對碼流的解析過程來講講怎么讀標準:
1、如果是字節流的碼流當然就首先要對字節流進行解析,這就要看附錄 B 了;如果是 RTP 格式的碼流,那首先就要按 RFC3984 來解析了(標準沒有規定 RTP 格式碼流的解析過程);
2、字節流解析完后提取出來的就是 NALU 了,對 NALU 的解析就要看 7.3.1 小節了。第七章中黑色的粗體字都是在碼流中可能出現的語法元素,解碼器的首要任務就是要對這些語法元素進行解析。對于這些碼流中的語法元素我們要進行解析必須知道三個問題:
(1)、什么時候存在于碼流中?這樣我們才能知道當前解析的是哪個語法元素;
(2)、采用什么樣的熵編碼方式?這樣我們才能知道如何解析;
(3)、含義是什么?這樣我們才知道解析出來之后用來干什么。
三個問題的答案分別是:
(1)、有 if 條件關聯的就是可能出現的,沒有 if 條件關聯的就是必然出現的。例如,7.3.1 小節表中的 forbidden_zero_bit 就沒有 if 條件關聯,所以它必然出現在碼流中;
(2)、每個語法表最后一列都對所在行語法元素的熵編碼方式做了規定,而最后一列各個符號具體是代表什么編碼方式那就去看 7.2 小節最后的部分;
(3)、看 7.4 小節與語法表對應的語義部分,例如你查的語法表是 7.3.1,那么該語法表里出現的語法元素的解釋就在 7.4.1 小節中。
3、NALU 的前面三個語法元素所組成的一個字節我們稱為 NALU 頭,其余部分(也就是語法表 7.3.1 中的其余部分)我們稱為 NALU 體。對 NALU 體的解析要看 7.3.2 小節。因為 NALU 有很多種類型,所以要針對 NALU 的不同類型去解析 NALU 體(表 7-1 說明了不同 NALU 對應的語法表)。例如,如果當前的 NALU 是 SPS,那么當然就要看 7.3.1 小節;如果當前的 NALU 是 DPA,那么當然就要看 7.3.2.9.1 小節了;
4、對于屬于 VCL 的 NALU(哪些 NALU 是 VCL NALU 呢?如果你看了 nal_unit_type 的語義,你就應該知道),例如表 7-1 中類型為 5 的 NALU,根據表 7-1 我們知道 NALU 體的語法表是 7.3.2.8。而從 7.3.2.8 我們可以看到,對這種 NALU 的 NALU 體解析實際就是對片級語法進行解析。語法表 7.3.2.8 顯示片級語法解析首先要解析 slice_header()(這種帶括號的表示是另一個語法結構),那么 slice_header() 怎么解析呢?往下看,7.3.3 的所有內容都被第一行的 slice_header() 包括在內,所以 7.3.3 就是對 slice_header() 這個語法層的碼流規定;
5、按照語法表 7.3.2.8 解析完了 slice_header() 就該解析 slice_data() 了。下面以最常見的 I 幀(CAVLC 熵編碼、非 MBAFF)的解析過程為例簡單描述怎么繼續讀標準。這時在碼流中出現的第一個 slice_data() 層的語法元素是語法表 7.3.4 中的 macroblock_layer(),也就是說直接到了宏塊層的語法解析,那就要又要看 7.3.5 小節了;
6、基于我們對編解碼流程的了解,我們知道解碼是一個預測值加殘差得到重建圖像的過程,那么我們下面的解碼過程就要分成兩步走了:首先,得到預測值;其次,得到殘差。基于我們對 H.264 關鍵技術的了解,我們知道 intra 宏塊(提醒:我們舉的例子是 I 幀,因此解析的是 intra 宏塊)的預測值是需要使用到預測模式的,所以我們需要解析語法表 7.3.5 中的 mb_pred(mb_type) 語法層,那么又去看 7.3.5.1 小節。按照 7.3.5.1 小節解析出宏塊或塊的預測方式后我們怎么計算預測值呢?去看標準 8.3 小節;得到預測值后我們繼續按照語法表 7.3.5 解析語法元素直到 residual() 語法層,這就又要去看 7.3.5.3 小節;按照 7.3.5.3 小節解析出殘差系數后我們如何把它還原成真實的殘差呢?去看標準 8.5 小節;
7、預測值和殘差都有了,加起來就是解碼圖像了。解碼的主要工作到此也算基本完成了。當然,上面的過程中還會用到標準其他章節的相關內容(例如,8.5 小節會用到 5.7 小節中定義的 InverseRasterScan),這就留給大家自己去學習了。
上面講了如何讀標準,那么如何讀代碼呢?非常簡單,因為你現在已經知道了代碼中各個函數所實現的功能以及標準各個章節所涉及的內容,那么你就該知道標準某個部分的內容與代碼中的哪個函數對應,因此對于你想詳細了解實現過程的模塊,對照標準去仔細啃那個函數吧。對于代碼中不明白的變量或者參數,把程序跑起來,看第 1 個 MB 解碼時候該變量的值是多少,第 23 個 MB 解碼時候該變量的值是多少……多做幾個觀察值,注意不要選擇特殊位置,然后總結一下規律,這樣你就自然能分析出該變量的作用和含義了。
以上講的是解碼過程,編碼過程就是解碼的反過程,因此同理。
——天之驕子(firstime)——
2008年8月5日
原文:bbs.chinavideo.org/viewthread.php?tid=4164