视频播放时,屏幕上显示的每一帧都是一幅完整画面。
I、P、B 描述的不是画面完整不完整,而是:解码这一帧时,要不要参考其他已经解码的画面。
先看三个最常见的参考关系。箭头表示“右边的画面在解码时使用了左边的画面”。
I 帧:不参考其他画面
[ I0 ]解码 I0 时,不需要先得到其他画面。I 帧仍然经过压缩,不等于一张原始图片。
记住“解码 I 帧不需要别的画面”即可。
P 帧:使用一组参考画面
[ I0 ] ──参考──► [ P1 ]解码 P1 时,可以使用已经解码的参考画面。常见情况下,参考画面位于 P1 的显示时间之前。
P 帧允许从一组参考画面取得预测结果。
B 帧:可以组合两组参考
[ I0 ] ──参考──► [ B1 ] ◄──参考── [ P2 ]这个例子中,B1 位于 I0 和 P2 之间。编码器可以分别从两组参考画面取得预测结果,再组合使用。
P 帧使用一组参考画面候选;B 帧有两组,因此可以组合两个预测结果。
实际编码时,B 帧不一定每次都同时使用两边,但它允许使用两组参考。
三种帧最后都要得到完整画面
播放器不能把“参考关系”直接显示在屏幕上。解码器要先根据编码数据把当前画面重建完整,再交给播放器。
I:使用当前画面自己的编码数据P:参考已解码画面,再补上当前画面不同的部分B:可以组合两组参考,再补上当前画面不同的部分
解码完成后:三者都是完整画面编码器根据参考画面算出的近似结果叫预测(Prediction)。预测与当前原始画面之间仍有差别,这部分叫残差(Residual)。解码器把预测结果和还原后的残差合在一起,得到当前画面。
下面的动画只演示这一步。先选择 P,连续点击“下一步”;再切换 I 和 B,看参考画面来自哪里。
交互演示需要 JavaScript。文字版:I 不使用其他画面;P 使用一组参考画面;本例 B 使用两组参考画面。解码器把预测结果和残差合在一起,得到当前画面。
动画只展示参考、预测和残差之间的关系,不代表实际编码过程的全部细节。
B1 为什么要等 P2
再看一次 B 帧的例子:
显示顺序:I0 → B1 → P2参考关系:I0 → B1 ← P2B1 要用 P2 作参考,因此解码器必须先得到 P2:
解码顺序:I0 → P2 → B1显示顺序:I0 → B1 → P2P2 只是显示得比 B1 晚,并不是在 B1 解码完成以后才到达。编码数据会按解码器需要的顺序提供,播放器再按显示顺序输出画面。
交互演示需要 JavaScript。本例先解码 I0 和 P2,再用它们重建 B1;显示时仍按 I0、B1、P2 排列。
FFmpeg 在这里做什么
FFmpeg 读取压缩视频后,解码器会按参考关系还原画面。还原后的 Frame 才能交给 Filter 处理。
ffprobe -show_frames 可以查看解码得到的 Frame 信息,其中 pict_type 会显示常见的 I、P、B 类型。这是观察编码结果,不是 FFmpeg 自己重新发明了三种帧。
到这里就够了
- I、P、B 解码后都是完整画面。
- P 使用一组参考画面;B 有两组参考画面,并且可以组合两个预测结果。
- B 帧使用显示时间更晚的画面作参考时,解码顺序和显示顺序会不同。
如果这三点已经能用自己的话说出来,本篇就可以停在这里。
下一篇再讲H.264、H.265、码流和 MP4。RTSP、RTP、Packet、GOP、IDR 等名称不会在本篇提前展开。
资料来源
- ITU-T H.264、ITU-T H.265:图像编码类型、参考图像和解码过程。
- RFC 7798 §1.1:H.264/HEVC 使用帧内、帧间预测和残差编码的概述。
- FFmpeg 官方命令行文档、ffprobe 官方文档:解码、Frame、Filtergraph 与
-show_frames。
文中的 I0、B1、P2 只表示画面在示例中的显示位置;参考关系和处理顺序依据上述规范简化绘制。
评论