Android音視頻【二】 H264碼流結構

人間觀察
因爲窮,人會放棄體面: 個人形象的體面,工作的體面,社交的體面,尊嚴的體面。

在分析H.264碼流前,我們得得先獲取一個H.264的碼流,兩種方法獲取:一是自己寫個代碼編碼爲h264的碼流(後續介紹),二是是直接從視頻文件裏抽取。我們這裏採用方法二。當然也有其它方法。

快手抖音的短視頻/直播,毫無疑問採取的編碼方式肯定是H.264AAC生成的MP4封裝格式的視頻,我們下載一個mp4(可以看一下文件的簡介中的編解碼器是否是H.264,AAC),用如下ffmpeg命令抽取h264和aac:

// ffmpeg命令 抽取aac到文件
ffmpeg -i v0200f7b0000bq9dpgfiv42bsnt20920.MP4 -acodec copy -vn  1.aac
// ffmpeg命令 抽取h264到文件
ffmpeg -i v0200f7b0000bq9dpgfiv42bsnt20920.MP4  -c:v copy -bsf:v h264_mp4toannexb -an  1.h264

抽取的h264和aac可以播放嗎?當然可以,我用的是mac,mac上可以用vlc播放。

ffmpeg命令可以從官網直接下載可執行的二進制
關於在Android中如何利用clang交叉編譯ffmpeg後續文章介紹

H.264碼流格式

h264的有兩種碼流格式:字節流格式和RTP包格式。

字節流格式

Annex-B Byte stream format,這個是官方h264協議文檔中規定的格式,所以它是大多數編碼器默認的編碼後的輸出格式。它的基本數據單位爲NAL單元,簡稱NALU(Network Abstraction Layer Unit)。每個NALU的前面加上起始碼:0x000001(3個字節)或0x00000001(4個字節)用於分割,後面會介紹。

RTP包格式

這種格式沒有在h264中規定,這種格式不需要起始碼分割NALU,而是在NALU開始的幾個字節代表NALU的長度。這個我沒有過多研究,應該是不常用的。

所以我們這裏主要介紹的就是字節流格式的h264裸流。所謂的裸流就是經編碼器編碼後輸出的數據,而沒有經過傳輸協議(比如flv)封裝的數據,這樣的數據就叫做裸流。

H.264結構

碼流分層

如上所說h264碼流是由一個接一個的 NALU組成的,但是它按照功能分爲

視頻編碼層:VCL(Video Coding Layer),編碼器壓縮處理後的壓縮視頻數據序列。

網絡抽象層:NAL(Network Abstraction Layer),負責以網絡要求的格式對數據進行打包和傳送,是傳輸層。不管是本地保存還是在網絡上傳送,都需要通過這一層來傳輸。

也就是視頻編碼數據(VCL)在傳輸或存儲(保存到文件)之前,會先被封裝進NAL(也就是NALU)單元纔可以。

NALU(NAL單元)

h264碼流是一系列的NALU組成,用起始碼分割每個。所以整體看碼流的格式就是:

H264碼流 = …Start_Code_Prefix + NALU + Start_Code_Prefix + NALU + …

Start_Code_Prefix 標示的就是起始碼,起始碼爲:0x000001(3個字節)或0x00000001(4個字節),起始碼中間的部分就是NALU的部分。

我們看下我們從抖音/快手提取的h264文件的開始部分(因爲h264格式開始有SPS,PPS,SEI 分割較多,你可以搜索一下文件後後面的數據流也有):

起始碼.png

NALU的主體是:NALU=NALU Header + EBSP

NALU的主體有細分:分別爲EBSP、RBSP和SODB。其中EBSP完全等價於NALU主體,而且它們三個的結構關係爲:

EBSP包含RBSP,RBSP包含SODB。

EBSP名字叫:擴展字節序列載荷(Encapsulated Byte Sequence Payload)

RBSP名字叫:原始字節序列載荷(Raw Byte Sequence Payload)

SODB(String Of Data Bits)就是最原始的編碼數據。

後續介紹,先有個大概的概念區分,真的是概念非常多。

NALU Header

NALU Header 在每個的NALU中,佔據一個字節也就是8位。分三部分,如下:

名稱 佔據位數bit 代表的意義
forbidden_zero_bit 1bit h264文檔規定,這個值應該爲0,當它不爲0時,表示網絡傳輸過程中,當前NALU中可能存在錯誤,解碼器可以考慮不對這個NALU進行解碼
nal_ref_idc 2bit 取值0~3,代表當前這個NALU的重要性,取值越大,代表當前NALU越重要
nal_unit_type 5bit NALU的數據類型,比如是sps,pps,sei,idr等

我們主要看一下nal_unit_type在h264協議中定義如下:

nal_unit_type.png

nal_unit_type =1-5是VCL(視頻編碼層)單元。

6-代表當前NALU爲輔助增強信息(SEI)。一般會埋入視頻版權等信息。

7-代表當前NALU爲序列參數集SPS,包括一個圖像序列的所有信息,即兩個 IDR 圖像間的所有圖像信息,如圖像尺寸、視頻格式等

8-代表當前NALU爲圖像參數集PPS,包括一個圖像的所有分片的所有相關信息, 包括圖像類型、序列號等

一般h264的碼流最開始都是SEI,SPS,PPS,IDR(I幀)…,SPS,PPS,IDR(I幀). 一般在IDR(I幀)前有SPS,PPS,也就是每一組圖像(GOP序列,圖片組)都給予了圖像參數集(PPS)和這個序列參數集SPS(SPS)。我們看下最開始提取的抖音的h264文件(也就是上面啓始碼的後一字節)。

// 這裏只貼了關鍵字節,省略其它的
// 16進制打開,每2位數是一個字節byte=8位(bit)
// 1F的二進制位的後五位爲:11111
0000 0001 0605 ffff e1dc 45e9 bde6 d948  SEI  06&1F取該字節的後五位=6

3d31 3a31 2e30 3000 8000 0000 0167 6400  SPS  67&1F取該字節的後五位=7

0303 c0f1 8319 a000 0000 0168 e978 b2c8  PPS  68&1F取該字節的後五位=8

b000 0001 6588 8400 4ffe 841f c0a5 9f35  IDR  65&1F取該字節的後五位=5

71b9 4cd3 13c1 0000 0001 419a 246c 47ff  slice(片)  41&1F取該字節的後五位=1

視頻的寬高就是在SPS中取出來的。

EBSP和RBSP

NALU的起始碼爲0x0000010x00000001,但是有一種在NALU的內部也有0x0000010x00000001的數據怎麼辦?H264採用了一種方法如果NALU內部出現了編碼器就在最後一個字節前,插入一個新的字節:0x03。做了如下4種情況的處理:

0x000000  插入x03  0x00000300
0x000001  插入x03  0x00000301
0x000002  插入x03  0x00000302
0x000003  插入x03  0x00000303

0x000003是爲了防止NALU內部本來就有0x000003這樣的數據。
所以說EBSP相較於RBSP,多了防止衝突的一個字節:0x03。當使用EBSP時,就需要檢測EBSP內是否有序列:0x000003,如果有,則去掉其中的0x03。這樣一來,我們就能得到原始字節序列載荷:RBSP。

我們用提取的抖音的h264文件找下:

3d31 3a31 2e30 3000 8000 0000 0167 6400
1fac d980 b40a 1b01 1000 0003 0010 0000
// 比如67=SPS 的NALU就有一個0303
0303 c0f1 8319 a000 0000 0168 e978 b2c8
b000 0001 6588 8400 4ffe 841f c0a5 9f35
11fe 06cb d3bf 26e6 9d1f ff2c e1b1 aaf2

RBSP和SODB

原始編碼數據SODB(String Of Data Bits)他們2個的關係是:

RBSP = SODB + RBSP尾部

RBSP尾部

H264協議文檔中有兩種尾部表示,如下:

RBSP尾部.png

尾部特RBSP語法

RBSP最後一個字節的最後一個比特爲rbsp_stop_one_bit,其值爲1,並且當rbsp_stop_one_bit不是最後一個比特時,用一個或多個rbsp_alignment_zero_bit,其值爲0,補齊以形成一個字節對齊。

條帶RBSP尾部

nal_unit_type等於1~5時採用這種尾部。在尾部特RBSP語法的基礎上,如果當entropy_coding_mode_flag值爲1,也即當前採用的熵編碼爲CABAC,而且more_rbsp_trailing_data返回爲true,也即RBSP中有更多數據時,添加一個或多個0x0000

H264的碼流結構

所以整體H.264的Annex-B碼流格式從概念上來看就是,SODB裏就是原始的編碼數據。

H.264 Annex-B 碼流格式.png

如有描述不準確歡迎指正。

H.264的協議文檔

http://www.itu.int/rec/T-REC-H.264

http://www.itu.int/rec/T-REC-H.264-200503-S/en

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章