[ffmpeg] 滤波
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]
以上是一个连续的字符串,为了方便分析我们把该字符串进行了划分,每一行都是一个滤波器实例,对于一行:
- 开头是一对中括号,中括号内的是输入的标识名0。
- 中括号后面接着的是滤波器名称trim。
- 名称后的第一个等号后面是滤波器参数start_frame=10:end_frame=20,这里有两组参数,两组参数用冒号分开。
- 第一组参数名称为start_frame,参数值为10,中间用等号分开。
- 第二组参数名称为end_frame,参数值为20,中间用等号分开。
- 最后也有一对中括号,中括号内的是输出的标识名v0。
- 如果一个滤波实例的输入标识名与另一个滤波实例的输出标识名相同,则表示这两个滤波实例构成滤波链。
- 如果一个滤波实例的输入标识名或者输出标识名一直没有与其它滤波实例的输出标识名或者输入标识名相同,则表明这些为外部的输入输出,通常我们会为其接上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] 滤波的更多相关文章
- [ffmpeg] 滤波格式协商
ffmpeg的中滤波器是以帧为原料来进行滤波的,那么自然地就会对帧的格式有所要求,可以说如果滤波器不知道帧的格式,就无法对帧进行处理.在进行视频滤波时,滤波格式指的是视频的像素格式:在进行音频滤波时, ...
- [ffmpeg] 定制滤波器
如果有定制ffmpeg滤波器的需求,有两个结构体是必须要了解的:AVFilter.AVFilterPad,所定制的滤波器主要就是通过填充这两个结构体来实现的.我们下面将详细解析这两个结构体,并通过对滤 ...
- FFmpeg的HEVC解码器源代码简单分析:环路滤波(Loop Filter)
===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...
- FFmpeg的H.264解码器源代码简单分析:环路滤波(Loop Filter)部分
===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...
- [ffmpeg] 多输入滤波同步方式(framesync)
滤波也不总是单一的输入,也存在对多个输入流进行滤波的需求,最常见的就是对视频添加可视水印,水印的组成通常为原视频以及作为水印的图片或者小动画,在ffmpeg中可以使用overlay滤波器进行水印添加. ...
- FFMPEG视音频编解码零基础学习方法-b
感谢大神分享,虽然现在还看不懂,留着大家一起看啦 PS:有不少人不清楚“FFmpeg”应该怎么读.它读作“ef ef em peg” 0. 背景知识 本章主要介绍一下FFMPEG都用在了哪里(在这里仅 ...
- [总结]FFMPEG视音频编解码零基础学习方法
在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...
- FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-TU
===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...
随机推荐
- 爸爸又给Spring MVC生了个弟弟叫Spring WebFlux
情景引入 很早之前,Java就火起来了,是因为它善于开发和处理网络方面的应用. Java有一个爱好,就是喜欢制定规范标准,但自己又不善于去实现. 反倒是一些服务提供商使用它的规范标准来制造应用服务器而 ...
- Golang struct结构
结构struct Go中的struct与C中的struct非常相似,并且Go没有class,代替了class的位置,但并没有代替class的功能 使用type struct{} 定义结构,名称遵循可见 ...
- 杭电ACM2018--母牛的故事
母牛的故事 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submi ...
- 如何编写最佳的Dockerfile
译者按: Dockerfile 的语法非常简单,然而如何加快镜像构建速度,如何减少 Docker 镜像的大小却不是那么直观,需要积累实践经验.这篇博客可以帮助你快速掌握编写 Dockerfile 的技 ...
- 安装centos5.x的基本优化配置
1.添加账号
- Dotspatial 空间要素选择
//通过遍历选择要素,获取选择要素相交的要素 private void toolStripButton43_Click(object sender, EventArgs e) { //查看与选中要素重 ...
- 评估指标【交叉验证&ROC曲线】
# -*- coding: utf-8 -*- """ Created on Mon Sep 10 11:21:27 2018 @author: zhen "& ...
- MySQL:select command denied to user for table 'proc'案例
使用EMS MySQL Manager Pro(3.4.0.1)连接MySQL 5.6.20时,报错:"SELECT command denied to user xxx@xxx.xxx.x ...
- Postgres中文分词
环境 CentOS Linux release 7.2.1511 (Core) 安装Postgres 安装postgres很简单 yum安装 sudo yum install postgresql-s ...
- Vue的安装及使用快速入门
一.安装vue 1.安装node.js,安装完node.js之后,npm也会自动安装 查询是否安装成功的命令: node -v npm -v 2.全局安装脚手架工具vue-cli,命令如下: npm ...