前排提示:之所以叫 “速通版”,前提是对有一定音视频基础知识的人群,可参考本文快速了解一些重点信息和内容。
目录
1. 视频序列 (Coded Video Sequence)
2. GOP (group of pictures,图像组)
3. IDR (Instantaneous Decoding Refresh,即时解码刷新)
4. NALU (Network Abstract Layer Unit) 结构
5. SPS(Sequence Paramter Set,序列参数集)
6. PPS(Picture Paramter Set,图像参数集)
H264 编码协议
编码原理 —— 使用帧内压缩和帧间压缩的⽅式提⾼编码压缩率:
- ① 每帧的像素块之间存在相似性,通过存储差异数据来压缩图像数据,即帧内压缩
- ② 帧与帧之间也存在相似性,同样也可以存储差异数据来再次压缩数据,即帧间压缩
重点需要关注的是 帧内压缩。 H264 采⽤了 I 帧、P 帧和 B 帧策略来实现连续帧之间的压缩。( 注意,这里的 I、P、B帧是 H264 编码协议里面的的定义概念,并不是真正的物理的存在的事物。)
- I 帧 Intra picture 帧内编码帧
I 帧通常是每个 GOP(group of pictures) 的第一个帧;
一个 I 帧可以看成是⼀张图片经过压缩后的产物;
I 帧本身可以通过视频解压算法解压成⼀张单独的完整的图片。
- P 帧 Predicative-frame 向前预测编码帧
P 帧需要参考其前⾯的⼀个 I 帧或者 P 帧来生成⼀张完整的图片。
- B 帧 Bi-directional interpolated prediction frame 双向预测帧
B 帧需要参考其前⼀个 I 或者 P 帧及其后⾯的⼀个 P 帧来⽣成⼀张完整的图片。
很显然,不同的帧所占字节数大小: I 帧 > P 帧 > B 帧
不同帧的编码复杂程度:B 帧 > P 帧 > I 帧
一、H264 整体结构
众所周知,网络协议的设计都是分层的。相似的原理,为了方便 H264 文件进行网络传输,H264 定义了一套封装分层结构。 H264 将文件先分为连续的视频帧序,帧内的图像数据再一层层的分为片、宏块和子块进行传输,具体分层如下:
- 序列 (GOP)
- 图片 (pictrue)
- 片 (Slice)
- 宏块 (Macroblock)
- 子块 (subblock)
示意图如下:
二、H264 关键概念
1. 视频序列 (Coded Video Sequence)
就是⼀段内容差别不是很⼤的图像编码后生成的⼀串数据流。
当图像内容(运动)变化比较少时,⼀个序列可以很长(此时可以编⼀个 I 帧,然后⼀直 P 帧、B帧);
当运动变化多时,⼀个序列就比较短(此时可以只包含⼀个 I 帧和大概3、4个 P 帧)。
在视频编码序列中, Reference(参考周期)指两个 P 帧之间的距离。
所以,在码率不变的前提下 ——
- ① GOP 值越⼤,P、B 帧的数量会越多,平均每个 I、P、B 帧所占⽤的 字节数就越多,也就更容易获取较好的图像质量;
- ② Reference 越⼤,B 帧的数量越多,同理也 更容易获得较好的图像质量。
2. GOP (group of pictures,图像组)
主要⽤作形容⼀个 IDR 帧 到下⼀个 IDR 帧之间的间隔了多少个帧 (当然,也可以指两个 I 帧之间的距离)。 注意,GOP 值不宜设置过大。
在视频内容里的场景切换(图像内容变化大)时,H.264编码器会自动强制插入⼀个 I 帧,此时,实际的 GOP 值被缩短了。另一方面,当 I 帧的图像质量比较差时,会影响到当前 GOP 内后续 P、B 帧的图像质量,直到下⼀个 GOP 开始才有可能得以恢复。同时,由于 P、B 帧比 I 帧复杂,所以过多的 P、B 帧会影响编码效率,使编码效率降低。
另外,GOP 会影响 Seek 操作的响应速度。当Seek 操作需要直接定位、解码某⼀个 P 或 B 帧时,就需要先解码得到对应 GOP 内的 I 帧及前 N 个预测帧。所以,GOP 值越大,需要解码的预测帧就越多,Seek 响应的时间也越长。
3. IDR (Instantaneous Decoding Refresh,即时解码刷新)
⼀个序列的第⼀个图像叫做 IDR 图像(立即刷新图像),所有的IDR 图像都是 I 帧图像。
I 帧是帧组 GOP 的基础帧 (如果为 IDR, 则为第⼀帧), 在⼀组中只有⼀个 IDR 帧,可以有⼀个或多个 I 帧;
I 帧不用参考任何帧,但是之后的 P 帧和 B 帧是有可能参考这个 I 帧之前的帧的。但是,IDR 不允许这样。
IDR帧的核心作用 —— 为了解码的重同步。
当解码器解码到 IDR 图像时,⽴即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始⼀个新的序列。 这样,如果前⼀个序列出现重⼤错误,在这里可以获得重新同步的机会。IDR 图像之后的图像永远不会使用 IDR 之前的图像的数据来解码。
4. NALU (Network Abstract Layer Unit) 结构
H.264 原始码流(裸流)是由⼀个接⼀个 NALU 组成,NALU 的功能分为 2 层:
- VCL (视频编码层): 包括核心压缩引擎和块,宏块和片的语法级别定义,设计目标是尽可能地独立于网络进行高效的编码;
- NAL (网络提取层): 负责将 VCL 产⽣的比特字符串适配到各种各样的⽹络和多元环境中,覆盖了所有片级以上的语法级别。
原始 NALU 单元 = [ StartCode ] + [ NALU Header ] + [ NALU Payload ]
其中, Start Code 标示 NALU 单元的开始,必须是"00 00 00 01" 或"00 00 01";
除此之外,基本相当于⼀个 NAL Header + RBSP (Raw Byte Sequence Playload 原始字节序列负荷);
5. SPS(Sequence Paramter Set,序列参数集)
SPS 中保存了⼀组编码视频序列的全局参数。
6. PPS(Picture Paramter Set,图像参数集)
对应的是⼀个序列中某⼀幅图像或者某几幅图像的参数。
注意,传输 H264 原始码流(裸流)过程中,在发送 I 帧之前,至少要 先发⼀次 SPS 和 PPS。
二、H264 封装模式
H264 编码后数据,有 2 种封装模式:
- annexb 模式:
传统模式,有 startcode,SPS 和 PPS 是在 ES 中;
- mp4 模式:
⼀般的 mp4、mkv 文件都是 mp4 模式,没有 startcode。
SPS 和 PPS 以及其它信息都被封装在 container 中,每⼀个 frame 前⾯ 4 个字节是这个 frame的长度。
三、 MP4 转 H264
FFmpeg 的代码层面,提供了名称为 h264_mp4toannexb 的 bsf-Bit Stream Filter 来实现这个功能。
FFmpeg 命令行,也可以实现将 MP4 转换成 H264 (注意 -bsf: 后面需要空格)
ffmpeg -i test.mp4 -codec copy -bsf: h264_mp4toannexb -f h264 test.h264
补充,使用命令行查看 FFmpeg 支持的所有 Bit Stream Filter 类型
ffmpeg -bsfs
运行结果:
PS:分享
一个 系统深入学习音视频 知识的课程 —— https://xxetb.xetslk.com/s/45jaSl
(学无止境,文章如有纰漏不足,欢迎您不吝指正!谢谢 ^_^)