FFmpeg中的主动丢帧功能

1、主动丢帧的应用场景

视频丢帧有被动的情况,例如数据丢失损坏导致的丢帧;也有主动的丢帧,例如:

  1. 高倍速播放(4倍速、8倍速等),解码全部视频帧可能解码速度跟不上,丢弃一部分也不影响播放渲染效果
  2. 按一定的间隔截图
  3. 服务器发送码流出现网络拥塞严重的情况,需要主动丢帧降低拥塞

丢帧可以是解码前,也可以是解码后。视频解码算力开销大,与解码后丢帧相比,能在解码前丢帧可以降低算力开销。

2、FFmpeg解封装丢帧

FFmpeg libavformat解封装可以配置主动丢帧,配置的地方在AVStream::discard

enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed.

enum AVDiscard的定义:

/**
 * @ingroup lavc_decoding
 */
enum AVDiscard{
    /* We leave some space between them for extensions (drop some
     * keyframes for intra-only or drop just some bidir frames). */
    AVDISCARD_NONE    =-16, ///< discard nothing
    AVDISCARD_DEFAULT =  0, ///< discard useless packets like 0 size packets in avi
    AVDISCARD_NONREF  =  8, ///< discard all non reference
    AVDISCARD_BIDIR   = 16, ///< discard all bidirectional frames
    AVDISCARD_NONINTRA= 24, ///< discard all non intra frames
    AVDISCARD_NONKEY  = 32, ///< discard all frames except keyframes
    AVDISCARD_ALL     = 48, ///< discard all
};
  • AVDISCARD_NONREF  丢弃非参考帧
  • AVDISCARD_NONREF  丢弃B帧
  • AVDISCARD_NONINTRA 丢弃非I帧
  • AVDISCARD_NONKEY  丢弃非关键帧

这里值得一提的有两点:

  1. B帧可以是参考帧,所以FFmpeg区分定义了非参考帧和B帧
  2. I帧不等于关键帧(IDR帧),所以FFmpeg区分定义了non intra和non key

libavformat demux有主动丢帧能力,但是功能十分有限:

  1. 只有部分封装格式实现了,其他封装格式忽略discard配置
  2. 虽然enum AVDiscard有多个等级区分,但是封装格式因解析层度过浅,往往只能区分关键帧和非关键帧,没法进行精细的丢帧控制

怎样实现更精细的丢帧逻辑控制呢?我们往下看。

3、FFmpeg解码器丢帧

FFmpeg的解码器支持丢帧配置。看AVCodecContext的三个字段:


    /**

     * Skip loop filtering for selected frames.

     * - encoding: unused

     * - decoding: Set by user.

     */

    enum AVDiscard skip_loop_filter;



    /**

     * Skip IDCT/dequantization for selected frames.

     * - encoding: unused

     * - decoding: Set by user.

     */

    enum AVDiscard skip_idct;


    /**

     * Skip decoding for selected frames.

     * - encoding: unused

     * - decoding: Set by user.

     */

    enum AVDiscard skip_frame;
  • skip_loop_filter是跳过视频解码的一个环节:in-loop filter
  • skip_idct是跳过反离散余弦变换,一些老的编码格式有实现
  • skip_frame是按帧类型或者说帧的特性,来丢弃一些帧不解码

我们这里主要关注skip_frame。skip_frame是由解码器实现的,在解析到一定深度之后,根据skip_frame的配置,判断是否丢弃当前正在处理的视频帧,不做进一步的解码

越早做决策,越早放弃,节省的算力越多。所以实现方式上,当具备了所需的信息之后,解码器会尽早做丢帧处理,能解析完NALU header就丢帧的,那就不要再解析slice header了。

与libavformat demuxer丢帧相比,libavcodec decoder丢帧实现了精细控制。但是解码丢帧也有一个缺点:没法用在非解码的场景。例如服务器码流转发/分发遇到网络拥塞时,要按需丢弃视频数据包,解码丢帧无意义。

FFmpeg能否实现像demuxer一样不解码、像decoder一样精细控制丢帧逻辑呢?借助libavcodec cbs框架和bitstream filter的能力,我实现了这个功能。

4、FFmpeg bitstream filter 丢帧

libavcodec/cbs*(coded bitstream)是一套libavcodec内部使用的、多种编码格式通用的、码流解析修改合成工具,提供统一的接口,实现代码复用。一些新的parser是在cbs之上实现的,一些bsf(bitstream filter)也是借助cbs实现的。

cbs原来没有丢帧的功能,所以我们第一步是加上响应的API,见patch 1/6(https://patchwork.ffmpeg.org/project/ffmpeg/list/?series=8947&state=*)。

对于H.264、H.265来说,cbs能够解析到slice header,拥有足够的信息做丢弃判断。cbs H.264 H.265丢帧逻辑实现见patch 2/6,3/6。H.264、H.265判断IDR、I、B、非参考帧等的逻辑这里就不介绍了,感兴趣的可以看下代码。

cbs是FFmpeg内部的功能,不具有对外的API。怎样对外暴露新加的丢帧功能呢?我选择用filter_units bsf 实现,见patch 4/6。filter_units bsf原来是按照NALU的类型做丢帧的一个filter(比如你可以用它来过滤掉SEI),我新增的功能是按照enum AVDiscard来丢NALU。

patch 5/6是代码格式调整(FFmpeg因为用邮件做patch review,功能实现和代码格式调整往往要分开提交),patch 6/6是添加测试。

通过命令行的使用示例:

./ffmpeg -i /tmp/test.mp4 -c:v copy -bsf:v filter_units=discard=nonref /tmp/abc.mp4

输入的test.mp4的帧类型

GOP: IPPBPBPBPBPBPBPBPBPBPBPBPBPPBPBPBPBPBPBPBPBPBPBPBPPBBPBBPBPBPBPBPBPBPBPBPBPBPBPBBPBPBPBPBPBPBPBPBPBPBPPBPBPBPBPBPBPBPBPBPBPPPPPP 128 CLOSED
GOP: IBPPPBPBPPPPBPPBPPBPPPBPPPBPPPPPBBPBPBPPBPPBPBPBPBPBPPBPBPBBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPPBPPBPPPPPP 106 CLOSED
GOP: IPPPPBPBPBPPBPBPP 17 CLOSED

输出的abc.mp4的帧类型

GOP: IPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 68 CLOSED
GOP: IPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 65 CLOSED
GOP: IPPPPPPPPPPP 12 CLOSED

在实现这个功能的时候,我特意考虑了根据网络情况动态调整丢帧的情况,所以允许filter_units的discard配置随时可变,不需要重复的创建和销毁filter_units的实例。当然,ffmpeg命令无法展示这个功能,只有通过bsf API调用才能实现动态配置。

此外还考虑了一些细节,比如只丢弃视频帧数据,保留其他的NALU,典型场景是保留SEI。

还有一个有趣的应用场景,当你使用第三方解码器,特别是硬件解码的时候,第三方解码器没实现丢帧能力,你可以用filter_units做主动丢帧。视频播放特别是倍速播放会很有用处。

TODO:为更多编码格式添加实现。

作者:quink
来源:Fun With FFmpeg
原文:https://mp.weixin.qq.com/s/lZ1jzDy2FjMvn8HB8JngQA

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论