參考《新一代視頻壓縮編碼標(biāo)準(zhǔn) 畢厚杰 第7章 H.264的句法和語義》
一、句法元素與變量
編碼器將數(shù)據(jù)編碼為句法元素然后依次發(fā)送咧虎。在解碼器端卓缰,通常要將句法元素作求值計(jì)算,得出一些中間數(shù)據(jù)砰诵,這些中間數(shù)據(jù)就是 H.264 定義的變量征唬。如圖 7.5:
圖中,pic_width_in_mbs_minus1 是解碼器直接從碼流中提取的句法元素茁彭,這個(gè)句法元素表征圖像的寬度总寒,以宏塊為單位。我們看到理肺,為了提高編碼效率摄闸,H.264 將圖像實(shí)際的寬度減去 1 后再傳送。
PicWidthInMbs = pic_width_in_mbs_minus1 + 1
PicWidthInSamplesL = PicWidthInMbs * 16
PicWidthInSamplesC = PicWidthInMbs * 8
以 上 變 量 PicWidthInMbs 表 示 圖 像 以 宏 塊 為 單 位 的 寬 妹萨, 變 量 PicWidthInSamplesL 年枕、PicWidthInSamplesC 分別表示圖像的亮度、色度分量以像素為單位的寬乎完。H.264 定義這些變量是因?yàn)樵诤罄m(xù)句法元素的提取算法或圖像的重建中需要用到它們的值熏兄。在 H.264 中,句法元素的名稱是由小寫字母和一系列的下劃線組成树姨,而變量名稱是大小寫字母組成摩桶,中間沒有下劃線。
二帽揪、語法
句法是句法元素的組織結(jié)構(gòu)硝清,而對(duì)一個(gè)結(jié)構(gòu)的描述必然少不了對(duì)應(yīng)的語法,語法提供判斷转晰、循環(huán)等必要的描述方法芦拿。H.264 采用一種類 C 語法砾肺。
判斷
if ( 條件 )
{
…
}
else
{
…
}
與 C 語言類似,H.264 有三種循環(huán)體:
a)
do
{
…
}while ( 條件 )
b)
while ( 條件 )
{
…
}
c)
for ( 初始 ; 條件 ; 求值 )
{
…
}
三防嗡、描述子
描述子是指從比特流提取句法元素的方法,即句法元素的解碼算法侠坎,每個(gè)句法元素都有相對(duì)應(yīng)的描述子蚁趁。由于 H.264 編碼的最后一步是熵編碼,所以這里的描述子大多是熵編碼的解碼算法实胸。H.264定義了如下幾種描述子:
- a) ae(v) 基于上下文自適應(yīng)的二進(jìn)制算術(shù)熵編碼
- b) b(8) 讀進(jìn)連續(xù)的 8 個(gè)比特
- c) ce(v) 基于上下文自適應(yīng)的可變長熵編碼
- d) f(n) 讀進(jìn)連續(xù)的 n 個(gè)比特
- e) i(n)/i(v) 讀進(jìn)連續(xù)的若干比特他嫡,并把它們解釋為有符號(hào)整數(shù)
- f) me(v) 映射指數(shù) Golomb 熵編碼
- g) se(v) 有符號(hào)指數(shù) Golomb 熵編碼
- h) te(v) 截?cái)嘀笖?shù) Golomb 熵編碼
- i) u(n)/u(v) 讀進(jìn)連續(xù)的若干比特,并將它們解釋為無符號(hào)整數(shù)
- j) ue(v) 無符號(hào)指數(shù) Golomb 熵編碼
我們看到庐完,描述子都在括號(hào)中帶有一個(gè)參數(shù)钢属,這個(gè)參數(shù)表示需要提取的比特?cái)?shù)。當(dāng)參數(shù)是 n 時(shí)门躯,表明調(diào)用這個(gè)描述子的時(shí)候會(huì)指明 n 的值淆党,也即該句法元素是定長編碼的。當(dāng)參數(shù)是 v 時(shí)讶凉,對(duì)應(yīng)的句法元素是變長編碼染乌,這時(shí)有兩種情況:i(v) 和 u(v) 兩個(gè)描述子的 v 由以前的句法元素指定,也就是說在前面會(huì)有句法元素指定當(dāng)前句法元素的比特長度懂讯;除了這兩個(gè)描述子外荷憋,其它描述子都是熵編碼,它們的解碼算法本身能夠確定當(dāng)前句法元素的比特長度褐望。
四勒庄、句法表
句法表定義了 H.264 的句法,指明在碼流中依次出現(xiàn)的句法元素及它們出現(xiàn)的條件瘫里、提取描述子等实蔽。就象前文所提,句法表是分層嵌套的减宣。
在句法表中的 C 字段表示該句法元素的分類盐须,這是為片分區(qū)服務(wù)的,句法元素分類的具體含義在表 7.20 詳細(xì)介紹漆腌。Descriptor 指定對(duì)應(yīng)句法元素的描述子贼邓。
下面的表太長,可以參見原書……
五闷尿、VCL NAL
視頻編碼中采用的如預(yù)測編碼塑径、變化量化、熵編碼等編碼工具主要工作在slice層或以下填具,這一層通常被稱為“視頻編碼層”(Video Coding Layer, VCL)统舀。
相對(duì)的匆骗,在slice以上所進(jìn)行的數(shù)據(jù)和算法通常稱之為“網(wǎng)絡(luò)抽象層”(Network Abstraction Layer, NAL)。設(shè)計(jì)定義NAL層的主要意義在于提升H.264格式的視頻對(duì)網(wǎng)絡(luò)傳輸和數(shù)據(jù)存儲(chǔ)的親和性誉简。
六碉就、NAL 層語義
1.起始碼
在網(wǎng)絡(luò)傳輸?shù)沫h(huán)境下,編碼器將每個(gè) NAL 各自獨(dú)立闷串、完整地放入一個(gè)分組瓮钥,由于分組都有頭部,解碼器可以很方便地檢測出 NAL 的分界烹吵,依次取出 NAL 進(jìn)行解碼碉熄。為了節(jié)省碼流,H.264 沒有另外在 NAL 的頭部設(shè)立表示起始的句法元素肋拔,我們從表 7.1 可以看到這點(diǎn)锈津。
但是如果編碼數(shù)據(jù)是儲(chǔ)存在介質(zhì)(如 DVD 光盤)上,由于 NAL 是依次緊密排列凉蜂,解碼器將無法在數(shù)據(jù)流中分辨每個(gè) NAL的起始和終止琼梆,所以必須要有另外的機(jī)制來解決這個(gè)問題。針對(duì)這個(gè)問題跃惫,H.264 草案的附錄 B 中指明了一種簡單又高效的方案叮叹。當(dāng)數(shù)據(jù)流是存儲(chǔ)在介質(zhì)上時(shí),在每個(gè) NAL 前添加起始碼:0x000001
在某些類型的介質(zhì)上爆存,為了尋址的方便蛉顽,要求數(shù)據(jù)流在長度上對(duì)齊,或必須是某個(gè)常數(shù)的倍數(shù)先较⌒考慮到這種情況,H.264 建議在起始碼前添加若干字節(jié)的 0 來填充闲勺,直到該 NAL 的長度符合要求曾棕。
在這樣的機(jī)制下,解碼器在碼流中檢測起始碼菜循,作為一個(gè) NAL 的起始標(biāo)識(shí)翘地,當(dāng)檢測到下一個(gè)起始碼時(shí)當(dāng)前 NAL 結(jié)束。H.264 規(guī)定當(dāng)檢測到 0x000000
時(shí)也可以表征當(dāng)前 NAL 的結(jié)束癌幕,這是因?yàn)檫B著的三個(gè)字節(jié)的 0 中的任何一個(gè)字節(jié)的 0 要么屬于起始碼要么是起始碼前面添加的 0衙耕。
添加起始碼是一個(gè)解決問題的很好的方法,但上面關(guān)于起始碼的介紹還不完整勺远,因?yàn)楹雎粤艘粋€(gè)重要的問題:如果在 NAL 內(nèi)部出現(xiàn)了 0x000001 或是 0x000000 的序列怎么辦橙喘?毫無疑問這種情況是致命的,解碼器將把這些本來不是起始碼的字節(jié)序列當(dāng)作起始碼胶逢,而錯(cuò)誤地認(rèn)為這里往后是一個(gè)新的 NAL 的開始厅瞎,進(jìn)而造成解碼數(shù)據(jù)的錯(cuò)位饰潜!而我們做的大量實(shí)驗(yàn)證明,NAL 內(nèi)部經(jīng)常會(huì)出現(xiàn)這樣的字節(jié)序列和簸。
于是 H.264 提出了另外一種機(jī)制彭雾,叫做“防止競爭”,在編碼器編碼完一個(gè) NAL 時(shí)锁保,應(yīng)該檢測是否出現(xiàn)圖 7.6 左側(cè) 中的四個(gè)字節(jié)序列冠跷,以防止它們和起始碼競爭。如果檢測到這些序列存在身诺,編碼器將在最后一個(gè)字節(jié)前插入一個(gè)新的字節(jié):0x03,從而使它們變成圖 7.6 右測的樣子抄囚。當(dāng)解碼器在 NAL 內(nèi)部檢測到有 0x000003 的序列時(shí)霉赡,將把 0x03 拋棄,恢復(fù)原始數(shù)據(jù)幔托。
圖 7.6 中的前兩個(gè)序列我們前文中已經(jīng)提到穴亏,第三個(gè) 0x000002 是作保留用,而第四個(gè) 0x000003是為了保證解碼器能正常工作重挑,因?yàn)槲覀儎偛盘岬缴せ獯a器恢復(fù)原始數(shù)據(jù)的方法是檢測到 0x000003就拋棄其中的 0x03,這樣當(dāng)出現(xiàn)原始數(shù)據(jù)為 0x000003 時(shí)會(huì)破壞數(shù)據(jù)谬哀,所以必須也應(yīng)該給這個(gè)序列插入 0x03刺覆。
我們可以從句法表 7.1 中看到解碼器在 NAL 層的處理步驟,其中變量 NumBytesInNALunit 是解碼器計(jì)算出來的史煎,解碼器在逐個(gè)字節(jié)地讀一個(gè) NAL 時(shí)并不同時(shí)對(duì)它解碼谦屑,而是要通過起始碼機(jī)制將整個(gè) NAL 讀進(jìn)、計(jì)算出長度后再開始解碼篇梭。
2.forbidden_zero_bi
等于 0
3.nal_ref_idc
指示當(dāng)前 NAL 的優(yōu)先級(jí)氢橙。取值范圍為 0-3, ,值越高,表示當(dāng)前 NAL 越重要,需要優(yōu)先受到保護(hù)。H.264 規(guī)定如果當(dāng)前 NAL 是屬于參考幀的片恬偷,或是序列參數(shù)集悍手,或是圖像參數(shù)集這些重要的數(shù)據(jù)單位時(shí),本句法元素必須大于 0袍患。但在大于 0 時(shí)具體該取何值坦康,卻沒有進(jìn)一步規(guī)定,通信雙方可以靈活地制定策略。
4.nal_unit_type 指明當(dāng)前 NAL unit 的類型协怒,具體類型的定義如表 7.20:
nal_unit_type | NAL 類型 | C |
---|---|---|
0 | 未使用 | |
1 | 不分區(qū)涝焙、非 IDR 圖像的片 | 2, 3, 4 |
2 | 片分區(qū) A | 2 |
3 | 片分區(qū) B | 3 |
4 | 片分區(qū) C | 4 |
5 | IDR 圖像中的片 | 2, 3 |
6 | 補(bǔ)充增強(qiáng)信息單元(SEI) | 5 |
7 | 序列參數(shù)集 | 0 |
8 | 圖像參數(shù)集 | 1 |
9 | 分界符 | 6 |
10 | 序列結(jié)束 | 7 |
11 | 碼流結(jié)束 | 8 |
12 | 填充 | 9 |
13..23 | 保留 | |
24..31 | 未使用 |
nal_unit_type=5 時(shí),表示當(dāng)前 NAL 是 IDR 圖像的一個(gè)片孕暇,在這種情況下仑撞,IDR 圖像中的每個(gè)片的nal_unit_type 都應(yīng)該等于 5赤兴。注意 IDR 圖像不能使用片分區(qū)。
5.rbsp_byte[i]
RBSP 的第 i 個(gè)字節(jié)隧哮。RBSP 指原始字節(jié)載荷桶良,它是 NAL 單元的數(shù)據(jù)部分的封裝格式,封裝的數(shù)據(jù)來自 SODB(原始數(shù)據(jù)比特流)沮翔。SODB 是編碼后的原始數(shù)據(jù)陨帆,SODB 經(jīng)封裝為 RBSP 后放入 NAL 的數(shù)據(jù)部分。下面介紹一個(gè) RBSP 的生成順序采蚀。
從 SODB 到 RBSP 的生成過程:
- 如果 SODB 內(nèi)容是空的疲牵,生成的 RBSP 也是空的
- 否則,RBSP 由如下的方式生成:
- 1) RBSP 的第一個(gè)字節(jié)直接取自 SODB 的第 1 到 8 個(gè)比特榆鼠,(RBSP 字節(jié)內(nèi)的比特按照從左到右對(duì)應(yīng)為從高到低的順序排列纲爸,most significant),以此類推,RBSP 其余的每個(gè)字節(jié)都直接取自 SODB 的相應(yīng)比特妆够。RBSP 的最后一個(gè)字節(jié)包含 SODB 的最后幾個(gè)比特识啦,及如下的rbsp_trailing_bits()
- 2) rbsp_trailing_bits()的第一個(gè)比特是 1,接下來填充 0,直到字節(jié)對(duì)齊神妹。(填充 0 的目的也是為了字節(jié)對(duì)齊)
- 3) 最后添加若干個(gè) cabac_zero_word(其值等于 0x0000)
6.emulation_prevention_three_byte
NAL 內(nèi)部為防止與起始碼競爭而引入的填充字節(jié) ,值為 0x03颓哮。