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