【FFmpeg小点记】AVDiscard的做用

声明定义

AVDiscard 定义在 avcode.h 中。内容以下:html

/**
  * @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
 };

上述是FFmpeg v4.1 中的定义。简单的中文翻译下:less

字段 中文解释
AVDISCARD_NONE 不丢弃
AVDISCARD_DEFAULT 丢弃 avi 中的无效数据(如:size == 0)
AVDISCARD_NONREF 丢弃全部的非参考帧
AVDISCARD_BIDIR 丢弃全部的双向帧
AVDISCARD_NONINTRA 丢弃全部非内帧
AVDISCARD_NONKEY 丢弃全部非关键帧
AVDISCARD_ALL 丢弃全部帧

从定义上能够看出,AVDiscard 枚举的做用是:过滤数据oop

应用场景

场景一

相信学习音视频的同窗都多多少少了解过ffmpeg或者ffplay的源码。其中 ffplay.c 中的 read_thread() 接口里有这样一段代码:学习

for (i = 0; i < ic->nb_streams; i++) {
        AVStream *st = ic->streams[i];
        enum AVMediaType type = st->codecpar->codec_type;
        
        // ++++++++注意看这里++++++++
        st->discard = AVDISCARD_ALL;
        // ------------------------
        
        if (type >= 0 && wanted_stream_spec[type] && st_index[type] == -1)
            if (avformat_match_stream_specifier(ic, st, wanted_stream_spec[type]) > 0)
                st_index[type] = i;
}

// ... 省略代码


// 若是存在音频流
if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
        stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
}
// 须要存在视频流
if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
        ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
}

接着看下 stream_component_open() 接口中的内容:google

// .... 省略代码

ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
switch (avctx->codec_type) {
    // do something ...
}

从上面的代码中,能够看出在执行 av_find_best_stream() 以前,首先使用 AVDISCARD_ALL 过滤了全部的流中的数据。接着在开始执行 av_read_frame() 以前,将所需流的 discard 的字段值置为 AVDISCARD_DEFAULT ,仅过滤流中的无效的数据。
【注】这种使用场景,我没有对比过有/无该操做之间的差别。目前还不是很清楚其带来的实际意义是什么?这里就算是对代码理解的记录吧。spa

场景二

相信在解码时,配置解码上下文(AVCodecContext)参数里有同窗应该配置过 skip_loop_filterskip_dictskip_frame这是那个参数吧。翻译

查看FFmpeg源码,会发现这三个变量的类型,恰好是 AVDiscard 。那么这种场景下,它是如何用的呢?相信使用过的同窗很清楚这种场景下是如何使用的。不清楚的同窗,能够参考下: Chromium中的ffplay.c 里的 ffplay.c 中的源码。3d

这里简单说下,这种场景下使用其的做用(以 skip_loop_filter 为例):
loop_filter 是指环路滤波的意思。
skip_loop_filter 是指对某些帧不使用环路滤波的意思。code

对哪些帧不使用环路滤波呢?可经过AVDiscard去指定。component

AVCodecContext *avctx = ...;
// 对全部的帧,都不使用环路滤波
avctx->skip_loop_filter = AVDISCARD_ALL;

// 对全部非关键帧,不使用环路滤波
avctx->skip_loop_filter = AVDISCARD_NONKEY;

// 其余 AVDISCARD_XXX 表示的含义同理。

skip_dictskip_frame 表示的含义同理。

查资料说,合理的配置这几个参数,能够提升画面清晰度,下降CPU负载。可是没有亲自踩坑分析数据,暂时记录到这里。

总结

总算是对这个 discard 有了一点点稍微清晰的了解了。可是还不清楚这样作的本质是什么(没有深刻跟踪代码, 但愿有大牛指导)?只是看到别人在这两个场景中使用。若是是本身写程序的,是否是还有其余场景可使用?

感谢

  1. ffmpeg: Decoding specific AVProgram from the hls stream
  2. Set AVDISCARD_ALL flag for disabled streams in FFmpegDemuxer (Closed)
  3. dts, pts, duration, repeat_pic and others
  4. IJKPlayer相关指南
  5. ffmpeg和ijkplayer里的skip_loop_filter
  6. How to properly seek in audio?
相关文章
相关标签/搜索