音视频开发之H.264码流结构与编码原理

音视频开发之H.264码流结构与编码原理

目前主流的H.264和H.265编码格式是由ITU和MPEG两个组织合力制定的。

在早期,ITU和MPEG两家组织都是各搞各的,ITU组织推行了H.261、H.262、H.263编码格式,而MPEG组织则推行了MPEG-1、MPEG-2、MPEG-3标准族群。后来两家组织准备合力制作新一代的视频编码标准,对于ITU组织来说,将这个新一代的编码标准命名为H.264,而对于MPEG来说,这个新一代的压缩标准只是其MPEG-4标准的第10部分,其第10部分叫做高级视频编码AVC(Advanced Video Coding)。

所以可以简单的认为H.264就是MPEG-4 AVC。

码流结构

NAL单元组成

H.264原始码流是由一个一个的NALU组成的,而NALU是由一个字节的Header和RBSP组成,而RBSP是由SODB和对齐字节数据组成(因为SODB是原始数据比特流, 长度不一定是8的倍数,需补足对齐),SODB就是真实的编码数据。

对于SODB中的数据,根据NAL单元的类型不同,存储了不同的数据,比如说对于Slice类型,其内部存储的就是Slice数据
,分为Slice Header和Slice Data。Slice Data中是由一个一个的MB(宏块)数据组成,MB又可以分为一个个的子MB,在子MB中存储了mb_type(宏块类型)、mb_pred(宏块预测类型)、coded residual(残差值)。

所以简单来看一个NAL单元是由一个字节的Header加上Slice数据组成,而Slice数据是由一个一个的宏块数据组成。

对于H.264来说,编码器会将每一帧图像都拆分成一个或多个Slice,每个Slice又分割成多个宏块,每个宏块是一个nxm大小的像素区域,每个宏块又可以切分成更小的子宏块。每个Slice编码完成后,会将当前Slice的编码数据打包成NAL单元下发出去。

H264码流结构

Annex B格式

这种格式的H.264码流用于实时流的传播中,其特点是码流中每个NALU(单元块)之间通过起始码来分割,起始码分为两种,一帧开始则用四个字节的1来表示,不是一帧开始就用三个字节的1来表示。

NAL单元类型

NAL单元的Header由一个字节的数据组成,由三个数据组成

  • forbidden_zero_bit : 1个比特,在H.264规范中规定了这一位必须为0。
  • nal_ref_idc : 2个比特,取00~11,指示这个NALU的重要性,取值越大,表示当前NAL越重要,需要优先受到保护。
  • nal_unit_type : 5个比特,表示NALU单元的类型。

常见的NAL单元类型有:

  • NALU_TYPE_SLICE : 1, 非IDR图像的Slice
  • NALU_TYPE_IDR : 5, IDR图像的Slice
  • NALU_TYPE_SPS : 7, SPS 序列参数集
  • NALU_TYPE_PPS : 8, PPS 图像参数集
IDR和GOP

GOP就是一组图像序列,序列中的图像强相关,差别小。每个GOP序列中,第一个图像就是IDR图像,IDR图像一定是切分成I类型的Slice,但是I类型的Slice并不一定是属于IDR图像。

SPS

序列参数集,作用于一个序列中的所有图像,对于H.264原始流来说,每个IDR图像之前都需要加入一个SPS,但是对于本地存储比如MP4文件来说,只需要一个SPS就可以了。

SPS中主要包含以下参数:

  • Profile
  • Level
  • pic_width_in_mbs_minus1 图像宽度包含的宏块个数-1,计算时需+1
  • pic_height_in_mbs_minus1 图像高度包含的宏块个数-1,计算时需+1
  • frame_mbs_only_flag 帧编码还是场编码(场隔行扫描,产生两张图),1表示帧编码
  • frame_cropping_flag 图像是否需要裁剪 (值需要x2)
    • frame_crop_left_offset 减去左侧的偏移量
    • frame_crop_right_offset 减去右侧的偏移量
    • frame_crop_top_offset 减去顶部的偏移量
    • frame_crop_bottom_offset 减去底部的偏移量
  • log2_max_frame_num_minus4 一个GOP中最大的帧数
  • max_num_ref_frames 参考帧的缓冲区大小,最大参考帧数
  • pic_order_cnt_type 显示的顺序
  • 帧率framerate = (float)(sps->vui.vui_time_scale) / (float)(sps->vui.vui_num_units_in_tick) / 2;
Profile

H.264针对不同的程序类型,定义了几套功能组合,称为profile。

最基础的profile是Constrained Baseline,基于这种组合有两条扩展线

  • Constrained Baseline -> Baseline -> Extend
  • Constrained Baseline -> Main -> High -> High10 -> High422 -> High444

下图是每种profile支持的功能列表。

h264_profile

Level

Level是一组解码性能约束,每一个Level都限定了当前能够解码的最大分辨率、帧率、码率、最大宏块大小、最高解码速度。Level越高,能处理的视频相关质量也就越高。比如说想要解码1920x1080分辨率的视频,那么Level就必须大于等于4。

h264_level

PPS

图像参数集,作用于一个序列中的所有图像,和SPS一样,对于H.264原始流来说,每个IDR图像之前都需要加入一个PPS,但是对于本地存储比如MP4文件来说,PPS可以只有一个也可以多个。
PPS中主要包含以下参数:

  • entropy_coding_mode_flag 熵编码,1表示使用CABAC
  • num_slice_groups_minus1 分片数量-1
  • weighted_pred_flag 在p/sp slice中是否开启权重预测
  • weighted_bipred_idc 在b slice中加权预测的方法
  • pic_init_qp_minus26/pic_init_qs_minus26 初始化量化参数,实际参数在slice header
  • chroma_qp_index_offset 用于计算色度分量的量化参数
  • deblocking_filter_control_present_flag 表示slice header中是否存在用于去块滤波器控制的信息
  • constrained_intra_pred_flag 若该标识为1,表示I宏块在进行帧内预测时只能使用来自I和SI类型宏块的信息
  • redundant_pic_cnt_present_flag 用于表示slice header中是否存在redundant_pic语法元素
Slice

对于NAL单元常用的类型中,SPS和PPS我们已经讲过了,剩下的就是IDR图像的Slice和非IDR图像的Slice。前面也讲过,H.264会将一张图像分割成一个或多个Slice(其实常用的一般都是一个Slice)。

Slice = Slice Header + Slice Data

Slice Header

在Header中描述了当前Slice的类型以及当前Slice的解码显示顺序等信息。

  • slice_type : 类型, 常见的类型有I Slice、P Slice、B Slice。
  • frame_num : GOP中解码帧序号,frame_num表示解码的顺序。该图像是参考帧的时候,frame_num才有意义。也就是说B帧的frame_num是没有意义的。H264中frame_num定义如下:当参数集中的句法元素gaps_in_frame_num_value_allowed_flag不为1时,每个图像的frame_num值是它前一个参考帧的frame_num值增加1。如果两个相邻的帧具有相同的frame_num,那么其中一个是参考帧,一个是非参考帧。参考帧在非参考帧之前进行编解码。
  • POC : 图像显示序号,用于标识图像播放顺序。计算POC有三种方法,根据pic_order_cnt_type指定。

在MP4存储时,是按照frame_num的顺序进行存储的,但是在播放的时候需要解析器POC,按照POC的顺序进行播放。只有当存在B帧是,显示顺序和编码顺序才不一样。

1
2
3
4
5
6
7
录制顺序
1 2 3 4 5
编码后顺序
1 3 2 5 4
I P B P B
播放顺序
1 2 3 4 5

I/B/P Slice
  • I Slice所属的图像就是我们平常所说的关键帧,采用的是帧内压缩技术,压缩比最小。
  • P Slice采用的是向前预测,编码时,只参考前面已经处理的图像,P Slice约占I Slice一半大小。
  • B Slice采用的是双向预测,编码时,即参考前面已经处理的图像,也参考后面的图像,所以对于B Slice来说,它所参考的Slice必须先处理,B Slice约占I Slice的1/4大小。

编码原理

编码技术分为两种,一种是有损编码,另一种是无损编码。有损编码中有帧内压缩技术和帧间压缩技术;无损编码中有DCT变换和VLC压缩和CABAC压缩技术。

H264编码流程

帧内压缩

对于I Slice,采用的是帧内压缩技术,所以I Slice的压缩比最低。

帧内压缩技术基于人眼对亮度的敏感度超过色度,且YUV很容易将亮度和色度分开来实现。一张图像中相邻像素之间差别都不大,所以当已知当前宏块的颜色值时,可以通过一种预测模式来进行推导,预测出相邻宏块的颜色值。在H.264中提供了九种预测模式,每个宏块在进行处理后只需要记录下其采用的预测模式的编号即可,在预测的过程中,亮度和色度是分开预测的。

只有预测模式肯定是不够的,在确定了每个宏块的预测模式后,通过将预测推导出来的图像和原始图像进行比较,得到残差值,这些残差值才是细节的数据,也需要保存下来,所以帧内压缩数据 = 宏块预测模式 + 预测残差值

帧间压缩

对于P Slice和B Slice,采用的都是帧间压缩就,所以其压缩比高。其中P Slice是单向向前参考,而B Slice是双向参考。
帧间参考是指在一个GOP序列中的帧进行参考,不同的GOP序列不能参考。所以一个GOP序列中的所有帧,只需要当前GOP序列中的数据就可以全部显示。

帧间压缩是基于运动估计来实现的,通过对当前Slice中每一个宏块在参考Slice中进行宏块运动轨迹的查找匹配,计算出运动矢量,通每个宏块的运动矢量结合参考Slice进行预测,将当前Slice和预测的结果进行比较,得到残差值作为运动补偿。所以帧间压缩数据 = 宏块运动矢量 + 预测残差值

变换与量化

变换编码

大多数视频图像都和上面展示的一样,变动的区域只是一小部分,大部分区域是不变的,可就是说低频区占大部分,高频区占小部分。然后将空间域的图像变换到频率域,会产生相关性很小的变换系数,然后再对变换系数进行量化,利用上面人类视觉体统特点所说到的,人对高频信息不敏感的特征,对低频区的系数进行细量化,对高频区的系数进行粗量化,从而降低信息传递量,达到压缩的目的。

简单的解释变换,就是如下图所示,坐标系中有ABC三点,我们将坐标轴调整一下,ABC三点的坐标值就会变小。

一个8x8的像素区域,假设其像素颜色值如下图所示

像素区域经过DCT(离散余弦变换)变换后,生产的变换系数,如下图所示

对变换系数进行量化,量化后大部分都会变成0,此时只需要将这些非0的值进行编码即可,入下图所示

量化

利用信息的统计特性,使用新的编码来表示输入的数据。H.264中支持两种量化技术,一种是VLC,另一种是CABAC。通过PPS中的entropy_coding_mode_flag参数可以知道当前视频采用了哪种编码技术。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×