h264
H264--編碼原理以及I幀B幀P幀--1
?
前言
-----------------------
H264是新一代的編碼標(biāo)準(zhǔn),以高壓縮高質(zhì)量和支持多種網(wǎng)絡(luò)的流媒體傳輸著稱,在編碼方面,我理解的他的理論依據(jù)是:參照一段時(shí)間內(nèi)圖像的統(tǒng)計(jì)結(jié)果表明,在相鄰幾幅圖像畫面中,一般有差別的像素只有10%以內(nèi)的點(diǎn),亮度差值變化不超過(guò)2%,而色度差值的變化只有1%以內(nèi)。所以對(duì)于一段變化不大圖像畫面,我們可以先編碼出一個(gè)完整的圖像幀A,隨后的B幀就不編碼全部圖像,只寫入與A幀的差別,這樣B幀的大小就只有完整幀的1/10或更小!B幀之后的C幀如果變化不大,我們可以繼續(xù)以參考B的方式編碼C幀,這樣循環(huán)下去。這段圖像我們稱為一個(gè)序列(序列就是有相同特點(diǎn)的一段數(shù)據(jù)),當(dāng)某個(gè)圖像與之前的圖像變化很大,無(wú)法參考前面的幀來(lái)生成,那我們就結(jié)束上一個(gè)序列,開(kāi)始下一段序列,也就是對(duì)這個(gè)圖像生成一個(gè)完整幀A1,隨后的圖像就參考A1生成,只寫入與A1的差別內(nèi)容。
在H264協(xié)議里定義了三種幀,完整編碼的幀叫I幀,參考之前的I幀生成的只包含差異部分編碼的幀叫P幀,還有一種參考前后的幀編碼的幀叫B幀。
H264采用的核心算法是幀內(nèi)壓縮和幀間壓縮,幀內(nèi)壓縮是生成I幀的算法,幀間壓縮是生成B幀和P幀的算法。
?
----------------------
序列的說(shuō)明
----------------------
在H264中圖像以序列為單位進(jìn)行組織,一個(gè)序列是一段圖像編碼后的數(shù)據(jù)流,以I幀開(kāi)始,到下一個(gè)I幀結(jié)束。
一個(gè)序列的第一個(gè)圖像叫做 IDR 圖像(立即刷新圖像),IDR 圖像都是 I 幀圖像。H.264 引入 IDR 圖像是為了解碼的重同步,當(dāng)解碼器解碼到 IDR 圖像時(shí),立即將參考幀隊(duì)列清空,將已解碼的數(shù)據(jù)全部輸出或拋棄,重新查找參數(shù)集,開(kāi)始一個(gè)新的序列。這樣,如果前一個(gè)序列出現(xiàn)重大錯(cuò)誤,在這里可以獲得重新同步的機(jī)會(huì)。IDR圖像之后的圖像永遠(yuǎn)不會(huì)使用IDR之前的圖像的數(shù)據(jù)來(lái)解碼。
一個(gè)序列就是一段內(nèi)容差異不太大的圖像編碼后生成的一串?dāng)?shù)據(jù)流。當(dāng)運(yùn)動(dòng)變化比較少時(shí),一個(gè)序列可以很長(zhǎng),因?yàn)檫\(yùn)動(dòng)變化少就代表圖像畫面的內(nèi)容變動(dòng)很小,所以就可以編一個(gè)I幀,然后一直P幀、B幀了。當(dāng)運(yùn)動(dòng)變化多時(shí),可能一個(gè)序列就比較短了,比如就包含一個(gè)I幀和3、4個(gè)P幀。
-----------------------
三種幀的說(shuō)明
-----------------------
I幀:幀內(nèi)編碼幀 ,I幀表示關(guān)鍵幀,你可以理解為這一幀畫面的完整保留;解碼時(shí)只需要本幀數(shù)據(jù)就可以完成(因?yàn)榘暾嬅?#xff09;
I幀特點(diǎn):?
1.它是一個(gè)全幀壓縮編碼幀。它將全幀圖像信息進(jìn)行JPEG壓縮編碼及傳輸;?
2.解碼時(shí)僅用I幀的數(shù)據(jù)就可重構(gòu)完整圖像;?
3.I幀描述了圖像背景和運(yùn)動(dòng)主體的詳情;?
4.I幀不需要參考其他畫面而生成;?
5.I幀是P幀和B幀的參考幀(其質(zhì)量直接影響到同組中以后各幀的質(zhì)量);?
6.I幀是幀組GOP的基礎(chǔ)幀(第一幀),在一組中只有一個(gè)I幀;?
7.I幀不需要考慮運(yùn)動(dòng)矢量;?
8.I幀所占數(shù)據(jù)的信息量比較大。?
P幀:前向預(yù)測(cè)編碼幀。P幀表示的是這一幀跟之前的一個(gè)關(guān)鍵幀(或P幀)的差別,解碼時(shí)需要用之前緩存的畫面疊加上本幀定義的差別,生成最終畫面。(也就是差別幀,P幀沒(méi)有完整畫面數(shù)據(jù),只有與前一幀的畫面差別的數(shù)據(jù))
P幀的預(yù)測(cè)與重構(gòu):P幀是以I幀為參考幀,在I幀中找出P幀“某點(diǎn)”的預(yù)測(cè)值和運(yùn)動(dòng)矢量,取預(yù)測(cè)差值和運(yùn)動(dòng)矢量一起傳送。在接收端根據(jù)運(yùn)動(dòng)矢量從I幀中找出P幀“某點(diǎn)”的預(yù)測(cè)值并與差值相加以得到P幀“某點(diǎn)”樣值,從而可得到完整的P幀。
P幀特點(diǎn):?
1.P幀是I幀后面相隔1~2幀的編碼幀;?
2.P幀采用運(yùn)動(dòng)補(bǔ)償?shù)姆椒▊魉退c前面的I或P幀的差值及運(yùn)動(dòng)矢量(預(yù)測(cè)誤差);?
3.解碼時(shí)必須將I幀中的預(yù)測(cè)值與預(yù)測(cè)誤差求和后才能重構(gòu)完整的P幀圖像;?
4.P幀屬于前向預(yù)測(cè)的幀間編碼。它只參考前面最靠近它的I幀或P幀;?
5.P幀可以是其后面P幀的參考幀,也可以是其前后的B幀的參考幀;?
6.由于P幀是參考幀,它可能造成解碼錯(cuò)誤的擴(kuò)散;?
7.由于是差值傳送,P幀的壓縮比較高。?
B幀:雙向預(yù)測(cè)內(nèi)插編碼幀。B幀是雙向差別幀,也就是B幀記錄的是本幀與前后幀的差別(具體比較復(fù)雜,有4種情況,但我這樣說(shuō)簡(jiǎn)單些),換言之,要解碼B幀,不僅要取得之前的緩存畫面,還要解碼之后的畫面,通過(guò)前后畫面的與本幀數(shù)據(jù)的疊加取得最終的畫面。B幀壓縮率高,但是解碼時(shí)CPU會(huì)比較累。
B幀的預(yù)測(cè)與重構(gòu)?
B幀以前面的I或P幀和后面的P幀為參考幀,“找出”B幀“某點(diǎn)”的預(yù)測(cè)值和兩個(gè)運(yùn)動(dòng)矢量,并取預(yù)測(cè)差值和運(yùn)動(dòng)矢量傳送。接收端根據(jù)運(yùn)動(dòng)矢量在兩個(gè)參考幀中“找出(算出)”預(yù)測(cè)值并與差值求和,得到B幀“某點(diǎn)”樣值,從而可得到完整的B幀。
B幀特點(diǎn)?
1.B幀是由前面的I或P幀和后面的P幀來(lái)進(jìn)行預(yù)測(cè)的;?
2.B幀傳送的是它與前面的I或P幀和后面的P幀之間的預(yù)測(cè)誤差及運(yùn)動(dòng)矢量;?
3.B幀是雙向預(yù)測(cè)編碼幀;?
4.B幀壓縮比最高,因?yàn)樗环从潮麉⒖紟g運(yùn)動(dòng)主體的變化情況,預(yù)測(cè)比較準(zhǔn)確;?
5.B幀不是參考幀,不會(huì)造成解碼錯(cuò)誤的擴(kuò)散。?
注:I、B、P各幀是根據(jù)壓縮算法的需要,是人為定義的,它們都是實(shí)實(shí)在在的物理幀。一般來(lái)說(shuō),I幀的壓縮率是7(跟JPG差不多),P幀是20,B幀可以達(dá)到50。可見(jiàn)使用B幀能節(jié)省大量空間,節(jié)省出來(lái)的空間可以用來(lái)保存多一些I幀,這樣在相同碼率下,可以提供更好的畫質(zhì)。
--------------------------------
壓縮算法的說(shuō)明
--------------------------------
h264的壓縮方法:
1.分組:把幾幀圖像分為一組(GOP,也就是一個(gè)序列),為防止運(yùn)動(dòng)變化,幀數(shù)不宜取多。?
2.定義幀:將每組內(nèi)各幀圖像定義為三種類型,即I幀、B幀和P幀;?
3.預(yù)測(cè)幀:以I幀做為基礎(chǔ)幀,以I幀預(yù)測(cè)P幀,再由I幀和P幀預(yù)測(cè)B幀;?
4.數(shù)據(jù)傳輸:最后將I幀數(shù)據(jù)與預(yù)測(cè)的差值信息進(jìn)行存儲(chǔ)和傳輸。
?
幀內(nèi)(Intraframe)壓縮也稱為空間壓縮(Spatial compression)。當(dāng)壓縮一幀圖像時(shí),僅考慮本幀的數(shù)據(jù)而不考慮相鄰幀之間的冗余信息,這實(shí)際上與靜態(tài)圖像壓縮類似。幀內(nèi)一般采用有損壓縮算法,由于幀內(nèi)壓縮是編碼一個(gè)完整的圖像,所以可以獨(dú)立的解碼、顯示。幀內(nèi)壓縮一般達(dá)不到很高的壓縮,跟編碼jpeg差不多。
幀間(Interframe)壓縮的原理是:相鄰幾幀的數(shù)據(jù)有很大的相關(guān)性,或者說(shuō)前后兩幀信息變化很小的特點(diǎn)。也即連續(xù)的視頻其相鄰幀之間具有冗余信息,根據(jù)這一特性,壓縮相鄰幀之間的冗余量就可以進(jìn)一步提高壓縮量,減小壓縮比。幀間壓縮也稱為時(shí)間壓縮(Temporal compression),它通過(guò)比較時(shí)間軸上不同幀之間的數(shù)據(jù)進(jìn)行壓縮。幀間壓縮一般是無(wú)損的。幀差值(Frame differencing)算法是一種典型的時(shí)間壓縮法,它通過(guò)比較本幀與相鄰幀之間的差異,僅記錄本幀與其相鄰幀的差值,這樣可以大大減少數(shù)據(jù)量。
順便說(shuō)下有損(Lossy )壓縮和無(wú)損(Lossy less)壓縮。無(wú)損壓縮也即壓縮前和解壓縮后的數(shù)據(jù)完全一致。多數(shù)的無(wú)損壓縮都采用RLE行程編碼算法。有損壓縮意味著解壓縮后的數(shù)據(jù)與壓縮前的數(shù)據(jù)不一致。在壓縮的過(guò)程中要丟失一些人眼和人耳所不敏感的圖像或音頻信息,而且丟失的信息不可恢復(fù)。幾乎所有高壓縮的算法都采用有損壓縮,這樣才能達(dá)到低數(shù)據(jù)率的目標(biāo)。丟失的數(shù)據(jù)率與壓縮比有關(guān),壓縮比越小,丟失的數(shù)據(jù)越多,解壓縮后的效果一般越差。此外,某些有損壓縮算法采用多次重復(fù)壓縮的方式,這樣還會(huì)引起額外的數(shù)據(jù)丟失。
?
前言
-------------
H264結(jié)構(gòu)中,一個(gè)視頻圖像編碼后的數(shù)據(jù)叫做一幀,一幀由一個(gè)片(slice)或多個(gè)片組成,一個(gè)片由一個(gè)或多個(gè)宏塊(MB)組成,一個(gè)宏塊由16x16的yuv數(shù)據(jù)組成。宏塊作為H264編碼的基本單位。
-------------------------
名詞解釋 ------------------------- 場(chǎng)和幀 :?視頻的一場(chǎng)或一幀可用來(lái)產(chǎn)生一個(gè)編碼圖像。在電視中,為減少大面積閃爍現(xiàn)象,把一幀分成兩個(gè)隔行的場(chǎng)。 宏塊?:一個(gè)編碼圖像通常劃分成若干宏塊組成,一個(gè)宏塊由一個(gè)16×16亮度像素和附加的一個(gè)8×8 Cb和一個(gè)8×8 Cr彩色像素塊組成。 片:每個(gè)圖象中,若干宏塊被排列成片的形式。片分為I片、B片、P片和其他一些片。 -- I片只包含I宏塊,P片可包含P和I宏塊,而B片可包含B和I宏塊。 -- I宏塊利用從當(dāng)前片中已解碼的像素作為參考進(jìn)行幀內(nèi)預(yù)測(cè)。 -- P宏塊利用前面已編碼圖象作為參考圖象進(jìn)行幀內(nèi)預(yù)測(cè)。 -- B宏塊則利用雙向的參考圖象(前一幀和后一幀)進(jìn)行幀內(nèi)預(yù)測(cè)。 ?片的目的是為了限制誤碼的擴(kuò)散和傳輸,使編碼片相互間是獨(dú)立的。某片的預(yù)測(cè)不能以其它片中的宏塊為參考圖像,這樣某一片中的預(yù)測(cè)誤差才不會(huì)傳播到其它片中去。-----------------------------------------------
H264/AVC 的分層結(jié)構(gòu)
-----------------------------------------------
H.264的主要目標(biāo)是:
1.高的視頻壓縮比;
2.良好的網(wǎng)絡(luò)親和性;
為了完成這些目標(biāo)H264的解決方案是:
1.VCL video coding layer?視頻編碼層;
2.NAL network abstraction layer?網(wǎng)絡(luò)提取層;
?
其中,VCL層是對(duì)核心算法引擎,塊,宏塊及片的語(yǔ)法級(jí)別的定義,他最終輸出編碼完的數(shù)據(jù) SODB;
NAL層定義片級(jí)以上的語(yǔ)法級(jí)別(如序列參數(shù)集和圖像參數(shù)集,針對(duì)網(wǎng)絡(luò)傳輸),同時(shí)支持以下功能:獨(dú)立片解碼,起始碼唯一保證,SEI以及流格式編碼數(shù)據(jù)傳送,NAL層將SODB打包成RBSP然后加上NAL頭,組成一個(gè)NALU(NAL單元);
---------------------------------------------
H264網(wǎng)絡(luò)傳輸?shù)慕Y(jié)構(gòu)
---------------------------------------------
H264在網(wǎng)絡(luò)傳輸?shù)氖荖ALU,NALU的結(jié)構(gòu)是:NAL頭+RBSP,實(shí)際傳輸中的數(shù)據(jù)流如圖所示:
?
?
?
從前面的分析我們知道,VCL層出來(lái)的是編碼完的視頻幀數(shù)據(jù),這些幀可能是I、B、P幀,而且這些幀可能屬于不同的序列,再者同一個(gè)序列還有相對(duì)應(yīng)的一套序列參數(shù)集和圖片參數(shù)集等等,所以要完成視頻的解碼,不僅需要傳輸VCL層編碼出來(lái)的視頻幀數(shù)據(jù),還需要傳輸序列參數(shù)集、圖像參數(shù)集等數(shù)據(jù)。
NALU頭用來(lái)標(biāo)識(shí)后面的RBSP是什么類型的數(shù)據(jù),他是否會(huì)被其他幀參考以及網(wǎng)絡(luò)傳輸是否有錯(cuò)誤。
RBSP用來(lái)存放下表中的一種:
?
| RBSP類型 | 所寫 | 描述 |
| 參數(shù)集 | PS | 序列的全局信息,如圖像尺寸,視頻格式等 |
| 增強(qiáng)信息 | SEI | 視頻序列解碼的增強(qiáng)信息 |
| 圖像界定符 | PD | 視頻圖像的邊界 |
| 編碼片 | SLICE | 編碼片的頭信息和數(shù)據(jù) |
| 數(shù)據(jù)分割 | ? | DP片層的數(shù)據(jù),用于錯(cuò)誤恢復(fù)解碼 |
| 序列結(jié)束符 | ? | 表明一個(gè)序列的結(jié)束,下一個(gè)圖像為IDR圖像 |
| 流結(jié)束符 | ? | 表明該流中已沒(méi)有圖像 |
| 填充數(shù)據(jù) | ? | 亞元數(shù)據(jù),用于填充字節(jié) |
?
其中,
參數(shù)集:包括序列參數(shù)集 SPS?和圖像參數(shù)集 PPS
SPS?包含的是針對(duì)一連續(xù)編碼視頻序列的參數(shù),如標(biāo)識(shí)符 seq_parameter_set_id、幀數(shù)及 POC 的約束、參考幀數(shù)目、解碼圖像尺寸和幀場(chǎng)編碼模式選擇標(biāo)識(shí)等等。
PPS對(duì)應(yīng)的是一個(gè)序列中某一幅圖像或者某幾幅圖像,其參數(shù)如標(biāo)識(shí)符 pic_parameter_set_id、可選的 seq_parameter_set_id、熵編碼模式選擇標(biāo)識(shí)、片組數(shù)目、初始量化參數(shù)和去方塊濾波系數(shù)調(diào)整標(biāo)識(shí)等等。
數(shù)據(jù)分割:組成片的編碼數(shù)據(jù)存放在 3 個(gè)獨(dú)立的 DP(數(shù)據(jù)分割,A、B、C)中,各自包含一個(gè)編碼片的子集。分割A包含片頭和片中每個(gè)宏塊頭數(shù)據(jù)。分割B包含幀內(nèi)和 SI 片宏塊的編碼殘差數(shù)據(jù)。分割 C包含幀間宏塊的編碼殘差數(shù)據(jù)。每個(gè)分割可放在獨(dú)立的 NAL 單元并獨(dú)立傳輸。
-----------------------------------------
NALU頭結(jié)構(gòu)
----------------------------------------
NALU頭結(jié)構(gòu):nal_unit_type(5bit)+nal_reference_bit(2bit)+forbidden_bit(1bit)
1.nal_unit_type:NALU類型取值如下表所示。
?
| nal_unit_type | NAL類型 | C |
| 0 | 未使用 | ? |
| 1 | 非IDR圖像中不采用數(shù)據(jù)劃分的片段 | 2,3,4 |
| 2 | 非IDR圖像中A類數(shù)據(jù)劃分片段 | 2 |
| 3 | 非IDR圖像中B類數(shù)據(jù)劃分片段 | 3 |
| 4 | 非IDR圖像中C類數(shù)據(jù)劃分片段 | 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 | 不保留 | ? |
?
?
2.nal_reference_bit:nal重要性指示,標(biāo)志該NAL單元的重要性,值越大,越重要,解碼器在解碼處理不過(guò)來(lái)的時(shí)候,可以丟掉重要性為0的NALU。不同類型的NALU的重要性指示如下表所示。
?
| nal_unit_type | NAL類型 | nal_reference_bit |
| 0 | 未使用 | 0 |
| 1 | 非IDR的片 | 此片屬于參考幀,則不等于0, 不屬于參考幀,則等與0 |
| 2 | 片數(shù)據(jù)A分區(qū) | 同上 |
| 3 | 片數(shù)據(jù)B分區(qū) | 同上 |
| 4 | 片數(shù)據(jù)C分區(qū) | 同上 |
| 5 | IDR圖像的片 | 5 |
| 6 | 補(bǔ)充增強(qiáng)信息單元(SEI) | 0 |
| 7 | 序列參數(shù)集 | 非0 |
| 8 | 圖像參數(shù)集 | 非0 |
| 9 | 分界符 | 0 |
| 10 | 序列結(jié)束 | 0 |
| 11 | 碼流結(jié)束 | 0 |
| 12 | 填充 | 0 |
| 13..23 | 保留 | 0 |
| 24..31 | 不保留 | 0 |
?
所謂參考幀,就是在其他幀解碼時(shí)需要參照的幀。比如一個(gè)I幀可能被一個(gè)或多個(gè)B幀參考,一個(gè)B幀可能被某個(gè)P幀參考。
從這個(gè)表我們也可以看出來(lái),DIR的I幀是非常重要的,他一丟,那么這個(gè)序列的所有幀都沒(méi)辦法解碼了;然后序列參數(shù)集和圖像參數(shù)集也很重要,沒(méi)有序列參數(shù)集,這個(gè)序列的幀就沒(méi)法解;沒(méi)有圖像參數(shù)集,那用到這個(gè)圖像參數(shù)集的幀都沒(méi)法解。
3.forbidden_bit:禁止位,初始為0,當(dāng)網(wǎng)絡(luò)發(fā)現(xiàn)NAL單元有比特錯(cuò)誤時(shí)可設(shè)置該比特為1,以便接收方糾錯(cuò)或丟掉該單元。
?
-------------------------------------
NAL的開(kāi)始和結(jié)束
-------------------------------
編碼器將每個(gè)NAL各自獨(dú)立、完整地放入一個(gè)分組,因?yàn)榉纸M都有頭部,解碼器可以方便地檢測(cè)出NAL的分界,并依次取出NAL進(jìn)行解碼。每個(gè)NAL前有一個(gè)起始碼 0x00 00 01(或者0x00 00 00 01),解碼器檢測(cè)每個(gè)起始碼,作為一個(gè)NAL的起始標(biāo)識(shí),當(dāng)檢測(cè)到下一個(gè)起始碼時(shí),當(dāng)前NAL結(jié)束。同時(shí)H.264規(guī)定,當(dāng)檢測(cè)到0x000000時(shí),也可以表征當(dāng)前NAL的結(jié)束。那么NAL中數(shù)據(jù)出現(xiàn)0x000001或0x000000時(shí)怎么辦?H.264引入了防止競(jìng)爭(zhēng)機(jī)制,如果編碼器檢測(cè)到NAL數(shù)據(jù)存在0x000001或0x000000時(shí),編碼器會(huì)在最后個(gè)字節(jié)前插入一個(gè)新的字節(jié)0x03,這樣:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解碼器檢測(cè)到0x000003時(shí),把03拋棄,恢復(fù)原始數(shù)據(jù)。解碼器在解碼時(shí),首先逐個(gè)字節(jié)讀取NAL的數(shù)據(jù),統(tǒng)計(jì)NAL的長(zhǎng)度,然后再開(kāi)始解碼。
?
--------------------------------------
NALU的順序要求
--------------------------------------
H.264/AVC標(biāo)準(zhǔn)對(duì)送到解碼器的NAL單元順序是有嚴(yán)格要求的,如果NAL單元的順序是混亂的,必須將其重新依照規(guī)范組織后送入解碼器,否則解碼器不能夠正確解碼。
1.序列參數(shù)集NAL單元 必須在傳送所有以此參數(shù)集為參考的其他NAL單元之前傳送,不過(guò)允許這些NAL單元中間出現(xiàn)重復(fù)的序列參數(shù)集NAL單元。
所謂重復(fù)的詳細(xì)解釋為:序列參數(shù)集NAL單元都有其專門的標(biāo)識(shí),如果兩個(gè)序列參數(shù)集NAL單元的標(biāo)識(shí)相同,就可以認(rèn)為后一個(gè)只不過(guò)是前一個(gè)的拷貝,而非新的序列參數(shù)集。
2.圖像參數(shù)集NAL單元 必須在所有以此參數(shù)集為參考的其他NAL單元之前傳送,不過(guò)允許這些NAL單元中間出現(xiàn)重復(fù)的圖像參數(shù)集NAL單元,這一點(diǎn)與上述的序列參數(shù)集NAL單元是相同的。
3.不同基本編碼圖像中的片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元在順序上不可以相互交叉,即不允許屬于某一基本編碼圖像的一系列片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元中忽然出現(xiàn)另一個(gè)基本編碼圖像的片段(slice)單元片段和數(shù)據(jù)劃分片段(data partition)單元。
4.參考圖像的影響:如果一幅圖像以另一幅圖像為參考,則屬于前者的所有片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元必須在屬于后者的片段和數(shù)據(jù)劃分片段之后,無(wú)論是基本編碼圖像還是冗余編碼圖像都必須遵守這個(gè)規(guī)則。
5.基本編碼圖像的所有片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元必須在屬于相應(yīng)冗余編碼圖像的片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元之前。
6.如果數(shù)據(jù)流中出現(xiàn)了連續(xù)的無(wú)參考基本編碼圖像,則圖像序號(hào)小的在前面。
7.如果arbitrary_slice_order_allowed_flag置為1,一個(gè)基本編碼圖像中的片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元的順序是任意的,如果arbitrary_slice_order_allowed_flag置為零,則要按照片段中第一個(gè)宏塊的位置來(lái)確定片段的順序,若使用數(shù)據(jù)劃分,則A類數(shù)據(jù)劃分片段在B類數(shù)據(jù)劃分片段之前,B類數(shù)據(jù)劃分片段在C類數(shù)據(jù)劃分片段之前,而且對(duì)應(yīng)不同片段的數(shù)據(jù)劃分片段不能相互交叉,也不能與沒(méi)有數(shù)據(jù)劃分的片段相互交叉。
8.如果存在SEI(補(bǔ)充增強(qiáng)信息)單元的話,它必須在它所對(duì)應(yīng)的基本編碼圖像的片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元之前,并同時(shí)必須緊接在上一個(gè)基本編碼圖像的所有片段(slice)單元和數(shù)據(jù)劃分片段(data partition)單元后邊。假如SEI屬于多個(gè)基本編碼圖像,其順序僅以第一個(gè)基本編碼圖像為參照。
9.如果存在圖像分割符的話,它必須在所有SEI 單元、基本編碼圖像的所有片段slice)單元和數(shù)據(jù)劃分片段(data partition)單元之前,并且緊接著上一個(gè)基本編碼圖像那些NAL單元。
10.如果存在序列結(jié)束符,且序列結(jié)束符后還有圖像,則該圖像必須是IDR(即時(shí)解碼器刷新)圖像。序列結(jié)束符的位置應(yīng)當(dāng)在屬于這個(gè)IDR圖像的分割符、SEI 單元等數(shù)據(jù)之前,且緊接著前面那些圖像的NAL單元。如果序列結(jié)束符后沒(méi)有圖像了,那么它的就在比特流中所有圖像數(shù)據(jù)之后。
11.流結(jié)束符在比特流中的最后。
?
------------------------------
H.264的NAL層處理
------------------------------
H264以NALU(NAL unit)為單位來(lái)支持編碼數(shù)據(jù)在基于分組交換技術(shù)網(wǎng)絡(luò)中傳輸。
NALU定義了可用于基于分組和基于比特流系統(tǒng)的基本格式,同時(shí)給出頭信息,從而提供了視頻編碼和外部世界的接口。
H264編碼過(guò)程中的三種不同的數(shù)據(jù)形式:
SODB 數(shù)據(jù)比特串-->最原始的編碼數(shù)據(jù),即VCL數(shù)據(jù);
RBSP 原始字節(jié)序列載荷-->在SODB的后面填加了結(jié)尾比特(RBSP trailing bits 一個(gè)bit“1”)若干比特“0”,以便字節(jié)對(duì)齊;
EBSP 擴(kuò)展字節(jié)序列載荷-->在RBSP基礎(chǔ)上填加了仿校驗(yàn)字節(jié)(0X03)它的原因是: 在NALU加到Annexb上時(shí),需要添加每組NALU之前的開(kāi)始碼StartCodePrefix,如果該NALU對(duì)應(yīng)的slice為一幀的開(kāi)始則用4位字節(jié)表示,ox00000001,否則用3位字節(jié)表示ox000001(是一幀的一部分)。另外,為了使NALU主體中不包括與開(kāi)始碼相沖突的,在編碼時(shí),每遇到兩個(gè)字節(jié)連續(xù)為0,就插入一個(gè)字節(jié)的0x03。解碼時(shí)將0x03去掉。也稱為脫殼操作。
編碼處理過(guò)程:
1.??將VCL層輸出的SODB封裝成nal_unit,?NALU是一個(gè)通用封裝格式,可以適用于有序字節(jié)流方式和IP包交換方式。
2.??針對(duì)不同的傳送網(wǎng)絡(luò)(電路交換|包交換),將nal_unit封裝成針對(duì)不同網(wǎng)絡(luò)的封裝格式(比如把nalu封裝成rtp包)。
---------------------------------------------------
處理過(guò)程一,VCL數(shù)據(jù)封裝成NALU
---------------------------------------------------
VCL層輸出的比特流SODB(String Of Data Bits),到nal_unit之間,經(jīng)過(guò)了以下三步處理:
1.SODB字節(jié)對(duì)齊處理后封裝成RBSP(Raw Byte Sequence Payload)。
2.為防止RBSP的字節(jié)流與有序字節(jié)流傳送方式下的SCP(start_code_prefix_one_3bytes,0x000001)出現(xiàn)字節(jié)競(jìng)爭(zhēng)情形,循環(huán)檢測(cè)RBSP前三個(gè)字節(jié),在出現(xiàn)字節(jié)競(jìng)爭(zhēng)時(shí)在第三字節(jié)前加入emulation_prevention_three_byte(0x03),具體方法:?
nal_unit( NumBytesInNALunit ) {
forbidden_zero_bit
nal_ref_idc
nal_unit_type
NumBytesInRBSP = 0
for( i = 1; i < NumBytesInNALunit; i++ ) {
if( i + 2 < NumBytesInNALunit && next_bits( 24 ) = = 0x000003 ) {
rbsp_byte[ NumBytesInRBSP++ ]
rbsp_byte[ NumBytesInRBSP++ ]
i += 2
emulation_prevention_three_byte /* equal to 0x03 */
} else
rbsp_byte[ NumBytesInRBSP++ ]
}
}
3.?防字節(jié)競(jìng)爭(zhēng)處理后的RBSP再加一個(gè)字節(jié)的header(forbidden_zero_bit+ nal_ref_idc+ nal_unit_type),封裝成nal_unit.?
------------------------------------------------
處理過(guò)程二,NALU的RTP打包
------------------------------------------------
一、NALU打包成RTP的方式有三種:
1. 單一 NAL 單元模式
???? 即一個(gè) RTP 包僅由一個(gè)完整的 NALU 組成. 這種情況下 RTP NAL 頭類型字段和原始的 H.264的
NALU 頭類型字段是一樣的.
2. 組合封包模式
??? 即可能是由多個(gè) NAL 單元組成一個(gè) RTP 包. 分別有4種組合方式: STAP-A, STAP-B, MTAP16, MTAP24.
那么這里的類型值分別是 24, 25, 26 以及 27.
3. 分片封包模式
??? 用于把一個(gè) NALU 單元封裝成多個(gè) RTP 包. 存在兩種類型 FU-A 和 FU-B. 類型值分別是 28 和 29.
?
還記得前面nal_unit_type的定義吧,0~23是給H264用的,24~31未使用,在rtp打包時(shí),如果一個(gè)NALU放在一個(gè)RTP包里,可 以使用NALU的nal_unit_type,但是當(dāng)需要把多個(gè)NALU打包成一個(gè)RTP包,或者需要把一個(gè)NALU打包成多個(gè)RTP包時(shí),就定義新的 type來(lái)標(biāo)識(shí)。
??????Type?? Packet????? Type name???????????????????????
????? ---------------------------------------------------------
????? 0????? undefined??????????????????????????????????? -
????? 1-23?? NAL unit??? Single NAL unit packet per H.264??
????? 24???? STAP-A???? Single-time aggregation packet????
????? 25???? STAP-B???? Single-time aggregation packet????
????? 26???? MTAP16??? Multi-time aggregation packet?????
????? 27???? MTAP24??? Multi-time aggregation packet?????
????? 28???? FU-A????? Fragmentation unit????????????????
????? 29???? FU-B????? Fragmentation unit?????????????????
????? 30-31? undefined???????????????????????????????????
?
二、三種打包方式的具體格式
1 .單一 NAL 單元模式
對(duì)于 NALU 的長(zhǎng)度小于 MTU 大小的包, 一般采用單一 NAL 單元模式.
對(duì)于一個(gè)原始的 H.264 NALU 單元常由 [Start Code] [NALU Header] [NALU Payload] 三部分組成, 其中 Start Code 用于標(biāo)示這是一個(gè)
NALU 單元的開(kāi)始, 必須是 "00 00 00 01" 或 "00 00 01", NALU 頭僅一個(gè)字節(jié), 其后都是 NALU 單元內(nèi)容.
打包時(shí)去除 "00 00 01" 或 "00 00 00 01" 的開(kāi)始碼, 把其他數(shù)據(jù)封包的 RTP 包即可.
?????? 0?????????????????? 1?????????????????? 2?????????????????? 3
?????? 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |F|NRI| type?? |?????????????????????????????????????????????? |
????? +-+-+-+-+-+-+-+-+?????????????????????????????????????????????? |
????? |?????????????????????????????????????????????????????????????? |
????? |?????????????? Bytes 2..n of a Single NAL unit???????????????? |
????? |?????????????????????????????????????????????????????????????? |
????? |?????????????????????????????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |?????????????????????????????? :...OPTIONAL RTP padding??????? |
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
如有一個(gè) H.264 的 NALU 是這樣的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
這是一個(gè)序列參數(shù)集 NAL 單元. [00 00 00 01] 是四個(gè)字節(jié)的開(kāi)始碼, 67 是 NALU 頭, 42 開(kāi)始的數(shù)據(jù)是 NALU 內(nèi)容.
封裝成 RTP 包將如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]
即只要去掉 4 個(gè)字節(jié)的開(kāi)始碼就可以了.
2 組合封包模式
其次, 當(dāng) NALU 的長(zhǎng)度特別小時(shí), 可以把幾個(gè) NALU 單元封在一個(gè) RTP 包中.
?????? 0?????????????????? 1?????????????????? 2?????????????????? 3
?????? 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |????????????????????????? RTP Header?????????????????????????? |
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |STAP-A NAL HDR |???????? NALU 1 Size?????????? | NALU 1 HDR??? |
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |???????????????????????? NALU 1 Data?????????????????????????? |
????? :?????????????????????????????????????????????????????????????? :
????? +?????????????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |?????????????? | NALU 2 Size?????????????????? | NALU 2 HDR??? |
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |???????????????????????? NALU 2 Data?????????????????????????? |
????? :?????????????????????????????????????????????????????????????? :
????? |?????????????????????????????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |?????????????????????????????? :...OPTIONAL RTP padding??????? |
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3 Fragmentation Units (FUs).
而當(dāng) NALU 的長(zhǎng)度超過(guò) MTU 時(shí), 就必須對(duì) NALU 單元進(jìn)行分片封包. 也稱為 Fragmentation Units (FUs).
?????? 0?????????????????? 1?????????????????? 2?????????????????? 3
?????? 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? | FU indicator |?? FU header?? |?????????????????????????????? |
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+?????????????????????????????? |
????? |?????????????????????????????????????????????????????????????? |
????? |???????????????????????? FU payload??????????????????????????? |
????? |?????????????????????????????????????????????????????????????? |
????? |?????????????????????????????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? |?????????????????????????????? :...OPTIONAL RTP padding??????? |
????? +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
????? Figure 14. RTP payload format for FU-A
?? FU indicator有以下格式:
????? +---------------+
????? |0|1|2|3|4|5|6|7|
????? +-+-+-+-+-+-+-+-+
????? |F|NRI|? Type?? |
????? +---------------+
?? FU指示字節(jié)的類型域 Type=28表示FU-A。。NRI域的值必須根據(jù)分片NAL單元的NRI域的值設(shè)置。
?
?? FU header的格式如下:
????? +---------------+
????? |0|1|2|3|4|5|6|7|
????? +-+-+-+-+-+-+-+-+
????? |S|E|R|? Type?? |
????? +---------------+
?? S: 1 bit
?? 當(dāng)設(shè)置成1,開(kāi)始位指示分片NAL單元的開(kāi)始。當(dāng)跟隨的FU荷載不是分片NAL單元荷載的開(kāi)始,開(kāi)始位設(shè)為0。
?? E: 1 bit
?? 當(dāng)設(shè)置成1, 結(jié)束位指示分片NAL單元的結(jié)束,即, 荷載的最后字節(jié)也是分片NAL單元的最后一個(gè)字節(jié)。當(dāng)跟隨的FU荷載不是分片NAL單元的最后分片,結(jié)束位設(shè)置為0。
?? R: 1 bit
?? 保留位必須設(shè)置為0,接收者必須忽略該位。
?? Type: 5 bits
三、拆包和解包
拆包:當(dāng)編碼器在編碼時(shí)需要將原有一個(gè)NAL按照FU-A進(jìn)行分片,原有的NAL的單元頭與分片后的FU-A的單元頭有如下關(guān)系:
原始的NAL頭的前三位為FU indicator的前三位,原始的NAL頭的后五位為FU header的后五位,FU indicator與FU header的剩余位數(shù)根據(jù)實(shí)際情況決定。
?
解包:當(dāng)接收端收到FU-A的分片數(shù)據(jù),需要將所有的分片包組合還原成原始的NAl包時(shí),FU-A的單元頭與還原后的NAL的關(guān)系如下:
還原后的NAL頭的八位是由FU indicator的前三位加FU header的后五位組成,即:
nal_unit_type = (fu_indicator & 0xe0) | (fu_header & 0x1f)
四、代碼實(shí)現(xiàn)
從RTP包里面得到H264視頻數(shù)據(jù)的方法:
?
??//?功能:解碼RTP?H.264視頻
?//?參數(shù):1.RTP包緩沖地址?2.RTP包數(shù)據(jù)大小?3.H264輸出地址?4.輸出數(shù)據(jù)大小
?//?返回:true:表示一幀結(jié)束??false:FU-A分片未結(jié)束或幀未結(jié)束?
?#define??RTP_HEADLEN?12?
?bool??UnpackRTPH264(?void???*??bufIn,??int??len,???void?**??pBufOut,???int???*??pOutLen)
??{
?????*?pOutLen??=???0?;
?????if??(len??<??RTP_HEADLEN)
??????{
?????????return???false?;
????}?
?
????unsigned??char?*??src??=??(unsigned??char?*?)bufIn??+??RTP_HEADLEN;
????unsigned??char??head1??=???*?src;?//?獲取第一個(gè)字節(jié)?
?????unsigned??char??head2??=???*?(src?+?1?);?//?獲取第二個(gè)字節(jié)?
?????unsigned??char??nal??=??head1??&???0x1f?;?//?獲取FU?indicator的類型域,?
?????unsigned??char??flag??=??head2??&???0xe0?;?//?獲取FU?header的前三位,判斷當(dāng)前是分包的開(kāi)始、中間或結(jié)束?
?????unsigned??char??nal_fua??=??(head1??&???0xe0?)??|??(head2??&???0x1f?);?//?FU_A?nal?
??????bool??bFinishFrame??=???false?;
?????if??(nal?==?0x1c?)?//?判斷NAL的類型為0x1c=28,說(shuō)明是FU-A分片?
????????{?//?fu-a?
??????????if??(flag?==?0x80?)?//?開(kāi)始?
????????????{
?????????????*?pBufOut??=??src?-?3?;
?????????????*?((?int?*?)(?*?pBufOut))??=???0x01000000??;?//?zyf:大模式會(huì)有問(wèn)題?
??????????????*?((?char?*?)(?*?pBufOut)?+?4?)??=??nal_fua;
?????????????*??pOutLen??=??len??-??RTP_HEADLEN??+???3?;
????????}?
?????????else???if?(flag?==?0x40?)?//?結(jié)束?
????????????{
?????????????*?pBufOut??=??src?+?2?;
?????????????*??pOutLen??=??len??-??RTP_HEADLEN??-???2?;
????????}?
?????????else?//?中間?
????????????{
?????????????*?pBufOut??=??src?+?2?;
?????????????*??pOutLen??=??len??-??RTP_HEADLEN??-???2?;
????????}?
????}?
?????else?//?單包數(shù)據(jù)?
????????{
?????????*?pBufOut??=??src?-?4?;
?????????*?((?int?*?)(?*?pBufOut))??=???0x01000000?;?//?zyf:大模式會(huì)有問(wèn)題?
??????????*??pOutLen??=??len??-??RTP_HEADLEN??+???4?;
????}?
?
????unsigned??char?*??bufTmp??=??(unsigned??char?*?)bufIn;
?????if??(bufTmp[?1?]??&???0x80?)
??????{
????????bFinishFrame??=???true?;?//?rtp?mark?
?????}?
?????else?
???????{
????????bFinishFrame??=???false?;
????}?
?????return??bFinishFrame;
}???
從RTP包里面得到AAC音頻數(shù)據(jù)的方法:
//功能:解RTP?AAC音頻包,聲道和采樣頻率必須知道。
//參數(shù):1.RTP包緩沖地址?2.RTP包數(shù)據(jù)大小?3.H264輸出地址?4.輸出數(shù)據(jù)大小
//返回:true:表示一幀結(jié)束??false:幀未結(jié)束?一般AAC音頻包比較小,沒(méi)有分片。
bool?UnpackRTPAAC(void?*?bufIn,?int?recvLen,?void**?pBufOut,??int*?pOutLen)
{
????unsigned?char*??bufRecv?=?(unsigned?char*)bufIn;
????//char?strFileName[20];
????
????unsigned?char?ADTS[]?=?{0xFF,?0xF1,?0x00,?0x00,?0x00,?0x00,?0xFC};?
????int?audioSamprate?=?32000;//音頻采樣率
????int?audioChannel?=?2;//音頻聲道?1或2
????int?audioBit?=?16;//16位?固定
????switch(audioSamprate)
????{
????case??16000:
????????ADTS[2]?=?0x60;
????????break;
????case??32000:
????????ADTS[2]?=?0x54;
????????break;
????case??44100:
????????ADTS[2]?=?0x50;
????????break;
????case??48000:
????????ADTS[2]?=?0x4C;
????????break;
????case??96000:
????????ADTS[2]?=?0x40;
????????break;
????default:
????????break;
????}
????ADTS[3]?=?(audioChannel==2)?0x80:0x40;
????int?len?=?recvLen?-?16?+?7;
????len?<<=?5;//8bit?*?2?-?11?=?5(headerSize?11bit)
????len?|=?0x1F;//5?bit????1????????????
????ADTS[4]?=?len>>8;
????ADTS[5]?=?len?&?0xFF;
????*pBufOut?=?(char*)bufIn+16-7;
????memcpy(*pBufOut,?ADTS,?sizeof(ADTS));
????*pOutLen?=?recvLen?-?16?+?7;
????unsigned?char*?bufTmp?=?(unsigned?char*)bufIn;
????bool?bFinishFrame?=?false;
????if?(bufTmp[1]?&?0x80)
????{
????????//DebugTrace::D("Marker");
????????bFinishFrame?=?true;
????}
????else
????{
????????bFinishFrame?=?false;
????}
????return?true;
}
編碼器輸出格式
----------------------------------
總的來(lái)說(shuō)H264的碼流的打包方式有兩種,一種為annex-b byte stream format的格式,這個(gè)是絕大部分編碼器的默認(rèn)輸出格式,就是每個(gè)幀的開(kāi)頭的3~4個(gè)字節(jié)是H264的start_code,0x00000001或者0x000001。
另一種是原始的NAL打包格式,就是開(kāi)始的若干字節(jié)(1,2,4字節(jié))是NAL的長(zhǎng)度,而不是start_code,此時(shí)必須借助某個(gè)全局的數(shù)據(jù)來(lái)獲得編碼器的profile,level,PPS,SPS等信息才可以解碼。
?
@之前還疑惑過(guò)PPS 和SPS是哪里來(lái)的,答案是編碼器給出的。
?
------------------------------
編碼數(shù)據(jù)流分析
------------------------------
首先明確一下,NAL數(shù)據(jù)流的組成:開(kāi)始碼+NAL頭(forbidden_bit+nal_ref+nal_type)+RBSP
下面對(duì)一段H264編碼數(shù)據(jù)流進(jìn)行分析。
?
00 00 00 01 67 42 00 1E 99 A0 B1 31 00 00 00 01
H264的數(shù)據(jù)流分為兩種,一種是NAL UNIT stream(RTP),一種是 bits stream,
兩者可以互相轉(zhuǎn)換。我們分析的這個(gè)是 bit stream,根據(jù)AnnexB
00 00 00 01 67 42 00 1E 99 A0 B1 31 是 一個(gè)NAL,在兩個(gè)00 00 00 01之間
0110 0111 0100 0010 0000 0000 0001 1110 1001 1001 1010 0000 1011 0001 0011 0001
forbidden_zero_bit(1)= 0//網(wǎng)絡(luò)傳輸正確
nal_ref_idc(2)= 11//參考值為3
nal_unit_type(5) = 0 0111:seq_parameter_set_rbsp( )//7,序列參數(shù)集
說(shuō)明這個(gè)NALU的RBSP里裝的是SPS數(shù)據(jù) ,所以 processSPS
profile_idc(8):42:0100 0010
constraint_set0_flag(1):0
constraint_set1_flag(1):0
constraint_set2_flag(1):0
constraint_set3_flag(1):0
reserved_zero_4bits(4):0
level_idc(8):1E
seq_parameter_set_id(UE(V)):
ue(v): unsigned integer Exp-Golomb-coded syntax element with the left bit first. The parsing process for this descriptor is specified in subclause9.1
uvlC: 1001:根據(jù)Table9.1 , value= 0,只占1bit.
根據(jù)profile_idc忽略掉一部分。
log2_max_frame_num_minus4(ue(v): 001 1001,len = 5,value= 5
pic_order_cnt_type(ue(v)):01 1010,len = 3,value = 2
根據(jù)pic_order_cnt_type忽略幾個(gè)參數(shù)
num_ref_frames(ue):010,len = 3,value = 1
0000?1011 0001 0011 0001
gaps_in_frame_num_value_allowed_flag(1) = 0
pic_width_in_mbs_minus1(ue):000 1011 ,len = 7,value = 10;
pic_height_in_map_units_minus1(ue):0001 001,len = 7,value = 8
frame_mbs_only_flag(1) = 1
忽略1
direct_8x8_inference_flag(1):0
忽略
vui_parameters_present_flag(1):0
忽略
NALU結(jié)束
?
68 CE 38 80 00 00 00 01
0110 1000
forbidden_zero_bit(1)= 0
nal_ref_idc(2)= 11
nal_unit_type(5) =01000:pic_parameter_set_rbsp( ),7.3.2.2//8圖像參數(shù)集
1100
pic_parameter_set_id (ue)=0
seq_parameter_set_id(ue)=0
entropy_coding_mode_flag(1) :0, 重要的flag,0表示編碼Exp-Golomb coded and CAVLC,1表示CABAC
pic_order_present_flag(1):0
1110
num_slice_groups_minus1(ue):0
忽略
num_ref_idx_l0_active_minus1(ue):0
num_ref_idx_l1_active_minus1(ue):0
weighted_pred_flag(1);0
0011 1000 1000 0000
weighted_bipred_idc(2):00
pic_init_qp_minus26 /* relative to 26 */(se):0
pic_init_qs_minus26?/* relative to 26 */(se):0
chroma_qp_index_offset(se):0
deblocking_filter_control_present_flag(1);0
constrained_intra_pred_flag(1):0
redundant_pic_cnt_present_flag(1):0
忽略
NALU結(jié)束
?
65 88 80 21 71 27 1B 88…….3888*16 byte
65:0110 0101
forbidden_zero_bit(1)= 0
nal_ref_idc(2)= 11
nal_unit_type(5) =0 0101:slice_layer_without_partitioning_rbsp( )//IDR幀
Slice
Slice_Header:
first_mb_in_slice(ue):0
slice_type(ue):000 1000 = 7
pic_parameter_set_id(ue) = 0
80 21:000 0000 0010 0001
frame_num(u(v): frame_num?is used as an identifier for pictures and shall be represented by log2_max_frame_num_minus4 + 4 bits,9 bits = 0
忽略
if( nal_unit_type = = 5 ) //IDR frame
idr_pic_id(u(e)):0
忽略N多
ref_pic_list_reordering( ) 見(jiàn)7。3。3。1忽略,Islice,SI slice,B slice
nal_ref_idc =11 所以dec_ref_pic_marking( )
nal_unit_type = 5,所以
no_output_of_prior_pics_flag(1):0
long_term_reference_flag(1):0
忽略
。。71 27
001 0111 0001 0010 0111
slice_qp_delta(se(v):001 01,4:-2
忽略
?
slice_data( ):7.3.4
對(duì)I-Slice:忽略N多
進(jìn)入if( moreDataFlag ) { if( MbaffFrameFlag && ( CurrMbAddr % 2 = = 0 | | ( CurrMbAddr % 2 = = 1 && prevMbSkipped ) ) )mb_field_decoding_flag
macroblock_layer( )}
mb_field_decoding_flag忽略
macroblock_layer( )
mb_type(ue(v):0
mb_pred( mb_type )
prev_intra4x4_pred_mode_flag[?luma4x4BlkIdx?](1bit,對(duì)babac是ae(v)):1
1 27:0001 0010 0111
prev_intra4x4_pred_mode_flag[?1?] : 0001,0,001
0010 0111
prev_intra4x4_pred_mode_flag[?2?] : 0010,0,010
prev_intra4x4_pred_mode_flag[?3] : 0111,0,111
……16個(gè)
1b 88 00 3e cf.
intra_chroma_pred_mode(ue(v)) :最后的一個(gè)1bit:0
?
接下來(lái)是macroblock_layer的coded_block_pattern和run level,既系數(shù)
c0 06 ad a0 18
1100 0000 0000 0110 1010 0000 0001 1000
coded_block_pattern(me(v):0,根據(jù)T= 47,0x2f
mb_qp_delta(se(v):):0 len =1
residual( )見(jiàn)7.3.5.3
residual_block( LumaLevel[ i8x8 * 4 + i4x4 ], 16 )
coeff_token(ce(v): 00 0000 0000 0110 1
nc = 0(left block and top block 相關(guān)的):
len: { // 0702
{ 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16},
{ 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16},
{ 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16},
{ 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16},
},
{
{ 2, 6, 6, 7, 8, 8, 9,11,11,12,12,12,13,13,13,14,14},
{ 0, 2, 5, 6, 6, 7, 8, 9,11,11,12,12,13,13,14,14,14},
{ 0, 0, 3, 6, 6, 7, 8, 9,11,11,12,12,13,13,13,14,14},
{ 0, 0, 0, 4, 4, 5, 6, 6, 7, 9,11,11,12,13,13,13,14},
},
{
{ 4, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9,10,10,10,10},
{ 0, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9,10,10,10},
{ 0, 0, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10},
{ 0, 0, 0, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 9,10,10,10},
},
code:
{ 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7,4},
{ 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10,6},
{ 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9,5},
{ 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12,8},
},
{
{ 3,11, 7, 7, 7, 4, 7,15,11,15,11, 8,15,11, 7, 9,7},
{ 0, 2, 7,10, 6, 6, 6, 6,14,10,14,10,14,10,11, 8,6},
{ 0, 0, 3, 9, 5, 5, 5, 5,13, 9,13, 9,13, 9, 6,10,5},
{ 0, 0, 0, 5, 4, 6, 8, 4, 4, 4,12, 8,12,12, 8, 1,4},
},
{
{15,15,11, 8,15,11, 9, 8,15,11,15,11, 8,13, 9, 5,1},
{ 0,14,15,12,10, 8,14,10,14,14,10,14,10, 7,12, 8,4},
{ 0, 0,13,14,11, 9,13, 9,13,10,13, 9,13, 9,11, 7,3},
{ 0, 0, 0,12,11,10, 9, 8,13,12,12,12, 8,12,10, 6,2},
},
根據(jù)表查的:
code = 13,len = 15,i= 12,j=2
所以numcoeff = 12,numtrailingones = 2
010 0000 0001 1000: totalzeros:根據(jù)numcoeff
int lentab[TOTRUN_NUM][16] =
{
?
{ 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
{ 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
{ 4,3,3,3,4,4,3,3,4,5,5,6,5,6},
{ 5,3,4,4,3,3,3,4,3,4,5,5,5},
{ 4,4,4,3,3,3,3,3,4,5,4,5},
{ 6,5,3,3,3,3,3,3,4,3,6},
{ 6,5,3,3,3,2,3,4,3,6},
{ 6,4,5,3,2,2,3,3,6},
{ 6,6,4,2,2,3,2,5},
{ 5,5,3,2,2,2,4},
{ 4,4,3,3,1,3},
{ 4,4,2,1,3},?numcoeff開(kāi)始
{ 3,3,1,2},
{ 2,2,1},
{ 1,1},
};
?
int codtab[TOTRUN_NUM][16] =
{
{1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
{7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
{5,7,6,5,4,3,4,3,2,3,2,1,1,0},
{3,7,5,4,6,5,4,3,3,2,2,1,0},
{5,4,3,7,6,5,4,3,2,1,1,0},
{1,1,7,6,5,4,3,2,1,1,0},
{1,1,5,4,3,3,2,1,1,0},
{1,1,1,3,3,2,2,1,0},
{1,0,1,3,2,1,1,1,},
{1,0,1,3,2,1,1,},
{0,1,1,2,1,3},
{0,1,1,1,1},?numcoeff開(kāi)始
{0,1,1,1},
{0,1,1},
{0,1},
};
Code = 1,len = 2,i=2,j = 0,?totzeros = 2
Read run:?0 0000 0001 1000根據(jù)totzeros = 2
int lentab[TOTRUN_NUM][16] =
{
{1,1},
{1,2,2},
{2,2,2,2},
{2,2,2,3,3},
{2,2,3,3,3,3},
{2,3,3,3,3,3,3},
{3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
};
?
int codtab[TOTRUN_NUM][16] =
{
{1,0},
{1,1,0},
{3,2,1,0},
{3,2,1,1,0},
{3,2,3,2,1,0},
{3,0,1,3,2,5,4},
{7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
Code = 1,len =1,I = 0,j = 0
?
0.1.1 Slice data syntax
?
| slice_data( ) { | C | Descriptor |
| if( entropy_coding_mode_flag ) | ? | ? |
| while( !byte_aligned( ) ) | ? | ? |
| cabac_alignment_one_bit | 2 | f(1) |
| CurrMbAddr = first_mb_in_slice * ( 1 + MbaffFrameFlag ) | ? | ? |
| moreDataFlag = 1 | ? | ? |
| prevMbSkipped = 0 | ? | ? |
| do { | ? | ? |
| if( slice_type != I && slice_type != SI ) | ? | ? |
| if( !entropy_coding_mode_flag ) { | ? | ? |
| mb_skip_run | 2 | ue(v) |
| prevMbSkipped = ( mb_skip_run > 0 ) | ? | ? |
| for( i=0; i<mb_skip_run; i++ ) | ? | ? |
| CurrMbAddr = NextMbAddress( CurrMbAddr ) | ? | ? |
| moreDataFlag = more_rbsp_data( ) | ? | ? |
| } else { | ? | ? |
| mb_skip_flag | 2 | ae(v) |
| moreDataFlag = !mb_skip_flag | ? | ? |
| } | ? | ? |
| if( moreDataFlag ) { | ? | ? |
| if( MbaffFrameFlag && ( CurrMbAddr % 2 = = 0 | | ( CurrMbAddr % 2 = = 1 && prevMbSkipped ) ) ) | ? | ? |
| mb_field_decoding_flag | 2 | u(1) | ae(v) |
| macroblock_layer( ) | 2 | 3 | 4 | ? |
| } | ? | ? |
| if( !entropy_coding_mode_flag ) | ? | ? |
| moreDataFlag = more_rbsp_data( ) | ? | ? |
| else { | ? | ? |
| if( slice_type != I && slice_type != SI ) | ? | ? |
| prevMbSkipped = mb_skip_flag | ? | ? |
| if( MbaffFrameFlag && CurrMbAddr % 2 = = 0 ) | ? | ? |
| moreDataFlag = 1 | ? | ? |
| else { | ? | ? |
| end_of_slice_flag | 2 | ae(v) |
| moreDataFlag = !end_of_slice_flag | ? | ? |
| } | ? | ? |
| } | ? | ? |
| CurrMbAddr = NextMbAddress( CurrMbAddr ) | ? | ? |
| } while( moreDataFlag ) | ? | ? |
| } | ? | ? |
?
?
?
?
se(v) : CABAC正式介紹。根據(jù)Table 9 5 – coeff_token mapping to TotalCoeff( coeff_token ) and TrailingOnes( coeff_token )。
chroma_format_idc 無(wú)
?
?
?
Table9?1 – Bit strings with “prefix” and “suffix” bits and assignment to codeNum ranges (informative)
| Bit string form | Range of codeNum |
| 1 | 0 |
| 0 1 x0 | 1-2 |
| 0 0 1 x1?x0 | 3-6 |
| 0 0 0 1 x2?x1?x0 | 7-14 |
| 0 0 0 0 1 x3?x2?x1?x0 | 15-30 |
| 0 0 0 0 0 1 x4?x3?x2?x1?x0 | 31-62 |
| … | … |
?
0.1.1.1 Slice layer without partitioning RBSP syntax
?
| slice_layer_without_partitioning_rbsp( ) { | C | Descriptor |
| slice_header( ) | 2 | ? |
| slice_data( ) /* all categories of slice_data( ) syntax */ | 2 | 3 | 4 | ? |
| rbsp_slice_trailing_bits( ) | 2 | ? |
| } | ? | ? |
?
?
?
?
0.1.1.2 Sequence parameter set RBSP syntax
?
| seq_parameter_set_rbsp( ) { | C | Descriptor |
| profile_idc | 0 | u(8) |
| constraint_set0_flag | 0 | u(1) |
| constraint_set1_flag | 0 | u(1) |
| constraint_set2_flag | 0 | u(1) |
| constraint_set3_flag | 0 | u(1) |
| reserved_zero_4bits?/* equal to 0 */ | 0 | u(4) |
| level_idc | 0 | u(8) |
| seq_parameter_set_id | 0 | ue(v) |
| if( profile_idc = = 100 | | profile_idc = = 110 | | | ? | ? |
| chroma_format_idc | 0 | ue(v) |
| if( chroma_format_idc = = 3 ) | ? | ? |
| residual_colour_transform_flag | 0 | u(1) |
| bit_depth_luma_minus8 | 0 | ue(v) |
| bit_depth_chroma_minus8 | 0 | ue(v) |
| qpprime_y_zero_transform_bypass_flag | 0 | u(1) |
| seq_scaling_matrix_present_flag | 0 | u(1) |
| if( seq_scaling_matrix_present_flag ) | ? | ? |
| for( i = 0; i < 8; i++ ) { | ? | ? |
| seq_scaling_list_present_flag[?i] | 0 | u(1) |
| if( seq_scaling_list_present_flag[ i ] ) | ? | ? |
| if( i < 6 ) | ? | ? |
| scaling_list( ScalingList4x4[ i ], 16, | 0 | ? |
| else | ? | ? |
| scaling_list( ScalingList8x8[ i – 6 ], 64, | 0 | ? |
| } | ? | ? |
| } | ? | ? |
| log2_max_frame_num_minus4 | 0 | ue(v) |
| pic_order_cnt_type | 0 | ue(v) |
| if( pic_order_cnt_type = = 0 ) | ? | ? |
| log2_max_pic_order_cnt_lsb_minus4 | 0 | ue(v) |
| else if( pic_order_cnt_type = = 1 ) { | ? | ? |
| delta_pic_order_always_zero_flag | 0 | u(1) |
| offset_for_non_ref_pic | 0 | se(v) |
| offset_for_top_to_bottom_field | 0 | se(v) |
| num_ref_frames_in_pic_order_cnt_cycle | 0 | ue(v) |
| for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) | ? | ? |
| offset_for_ref_frame[?i?] | 0 | se(v) |
| } | ? | ? |
| num_ref_frames | 0 | ue(v) |
| gaps_in_frame_num_value_allowed_flag | 0 | u(1) |
| pic_width_in_mbs_minus1 | 0 | ue(v) |
| pic_height_in_map_units_minus1 | 0 | ue(v) |
| frame_mbs_only_flag | 0 | u(1) |
| if( !frame_mbs_only_flag ) | ? | ? |
| mb_adaptive_frame_field_flag | 0 | u(1) |
| direct_8x8_inference_flag | 0 | u(1) |
| frame_cropping_flag | 0 | u(1) |
| if( frame_cropping_flag ) { | ? | ? |
| frame_crop_left_offset | 0 | ue(v) |
| frame_crop_right_offset | 0 | ue(v) |
| frame_crop_top_offset | 0 | ue(v) |
| frame_crop_bottom_offset | 0 | ue(v) |
| } | ? | ? |
| vui_parameters_present_flag | 0 | u(1) |
| if( vui_parameters_present_flag ) | ? | ? |
| vui_parameters( ) | 0 | ? |
| rbsp_trailing_bits( ) | 0 | ? |
| } | ? | ? |
?
?
Table7?1 – NAL unit type codes
| nal_unit_type | Content of NAL unit and RBSP syntax structure | C |
| 0 | Unspecified | ? |
| 1 | Coded slice of a non-IDR picture | 2, 3, 4 |
| 2 | Coded slice data partition A? | 2 |
| 3 | Coded slice data partition B? | 3 |
| 4 | Coded slice data partition C? | 4 |
| 5 | Coded slice of an IDR picture | 2, 3 |
| 6 | Supplemental enhancement information (SEI) | 5 |
| 7 | Sequence parameter set | 0 |
| 8 | Picture parameter set | 1 |
| 9 | Access unit delimiter | 6 |
| 10 | End of sequence | 7 |
| 11 | End of stream | 8 |
| 12 | Filler data | 9 |
| 13 | Sequence parameter set extension | 10 |
| 14..18 | Reserved | ? |
| 19 | Coded slice of an auxiliary coded picture without partitioning | 2, 3, 4 |
| 20..23 | Reserved | ? |
| 24..31 | Unspecified | ? |
?
?
?
?
| byte_stream_nal_unit( NumBytesInNALunit ) { | C | Descriptor |
| while( next_bits( 24 ) != 0x000001 && | ? | ? |
| leading_zero_8bits?/* equal to 0x00 */ | ? | f(8) |
| if( next_bits( 24 ) != 0x000001 ) | ? | ? |
| zero_byte?/* equal to 0x00 */ | ? | f(8) |
| start_code_prefix_one_3bytes?/* equal to 0x000001 */ | ? | f(24) |
| nal_unit( NumBytesInNALunit) | ? | ? |
| while( more_data_in_byte_stream( ) && | ? | ? |
| trailing_zero_8bits?/* equal to 0x00 */ | ? | f(8) |
| } | ? | ? |
?
?
?
?
0.1.1.3 Picture parameter set RBSP syntax
?
| pic_parameter_set_rbsp( ) { | C | Descriptor |
| pic_parameter_set_id | 1 | ue(v) |
| seq_parameter_set_id | 1 | ue(v) |
| entropy_coding_mode_flag | 1 | u(1) |
| pic_order_present_flag | 1 | u(1) |
| num_slice_groups_minus1 | 1 | ue(v) |
| if( num_slice_groups_minus1 > 0 ) { | ? | ? |
| slice_group_map_type | 1 | ue(v) |
| if( slice_group_map_type = = 0 ) | ? | ? |
| for( iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++ ) | ? | ? |
| run_length_minus1[?iGroup?] | 1 | ue(v) |
| else if( slice_group_map_type = = 2 ) | ? | ? |
| for( iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ ) { | ? | ? |
| top_left[?iGroup?] | 1 | ue(v) |
| bottom_right[?iGroup?] | 1 | ue(v) |
| } | ? | ? |
| else if( slice_group_map_type = = 3 | |? | ? | ? |
| slice_group_change_direction_flag | 1 | u(1) |
| slice_group_change_rate_minus1 | 1 | ue(v) |
| } else if( slice_group_map_type = = 6 ) { | ? | ? |
| pic_size_in_map_units_minus1 | 1 | ue(v) |
| for( i = 0; i <= pic_size_in_map_units_minus1; i++ ) | ? | ? |
| slice_group_id[?i?] | 1 | u(v) |
| } | ? | ? |
| } | ? | ? |
| num_ref_idx_l0_active_minus1 | 1 | ue(v) |
| num_ref_idx_l1_active_minus1 | 1 | ue(v) |
| weighted_pred_flag | 1 | u(1) |
| weighted_bipred_idc | 1 | u(2) |
| pic_init_qp_minus26 /* relative to 26 */ | 1 | se(v) |
| pic_init_qs_minus26?/* relative to 26 */ | 1 | se(v) |
| chroma_qp_index_offset | 1 | se(v) |
| deblocking_filter_control_present_flag | 1 | u(1) |
| constrained_intra_pred_flag | 1 | u(1) |
| redundant_pic_cnt_present_flag | 1 | u(1) |
| if( more_rbsp_data( ) ) { | ? | ? |
| transform_8x8_mode_flag | 1 | u(1) |
| pic_scaling_matrix_present_flag | 1 | u(1) |
| if( pic_scaling_matrix_present_flag ) | ? | ? |
| for( i = 0; i < 6 + 2* transform_8x8_mode_flag; i++ ) { | ? | ? |
| pic_scaling_list_present_flag[?i] | 1 | u(1) |
| if( pic_scaling_list_present_flag[ i ] ) | ? | ? |
| if( i < 6 ) | ? | ? |
| scaling_list( ScalingList4x4[ i ], 16, | 1 | ? |
| else | ? | ? |
| scaling_list( ScalingList8x8[ i – 6 ], 64, | 1 | ? |
| } | ? | ? |
| second_chroma_qp_index_offset | 1 | se(v) |
| } | ? | ? |
| rbsp_trailing_bits( ) | 1 | ? |
| } | ? | ? |
?
0.1.2 Slice header syntax
?
| slice_header( ) { | C | Descriptor |
| first_mb_in_slice | 2 | ue(v) |
| slice_type | 2 | ue(v) |
| pic_parameter_set_id | 2 | ue(v) |
| frame_num | 2 | u(v) |
| if( !frame_mbs_only_flag ) { | ? | ? |
| field_pic_flag | 2 | u(1) |
| if( field_pic_flag ) | ? | ? |
| bottom_field_flag | 2 | u(1) |
| } | ? | ? |
| if( nal_unit_type = = 5 ) | ? | ? |
| idr_pic_id | 2 | ue(v) |
| if( pic_order_cnt_type = = 0 ) { | ? | ? |
| pic_order_cnt_lsb | 2 | u(v) |
| if( pic_order_present_flag && !field_pic_flag ) | ? | ? |
| delta_pic_order_cnt_bottom | 2 | se(v) |
| } | ? | ? |
| if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) { | ? | ? |
| delta_pic_order_cnt[?0?] | 2 | se(v) |
| if( pic_order_present_flag && !field_pic_flag ) | ? | ? |
| delta_pic_order_cnt[?1?] | 2 | se(v) |
| } | ? | ? |
| if( redundant_pic_cnt_present_flag ) | ? | ? |
| redundant_pic_cnt | 2 | ue(v) |
| if( slice_type = = B ) | ? | ? |
| direct_spatial_mv_pred_flag | 2 | u(1) |
| if( slice_type = = P | | slice_type = = SP | | slice_type = = B ) { | ? | ? |
| num_ref_idx_active_override_flag | 2 | u(1) |
| if( num_ref_idx_active_override_flag ) { | ? | ? |
| num_ref_idx_l0_active_minus1 | 2 | ue(v) |
| if( slice_type = = B ) | ? | ? |
| num_ref_idx_l1_active_minus1 | 2 | ue(v) |
| } | ? | ? |
| } | ? | ? |
| ref_pic_list_reordering( ) | 2 | ? |
| if( ( weighted_pred_flag && ( slice_type = = P | | slice_type = = SP ) ) | | | ? | ? |
| pred_weight_table( ) | 2 | ? |
| if( nal_ref_idc != 0 ) | ? | ? |
| dec_ref_pic_marking( ) | 2 | ? |
| if( entropy_coding_mode_flag && slice_type != I && slice_type != SI ) | ? | ? |
| cabac_init_idc | 2 | ue(v) |
| slice_qp_delta | 2 | se(v) |
| if( slice_type = = SP | | slice_type = = SI ) { | ? | ? |
| if( slice_type = = SP ) | ? | ? |
| sp_for_switch_flag | 2 | u(1) |
| slice_qs_delta | 2 | se(v) |
| } | ? | ? |
| if( deblocking_filter_control_present_flag ) { | ? | ? |
| disable_deblocking_filter_idc | 2 | ue(v) |
| if( disable_deblocking_filter_idc != 1 ) { | ? | ? |
| slice_alpha_c0_offset_div2 | 2 | se(v) |
| slice_beta_offset_div2 | 2 | se(v) |
| } | ? | ? |
| } | ? | ? |
| if( num_slice_groups_minus1 > 0 && | ? | ? |
| slice_group_change_cycle | 2 | u(v) |
| } | ? | ? |
?
0.1.3 Slice data syntax
?
| slice_data( ) { | C | Descriptor |
| if( entropy_coding_mode_flag ) | ? | ? |
| while( !byte_aligned( ) ) | ? | ? |
| cabac_alignment_one_bit | 2 | f(1) |
| CurrMbAddr = first_mb_in_slice * ( 1 + MbaffFrameFlag ) | ? | ? |
| moreDataFlag = 1 | ? | ? |
| prevMbSkipped = 0 | ? | ? |
| do { | ? | ? |
| if( slice_type != I && slice_type != SI ) | ? | ? |
| if( !entropy_coding_mode_flag ) { | ? | ? |
| mb_skip_run | 2 | ue(v) |
| prevMbSkipped = ( mb_skip_run > 0 ) | ? | ? |
| for( i=0; i<mb_skip_run; i++ ) | ? | ? |
| CurrMbAddr = NextMbAddress( CurrMbAddr ) | ? | ? |
| moreDataFlag = more_rbsp_data( ) | ? | ? |
| } else { | ? | ? |
| mb_skip_flag | 2 | ae(v) |
| moreDataFlag = !mb_skip_flag | ? | ? |
| } | ? | ? |
| if( moreDataFlag ) { | ? | ? |
| if( MbaffFrameFlag && ( CurrMbAddr % 2 = = 0 | | ( CurrMbAddr % 2 = = 1 && prevMbSkipped ) ) ) | ? | ? |
| mb_field_decoding_flag | 2 | u(1) | ae(v) |
| macroblock_layer( ) | 2 | 3 | 4 | ? |
| } | ? | ? |
| if( !entropy_coding_mode_flag ) | ? | ? |
| moreDataFlag = more_rbsp_data( ) | ? | ? |
| else { | ? | ? |
| if( slice_type != I && slice_type != SI ) | ? | ? |
| prevMbSkipped = mb_skip_flag | ? | ? |
| if( MbaffFrameFlag && CurrMbAddr % 2 = = 0 ) | ? | ? |
| moreDataFlag = 1 | ? | ? |
| else { | ? | ? |
| end_of_slice_flag | 2 | ae(v) |
| moreDataFlag = !end_of_slice_flag | ? | ? |
| } | ? | ? |
| } | ? | ? |
| CurrMbAddr = NextMbAddress( CurrMbAddr ) | ? | ? |
| } while( moreDataFlag ) | ? | ? |
| } | ? | ? |
The variable MbaffFrameFlag is derived as follows.
MbaffFrameFlag = ( mb_adaptive_frame_field_flag && !field_pic_flag ) (7-22)
?
0.1.4 Macroblock layer syntax
?
| macroblock_layer( ) { | C | Descriptor |
| mb_type | 2 | ue(v) | ae(v) |
| if( mb_type = = I_PCM ) { | ? | ? |
| while( !byte_aligned( ) ) | ? | ? |
| pcm_alignment_zero_bit | 2 | f(1) |
| for( i = 0; i < 256; i++ ) | ? | ? |
| pcm_sample_luma[?i?] | 2 | u(v) |
| for( i = 0; i < 2 * MbWidthC * MbHeightC; i++ ) | ? | ? |
| pcm_sample_chroma[?i?] | 2 | u(v) |
| } else { | ? | ? |
| noSubMbPartSizeLessThan8x8Flag = 1 | ? | ? |
| if( mb_type != I_NxN && MbPartPredMode( mb_type, 0 ) != Intra_16x16 && NumMbPart( mb_type ) = = 4 ) { | ? | ? |
| sub_mb_pred( mb_type ) | 2 | ? |
| for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ ) | ? | ? |
| if( sub_mb_type[ mbPartIdx ] != B_Direct_8x8 ) { | ? | ? |
| if( NumSubMbPart( sub_mb_type[ mbPartIdx ] ) > 1 ) | ? | ? |
| noSubMbPartSizeLessThan8x8Flag = 0 | ? | ? |
| } else if( !direct_8x8_inference_flag ) | ? | ? |
| noSubMbPartSizeLessThan8x8Flag = 0 | ? | ? |
| } else { | ? | ? |
| if( transform_8x8_mode_flag && mb_type = = I_NxN ) | ? | ? |
| transform_size_8x8_flag | 2 | u(1) | ae(v) |
| mb_pred( mb_type ) | 2 | ? |
| } | ? | ? |
| if( MbPartPredMode( mb_type, 0 ) != Intra_16x16 ) { | ? | ? |
| coded_block_pattern | 2 | me(v) | ae(v) |
| if( CodedBlockPatternLuma > 0 && transform_8x8_mode_flag && mb_type != I_NxN && noSubMbPartSizeLessThan8x8Flag && ( mb_type != B_Direct_16x16 | | direct_8x8_inference_flag ) ) | ? | ? |
| transform_size_8x8_flag | 2 | u(1) | ae(v) |
| } | ? | ? |
| if( CodedBlockPatternLuma > 0 | | CodedBlockPatternChroma > 0 | | | ? | ? |
| mb_qp_delta | 2 | se(v) | ae(v) |
| residual( ) | 3 | 4 | ? |
| } | ? | ? |
| } | ? | ? |
| } | ? | ? |
?
0.1.4.1 Macroblock prediction syntax
?
| mb_pred( mb_type ) { | C | Descriptor |
| if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 | |? | ? | ? |
| if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 ) | ? | ? |
| for( luma4x4BlkIdx=0; luma4x4BlkIdx<16; luma4x4BlkIdx++ ) { | ? | ? |
| prev_intra4x4_pred_mode_flag[?luma4x4BlkIdx?] | 2 | u(1) | ae(v) |
| if( !prev_intra4x4_pred_mode_flag[?luma4x4BlkIdx?]?) | ? | ? |
| rem_intra4x4_pred_mode[?luma4x4BlkIdx?] | 2 | u(3) | ae(v) |
| } | ? | ? |
| if( MbPartPredMode( mb_type, 0 ) = = Intra_8x8 ) | ? | ? |
| for( luma8x8BlkIdx=0; luma8x8BlkIdx<4; luma8x8BlkIdx++ ) { | ? | ? |
| prev_intra8x8_pred_mode_flag[?luma8x8BlkIdx?] | 2 | u(1) | ae(v) |
| if( !prev_intra8x8_pred_mode_flag[ luma8x8BlkIdx ] ) | ? | ? |
| rem_intra8x8_pred_mode[?luma8x8BlkIdx?] | 2 | u(3) | ae(v) |
| } | ? | ? |
| if( chroma_format_idc != 0 ) | ? | ? |
| intra_chroma_pred_mode | 2 | ue(v) | ae(v) |
| } else if( MbPartPredMode( mb_type, 0 ) != Direct ) { | ? | ? |
| for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) | ? | ? |
| if( ( num_ref_idx_l0_active_minus1 > 0 | | | ? | ? |
| ref_idx_l0[?mbPartIdx?] | 2 | te(v) | ae(v) |
| for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) | ? | ? |
| if( ( num_ref_idx_l1_active_minus1 > 0 | | | ? | ? |
| ref_idx_l1[?mbPartIdx?] | 2 | te(v) | ae(v) |
| for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) | ? | ? |
| if( MbPartPredMode ( mb_type, mbPartIdx ) != Pred_L1 ) | ? | ? |
| for( compIdx = 0; compIdx < 2; compIdx++ ) | ? | ? |
| mvd_l0[?mbPartIdx?][?0?][?compIdx] | 2 | se(v) | ae(v) |
| for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) | ? | ? |
| if( MbPartPredMode( mb_type, mbPartIdx ) != Pred_L0 ) | ? | ? |
| for( compIdx = 0; compIdx < 2; compIdx++ ) | ? | ? |
| mvd_l1[?mbPartIdx?][?0?][?compIdx] | 2 | se(v) | ae(v) |
| } | ? | ? |
| } | ? | ? |
?
0.1.4.2 Residual data syntax
?
| residual( ) { | C | Descriptor |
| if( !entropy_coding_mode_flag ) | ? | ? |
| residual_block = residual_block_cavlc | ? | ? |
| else | ? | ? |
| residual_block = residual_block_cabac | ? | ? |
| if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) | ? | ? |
| residual_block( Intra16x16DCLevel, 16 ) | 3 | ? |
| for( i8x8 = 0; i8x8 < 4; i8x8++ ) /* each luma 8x8 block */ | ? | ? |
| if( !transform_size_8x8_flag | | !entropy_coding_mode_flag ) | ? | ? |
| for( i4x4 = 0; i4x4 < 4; i4x4++ ) { /* each 4x4 sub-block of block */ | ? | ? |
| if( CodedBlockPatternLuma & ( 1 << i8x8 ) ) | ? | ? |
| if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) | ? | ? |
| residual_block( Intra16x16ACLevel[ i8x8 * 4 + i4x4 ], 15 ) | 3 | ? |
| else | ? | ? |
| residual_block( LumaLevel[ i8x8 * 4 + i4x4 ], 16 ) | 3 | 4 | ? |
| else if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) | ? | ? |
| for( i = 0; i < 15; i++ ) | ? | ? |
| Intra16x16ACLevel[ i8x8 * 4 + i4x4 ][ i ] = 0 | ? | ? |
| else | ? | ? |
| for( i = 0; i < 16; i++ ) | ? | ? |
| LumaLevel[ i8x8 * 4 + i4x4 ][ i ] = 0 | ? | ? |
| if( !entropy_coding_mode_flag && transform_size_8x8_flag ) | ? | ? |
| for( i = 0; i < 16; i++ ) | ? | ? |
| LumaLevel8x8[ i8x8 ][ 4 * i + i4x4 ] = | ? | ? |
| } | ? | ? |
| else if( CodedBlockPatternLuma & ( 1 << i8x8 ) ) | ? | ? |
| residual_block( LumaLevel8x8[ i8x8 ], 64 ) | 3 | 4 | ? |
| else | ? | ? |
| for( i = 0; i < 64; i++ ) | ? | ? |
| LumaLevel8x8[ i8x8 ][ i ] = 0 | ? | ? |
| if( chroma_format_idc != 0 ) { | ? | ? |
| NumC8x8 = 4 / ( SubWidthC * SubHeightC ) | ? | ? |
| for( iCbCr = 0; iCbCr < 2; iCbCr++ ) | ? | ? |
| if( CodedBlockPatternChroma & 3 ) /* chroma DC residual present */ | ? | ? |
| residual_block( ChromaDCLevel[ iCbCr ], 4 * NumC8x8 ) | 3 | 4 | ? |
| else | ? | ? |
| for( i = 0; i < 4 * NumC8x8; i++ ) | ? | ? |
| ChromaDCLevel[ iCbCr ][ i ] = 0 | ? | ? |
| for( iCbCr = 0; iCbCr < 2; iCbCr++ ) | ? | ? |
| for( i8x8 = 0; i8x8 < NumC8x8; i8x8++ ) | ? | ? |
| for( i4x4 = 0; i4x4 < 4; i4x4++ ) | ? | ? |
| if( CodedBlockPatternChroma & 2 ) | ? | ? |
| residual_block( ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ], 15) | 3 | 4 | ? |
| else | ? | ? |
| for( i = 0; i < 15; i++ ) | ? | ? |
| ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ][ i ] = 0 | ? | ? |
| } | ? | ? |
?
?
| residual_block_cavlc( coeffLevel, maxNumCoeff ) { | C | Descriptor |
| for( i = 0; i < maxNumCoeff; i++ ) | ? | ? |
| coeffLevel[ i ] = 0 | ? | ? |
| coeff_token | 3 | 4 | ce(v) |
| if( TotalCoeff( coeff_token ) > 0 ) { | ? | ? |
| if( TotalCoeff( coeff_token ) > 10 && TrailingOnes( coeff_token ) < 3 ) | ? | ? |
| suffixLength = 1 | ? | ? |
| else | ? | ? |
| suffixLength = 0 | ? | ? |
| for( i = 0; i < TotalCoeff( coeff_token ); i++ ) | ? | ? |
| if( i < TrailingOnes( coeff_token ) ) { | ? | ? |
| trailing_ones_sign_flag | 3 | 4 | u(1) |
| level[ i ] = 1 – 2 * trailing_ones_sign_flag | ? | ? |
| } else { | ? | ? |
| level_prefix | 3 | 4 | ce(v) |
| levelCode = ( Min( 15, level_prefix ) << suffixLength ) | ? | ? |
| if( suffixLength > 0 | | level_prefix >= 14 ) { | ? | ? |
| level_suffix | 3 | 4 | u(v) |
| levelCode += level_suffix | ? | ? |
| } | ? | ? |
| if( level_prefix > = 15 && suffixLength = = 0 ) | ? | ? |
| levelCode += 15 | ? | ? |
| if( level_prefix > = 16 ) | ? | ? |
| levelCode += ( 1 << ( level_prefix – 3 ) ) – 4096 | ? | ? |
| if( i = = TrailingOnes( coeff_token ) &&? | ? | ? |
| levelCode += 2 | ? | ? |
| if( levelCode % 2 = = 0 ) | ? | ? |
| level[ i ] = ( levelCode + 2 ) >> 1 | ? | ? |
| else | ? | ? |
| level[ i ] = ( –levelCode – 1 ) >> 1 | ? | ? |
| if( suffixLength = = 0 ) | ? | ? |
| suffixLength = 1 | ? | ? |
| if( Abs( level[ i ] ) > ( 3 << ( suffixLength – 1 ) ) &&? | ? | ? |
| suffixLength++ | ? | ? |
| } | ? | ? |
| if( TotalCoeff( coeff_token ) < maxNumCoeff ) { | ? | ? |
| total_zeros | 3 | 4 | ce(v) |
| zerosLeft = total_zeros | ? | ? |
| } else | ? | ? |
| zerosLeft = 0 | ? | ? |
| for( i = 0; i < TotalCoeff( coeff_token ) – 1; i++ ) { | ? | ? |
| if( zerosLeft > 0 ) { | ? | ? |
| run_before | 3 | 4 | ce(v) |
| run[ i ] = run_before | ? | ? |
| } else | ? | ? |
| run[ i ] = 0 | ? | ? |
| zerosLeft = zerosLeft – run[ i ] | ? | ? |
| } | ? | ? |
| run[ TotalCoeff( coeff_token ) – 1 ] = zerosLeft | ? | ? |
| coeffNum = ?1 | ? | ? |
| for( i = TotalCoeff( coeff_token ) – 1; i >= 0; i-- ) { | ? | ? |
| coeffNum += run[ i ] + 1 | ? | ? |
| coeffLevel[ coeffNum ] = level[ i ] | ? | ? |
| } | ? | ? |
| } | ? | ? |
| } |
解碼器在解碼時(shí),首先逐個(gè)字節(jié)讀取NAL的數(shù)據(jù),統(tǒng)計(jì)NAL的長(zhǎng)度,然后再開(kāi)始解碼。
nal_unit( NumBytesInNALunit ) {/* NumBytesInNALunit為統(tǒng)計(jì)出來(lái)的數(shù)據(jù)長(zhǎng)度 */
forbidden_zero_bit?// forbidden_zero_bit 等于 0表示網(wǎng)絡(luò)傳輸沒(méi)有出錯(cuò)
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。
nal_unit_type?// NAL類型 指明當(dāng)前 NAL unit 的類型
NumBytesInRBSP?=?0?
/* rbsp_byte[i] RBSP 的第 i 個(gè)字節(jié)。 RBSP 指原始字節(jié)載荷,它是 NAL 單元的數(shù)據(jù)部分的封裝格式,封裝的數(shù)據(jù)來(lái)自 SODB(原始數(shù)據(jù)比特流)。SODB 是編碼后的原始數(shù)據(jù),SODB 經(jīng)封裝為 RBSP 后放入 NAL 的數(shù)據(jù)部分。下面介紹一個(gè) RBSP 的生成順序。
從 SODB 到 RBSP 的生成過(guò)程:?
- 如果 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,接下來(lái)填充 0,直到字節(jié)對(duì)齊。(填充 0 的目的也是為了字節(jié)對(duì)齊)?
3) 最后添加若干個(gè) cabac_zero_word(其值等于 0x0000)?
*/?
for( i?=?1; i?<</span> NumBytesInNALunit; i++?) {
if( i?+?2?<</span> NumBytesInNALunit&&?next_bits(24?)=?=?0x000003?) {?
/* 0x000003偽起始碼,需要?jiǎng)h除0x03這個(gè)字節(jié) */?
rbsp_byte[ NumBytesInRBSP++?]?
rbsp_byte[ NumBytesInRBSP++?]?
i?+=?2?/* 取出前兩個(gè)0x00后,跳過(guò)0x03 */?
//emulation_prevention_three_byte NAL 內(nèi)部為防止與起始碼競(jìng)爭(zhēng)而引入的填充字節(jié) ,值為 0x03。
emulation_prevention_three_byte?
}?else?
rbsp_byte[ NumBytesInRBSP++?]?/* 繼續(xù)讀取后面的字節(jié) */
}?
}
?
序列參數(shù)集(SPS)
| 句法 | C | Desc |
| seq_parameter_set_rbsp(){ | ? | ? |
| profile_idc/*指明所用的Profile */ | 0 | u(8) |
| constraint_set0_flag | 0 | u(1) |
| constraint_set1_flag | 0 | u(1) |
| constraint_set1_flag | 0 | u(1) |
| reserved_zero_5bits?/* equal to 0 */ | 0 | u(5) |
| level_idc?/*?指明所用的Level */ | 0 | u(8) |
| seq_parameter_set_id?/*指明本序列參數(shù)集的id號(hào),0-31,被圖像集引用,編碼需要產(chǎn)生新的序列集時(shí),使用新的id,而不是改變?cè)瓉?lái)參數(shù)集的內(nèi)容?*/ | 0 | ue(v) |
| log2_max_frame_num_minus4/*為讀取元素frame_num服務(wù),frame_num標(biāo)識(shí)圖像的解碼順序,frame_num的解碼函數(shù)是ue(v),其中v=log2_max_frame_num_minus4+4,該元素同時(shí)指明frame_num的最大值MaxFrameNum=2( log2_max_frame_num_minus4+4)*/ | 0 | ue(v) |
| pic_order_cnt_type?/*?指明poc的編碼方法,poc標(biāo)識(shí)圖像的播放順序,poc可以由frame_num計(jì)算,也可以顯示傳送。poc共三種計(jì)算方式?*/ | 0 | ue(v) |
| if(pic_order_cnt_type==0) | ? | ? |
| log2_max_pic_order_cnt_lsb_minus4?/*?指明變量MaxPicOrderCntLsb的值,MaxPicOrderCntLsb=2(log2_max_pic_order_cnt_lsb_minus4+4) */ | 0 | ue(v) |
| else if(pic_order_cnt_type==1){ | ? | ? |
| delta_pic_order_always_zero_flag?/*?等于1時(shí),元素delta_pic_order_cnt[0]和delta_pic_order_cnt[1]不在片頭中出現(xiàn),并且它們的默認(rèn)值是0,等于0時(shí),上述兩元素出現(xiàn)的片頭中?*/ | 0 | u(1) |
| offset_for_non_ref_pic?/*?用來(lái)計(jì)算非參考幀或場(chǎng)的poc,[-231,231-1] */ | 0 | se(v) |
| offset_for_top_to_bottom_field/*計(jì)算幀的底場(chǎng)的poc */ | 0 | se(v) |
| num_ref_frames_inpic_order_cnt_cycle?/*?用來(lái)解碼poc,[0.255] */ | 0 | ue(v) |
| for(i=0;i<num_ref_frames_inpic_order_cnt_cycle;i++) | ? | ? |
| offset_for_ref_frame[i]/*用來(lái)解碼poc,對(duì)于循環(huán)中的每個(gè)元素指定一個(gè)偏移?*/ | 0 | se(v) |
| } | ? | ? |
| num_ref_frames?/*?參考幀隊(duì)列可達(dá)到的最大長(zhǎng)度,[0,16] */ | 0 | ue(v) |
| gaps_in_frame_num_value_allowed_flag /* 為1,允許slice header中的frame_num不連續(xù) */ | 0 | u(1) |
| pic_width_inmbs_minus1?/*本元素加1,指明以宏塊為單位的圖像寬度PicWidthInMbs=pic_width_in_mbs_minus1+1 */ | 0 | ue(v) |
| pic_height_in_map_units_minus1?/*?本元素加1,指明以宏塊為單位的圖像高寬度PicHeightInMapUnitsMbs=pic_height_in_map_units_minus1+1 */ | 0 | ue(v) |
| frame_mbs_only_flag?/*?等于0表示本序列中所有圖像均為幀編碼;等于1,表示可能是幀,也可能場(chǎng)或幀場(chǎng)自適應(yīng),具體編碼方式由其它元素決定。結(jié)合前一元素:FrameHeightInMbs=(2-frame_mbs_only_flag)*PicHeightInMapUnits */ | 0 | ue(v) |
| if(frame_mbs_only_flag) | ? | ? |
| mb_adaptiv_frame_field_flag?/*?指明本序列是否是幀場(chǎng)自適應(yīng)模式: frame_mbs_only_flag=1,全部是幀 frame_mbs_only_flag=0,?mb_adaptiv_frame_field_flag=0,幀場(chǎng)共存 frame_mbs_only_flag=0,?mb_adaptiv_frame_field_flag=1,幀場(chǎng)自適應(yīng)和場(chǎng)共存*/ | 0 | u(1) |
| direct_8x8_inference_flag?/*?用于指明B片的直接和skip模式下的運(yùn)動(dòng)矢量的計(jì)算方式?*/ | 0 | u(1) |
| frame_cropping_flag?/*解碼器是否要將圖像裁剪后輸出,如果是,后面為裁剪的左右上下的寬度?*/ | 0 | u(1) |
| if(frame_cropping_flag){ | ? | ? |
| frame_crop_left_offset | 0 | ue(1) |
| frame_crop_right_offset | 0 | ue(1) |
| frame_crop_top_offset | 0 | ue(1) |
| frame_crop_bottom_offset | 0 | ue(1) |
| } | ? | ? |
| vui_parameters_present_flag?/*?指明vui子結(jié)構(gòu)是否出現(xiàn)在碼流中,vui子結(jié)構(gòu)在附錄中指明,用于表征視頻格式的信息?*/ | 0 | u(1) |
| if(vui_parameters_present_flag) | ? | ? |
| vui_parameters() | 0 | ? |
| rbsp_trailing_bits() | 0 | ? |
| } | ? | ? |
轉(zhuǎn)載于:https://www.cnblogs.com/virusolf/p/4942998.html
總結(jié)
- 上一篇: Fiddler使用方法简介
- 下一篇: signed distance fiel