04.视频编码之H.264
视频编码
为什么需要编码?
摄像头采集画面直接写入到文件中时,我们会发现没一会文件已经非常大了。这导致很不适合保存和传输,所以需要编码,把画面数据进行压缩。视频编码标准有很多,而我们这里讲的是 H.264 编码,其他编码格式见:视频编码标准汇总及比较-CSDN博客
H.264
编码
制订 H.264 的主要目标有两个:
- 视频编码层 (VCL,全称:Video Coding Layer):得到高的视频压缩比。
- 网络提取层 (NAL,全称:Network Abstraction Layer):具有良好的网络亲和性,即可适用于各种传输网络。而 NAL 则是以 NALU(NAL Unit)为单元来支持编码数据在基于包交换技术网络中传输的。
H.264
编码器
 |
上面是 H264 的编码器原理图,编码时进行 帧内编码
和 帧间编码
。用相机录像举例,当相机录像过程帧出现一帧帧画面,没有压缩之间都是可以单独作为一张完整的照片,经过 H264 编码器后会出现:
- 1.SPS、PPS:生成图片的一些参数信息,比如宽高等。一般是开启编码器后的第一次 I 帧前输出一次。
- 2.I 帧:编码后第一个完整帧,包含一张图片的完整信息,但是也是压缩的。主要是进行帧内压缩,把一帧图片划分为很多
宏块
,如:16x16、8x16、8x8、4x4 等等,记录宏块顶部和左侧像素点数据,以及预测的方向。 - 3.B 帧:叫双向预测帧。编码器遇到下一帧画面与前面帧变化非常非常小(相似度在 95% 之内),比如在录像中的人在发呆,当遇到变化略微有点大的 P 帧才停止,这会出现多张 B 帧,为了尽可能的存储更少的信息,将参考前面的 I 和后面的 P 帧,把中间变化的数据存储下来,这样编码记录运动矢量和残差数据即可。所以编码器当遇到这些帧时,先等待 P 帧编码结束后才进行编码 B 帧,因此输出顺序在 P 帧之后。
- 4.P 帧:有了 I 帧作为基础,P 就有参考的对象。跟前面 I 帧的变化非常少(相似度 70% 之内)。
- 5.GOF:按上面说录像人在发呆场景时,将一组数据相差较小的图片进行编码,这就有了 GOF,即:
group of picture
,即一组图片,也叫一个场景的一组图片。是从一个 I 帧开始到下一个 I 帧前的一组数据。接着编码就是这样的一个个 GOF 的轮回。
把他看成一个 “ 黑盒 “ 的话,编码成了:
 |
解码成了:
 |
编码的输入与输出
一张张画面通过以 H.264
编码标准的编码器 (如 x264) 编码后,输出一段包含 N 个 NALU
的数据,每个 NALU 之间通过 起始码
来分隔,如图:
 |
- 起始码: 0x00 00 01 或者 0x00 00 00 01。
在网络传输(如 RTMP)或者一些容器中(如 FLV),通常会把 NALU 整合到视频区域的数据中。如下图的 flv 格式:
 |
NALU(NAL 单元)
NALU(NAL Unit,NAL 单元) 的组成部分如下图。其中,f(0) 占 1bit,u(2) 占 2bit,u(5) 占 5bit,文中如有出现类似描述符请看 H.264描述符 。
 |
forbidden_zero_bit
:禁止位,初始为 0。当客户端接受到该位为 1 时,则会丢弃该 NAL 单元的解码,表示异常数据。
nal_ref_idc
:优先级,越大优先级越高。比如:I 帧优先级大于 B 帧,DPS 芯片会优先解码重要性高的。
从上图可以看出来,当前 NAL 单元属于什么样的类型,这取决于 RBSP 具体是什么样的类型,而 RBSP 的类型是根据 nal_unit_type
的值来定义的。 ①当 nal_unit_type
为 1~5 时:RBSP 为切片类型(有 5 种切片类型);整个 NAL 单元类型为 VCL NAL 单元,VCL 是上面说的视频编码层,里面有编码后画面数据。 ②当 nal_unit_type
为其他时:RBSP 为序列参数集类型、图像参数集类型等等;整个 NAL 单元类型为非 VCL NAL 单元。 具体的 nal_unit_type
所对应的 RBSP 类型如下表所示:
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( ) |
6 | 辅助增强信息 (SEI) sei_rbsp( ) |
7 | 序列参数集(SPS) seq_parameter_set_rbsp( ) |
8 | 图像参数集 (PPS) pic_parameter_set_rbsp( ) |
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 | 未指定 |
SPS(序列参数集)
SPS 全称 Sequence parameter set
(序列参数集),当 nal_unit_type
=7 时,RBSP 就是 SPS 类型,也可以说 NAL 单元为 SPS 的 NAL 单元。SPS 主要包含的是针对一连续编码视频序列的参数,如帧数、图像尺寸等;
序列参数集 RBSP 语法:
 |
PPS(图像参数集)
PPS 全称 picture parameter set
(图像参数集),当 nal_unit_type
=8 时,RBSP 就是 PPS 类型,也可以说 NAL 单元为 SPS 的 NAL 单元。一个序列中某一幅图像或者某几幅图像,其参数如标识符 pic_parameter_set_id、可选的 seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等;详见下表 图像参数集 RBSP 语法:
 |
X264
这是国际好评的 H.264 协议标准的编码工具,这里简单介绍一下如何使用。
(1)下载:https://www.videolan.org/developers/x264.html
(2)编译(Android 的交叉编译,平台:Mac)