深入淺出了解視訊編碼H264結構
編碼流程:
那麼 H.264 其編解碼流程是怎麼樣的呢?其實可以主要分為 5 部分: 幀間和幀内預測(Estimation)、變換(Transform)和反變換、量化(Quantization)和反量化、環路濾波(Loop Filter)、熵編碼(Entropy Coding)。
看起來很高深的樣子,實際上也是很高深的樣子,因為這裡面包含着許許多多的算法和專業知識,這裡我們就不做過多的講解,有興趣的同學可以上網翻翻,夠你看到睡覺的了。H.264詳細文檔
原理簡介
H.264 原始碼流(又稱為裸流),是有一個接一個的 NALU 組成的,而它的功能分為兩層:視訊編碼層(VCL, Video Coding Layer)和網絡提取層(NAL, Network Abstraction Layer)。
VCL 資料即編碼處理的輸出,它表示被壓縮編碼後的視訊資料 序列。在 VCL 資料傳輸或存儲之前,這些編碼的 VCL 資料,先被映射或封裝進 NAL 單元(以下簡稱 NALU,Nal Unit) 中。每個 NALU 包括一個原始位元組序列負荷(RBSP, Raw Byte Sequence Payload)、一組 對應于視訊編碼的 NALU 頭部資訊。RBSP 的基本結構是:在原始編碼資料的後面填加了結尾 比特。一個 bit“1”若幹比特“0”,以便位元組對齊。
NAL 單元排列
上圖中的 NALU頭 + RBSP 就相當與一個 NALU (Nal Unit), 每個單元都按獨立的 NALU 傳送。 其實說白了,H.264 中的結構全部都是以 NALU 為主的,了解了 NALU,就了解 H.264 的結構了。
一幀圖檔跟 NALU 的關聯 :
究竟 NALU 是怎麼由一幀圖檔變化而來的呀,H.264究竟為什麼這麼神奇?
一幀圖檔經過 H.264 編碼器之後,就被編碼為一個或多個片(slice),而裝載着這些片(slice)的載體,就是 NALU 了,我們可以來看看 NALU 跟片的關系(slice)。
圖檔編碼後
NALU 結構
小夥伴們要明白,片(slice)的概念不同與幀(frame),幀(frame)是用作描述一張圖檔的,一幀(frame)對應一張圖檔,而片(slice),是 H.264 中提出的新概念,是通過編碼圖檔後切分通過高效的方式整合出來的概念,一張圖檔至少有一個或多個片(slice)。
上圖中可以看出,片(slice)都是又 NALU 裝載并進行網絡傳輸的,但是這并不代表 NALU 内就一定是切片,這是充分不必要條件,因為 NALU 還有可能裝載着其他用作描述視訊的資訊。
什麼是切片(slice)?
片的主要作用是用作宏塊(Macroblock)的載體(ps:下面會介紹到宏塊的概念)。片之是以被創造出來,主要目的是為限制誤碼的擴散和傳輸。
如何限制誤碼的擴散和傳輸?
每個片(slice)都應該是互相獨立被傳輸的,某片的預測(片(slice)内預測和片(slice)間預測)不能以其它片中的宏塊(Macroblock)為參考圖像。
那麼片(slice)的具體結構,我們用一張圖來直覺說明吧:
我們可以了解為一 張/幀 圖檔可以包含一個或多個分片(Slice),而每一個分片(Slice)包含整數個宏塊(Macroblock),即每片(slice)至少一個 宏塊(Macroblock),最多時每片包 整個圖像的宏塊。
上圖結構中,我們不難看出,每個分片也包含着頭和資料兩部分:
1、分片頭中包含着分片類型、分片中的宏塊類型、分片幀的數量、分片屬于那個圖像以及對應的幀的設定和參數等資訊。
2、分片資料中則是宏塊,這裡就是我們要找的存儲像素資料的地方。
什麼是宏塊?
宏塊是視訊資訊的主要承載者,因為它包含着每一個像素的亮度和色度資訊。視訊解碼最主要的工作則是提供高效的方式從碼流中獲得宏塊中的像素陣列。
組成部分:一個宏塊由一個16×16亮度像素和附加的一個8×8 Cb和一個 8×8 Cr 彩色像素塊組成。每個圖象中,若幹宏塊被排列成片的形式。
我們先來看看宏塊的結構圖:
從上圖中,可以看到,宏塊中包含了宏塊類型、預測類型、Coded Block Pattern、Quantization Parameter、像素的亮度和色度資料集等等資訊。
切片(slice)類型跟宏塊類型的關系
對于切片(slice)來講,分為以下幾種類型:
0 P-slice. Consists of P-macroblocks (each macro block is predicted using one reference frame) and / or I-macroblocks.
1 B-slice. Consists of B-macroblocks (each macroblock is predicted using one or two reference frames) and / or I-macroblocks.
2 I-slice. Contains only I-macroblocks. Each macroblock is predicted from previously coded blocks of the same slice.
3 SP-slice. Consists of P and / or I-macroblocks and lets you switch between encoded streams.
4 SI-slice. It consists of a special type of SI-macroblocks and lets you switch between encoded streams.
I片:隻包 I宏塊,I 宏塊利用從目前片中已解碼的像素作為參考進行幀内預測(不能取其它片中的已解碼像素作為參考進行幀内預測)。
P片:可包 P和I宏塊,P 宏塊利用前面已編碼圖象作為參考圖象進行幀内預測,一個幀内編碼的宏塊可進一步作宏塊的分割:即 16×16、16×8、8×16 或 8×8 亮度像素塊(以及附帶的彩色像素);如果選了 8×8 的子宏塊,則可再分成各種子宏塊的分割,其尺寸為 8×8、8×4、4×8 或 4×4 亮度像素塊(以及附帶的彩色像素)。
B片:可包 B和I宏塊,B 宏塊則利用雙向的參考圖象(目前和 來的已編碼圖象幀)進行幀内預測。
SP片(切換P):用于不同編碼流之間的切換,包含 P 和/或 I 宏塊
SI片:擴充檔次中必須具有的切換,它包 了一種特殊類型的編碼宏塊,叫做 SI 宏塊,SI 也是擴充檔次中的必備功能。
整體結構
通過剖析了這麼多個小零件,是時候個大家一個世界地圖了,
那麼我們的 NALU 整體結構可以呼之欲出了,以下就引用 H.264 文檔當中的一幅圖了
其實 H.264 的碼流結構并沒有大家想的那麼複雜,編碼後視訊的每一組圖像(GOP,圖像組)都給予了傳輸中的序列(PPS)和本身這個幀的圖像參數(SPS),是以,我們的整體結構,應該如此:
GOP (圖像組)主要用作形容一個 i 幀 到下一個 i 幀之間的間隔了多少個幀,增大圖檔組能有效的減少編碼後的視訊體積,但是也會降低視訊品質,至于怎麼取舍,得看需求了。
主題外:(未完待續)
那麼,NALU 頭部中的類型确定着什麼資訊呢?
我們首先來看看 NALU 中究竟有哪幾種類型,我們來看看 H.264 中源碼對 nal_unit_type_e 中的定義:
enum nal_unit_type_e
{
NAL_UNKNOWN = 0, // 未使用
NAL_SLICE = 1, // 不分區、非 IDR 圖像的片(片的頭資訊和資料)
NAL_SLICE_DPA = 2, // 片分區 A
NAL_SLICE_DPB = 3, // 片分區 B
NAL_SLICE_DPC = 4, // 片分區 C
NAL_SLICE_IDR = 5, / ref_idc != 0 / // IDR 圖像中的片
NAL_SEI = 6, / ref_idc == 0 / // 補充增強資訊單元
-
參數集是 H.264 标準的一個新概念,是一種通過改進視訊碼流結構增強錯誤恢複能力的方法。
NAL_SPS = 7, // 序列參數集 (包括一個圖像序列的所有資訊,即兩個 IDR 圖像間的所有圖像資訊,如圖像尺寸、視訊格式等)
NAL_PPS = 8, // 圖像參數集 (包括一個圖像的所有分片的所有相關資訊, 包括圖像類型、序列号等,解碼時某些序列号的丢失可用來檢驗資訊包的丢失與否)
-
NAL_AUD = 9, // 分界符
NAL_FILLER = 12, // 填充(啞中繼資料,用于填充位元組)
/ ref_idc == 0 for 6,9, 10 (表明下一圖像為 IDR 圖像),11(表明該流中已沒有圖像),12 /
};
ps: 以上括号()中的為類型描述
上面NALU類型當中,分片/切片(slice)的概念我們都已經很清楚了,但是用 NALU 作載體的還有 SEI、SPS、PPS 等等。
今天我們不一一聚述這些類型對整個流程的作用了,我們挑出兩個符合我們今天主題的類型來講,PPS 和 SPS。
那麼今天我們講的 H.264 的碼流結構相信大家都有個大概輪廓的了解了,總結的一句話就是:
H.264 中,句法元素共被組織成 序列、圖像、片、宏塊、子宏塊五個層次。