ffmpeg中有很多已经实现好的滤波器,这些滤波器的实现位于libavfilter目录之下,用户需要进行滤波时,就是是调用这些滤波器来实现的。ffmpeg对于调用滤波器有一整套的调用机制。

基本结构

我们把一整个滤波的流程称为滤波过程。下面是一个滤波过程的结构

图中简要指示出了滤波所用到的各个结构体,各个结构体有如下作用:

AVFilterGraph 用于统合这整个滤波过程的结构体。
AVFilter 滤波器,滤波器的实现是通过AVFilter以及位于其下的结构体/函数来维护的。
AVFilterContext 一个滤波器实例,即使是同一个滤波器,但是在进行实际的滤波时,也会由于输入的参数不同而有不同的滤波效果,AVFilterContext就是在实际进行滤波时用于维护滤波相关信息的实体。
AVFilterLink 滤波器链,作用主要是用于连接相邻的两个AVFilterContext。为了实现一个滤波过程,可能会需要多个滤波器协同完成,即一个滤波器的输出可能会是另一个滤波器的输入,AVFilterLink的作用是串联两个相邻的滤波器实例,形成两个滤波器之间的通道。
AVFilterPad 滤波器的输入输出端口,一个滤波器可以有多个输入以及多个输出端口,相邻滤波器之间是通过AVFilterLink来串联的,而位于AVFilterLink两端的分别就是前一个滤波器的输出端口以及后一个滤波器的输入端口。
buffersrc 一个特殊的滤波器,这个滤波器的作用就是充当整个滤波过程的入口,通过调用该滤波器提供的函数(如av_buffersrc_add_frame)可以把需要滤波的帧传输进入滤波过程。在创建该滤波器实例的时候需要提供一些关于所输入的帧的格式的必要参数(如:time_base、图像的宽高、图像像素格式等)。
buffersink 一个特殊的滤波器,这个滤波器的作用就是充当整个滤波过程的出口,通过调用该滤波器提供的函数(如av_buffersink_get_frame)可以提取出被滤波过程滤波完成后的帧。

创建简单的滤波过程

创建整个滤波过程包含以下步骤:

首先需要得到整个滤波过程所需的滤波器(AVFilter),其中buffersrc以及buffersink是作为输入以及输出所必须的两个滤波器。

    const AVFilter *buffersrc  = avfilter_get_by_name("buffer");
const AVFilter *buffersink = avfilter_get_by_name("buffersink");
const AVFilter *myfilter = avfilter_get_by_name("myfilter");

创建统合整个滤波过程的滤波图结构体(AVFilterGraph)

    filter_graph = avfilter_graph_alloc();

创建用于维护滤波相关信息的滤波器实例(AVFilterContext)

    AVFilterContext *in_video_filter = NULL;
AVFilterContext *out_video_filter = NULL;
AVFilterContext *my_video_filter = NULL;
avfilter_graph_create_filter(&in_video_filter, buffersrc, "in", args, NULL, filter_graph);
avfilter_graph_create_filter(&out_video_filter, buffersink, "out", NULL, NULL, filter_graph);
avfilter_graph_create_filter(&my_video_filter, myfilter, "myfilter", NULL, NULL, filter_graph);

用AVFilterLink把相邻的两个滤波实例连接起来

    avfilter_link(in_video_filter, 0, my_video_filter, 0);
avfilter_link(my_video_filter, 0, out_video_filter, 0);

提交整个滤波图

    avfilter_graph_config(filter_graph, NULL);

创建复杂的滤波过程

当滤波过程复杂到一定程度时,即需要多个滤波器进行复杂的连接来实现整个滤波过程,这时候对于调用者来说,继续采用上述方法来构建滤波图就显得不够效率。对于复杂的滤波过程,ffmpeg提供了一个更为方便的滤波过程创建方式。

这种复杂的滤波器过程创建方式要求用户以字符串的方式描述各个滤波器之间的关系。如下是一个描述复杂滤波过程的字符串的例子:

    [0]trim=start_frame=10:end_frame=20[v0];\
[0]trim=start_frame=30:end_frame=40[v1];\
[v0][v1]concat=n=2[v2];\
[1]hflip[v3];\
[v2][v3]overlay=eof_action=repeat[v4];\
[v4]drawbox=50:50:120:120:red:t=5[v5]

以上是一个连续的字符串,为了方便分析我们把该字符串进行了划分,每一行都是一个滤波器实例,对于一行:

  1. 开头是一对中括号,中括号内的是输入的标识名0。
  2. 中括号后面接着的是滤波器名称trim。
  3. 名称后的第一个等号后面是滤波器参数start_frame=10:end_frame=20,这里有两组参数,两组参数用冒号分开。
  4. 第一组参数名称为start_frame,参数值为10,中间用等号分开。
  5. 第二组参数名称为end_frame,参数值为20,中间用等号分开。
  6. 最后也有一对中括号,中括号内的是输出的标识名v0。
  7. 如果一个滤波实例的输入标识名与另一个滤波实例的输出标识名相同,则表示这两个滤波实例构成滤波链。
  8. 如果一个滤波实例的输入标识名或者输出标识名一直没有与其它滤波实例的输出标识名或者输入标识名相同,则表明这些为外部的输入输出,通常我们会为其接上buffersrc以及buffersink。

按照这种规则,上面的滤波过程可以被描绘成以下滤波图:

ffmpeg提供一个函数用于解析这种字符串:avfilter_graph_parse2。这个函数会把输入的字符串生成如上面的滤波图,不过我们需要自行生成buffersrc以及buffersink的实例,并通过该函数提供的输入以及输出接口把buffersrc、buffersink与该滤波图连接起来。整个流程包含以下步骤:

创建统合整个滤波过程的滤波图结构体(AVFilterGraph)

    filter_graph = avfilter_graph_alloc();

解析字符串,并构建该字符串所描述的滤波图

    avfilter_graph_parse2(filter_graph, graph_desc, &inputs, &outputs);

其中inputs与outputs分别为输入与输出的接口集合,我们需要为这些接口接上输入以及输出。

    for (cur = inputs, i = 0; cur; cur = cur->next, i++) {
const AVFilter *buffersrc = avfilter_get_by_name("buffer");
avfilter_graph_create_filter(&filter, buffersrc, name, args, NULL, filter_graph);
avfilter_link(filter, 0, cur->filter_ctx, cur->pad_idx);
}
avfilter_inout_free(&inputs); for (cur = outputs, i = 0; cur; cur = cur->next, i++) {
const AVFilter *buffersink = avfilter_get_by_name("buffersink");
avfilter_graph_create_filter(&filter, buffersink, name, NULL, NULL, filter_graph);
avfilter_link(cur->filter_ctx, cur->pad_idx, filter, 0);
}
avfilter_inout_free(&outputs);

提交整个滤波图

    avfilter_graph_config(filter_graph, NULL);

滤波API

上面主要讨论了如何创建滤波过程,不过要进行滤波还需要把帧传输进入该过程,并在滤波完成后从该过程中提取出滤波完成的帧。

buffersrc提供了向滤波过程输入帧的API:av_buffersrc_add_frame。向指定的buffersrc实例输入想要进行滤波的帧就可以把帧传入滤波过程。

    av_buffersrc_add_frame(c->in_filter, pFrame);

buffersink提供了从滤波过程提取帧的API:av_buffersink_get_frame。可以从指定的buffersink实例提取滤波完成的帧。

    av_buffersink_get_frame(c->out_filter, pFrame);

当av_buffersink_get_frame返回值大于0则表示提取成功。

[ffmpeg] 滤波的更多相关文章

  1. [ffmpeg] 滤波格式协商

    ffmpeg的中滤波器是以帧为原料来进行滤波的,那么自然地就会对帧的格式有所要求,可以说如果滤波器不知道帧的格式,就无法对帧进行处理.在进行视频滤波时,滤波格式指的是视频的像素格式:在进行音频滤波时, ...

  2. [ffmpeg] 定制滤波器

    如果有定制ffmpeg滤波器的需求,有两个结构体是必须要了解的:AVFilter.AVFilterPad,所定制的滤波器主要就是通过填充这两个结构体来实现的.我们下面将详细解析这两个结构体,并通过对滤 ...

  3. FFmpeg的HEVC解码器源代码简单分析:环路滤波(Loop Filter)

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  4. FFmpeg的H.264解码器源代码简单分析:环路滤波(Loop Filter)部分

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  5. [ffmpeg] 多输入滤波同步方式(framesync)

    滤波也不总是单一的输入,也存在对多个输入流进行滤波的需求,最常见的就是对视频添加可视水印,水印的组成通常为原视频以及作为水印的图片或者小动画,在ffmpeg中可以使用overlay滤波器进行水印添加. ...

  6. FFMPEG视音频编解码零基础学习方法-b

    感谢大神分享,虽然现在还看不懂,留着大家一起看啦 PS:有不少人不清楚“FFmpeg”应该怎么读.它读作“ef ef em peg” 0. 背景知识 本章主要介绍一下FFMPEG都用在了哪里(在这里仅 ...

  7. [总结]FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  8. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  9. FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-TU

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

随机推荐

  1. idea avtiviti 插件中文乱码

    idea 安转activiti插件后,编辑流程图发现保存后中文乱码,并且idea的字符集(Settings—>Editor—>File Encodings)已经设置为UTF-8,流程图中中 ...

  2. Python实战171202元组访问

    学生信息系统中数据为固定格式: (名字,年龄,性别,邮箱地址,......) 学生数量很大为了减小存储开销,对每个学生信息用元组表示: ('jim',18,'male','jim8765@gmail. ...

  3. linux下载服务器上的文件命令-sz

    语法:sz  文件 比如要下载下面这个com.zip这个压缩包 输入sz com.zip 弹出下载页面,即可开始下载文件

  4. Error fetching https://gems.ruby-china.org/: bad response Not Found 404 (https://gems.ruby-china.org/specs.4.8.gz) 报错解决办法

    执行换源操作 gem source -a https://gems.ruby-china.org/ 时报错: Error fetching https://gems.ruby-china.org/: ...

  5. Hadoop综合大作业

    Hadoop综合大作业 要求: 用Hive对爬虫大作业产生的文本文件(或者英文词频统计下载的英文长篇小说)词频统计. 用Hive对爬虫大作业产生的csv文件进行数据分析 1. 用Hive对爬虫大作业产 ...

  6. vuex的用法

    https://segmentfault.com/a/1190000015782272

  7. 纯CSS编写汉克狗

    1,CSS中原生的变量定义语法是:--*,变量使用语法是:var(--*),其中*表示我们的变量名称:在CSS变量命名中,不能包含$,[,^,(,%等字符,普通字符局限在只要是“数字[0-9]”“字母 ...

  8. Dynamics 365-关于Solution的那些事(三)

    这一篇的内容,是关于Solution的使用建议的,如果大家有什么实用的建议,欢迎留言讨论. 一. 版本控制 Solution是有版本号的,率性的人可能在新建一个solution的时候,直接赋值1.0, ...

  9. RxJava2.0入门篇

    传统用法:开启子线程去做耗时任务,业务逻辑越复杂,代码嵌套越严重,Rx系列出来很久了,想自己做一个总结,希望能帮到一部分人 观察者模式先提一嘴 这个老模式简直不想说太多,就说一下流程 1创建被观察者 ...

  10. 微信小程序(基本知识点)

    创建页面的两种方式:   1.通过创建文件夹的方式创建(.wxml/.wxss/.json/.js/)   2.在app.json的"pages": []中添加路径"pa ...