前一篇文章《为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. #if CONFIG_AVFILTER
  10. if (configure_video_filters(ist, ost)) {
  11. fprintf(stderr, "Error opening filters!\n");
  12. ret = AVERROR(EINVAL);
  13. goto fail;
  14. }
  15. #endif
  16. 编码参数计算;
  17. output_packet(ist, ist_index, ost_table,
  18.                 nb_ostreams, &pkt, &errorflag);
  19. ……
  20. return ret;
  21. }

函数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();
    // 初始化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. // 检查Pad支持的媒体类型
  4. if ((ret = ff_avfilter_graph_config_formats(graphctx, log_ctx)))
  5. 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. return 0;
  18. }

点击(此处)折叠或打开

  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 (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!\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. }

完结。

FFmpeg滤镜代码级分析相关推荐

  1. ZigBee组网(代码级分析)

    转自:http://blog.sina.com.cn/s/blog_9d48d26f01015tph.html(老实人的博客) 第一个功能:协调器的组网,终端设备和路由设备发现网络以及加入网络 //第 ...

  2. NVIDIA FFmpeg 转码技术分析

    NVIDIA FFmpeg 转码技术分析 所有从 Kepler 一代开始的 NVIDIA GPUs 都支持完全加速的硬件视频编码,而从费米一代开始的所有 GPUs 都支持完全加速的硬件视频解码.截至 ...

  3. Blueprint代码详细分析-Android10.0编译系统(七)

    摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的? 阅读本文大约需要花费18分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  4. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

  5. 【u-boot】uboot代码简要分析 (u-boot 移植)

    uboot代码简要分析 (u-boot 移植) 2012-12-19 22:46:04 [转] 先来看看源码目录结构,再按照代码的执行顺序简单地分析源码 1.U-boot源码整体框架 源码解压以后,我 ...

  6. rtsp流媒体播放器----ffmpeg相关代码走读(一)

    rtsp流媒体播放器--ffmpeg相关代码走读(一) 本文介绍avformat_open_input和init_input两个函数 直接贴代码如下 avformat_open_input 函数 in ...

  7. 视频特效-使用ffmpeg滤镜

    视频特效-使用ffmpeg滤镜 前言 ffmpeg的滤镜分为简单滤镜和复杂滤镜. 复杂滤镜存在多个输入和多个输出如图: 在命令行中可以通过 -filter_complex 或 -lavfi 来使用. ...

  8. 纯c语言编译器pelloc,大规模并行粒子模拟系统代码级优化研究和实现.pdf

    大规模并行粒子模拟系统代码级优化研究和实现.pdf 第25卷第9期 计算机与应用化学 V01.25.No.9 2008年9月28日 and ComputersAppIiedChemistry 大规模并 ...

  9. ffplay使用ffmpeg滤镜实现倍速播放

    第一章 自定义播放器接口 第二章 倍速播放(本章) ______第一节 sonic实现倍速播放 ______第二节 soundtouch实现倍速播放 ______第三节 ffmpeg滤镜实现倍速播放( ...

  10. fork的黑科技,它到底做了个啥,源码级分析linux内核的内存管理

    最近一直在学习linux内核源码,总结一下 https://github.com/xiaozhang8tuo/linux-kernel-0.11 一份带注释的源码,学习用. fork的黑科技,它到底做 ...

最新文章

  1. [译] 解密 Airbnb 的数据科学部门如何构建知识仓库
  2. .NET 2.0 泛型在实际开发中的一次小应用
  3. 【错误记录】解压 Linux 内核报错 ( Can not create symbolic link : 客户端没有所需的特权 | Windows 中配置 7z 命令行执行解压操作 )
  4. 新课标下计算机教学,浅析新课程标准下小学信息技术教学
  5. el-amap 第一次正常第二次报错_flutter run: build tools revision 报错解决
  6. Django(part23)--Django shell的使用
  7. 装 linux后 win7消失了,win7系统重装后ubuntu启动消失不见的解决方法
  8. 如何兼职创业并避免风险
  9. java客户端连接请求发不出去_java – Spring:客户端发送的请求在语法上不正确()...
  10. gogs可以自动化部署吗_效率提升利器:你还害怕自动化部署吗?
  11. 何为计算机视觉?计算机视觉与数字图像处理的区别、Opencv的起源。
  12. DSP CCS5.5安装步骤
  13. 48个快速操作office技巧
  14. jq 登陆界面 php,利用jquery制作用户登陆界面
  15. PDF Expert for Mac最新2020注册码激活版下载
  16. 多核cpu应用场景_1分钟搞懂 CPU该买多核还是高主频?
  17. OpenAVNU 带宽预留协议SRP代码分析
  18. 罗升阳 android系统源代码情景分析,Android系统源代码情景分析
  19. 上下定高 中间自适应_css经典布局——头尾固定高度中间高度自适应布局
  20. 状态码的含义,以及HTTP中常见的状态码

热门文章

  1. 交易平台谁能在移动支付的大潮中成为赢家?
  2. linux gcc下实现简单socket套接字小程序
  3. HBase 写优化之 BulkLoad 实现数据快速入库
  4. ElasticSearch2.1 基于空间位置geo_query距离计算
  5. SqlServer2008 R2 自动备份和自动清除过期备份
  6. 设置Webdriver启动chrome为默认用户的配置信息
  7. 《Adobe Premiere Pro CC经典教程(彩色版)》——第2课 设置项目 2.1 开始
  8. SSDP:DDoS***的“新宠”
  9. SharePoint 使用ECMAscript对象模型来读取帖子列表
  10. 深入 Java 调试体系: 第 1 部分,JPDA 体系概览