推荐此文,整理的比我好,别看我的了:https://blog.csdn.net/luoyaxing0812/article/details/111352155
本文参考:https://blog.csdn.net/weixin_30249203/article/details/98511939
https://blog.csdn.net/yanghangwww/article/details/103676530?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
1.VCL与NAL
H.264的功能分两层:
VCL(Video Codeing Layer):视频编码层,负责的是视频内容的处理,重点在编解码算法;
NAL(Network Abstraction Layer):网络抽象层,负责将编码后的数据以网络要求的格式进行打包和传输;
2.h264编码过程中的三种数据格式
1)SODB(String of Data Bits)数据比特串:
最原始的编码数据比特流,即VCL数据,长度不一定是8的倍数,所以需要补齐
2)RBSP(Raw Byte Sequence Payload)原始字节序列载荷:
在SODB数据后面加了结尾比特(rbsp_trailing_bits),一个bit的"1",若干比特的"0",目的是为了字节对齐;
RBSP = SODB + rbsp_trailing_bits
3)EBSP(Encapsulated Byte Sequence Packets)扩展字节序列载荷:
在 RBSP 数据的基础上添加了防止竞争的一个字节“0x03”;
原因是:
H264中NALU的起始码为0x000001或0x00000001,
同时H264规定,当检测到0x000000时也可以表示当前NALU的结束,
但是如果在NALU的内部出现了0x000001或0x00000001时该怎么办?
所以H264就提出了“防止竞争”这样一种机制,当编码器编完一个NALU时,
应该检测NALU内部是否出现了如下左侧的数据,如果检测到它们的存在,
编码器就在最后一个字节前,插入一个新字节“0x03”:
0x000000 --> 0x00000300
0x000001 --> 0x00000301
0x000002 --> 0x00000302
0x000003 --> 0x00000303
注:
NALU的头是0x000001或0x00000001,都按上面的方式处理,
对于0x000001就是在第2个字节后插入0x03,
对于0x00000001就是在第3个字节后插入0x03。
当解码器解码时,将0x03去掉即可,也称为脱壳操作。
注:在H264的文档中,并没有EBSP这一名词出现,但是在H264的官方参考软件JM里,却使用了EBSP。
EBSP = RBSP + “0x03”
3.NALU
H264在网络传输的是NALU,NALU的结构是:NAL头+RBSP,实际传输中的数据流如图所示:
NAL单元(NALU):每个NAL单元是一个一定语法元素的可变长字节字符串,包括一个字节的头信息(用来表示数据类型),以及若干整数字节的原始字节序列负荷(RBSP)。
一个NAL单元可以携带一个编码片,I帧、P帧、B帧、一个序列参数集、或一个图像参数集。
H264采用NAL单元可以适用于多种网络,而且进一步提高其抗误码能力。
序列号的设置可以发现丢失的是哪一个VLC单元,
冗余编码图像使得基本编码图像丢失仍可得到较粗糙的图像。
NALU = NAL header(1 byte) + RBSP
数据流储存在介质上时: 每个NALU 前添加起始码:0x00000001(或者0x000001),用来指示一个 NALU的起始和终止位置。我们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。
4.NAL头信息
NAL头由一个字节组成,语法如下:
±--------------+
|0|1|2|3|4|5|6|7|
±±±±±±±±+
|F|NRI| Type |
±--------------+
F: 1 个比特.
forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
NRI: 2 个比特.
nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如00的NALU解码器可以丢弃它而不影响图像的回放,0~3,取值越大,表示当前NAL越重要,需要优先受到保护。如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,本句法元素必需大于0。
Type: 5 个比特.
nal_unit_type. 这个 NALU 单元的类型. 简述如下:
0 没有定义
1 一个非IDR图像的编码条带 (bp帧)
2 编码条带数据分割块A
3 编码条带数据分割块B
4 编码条带数据分割块C
5 IDR图像的编码条带 (i帧)
6 辅助增强信息 (SEI)
7 序列参数集 (sps帧)
8 图像参数集
9 访问单元分隔符
10 序列结尾
11 流结尾
12 填充数据
13 序列参数集扩展
14...18 保留
19 未分割的辅助编码图像的编码条带
20...23 保留
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
5.VCL(RBSP)
6.序列
GOP:两个I帧之间是一个图像序列;一个图像序列中只有一个I帧。
一般来说,编码器编出来的首帧数据是PPS,然后是SPS,接下来是I帧,P帧、B帧等;
一个序列 == 一个SPS + 1个PPS + 一个I帧 + 若干P帧 + 若干B帧
7.切片
一帧图像被分为一个或多个片
8.宏块
详见:https://blog.csdn.net/CrystalShaw/article/details/84138743
宏块是视频信息的主要承载者。一个编码图像通常划分为多个宏块组成.包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中像素阵列。
一个宏块 = 一个16*16的亮度像素 + 一个8×8Cb + 一个8×8Cr彩色像素块组成。(YCbCr 是属于 YUV 家族的一员,在YCbCr 中 Y 是指亮度分量,Cb 指蓝色色度分量,而 Cr 指红色色度分量)
9.码流分层结构
H264码流分为两种格式:
1)Annexb格式:用于保存在文件中,每一个NAL单元前都要加起始码(startcode)
2)RTP格式:只是在网络上传输的,直接传输NAL单元,不需要加起始码
10.SPS(Sequence Paramater Set)序列参数集
SPS中保存了一组视频编码序列(Codec Video Sequence)的全局参数,
序列中每一帧编码后的数据所依赖的参数保存于图像参数集中,
一般情况下SPS和PPS的NAL单元通常位与整个码流的起始位置,但是在某些特殊情况下,
在码流中间也可能出现这两种结构,主要的原因可能为:
*解码器需要在码流中间开始解码;
*编码器在编码的过程中改变了码流的参数(如图像的分辨率);
11.PPS(Picture Paramater Set)图像参数集
PPS类似于SPS,在H264的码流中单独保存在一个NAL单元中,
只是PPS NAL Unit的nal_unit_type值为8,
而在封装格式中,PPS通常与SPS一起,保存在视频文件的文件头中。
12.SEI辅助增强信息
SEI是H264标准中一个重要的技术,主要起补充和增强的作用。
SEI没有图像数据信息,只是对图像数据信息或者视频流的补充,
有些内容可能对解码有帮助
附录
由于NAL的语法中没有给出长度信息,实际的传输、存储系统需要增加额外的头实现各个NAL单元的定界。
其中,AVI文件和MPEG TS广播流采取的是字节流的语法格式,即在NAL单元之前增加0x00000001的同步码,则从AVI文件或MPEG TS PES包中读出的一个H.264视频帧以下面的形式存在:
00 00 00 01 06 … 00 00 00 01 67 … 00 00 00 01 68 … 00 00 00 01 65 …
SEI信息 SPS PPS IDR Slice
而对于MP4文件,NAL单元之前没有同步码,却有若干字节的长度码,来表示NAL单元的长度,这个长度码所占用的字节数由MP4文件头给出;此外,从MP4读出来的视频帧不包含PPS和SPS,这些信息位于MP4的文件头中,解析器必须在打开文件的时候就获取它们。从MP4文件读出的一个H.264帧往往是下面的形式(假设长度码为2字节):
00 19 06 [… 25 字节…] 24 aa 65 [… 9386 字节…]
SEI信息 IDR Slice