【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?
相關文章
相關標籤/搜索