ffmpeg濾波
ffmpeg中有很多已經實現好的濾波器,這些濾波器的實現位於libavfilter目錄之下,使用者需要進行濾波時,就是是呼叫這些濾波器來實現的。ffmpeg對於呼叫濾波器有一整套的呼叫機制。
基本結構
我們把一整個濾波的流程稱為濾波過程。下面是一個濾波過程的結構
圖中簡要指示出了濾波所用到的各個結構體,各個結構體有如下作用:
AVFilterGraph | 用於統合這整個濾波過程的結構體。 |
AVFilter | 濾波器,濾波器的實現是通過AVFilter以及位於其下的結構體/函式來維護的。 |
AVFilterContext | 一個濾波器例項,即使是同一個濾波器,但是在進行實際的濾波時,也會由於輸入的引數不同而有不同的濾波效果,AVFilterContext就是在實際進行濾波時用於維護濾波相關資訊的實體。 |
AVFilterLink | 濾波器鏈,作用主要是用於連線相鄰的兩個AVFilterContext。為了實現一個濾波過程,可能會需要多個濾波器協同完成,即一個濾波器的輸出可能會是另一個濾波器的輸入,AVFilterLink的作用是串聯兩個相鄰的濾波器例項,形成兩個濾波器之間的通道。 |
AVFilterPad | 濾波器的輸入輸出埠,一個濾波器可以有多個輸入以及多個輸出埠,相鄰濾波器之間是通過AVFilterLink來串聯的,而位於AVFilterLink兩端的分別就是前一個濾波器的輸出埠以及後一個濾波器的輸入埠。 |
buffersrc | 一個特殊的濾波器,這個濾波器的作用就是充當整個濾波過程的入口,通過呼叫該濾波器提供的函式(如av_buffersrc_add_frame)可以把需要濾波的幀傳輸進入濾波過程。在建立該濾波器例項的時候需要提供一些關於所輸入的幀的格式的必要引數(如:time_base、影象的寬高、影象畫素格式等)。 |
buffersink | 一個特殊的濾波器,這個濾波器的作用就是充當整個濾波過程的出口,通過呼叫該濾波器提供的函式(如av_buffersink_get_frame)可以提取出被濾波過程濾波完成後的幀。 |
建立簡單的濾波過程
建立整個濾波過程包含以下步驟:
首先需要得到整個濾波過程所需的濾波器(AVFilter),其中buffersrc以及buffersink是作為輸入以及輸出所必須的兩個濾波器。
const AVFilter *buffersrc= avfilter_get_by_name("buffer"); const AVFilter *buffersink = avfilter_get_by_name("buffersink"); const AVFilter *myfilter= avfilter_get_by_name("myfilter");
建立統合整個濾波過程的濾波圖結構體(AVFilterGraph)
filter_graph = avfilter_graph_alloc();
建立用於維護濾波相關資訊的濾波器例項(AVFilterContext)
AVFilterContext *in_video_filter = NULL; AVFilterContext *out_video_filter = NULL; AVFilterContext *my_video_filter = NULL; avfilter_graph_create_filter(&in_video_filter, buffersrc, "in", args, NULL, filter_graph); avfilter_graph_create_filter(&out_video_filter, buffersink, "out", NULL, NULL, filter_graph); avfilter_graph_create_filter(&my_video_filter, myfilter, "myfilter", NULL, NULL, filter_graph);
用AVFilterLink把相鄰的兩個濾波例項連線起來
avfilter_link(in_video_filter, 0, my_video_filter, 0); avfilter_link(my_video_filter, 0, out_video_filter, 0);
提交整個濾波圖
avfilter_graph_config(filter_graph, NULL);
建立複雜的濾波過程
當濾波過程複雜到一定程度時,即需要多個濾波器進行復雜的連線來實現整個濾波過程,這時候對於呼叫者來說,繼續採用上述方法來構建濾波圖就顯得不夠效率。對於複雜的濾波過程,ffmpeg提供了一個更為方便的濾波過程建立方式。
這種複雜的濾波器過程建立方式要求使用者以字串的方式描述各個濾波器之間的關係。如下是一個描述複雜濾波過程的字串的例子:
[0]trim=start_frame=10:end_frame=20[v0];\ [0]trim=start_frame=30:end_frame=40[v1];\ [v0][v1]concat=n=2[v2];\ [1]hflip[v3];\ [v2][v3]overlay=eof_action=repeat[v4];\ [v4]drawbox=50:50:120:120:red:t=5[v5]
以上是一個連續的字串,為了方便分析我們把該字串進行了劃分,每一行都是一個濾波器例項,對於一行:
- 開頭是一對中括號,中括號內的是輸入的標識名0。
- 中括號後面接著的是濾波器名稱trim。
- 名稱後的第一個等號後面是濾波器引數start_frame=10:end_frame=20,這裡有兩組引數,兩組引數用冒號分開。
- 第一組引數名稱為start_frame,引數值為10,中間用等號分開。
- 第二組引數名稱為end_frame,引數值為20,中間用等號分開。
- 最後也有一對中括號,中括號內的是輸出的標識名v0。
- 如果一個濾波例項的輸入標識名與另一個濾波例項的輸出標識名相同,則表示這兩個濾波例項構成濾波鏈。
- 如果一個濾波例項的輸入標識名或者輸出標識名一直沒有與其它濾波例項的輸出標識名或者輸入標識名相同,則表明這些為外部的輸入輸出,通常我們會為其接上buffersrc以及buffersink。
按照這種規則,上面的濾波過程可以被描繪成以下濾波圖:
ffmpeg提供一個函式用於解析這種字串:ofollow,noindex" target="_blank">avfilter_graph_parse2 。這個函式會把輸入的字串生成如上面的濾波圖,不過我們需要自行生成buffersrc以及buffersink的例項,並通過該函式提供的輸入以及輸出介面把buffersrc、buffersink與該濾波圖連線起來。整個流程包含以下步驟:
建立統合整個濾波過程的濾波圖結構體(AVFilterGraph)
filter_graph = avfilter_graph_alloc();
解析字串,並構建該字串所描述的濾波圖
avfilter_graph_parse2(filter_graph, graph_desc, &inputs, &outputs);
其中inputs與outputs分別為輸入與輸出的介面集合,我們需要為這些介面接上輸入以及輸出。
for (cur = inputs, i = 0; cur; cur = cur->next, i++) { const AVFilter *buffersrc = avfilter_get_by_name("buffer"); avfilter_graph_create_filter(&filter, buffersrc, name, args, NULL, filter_graph); avfilter_link(filter, 0, cur->filter_ctx, cur->pad_idx); } avfilter_inout_free(&inputs); for (cur = outputs, i = 0; cur; cur = cur->next, i++) { const AVFilter *buffersink = avfilter_get_by_name("buffersink"); avfilter_graph_create_filter(&filter, buffersink, name, NULL, NULL, filter_graph); avfilter_link(cur->filter_ctx, cur->pad_idx, filter, 0); } avfilter_inout_free(&outputs);
提交整個濾波圖
avfilter_graph_config(filter_graph, NULL);
濾波API
上面主要討論瞭如何建立濾波過程,不過要進行濾波還需要把幀傳輸進入該過程,並在濾波完成後從該過程中提取出濾波完成的幀。
buffersrc提供了向濾波過程輸入幀的API:av_buffersrc_add_frame 。向指定的buffersrc例項輸入想要進行濾波的幀就可以把幀傳入濾波過程。
av_buffersrc_add_frame(c->in_filter, pFrame);
buffersink提供了從濾波過程提取幀的API:av_buffersink_get_frame 。可以從指定的buffersink例項提取濾波完成的幀。
av_buffersink_get_frame(c->out_filter, pFrame);
當av_buffersink_get_frame返回值大於0則表示提取成功。