FFmpeg滤镜代码级分析

转载 2015年07月09日 06:49:24


摘自: http://blog.csdn.net/fireroll/article/details/8607950         北雨南萍


前一篇文章《为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!\n");
  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, "=,;[\n");
  7.     int ret;

  8.     if (**buf == '=') {
  9.         (*buf)++;
  10.         opts = av_get_token(buf, "[],;\n");
  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 (= 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 (= 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.     picref->buf->data[0] = NULL;
  13.     avfilter_unref_buffer(picref);

  14.     return ret;
  15. }


  16. int av_vsrc_buffer_add_video_buffer_ref(AVFilterContext *buffer_filter,
  17.                                         AVFilterBufferRef *picref, int flags)"
  18. {

  19.     智能插入相应的filter; 

  20.     c->picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE,
  21.                                           picref->video->w, picref->video->h);

  22.     av_image_copy(c->picref->data, c->picref->linesize,
  23.                   picref->data, picref->linesize,
  24.                   picref->format, picref->video->w, picref->video->h);
  25.     avfilter_copy_buffer_ref_props(c->picref, picref);

  26.     return 0;
  27. }


  28. AVFilterBufferRef *avfilter_get_video_buffer(AVFilterLink *link,
  29.                                       int perms, int w, int h)
  30. {
  31.     AVFilterBufferRef *ret = NULL;

  32.     if (link->dstpad->get_video_buffer)
  33.         ret = link->dstpad->get_video_buffer(link, perms, w, h);

  34.     if (!ret)
  35.         ret = avfilter_default_get_video_buffer(link, perms, w, h);

  36.     if (ret)
  37.         ret->type = AVMEDIA_TYPE_VIDEO;

  38.     return ret;
  39. }

  40. AVFilterBufferRef *get_video_buffer(AVFilterLink *link, int perms, 
  41.                                     int w, int h)
  42. {
  43.     AVFilterBufferRef *picref;
  44.     int width = FFALIGN(w, 32);
  45.     int height= FFALIGN(h+2, 32);
  46.     int i;

  47.     picref = avfilter_default_get_video_buffer(link, perms, width, height);

  48.     picref->video->w = w;
  49.     picref->video->h = h;

  50.     for (i = 0; i < 3; i++)
  51.         picref->data[i] += picref->linesize[i];

  52.     return picref;
  53. }
最后来看图像处理函数的实现:


点击(此处)折叠或打开

  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 (= 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==&& !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 *= link->src->priv;

  42.     if (!c->picref) {
  43.         av_log(link->src, AV_LOG_WARNING,
  44.                "request_frame() called with no available frame!\n");
  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.     avfilter_unref_buffer(c->picref);
  5.     c->picref = NULL;

  6.     return 0;
  7. }
完结。

版权声明:本文为博主原创文章,未经博主允许不得转载。


相关文章推荐

idea 的代码分析

IntelliJ IDEA 代码分析

FFmpeg滤镜代码级分析

前一篇文章《为FFmpeg添加自定义滤镜》详细讲述了FFmpeg的滤镜添加步骤,并给出了代码实例。 本文将以FFmpeg自带的deinterlace滤镜”yadif – yet another de...

FFmpeg滤镜代码级分析

前一篇文章《为FFmpeg添加自定义滤镜》详细讲述了FFmpeg的滤镜添加步骤,并给出了代码实例。 本文将以FFmpeg自带的deinterlace滤镜”yadif – yet another de...

ffmpeg滤镜

过滤器分类 1.1简单过滤器 在简单过滤器中,只包含一个输入和一个输出,并且输入输出是同一类型。在下面的处理过程中,仅仅是在解码和编码之间加上一个额外的过滤步骤。 简单过滤器由per-stream...

ffmpeg文档37-视频滤镜

37 视频滤镜 在配置编译FFmpeg时可以通过--disable-filters来禁止所有滤镜的编译。也可以配置编译脚本来输出所有包含进编译的滤镜信息。 下面是当前可用的视频滤镜介绍。 ...
  • isuker
  • isuker
  • 2016年05月20日 17:20
  • 1205

ffmpeg超详细综合教程(二)——为直播流添加滤镜

在上一篇文章中,讲解了如何利用ffmpeg实现摄像头直播,本文将在此基础上,实现一个可以选择各种视频滤镜的摄像头直播示例。本文包含以下内容 1、AVFilter的基本介绍 2、如何利用ffmpeg...

FFmpeg中的滤镜(filter)知识点记录

FFmpeg支持多种滤镜,查看全部滤镜 $ ffmpeg   -filters 1、滤镜图、滤镜链、滤镜之间关系 滤镜图(filtergraph):跟在 -vf 之后的就是一个滤镜图  滤镜链...
  • tkp2014
  • tkp2014
  • 2016年11月23日 20:30
  • 724
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:FFmpeg滤镜代码级分析
举报原因:
原因补充:

(最多只允许输入30个字)