H.264 的句法和語義
                                    在編碼器輸出的碼流中,數(shù)據(jù)的基本單位是句法元素,每個句法元素由若干比特組成,它表示某個特定的物理意義,例如:宏塊類型、量化參數(shù)等。句法表征句法元素的組織結(jié)構(gòu),語義闡述句法元素的具體含義。所有的視頻編碼標(biāo)準(zhǔn)都是通過定義句法和語義來規(guī)范編解碼器的工作流程。
                                    
                                    句法元素的分層結(jié)構(gòu)
                                    編碼器輸出的比特碼流中,每個比特都隸屬某個句法元素,也就是說,碼流是由一個個句法元素依次銜接組成的,碼流中除了句法元素并不存在專門用于控制或同步的內(nèi)容。在H.264 定義的碼流中,句法元素被組織成有層次的結(jié)構(gòu),分別描述各個層次的信息。
                                    
                                    句法元素的分層結(jié)構(gòu)有助于更有效地節(jié)省碼流。例如,在一個圖像中,經(jīng)常會在各個片之間有
                                    相同的數(shù)據(jù),如果每個片都同時攜帶這些數(shù)據(jù),勢必會造成碼流的浪費(fèi)。更為有效的做法是將該圖
                                    像的公共信息抽取出來,形成圖像一級的句法元素,而在片級只攜帶該片自身獨(dú)有的句法元素。在
                                    H.264 中,句法元素共被組織成 序列、圖像、片、宏塊、子宏塊五個層次。
                                    
                                    H.264 的分層結(jié)構(gòu)是經(jīng)過精心設(shè)計的,與以往的視頻編碼標(biāo)準(zhǔn)相比有很大的改進(jìn),這些改進(jìn)主
                                    要針對傳輸中的錯誤掩藏,在有誤碼發(fā)生時可以提高圖像重建的性能。在以往的標(biāo)準(zhǔn)中,分層的組
                                    織結(jié)構(gòu)如圖7.2,它們?nèi)缤琓CP/IP 協(xié)議的結(jié)構(gòu),每一層都有頭部,然后在每層的數(shù)據(jù)部分包含該層
                                    的數(shù)據(jù)。
                                    
                                    在這樣的結(jié)構(gòu)中,每一層的頭部和它的數(shù)據(jù)部分形成管理與被管理的強(qiáng)依賴關(guān)系,頭部的句法
                                    元素是該層數(shù)據(jù)的核心,而一旦頭部丟失,數(shù)據(jù)部分的信息幾乎不可能再被正確解碼出來。尤其在
                                    序列層及圖像層,由于網(wǎng)絡(luò)中MTU(最大傳輸單元)大小的限制,不可能將整個層的句法元素全部
                                    放入同一個分組中,這個時候如果頭部所在的分組丟失,該層其他分組即使能被正確接收也無法解
                                    碼,造成資源浪費(fèi)。(應(yīng)該可以這樣理解,也就是說,一個圖像層作為一個單元被交付給IP層,但是被分割成了幾個分組,如果圖像層的頭部信息的分組丟失,那么這個圖像層的所有片都無法被解碼)
                                     
                                    
                                     
                                     
                                     
                                    
                                    
                                    H.264 中,分層結(jié)構(gòu)最大的不同是取消了序列層和圖像層,并將原本屬于序列和圖像頭部的
                                    大部分句法元素游離出來形成序列和圖像兩級參數(shù)集,其余的部分則放入片層。參數(shù)集是一個獨(dú)立
                                    的數(shù)據(jù)單位,不依賴于參數(shù)集外的其他句法元素。圖7.3 描述了參數(shù)集與參數(shù)集外句法元素的關(guān)系,
                                    在圖中我們可以看到,參數(shù)集只是在片層句法元素需要的時候被引用,而且,一個參數(shù)集并不對應(yīng)
                                    某個特定的圖像或序列,同一個序列參數(shù)集可以被多個序列中的圖像參數(shù)集引用,同理,同一個圖
                                    像參數(shù)集也可以被多個圖像引用。只在編碼器認(rèn)為需要更新參數(shù)集的內(nèi)容時,才會發(fā)送出新的參數(shù)
                                    集。在這種機(jī)制下,由于參數(shù)集是獨(dú)立的,可以被多次重發(fā)或者采用特殊技術(shù)加以保護(hù)。
                                    
                                    
                                    
                                    
                                    在圖7.3 的描述中,參數(shù)集與參數(shù)集外部的句法元素處于不同信道中,這是H.264 的一個建議,
                                    我們可以使用更安全但成本更昂貴的通道來傳輸參數(shù)集,而使用成本低但不夠可靠的信道傳輸其他
                                    句法元素,只需要保證片層中的某個句法元素需要引用某個參數(shù)集時,那個參數(shù)集已經(jīng)到達(dá)解碼器,
                                    也就是參數(shù)集在時間上必須先被傳送。當(dāng)然,在條件不允許的情況下,我們也可以采用妥協(xié)的辦法:
                                    在同一個物理信道中傳輸所有的句法元素,但專門為參數(shù)集采用安全可靠的通信協(xié)議,如TCP。當(dāng)
                                    然,H.264 也允許我們?yōu)榘▍?shù)集在內(nèi)的所有句法元素指定同樣的通信協(xié)議,但這時所有參數(shù)集
                                    必須被多次重發(fā),以保證解碼器最終至少能接收到一個。在參數(shù)集和片使用同個物理信道的情況下,
                                    圖7.3 中的信道1 和信道2 應(yīng)該被理解為邏輯上的信道,因?yàn)閺倪壿嬌峡?,參?shù)集與其它句法元素
                                    還是處于各自彼此獨(dú)立的信道中。
                                    
                                    H.264 在片層增加了新的句法元素指明所引用的參數(shù)集的編號,同時因?yàn)槿∠藞D像層,片成
                                    為了信道2 中最上層的獨(dú)立的數(shù)據(jù)單位,每個片必須自己攜帶關(guān)于所屬圖像的編號、大小等基本信
                                    息,這些信息在同一圖像的每個片中都必須是一致的。在編碼時,H.264 的規(guī)范要求將參數(shù)集、片
                                    這些獨(dú)立的數(shù)據(jù)單位盡可能各自完整地放入一個分組中被傳送。
                                    
                                    從表面上看來,H.264 關(guān)于參數(shù)集和片層的結(jié)構(gòu)增加了編碼后數(shù)據(jù)的冗余度(比如參數(shù)集必須
                                    多次重發(fā),又如每個片都必須攜帶一部分相同的關(guān)于整個圖像的信息,而這些數(shù)據(jù)完全是重復(fù)的),
                                    降低了編碼效率,但這些技術(shù)的采用使得通信的魯棒性大大增強(qiáng),當(dāng)數(shù)據(jù)傳輸中出現(xiàn)丟包,能夠?qū)?/span>
                                    使錯誤限制在最小范圍,防止錯誤的擴(kuò)散,解碼后對錯誤的掩藏和恢復(fù)也能起到很好的作用。 一個
                                    片的丟失將不會影響其它片的解碼,還可以通過該片前后的片來恢復(fù)該片的數(shù)據(jù)。
                                    H.264 片層以下的句法元素的結(jié)構(gòu)大體上和以往標(biāo)準(zhǔn)類似,但在相當(dāng)多的細(xì)節(jié)上有所改進(jìn),所
                                    有的改進(jìn)的目的不外乎兩個:在錯誤發(fā)生時防止錯誤擴(kuò)散、減少冗余信息提高編碼效率。這兩者往
                                    往是矛盾的,H.264 在這兩者上的取舍顯得頗具匠心。
                                    
                                    圖7.3 所示的碼流的結(jié)構(gòu)是一種簡化的模型,這個模型已經(jīng)能夠正確工作,但還不夠完善,不
                                    適合復(fù)雜的場合。在復(fù)雜的通信環(huán)境中,除了片和參數(shù)集外還需要其他的數(shù)據(jù)單位來提供額外的信
                                    息。圖7.4 描述了在復(fù)雜通信中的碼流中可能出現(xiàn)的數(shù)據(jù)單位。如前文所述,參數(shù)集可以被抽取出
                                    來使用其它信道。
                                    
                                     
                                     
                                     
                                     
                                    
                                    
                                    (更正:看來一個片不適合作為一個分組,可能太小了。應(yīng)該是以這里的數(shù)據(jù)單元為一個分組的?)
                                    
                                    在圖7.4 中我們看到,一個序列的第一個圖像叫做IDR 圖像(立即刷新圖像),IDR 圖像都是I
                                    圖像。H.264 引入IDR 圖像是為了解碼的重同步,當(dāng)解碼器解碼到IDR 圖像時,立即將參考幀隊(duì)列
                                    清空,將已解碼的數(shù)據(jù)全部輸出或拋棄,重新查找參數(shù)集,開始一個新的序列。這樣,如果在前一
                                    個序列的傳輸中發(fā)生重大錯誤,如嚴(yán)重的丟包,或其他原因引起數(shù)據(jù)錯位,在這里可以獲得重新同
                                    步。IDR 圖像之后的圖像永遠(yuǎn)不會引用IDR 圖像之前的圖像的數(shù)據(jù)來解碼。
                                    要注意IDR 圖像和I 圖像的區(qū)別,IDR 圖像一定是I 圖像,但I(xiàn) 圖像不一定是IDR 圖像。一個
                                    序列中可以有很多的I 圖像,I 圖像之后的圖像可以引用I 圖像之間的圖像做運(yùn)動參考。
                                    在圖7.4 中,除了參數(shù)集與片外還有其它的數(shù)據(jù)單位,這些數(shù)據(jù)單位可以提供額外的數(shù)據(jù)或同
                                    步信息,這些數(shù)據(jù)單位也是一系列句法元素的集合。它們在解碼過程中不是必需的,但卻可以適當(dāng)
                                    提高同步性能或定義圖像的復(fù)雜特征。
                                    插入:關(guān)于I,P片的概念
                                    
                                     
                                    
                                        
                                            
                                                | 
 宏塊、片 一個編碼圖像通常劃分成若干宏塊組成,一個宏塊由一個16×16 亮度像素和附加的一個8×8 Cb和一個8×8 Cr 彩色像素塊組成。每個圖象中,若干宏塊被排列成片的形式。 I 片只包含I 宏塊,P 片可包含P 和I 宏塊,而B 片可包含B 和I 宏塊。 I 宏塊利用從當(dāng)前片中已解碼的像素作為參考進(jìn)行幀內(nèi)預(yù)測(不能取其它片中的已解碼像素作為參考進(jìn)行幀內(nèi)預(yù)測)。 P 宏塊利用前面已編碼圖象作為參考圖象進(jìn)行幀內(nèi)預(yù)測,一個幀內(nèi)編碼的宏塊可進(jìn)一步作宏塊的分割:即16×16、16×8、8×16 或8×8 亮度像素塊(以及附帶的彩色像素);如果選了8×8 的子宏塊,則可再分成各種子宏塊的分割,其尺寸為8×8、8×4、4×8 或4×4 亮度像素塊(以及附帶的彩色像素)。 B 宏塊則利用雙向的參考圖象(當(dāng)前和未來的已編碼圖象幀)進(jìn)行幀內(nèi)預(yù)測。 檔次和級 H.264 規(guī)定了三種檔次,每個檔次支持一組特定的編碼功能,并支持一類特定的應(yīng)用。 1)基本檔次:利用I 片和P 片支持幀內(nèi)和幀間編碼,支持利用基于上下文的自適應(yīng)的變長編碼 進(jìn)行的熵編碼(CAVLC)。主要用于可視電話、會議電視、無線通信等實(shí)時視頻通信; 2)主要檔次:支持隔行視頻,采用B 片的幀間編碼和采用加權(quán)預(yù)測的幀內(nèi)編碼;支持利用基于 上下文的自適應(yīng)的算術(shù)編碼(CABAC)。主要用于數(shù)字廣播電視與數(shù)字視頻存儲; 3)擴(kuò)展檔次:支持碼流之間有效的切換(SP 和SI 片)、改進(jìn)誤碼性能(數(shù)據(jù)分割),但不支持 隔行視頻和CABAC。 | 
                                        
                                    
                                    
                                    以下是解碼時的知識,現(xiàn)在先不用考慮。
                                    
                                    句法的表示方法
                                    句法元素與變量
                                    編碼器將數(shù)據(jù)編碼為句法元素然后依次發(fā)送。在解碼器端,通常要將句法元素作求值計算,得
                                    出一些中間數(shù)據(jù),這些中間數(shù)據(jù)就是H.264 定義的變量。如圖7.5:
                                    
                                    
                                    圖7.5 從句法元素解出變量
                                    圖中,pic_width_in_mbs_minus1 是解碼器直接從碼流中提取的句法元素,這個句法元素表征圖
                                    像的寬度,以宏塊為單位。我們看到,為了提高編碼效率,H.264 將圖像實(shí)際的寬度減去1 后再傳
                                    送。
                                    PicWidthInMbs = pic_width_in_mbs_minus1 + 1
                                    PicWidthInSamplesL = PicWidthInMbs * 16
                                    PicWidthInSamplesC = PicWidthInMbs * 8
                                    以上變量PicWidthInMbs 表示圖像以宏塊為單位的寬, 變量PicWidthInSamplesL 、
                                    PicWidthInSamplesC 分別表示圖像的亮度、色度分量以像素為單位的寬。H.264 定義這些變量是因
                                    為在后續(xù)句法元素的提取算法或圖像的重建中需要用到它們的值。在H.264 中,句法元素的名稱是
                                    由小寫字母和一系列的下劃線組成,而變量名稱是大小寫字母組成,中間沒有下劃線。(根據(jù)句法元素可以求解句法變量)
                                    句法
                                    ...
                                    語義
                                    
                                     NAL層語義
                                    在網(wǎng)絡(luò)傳輸?shù)沫h(huán)境下,編碼器將每個NAL 各自獨(dú)立、完整地放入一個分組,由于分組都有頭部,
                                    解碼器可以很方便地檢測出NAL 的分界,依次取出NAL 進(jìn)行解碼。為了節(jié)省碼流,H.264 沒有另
                                    外在NAL 的頭部設(shè)立表示起始的句法元素,我們從表7.1 可以看到這點(diǎn)。但是如果編碼數(shù)據(jù)是儲存
                                    在介質(zhì)(如DVD 光盤)上,由于NAL 是依次緊密排列,解碼器將無法在數(shù)據(jù)流中分辨每個NAL
                                    的起始和終止,所以必須要有另外的機(jī)制來解決這個問題。
                                    針對這個問題,H.264 草案的附錄B 中指明了一種簡單又高效的方案。當(dāng)數(shù)據(jù)流是存儲在介質(zhì)
                                    上時,在每個NAL 前添加起始碼:
                                    0x000001
                                    在某些類型的介質(zhì)上,為了尋址的方便,要求數(shù)據(jù)流在長度上對齊,或必須是某個常數(shù)的倍數(shù)???/span>
                                    慮到這種情況,H.264 建議在起始碼前添加若干字節(jié)的0 來填充,直到該NAL 的長度符合要求。
                                    在這樣的機(jī)制下,解碼器在碼流中檢測起始碼,作為一個NAL 的起始標(biāo)識,當(dāng)檢測到下一個
                                    起始碼時當(dāng)前NAL 結(jié)束。H.264 規(guī)定當(dāng)檢測到0x000000 時也可以表征當(dāng)前NAL 的結(jié)束,這是因?yàn)?/span>
                                    連著的三個字節(jié)的0 中的任何一個字節(jié)的0 要么屬于起始碼要么是起始碼前面添加的0。
                                    添加起始碼是一個解決問題的很好的方法,但上面關(guān)于起始碼的介紹還不完整,因?yàn)楹雎粤艘?/span>
                                    個重要的問題:如果在NAL 內(nèi)部出現(xiàn)了0x000001 或是0x000000 的序列怎么辦?毫無疑問這種情
                                    況是致命的,解碼器將把這些本來不是起始碼的字節(jié)序列當(dāng)作起始碼,而錯誤地認(rèn)為這里往后是一
                                    個新的NAL 的開始,進(jìn)而造成解碼數(shù)據(jù)的錯位!而我們做的大量實(shí)驗(yàn)證明,NAL 內(nèi)部經(jīng)常會出現(xiàn)
                                    這樣的字節(jié)序列。
                                     
                                    
                                    于是H.264 提出了另外一種機(jī)制,叫做“防止競爭”,在編碼器編碼完一個NAL 時,應(yīng)該檢測是
                                    否出現(xiàn)圖7.6 左側(cè) 中的四個字節(jié)序列,以防止它們和起始碼競爭。如果檢測到這些序列存在,編
                                    碼器將在最后一個字節(jié)前插入一個新的字節(jié):0x03,從而使它們變成圖7.6 右測的樣子。當(dāng)解碼器
                                    在NAL 內(nèi)部檢測到有0x000003 的序列時,將把0x03 拋棄,恢復(fù)原始數(shù)據(jù)。
                                    圖7.6 NAL 內(nèi)部為防止與起始碼競爭插入 0x03
                                    圖7.6 中的前兩個序列我們前文中已經(jīng)提到,第三個0x000002 是作保留用,而第四個0x000003
                                    是為了保證解碼器能正常工作,因?yàn)槲覀儎偛盘岬?,解碼器恢復(fù)原始數(shù)據(jù)的方法是檢測到0x000003
                                    就拋棄其中的0x03,這樣當(dāng)出現(xiàn)原始數(shù)據(jù)為0x000003 時會破壞數(shù)據(jù),所以必須也應(yīng)該給這個序列
                                    插入0x03。
                                    我們可以從句法表7.1 中看到解碼器在NAL 層的處理步驟,其中變量NumBytesInNALunit 是
                                    解碼器計算出來的,解碼器在逐個字節(jié)地讀一個NAL 時并不同時對它解碼,而是要通過起始碼機(jī)
                                    制將整個NAL 讀進(jìn)、計算出長度后再開始解碼。
                                    接下來的部分就非常重要了,句法元素的語義。想要理解一個編碼器輸出的碼流,就需要熟悉這些。
                                    
                                    forbidden_zero_bit 等于0
                                    nal_ref_idc 指示當(dāng)前NAL 的優(yōu)先級。取值范圍為0-3, ,值越高,表示當(dāng)前NAL 越重要,需要優(yōu)		先受到保護(hù)。H.264 規(guī)定如果當(dāng)前NAL 是屬于參考幀的片,或是序列參數(shù)集,或是圖像	參數(shù)集這些重要的數(shù)據(jù)單位時,本句法元素必須大于0。但在大于0 時具體該取何值,卻沒	有進(jìn)一步規(guī)定,通信雙方可以靈活地制定策略。
                                    
                                    nal_unit_type 指明當(dāng)前NAL unit 的類型,具體類型的定義如表7.20:
                                    表7.20 nal_uint_type 語義
                                    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 時,表示當(dāng)前NAL 是IDR 圖像的一個片,在這種情況下,IDR 圖像中的每個片的nal_unit_type 都應(yīng)該等于5。注意IDR 圖像不能使用片分區(qū)。
                                    
                                    rbsp_byte[i] RBSP 的第i 個字節(jié)。RBSP 指原始字節(jié)載荷,它是NAL 單元的數(shù)據(jù)部分的封裝格式,
                                    封裝的數(shù)據(jù)來自SODB(原始數(shù)據(jù)比特流)。SODB 是編碼后的原始數(shù)據(jù),SODB 經(jīng)封裝為RBSP 后
                                    放入NAL 的數(shù)據(jù)部分。下面介紹一個RBSP 的生成順序。
                                    從SODB 到RBSP 的生成過程:
                                    - 如果SODB 內(nèi)容是空的,生成的RBSP 也是空的
                                    - 否則,RBSP 由如下的方式生成:
                                    1) RBSP 的第一個字節(jié)直接取自SODB 的第1 到8 個比特,(RBSP 字節(jié)內(nèi)的比特按照從左到
                                    右對應(yīng)為從高到低的順序排列,most significant),以此類推,RBSP 其余的每個字節(jié)都直接
                                    取自SODB 的相應(yīng)比特。RBSP 的最后一個字節(jié)包含SODB 的最后幾個比特,及如下的
                                    rbsp_trailing_bits()
                                    2) rbsp_trailing_bits()的第一個比特是1,接下來填充0,直到字節(jié)對齊。(填充0 的目的也是為了
                                    字節(jié)對齊)
                                    3) 最后添加若干個cabac_zero_word(其值等于0x0000)
                                    emulation_prevention_three_byte NAL 內(nèi)部為防止與起始碼競爭而引入的填充字節(jié) ,值為0x03。