ffmpeg的新东东:AVFilter
http://blog.csdn.net/niu_gao/article/details/7219641
?
?
?
利用ffmpeg做圖像的pixel format轉換你還在用libswscale嗎?嘿嘿,過時啦!
ffmpeg中有了新東西:libavfilter.使用它,可以完全代替libswscale,并且可以自動完成一些復雜的轉換操作呢.libavfilter啊,用了都說好!但就是太復雜...
如果你僅僅是做圖像的pixel format處理,用libswscale是相當簡單,可以看看最新的ffplay.c中的代碼,被#if CONFIG_AVFILTER #endif包圍的代碼量非常大,而且讓人一上來看得一頭霧水,但為了趕潮流,我們還是得學習它啊...
先弄清楚avfilter中的幾個相關的概念(注意:如果沒有directShow基礎的同學看不懂以下解釋,請先學DirectShow的基本概念):
1 AVFilterGraph:幾乎完全等同與directShow中的fitlerGraph,代表一串連接起來的filter們.
AVFilter:代表一個filter.
AVFilterPad:代表一個filter的輸入或輸出口,等同于DShow中的Pin.只有輸出pad的filter叫source,只有輸入pad的tilter叫sink.
AVFilterLink:代表兩個連接的fitler之間的粘合物.
其實總體看起來,libavfitler跟DShow幾乎一樣了.
下面看一下AVFilter是如何被使用的,我們以ffplay.c為例吧,分析一下其中AVFilter相關的代碼.
1 產生graph:
AVFilterGraph *graph = avfilter_graph_alloc();
2 創建source
AVFilterContext *filt_src;
avfilter_graph_create_filter(&filt_src, &input_filter, "src",NULL, is, graph);
第一個參數是生成的filter(是一個source),第二個參數是一個AVFilter結構的實例,第三個參數是要創建的fitler的名字,第四個 參數是不知道什么用,第五個參數是user data(調用者的私有數據),第六個參數是graph的指針.其中第二個參數的實例必須由調用者自己實現,才能將幀送到graph中.
3 創建sink
AVFilterContext *filt_out;
ret = avfilter_graph_create_filter(&filt_out, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts, graph);
參數同上,不解釋.所創建的這個sink是一個buffersink,可參考libavfitler的源碼文件sink_buffer.c看看它是個什么 玩意.sink_buffer其實是一個能通過buffer輸出幀的sink,當然它的輸出不是通過pad,因為它后面沒有fitler了.用它做 sink,可以讓使用這個graph的代碼輕松取得graph處理后的幀.
4 連接source和sink
avfilter_link(filt_src, 0, filt_out, 0);
第一個參數是接在前面的filter,第二個參數是前fitler的要連接的pad的序號,第三個參數是后面的filter,第四個參數是后filter的要連接的pad.
4 對graph做最后的檢查
avfilter_graph_config(graph, NULL);
我們是從sink中取出處理完成的幀,所以最好把sink的引用保存下來,比如:
AVFilterContext *out_video_filter=filt_out;
6實現input_filter
由于input_filter是個source,所以只為它分配output pad,并且只有一個pad.
[cpp] view plaincopy 再實現AVFilter的回調函數們:init()和uninit()--用于初始化/銷毀所用到的資源.
看一下ffplay.c中的實現:
FilterPriv是ffplay實現的filter(也就是input_filter)的私有數據結構.主要的工作是分配了一個AVFrame,用于保存從設備取得的幀.uninit()更簡單,就不用看了.
還需實現output pad的request_frame(),才能使input_filter后面的filter獲取到幀
調用者從sink中獲取處理后的幀:
av_buffersink_get_buffer_ref(filt_out, &picref, 0);
獲取后的幀保存在picref中.這個函數會引起graph中的filter從后向前依次調用上一個filter的outpad的 request_frame(),最后調用到source的request_frame(),也就是 input_request_frame(),input_request_frame()調用get_video_frame()(見 ffplay.c)從設備獲取一幀(可能需要解碼),然后將這幀數據復制到picref中,filter們處理的幀是用 AVFilterBufferRef表示的.然后將幀的一些屬性也復制到picref中,最后調用avfilter_start_frame(link, picref);avfilter_draw_slice(link, 0, link->h, 1);avfilter_end_frame(link);來處理這一幀.這三個函數對應著pad上的三個函數指 針:start_frame,draw_slice,end_frame.以start_frame為例,其調用過程是這樣的:首先是source的 start_frame被調用,做一些必要的處理后,再調用連接到source之后的filter的start_frame.每個filter的 output pad都負責在這個函數中向下傳遞這個調用.當sink調用完start_frame()時再一層層返回到source的output pad.當這三個函數都被source的output pad調用完成后,這一幀的最終結果就出來了.于是可以用sink上獲得.
與DShow比較起來,avfilter沒有那些推模式,拉模式的概念,沒有在source的output pad上實現線程,整個graph的運轉都是由調用者驅動.
總結
以上是生活随笔為你收集整理的ffmpeg的新东东:AVFilter的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 避免Eclipse经常出现Out Of
- 下一篇: 分享“消防图纸”识图方法,让你一眼秒懂!