zoukankan      html  css  js  c++  java
  • FFmpeg滤镜代码级分析

    http://blog.chinaunix.net/uid-26000296-id-3322071.html

    前一篇文章《为FFmpeg添加自定义滤镜》详细讲述了FFmpeg的滤镜添加步骤,并给出了代码实例。

    本文将以FFmpeg自带的deinterlace滤镜”yadif – yet another deinterlace filter”为例分析

    FFmpeg滤镜的代码级实现机制。

     

    总的来说,FFmpeg的滤镜机制和MicroSoft Directshow机制基本相同——不知道谁学了谁的,还是程

    序员所见略同:

    构建机制上:FFmpeg自己实现了统一的对象模型,DShow是COM的实现;

    框架实现上:都使用filter graph来管理滤镜;

    连接方式上:FFmpeg使用Pad结构体,DShow使用Pin,两者都是分配器的机制;

     

    下面是以层级来分析代码:

    FFmpeg转码层的伪码如下所示,红色粗体是和filter相关的函数:

    点击(此处)折叠或打开

    1. int transcode(AVFormatContext **output_files,
    2.               int nb_output_files,
    3.               AVInputFile *input_files,
    4.               int nb_input_files,
    5.               AVStreamMap *stream_maps, 
    6.               int nb_stream_maps)
    7. {
    8.   …
    9.   解码参数计算;
    10.   #if CONFIG_AVFILTER
    11.   if (configure_video_filters(ist, ost)) {
    12.     fprintf(stderr, "Error opening filters! ");
    13.     ret = AVERROR(EINVAL);
    14.     goto fail;
    15.   }
    16.   #endif
    17.   编码参数计算;
    18.   output_packet(ist, ist_index, ost_table,
    19.                 nb_ostreams, &pkt, &errorflag);
    20.   ……
    21.   return ret;
    22. }

    函数configure_video_filters是用来初始化filter graph 和filter本身的初始化;

    函数output_packet是FFmpeg整个解码,滤镜处理,编码调用的实现。

     

    1. 先来看configure_video_filters的实现:

    点击(此处)折叠或打开

    1. int configure_video_filters(AVInputStream *ist, 
    2.                             AVOutputStream *ost)
    3. {
    4.    // 类似于directshow一样初始化filter graph等;
    5.   ost->graph = avfilter_graph_alloc();
    6.   …
        // 初始化filter graph和filter
    1.   avfilter_graph_parse(ost->graph, ost->avfilter, 
    2.                        &inputs, &outputs, NULL)
    3.   // 检查filter支持的媒体类型
    4.   avfilter_graph_config(ost->graph, NULL);
    5.   。。。
    6.   return 0;
    7. }

    filter graph和filter的初始化:

    点击(此处)折叠或打开

    1. int avfilter_graph_parse(AVFilterGraph *graph, 
    2.                          const char *filters,
    3.                          AVFilterInOut **open_inputs, 
    4.                          AVFilterInOut **open_outputs,
    5.                          void *log_ctx)
    6. {
    7.     do {
    8.         parse_inputs(&filters, &curr_inputs, open_outputs, log_ctx);
    9.         parse_filter(&filter, &filters, graph, index, log_ctx);
    10.         if (filter->input_count == 1 && !curr_inputs && !index) {
    11.             /* First input can be omitted if it is "[in]" */
    12.             const char *tmp = "[in]";
    13.             if ((ret = parse_inputs(&tmp, &curr_inputs, open_outputs, 
    14.                                     log_ctx)) < 0)
    15.                 goto fail;
    16.         }
    17.         link_filter_inouts(filter, &curr_inputs, open_inputs, log_ctx);
    18.         parse_outputs(&filters, &curr_inputs, open_inputs, 
    19.                       open_outputs, log_ctx);
    20.         filters += strspn(filters, WHITESPACES);
    21.         chr = *filters++;
    22.         index++;
    23.     } while (chr == ',' || chr == ';');
    24.     if (open_inputs && *open_inputs && 
    25.         !strcmp((*open_inputs)->name, ""out"") && curr_inputs) {
    26.         /* Last output can be omitted if it is "[out]" */
    27.         const char *tmp = "[out]";
    28.         if ((ret = parse_outputs(&tmp, &curr_inputs, open_inputs, 
    29.                                               open_outputs, log_ctx)) < 0)
    30.             goto fail;
    31.     }
    32.     return 0;
    33. }

    点击(此处)折叠或打开

    1. static int parse_filter(AVFilterContext **filt_ctx, 
    2.                         const char **buf, AVFilterGraph *graph,"
    3.                         int index, void *log_ctx)
    4. {
    5.     char *opts = NULL;
    6.     char *name = av_get_token(buf, "=,;[ ");
    7.     int ret;
    8.     if (**buf == '=') {
    9.         (*buf)++;
    10.         opts = av_get_token(buf, "[],; ");
    11.     }
    12.     ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
    13.     av_free(name);
    14.     av_free(opts);
    15.     return ret;
    16. }

    到了真正的Filter graph初始化,这部分和DShow很相似:

    点击(此处)折叠或打开

    1. int create_filter(AVFilterContext **filt_ctx, 
    2.                   AVFilterGraph *ctx, int index,
    3.                   const char *filt_name, const char *args, 
    4.                   void *log_ctx)
    5. {
    6.     AVFilter *filt;
    7.     char inst_name[30];
    8.     char tmp_args[256];
    9.     int ret;
    10.     // 注册当前filter到静态数组中
    11.     filt = avfilter_get_by_name(filt_name);
    12.     // 分配并初始化输入输出Pad
    13.     ret = avfilter_open(filt_ctx, filt, inst_name);
    14.     // 将当前filter添加到filter graph中
    15.     ret = avfilter_graph_add_filter(ctx, *filt_ctx);
    16.     // 通过函数指针,调用当前filter的初始化函数
    17.     ret = avfilter_init_filter(*filt_ctx, args, NULL);
    18.     return 0;
    19. }

    点击(此处)折叠或打开

    1. int avfilter_init_filter(AVFilterContext *filter, 
    2.                          const char *args, 
    3.                          void *opaque)
    4. {
    5.     int ret=0;
    6.     if (filter->filter->init)
    7.         ret = filter->filter->init(filter, args, opaque);
    8.     return ret;
    9. }

    点击(此处)折叠或打开

    1. static av_cold int init(AVFilterContext *ctx, 
    2.                                   const char *args, 
    3.                                   void *opaque)
    4. {
    5.     YADIFContext *yadif = ctx->priv;
    6.     av_unused int cpu_flags = av_get_cpu_flags();
    7.     yadif->mode = 0;
    8.     yadif->parity = -1;
    9.     yadif->csp = NULL;
    10.     // 滤镜函数指针初始化赋值
    11.     yadif->filter_line = filter_line_c;
    12.     return 0;
    13. }

    2. 接着来看两个filter的Pad连接时的媒体类型的协商机制

    点击(此处)折叠或打开

    1. int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx)
    2. {
    3.     int ret;
         // 检查filter的权限
    1.     if ((ret = ff_avfilter_graph_check_validity(graphctx, log_ctx)))
    2.         return ret;
    3.     
    4.     // 检查Pad支持的媒体类型
    5.     if ((ret = ff_avfilter_graph_config_formats(graphctx, log_ctx)))
    6.         return ret;
         // 
    1.     if ((ret = ff_avfilter_graph_config_links(graphctx, log_ctx)))
    2.         return ret;
    3.     return 0;
    4. }

    检查Pad支持的媒体类型 :

    点击(此处)折叠或打开

    1. int ff_avfilter_graph_config_formats(AVFilterGraph *graph, 
    2.                                                           AVClass *log_ctx)
    3. {
    4.     int ret;
    5.     /* find supported formats from sub-filters, and merge along links */
    6.     if ((ret = query_formats(graph, log_ctx)) < 0)
    7.         return ret;
    8.     /* Once everything is merged, it's possible that we'll still have
    9.      * multiple valid media format choices. We pick the first one. */
    10.     pick_formats(graph);
    11.     return 0;
    12. }

    点击(此处)折叠或打开

    1. static int query_formats(AVFilterGraph *graph, 
    2.                          AVClass *log_ctx)
    3. {
    4.     int i, j, ret;
    5.     int scaler_count = 0;
    6.     char inst_name[30];
    7.     /* ask all the sub-filters for their supported media formats */
    8.     for (i = 0; i < graph->filter_count; i++) {
    9.         if (graph->filters[i]->filter->query_formats)
    10.             graph->filters[i]->filter->query_formats(graph->filters[i]);
    11.         else
    12.             avfilter_default_query_formats(graph->filters[i]);
    13.     }
    14.     /* go through and merge as many format lists as possible */
    15.     for (i = 0; i < graph->filter_count; i++) {
    16.        … 
    17.     
    18.     }
    19.     return 0;
    20. }

    点击(此处)折叠或打开

    1. /* 检查连接用Pad支持的媒体类型 */
    2. static int query_formats(AVFilterContext *ctx)
    3. {
    4.     static const enum PixelFormat pix_fmts[] = {
    5.         PIX_FMT_YUV420P,
    6.         PIX_FMT_YUV422P,
    7.         PIX_FMT_YUV444P,
    8.         PIX_FMT_YUV410P,
    9.         PIX_FMT_YUV411P,
    10.         PIX_FMT_GRAY8,
    11.         PIX_FMT_YUVJ420P,
    12.         PIX_FMT_YUVJ422P,
    13.         PIX_FMT_YUVJ444P,
    14.         AV_NE( PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE ),
    15.         PIX_FMT_YUV440P,
    16.         PIX_FMT_YUVJ440P,
    17.         AV_NE( PIX_FMT_YUV420P16BE, PIX_FMT_YUV420P16LE ),
    18.         AV_NE( PIX_FMT_YUV422P16BE, PIX_FMT_YUV422P16LE ),
    19.         AV_NE( PIX_FMT_YUV444P16BE, PIX_FMT_YUV444P16LE ),
    20.         PIX_FMT_NONE
    21.     };
    22.     avfilter_set_common_pixel_formats(ctx, 
    23.                                       avfilter_make_format_list(pix_fmts));
    24.     return 0;
    25. }

    3. 最后来看滤镜的连接和对每帧图像处理时的调用

    点击(此处)折叠或打开

    1. int output_packet(AVInputStream *ist, int ist_index,
    2.                   AVOutputStream **ost_table,
    3.                   int nb_ostreams,
    4.                   const AVPacket *pkt, 
    5.                   int *errorflag)
    6. {
    7.   /* decode the packet if needed */
    8.   if (ist->decoding_needed) {
    9.     解码;
    10. #if CONFIG_AVFILTER
    11.     if(ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    12.     if (start_time == 0 || ist->pts >= start_time) {
    13.       for(i=0;i<nb_ostreams;i++) {
    14.         ost = ost_table[i];
    15.         if (ost->input_video_filter && ost->source_index == ist_index) {
    16.           if (!picture.sample_aspect_ratio.num)
    17.           picture.sample_aspect_ratio = ist->st->sample_aspect_ratio;
    18.           picture.pts = ist->pts;
    19.           av_vsrc_buffer_add_frame(ost->input_video_filter, &picture, AV_VSRC_BUF_FLAG_OVERWRITE);
    20.         }
    21.       }
    22.     }
    23. #endif
    24. #if CONFIG_AVFILTER
    25.     frame_available = ist->st->codec->codec_type
    26.                       != AVMEDIA_TYPE_VIDEO ||
    27.                       !ost->output_video_filter ||
    28.                       avfilter_poll_frame(ost->output_video_filter->inputs[0]);
    29.     while (frame_available) {
    30.       if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
    31.           ost->output_video_filter) {
    32.         AVRational ist_pts_tb = ost->output_video_filter->inputs[0]->time_base;
    33.         if (av_vsink_buffer_get_video_buffer_ref(ost->output_video_filter,
    34.             &ost->picref, 0) < 0)
    35.           goto cont;
    36.         if (ost->picref) {
    37.           AVRational tempVar = {1, AV_TIME_BASE};
    38.           avfilter_fill_frame_from_video_buffer_ref(&picture, ost->picref);
    39.           //ist->pts = av_rescale_q(ost->picref->pts, ist_pts_tb, AV_TIME_BASE_Q);
    40.           ist->pts = av_rescale_q(ost->picref->pts, ist_pts_tb, tempVar);//modify by chenrui
    41.         }
    42.       }
    43. #endif
    44.       编码;
    45.       return 0;
    46.       fail_decode:
    47.       return -1;
    48.    }
    49.   }
    50. }

    先来看滤镜的连接:

    点击(此处)折叠或打开

    1. int av_vsrc_buffer_add_frame(AVFilterContext *buffer_src,
    2.                              const AVFrame *frame, int flags)
    3. {
    4.     // 从帧列表中得到当前帧
    5.     AVFilterBufferRef *picref =
    6.         avfilter_get_video_buffer_ref_from_frame(frame, 
    7.                                                  AV_PERM_WRITE);
    8.     if (!picref)
    9.         return AVERROR(ENOMEM);
    10.     ret = av_vsrc_buffer_add_video_buffer_ref(buffer_src, 
    11.                                               picref, flags);
    12.  
    13.  
    14.     picref->buf->data[0] = NULL;
    15.     avfilter_unref_buffer(picref);
    16.  
    17.     return ret;
    18. }
    19.  
    20.  
    21. int av_vsrc_buffer_add_video_buffer_ref(AVFilterContext *buffer_filter,
    22.                                         AVFilterBufferRef *picref, int flags)"
    23. {
    24.  
    25.     智能插入相应的filter; 
    26.  
    27.     c->picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE,
    28.                                           picref->video->w, picref->video->h);
    29.  
    30.     av_image_copy(c->picref->data, c->picref->linesize,
    31.                   picref->data, picref->linesize,
    32.                   picref->format, picref->video->w, picref->video->h);
    33.     avfilter_copy_buffer_ref_props(c->picref, picref);
    34.  
    35.     return 0;
    36. }
    37.  
    38.  
    39. AVFilterBufferRef *avfilter_get_video_buffer(AVFilterLink *link,
    40.                                       int perms, int w, int h)
    41. {
    42.     AVFilterBufferRef *ret = NULL;
    43.  
    44.     if (link->dstpad->get_video_buffer)
    45.         ret = link->dstpad->get_video_buffer(link, perms, w, h);
    46.  
    47.     if (!ret)
    48.         ret = avfilter_default_get_video_buffer(link, perms, w, h);
    49.  
    50.     if (ret)
    51.         ret->type = AVMEDIA_TYPE_VIDEO;
    52.  
    53.     return ret;
    54. }
    55.  
    56. AVFilterBufferRef *get_video_buffer(AVFilterLink *link, int perms, 
    57.                                     int w, int h)
    58. {
    59.     AVFilterBufferRef *picref;
    60.     int width = FFALIGN(w, 32);
    61.     int height= FFALIGN(h+2, 32);
    62.     int i;
    63.  
    64.     picref = avfilter_default_get_video_buffer(link, perms, width, height);
    65.  
    66.     picref->video->w = w;
    67.     picref->video->h = h;
    68.  
    69.     for (i = 0; i < 3; i++)
    70.         picref->data[i] += picref->linesize[i];
    71.  
    72.     return picref;
    73. }

    最后来看图像处理函数的实现:

    点击(此处)折叠或打开

    1. int avfilter_poll_frame(AVFilterLink *link)
    2. {
    3.     int i, min = INT_MAX;
    4.     if (link->srcpad->poll_frame)
    5.         return link->srcpad->poll_frame(link);
    6.     for (i = 0; i < link->src->input_count; i++) {
    7.         int val;
    8.         if (!link->src->inputs[i])
    9.             return -1;
    10.         val = avfilter_poll_frame(link->src->inputs[i]);
    11.         min = FFMIN(min, val);
    12.     }
    13.     return min;
    14. }
    15. int poll_frame(AVFilterLink *link)
    16. {
    17.     YADIFContext *yadif = link->src->priv;
    18.     int ret, val;
    19.     if (yadif->frame_pending)
    20.         return 1;
    21.     val = avfilter_poll_frame(link->src->inputs[0]);
    22.     if (val==1 && !yadif->next) { 
    23.       //FIXME change API to not requre this red tape
    24.       if ((ret = avfilter_request_frame(link->src->inputs[0])) < 0)
    25.         return ret;
    26.       val = avfilter_poll_frame(link->src->inputs[0]);
    27.     }
    28.     assert(yadif->next || !val);
    29.     return val * ((yadif->mode&1)+1);
    30. }
    31. int avfilter_request_frame(AVFilterLink *link)
    32. {
    33.     if (link->srcpad->request_frame)
    34.         return link->srcpad->request_frame(link);
    35.     else if (link->src->inputs[0])
    36.         return avfilter_request_frame(link->src->inputs[0]);
    37.     else return -1;
    38. }
    39. static int request_frame(AVFilterLink *link)
    40. {
    41.     BufferSourceContext *c = link->src->priv;
    42.     if (!c->picref) {
    43.         av_log(link->src, AV_LOG_WARNING,
    44.                "request_frame() called with no available frame! ");
    45.         return AVERROR(EINVAL);
    46.     }
         // 图像处理函数的真正实现
    1.     avfilter_start_frame(link, avfilter_ref_buffer(c->picref, ~0));
    2.     avfilter_draw_slice(link, 0, link->h, 1);
    3.     avfilter_end_frame(link);
    4.  
    5.     avfilter_unref_buffer(c->picref);
    6.     c->picref = NULL;
    7.     return 0;
    8. }

    完结。

  • 相关阅读:
    RegExp.$1
    Wide&Deep 模型学习教程
    docker 安装与使用的相关问题
    Centos 防火墙
    odoo ERP 系统安装与使用
    Linux 开机自动启动脚本
    intel RDT技术管理cache和memory_bandwidth
    tensorflow 中 inter_op 和 intra_op
    centos 7 安装 nginx 或 apache,及其比较
    Dependency injection in .NET Core的最佳实践
  • 原文地址:https://www.cnblogs.com/youngt/p/3737036.html
Copyright © 2011-2022 走看看