mpp h264
设备调试细节 project/3516cv300-264+-265+.txt · wigewige/project
科普 H264 编解码协议详解_网络_岁月斑驳7的博客-CSDN博客
非常棒 - 视频和视频帧:H264编码格式整理 - 知乎
(7条消息)H264的NAL单元详解_网络_qq_40334837的博客-CSDN博客
H264编码基础概念+格式分析_网络_qq_31186123的博客-CSDN博客
海思3531 一如何实时观看摄像头VENC编码后的视频 - 试客网
h264 NAL头解析_occupy8的专栏-CSDN博客
H264编码的理解 - 知乎
H264基本原理 - 知乎
H264--1--编码原理以及I帧B帧P帧、pts&dts - 知乎
编码码流帧配置支持两种模式:单包模式和多包模式(在不调用 slice 分割接口及其插入用户数据接口的情况下)
IDR 第一个 I 帧称作 IDR
一个序列的第一帧叫做 IDR帧(Instantaneous Decoding Refresh,立即解码刷新)。
I 帧和 IDR 帧都是使用帧内预测,本质上是同一个东西,在解码和编码中为了方便,将视频序列中第一个 I 帧和其他 I 帧区分开,所以把第一个 I 帧称作 IDR,这样就方便控制编码和解码流程。
IDR 帧的作用是立刻刷新,使错误不致传播,从 IDR 帧开始,重新算一个新的序列开始编码。
1、多包模式:对于 H.264,当为 I 帧时,调用 HI_MPI_VENC_GetStream 接口,一个I 帧包含 4 个 NAL 包( 4 个 NAL 包分别为 sps 包、 pps 包、 sei 包、 Islice 包,这里假设 pps 包只有一个,且 4 个 NAL 包是独立的,包类型不同);对于 JPEG,一帧图像包含 2 个包( 1 个图像参数包, 1 个图像数据包, 2 个包是独立的,包类型不同)。
2、单包模式:对于 H.264,当为 I 帧时,调用 HI_MPI_VENC_GetStream 接口,一个I 帧包含 1 个 NAL 包(该 NAL 包的包类型为 Islice 包,且包含 sps、 pps、 sei、Islice 的数据);对于 JPEG,一帧图像只有 1 个包(该包的包类型为图像数据包,且包含图像参数包的数据)。
两种模式可通过 ko 加载时设置模块参数 OneStreamBuffer 来选择。 OneStreamBuffer=1 表示单包模式; OneStreamBuffer=0 表示多包模式,系统默认 OneStreamBuffer=0。
在ko文件夹中的load3516a,修改 insmod hi3516a_h264e.ko OneStreamBuffer=1
NALU Header
首先,『NALU Header』只占1个字节,即8位,其组成如下:
| forbidden_zero_bit | nal_ref_idc | nal_unit_type |--------------------+-------------+---------------
| 1 bit | 2 bit | 5 bit |
下面详细介绍:
『forbidden_zero_bit』:禁止位,笔者在有的地方看到是『forbidden_bit』。在网络传输中发生错误时,会被置为1,告诉接收方丢掉该单元;否则为0。这里笔者有几个疑问:【又挖坑了】
网络传输发生错误时,由谁来对这个位进行置1处理?
置1后被对方接收了,由谁来丢弃?(笔者怀疑是解码器)
『nal_unit_type』:表示NALU的类型。笔者从Introduction to H.264: NAL Unit摘录了并翻译如下:
(先解释下每列的含义,第一列是『nal_unit_type』的取值,第二列如中文含义,第三列比较迷,笔者还没有完全弄明白,第四列表示该类型的NALU是否为VCL)
- 1-4:I/P/B帧,合起来介绍的原因是,他们是依据VLC的slice区分的,这块因为本文不涉及,一方面是这个太过于细节,真要展开篇幅太长;另一个原因是就算不了解slice、macroblock也不影响对H264格式的理解。
- 5:IDR帧。I帧的一种,告诉解码器,之前依赖的解码参数集合(接下来要出现的SPS\PPS等)可以被刷新了。
- 6:SEI,英文全称Supplemental Enhancement Information,翻译为“补充增强信息”,提供了向视频码流中加入额外信息的方法。
- 7:SPS,全称Sequence Paramater Set,翻译为“序列参数集”。SPS中保存了一组编码视频序列(Coded Video Sequence)的全局参数。因此该类型保存的是和编码序列相关的参数。
- 8: PPS,全称Picture Paramater Set,翻译为“图像参数集”。该类型保存了整体图像相关的参数。
- 9:AU分隔符,AU全称Access Unit,它是一个或者多个NALU的集合,代表了一个完整的帧。
特殊的NALU类型:SPS和PPS
SPS和PPS存储了编解码需要一些图像参数,从What are SPS and PPS in video codecs?回答中来说,在之前的协议中,发现实际网络传输编码好的数据流的时候会出现丢包,而如果丢包数据为图像头等关键信息的时候甚至会导致后续解码失败。在H264之前,为了应对图像头关键信息被丢失的做法是在很多包(也有说法是每一个包)都会携带图像头关键信息(冗余做灾备的思想)。但是,在H264种,为了提高网络传输鲁棒性,重新设计出SPS和PPS。【这里笔者需要给自己挖一个坑:SPS和PPS较之前的做法优越在哪里?是因为SPS和PPS存储的仅仅是一个GOP的信息,而之前的头信息存储的是整个视频关键信息嘛?】
提到这里,笔者插一句题外故事:
有一次,笔者在实际生产环境中遇到过用RTSP连接一款摄像头有问题,会提示“no-existing PPS 0 referenced”,出错信息如下
P-Frame (Predictive-Frame) ,只利用之前的I帧或P帧,采用运动预测的方式进行帧间预测编码;
B-Frame (bi predictive-Frame) ,bi双向的意思,双向预测编码图像帧),提供最高的压缩比,它既需要之前的图像帧(I帧或P帧),也需要后来的图像帧(P帧),采用运动预测的方式进行帧间双向预测编码。
所以P帧和B帧只包含变化的信息。
但是啊,有的时候,比如镜头切换等,变化的信息量反而更大,那使用P帧或者B帧反而得不偿失。怎么办呢?干脆算了,h264编码这个时候就会插入key frame(关键帧),也就是不依赖前后帧的独立的一帧图像。key frame也叫I-Frame,也就是intra-frame。只有这个时候需要插入key frame吗?不是的!假设一个视频从头到尾都没有这样的剧烈变化的镜头,那就只有第一帧是key frame了,那么我做seek的时候(你想快进啊,或者想从中间看视频啊),那就灾难了,比方你要seek到第1小时,那程序就得先decode 1小时的视频才能计算出你要播放的帧...卒!
所以啊,一般都是以有规律的interval来插入key frame,这个有规律的interval就叫做I-Frame interval ,或者叫做I-Frame distance,或者叫做GOP length/size(Group Of Images),这个值一般是10倍的fps(libx264默认将这个interval设置为250,另外,x264编码器在检测到大的场景变化时,会在变化开始处插入key frame) 。另外,ESPN是每10秒插入一个key frame,YouTube每2秒插入一个关键帧,Apple每3秒到每10秒插入一个key frame。
GOPGOP结构一般会使用2个数字来描述,比如, M=3, N=12。第一个数字3表示的是2个anchor frame(I帧 或者 P帧)之间的距离,第二个数字12表示2个key frame之间的距离(也就是GOP size或者GOP length),那么对于这个例子来说, GOP结构就是IBBPBBPBBPBBI。
GOP说白了就是两个I帧之间的间隔.比较说GOP为120,如果是720p60的话,那就是2s一次I帧.
IDR(instantaneous decoder refresh) frame首先是 keyframe,对于普通的keyframe(non-IDR keyframe)来说,其后的P-Frame和B-Frame可以引用此keyframe之前的帧,但是IDR就不行,IDR后的 P-Frame和B-Frame不能引用此IDR之前的帧。所以decoder遇到IDR后,就可以毫不犹豫的抛弃之前的解码序列,从新开始(refresh)。这样当遇到解码错误的时候,错误不会影响太远,将止步于IDR。
一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。
PTS:Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS:Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。
在没有B帧存在的情况下DTS的顺序和PTS的顺序应该是一样的。
IPB帧的不同:
I frame:自身可以通过视频解压算法解压成一张单独的完整的图片。
P frame:需要参考其前面的一个I frame 或者B frame来生成一张完整的图片。
B frame:则要参考其前一个I或者P帧及其后面的一个P帧来生成一张完整的图片。
两个I frame之间形成一个GOP,在x264中同时可以通过参数来设定bf的大小,即:I 和p或者两个P之间B的数量。
通过上述基本可以说明如果有B frame 存在的情况下一个GOP的最后一个frame一定是P.
DTS和PTS的不同:
DTS主要用于视频的解码,在解码阶段使用.PTS主要用于视频的同步和输出.在display的时候使用.
在没有B frame的情况下.DTS和PTS的输出顺序是一样的.
例子:
下面给出一个GOP为15的例子,其解码的参照frame及其解码的顺序都在里面:
如上图:I frame 的解码不依赖于任何的其它的帧.而p frame的解码则依赖于其前面的I frame或者P frame.B frame的解码则依赖于其前的最近的一个I frame或者P frame 及其后的最近的一个P frame.
一、序言
h264常见的帧头数据为:
00 00 00 01 67 (SPS)
00 00 00 01 68 (PPS)
00 00 00 01 65 ( IDR 帧)
00 00 00 01 61 (P帧)
等等,那么他们代表的意思是什么呢?
PPS(Picture Parameter Set):图像参数集,
SPS(Sequence Parameter Set):序列参数集,保存视频的分辨率等信息;
二、start code
start code有两种,四个字节的“00 00 00 01”和三个字节的“00 00 01”都是。
三、NALU indicator == NALU header
| forbidden_zero_bit | nal_ref_idc | nal_unit_type |
|--------------------+-------------+---------------|
| 1 bit | 2 bit | 5 bit |
+-------------------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+---+
| F | NRI | Type |
+-------------------------------+
上述的 67,68,65,61,还有41等,都是该NALU的识别级别。
F: 禁止为,0表示正常,1表示错误,一般都是0
NRI:重要级别,11表示非常重要。
TYPE:表示该NALU的类型是什么,见下表7.20,由此可知7为序列参数集(SPS),8为图像参数集(PPS),5代表I帧。1代表非I帧。由此可知,61和41其实都是P帧(type值为1),只是重要级别不一样(它们的NRI一个是11BIN,一个是10BIN)
nal_unit_type为1, 2, 3, 4, 5的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。
* 0:未规定
* 1:非IDR图像中不采用数据划分的片段
* 2:非IDR图像中A类数据划分片段
* 3:非IDR图像中B类数据划分片段
* 4:非IDR图像中C类数据划分片段
* 5:IDR图像的片段
* 6:补充增强信息(SEI)
* 7:序列参数集(SPS)
* 8:图像参数集(PPS)
* 9:分割符
* 10:序列结束符
* 11:流结束符
* 12:填充数据
* 13:序列参数集扩展
* 14:带前缀的NAL单元
* 15:子序列参数集
* 16 – 18:保留
* 19:不采用数据划分的辅助编码图像片段
* 20:编码片段扩展
* 21 – 23:保留
* 24 – 31:未规定
————————————————
对于H.264而言,每帧的界定符为00 00 00 01 或者00 00 01。
例如下面是一个H264的文件片段
00 00 00 01 67 42 C0 28 DA 01 E0 08 9F 96 10 00
00 03 00 10 00 00 03 01 48 F1 83 2A 00 00 00 01
68 CE 3C 80 00 00 01 06 05 FF FF 5D DC 45 E9 BD
E6 D9 48 B7 96 2C D8 20 D9 23 EE EF …
第一帧是00 00 00 01 67 42 C0 28 DA 01 E0 08 9F 96 10 00 00 03 00 10 00 00 03 01 48 F1 83 2A
第二帧是00 00 00 01 68 CE 3C 80
第三帧是00 00 01 06 05 FF FF 5D DC 45 E9 BD E6 D9 48 B7 96 2C D8 20 D9 23 EE EF ..
帧类型有:
NAL_SLICE = 1 非关键帧
NAL_SLICE_DPA = 2
NAL_SLICE_DPB = 3
NAL_SLICE_DPC =4
NAL_SLICE_IDR =5 关键帧
NAL_SEI = 6
NAL_SPS = 7 SPS帧
NAL_PPS = 8 PPS帧
NAL_AUD = 9
NAL_FILLER = 12
SPS 对于H264而言,就是编码后的第一帧,如果是读取的H264文件,就是第一个帧界定符和第二个帧界定符之间的数据的长度是4
PPS 就是编码后的第二帧,如果是读取的H264文件,就是第二帧界定符和第三帧界定符中间的数据长度不固定。
如图1,所示,我们可以通过SPS帧中获取视频图像的分辨率信息:
(79+1)*16 = 1280
(44+1)*16 = 720
可以看出我们的视频图像分辨率为1280*720,与VLC中获取到的视频分辨率信息相同,(图2中是通过VLC播放器获取的视频分辨率)。
细心的同志们可能注意到了我们算分辨率的方法,先是加1,然后乘以16,为什么要这么算呢?那是因为pic_width_in_mbs_minus1、pic_height_in_map_units_minus1 这两个字段的值的取值是以宏块(16*16)减1得到的。
版权声明:本文为CSDN博主「Season_hangzhou」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Season_hangzhou/article/details/51123979
NAL全称Network Abstract Layer,即网络抽象层。在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。
其中,前者(VCL)负责有效表示视频数据的内容,而后者(NAL)则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。
NAL单元是NAL的基本语法结构,它包含一个字节的头信息和一系列来自VCL的称为原始字节序列载荷(RBSP)的字节流。
如果NALU对应的Slice为一帧的开始,则用4字节表示,即0x00000001;否则用3字节表示,0x000001。
NAL Header:forbidden_bit,nal_reference_bit(优先级)2bit,nal_unit_type(类型)5bit。
标识NAL单元中的RBSP数据类型,其中,nal_unit_type为1, 2, 3, 4, 5的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。
- 0:未规定
- 1:非IDR图像中不采用数据划分的片段
- 2:非IDR图像中A类数据划分片段
- 3:非IDR图像中B类数据划分片段
- 4:非IDR图像中C类数据划分片段
- 5:IDR图像的片段
- 6:补充增强信息(SEI)
- 7:序列参数集(SPS)
- 8:图像参数集(PPS)
- 9:分割符
- 10:序列结束符
- 11:流结束符
- 12:填充数据
- 13:序列参数集扩展
- 14:带前缀的NAL单元
- 15:子序列参数集
- 16 – 18:保留
- 19:不采用数据划分的辅助编码图像片段
- 20:编码片段扩展
- 21 – 23:保留
- 24 – 31:未规定
NAL的头占用了一个字节,按照比特自高至低排列可以表示如下:
0AABBBBB
其中,AA用于表示该NAL是否可以丢弃(有无被其后的NAL参考),00b表示没有参考作用,可丢弃,如B slice、SEI等,非零——包括01b、10b、11b——表示该NAL不可丢弃,如SPS、PPS、I Slice、P Slice等。常用的NAL头的取值如:
0x67: SPS
0x68: PPS
0x65: IDR
0x61: non-IDR Slice
0x01: B Slice
0x06: SEI
0x09: AU Delimiter
由于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
一、如何从H264数据流中获取NALU
0x00000001或0x000001是一个nalu的起始标志,遇到下一个此标志时为该nalu的结尾。起始标志的后面第一个字节(type)里包含有nalu的类型,type & 0x1F即为该nalu的类型(nal_unit_type),具体类型分析详见下节。
二、H264帧分类
1、PPS与SPS
nal_unit_type=7时,nalu为SPS;
nal_unit_type=8时,nalu为PPS。
SPS(Sequence Parameter Set)序列参数集,
PPS(Picture Parameter Set)图像参数集。SPS和PPS串,包含了初始化H.264解码器所需要的信息参数,包括编码所用的profile,level,图像的宽和高,deblock滤波器等。如果对获取的nalu去掉开始码之后进行base64编码,得到的信息就可以用于sdp(SPS和PPS需要用逗号分隔开来)。下图为一个完整的sdp,sprop-parameter-sets为SPS和PPS的base64码,逗号前的为SPS,逗号后的为PPS:
2、I帧
nal_unit_type=5 是nalu为I帧。
I帧全名叫“IDR图像的编码条带”,俗称关键帧,也叫帧内编码帧 ,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)。
I帧特点:
1)它是一个全帧压缩编码帧。它将全帧图像信息进行JPEG压缩编码及传输;
2)解码时仅用I帧的数据就可重构完整图像;
3)I帧描述了图像背景和运动主体的详情;
4)I帧不需要参考其他画面而生成;
5)I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量);
6)I帧是帧组GOP(Group of Pictures)的基础帧(第一帧),在一组中只有一个I帧;
7)I帧不需要考虑运动矢量;
8)I帧所占数据的信息量比较大。
3、nal_unit_type=1时,nalu为“非IDR图像的编码条带”,为P或B帧。
P帧:前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)
P帧的预测与重构:P帧是以I帧为参考帧,在I帧中找出P帧“某点”的预测值和运动矢量,取预测差值和运动矢量一起传送。在接收端根据运动矢量从I帧中找出P帧“某点”的预测值并与差值相加以得到P帧“某点”样值,从而可得到完整的P帧。
P帧特点:
1)P帧是I帧后面相隔1~2帧的编码帧;
2)P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差);
3)解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像;
4)P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧;
5)P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧;
6)由于P帧是参考帧,它可能造成解码错误的扩散;
7)由于是差值传送,P帧的压缩比较高。
B帧:双向预测内插编码帧。B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别(具体比较复杂,有4种情况,但我这样说简单些),换言之,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU会比较累。
B帧的预测与重构
B帧以前面的I或P帧和后面的P帧为参考帧,“找出”B帧“某点”的预测值和两个运动矢量,并取预测差值和运动矢量传送。接收端根据运动矢量在两个参考帧中“找出(算出)”预测值并与差值求和,得到B帧“某点”样值,从而可得到完整的B帧。
B帧特点
1)B帧是由前面的I或P帧和后面的P帧来进行预测的;
2)B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量;
3)B帧是双向预测编码帧;
4)B帧压缩比最高,因为它只反映了参考帧间运动主体的变化情况,预测比较准确;
5)B帧不是参考帧,不会造成解码错误的扩散。
注:I、B、P各帧是根据压缩算法的需要,是人为定义的,它们都是实实在在的物理帧。一般来说,I帧的压缩率是7(跟JPG差不多),P帧是20,B帧可以达到50。可见使用B帧能节省大量空间,节省出来的空间可以用来保存多一些I帧,这样在相同码率下,可以提供更好的画质。
三、h264 NALU分析示例
下图为用UE打开的一个h264文件:
该段数据中共有4个nalu,红色标注部分为4个nalu的起始位置,开头均为0x00000001四个字节,根据起始的第五个字节分析,段1:0x06 & 0x1f = 6,nalu为辅助增强信息 (SEI);段2:0x67 & 0x1f = 7,nalu为SPS;段3:0x68 & 0x1f = 8,nalu为PPS;段4:0x65 & 0x1f = 5,nalu为I帧。
四、附录
nal_unit_type类型列表:
nal_unit_type NAL 单元和 RBSP 语法结构的内容 备注
0 未指定
1 一个非IDR图像的编码条带
slice_layer_without_partitioning_rbsp( )
2 编码条带数据分割块A
slice_data_partition_a_layer_rbsp( )
3 编码条带数据分割块B
slice_data_partition_b_layer_rbsp( )
4 编码条带数据分割块C
slice_data_partition_c_layer_rbsp( )
5 IDR图像的编码条带
slice_layer_without_partitioning_rbsp( ) I帧
6 辅助增强信息 (SEI)
sei_rbsp( )
7 序列参数集
seq_parameter_set_rbsp( ) SPS
8 图像参数集
pic_parameter_set_rbsp( ) PPS
9 访问单元分隔符
access_unit_delimiter_rbsp( )
10 序列结尾
end_of_seq_rbsp( )
11 流结尾
end_of_stream_rbsp( )
12 填充数据
filler_data_rbsp( )
13 序列参数集扩展
seq_parameter_set_extension_rbsp( )
14...18 保留
19 未分割的辅助编码图像的编码条带
slice_layer_without_partitioning_rbsp( )
20...23 保留
24...31 未指定
下面是h264标准中定义的nalu类型宏:
#define NALU_TYPE_SLICE 1
#define NALU_TYPE_DPA 2
#define NALU_TYPE_DPB 3
#define NALU_TYPE_DPC 4
#define NALU_TYPE_IDR 5
#define NALU_TYPE_SEI 6
#define NALU_TYPE_SPS 7
#define NALU_TYPE_PPS 8
#define NALU_TYPE_AUD 9
#define NALU_TYPE_EOSEQ 10
#define NALU_TYPE_EOSTREAM 11
#define NALU_TYPE_FILL 12
————————————————
版权声明:本文为CSDN博主「路儿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaoluer/article/details/53462894
H264的目标:
1.高的视频压缩比
- 良好的网络亲和性
为了完成目标H264的解决方案:
1.VLC 视频编码层
2.NAL 网络提取层
其中,VLC层是对核心算法引擎,块,宏块及片语法级别的定义,它最终输出编码完的数据SODB。
NAL层定义了片级以上的语法级别(如序列参数集和图像参数集,针对网络传输)。
NAL层同时支持以下功能:独立片解码,起始码唯一保证,SEI以及流格式编码数据传输。
NAL层将SODB打包成RBSP然后加上NAL头,成为一个NALU(NAL单元)。
H264网络传输的结构:
网络传输NALU传输,即NALU头+RVSP。NALU头用来标识RBSP是什么类型的数据,它是否会被其他帧参考以及网络传输是否有错误。 NALU头结构: 长度:1byte forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit) 1.forbidden_bit:禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。 2.nal_reference_bit:nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。 不同类型的NALU的重要性指示如下表所示。所谓的参考帧就是其他帧解码的时候需要参考的帧。比如一个I帧可能被多个B帧参考,一个B帧可能被多个P帧参考。 从表中看到,一个DIR帧是非常重要的,它一旦丢失 其他帧就无法解码。 序列参数集和图像参数集非常重要,他们丢失会导致这个序列的帧和图像帧不能解码。 nal_unit_type:NALU类型取值如下:
RTP 打包时的扩展类型
RBSP
RBSP数据是下表中的一种
从上面的了解到VLC是编码完的视频帧数据。
这些帧可能是I,B,P帧,而且这些帧可能属于不同的序列 ,就是同一个序列也有相对应的不同的序列参数集和图像参数集。
所以要完成视频的解码,不仅要传输VLC层编码处理的视频帧数据,还要传输序列参数集(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 单元并独立传输。
编码器将每个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的顺序要求
H.264/AVC标准对送到解码器的NAL单元顺序是有严格要求的,如果NAL单元的顺序是混乱的,必须将其重新依照规范组织后送入解码器,否则解码器不能够正确解码。
1.序列参数集NAL单元
必须在传送所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的序列参数集NAL单元。
所谓重复的详细解释为:序列参数集NAL单元都有其专门的标识,如果两个序列参数集NAL单元的标识相同,就可以认为后一个只不过是前一个的拷贝,而非新的序列参数集。
2.图像参数集NAL单元
必须在所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的图像参数集NAL单元,这一点与上述的序列参数集NAL单元是相同的。
3.不同基本编码图像中的片段(slice)单元和数据划分片段(data
partition)单元在顺序上不可以相互交叉,即不允许属于某一基本编码图像的一系列片段(slice)单元和数据划分片段(data
partition)单元中忽然出现另一个基本编码图像的片段(slice)单元片段和数据划分片段(data partition)单元。
4.参考图像的影响:如果一幅图像以另一幅图像为参考,则属于前者的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于后者的片段和数据划分片段之后,无论是基本编码图像还是冗余编码图像都必须遵守这个规则。
5.基本编码图像的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于相应冗余编码图像的片段(slice)单元和数据划分片段(data partition)单元之前。
6.如果数据流中出现了连续的无参考基本编码图像,则图像序号小的在前面。
7.如果arbitrary_slice_order_allowed_flag置为1,一个基本编码图像中的片段(slice)单元和数据划分片段(data
partition)单元的顺序是任意的,如果arbitrary_slice_order_allowed_flag置为零,则要按照片段中第一个宏块的位置来确定片段的顺序,若使用数据划分,则A类数据划分片段在B类数据划分片段之前,B类数据划分片段在C类数据划分片段之前,而且对应不同片段的数据划分片段不能相互交叉,也不能与没有数据划分的片段相互交叉。
8.如果存在SEI(补充增强信息)单元的话,它必须在它所对应的基本编码图像的片段(slice)单元和数据划分片段(data
partition)单元之前,并同时必须紧接在上一个基本编码图像的所有片段(slice)单元和数据划分片段(data
partition)单元后边。假如SEI属于多个基本编码图像,其顺序仅以第一个基本编码图像为参照。
9.如果存在图像分割符的话,它必须在所有SEI 单元、基本编码图像的所有片段slice)单元和数据划分片段(data partition)单元之前,并且紧接着上一个基本编码图像那些NAL单元。
10.如果存在序列结束符,且序列结束符后还有图像,则该图像必须是IDR(即时解码器刷新)图像。序列结束符的位置应当在属于这个IDR图像的分割符、SEI
单元等数据之前,且紧接着前面那些图像的NAL单元。如果序列结束符后没有图像了,那么它的就在比特流中所有图像数据之后。
11.流结束符在比特流中的最后。