注:本篇博文完全是參考月下伊人佳酒淳的。在此向大神表示感謝宠互。
引言:
在國(guó)內(nèi)直播"欣欣向榮"(ps: 其實(shí)大多都虧錢壁袄,為的就是炒概念)的年代寓调,相信很多小伙伴也投入了技術(shù)的浩瀚大洋當(dāng)中(ps: 其實(shí)就是搬磚)铝侵,日復(fù)一日灼伤,音/視頻的神秘面紗開(kāi)始讓更多的小伙伴扯下,而本博主咪鲜,也只是剛窺探門道狐赡,慢慢摸索。好了疟丙,廢話不扯颖侄,我們今天就來(lái)說(shuō)說(shuō)我們經(jīng)常在視頻編碼當(dāng)中用到的 H.264編碼格式 的結(jié)構(gòu),相信 H.264 這個(gè)東西很多小伙伴都不陌生了隆敢,也有著自己的理解发皿,但這東西頗為巨大,里面算法千千萬(wàn)萬(wàn)拂蝎,博主也不會(huì)講太高深的東西,只是讓各位小伙伴慢慢理解理解 H.264的主體機(jī)構(gòu)惶室,如果沒(méi)有興趣的小伙伴請(qǐng)繞道温自,如果有技術(shù)大牛玄货,請(qǐng)指正本博主那愚鈍的腦袋。
首先來(lái)一段大家都熟悉的官方話來(lái)介紹一下 H.264
H.264: H.264/AVC項(xiàng)目的目的是為了創(chuàng)建一個(gè)比以前的視頻壓縮標(biāo)準(zhǔn)悼泌,在更低的比特率的情況下依然能夠提供良好視頻質(zhì)量的標(biāo)準(zhǔn)(如松捉,一半或者更少于MPEG-2,H.263,或者M(jìn)PEG-4 Part2 )。同時(shí)馆里,還要不會(huì)太大的增加設(shè)計(jì)的復(fù)雜性隘世。
優(yōu)勢(shì):
1)網(wǎng)絡(luò)親和性,即可適用于各種傳輸網(wǎng)絡(luò)
2)高的視頻壓縮比鸠踪,當(dāng)初提出的指標(biāo)是比 H.263丙者,MPEG-4,約為它們的 2 倍营密,現(xiàn)在都已基 實(shí)現(xiàn);
那么很明顯械媒,什么時(shí)候需要到壓縮呢?當(dāng)然是文件體積太大的時(shí)候啦评汰,我們想想纷捞,所謂的視頻,就是像小時(shí)候的連環(huán)畫(huà)一樣被去,在一秒內(nèi)翻過(guò) 24 張以上的圖片主儡,就感覺(jué)圖像是連續(xù)的了,這就是視頻的原理惨缆。但是大家有沒(méi)有想過(guò)糜值,一張圖片有多大呢?我們的屏幕分辨率按 1280 * 720 算的話踪央,一秒鐘的視頻大概就 2.64 MB 了臀玄,大家想想,我們大部分的小伙伴為了下載個(gè)小嗨片省吃儉用才開(kāi)了個(gè) 1M 的網(wǎng)線畅蹂,然后連個(gè)直播都看不了是什么感覺(jué)健无。那肯定不能這樣了,所以我們要進(jìn)行壓縮液斜,而 H.264 不僅壓縮比比較高累贤,對(duì)網(wǎng)絡(luò)的兼容性也非常好,所以大多數(shù)人做直播也就選擇了 H.264 作為編碼格式了少漆。
編碼流程:
那么 H.264 其編解碼流程是怎么樣的呢臼膏?其實(shí)可以主要分為 5 部分: 幀間和幀內(nèi)預(yù)測(cè)(Estimation)、變換(Transform)和反變換示损、量化(Quantization)和反量化渗磅、環(huán)路濾波(Loop Filter)、熵編碼(Entropy Coding)。
看起來(lái)很高深的樣子始鱼,實(shí)際上也是很高深的樣子仔掸,因?yàn)檫@里面包含著許許多多的算法和專業(yè)知識(shí),這里我們就不做過(guò)多的講解医清,有興趣的同學(xué)可以上網(wǎng)翻翻起暮,夠你看到睡覺(jué)的了。H.264詳細(xì)文檔
原理簡(jiǎn)介
H.264 原始碼流(又稱為裸流)会烙,是有一個(gè)接一個(gè)的 NALU 組成的负懦,而它的功能分為兩層:視頻編碼層(VCL, Video Coding Layer)和網(wǎng)絡(luò)提取層(NAL, Network Abstraction Layer)。
VCL 數(shù)據(jù)即編碼處理的輸出柏腻,它表示被壓縮編碼后的視頻數(shù)據(jù) 序列纸厉。在 VCL 數(shù)據(jù)傳輸或存儲(chǔ)之前,這些編碼的 VCL 數(shù)據(jù)葫盼,先被映射或封裝進(jìn) NAL 單元(以下簡(jiǎn)稱 NALU残腌,Nal Unit) 中。每個(gè) NALU 包括一個(gè)原始字節(jié)序列負(fù)荷(RBSP, Raw Byte Sequence Payload)贫导、一組 對(duì)應(yīng)于視頻編碼的 NALU 頭部信息抛猫。RBSP 的基本結(jié)構(gòu)是:在原始編碼數(shù)據(jù)的后面填加了結(jié)尾 比特。一個(gè) bit“1”若干比特“0”孩灯,以便字節(jié)對(duì)齊闺金。
上圖中的 NALU頭 + RBSP 就相當(dāng)與一個(gè) NALU (Nal Unit), 每個(gè)單元都按獨(dú)立的 NALU 傳送。 其實(shí)說(shuō)白了峰档,H.264 中的結(jié)構(gòu)全部都是以 NALU 為主的败匹,理解了 NALU,就理解 H.264 的結(jié)構(gòu)了讥巡。
一幀圖片跟 NALU 的關(guān)聯(lián) :
究竟 NALU 是怎么由一幀圖片變化而來(lái)的呀掀亩,H.264究竟為什么這么神奇?
一幀圖片經(jīng)過(guò) H.264 編碼器之后欢顷,就被編碼為一個(gè)或多個(gè)片(slice)槽棍,而裝載著這些片(slice)的載體,就是 NALU 了抬驴,我們可以來(lái)看看 NALU 跟片的關(guān)系(slice)炼七。
小伙伴們要明白,片(slice)的概念不同與幀(frame)布持,幀(frame)是用作描述一張圖片的豌拙,一幀(frame)對(duì)應(yīng)一張圖片,而片(slice)题暖,是 H.264 中提出的新概念按傅,是通過(guò)編碼圖片后切分通過(guò)高效的方式整合出來(lái)的概念捉超,一張圖片至少有一個(gè)或多個(gè)片(slice)。
上圖中可以看出逞敷,片(slice)都是又 NALU 裝載并進(jìn)行網(wǎng)絡(luò)傳輸?shù)目袂兀沁@并不代表 NALU 內(nèi)就一定是切片灌侣,這是充分不必要條件推捐,因?yàn)?NALU 還有可能裝載著其他用作描述視頻的信息。
什么是切片(slice)?
片的主要作用是用作宏塊(Macroblock)的載體(ps:下面會(huì)介紹到宏塊的概念)侧啼。片之所以被創(chuàng)造出來(lái)牛柒,主要目的是為限制誤碼的擴(kuò)散和傳輸。
如何限制誤碼的擴(kuò)散和傳輸痊乾?
每個(gè)片(slice)都應(yīng)該是互相獨(dú)立被傳輸?shù)钠け冢称念A(yù)測(cè)(片(slice)內(nèi)預(yù)測(cè)和片(slice)間預(yù)測(cè))不能以其它片中的宏塊(Macroblock)為參考圖像。
那么片(slice)的具體結(jié)構(gòu)哪审,我們用一張圖來(lái)直觀說(shuō)明吧:
我們可以理解為一 張/幀 圖片可以包含一個(gè)或多個(gè)分片(Slice)蛾魄,而每一個(gè)分片(Slice)包含整數(shù)個(gè)宏塊(Macroblock),即每片(slice)至少一個(gè) 宏塊(Macroblock)湿滓,最多時(shí)每片包 整個(gè)圖像的宏塊滴须。
上圖結(jié)構(gòu)中,我們不難看出叽奥,每個(gè)分片也包含著頭和數(shù)據(jù)兩部分:
1扔水、分片頭中包含著分片類型、分片中的宏塊類型朝氓、分片幀的數(shù)量魔市、分片屬于那個(gè)圖像以及對(duì)應(yīng)的幀的設(shè)置和參數(shù)等信息。
2赵哲、分片數(shù)據(jù)中則是宏塊待德,這里就是我們要找的存儲(chǔ)像素?cái)?shù)據(jù)的地方攒至。
什么是宏塊途样?
宏塊是視頻信息的主要承載者虑润,因?yàn)樗恳粋€(gè)像素的亮度和色度信息诗力。視頻解碼最主要的工作則是提供高效的方式從碼流中獲得宏塊中的像素陣列婴削。
組成部分:一個(gè)宏塊由一個(gè)16×16亮度像素和附加的一個(gè)8×8 Cb和一個(gè) 8×8 Cr 彩色像素塊組成饲化。每個(gè)圖象中第岖,若干宏塊被排列成片的形式块差。
我們先來(lái)看看宏塊的結(jié)構(gòu)圖:
從上圖中毙死,可以看到燎潮,宏塊中包含了宏塊類型、預(yù)測(cè)類型扼倘、Coded Block Pattern确封、Quantization Parameter除呵、像素的亮度和色度數(shù)據(jù)集等等信息。
切片(slice)類型跟宏塊類型的關(guān)系
對(duì)于切片(slice)來(lái)講爪喘,分為以下幾種類型:
- P-slice. Consists of P-macroblocks (each macro block is predicted using one reference frame) and / or I-macroblocks.
- B-slice. Consists of B-macroblocks (each macroblock is predicted using one or two reference frames) and / or I-macroblocks.
- I-slice. Contains only I-macroblocks. Each macroblock is predicted from previously coded blocks of the same slice.
- SP-slice. Consists of P and / or I-macroblocks and lets you switch between encoded streams.
- SI-slice. It consists of a special type of SI-macroblocks and lets you switch between encoded streams.
I片:只包 I宏塊颜曾,I 宏塊利用從當(dāng)前片中已解碼的像素作為參考進(jìn)行幀內(nèi)預(yù)測(cè)(不能取其它片中的已解碼像素作為參考進(jìn)行幀內(nèi)預(yù)測(cè))。
P片:可包 P和I宏塊秉剑,P 宏塊利用前面已編碼圖象作為參考圖象進(jìn)行幀內(nèi)預(yù)測(cè)泛豪,一個(gè)幀內(nèi)編碼的宏塊可進(jìn)一步作宏塊的分割:即 16×16、16×8侦鹏、8×16 或 8×8 亮度像素塊(以及附帶的彩色像素);如果選了 8×8 的子宏塊诡曙,則可再分成各種子宏塊的分割,其尺寸為 8×8略水、8×4价卤、4×8 或 4×4 亮度像素塊(以及附帶的彩色像素)。
B片:可包 B和I宏塊渊涝,B 宏塊則利用雙向的參考圖象(當(dāng)前和 來(lái)的已編碼圖象幀)進(jìn)行幀內(nèi)預(yù)測(cè)慎璧。
SP片(切換P):用于不同編碼流之間的切換,包含 P 和/或 I 宏塊
SI片:擴(kuò)展檔次中必須具有的切換跨释,它包 了一種特殊類型的編碼宏塊胸私,叫做 SI 宏塊,SI 也是擴(kuò)展檔次中的必備功能煤傍。
整體結(jié)構(gòu)
通過(guò)剖析了這么多個(gè)小零件盖文,是時(shí)候個(gè)大家一個(gè)世界地圖了,
那么我們的 NALU 整體結(jié)構(gòu)可以呼之欲出了蚯姆,以下就引用 H.264 文檔當(dāng)中的一幅圖了
其實(shí) H.264 的碼流結(jié)構(gòu)并沒(méi)有大家想的那么復(fù)雜五续,編碼后視頻的每一組圖像(GOP,圖像組)都給予了傳輸中的序列(PPS)和本身這個(gè)幀的圖像參數(shù)(SPS)龄恋,所以疙驾,我們的整體結(jié)構(gòu),應(yīng)該如此:
GOP (圖像組)主要用作形容一個(gè) i 幀 到下一個(gè) i 幀之間的間隔了多少個(gè)幀郭毕,增大圖片組能有效的減少編碼后的視頻體積它碎,但是也會(huì)降低視頻質(zhì)量,至于怎么取舍显押,得看需求了扳肛。
主題外:(未完待續(xù))
那么,NALU 頭部中的類型確定著什么信息呢乘碑?
我們首先來(lái)看看 NALU 中究竟有哪幾種類型挖息,我們來(lái)看看 H.264 中源碼對(duì) nal_unit_type_e 中的定義:
enum nal_unit_type_e
{
NAL_UNKNOWN = 0, // 未使用
NAL_SLICE = 1, // 不分區(qū)、非 IDR 圖像的片(片的頭信息和數(shù)據(jù))
NAL_SLICE_DPA = 2, // 片分區(qū) A
NAL_SLICE_DPB = 3, // 片分區(qū) B
NAL_SLICE_DPC = 4, // 片分區(qū) C
NAL_SLICE_IDR = 5, / ref_idc != 0 / // IDR 圖像中的片
NAL_SEI = 6, / ref_idc == 0 / // 補(bǔ)充增強(qiáng)信息單元
參數(shù)集是 H.264 標(biāo)準(zhǔn)的一個(gè)新概念兽肤,是一種通過(guò)改進(jìn)視頻碼流結(jié)構(gòu)增強(qiáng)錯(cuò)誤恢復(fù)能力的方法套腹。
NAL_SPS = 7, // 序列參數(shù)集 (包括一個(gè)圖像序列的所有信息绪抛,即兩個(gè) IDR 圖像間的所有圖像信息,如圖像尺寸电禀、視頻格式等)
NAL_PPS = 8, // 圖像參數(shù)集 (包括一個(gè)圖像的所有分片的所有相關(guān)信息幢码, 包括圖像類型、序列號(hào)等尖飞,解碼時(shí)某些序列號(hào)的丟失可用來(lái)檢驗(yàn)信息包的丟失與否)
NAL_AUD = 9, // 分界符
NAL_FILLER = 12, // 填充(啞元數(shù)據(jù)症副,用于填充字節(jié))
/ ref_idc == 0 for 6,9, 10 (表明下一圖像為 IDR 圖像),11(表明該流中已沒(méi)有圖像),12 /
};
ps: 以上括號(hào)()中的為類型描述
上面NALU類型當(dāng)中,分片/切片(slice)的概念我們都已經(jīng)很清楚了葫松,但是用 NALU 作載體的還有 SEI瓦糕、SPS、PPS 等等腋么。
今天我們不一一聚述這些類型對(duì)整個(gè)流程的作用了,我們挑出兩個(gè)符合我們今天主題的類型來(lái)講亥揖,PPS 和 SPS珊擂。
那么今天我們講的 H.264 的碼流結(jié)構(gòu)相信大家都有個(gè)大概輪廓的了解了,總結(jié)的一句話就是:
H.264 中费变,句法元素共被組織成 序列摧扇、圖像、片挚歧、宏塊扛稽、子宏塊五個(gè)層次。
希望大家用心體會(huì)滑负,畢竟手工打字和作圖不易在张,大家能關(guān)注的關(guān)注,能有閑錢的打賞一個(gè)矮慕,能有贊的贊一下嘛
未完待續(xù)