zoukankan      html  css  js  c++  java
  • 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的运转都是由调用者驱动.

  • 相关阅读:
    《C# to IL》第一章 IL入门
    multiple users to one ec2 instance setup
    Route53 health check与 Cloudwatch alarm 没法绑定
    rsync aws ec2 pem
    通过jvm 查看死锁
    wait, notify 使用清晰讲解
    for aws associate exam
    docker 容器不能联网
    本地运行aws lambda credential 配置 (missing credential config error)
    Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?
  • 原文地址:https://www.cnblogs.com/jingzhishen/p/3713741.html
Copyright © 2011-2022 走看看