流媒体专家H264协议详解II H264的分层结构与NALU介绍
Posted 奇妙之二进制
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了流媒体专家H264协议详解II H264的分层结构与NALU介绍相关的知识,希望对你有一定的参考价值。
前面一节介绍了H264协议,主要是让大家有个基本概念,了解其基本工作原理,和一些基本术语。对于我们做应用开发而言,我们并不需要知道其压缩算法的实现细节,我们关心的是它是如何在网络上传输的。
文章目录
1、H264/AVC 的分层结构
场和帧 : 视频的一场或一帧可用来产生一个编码图像。在电视中,为减少大面积闪烁现象,把一帧分成两个隔行的场。
宏块 :一个编码图像通常划分成若干宏块组成,一个宏块由一个16×16亮度像素和附加的一个8×8 Cb和一个8×8 Cr彩色像素块组成。
片:每个图象中,若干宏块被排列成片的形式。片分为I片、B片、P片和其他一些片。
– I片只包含I宏块,P片可包含P和I宏块,而B片可包含B和I宏块。
– I宏块利用从当前片中已解码的像素作为参考进行帧内预测。
– P宏块利用前面已编码图象作为参考图象进行帧内预测。
– B宏块则利用双向的参考图象(前一帧和后一帧)进行帧内预测。Ø 片的目的是为了限制误码的扩散和传输,使编码片相互间是独立的。某片的预测不能以其它片中的宏块为参考图像,这样某一片中的预测误差才不会传播到其它片中去。
H.264的主要目标是:
1.高的视频压缩比;
2.良好的网络亲和性;
为了完成这些目标H264的解决方案是:
1.VCL video coding layer 视频编码层,负责高效的视频内容表示;
2.NAL network abstraction layer 网络提取层,负责以网络所要求的恰当的方式对数据进行打包和传送。
其中,VCL层是对核心算法引擎,块,宏块及片的语法级别的定义,他最终输出编码完的数据 SODB;
NAL层定义片级以上的语法级别(如序列参数集和图像参数集,针对网络传输),同时支持以下功能:独立片解码,起始码唯一保证,SEI以及流格式编码数据传送,NAL层将SODB打包成RBSP然后加上NAL头,组成一个NALU(NAL单元)。
2、H264编码过程中的三种不同数据形式
SODB(String ofData Bits)数据比特串:最原始的编码数据,即VCL数据,没有任何附加数据。
RBSP(Raw ByteSequence Payload)原始字节序列载荷:在SODB的后面填加了结尾比特(RBSP trailing bits),一个bit“1”,若干bit “0”,以便字节对齐;
EBSP(EncapsulationByte Sequence Packets)扩展字节序列载荷:在RBSP基础上填加了仿校验字节(0X03)。它的原因是:在NALU加到Annexb上时,需要添加每组NALU之前的开始码StartCodePrefix,如果该NALU对应的slice为一帧的开始则用4位字节表示,ox00000001,否则用3位字节表示ox000001(是一帧的一部分)。解码器检测每个起始码,作为一个NAL的起始标识,当检测到下一个起始码时,当前NAL结束。对于NAL中数据出现0x000001或0x000000时,H.264引入了防止竞争机制,如果编码器遇到两个字节连续为0,就插入一个字节的0x03。解码时将0x03去掉,也称为脱壳操作。
3、H264网络传输的结构
VCL层是对核心算法引擎、块、宏块及片的语法级别的定义,最终输出压缩编码后的数据 SODB。VCL数据在传输或存储之前,先被映射或封装进NAL单元中。NAL层将SODB打包成RBSP然后加上NAL头,组成一个NALU(NAL单元)。NAL层定义片级以上的语法级别(如序列参数集和图像参数集,针对网络传输),同时支持以下功能:独立片解码,起始码唯一保证,SEI以及流格式编码数据传送。
每个NAL单元是一个一定字语法元素的可变长字节字符串,包括一个字节的头信息(用来表示数据类型),以及若干个整数字节的原始字节序列负荷(RBSP)。一个NAL单元可以携带一个编码片,A/B/C型数据分割或一个序列或一个图像参数集。H.264采用NAL单元接入可适用多种网络,而且进一步提高其抗误码能力。序列号的设置可以发现丢失的是哪一个VCL单元,冗余编码图像使得基本编码图像丢失仍可得到较“粗糙”的图像。
H264在网络传输的是NALU,NALU的结构是:NAL头+RBSP,实际传输中的数据流如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1mxKFojw-1623603794670)(http://hi.csdn.net/attachment/201004/9/0_12708080040gID.gif)]
从前面的分析我们知道,VCL层出来的是编码完的视频帧数据,这些帧可能是I、B、P帧,而且这些帧可能属于不同的序列,再者同一个序列还有相对应的一套序列参数集和图片参数集等等,所以要完成视频的解码,不仅需要传输VCL层编码出来的视频帧数据,还需要传输序列参数集、图像参数集等数据。
NALU头用来标识后面的RBSP是什么类型的数据,他是否会被其他帧参考以及网络传输是否有错误。
2.1 RBSP
RBSP用来存放下表中的一种:
RBSP类型 | 缩写 | 描述 |
---|---|---|
参数集 | PS | 序列的全局信息,如图像尺寸,视频格式等 |
增强信息 | SEI | 视频序列解码的增强信息 |
图像界定符 | PD | 视频图像的边界 |
编码片 | SLICE | 编码片的头信息和数据 |
数据分割 | DP片层的数据,用于错误恢复解码 | |
序列结束符 | 表明一个序列的结束,下一个图像为IDR图像 | |
流结束符 | 表明该流中已没有图像 | |
填充数据 | 亚元数据,用于填充字节 |
其中,
参数集:包括序列参数集 SPS 和图像参数集 PPS
SPS 包含的是针对一连续编码视频序列的参数,如标识符 seq_parameter_set_id、帧数及 POC 的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等等。
PPS对应的是一个序列中某一幅图像或者某几幅图像,其参数如标识符 pic_parameter_set_id、可选的 seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等等。
**数据分割:**组成片的编码数据存放在 3 个独立的 DP(数据分割,A、B、C)中,各自包含一个编码片的子集。分割A包含片头和片中每个宏块头数据。分割B包含帧内和 SI 片宏块的编码残差数据。分割 C包含帧间宏块的编码残差数据。每个分割可放在独立的 NAL 单元并独立传输。
2.2 NALU
2.2.1 NALU头结构
NALU头结构:nal_unit_type(5bit)+nal_reference_bit(2bit)+forbidden_bit(1bit)
1.nal_unit_type:NALU类型取值如下表所示。
nal_unit_type | NAL类型 | C |
---|---|---|
0 | 未使用 | |
1 | 非IDR图像中不采用数据划分的片段 | 2,3,4 |
2 | 非IDR图像中A类数据划分片段 | 2 |
3 | 非IDR图像中B类数据划分片段 | 3 |
4 | 非IDR图像中C类数据划分片段 | 4 |
5 | IDR图像的片 | 2,3 |
6 | 补充增强信息单元(SEI) | 5 |
7 | 序列参数集 | 0 |
8 | 图像参数集 | 1 |
9 | 分界符 | 6 |
10 | 序列结束 | 7 |
11 | 码流结束 | 8 |
12 | 填充 | 9 |
13…23 | 保留 | |
24…31 | 不保留 |
2.nal_reference_bit:nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。不同类型的NALU的重要性指示如下表所示。
nal_unit_type | NAL类型 | nal_reference_bit |
---|---|---|
0 | 未使用 | 0 |
1 | 非IDR的片 | 此片属于参考帧,则不等于0,不属于参考帧,则等与0 |
2 | 片数据A分区 | 同上 |
3 | 片数据B分区 | 同上 |
4 | 片数据C分区 | 同上 |
5 | IDR图像的片 | 5 |
6 | 补充增强信息单元(SEI) | 0 |
7 | 序列参数集 | 非0 |
8 | 图像参数集 | 非0 |
9 | 分界符 | 0 |
10 | 序列结束 | 0 |
11 | 码流结束 | 0 |
12 | 填充 | 0 |
13…23 | 保留 | 0 |
24…31 | 不保留 | 0 |
常见数据帧
序列参数集 SPS----7:
SPS即Sequence Paramater Set SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。所谓的编码视频序列即原始视频的一帧一帧的像素数据经过编码之后的结构组成的序列。而每一帧的编码后数据所依赖的参数保存于图像参数集中。一般情况SPS和PPS的NAL Unit通常位于整个码流的起始位置。但在某些特殊情况下,在码流中间也可能出现这两种结构,主要原因可能为:
1:解码器需要在码流中间开始解码;
2:编码器在编码的过程中改变了码流的参数(如图像分辨率等);
图像参数集 PPS----8:
除了序列参数集SPS之外,H.264中另一重要的参数集合为图像参数集Picture Paramater Set(PPS)。通常情况下,PPS类似于SPS,在H.264的裸码流中单独保存在一个NAL Unit中,只是PPS NAL Unit的nal_unit_type值为8;而在封装格式中,PPS通常与SPS一起,保存在视频文件的文件头中。
关键帧 IDR 帧----5:
I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)
P帧 ----1:
P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)
3.forbidden_bit:禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。
2.2.2 NAL的开始和结束
编码器将每个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的长度,然后再开始解码。
以上是关于流媒体专家H264协议详解II H264的分层结构与NALU介绍的主要内容,如果未能解决你的问题,请参考以下文章