H.264流媒體協議格式中的Annex B格式和AVCC格式深度解析
首先要理解的是沒有標準的H.264基本流格式。文件中的確包含了一個Annex,特別是描述了一種可能的格式Annex B格式,但是這個並不是一個必須要求的格式。標準文件中指定了視訊怎樣編碼成獨立的包,但是這些包是怎樣儲存和傳輸的卻是開放的。
一. Annex B
A.Network Abstraction Layer Units
視訊編碼成的包叫做Network Abstraction Layer Units, 也簡稱為NALU、NAL,每個NALU包都可以被單獨的解析和處理,每個NALU包的第一個位元組包含了NALU型別,bit3-bit7包含的內容尤其重要(bit 0一定是off的,bit1-2指定了這個NALU是否被其他NALU引用)。
NALU格式分為2類,VCL和non-VCL,總共有19種不同的NALU格式。
VCL, Video Coding Layer packets contain the actual visual information. 即視訊編碼後的資料
Non-VCL, contain metadata that may or may not be required to decode the video. 非視訊資料,配置資訊
一個單獨的NALU包、或者甚至一個VCL NALU包都不意味著是一個獨立的幀,一幀資料可以被分割成幾個NALU,一個或多個NALU組成了一個Access Units(AU),AU包含了一個完整的幀。把幀分割成幾個獨立的NALU需要耗費許多CPU資源,所以分割幀資料並不經常使用。
以下是所有定義了的NALU型別:
0 Unspecified non-VCL 1 Coded slice of a non-IDR picture VCL 2 Coded slice data partition A VCL 3 Coded slice data partition B VCL 4 Coded slice data partition C VCL 5 Coded slice of an IDR picture VCL 6 Supplemental enhancement information (SEI) non-VCL 7 Sequence parameter set non-VCL 8 Picture parameter set non-VCL 9 Access unit delimiter non-VCL 10 End of sequence non-VCL 11 End of stream non-VCL 12 Filler data non-VCL 13 Sequence parameter set extension non-VCL 14 Prefix NAL unit non-VCL 15 Subset sequence parameter set non-VCL 16 Depth parameter set non-VCL 17..18 Reserved non-VCL 19 Coded slice of an auxiliary coded picture without partitioning non-VCL 20 Coded slice extension non-VCL 21 Coded slice extension for depth view components non-VCL 22..23 Reserved non-VCL 24..31 Unspecified non-VCL
有幾種NALU格式的包包含了非常有用的資訊。
Sequence Parameter Set (SPS). This non-VCL NALU contains information required to configure the decoder such as profile, level, resolution, frame rate. Picture Parameter Set (PPS). Similar to the SPS, this non-VCL contains information on entropy coding mode, slice groups, motion prediction and deblocking filters. Instantaneous Decoder Refresh (IDR). This VCL NALU is a self contained image slice. That is, an IDR can be decoded and displayed without referencing any other NALU save SPS and PPS. Access Unit Delimiter (AUD). An AUD is an optional NALU that can be use to delimit frames in an elementary stream. It is not required (unless otherwise stated by the container/protocol, like TS), and is often not included in order to save space, but it can be useful to finds the start of a frame without having to fully parse each NALU.
B. NALU Start Codes, NALU包開始碼
一個NALU包中的資料並不包含它的大小(長度)資訊,因此不能簡單的連線NALU包來建立一個流,因為你不知道一個包從哪裡結束,另一個包從哪裡開始。
Annex B格式用開始碼來解決這個問題,即給每個NALU加上字首碼:2個或者3個0x00,後面再加一個0x01, 如:0x000001或者0x00000001。
4位元組型別的開始碼在在連續的資料傳輸中非常有用,因為用位元組來對齊、分割流資料,比如:用連續的31個bit0後接一個bit1來分割流資料,是很容易的。
如果接下來的bit是0(因為每個NALU都以bit0開始),那麼這就是一個NALU包資料的起始位置了。4位元組型別的開始碼通常只用於標識流中的隨機訪問點,
如SPS PPS AUD和IDR,然後其他地方都用3位元組型別的開始碼以減少資料量。
C. Emulation Prevention Bytes, 防競爭位元組
開始碼能起作用是因為3位元組的序列0x000000,0x000001,0x000002和0x000003(應該是所有的0x0000**)在non-VCL(原文是non-RBSP,譯者修改)NALU包中是非法的,所以在構建ANLU包時,必須確保排除這些數值序列,這是由向每個這種型別的序列插入防競爭位元組0x03實現的,那麼插入防競爭位元組後,0x000001變成了0x00000301。
當解碼的時候,查詢和去除防競爭位元組非常重要。因為防競爭位元組可能出現在NALU包的任意位置,在文件中通常更方便的做法是假定它們已經被去除了,Raw Byte Sequence Payload原始位元組序列負載 (RBSP)表示沒有防競爭位元組的資料序列(包)。
D. Example
以下是一個完整的例子:
<pre><code>0x0000 | 00 00 00 01 67 64 00 0A AC 72 84 44 26 84 00 00
0x0010 | 03 00 04 00 00 03 00 CA 3C 48 96 11 80 00 00 00
0x0020 | 01 68 E8 43 8F 13 21 30 00 00 01 65 88 81 00 05
0x0030 | 4E 7F 87 DF 61 A5 8B 95 EE A4 E9 38 B7 6A 30 6A
0x0040 | 71 B9 55 60 0B 76 2E B5 0E E4 80 59 27 B8 67 A9
0x0050 | 63 37 5E 82 20 55 FB E4 6A E9 37 35 72 E2 22 91
0x0060 | 9E 4D FF 60 86 CE 7E 42 B7 95 CE 2A E1 26 BE 87
0x0070 | 73 84 26 BA 16 36 F4 E6 9F 17 DA D8 64 75 54 B1
0x0080 | F3 45 0C 0B 3C 74 B3 9D BC EB 53 73 87 C3 0E 62
0x0090 | 47 48 62 CA 59 EB 86 3F 3A FA 86 B5 BF A8 6D 06
0x00A0 | 16 50 82 C4 CE 62 9E 4E E6 4C C7 30 3E DE A1 0B
0x00B0 | D8 83 0B B6 B8 28 BC A9 EB 77 43 FC 7A 17 94 85
0x00C0 | 21 CA 37 6B 30 95 B5 46 77 30 60 B7 12 D6 8C C5
0x00D0 | 54 85 29 D8 69 A9 6F 12 4E 71 DF E3 E2 B1 6B 6B
0x00E0 | BF 9F FB 2E 57 30 A9 69 76 C4 46 A2 DF FA 91 D9
0x00F0 | 50 74 55 1D 49 04 5A 1C D6 86 68 7C B6 61 48 6C
0x0100 | 96 E6 12 4C 27 AD BA C7 51 99 8E D0 F0 ED 8E F6
0x0110 | 65 79 79 A6 12 A1 95 DB C8 AE E3 B6 35 E6 8D BC
0x0120 | 48 A3 7F AF 4A 28 8A 53 E2 7E 68 08 9F 67 77 98
0x0130 | 52 DB 50 84 D6 5E 25 E1 4A 99 58 34 C7 11 D6 43
0x0140 | FF C4 FD 9A 44 16 D1 B2 FB 02 DB A1 89 69 34 C2
0x0150 | 32 55 98 F9 9B B2 31 3F 49 59 0C 06 8C DB A5 B2
0x0160 | 9D 7E 12 2F D0 87 94 44 E4 0A 76 EF 99 2D 91 18
0x0170 | 39 50 3B 29 3B F5 2C 97 73 48 91 83 B0 A6 F3 4B
0x0180 | 70 2F 1C 8F 3B 78 23 C6 AA 86 46 43 1D D7 2A 23
0x0190 | 5E 2C D9 48 0A F5 F5 2C D1 FB 3F F0 4B 78 37 E9
0x01A0 | 45 DD 72 CF 80 35 C3 95 07 F3 D9 06 E5 4A 58 76
0x01B0 | 03 6C 81 20 62 45 65 44 73 BC FE C1 9F 31 E5 DB
0x01C0 | 89 5C 6B 79 D8 68 90 D7 26 A8 A1 88 86 81 DC 9A
0x01D0 | 4F 40 A5 23 C7 DE BE 6F 76 AB 79 16 51 21 67 83
0x01E0 | 2E F3 D6 27 1A 42 C2 94 D1 5D 6C DB 4A 7A E2 CB
0x01F0 | 0B B0 68 0B BE 19 59 00 50 FC C0 BD 9D F5 F5 F8
0x0200 | A8 17 19 D6 B3 E9 74 BA 50 E5 2C 45 7B F9 93 EA
0x0210 | 5A F9 A9 30 B1 6F 5B 36 24 1E 8D 55 57 F4 CC 67
0x0220 | B2 65 6A A9 36 26 D0 06 B8 E2 E3 73 8B D1 C0 1C
0x0230 | 52 15 CA B5 AC 60 3E 36 42 F1 2C BD 99 77 AB A8
0x0240 | A9 A4 8E 9C 8B 84 DE 73 F0 91 29 97 AE DB AF D6
0x0250 | F8 5E 9B 86 B3 B3 03 B3 AC 75 6F A6 11 69 2F 3D
0x0260 | 3A CE FA 53 86 60 95 6C BB C5 4E F3</code>
這是一個完整的訪問單元(AU),包括3個NALU包,如你所見,資料序列以開始碼開始,後面接了一個SPS(SPS 以0x67開始),在SPS中,你可以看到有2個防競爭位元組。沒有這些位元組那麼非法的資料序列就會出現在這些位置。然後可以看到一個開始碼後面接著一個PPS(PPS 以0x68開始),然後是一個最後的開始碼,後面跟著一個IDR包。這是一個完整的H.264流,如果你把這些資料以16進位制的方式儲存到一個以.264為字尾名的檔案中,可以把這些資料轉換成以下圖片:
Annex B格式通常用於實時的流格式,比如說傳輸流,通過無線傳輸的廣播、DVD等。在這些格式中通常會週期性的重複SPS和PPS包,經常是在每一個關鍵幀之前,
因此據此建立解碼器可以一個隨機訪問的點,這樣就可以加入一個正在進行的流,及播放一個已經在傳輸的流。
二. AVCC
另一個儲存H.264流的方式是AVCC格式,在這種格式中,每一個NALU包都加上了一個指定其長度(NALU包大小)的字首(in big endian format大端格式),這種格式的包非常容易解析,但是這種格式去掉了Annex B格式中的位元組對齊特性,而且字首可以是1、2或4位元組,這讓AVCC格式變得更復雜了,指定字首位元組數(1、2或4位元組)的值儲存在一個頭部物件中(流開始的部分),這個頭通常稱為'extradata'或者'sequence header',它的基本格式如下:
bits
8 version ( always 0x01 )
8 avc profile ( sps[0][1] )
8 avc compatibility ( sps[0][2] )
8 avc level ( sps[0][3] )
6 reserved ( all bits on )
2 NALULengthSizeMinusOne // 這個值是(字首長度-1),值如果是3,那字首就是4,因為4-1=3
3 reserved ( all bits on )
5 number of SPS NALUs (usually 1)
repeated once per SPS:
16 SPS size
variable SPS NALU data
8 number of PPS NALUs (usually 1)
repeated once per PPS
16 PPS size
variable PPS NALU data
使用上面的例子,那麼AVCC extradata看起來像是這樣的:
0x0000 | 01 64 00 0A FF E1 00 19 67 64 00 0A AC 72 84 44
0x0010 | 26 84 00 00 03 00 04 00 00 03 00 CA 3C 48 96 11
0x0020 | 80 01 07 68 E8 43 8F 13 21 30
你會發現SPS和PPS被儲存在了非NALU包中(out of band帶外),即獨立於基本流資料。這些資料的儲存和傳輸是檔案容器的任務,超出了本文的範疇。注意:雖然AVCC格式不使用起始碼,防競爭位元組還是有的。
另外,extradata中有一個命名比較容易讓人困惑的變數NALULengthSizeMinusOne,這個變數告訴我們用幾個位元組來儲存NALU的長度(字首:1、2或4),如果NALULengthSizeMinusOne是0,那麼每個NALU使用一個位元組的字首來指定長度,那麼每個NALU包的最大長度是255位元組,這個明顯太小了,這種方式對於儲存一個完整的關鍵幀來說太小了。使用2個位元組的字首來指定長度,那麼每個NALU包的最大長度是64K位元組,這個對於我們的例子來說是足夠了,但是限制還是比較大;3位元組是比較完美的,但是因為一些原因沒有被廣泛支援;因此,4位元組長度的字首是目前使用最多的方式,也是這裡我們使用的方式:
0x0000 | 00 00 02 41 65 88 81 00 05 4E 7F 87 DF 61 A5 8B
0x0010 | 95 EE A4 E9 38 B7 6A 30 6A 71 B9 55 60 0B 76 2E
0x0020 | B5 0E E4 80 59 27 B8 67 A9 63 37 5E 82 20 55 FB
0x0030 | E4 6A E9 37 35 72 E2 22 91 9E 4D FF 60 86 CE 7E
0x0040 | 42 B7 95 CE 2A E1 26 BE 87 73 84 26 BA 16 36 F4
0x0050 | E6 9F 17 DA D8 64 75 54 B1 F3 45 0C 0B 3C 74 B3
0x0060 | 9D BC EB 53 73 87 C3 0E 62 47 48 62 CA 59 EB 86
0x0070 | 3F 3A FA 86 B5 BF A8 6D 06 16 50 82 C4 CE 62 9E
0x0080 | 4E E6 4C C7 30 3E DE A1 0B D8 83 0B B6 B8 28 BC
0x0090 | A9 EB 77 43 FC 7A 17 94 85 21 CA 37 6B 30 95 B5
0x00A0 | 46 77 30 60 B7 12 D6 8C C5 54 85 29 D8 69 A9 6F
0x00B0 | 12 4E 71 DF E3 E2 B1 6B 6B BF 9F FB 2E 57 30 A9
0x00C0 | 69 76 C4 46 A2 DF FA 91 D9 50 74 55 1D 49 04 5A
0x00D0 | 1C D6 86 68 7C B6 61 48 6C 96 E6 12 4C 27 AD BA
0x00E0 | C7 51 99 8E D0 F0 ED 8E F6 65 79 79 A6 12 A1 95
0x00F0 | DB C8 AE E3 B6 35 E6 8D BC 48 A3 7F AF 4A 28 8A
0x0100 | 53 E2 7E 68 08 9F 67 77 98 52 DB 50 84 D6 5E 25
0x0110 | E1 4A 99 58 34 C7 11 D6 43 FF C4 FD 9A 44 16 D1
0x0120 | B2 FB 02 DB A1 89 69 34 C2 32 55 98 F9 9B B2 31
0x0130 | 3F 49 59 0C 06 8C DB A5 B2 9D 7E 12 2F D0 87 94
0x0140 | 44 E4 0A 76 EF 99 2D 91 18 39 50 3B 29 3B F5 2C
0x0150 | 97 73 48 91 83 B0 A6 F3 4B 70 2F 1C 8F 3B 78 23
0x0160 | C6 AA 86 46 43 1D D7 2A 23 5E 2C D9 48 0A F5 F5
0x0170 | 2C D1 FB 3F F0 4B 78 37 E9 45 DD 72 CF 80 35 C3
0x0180 | 95 07 F3 D9 06 E5 4A 58 76 03 6C 81 20 62 45 65
0x0190 | 44 73 BC FE C1 9F 31 E5 DB 89 5C 6B 79 D8 68 90
0x01A0 | D7 26 A8 A1 88 86 81 DC 9A 4F 40 A5 23 C7 DE BE
0x01B0 | 6F 76 AB 79 16 51 21 67 83 2E F3 D6 27 1A 42 C2
0x01C0 | 94 D1 5D 6C DB 4A 7A E2 CB 0B B0 68 0B BE 19 59
0x01D0 | 00 50 FC C0 BD 9D F5 F5 F8 A8 17 19 D6 B3 E9 74
0x01E0 | BA 50 E5 2C 45 7B F9 93 EA 5A F9 A9 30 B1 6F 5B
0x01F0 | 36 24 1E 8D 55 57 F4 CC 67 B2 65 6A A9 36 26 D0
0x0200 | 06 B8 E2 E3 73 8B D1 C0 1C 52 15 CA B5 AC 60 3E
0x0210 | 36 42 F1 2C BD 99 77 AB A8 A9 A4 8E 9C 8B 84 DE
0x0220 | 73 F0 91 29 97 AE DB AF D6 F8 5E 9B 86 B3 B3 03
0x0230 | B3 AC 75 6F A6 11 69 2F 3D 3A CE FA 53 86 60 95
0x0240 | 6C BB C5 4E F3
AVCC格式的一個優點是在開始配置解碼器的時候可以跳到流的中間播放,這種格式通常用於可以被隨機訪問的多媒體資料,如儲存在硬碟的檔案。
也因為這個特性,MP4、MKV通常用AVCC格式來儲存。