目录
RTSP实时音视频开发实战课程:<RTSP实时音视频开发实战>
《YUV编码为H264视频流代码实现》链接:
https://edu.csdn.net/learn/38258/606133?spm=1003.2001.3001.4157
《H264视频流解码为YUV代码实现》链接:
https://edu.csdn.net/learn/38258/606144?spm=1003.2001.3001.4157
前言
在音视频开发入门基础知识(视频入门篇)中已经介绍了视频开发技术的基本的过程,其中就对H264编码做了简单的介绍;本文会对H264编码后的视频流做一个详细的介绍。
H264码流结构
通常一个H264码流中包含了多个GOP(图像组),每一个GOP里面包含多个视频编码帧,如下图所示。GOP(Group of Pictures)图像组的意思。H264码流对GOP的划分是两个邻近关键帧(IDR帧)之间的图像为一个GOP,包含前面的IDR帧,不包含后面的IDR帧,包含第一个IDR帧后面的所有P帧和B帧;如下图GOP图像包含了5个图像编码帧,一个IDR帧和两个P帧,两个B帧。GOP又分为开放(open)GOP和闭合(close)GOP;open GOP是指当前GOP中的P帧和B帧能将前一个GOP的图像作为参考帧,并且open gop中不存在IDR帧,会有I帧(下面会介绍),假设下图是open GOP的情况,序号为6的P帧能参考序号为0的IDR帧(此时应该叫I帧)或者序号为6的P帧参考序号为1和4的P帧。close GOP是指当前GOP中的P帧和B帧不能将前一个GOP的图像作为参考帧,若下图是close GOP的情况序号为6的P帧不能参考序号为0的IDR帧,也不能参考序号为1和4的P帧,他只能参考序号为5的IDR帧。
一个GOP包含了一个IDR帧和多个P帧或B帧。这里在介绍下H264的编码帧类型;
IDR帧:Instantaneous Decoding Refresh,即时解码刷新帧;也叫关键帧 。同时IDR帧也是I帧(帧内编码帧);IDR帧编码上采用帧内编码技术,即IDR帧的编码和解码不需要参考其他视频帧,只对图像做空间上冗余的压缩编码;解码过程遇到IDR帧会重新解析计算解码参数,并清空之前的解码信息,可以防止前面GOP内的错误延续到当前GOP。
I帧:帧内(Intraframe)压缩编码帧;帧内压缩过程,主要是通过空间上邻近像素相似的特点来解决空间冗余(当前编码块/像素和周围块/像素存在相似或者相同就是空间冗余)的一个编码方法,比如常见的jpeg图像就是通过帧内编码压缩的图像。I帧不一定是IDR帧(关键帧),IDR帧一定是I帧/关键帧;I帧解码不会像P帧B帧那样需要依赖前面或者后面视频帧图像,所以I帧可以单独一帧来解码;I帧和IDR帧最大的区别在于解码过程是否会清空之前的解码信息(IDR帧会清空之前的解码信息,I帧则不会)。
P帧:前向参考编码帧,通常采用帧间和帧内混合的编码方式。通常当前视频图像和前一帧视频图像有着相似和差异的内容,去除相似的内容,保留差异的值进行编码,就可以消除图像时间上的冗余;解码需要依赖参考帧,等参考帧解码完成后才能解码P帧。
B帧:双向参考编码帧,就是需要参考前面一帧图像也需要参考后面的一帧的图像;和P帧类似,B帧通常也采用帧间和帧内混合的编码方式;不同的地方是P帧是和前一帧的差异做编码,B帧不仅仅和前一帧差异做编码,也和后面一帧的差异进行编码;B帧解码需要依赖前后的参考帧,等前后参考帧解码全部完成后才能解码B帧。
P帧和B帧在编码的过程也可以采用帧内编码的模式,B帧和P帧主要靠帧间编码来提示压缩率的。下图2-1是一个I帧/IDR帧的信息,下面中画面中需要的橙色方块就是I帧/IDR帧采用帧内编码的数据块。下图2-2是P帧的信息,图2-2中方块的颜色大多是蓝色,这表示这些数据采用帧间编码;另外有小部分是橙色方块这些块采用帧内编码。
图 2-1
图 2-2
H264帧结构
我们知道H264视频码流是由若干个GOP组成,而GOP由一个I帧和多个P帧/B帧组成。那么I帧和P帧/B帧的构成是什么样子的呢?H264在功能上分为两层:NAL 层,和VLC层。
NAL层:NetworkAbstraction Layer,网络提取层;再将VLC层数据进行存储或者传输前需要将这些VLC数据映射或者封装到NAL单元中。
VLC层:VideoCoding Layer,视频编码层;视频图像编码后输出的数据,他表示被压缩后的视频序列。
在一帧编码图像中存在一个或多个NAL Unit(Nal 单元);NAL单元包含NAL Unit Header(Nal 单元头)和NAL Unit Body(Nal 单元主体数据);NAL Unit Header由起始码和Nal Type组成。NAL Unit Body通常是RBSP(Raw Byte Sequence Payload 原始字节序列载荷)数据 ,即编码后的比特流数据。
Nal Unit 起始码通常是十六进制数0x000001或者0x00000001;在解析Nal Unit的时候首先要找到起始码,根据起始码的位置来判断一个NalUnit的开始或者结束。NalUnit根据类型可以分为 SPS、PPS、I Slice、P slice、B slice等;
SPS:Sequence Paramater Set 序列参数集;主要记录了编码视频的的 Profile
、level
、图像宽高等。
PPS:Picture Paramater Set 图像参数集 ;主要记录编码数据所依赖的参数,如参考帧,图像序列计算方法等。
Islice/Pslice/Bslice:编码帧片数据;在编码I帧、P帧或者B帧的时候会将待编码的图像分成一个或多个Slice(片),对每个Slice单独编码生成Slice编码数据;在多线程的时候多个Slice可以并行编码提高编码速度。
SPS和PPS记录的数据是GOP里面图像的信息或者单个图像的信息,这些信息都是公共的部分,比如一个GOP的图像分辨率都是一样的;所以通常SPS和PPS Nal Unit数据放在GOP的IDR帧前面或者I帧前面,通常说PPS和SPS在IDR/I帧里面。
如何区分Nal Unit里面数据类型呢?通过Nal Type来判断当前Nal Unit的类型;Nal Unit 定义如下;根据nal_unit_type字段来解析Nal Unit的类型。下图可以看出nal_unit_type在码流前8个bit中,具体在前8个比特的高5bit的位置,nal_unit_type的取值计算为(data[0] &0x1f)。
nal_unit_type字段定义如下;如下图可以知道 nal_unit_type为7时候 Nal Unit是SPS;nal_unit_type为8时候 Nal Unit是PPS;nal_unit_type为5时候 Nal Unit是IDR Slice;nal_unit_type为1时候 Nal Unit是非IDR Slice;nal_unit_type为9时候 Nal Unit是SEI(视频增强信息)。
对于非IDR 的Slice我们可以进一步解析slice_header()中的slice_type;slice_header()定义如下图。
下图是slice_type的定义。
下图是码流分析工具StreamAnalyzer展现出的SPS Nal Unit的bit构成,如下蓝色区域数字,起始码为00000001 ,起始码后面的67为十六进制数,取高5bit得到nal_unit_type值是7。
下图是码流分析工具StreamAnalyzer展现出的PPS Nal Unit的bit构成,如下蓝色区域数字,起始码为00000001 ,起始码后面的68为十六进制数,取高5bit得到nal_unit_type值是8。
下图是码流分析工具StreamAnalyzer展现出的I slice Nal Unit的bit构成,如下蓝色区域数字,起始码为00000001 ,起始码后面的65为十六进制数,取高5bit得到nal_unit_type值是5。
下图是码流分析工具StreamAnalyzer展现出的P slice Nal Unit的bit构成,如下蓝色区域数字,起始码为00000001 ,起始码后面的61为十六进制数,取高5bit得到nal_unit_type值是1。
H264档次介绍
H264基本部分规定了三种档次,具体如下:
基本档次(Baseline Profile):只支持I帧和P帧;支持利用基于上下文自适应的变长编码进行的熵编码(CAVLC)。
主要档次(Main Profile):支持隔行视频;支持B帧编码以及加权预测帧间编码;支持利用基于上下文自适应的算术编码(CABAC)。
扩展层次 (Extend Profile):支持码流间的有效切换、改进误码性能,不支持隔行扫描和CABAC。
在新修订的H264规范中,增加了High、High10、High422、High444档次。H264的各个档次所支持的功能如下表所示。水平是H264新规范中支持的Profile,垂直是H264的编解码功能;Yes表示支持,No表示不支持。