H264
H264结构中,一个视频图像编码后的数据叫做一帧
,一帧由一个片(slice)
或多个片组成,一个片
由一个或多个宏块(MB)
组成,一个宏块由16x16的yuv
数据组成。宏块作为H264编码的基本单位。
一个宏块由一个16×16
亮度像素和附加的一个8×8 Cb
和一个8×8 Cr
彩色像素块组成,我们常见的YUV
格式I420
。
SODB(数据比特串)
最原始的编码数据,即VCL数据;
RBSP(原始字节序列载荷)
在SODB
的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若干比特“0”,以便字节对齐;
EBSP( 扩展字节序列载荷)
在RBSP
基础上填加了仿校验字节(0X03)它的原因是:在 NALU
加到Annexb
上时,需要添加每组NALU之前的开始码StartCodePrefix(0x00000001 or 0x000001)
,如果该NALU对应的slice
为一帧
的开始则用4位字节
表示,0x00000001
,否则用3位字节表示0x000001
(是一帧的一部分)。另外,为了使NALU主体
中不包括与开始码
相冲突的,在编码时,每遇到两个字节连续为0,就插入一个字节的0x03
。解码时将0x03去掉。也称为脱壳操作
。
注:
0x00
表示16
进制2位,则2进制的八位(8bit = 1byte
)。即一个字节。所以0x00 00 00 01
是4位字节,0x00 00 01
是3位字节。
数据构成 NAL
NAL (Network Abstract Layer)
, 即网络抽象层。
一般来说编码器编出的首帧数据为PPS
与SPS
,接着为I帧
,然后是P帧
编码器将每个NAL
各自独立、完整地放入一个分组,因为分组都有头部,解码器可以方便地检测出NAL
的分界,并依次取出NAL
进行解码。
起始码、结束码
每个 NAL
前有一个起始码 0x00 00 01
(或者0x00 00 00 01
),解码器检测每个起始码,作为一个NAL
的起始标识,当检测到下一个起始码时,当前NAL
结束。
同时H.264
规定,当检测到0x000000
时,也可以表征当前NAL
的结束。那么NAL中数据出现0x000001
或0x000000
时怎么办?H.264引入了防止竞争机制,如果编码器检测到NAL数据存在0x000001
或0x000000
时,编码器会在最后个字节前插入一个新的字节0x03
,这样:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解码器检测到0x000003
时,把03
抛弃,恢复原始数据(脱壳操作)。解码器在解码时,首先逐个字节读取NAL的数据,统计 NAL
的长度,然后再开始解码。
网络传输结构 NALU
H264
在网络传输的是NALU
,NALU
的结构是:NAL头+RBSP
,实际传输中的数据流如图所示:
NALU
头用来标识后面的RBSP
是什么类型
的数据,是否会被其他帧参考
以及网络传输
是否有错误。
NAL Header
NALU头结构
长度:1byte(1字节) = 8位
forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit)
forbidden_bit
:禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。nal_reference_bit
:nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU
。
例如: 0x65
表示关键帧IDR
0 11 00101
禁止位 = 0
相关性 = 11
NAL类型 = 5
这里以16进制显示 2^4=16,即4位,即0110 = 6,0101 = 5
最后数据为0x65
即一个起始码0x00000001 + 0x65 + RBSP
0x000001的开头一般是当一帧数据比较大时,需要拆分为几个子段,子段的开头就是0x000001开头的,一帧的开头就是3个00的0x00000001 (即4位字节)。
nal_unit_type | NAL类型 | nal_reference_bit |
0 | 未使用 | 0 |
1 | 非IDR的片 | 此片属于参考帧,则不等于0, 不属于参考帧,则等与0 |
2 | 片数据A分区 | 同上 |
3 | 片数据B分区 | 同上 |
4 | 片数据C分区 | 同上 |
5 | IDR图像的片 | 6 |
6 | 补充增强信息单元(SEI) | 0 |
7 | 序列参数集 | 非0 |
8 | 图像参数集 | 非0 |
9 | 分界符 | 0 |
10 | 序列结束 | 0 |
11 | 码流结束 | 0 |
12 | 填充 | 0 |
13..23 | 保留 | 0 |
24..31 | 不保留 | 0 |
所谓参考帧,就是在其他帧解码时需要参照的帧。比如一个I帧
可能被一个或多个B帧
参考,一个B帧
可能被某个P帧
参考。
IDR
的I帧
是非常重要的,他一丢,那么这个序列的所有帧都没办法解码了
序列参数集(SPS)
和图像参数集(PPS)
也很重要,没有序列参数集(SPS)
,这个序列的帧就没法解;
没有图像参数集(PPS)
,那用到这个图像参数集(PPS)
的帧都没法解。
NALU顺序
H.264/AVC标准
对送到解码器的NAL
单元顺序是有严格要求的,如果NAL单元
的顺序是混乱的,必须将其重新依照规范组织
后送入解码器,否则解码器不能够正确解码。
-
序列参数集SPS
必须在传送所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的序列参数集NAL单元。
所谓重复的详细解释为:序列参数集NAL单元都有其专门的标识,如果两个序列参数集NAL单元的标识相同,就可以认为后一个只不过是前一个的拷贝,而非新的序列参数集。
-
图像参数集PPS
必须在所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的图像参数集NAL单元,这一点与上述的序列参数集NAL单元是相同的。
-
不同基本编码图像中的片段(slice)单元和数据划分片段(data partition)单元在顺序上不可以相互交叉,即不允许属于某一基本编码图像的一系列片段(slice)单元和数据划分片段(data partition)单元中忽然出现另一个基本编码图像的片段(slice)单元片段和数据划分片段(data partition)单元。
-
参考图像的影响
:如果一幅图像P2
以另一幅图像P1
为参考,则属于P2
的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于P1
的片段和数据划分片段之后,无论是基本编码图像还是冗余编码图像都必须遵守这个规则。 -
基本编码图像的所有片段(
slice
)单元和数据划分片段(data partition
)单元必须在属于相应冗余编码图像的片段(slice
)单元和数据划分片段(data partition
)单元之前。 -
如果数据流中出现了连续的无参考基本编码图像,则图像序号小的在前面。
-
如果
arbitrary_slice_order_allowed_flag
置为1,一个基本编码图像中的片段(slice
)单元和数据划分片段(data partition
)单元的顺序是任意的,如果arbitrary_slice_order_allowed_flag
置为零,则要按照片段中第一个宏块的位置来确定片段的顺序,若使用数据划分,则A类
数据划分片段在B类
数据划分片段之前,B类
数据划分片段在C类
数据划分片段之前,而且对应不同片段的数据划分片段不能相互交叉,也不能与没有数据划分的片段相互交叉。 -
如果存在
SEI(补充增强信息)
单元的话,它必须在它所对应的基本编码图像的片段(slice
)单元和数据划分片段(data partition
)单元之前,并同时必须紧接在上一个基本编码图像的所有片段(slice
)单元和数据划分片段(data partition
)单元后边。假如SEI
属于多个基本编码图像,其顺序仅以第一个基本编码图像为参照。 -
如果存在图像分割符的话,它必须在所有
SEI 单元
、基本编码图像的所有片段slice
)单元和数据划分片段(data partition
)单元之前,并且紧接着上一个基本编码图像那些NAL单元
。 -
如果存在序列结束符,且序列结束符后还有图像,则该图像必须是
IDR(即时解码器刷新)图像
。序列结束符的位置应当在属于这个IDR
图像的分割符、SEI 单元
等数据之前,且紧接着前面那些图像的NAL
单元。如果序列结束符后没有图像了,那么它的就在比特流中所有图像数据之后。 -
流结束符在比特流中的最后。
参考自文章:
https://blog.csdn.net/yuanchunsi/article/details/73194569
https://blog.csdn.net/yangzhongxuan/article/details/8003494