共计 2606 个字符,预计需要花费 7 分钟才能阅读完成。
声明定义
AVDiscard 定义在 avcode.h 中。内容如下:
/** | |
* @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 中的定义。简单的中文翻译下:
字段 | 中文解释 |
---|---|
AVDISCARD_NONE | 不丢弃 |
AVDISCARD_DEFAULT | 丢弃 avi 中的无效数据(如:size == 0) |
AVDISCARD_NONREF | 丢弃所有的非参考帧 |
AVDISCARD_BIDIR | 丢弃所有的双向帧 |
AVDISCARD_NONINTRA | 丢弃所有非内帧 |
AVDISCARD_NONKEY | 丢弃所有非关键帧 |
AVDISCARD_ALL | 丢弃所有帧 |
从定义上可以看出,AVDiscard 枚举的作用是: 过滤数据 。
应用场景
场景一
相信学习音视频的同学都多多少少了解过 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() 接口中的内容:
// .... 省略代码 | |
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,仅过滤流中的无效的数据。
【注】这种使用场景,我没有对比过有 / 无该操作之间的差异。目前还不是很清楚其带来的实际意义是什么?这里就算是对代码理解的记录吧。
场景二
相信在解码时,配置解码上下文(AVCodecContext)参数里有同学应该配置过 skip_loop_filter、skip_dict、skip_frame 这是那个参数吧。
查看 FFmpeg 源码,会发现这三个变量的类型,刚好是 AVDiscard。那么这种场景下,它是如何用的呢?相信使用过的同学很清楚这种场景下是如何使用的。不清楚的同学,可以参考下:Chromium 中的 ffplay.c 里的 ffplay.c 中的源码。
这里简单说下,这种场景下使用其的作用(以 skip_loop_filter 为例):
loop_filter 是指环路滤波的意思。
skip_loop_filter 是指对某些帧不使用环路滤波的意思。
对哪些帧不使用环路滤波呢?可通过 AVDiscard 去指定。
AVCodecContext *avctx = ...; | |
// 对所有的帧,都不使用环路滤波 | |
avctx->skip_loop_filter = AVDISCARD_ALL; | |
// 对所有非关键帧,不使用环路滤波 | |
avctx->skip_loop_filter = AVDISCARD_NONKEY; | |
// 其他 AVDISCARD_XXX 表示的含义同理。 |
skip_dict、skip_frame 表示的含义同理。
查资料说,合理的配置这几个参数,可以提高画面清晰度,降低 CPU 负载。但是没有亲自踩坑分析数据,暂时记录到这里。
总结
总算是对这个 discard 有了一点点稍微清晰的了解了。但是还不清楚这样做的本质是什么(没有深入跟踪代码, 希望有大牛指导 )?只是看到别人在这两个场景中使用。如果是自己写程序的,是不是还有其他场景可以使用?
感谢
- ffmpeg: Decoding specific AVProgram from the hls stream
- Set AVDISCARD_ALL flag for disabled streams in FFmpegDemuxer (Closed)
- dts, pts, duration, repeat_pic and others
- IJKPlayer 相关指南
- ffmpeg 和 ijkplayer 里的 skip_loop_filter
- How to properly seek in audio?