H264码流有两种形式:Annex B和AVCC。这两种码流形式所对应不同的编码方式和格式解析。
Annex B中每个NALU中没有存储NALU长度字节
AVCC中每个NALU中存储了长度信息
H264编码分为两层:vcl和nal
- vcl:编码
- nal:网络传输
Annex B的编码方式:
SODB(string of data bits)表示初始的数据流,是最原始的编码数据
SODB数据在添加了traling bits之后数据之间能够对齐,得到RBSP(raw byte sequence payload),即原始字节序列载荷
RBSP在添加了extended byte(校验位)之后得到EBSP(Encapsulated Byte Sequence Payload),即扩展字节序列载荷
我们可以认为EBSP所包含的数据为帧信息,接下来需要添加的NALU-header常常用来表示帧的类型(sps、pps、idr帧、非idr帧)
NALU-header和EBSP组合之后得到的数据就是NALU
在Annex B编码方式中每个NALU并不包含长度信息,这导致解码的时候不能够区分不同的NALU,我们只需要加一个类似于分割符的东西,即start code,如果一个NALU对应的slice为一个GOP开始的地方则用四个字节表示0x00000001,否则用三个字节表示0x000001
GOP:
一个gop为group of picture,即一组图片,一个gop是两个i帧之间的帧数,一个gop有且只有一个i帧,以及若干个p帧和b帧。gop一般设置为每秒的帧数,如25帧/s,25帧为一个gop
那么一个视频文件有多少个NALU哪?
h264 序列、帧、slice关系从大到小依次为:序列(GOP)、帧(I、P、B帧)、片组、片(I、P、B、SP、SI片)、NALU、宏块、亚宏块、块、像素
序列
图像以序列为单位进行组织,GOP,序列就是一段连续图像帧且画面之间变化不大。 统计一段时间内图像的结果表明,相邻几幅图像,一般像素存在差别的点只有10%以内,亮度差值变化不超过2%,=色度差值变化只有1%以内。所以一段连续的图像画面之间变化不大,可以把第1帧编码为完整图像帧,第2帧只编码其与前面一帧的差别,这样第2帧只是前一帧的1/10或更小,依次对第2帧之后的图像帧做同样处理,这样的一段图像就是一个序列。当某个帧与前面的帧图像变化很大,无法参考前面帧生成时,就要开始一个新序列。
帧 :有1个或多个片组,如果不采用FMO(灵活宏块排序)机制,则一帧只有一个片组
片组 :包含1个片(slice)或多个片
片 :slice,由宏块组成,如果不采用DP(数据分割)机制,1个片只包含1个NALU,否则1个片由3个NALU组成,nal_unit_type值等于2、3、4的NALU属于同一个片分片的目的是限制误码的扩撒和传输,使片间保持独立,[ [slice header] [slice data] ],[slice header] 说明了片类型、属于哪个帧、参考帧等 [slice data] 里是整数个宏块
宏块 :编码处理的基本单元,由多个块组成,通常宏块大小为16X16像素,分为I、B、P宏块
块 :一个编码图像要划分成多个块才能进行处理,一个块是4X4像素。图像解码过程是按照slice进行解码,然后按照片组将解码宏块重组成图像
由上面可以看到一个nalu可能并不能表示一个完整的I帧或者P帧。
每个NALU-header可以用来表示NALU的类型,通常H264中的第一个NALU为SPS帧,第二个NALU为PPS帧,第三个NALU为IDR帧
IDR帧:
在I帧中所有的宏块都采用帧内预测的方法,解码时仅用I帧的数据就可以重构完整图像,不需要依赖其他的帧。
IDR帧是一种I帧,但不是所有的I帧都是IDR帧。IDR帧的特点是立即刷新,会导致DPB(Decoded Picture Buffer参考帧列表)清空,而I帧不会,所以IDR帧承担了随机访问功能,GOP中的第一个I帧为IDR帧,一个新的IDR帧开始,可以重新算一个新的Gop开始编码,播放器永远可以从一个IDR帧播放,因为在它之后没有任何帧引用之前的帧。如果一个视频中没有IDR帧,这个视频是不能随机访问的。所有位于IDR帧后的B帧和P帧都不能参考IDR帧以前的帧,而普通I帧后的B帧和P帧仍然可以参考I帧之前的其他帧。IDR帧阻断了误差的积累,而I帧并没有阻断误差的积累。
sps和pps:
H264中的sps(sequence parameter set序列参数集)和pps(picture parameter set图像参数集)保存了解码所需要的信息参数,包括编码所用的profile,level,图像的宽和高,deblock滤波器等。
- sps:描述图像序列,pps:描述单个或几幅图像图像
- sps和pps提供编解码参数
- sps和pps有对应的id,一个sps或pps被激活之后一直有效到下一个sps或pps被激活
- pps引用sps的信息
PPS和SPS,在H.264的裸码流中单独保存在一个NALU中。
用sps可以计算分辨率
那么从上面的sps中我们知道图像的宽,高。
宽=(19+1 )*16=320
高=(14+1)*16=240
SEI(补充增强信息)
- sei对应一个nalu,sei的nalu头为06
- sei对于解码器有用,但不是必须
NALU-header
bit位 | 描述 |
---|---|
F | 禁止位,表示nalu是否发生错误 |
NRI | 表示重要级别,数字越大重要级越高,11、10、01 |
Type:nal_unit_type | 表示类型 |
具体的nal_unit_type见下表:
nal_unit_type | 描述 |
0 | 未使用 |
1 | 非idr的slice |
2 | 片数据A分区 |
3 | 片数据B分区 |
4 | 片数据C分区 |
5 | 一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧 |
6 | 补充增强信息单元(SEI) |
7 | 序列参数集(SPS) |
8 | 图像参数集(PPS) |
9 | 分界符 |
10 | 序列结束 |
11 | 码流结束 |
12 | 填充 |
13-23 | 保留 |
24-31 | 未使用 |
nalu-header常用来表示nalu所在帧的类型,常见类型为0x67 0x68 0x65 0x61, 0x47 0x48 0x45 0x41, 0x27 0x28 0x25 0x21
NALU-header | 描述 |
---|---|
0x67,0x47,0x27 | sps(序列参数级),重要级递减 |
0x68,0x48,0x28 | pps(图像参数级) |
0x65,0x45,0x25 | idr |
0x61,0x41,0x21 | 非idr |
nalu的start code的防竞争处理:
一个NALU包中并不包含包的大小信息,因此不能简单将所有的NALU连接成流,需要加标识位分割不同的NALU,若一个NALU表示的是一个GOP的开始,那么用四字节的分割字段00 00 00 01,否则用三个字节的分割字段00 00 01。这个分割字段的出现意味着一个NALU包的结束以及另一个NALU包的开始,可是这种设置容易引发一个问题,如果一个NALU包中存在00 00 00 01字段或者00 00 01字段,那么解码器会将其识别为一个分割字段,从而造成错误的解码。为解决这个问题,当NALU包中出现 的时候,加入一个字节的0x03,在解码的时候删除0x03就可以了,我们称其为脱壳操作。同时H.264规定,当检测到0x00 00 00时也表示当前NALU结束,H264规定当检测到0x00 00 00或者0x00 00 01时要进行防竞争操作:
0x00 00 00 | 0x00 00 03 00 |
0x00 00 01 | 0x00 00 03 01 |
0x00 00 02 | 0x00 00 03 02 |
0x00 00 03 | 0x00 00 03 03 |
解码器在解码时,首先进行脱壳操作,恢复原始数据,统计NALU的长度,然后开始解码
nalu和帧、片、宏块、块之间的关系
- 运动图像序列>GOP>帧(片组)>片>宏块
- ABC类数据块:信息块
- 一个片有一个或三个nalu组成(ABC类数据块)
NALU打包
- mtu和mss
- mss:携带的数据的最大字节数,mtu = ip头+tcp头+mss,数据链路层
- mtu:mss+ip头,数据链路层
- 小于mtu采用单个nal单元包
- 大于mtu采用fus分片
下面是一个具体的h264编码的例子
//第一帧:00 00 00 01 67表示一个sps
//第二帧:00 00 00 01 68表示一个pps
//第三帧:00 00 00 01 06表示一个sei
//第四帧:00 00 00 01 65表示一个idr帧
下面的这个h.264流交给读者分析:
<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>