ffmpeg中有不少已經實現好的濾波器,這些濾波器的實現位於libavfilter目錄之下,用戶須要進行濾波時,就是是調用這些濾波器來實現的。ffmpeg對於調用濾波器有一整套的調用機制。html
咱們把一整個濾波的流程稱爲濾波過程。下面是一個濾波過程的結構ide
圖中簡要指示出了濾波所用到的各個結構體,各個結構體有以下做用:函數
AVFilterGraph | 用於統合這整個濾波過程的結構體。 |
AVFilter | 濾波器,濾波器的實現是經過AVFilter以及位於其下的結構體/函數來維護的。 |
AVFilterContext | 一個濾波器實例,即便是同一個濾波器,可是在進行實際的濾波時,也會因爲輸入的參數不一樣而有不一樣的濾波效果,AVFilterContext就是在實際進行濾波時用於維護濾波相關信息的實體。 |
AVFilterLink | 濾波器鏈,做用主要是用於鏈接相鄰的兩個AVFilterContext。爲了實現一個濾波過程,可能會須要多個濾波器協同完成,即一個濾波器的輸出可能會是另外一個濾波器的輸入,AVFilterLink的做用是串聯兩個相鄰的濾波器實例,造成兩個濾波器之間的通道。 |
AVFilterPad | 濾波器的輸入輸出端口,一個濾波器能夠有多個輸入以及多個輸出端口,相鄰濾波器之間是經過AVFilterLink來串聯的,而位於AVFilterLink兩端的分別就是前一個濾波器的輸出端口以及後一個濾波器的輸入端口。 |
buffersrc | 一個特殊的濾波器,這個濾波器的做用就是充當整個濾波過程的入口,經過調用該濾波器提供的函數(如av_buffersrc_add_frame)能夠把須要濾波的幀傳輸進入濾波過程。在建立該濾波器實例的時候須要提供一些關於所輸入的幀的格式的必要參數(如:time_base、圖像的寬高、圖像像素格式等)。 |
buffersink | 一個特殊的濾波器,這個濾波器的做用就是充當整個濾波過程的出口,經過調用該濾波器提供的函數(如av_buffersink_get_frame)能夠提取出被濾波過程濾波完成後的幀。 |
建立整個濾波過程包含如下步驟:htm
首先須要獲得整個濾波過程所需的濾波器(AVFilter),其中buffersrc以及buffersink是做爲輸入以及輸出所必須的兩個濾波器。blog
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)ip
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);
提交整個濾波圖get
avfilter_graph_config(filter_graph, NULL);
當濾波過程複雜到必定程度時,即須要多個濾波器進行復雜的鏈接來實現整個濾波過程,這時候對於調用者來講,繼續採用上述方法來構建濾波圖就顯得不夠效率。對於複雜的濾波過程,ffmpeg提供了一個更爲方便的濾波過程建立方式。input
這種複雜的濾波器過程建立方式要求用戶以字符串的方式描述各個濾波器之間的關係。以下是一個描述複雜濾波過程的字符串的例子:
[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]
以上是一個連續的字符串,爲了方便分析咱們把該字符串進行了劃分,每一行都是一個濾波器實例,對於一行:
按照這種規則,上面的濾波過程能夠被描繪成如下濾波圖:
ffmpeg提供一個函數用於解析這種字符串: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);
上面主要討論瞭如何建立濾波過程,不過要進行濾波還須要把幀傳輸進入該過程,並在濾波完成後從該過程當中提取出濾波完成的幀。
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則表示提取成功。