日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ffmpeg的新东东:AVFilter

發布時間:2023/12/10 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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
  • static?AVFilter?input_filter?=??
  • {??
  • ????.name??????=?"ffplay_input",??
  • ??
  • ????.priv_size?=?sizeof(FilterPriv),??
  • ??
  • ????.init??????=?input_init,??
  • ????.uninit????=?input_uninit,??
  • ??
  • ????.query_formats?=?input_query_formats,??
  • ??
  • ????.inputs????=?(AVFilterPad[])?{{?.name?=?NULL?}},??
  • ????.outputs???=?(AVFilterPad[])?{{?.name?=?"default",??
  • ????????????????????????????????????.type?=?AVMEDIA_TYPE_VIDEO,??
  • ????????????????????????????????????.request_frame?=?input_request_frame,??
  • ????????????????????????????????????.config_props??=?input_config_props,?},??
  • ??????????????????????????????????{?.name?=?NULL?}},??
  • };??
  • 再實現AVFilter的回調函數們:init()和uninit()--用于初始化/銷毀所用到的資源.
    看一下ffplay.c中的實現:

    [cpp] view plaincopy
  • static?int?input_init(AVFilterContext?*ctx,?const?char?*args,?void?*opaque)??
  • {??
  • ????FilterPriv?*priv?=?ctx->priv;??
  • ????AVCodecContext?*codec;??
  • ????if(!opaque)?return?-1;??
  • ??
  • ????priv->is?=?opaque;??
  • ????codec????=?priv->is->video_st->codec;??
  • ????codec->opaque?=?ctx;??
  • ????if((codec->codec->capabilities?&?CODEC_CAP_DR1))?{??
  • ????????av_assert0(codec->flags?&?CODEC_FLAG_EMU_EDGE);??
  • ????????priv->use_dr1?=?1;??
  • ????????codec->get_buffer?????=?input_get_buffer;??
  • ????????codec->release_buffer?=?input_release_buffer;??
  • ????????codec->reget_buffer???=?input_reget_buffer;??
  • ????????codec->thread_safe_callbacks?=?1;??
  • ????}??
  • ??
  • ????priv->frame?=?avcodec_alloc_frame();??
  • ??
  • ????return?0;??
  • }??
  • FilterPriv是ffplay實現的filter(也就是input_filter)的私有數據結構.主要的工作是分配了一個AVFrame,用于保存從設備取得的幀.uninit()更簡單,就不用看了.
    還需實現output pad的request_frame(),才能使input_filter后面的filter獲取到幀

    [cpp] view plaincopy
  • static?int?input_request_frame(AVFilterLink?*link)??
  • {??
  • ????FilterPriv?*priv?=?link->src->priv;??
  • ????AVFilterBufferRef?*picref;??
  • ????int64_t?pts?=?0;??
  • ????AVPacket?pkt;??
  • ????int?ret;??
  • ??
  • ????while?(!(ret?=?get_video_frame(priv->is,?priv->frame,?&pts,?&pkt)))??
  • ????????av_free_packet(&pkt);??
  • ????if?(ret?<?0)??
  • ????????return?-1;??
  • ??
  • ????if(priv->use_dr1?&&?priv->frame->opaque)?{??
  • ????????picref?=?avfilter_ref_buffer(priv->frame->opaque,?~0);??
  • ????}?else?{??
  • ????????picref?=?avfilter_get_video_buffer(link,?AV_PERM_WRITE,?link->w,?link->h);??
  • ????????av_image_copy(picref->data,?picref->linesize,??
  • ??????????????????????priv->frame->data,?priv->frame->linesize,??
  • ??????????????????????picref->format,?link->w,?link->h);??
  • ????}??
  • ????av_free_packet(&pkt);??
  • ??
  • ????avfilter_copy_frame_props(picref,?priv->frame);??
  • ????picref->pts?=?pts;??
  • ??
  • ????avfilter_start_frame(link,?picref);??
  • ????avfilter_draw_slice(link,?0,?link->h,?1);??
  • ????avfilter_end_frame(link);??
  • ??
  • ????return?0;??
  • }??
  • 調用者從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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。