ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

FFMPEG中的两输入Filter实现(一)

2022-02-25 18:31:18  阅读:226  来源: 互联网

标签:filter FFMPEG inputs graph nb Filter outputs fg 输入


开帖大吉! 

利用FFMPEG工作已有一年多,许多学习文档散落在电脑各处,没有一个清晰明确的组织脉络;还有踩过又填平的各种坑,时间久了难免遗忘,再次遭遇时仍然要从头查起;而且事必躬亲也是毫无疑问的低效率,不利于后来同事的成长。因此有了开博的决定,希望记录下自己走过的脚印,见证自己的成长,也能帮助后来人。

近期在ffmpeg3.3版本上开发一个基于overlay滤镜的新功能,因此花了些时间把双输入滤镜的实现梳理了一番,整理的大致的函数调用关系图如下:最左侧浅黄色分支为滤镜注册;中间分支浅绿色部分是滤镜的解析和初始化;最右侧分支的浅紫色部分是滤镜的主体实现过程。因为涉及到的函数比较多,因此本篇先分析滤镜注册和初始化部分,滤镜的具体实现留到下一篇分析。

 


使用的命令行如下:

ffmpeg -i input.ts -i logo.png -c:v libx264 -s 1280x720 -b:v 2000k -filter_complex "[0:v][1:v]overlay=100:100"  -c:a copy -f mpegts -y overlayout.ts

一、滤镜的注册

 avfilter_register_all():ffmpeg中,任何滤镜在使用之前,必须先经过注册。overlay滤镜的注册如下:

REGISTER_FILTER(OVERLAY,        overlay,        vf);
再来看看最终调用的注册函数:

  1. int avfilter_register(AVFilter *filter)
  2. {
  3. AVFilter **f = last_filter;
  4. /* the filter must select generic or internal exclusively */
  5. av_assert0((filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE) != AVFILTER_FLAG_SUPPORT_TIMELINE);
  6. filter->next = NULL;
  7. while(*f || avpriv_atomic_ptr_cas((void * volatile *)f, NULL, filter))
  8. f = &(*f)->next;
  9. last_filter = &filter->next; return 0;
  10. }
所谓的滤镜注册,也就是把目标滤镜的结构体加入到滤镜结构体链表中。
二、滤镜的解析与初始化



由于overlay的输入含有两个或以上,因此采用的是complex filter。complex filter的解析和初始化在ffmpeg_parse_options阶段完成,下面我们来逐层分析该阶段对滤镜的操作。

1. init_complex_filters():

  1. static int init_complex_filters(void)
  2. {
  3. int i, ret = 0;
  4. for (i = 0; i < nb_filtergraphs; i++) {
  5. ret = init_complex_filtergraph(filtergraphs[i]);
  6. if (ret < 0)
  7. return ret;
  8. }
  9. return 0;
  10. }
逐个初始化滤镜图,nb_filtergraphs在解析参数阶段被赋值,在本例中,nb_filtergraphs = 1。


2. init_complex_filtergraph():

  1. int init_complex_filtergraph(FilterGraph *fg)
  2. {
  3. AVFilterInOut *inputs, *outputs, *cur;
  4. AVFilterGraph *graph;
  5. int ret = 0;
  6. /* this graph is only used for determining the kinds of inputs
  7. * and outputs we have, and is discarded on exit from this function */
  8. graph = avfilter_graph_alloc(); //为临时filter graph分配空间
  9. if (!graph)
  10. return AVERROR(ENOMEM);
  11. ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs); //解析并创建filter
  12. if (ret < 0)
  13. goto fail;
  14. for (cur = inputs; cur; cur = cur->next) //初始化输入
  15. init_input_filter(fg, cur);
  16. for (cur = outputs; cur;) { //初始化输出,此例中只有一个输出
  17. GROW_ARRAY(fg->outputs, fg->nb_outputs);
  18. fg->outputs[fg->nb_outputs - 1] = av_mallocz(sizeof(*fg->outputs[0]));
  19. if (!fg->outputs[fg->nb_outputs - 1])
  20. exit_program(1);
  21. fg->outputs[fg->nb_outputs - 1]->graph = fg;
  22. fg->outputs[fg->nb_outputs - 1]->out_tmp = cur;
  23. fg->outputs[fg->nb_outputs - 1]->type = avfilter_pad_get_type(cur->filter_ctx->output_pads,
  24. cur->pad_idx);
  25. fg->outputs[fg->nb_outputs - 1]->name = describe_filter_link(fg, cur, 0);
  26. cur = cur->next;
  27. fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL;
  28. }
  29. fail:
  30. avfilter_inout_free(&inputs); //删除分配的临时空间
  31. avfilter_graph_free(&graph);
  32. return ret;
  33. }

3. av_filter_graph_parse2():

  1. int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
  2. AVFilterInOut **inputs,
  3. AVFilterInOut **outputs)
  4. {
  5. int index = 0, ret = 0;
  6. char chr = 0;
  7. AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL;
  8. filters += strspn(filters, WHITESPACES);
  9. if ((ret = parse_sws_flags(&filters, graph)) < 0)
  10. goto fail;
  11. do {
  12. AVFilterContext *filter;
  13. filters += strspn(filters, WHITESPACES);
  14. if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0) //根据命令行解析filter的输入,此例中为[0:v]和[1:v]
  15. goto end;
  16. if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0) //解析filter名称及其options,并创建该filter
  17. goto end;
  18. if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0) //将输入输出加入filter link链表
  19. goto end;
  20. if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, //解析filter输出
  21. graph)) < 0)
  22. goto end;
  23. filters += strspn(filters, WHITESPACES);
  24. chr = *filters++;
  25. if (chr == ';' && curr_inputs)
  26. append_inout(&open_outputs, &curr_inputs);
  27. index++;
  28. } while (chr == ',' || chr == ';');
  29. if (chr) {
  30. av_log(graph, AV_LOG_ERROR,
  31. "Unable to parse graph description substring: \"%s\"\n",
  32. filters - 1);
  33. ret = AVERROR(EINVAL);
  34. goto end;
  35. }
  36. append_inout(&open_outputs, &curr_inputs);
  37. *inputs = open_inputs;
  38. *outputs = open_outputs;
  39. return 0;
  40. fail:end:
  41. while (graph->nb_filters)
  42. avfilter_free(graph->filters[0]);
  43. av_freep(&graph->filters);
  44. avfilter_inout_free(&open_inputs);
  45. avfilter_inout_free(&open_outputs);
  46. avfilter_inout_free(&curr_inputs);
  47. *inputs = NULL;
  48. *outputs = NULL;
  49. return ret;
  50. }

4. init_input_filter():

  1. static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
  2. {
  3. InputStream *ist = NULL;
  4. enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); //获取filter输出类型,目前只支持视频类型和音频类型
  5. int i;
  6. // TODO: support other filter types
  7. if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
  8. av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
  9. "currently.\n");
  10. exit_program(1);
  11. }
  12. if (in->name) { //输入name,此例中为[0:v]或[1:v]
  13. AVFormatContext *s;
  14. AVStream *st = NULL;
  15. char *p;
  16. int file_idx = strtol(in->name, &p, 0); //通过输入name得到当前输入的file_index
  17. if (file_idx < 0 || file_idx >= nb_input_files) {
  18. av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n",
  19. file_idx, fg->graph_desc);
  20. exit_program(1);
  21. }
  22. s = input_files[file_idx]->ctx; //当前file的ACFormatContext结构体
  23. for (i = 0; i < s->nb_streams; i++) {
  24. enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
  25. if (stream_type != type &&
  26. !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
  27. type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
  28. continue;
  29. if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
  30. st = s->streams[i]; //确定当前输入的stream
  31. break;
  32. }
  33. }
  34. if (!st) {
  35. av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s "
  36. "matches no streams.\n", p, fg->graph_desc);
  37. exit_program(1);
  38. }
  39. ist = input_streams[input_files[file_idx]->ist_index + st->index]; //根据file_index和st->index确定input_stream[]数组中的index
  40. } else { //如果命令行中没有指定filter的输入,则根据media type确定相应的输入流
  41. /* find the first unused stream of corresponding type */
  42. for (i = 0; i < nb_input_streams; i++) {
  43. ist = input_streams[i];
  44. if (ist->dec_ctx->codec_type == type && ist->discard)
  45. break;
  46. }
  47. if (i == nb_input_streams) {
  48. av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for "
  49. "unlabeled input pad %d on filter %s\n", in->pad_idx,
  50. in->filter_ctx->name);
  51. exit_program(1);
  52. }
  53. }
  54. av_assert0(ist);
  55. ist->discard = 0;
  56. ist->decoding_needed |= DECODING_FOR_FILTER;
  57. ist->st->discard = AVDISCARD_NONE;
  58. GROW_ARRAY(fg->inputs, fg->nb_inputs); //扩展filter graph中input结构体
  59. if (!(fg->inputs[fg->nb_inputs - 1] = av_mallocz(sizeof(*fg->inputs[0])))) //为新扩展的输入分配空间
  60. exit_program(1);
  61. fg->inputs[fg->nb_inputs - 1]->ist = ist; //为新扩展的filter graph输入赋值
  62. fg->inputs[fg->nb_inputs - 1]->graph = fg;
  63. fg->inputs[fg->nb_inputs - 1]->format = -1;
  64. fg->inputs[fg->nb_inputs - 1]->type = ist->st->codecpar->codec_type;
  65. fg->inputs[fg->nb_inputs - 1]->name = describe_filter_link(fg, in, 1);
  66. fg->inputs[fg->nb_inputs - 1]->frame_queue = av_fifo_alloc(8 * sizeof(AVFrame*));
  67. if (!fg->inputs[fg->nb_inputs - 1]->frame_queue)
  68. exit_program(1);
  69. GROW_ARRAY(ist->filters, ist->nb_filters); //扩展输入流中的filter结构体,并将当前的filter graph写入到当前输入流中相应filter结构体
  70. ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1];
  71. }

5. parse_filter():

下面我们再来分析一下如何解析并创建一个filter实例。

  1. static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph,
  2. int index, void *log_ctx)
  3. {
  4. char *opts = NULL;
  5. char *name = av_get_token(buf, "=,;["); //根据命令行得到filter的名称,这里是"overlay"
  6. int ret;
  7. if (**buf == '=') { //提取命令行中filter的参数和选项,此处应该是100:00
  8. (*buf)++;
  9. opts = av_get_token(buf, "[],;");
  10. }
  11. ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx); //根据filter的名称和参数创建filter实例
  12. av_free(name);
  13. av_free(opts);
  14. return ret;
  15. }

6. create_filter():

最后,创建filter实例。

  1. static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index,
  2. const char *filt_name, const char *args, void *log_ctx)
  3. {
  4. AVFilter *filt;
  5. char inst_name[30];
  6. char *tmp_args = NULL;
  7. int ret;
  8. snprintf(inst_name, sizeof(inst_name), "Parsed_%s_%d", filt_name, index);
  9. filt = avfilter_get_by_name(filt_name); //根据filter name从注册的AVFilter链表中提取出该filter
  10. if (!filt) {
  11. av_log(log_ctx, AV_LOG_ERROR,
  12. "No such filter: '%s'\n", filt_name);
  13. return AVERROR(EINVAL);
  14. }
  15. *filt_ctx = avfilter_graph_alloc_filter(ctx, filt, inst_name); //创建filter实例,并加入到filter graph
  16. if (!*filt_ctx) {
  17. av_log(log_ctx, AV_LOG_ERROR,
  18. "Error creating filter '%s'\n", filt_name);
  19. return AVERROR(ENOMEM);
  20. }
  21. if (!strcmp(filt_name, "scale") && (!args || !strstr(args, "flags")) &&
  22. ctx->scale_sws_opts) {
  23. if (args) {
  24. tmp_args = av_asprintf("%s:%s",
  25. args, ctx->scale_sws_opts);
  26. if (!tmp_args)
  27. return AVERROR(ENOMEM);
  28. args = tmp_args;
  29. } else
  30. args = ctx->scale_sws_opts;
  31. }
  32. ret = avfilter_init_str(*filt_ctx, args); //解析filter的参数并加入到该filter的私有域,并完成实例的初始化工作
  33. if (ret < 0) {
  34. av_log(log_ctx, AV_LOG_ERROR,
  35. "Error initializing filter '%s'", filt_name);
  36. if (args)
  37. av_log(log_ctx, AV_LOG_ERROR, " with args '%s'", args);
  38. av_log(log_ctx, AV_LOG_ERROR, "\n");
  39. avfilter_free(*filt_ctx);
  40. *filt_ctx = NULL;
  41. }
  42. av_free(tmp_args);
  43. return ret;
  44. }
到此,overlay filter就创建成功了。至于该filter在转码过程中是如何使用的,将会在另一篇文章中来做详细说明。

以上内容只分析了filter的注册、解析、创建和初始化,而对于filter的一些基本概念,例如滤镜图(filter graph)、滤镜链(filter link)、输入输出pad等等,由于篇幅原因,未做详细说明。有时间准备单独开一篇来整理这些概念。

标签:filter,FFMPEG,inputs,graph,nb,Filter,outputs,fg,输入
来源: https://www.cnblogs.com/lidabo/p/15937348.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有