本篇隸屬于文集:《H264/AVC 句法和語義詳解》幻林,查看文集全部文章音念,請點擊文字鏈接。
想看最新文章整葡,可以直接關(guān)注微信公眾號:金架構(gòu)
上一篇中遭居,我們站在句法元素(或稱語法元素)的角度旬渠,介紹了H.264的句法和語義,和句法元素的分層結(jié)構(gòu)鼠次。在這篇中腥寇,我們更進一步,從比特的角度出發(fā)麻敌,來探索h264碼流的組成掂摔。通過這篇的學(xué)習(xí)术羔,我們會初步具備解析h264碼流的能力,從碼流中分離出NAL單元乙漓,并識別NAL類型级历。
1. H264碼流格式
不過大道始于腳下,我們還是先從頭介紹一下叭披,h264的兩種碼流格式寥殖,它們分別為:字節(jié)流格式和RTP包格式。
(1)字節(jié)流格式:這是在h264官方協(xié)議文檔中規(guī)定的格式涩蜘,處于文檔附錄B(Annex-B Byte stream format)中嚼贡。所以它也成為了大多數(shù)編碼器,默認的輸出格式粤策。它的基本數(shù)據(jù)單位為NAL單元,也即NALU误窖。為了從字節(jié)流中提取出NALU掐场,協(xié)議規(guī)定,在每個NALU的前面加上起始碼:0x000001或0x00000001(0x代表十六進制) 贩猎。
(2)RTP包格式:這種格式并沒有在h264中規(guī)定熊户,那為什么還要介紹它呢?是因為在h264的官方參考軟件JM里吭服,有這種封裝格式的實現(xiàn)嚷堡。在這種格式中,NALU并不需要起始碼Start_Code來進行識別艇棕,而是在NALU開始的若干字節(jié)(1蝌戒,2,4字節(jié))沼琉,代表NALU的長度北苟。
顯而易見,我們通常所指打瘪,以及接下來要研究的友鼻,是h264的字節(jié)流格式傻昙。由于它沒有經(jīng)過傳輸協(xié)議封裝,所以也可以稱之為裸流彩扔。比如我們打開一個妆档,經(jīng)編碼器編碼存于本地后綴為.h264文件,里面的數(shù)據(jù)即為h264裸流虫碉。
而我們接下來的研究方向贾惦,就從已經(jīng)打開了一個本地的.h264文件,然后對里面的h264裸流敦捧,按照字節(jié)流格式進行分析開始须板。所以拿到碼流的第一刻,我們需要知道兢卵,如何從中提取出NALU习瑰。
2. 起始碼與NALU
通過上面我們已經(jīng)知道:
H264比特流 = Start_Code_Prefix + NALU + Start_Code_Prefix + NALU + …
只要我們從碼流中,找到一個一個的起始碼济蝉,那么位于起始碼之間的數(shù)據(jù)杰刽,即為NALU菠发。所以拿到碼流王滤,我們需要先從頭開始,找到起始碼0x000001或0x00000001滓鸠,找到Start_Code_Prefix之后雁乡,從它之后的下一個字節(jié)開始,就是NALU的部分糜俗。
這部分的實現(xiàn)過程描述在H264官方文檔附錄B中踱稍,已經(jīng)下載的同學(xué)可以查看B.1.1節(jié):
3. NALU
看到這一小節(jié)時,我們已經(jīng)有能力根據(jù)附錄B的內(nèi)容悠抹,從h264碼流中找出NALU珠月,所以是時候來看一下,h264碼流結(jié)構(gòu)的組成了:
這就是NALU在H264碼流中的構(gòu)成了楔敌,由上圖我們也知道:
NALU = NALU Header + RBSP
這就是接下來我們要干的啤挎,分析NALU Header 和 RBSP,為了對NALU有個宏觀的認識卵凑,我們先來看一下庆聘,NALU有哪些句法元素構(gòu)成,這位于h264文檔的7.3.1節(jié):
可以看到勺卢,整個NALU語法元素分為三部分:(1)NALU Header伙判、(3)RBSP、(2)1和3之間的部分黑忱。
其中第2部分宴抚,是近期的h264文檔才更新的勒魔,所以我特意查看了JM、x264酱塔、FFmpeg等主流編解碼器沥邻,這部分是還沒有實現(xiàn)的,所以我們可以不必理睬羊娃。而且細心的同學(xué)會發(fā)現(xiàn)唐全,只有當nal_unit_type等于14、20蕊玷、21時邮利,才會進入第二部分。
所以接下來呢垃帅,我們就重點介紹NALU Header和RBSP延届。
3.1 NALU Header
通過上面我們也可以看到,NALU Header由三個句法元素組成贸诚,分別為:forbidden_zero_bit方庭、nal_ref_idc和nal_unit_type,它們總共占據(jù)一個字節(jié)酱固,也就是說械念,NALU Header,在整個NALU中运悲,占據(jù)一個字節(jié)龄减。
而且forbidden_zero_bit的值對應(yīng)1個bit,nal_ref_idc的值對應(yīng)2個bit班眯,nal_unit_type的值對應(yīng)5個bit希停,加起來剛好一個字節(jié)。
正如在上一篇(鏈接)中所介紹的署隘,知道了句法元素宠能,我們就來分別看看它的語義:
3.1.1 forbidden_zero_bit
h264文檔規(guī)定,這個值應(yīng)該為0磁餐,當它不為0時违崇,表示網(wǎng)絡(luò)傳輸過程中,當前NALU中可能存在錯誤崖媚,解碼器可以考慮不對這個NALU進行解碼亦歉。
3.1.2 nal_ref_idc
取值0~3,代表當前這個NALU的重要性畅哑,取值越大肴楷,代表當前NALU越重要,就需要優(yōu)先被保護荠呐。尤其是當前NALU為圖像參數(shù)集赛蔫、序列參數(shù)集或IDR圖像時砂客,或者為參考圖像條帶(片/Slice),或者為參考圖像的條帶數(shù)據(jù)分割時呵恢,nal_ref_idc值肯定不為0鞠值。
而當NALU 類型,nal_unit_type為6渗钉、9彤恶、10、11鳄橘、或12時声离,nal_ref_idc都為0。
【注】IDR幀瘫怜,即:即時解碼刷新圖像术徊,它是一個序列的第一個圖像,H.264引入IDR圖像是為了解碼的重新同步鲸湃。當解碼器解碼到IDR圖像時赠涮,立即將參考幀隊列清空,將已解碼的數(shù)據(jù)全部輸出或拋棄暗挑,重新查找參數(shù)集笋除,開始一個新的序列。這樣一來窿祥,如果前一個序列發(fā)生重大錯誤株憾,在這里就可以獲得重新同步蝙寨。
所以IDR圖像之后的圖像晒衩,永遠不會引用IDR圖像之前的圖像來解碼。并且IDR圖像一定是I圖像墙歪,而I圖像不一定是IDR圖像(H264里沒有圖像層听系,圖像可以理解為幀、片或宏塊)虹菲。
3.1.3 nal_unit_type
顧名思義靠胜,這個應(yīng)該是最好理解的了,它表示NALU Header后面的RBSP的數(shù)據(jù)結(jié)構(gòu)的類型毕源。下圖為nal_unit_type所有可能的取值浪漠,和對應(yīng)的語義,它處于h264文檔7.4.1節(jié):
可以看到霎褐,nal_unit_type的值為1-5時址愿,表示RBSP里面包含的數(shù)據(jù)為條帶(片/Slice)數(shù)據(jù),所以值為1-5的NALU統(tǒng)稱為VCL(視像編碼層)單元冻璃,其他的NALU則稱為非VCL NAL單元响谓。
當nal_unit_type為7時损合,代表當前NALU為序列參數(shù)集,為8時為圖像參數(shù)集娘纷。這也是我們打開.h264文件后嫁审,遇到的前兩個NALU,它們位于碼流的最前面赖晶。
而且當nal_unit_type為14-31時律适,我們可以不用理睬,目前幾乎用不到遏插。
解析完NALU Header之后擦耀,下面就開始解析RBSP了,它包含了NALU數(shù)據(jù)的主體部分涩堤,我們放在下一篇詳細介紹眷蜓。
4. 關(guān)于H.264的協(xié)議文檔
有的同學(xué)可能還沒下載H.264的官方文檔,這里我再貼一下下載地址:
全部版本胎围,下載2017最新版:
http://www.itu.int/rec/T-REC-H.264
最新版為英文版吁系,05年3月份有中文版: