• 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.

    1. static AVFilter input_filter =  
    2. {  
    3.     .name      = "ffplay_input",  
    4.   
    5.     .priv_size = sizeof(FilterPriv),  
    6.   
    7.     .init      = input_init,  
    8.     .uninit    = input_uninit,  
    9.   
    10.     .query_formats = input_query_formats,  
    11.   
    12.     .inputs    = (AVFilterPad[]) {{ .name = NULL }},  
    13.     .outputs   = (AVFilterPad[]) {{ .name = "default",  
    14.                                     .type = AVMEDIA_TYPE_VIDEO,  
    15.                                     .request_frame = input_request_frame,  
    16.                                     .config_props  = input_config_props, },  
    17.                                   { .name = NULL }},  
    18. };  

    再实现AVFilter的回调函数们:init()和uninit()--用于初始化/销毁所用到的资源.
    看一下ffplay.c中的实现:

    1. static int input_init(AVFilterContext *ctx, const char *args, void *opaque)  
    2. {  
    3.     FilterPriv *priv = ctx->priv;  
    4.     AVCodecContext *codec;  
    5.     if(!opaque) return -1;  
    6.   
    7.     priv->is = opaque;  
    8.     codec    = priv->is->video_st->codec;  
    9.     codec->opaque = ctx;  
    10.     if((codec->codec->capabilities & CODEC_CAP_DR1)) {  
    11.         av_assert0(codec->flags & CODEC_FLAG_EMU_EDGE);  
    12.         priv->use_dr1 = 1;  
    13.         codec->get_buffer     = input_get_buffer;  
    14.         codec->release_buffer = input_release_buffer;  
    15.         codec->reget_buffer   = input_reget_buffer;  
    16.         codec->thread_safe_callbacks = 1;  
    17.     }  
    18.   
    19.     priv->frame = avcodec_alloc_frame();  
    20.   
    21.     return 0;  
    22. }  

    FilterPriv是ffplay实现的filter(也就是input_filter)的私有数据结构.主要的工作是分配了一个AVFrame,用于保存从设备取得的帧.uninit()更简单,就不用看了.
    还需实现output pad的request_frame(),才能使input_filter后面的filter获取到帧

    1. static int input_request_frame(AVFilterLink *link)  
    2. {  
    3.     FilterPriv *priv = link->src->priv;  
    4.     AVFilterBufferRef *picref;  
    5.     int64_t pts = 0;  
    6.     AVPacket pkt;  
    7.     int ret;  
    8.   
    9.     while (!(ret = get_video_frame(priv->is, priv->frame, &pts, &pkt)))  
    10.         av_free_packet(&pkt);  
    11.     if (ret < 0)  
    12.         return -1;  
    13.   
    14.     if(priv->use_dr1 && priv->frame->opaque) {  
    15.         picref = avfilter_ref_buffer(priv->frame->opaque, ~0);  
    16.     } else {  
    17.         picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h);  
    18.         av_image_copy(picref->data, picref->linesize,  
    19.                       priv->frame->data, priv->frame->linesize,  
    20.                       picref->format, link->w, link->h);  
    21.     }  
    22.     av_free_packet(&pkt);  
    23.   
    24.     avfilter_copy_frame_props(picref, priv->frame);  
    25.     picref->pts = pts;  
    26.   
    27.     avfilter_start_frame(link, picref);  
    28.     avfilter_draw_slice(link, 0, link->h, 1);  
    29.     avfilter_end_frame(link);  
    30.   
    31.     return 0;  
    32. }  

    调用者从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的运转都是由调用者驱动.

  • 相关阅读:
    ubuntu故障处理
    最全http状态码
    go故障排查集锦
    docker知识11---docker service
    docker知识10---docker secret
    windows安装mysql
    信息收集
    模块
    Django:中间件
    Django:ORM单表操作
  • 原文地址:https://www.cnblogs.com/jingzhishen/p/3713741.html
Copyright © 2020-2023  润新知