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. Docker进阶之四:centos7安装docker

    centos7.6 安装docker 参考:https://docs.docker.com/install/linux/docker-ce/centos/ 一.存在老版本先删除 yum remove ...

  2. Java基础系列-equals方法和hashCode方法

    原创文章,转载请标注出处:<Java基础系列-equals方法和hashCode方法> 概述         equals方法和hashCode方法都是有Object类定义的. publi ...

  3. C#之使类型参数--泛型

    1.泛型是什么 泛型的就是“通用类型”,它可以代替任何的数据类型,使类型参数化,从而达到只实现一个方法就可以操作多种数据类型的目的. 2.为什么使用泛型 举一个比较两个数大小的例子: 以上例子实现in ...

  4. GraphQL基础篇

    最近参与了一个大型项目,大型项目随着系统业务量的增大,不同的应用和系统共同使用着许多的服务接口API,而随着业务的变化和发展,不同的应用对相同资源的不同使用方法最终会导致需要维护的服务API数量呈现爆 ...

  5. 硬杠后端(后端坑系列)——Django前期工作

    Django是一个开放源代码的Web应用框架,由Python写成,采用了MVC的框架模式. MVC MVC是一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集到一个部件 ...

  6. 《JavaScript高级程序设计》笔记:使用Canvas绘图(15)

    基本用法 要使用<canvas>元素,必须先设置其width和height属性,指定可以绘图的区域大小.出现在开始和结束标签中的内容是后备信息,如果浏览器不支持<canvas> ...

  7. HTML/CSS快速入门

    Web概念 JavaWeb 使用java语言开发基于互联网的项目 软件架构 C/S架构:Client/Server 客户端/服务器 用户本地有一个客户端程序,在远程有一个服务端程序 如QQ,英雄联盟. ...

  8. Mac Java Idea 下面Git配置简要教程

    Mac Java Idea 配置简要教程(Sunplay) 1.找到git路径 终端输入:which git 2.在idea的设置git路径 3.测试Git的效果 测试Git成功的效果

  9. android使用百度地图最新sdk5.0后后代码混淆时,地图无法显示闪退问题

    描述:刚开始遇到这个问题我一步一步去排除,最后发现在初始化地图的时候,代码混淆就有问题了, 问题描述:当跳显示地图的页面APP闪退, 解决对比: 1:对于老版本百度sdk:代码混淆时语句: -libr ...

  10. 利用ZYNQ SOC快速打开算法验证通路(6)——利用AXI总线实时配置sysGen子系统

    利用ZYNQ验证算法的一大优势在于,可以在上位机发送指令借助CPU的控制能力和C语言易开发特点,实时配置算法模块的工作模式.参数等对来对其算法模块性能进行全面的评估.最重要的是无需重新综合硬件模块. ...