【音视频基础】H264格式分析
介紹
H264是基于運動補償?shù)囊曨l編碼標準。所謂編碼我的理解就是對數(shù)據(jù)進行壓縮便于網(wǎng)絡(luò)傳輸。而視頻編碼就是依據(jù)圖像幀的像素塊之間的相似性對圖像進行壓縮。
相關(guān)概念
H264結(jié)構(gòu)中,一幅圖像編碼后的數(shù)據(jù)叫一幀,一幀由一個或多個Slice片組成,一片由一個或多個MB宏塊組成,一個宏塊由16*16的yuv數(shù)據(jù)組成。宏塊是H264編碼的基本單位。
幀類型
H264定義了三種幀,I幀,P幀,B幀。
I幀:關(guān)鍵幀,幀內(nèi)壓縮
P幀:前向預(yù)測編碼幀,幀間壓縮
B幀:雙向差別幀,幀間壓縮
GOP圖像組
group of picture 兩個I幀之間的所有幀為一個GOP。
H264對關(guān)聯(lián)度高的視頻幀進行分組,其算法是在相鄰幾幅圖像畫面中,一般有差別的像素只有10%以內(nèi)的點,亮度差值變化不超過2%,而色度差值的變化只有1%以內(nèi),我們認為這樣的圖可以分到一組GOP。
IDR幀:立即刷新圖像
IDR幀都是I幀,但I幀不一定是IDR幀。當(dāng)解碼器遇到IDR幀就會清空參考隊列,將已解碼的數(shù)據(jù)全部輸出或拋棄,開始解碼新的序列。
而普通的I幀不會清理參考隊列,也就是說IDR可以阻斷誤差的累計,而普通I幀不行。
PTS. DTS:
PTS(Presentation Time Stamp):PTS 主要用于度量解碼后的視頻幀什么時候被顯示出來
DTS(Decode Time Stamp):DTS 主要是標識內(nèi)存中的 bit 流什么時候開始送入解碼器中進行解碼
DTS 主要用戶視頻的解碼,在解碼階段使用。PTS主要用于視頻的同步和輸出,在顯示的時候使用。
由于B幀的存在,要參考前后幀,所以在有B幀的情況下 DTS!=PTS。沒有B幀則兩者相等。
Vega獲取的信息截圖:
壓縮技術(shù):
https://zhuanlan.zhihu.com/p/31056455
視頻圖像被送到H264編碼器中,編碼器給每一個圖像劃分宏塊。H264默認使用16*16大小作為一個宏塊。但為了更高的壓縮率,還可以將宏塊劃分為8*16、 16*8、 8*8、 4*8、 8*4、 4*4大小的子塊。
對劃分好的宏塊計算宏塊的像素值。最終一幅圖中每個宏塊都處理完后如下圖:
處理時間冗余(幀間預(yù)測壓縮):
運動估計與運動補償:
運動估計:當(dāng)前幀的某個區(qū)域(A)在參考幀中尋找一個合適的匹配區(qū)域(B)。
運動補償:找到區(qū)域A和區(qū)域B的不同。
這運動矢量就是炮二的區(qū)域移動到炮五的區(qū)域,移動后產(chǎn)生一個預(yù)測幀。預(yù)測幀和當(dāng)前幀并不完全一樣,他們的區(qū)別就是殘差。 此時的殘差則是炮二位置的棋格,以及炮五邊框的顏色變化。
預(yù)測性編碼的產(chǎn)出就是這些運動矢量和殘差,通過這個例子我們能看到這些產(chǎn)出數(shù)據(jù)是遠遠小于一個完整幀的數(shù)據(jù)量的。
處理空間冗余(幀內(nèi)壓縮):
https://www.cnblogs.com/charybdis/p/6049108.html
對于一幅圖像,相鄰的兩個像素的亮度和色度是比較接近的,所以在保存一個像素時不需要將這個像素的全部信息保存,只需要保存這個像素與其參考像素的插值即可。
使用上一個像素X’做參考像素,經(jīng)過幀內(nèi)預(yù)測獲得當(dāng)前像素X的預(yù)測像素Xp,X減去Xp就獲得了差值d。
在解碼的時候,同樣利用X’獲得預(yù)測像素Xp,Xp加上插值d,就可以獲取原始值X。
同時這個X可以作為下一個像素的X’從而成為一個完整的循環(huán)。
當(dāng)然在H264中,因為以像素為單位太小,所以以宏塊為單位(16*16像素、4*4像素)進行計算。
上述遺漏的問題,預(yù)測值Xp怎么來的?Xp是通過X’利用某個公式計算的。在白皮書中 4*4 有9種預(yù)測模式,16*16有4種預(yù)測模式。
如何在這9種算法種選擇,當(dāng)然是希望誤差越小越好,所以也有對應(yīng)的算法去計算誤差。例如:SAD,SATD等。
同時因為選擇了不同的算法,所以解碼器也需要知道每個宏塊具體使用哪種算法。所以有1bit用于保存是由與上一個一樣,如果不一樣則用4bit保存具體選擇哪個算法。
X’真的與原始X完全一樣么?
理論上上講按前面的算法應(yīng)該是一樣的,但因為差值傳到解碼器的過程種經(jīng)過了量化、變換、反變換和反量化,有了精度算是,因此X’真的與原始X無法完全一致。
H264編解碼流程:
H264分層結(jié)構(gòu)
H264的主要目標是為了有高視頻壓縮比和良好的網(wǎng)絡(luò)親和性。
為了這兩個目的H264將系統(tǒng)架構(gòu)劃分了兩個層面 VCL 和 NAL。
VCL:Video Coding Layer,視頻編碼層
對核心算法引擎、塊、宏塊及片的語法級別的定義,負責(zé)有效表示視頻數(shù)據(jù)的內(nèi)容,最終輸出編碼完的數(shù)據(jù)SODB(數(shù)據(jù)比特串)
NAL:Network Abstraction Layer,網(wǎng)絡(luò)提取層
定義了片級以上的語法級別(如序列參數(shù)集和圖像參數(shù)集),負責(zé)以網(wǎng)絡(luò)所要求的恰當(dāng)方式去格式化數(shù)據(jù)并提供頭信息,以保證數(shù)據(jù)適合各種信道和存儲介質(zhì)上的傳輸。
『NAL』就是為了包裝『VCL』以達到更好網(wǎng)絡(luò)傳輸效果。NAL層將 SODB 打包成 RBSP(原始字節(jié)序列負荷) 然后加上NAL header 組成一個NALU。
RBSP(Raw Byte Sequence Payload,原始字節(jié)序列載荷):
PBSP就是在SODB后添加了trailing bits,即一個bit 1和若干個bit 0,以便字節(jié)對齊。
傳統(tǒng)的視頻碼流僅有VCL視頻編碼層,而H264可以根據(jù)不同應(yīng)用增加不同的NAL header,用來適應(yīng)不同的網(wǎng)絡(luò)應(yīng)用環(huán)境,減少碼流的傳輸錯誤。VCL數(shù)據(jù)在傳輸前先被映射到NAL單元中。
EBSP:(Encapsulated Byte Sequence Payload, 擴展字節(jié)序列載荷)
H264規(guī)定,當(dāng)檢測到當(dāng)檢測到0x000000時,也可以表示當(dāng)前NALU的結(jié)束。
那這樣就會產(chǎn)生一個問題,就是如果在NALU的內(nèi)部,出現(xiàn)了0x000001或0x000000時該怎么辦?
在編碼時,每遇到兩個字節(jié)連續(xù)為0(0x0000),就插入一個字節(jié)的0x03。解碼時將0x03去掉。
H264碼流結(jié)構(gòu)(Annex-B格式)
ps:H264有兩種封裝:?種是annexb模式,傳統(tǒng)模式,有startcode。?種是mp4模式,?般mp4,mkv都是mp4模式,沒有startcode,SPS和PPS以及其它信息被封裝在container中,每?個frame前?4個字節(jié)是這個frame的?度很多解碼器只?持annexb這種模式。
H264碼流是一個個連續(xù)的NALU,一個NALU包含 [NALU Header][NALU Payload (RBSP)] 三部分。
StartCode:是一個NALU單元開始。
主要是為了將相鄰兩個NALU劃分開,讓他們有一個界線,方便解碼。必須是 0x00 00 00 01 或者0x00 00 01。
那么玩意數(shù)據(jù)中間正好有個 0x00 00 00 01 或者 0x00 00 01 怎么辦?見上述EBSP。
并且h264有個防止競爭的機制,在編碼一個NAL時,如果出現(xiàn)有連續(xù)兩個0x00字節(jié),就在連續(xù)兩個0x00后面插入一個0x03(解碼的時候這個0x03會被丟棄)。
NAL header:定義了RBSP單元的類型
由 1字節(jié)(8位)組成。禁止位(1位)、重要性指示位(2位)、NALU類型(5位)。
nal_unit_type取值說明:
SPS 和 PPS
https://zhuanlan.zhihu.com/p/27896239
從上圖可知SPS和PPS是一個NALU的類型。
實際網(wǎng)絡(luò)傳輸編碼好的數(shù)據(jù)流的時候會出現(xiàn)丟包,而如果丟包數(shù)據(jù)為圖像頭等關(guān)鍵信息的時候甚至?xí)?dǎo)致后續(xù)解碼失敗。在H264之前,為了應(yīng)對圖像頭關(guān)鍵信息被丟失的做法是在很多包(也有說法是每一個包)都會攜帶圖像頭關(guān)鍵信息(冗余做災(zāi)備的思想)。但是,在H264種,為了提高網(wǎng)絡(luò)傳輸魯棒性,重新設(shè)計出SPS和PPS。
SPS(序列參數(shù)集):SPS中保存了一組編碼視頻序列(Coded Video Sequence)的全局參數(shù)。因此該類型保存的是和編碼序列相關(guān)的參數(shù)。
https://yinwenjie.blog.csdn.net/article/details/52771030
PPS(圖像參數(shù)集):PPS中保存了整體圖像相關(guān)的參數(shù)。
https://yinwenjie.blog.csdn.net/article/details/52877689
根據(jù)Vega分析,IDR幀中就包含了SPS,PPS和IDR本身的NALU。
SEI:補充增強信息
Access Unit分隔符:Access Unit:是一個或者多個 NALU 的集合,代表了一個完整的幀。
H264碼流整體結(jié)構(gòu):
Level
通過 Vega 分析,不同的 H264 文件有不用的 Profile 和 level。
計算支持1080P(1920*1080)的最低級別:
一個宏塊大小16*16.。ceil是向上取整
水平宏塊數(shù)(PicWidthInMbs)= ceil(視頻寬度 / 16) = ceil(1080 / 16) = ceil(67.5) = 68
垂直宏塊數(shù)(FrameHeightInMbs)= ceil(視頻寬度 / 16) = ceil(1920 / 16) = ceil(120) = 120
每幀宏塊數(shù)(Macroblocks per frame)= 水平宏塊數(shù) * 垂直宏塊數(shù) = 68 * 120 = 8160
查詢上面的級別詳表可知,支持每幀8160個宏塊的最低級別是4。
級別4 允許的每秒最大宏塊數(shù)是 245,760 。所以 245760 / 8160 =30.1,即最高支持每秒30.1幀。當(dāng)然級別更高支持的幀數(shù)也更多。
MaxDpbMbs
表中最后一列為 MaxDpbMbs 最大解碼緩沖區(qū)宏塊數(shù)。也就是解碼時參考緩沖區(qū)中的宏塊數(shù)。
DpbMbs = ref(參考幀數(shù)) * PicWidthInMbs(水平宏塊數(shù)) * FrameHeightInMbs(垂直宏塊數(shù))
我們可以根據(jù) MaxDpbMbs 倒推出 最大參考幀數(shù)。
公式為:max_ref = min(floor(MaxDpbMbs / (PicWidthInMbs * FrameHeightInMbs)), 16)。floor是向下取整。
以1080P + Level 4 為例:
min(floor(32,768 / (68*120)),16) = 4 注:后面的16 是因為 參考幀數(shù)組大只能為16
所以1080P的視頻在 Level 4 級別下,最高支持 4 個參考幀。
反推可知,在解碼時參考幀的幀數(shù)并不只是前1幀,而是前多幀。同理編碼時當(dāng)前幀的參考幀也不只是前一幀,而是前多幀。
這也就應(yīng)證了I幀和IDR幀的區(qū)別。雖然應(yīng)證了,但還是存在疑問,既然I幀已經(jīng)可以獨立編碼解碼了,那么為什么在編碼解碼的時候還要參考I幀之前的幀?
總結(jié)
以上是生活随笔為你收集整理的【音视频基础】H264格式分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Py之lime:lime库的简介、安装、
- 下一篇: localStorage储存如何正确存储