WeCodec 知识库 · 视频编码
I、P、B 三种帧怎么互相参考,GOP 长度怎么取舍
I 帧能单独解码,P 帧参考前面的帧,B 帧可以同时参考前后的帧。压缩率从 I 到 B 依次提高。
GOP 是从一个 I 帧开始的一组帧。GOP 拉长能省码率,代价是拖动起播变慢,出错后也要更久才能恢复。
视频相邻两帧通常差别很小。编码器利用这一点,只有少数帧完整保存,其余帧只记录和参考帧的差别。按参考方式,帧分为三类(维基百科:Video compression picture types)。
I 帧(intra,帧内编码)只用本帧的数据,相当于一张单独压缩的图片。它最占码率,但解码器拿到它就能直接出图。
P 帧(predictive,预测帧)引用之前解码过的帧,只存运动信息和残差。残差是预测画面和真实画面之间的差值。B 帧(bi-predictive,双向预测帧)可以同时引用前面和后面的帧,压缩率最高。
| 类型 | 参考谁 | 码率开销 | 能否作为起播点 |
|---|---|---|---|
| I 帧 | 不参考其他帧 | 最高 | 能 |
| P 帧 | 之前解码的帧 | 中等 | 不能 |
| B 帧 | 前后两个方向的帧 | 最低 | 不能 |
下图按播放顺序画了 7 帧。P3 参考 I0,P6 参考 P3,串成一条前向链。B1 和 B2 夹在 I0 和 P3 之间,两头都参考。
MPEG-2 这类老标准限制很严。P 帧只能参考前面一个 I 帧或 P 帧,B 帧只能参考前后各一帧。H.264 和 HEVC 放宽了限制,可以参考更多帧,B 帧自己也能被参考(维基百科:Group of pictures)。
参考链也会传递错误。某一帧出错,直接或间接参考它的帧都会跟着出错,直到解码器处理到下一个正确的 I 帧(同上)。
B 帧要参考后面的帧,所以解码器必须先拿到后面那一帧。编码器因此会调整帧的发送顺序:先发 P3,再发 B1 和 B2。播放器解码后,再按时间顺序排回去。
解码端也要多做一步。P3 先解出来却不能马上显示,要放在缓存里,等 B1、B2 解完再按顺序送出去。所以用了 B 帧的码流,播放器要多留几帧的缓存。
这也是 B 帧会增加延迟的原因。编码 B1 之前,编码器得先等 P3 的画面到达。视频通话这类对延迟敏感的场景,可以减少或关掉 B 帧,FFmpeg 里对应的选项是 -bf,它设定两个非 B 帧之间最多放几个 B 帧(FFmpeg Codecs Documentation)。
GOP(group of pictures,图像组)是从一个 I 帧开始的一组连续帧。解码器遇到新的 GOP,就不再依赖之前的帧,所以拖动进度条时可以从这里起播(维基百科:Group of pictures)。
GOP 结构常用两个数描述。M 是两个锚帧之间的距离,锚帧指 I 帧或 P 帧。N 是两个 I 帧之间的距离。M=3、N=12 时,结构就是 IBBPBBPBBPBB(同上)。
GOP 还分封闭和开放两种。封闭 GOP 以 IDR 帧开头。IDR(instantaneous decoder refresh,即时解码刷新)是一种带标记的 I 帧,它后面的帧都不会参考它前面的帧。开放 GOP 允许 B 帧越过 I 帧去参考上一个 GOP,这时这个 I 帧就不再是 IDR 帧,不能当作干净的起播点(同上)。
I 帧越多,视频越方便剪辑和拖动,但所需码率也会明显上升(维基百科:Group of pictures)。设 GOP 长度,就是在这两者之间取舍。
| 对比项 | 短 GOP | 长 GOP |
|---|---|---|
| 同画质下的码率 | 较高 | 较低 |
| 拖动起播 | 快,附近总有 I 帧 | 慢,可能要从较远的 I 帧开始解 |
| 出错后恢复 | 快 | 慢 |
| 典型场景 | 数字电视常见约半秒一次帧内刷新 | 本地存档、点播 |
表中数字电视的例子来自维基百科:Video compression picture types。GOP 长度也不必固定。切换场景时插入 I 帧是常见做法(维基百科:Group of pictures),因为新画面和前一帧差别太大,P 帧和 B 帧预测不出有用的内容(维基百科:Video compression picture types)。
在 FFmpeg 里,-g 设 GOP 长度,-keyint_min 设 IDR 帧的最小间隔(FFmpeg Codecs Documentation)。