新手學習FFmpeg - 通過API實現可控的Filter調用鏈


雖然通過聲明[x][y]avfilter=a=x:b=y;avfilter=xxx的方式可以創建一個可用的Filter調用鏈,並且在絕大多數場合下這種方式都是靠譜和實用的。 但如果想精細化的管理AVFilter調用鏈,例如根據某些條件來動態生成AVFilter Graph。這種聲明方式就不太靈活(也可以通過if判斷來動態組裝字符串,如果你非常喜歡這種字符串聲明方式,到此為止不在建議你往下閱讀了)。

首先快速溫習一下,如何創建一個AVFilter Graph

             +-------+             +---------------------+         +---------------+
             |buffer |             |Filter ..... Filter N|         |   buffersink  |
 ----------> |    |output|------>|input|            |output|---> |input|           |-------->
             +-------+             +---------------------+         +---------------+

創建三部曲:

  1. 初始化bufferbuffersink
  2. 初始化其它filter
  3. 設定Filter Graph的Input和Output。

其中bufferbuffersink分別代表Graph的起始和結束。

然后快速封裝args也就是movie=t.png[wm];[in][wm]overlay=10:20[out]這樣的filter-complex命令。 而且通過avfilter_graph_parse_ptr完成中間filter的初始化,

最后指定各個filter的input和output,一個graph就算搞定了。

好,下面來看如何通過API精細化生成AVFilter Graph。 生成下面的Graph:

             +-------+             +---------------------+         +---------------+
             |buffer |             |       Filter        |         |   buffersink  |
 ----------> |    |output|------>|input|    Fade      |output|---> |input|           |-------->
             +-------+             +---------------------+         +---------------+

首先初始化各個AVFilter。所有的AVFilter的初始化都可以簡化為兩步操作:

  • 通過avfilter_get_by_name查找指定的AVFilter
  • 通過avfilter_graph_create_filter初始化AVFilterContext

AVcodecAVCodecContext的關系一樣, 所有的AVFilter的執行都依靠對應的AVFilterContext(在ffmpeg開發中,每個組件都會對應一個上下文管理器,由這個上下文管理器封裝各種參數然后調用組件執行)。

通過avfilter_get_by_name生成AVFilter實例之后,緊跟着就需要調用avfilter_graph_create_filter初始化上下文管理器。

按照下面的流程,依次初始化三個AVFilter:

buffer_src = avfilter_get_by_name("buffer");
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffer_src, "in", args, NULL, filter_graph);

這里重點聊一下avfilter_graph_create_filter。 下面是函數原型:

int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
                                 const char *name, const char *args, void *opaque,
                                 AVFilterGraph *graph_ctx);

filt_ctx表示這個AVFilter的上下文管理器。

name表明的是AVFilter在Graph中的名稱,這個名稱叫啥不重要但必須唯一。 例如Fade AVFilter就可以叫做fade1,fade2或者ifade等等。

args則是這個AVFilter的參數, 注意僅僅是這個AVFilter的參數,不是整個graph的參數。再拿Fade舉例,args就可以是t=in:st=3:d=3

opaque一般給NULL就可以了。

初始完這三個AVFilter之后,就進入到本次文檔的重點: avfilter_link.

int avfilter_link(	AVFilterContext * 	src,
                    unsigned 	srcpad,
                    AVFilterContext * 	dst,
                    unsigned 	dstpad
)

avfilter_link分別用來鏈接兩個AVFilter(傳說中的一手托兩家)。 srcdst分別表示源Filter和目標Filter。 srcpad表示src第N個輸出端, dstpad表示dst第N個輸入端。 如果不好理解,直接看下面的圖:


    +-------------+                  +-------------+
    |  src      srcpad 1 ----->   dstpad 3    dst  |
    |           srcpad 2 ----->   dstpad 2         |
    |           srcpad 3 ----->   dstpad 1         |
    +-------------+                  +-------------+

上圖假設srcdst分別有三個輸出端和三個輸入端(不是所有avfilter都有這么多的輸入輸出端,像fade只有一個,但overlay就有多個)。

srcpaddstpad表示的就是輸出/輸入端的序號。假如將buffer第一個輸出端對應fade第一個輸入端。 那么就應該這么寫:

avfilter_link(buffersrc_ctx, 0, ifade_ctx, 0);

然后將fade的第一個輸出端對應buffersink的輸入端,就這么寫:

avfilter_link(ifade_ctx, 0, buffersink_ctx, 0);

而所謂的精細化就是在這里體現的,通過代碼的邏輯判斷,可以動態的組合不同的AVFilter生成不同的Filter Graph。並且還可以組合不同的輸入/輸出端。

本次代碼示例可以參考ifilter。同時也可以參考 ffmpeg-go-server(一個嘗試為ffmpeg提供restful API的web server)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM