ffmpeg的新東東:AVFilter

來源:互聯網
上載者:User
利用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.

static AVFilter input_filter =<br />{<br /> .name = "ffplay_input",</p><p> .priv_size = sizeof(FilterPriv),</p><p> .init = input_init,<br /> .uninit = input_uninit,</p><p> .query_formats = input_query_formats,</p><p> .inputs = (AVFilterPad[]) {{ .name = NULL }},<br /> .outputs = (AVFilterPad[]) {{ .name = "default",<br /> .type = AVMEDIA_TYPE_VIDEO,<br /> .request_frame = input_request_frame,<br /> .config_props = input_config_props, },<br /> { .name = NULL }},<br />};再實現AVFilter的回呼函數們:init()和uninit()--用於初始化/銷毀所用到的資源.
看一下ffplay.c中的實現:

static int input_init(AVFilterContext *ctx, const char *args, void *opaque)<br />{<br /> FilterPriv *priv = ctx->priv;<br /> AVCodecContext *codec;<br /> if(!opaque) return -1;</p><p> priv->is = opaque;<br /> codec = priv->is->video_st->codec;<br /> codec->opaque = ctx;<br /> if((codec->codec->capabilities & CODEC_CAP_DR1)) {<br /> av_assert0(codec->flags & CODEC_FLAG_EMU_EDGE);<br /> priv->use_dr1 = 1;<br /> codec->get_buffer = input_get_buffer;<br /> codec->release_buffer = input_release_buffer;<br /> codec->reget_buffer = input_reget_buffer;<br /> codec->thread_safe_callbacks = 1;<br /> }</p><p> priv->frame = avcodec_alloc_frame();</p><p> return 0;<br />}FilterPriv是ffplay實現的filter(也就是input_filter)的私人資料結構.主要的工作是分配了一個AVFrame,用於儲存從裝置取得的幀.uninit()更簡單,就不用看了.
還需實現output pad的request_frame(),才能使input_filter後面的filter擷取到幀

static int input_request_frame(AVFilterLink *link)<br />{<br /> FilterPriv *priv = link->src->priv;<br /> AVFilterBufferRef *picref;<br /> int64_t pts = 0;<br /> AVPacket pkt;<br /> int ret;</p><p> while (!(ret = get_video_frame(priv->is, priv->frame, &pts, &pkt)))<br /> av_free_packet(&pkt);<br /> if (ret < 0)<br /> return -1;</p><p> if(priv->use_dr1 && priv->frame->opaque) {<br /> picref = avfilter_ref_buffer(priv->frame->opaque, ~0);<br /> } else {<br /> picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h);<br /> av_image_copy(picref->data, picref->linesize,<br /> priv->frame->data, priv->frame->linesize,<br /> picref->format, link->w, link->h);<br /> }<br /> av_free_packet(&pkt);</p><p> avfilter_copy_frame_props(picref, priv->frame);<br /> picref->pts = pts;</p><p> avfilter_start_frame(link, picref);<br /> avfilter_draw_slice(link, 0, link->h, 1);<br /> avfilter_end_frame(link);</p><p> return 0;<br />}調用者從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的運轉都是由調用者驅動.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.