生活中,视频随处可见。假设一个视频分辨率是1080p,帧率是25fps,时长是1小时,如果不做压缩的话,他的大小是 1920 x 1080 x 1.5 x 25 x 1 x 3600 = 260.7G,这样的质量对于数据传输和存储是不可接受的,于是就诞生了一系列的视频编码标准。
0x01 视频编码
谈到视频编码,一般从这几个方向去做压缩:
1.空间冗余: 将一张图片划分为很多图块,相邻的图块之间会有很多相似的地方,这种相似的地方叫做空间冗余,经过图片本身的压缩之后会产生一个压缩之后的图像称之为I帧。
2.时间冗余: 一个帧率为25fps的视频中,一秒会有25帧图片,每帧图片之间的变化比较小,相似度很高,这种称之为时间冗余。经过I帧与后一帧图像对比之后,只记录差异的帧是P帧和B帧,P帧和B帧不能独立解码出一张图片,需要依赖I帧。
3.视觉冗余: 人的眼睛对图像高频信息的敏感度小于低频信息的敏感度,去除一些高频信息,对于人眼没有太大差别,这个叫做视觉冗余。去除高频信息时会将图像的色值出去一个QP值,QP值越大,高频信息的色值越小,画面清晰度越低,码率就越低。
0x02 帧
帧主要包括以下类型:
-
- I-frames: 称之为关键帧,包含创建完整图片所需要的所有数据。由于他是独立帧,其尺寸是最大的,但也是解压最快的。
- P-framse: 称之为预测帧,是从基于最近的I-frames或者P-frames可预测的图片编码得到的。
- B-frames: 称之为双向帧,是基于使用之前和之后的帧信息编码得到的,因为p帧已经被压缩,p帧与p帧之间差异更小,所有压缩率极高。但其解压过程会耗时较长,因为他依赖周围其他的帧。
- IDR-framse: 称之为立即刷新关键帧,这个帧不依赖之前的I帧,如果普通的I帧解码错误,会影响其他的帧解码。IDR帧就会阻断这种错误。
帧排列顺序
与帧相关的概念:
-
- DTS(decode-Time-Stamp): DTS可以理解为解码顺序,如上图IDR1可以独立解码成图片 B1需要依赖IDR1和P1解码之后才能解码,同理B2也依赖IDR1和P1才能解码。综上所述,因为B帧原因,解码顺序不是按照帧的排列顺序来解码的。
- PTS(Presentation-Time-Stamp): PTS可以理解为显示顺序,如上图,显示顺序就是按照帧的排列顺序显示。
- GOP: 是指一个IDR帧开始到另一个IDR帧结束所有帧的集合称之为GOP,GOP里面包含IDR帧,I帧,B帧,P帧所有的帧,一个GOP可以解码出一组图片,如下图。
- slice: slice也叫做片,是将图片分割成几片,片与片之间相互独立、互不依赖、独立编码。他的作用是为了利用多线程对一张图片并行编码,提高编码速度。
- Marcoblock : Marcoblock称之为宏块,是视频编码技术的基本单元,宏块就是将图像分割成16*16大小的区域,一个slice包含若干个宏块
Group
图像层次结构的图解
0x03 H264码流结构
H264就是将以上图像编码之后按照一定的结构组装的文件。可以将视频文件压缩到合适大小,又不影响视频的观看效果。
H264的结构
H264的结构是Annexb格式,他是以4个字节或者3个字节作为分隔符。分隔符一般是0x000001或0x00000001。分隔符后面就是编码数据,他是以NALU形式排列的。
NAL单元
NAL称之为网络提取层,设计nal的目的是为了实现网络亲和性,可以适用于各种传输网络。下面是NALU的的格式
图像层次结构的图解
NALU HEADER
-
forbidden_zero_bit: 禁止位 必须为0 在网络传输中发生错误时,会被置为1,告诉接收方丢掉该单元。
-
nal_ref_idc 用于表示当前NALU的重要性,值越大,越重要。解码器在解码处理不过来的时候可以丢掉重要性为0的NALU。
-
nal_unit_type表示NALU的数据类型:
- 1-4: I/P/B帧5 IDR帧
- 6 SEI: 补充增强信息,提供了向视频码流中加入额外信息的方法
- 7 SPS: 序列参数集。保存了一组编码视频序列的全局参数。例如图像宽高,YUV格式和位深
- 8 PPS: 图像参数集。 保存了整体图像的参数。例如熵编码类型,基础QP,最大参考帧数量
- 9 AU分割符: 它是一个或者多个 NALU 的集合,代表了一个完整的帧,有时候用于解码中的帧边界识别。
NALU Payload结构前面说过NALU的分隔符是0x000001或者0x00000001,如果在编码数据里出现这个分隔符就会影响整体的编码结构。为了防止这种情况,h264会将编码数据中出现的分隔符做一个替换:
00 00 00 替换为 00 00 03 00 00 00 01 替换为 00 00 03 01 00 00 02 替换为 00 00 03 02 00 00 03 替换为 00 00 03 03
-
EBSP: 替换之后的编码数据称之为EBSP Encapsulated Byte Sequence Payload,称为扩展字节序列载荷1
-
SODB: 替换之前的编码数据称之为 SODB 英文全称 String Of Data Bits,称原始数据比特流,就是最原始的编码/压缩得到的数据。
-
RBSP : 为了提高读写效率引入RBSP 英文全称 Raw Byte Sequence Payload,又称原始字节序列载荷。和 SODB 关系如下:
引入 RBSP Trailing Bits 做 8 位字节补齐。
RBSP = SODB + RBSP Trailing Bits(RBSP尾部补齐字节)
NALU Payload的结构图如下:
h264结构中帧类型的NALU payload存储的并不是帧,而是slice,相当于帧类型的NALU存储了一个slice类型的NALU,slice类型的NALU存储这若干个MB结构如下图:
下图就是整体的h264结构