FFmpeg filter简介

本文深入解析FFmpeg中的libavfilter模块,介绍其提供的音视频滤镜机制,包括源滤镜、音频滤镜、视频滤镜和多媒体滤镜的分类与应用。详细解释了滤镜的原理、语法及经典案例,如视频水平镜像、音频延迟处理等,适合音视频处理领域的开发者参考。

1. 引言及示例

FFmpeg中的libavfilter提供了一整套的基于filter的机制。filter本身是一个插件的形式,可以快速的组装需要的效果。
比如下面的filter,可以实现视频的水平镜像效果。
ffplay.exe sample.rmvb -vf hflip

FFmpeg为什么重新定义filter API?

FFmpeg定义的libavcodec接口已经成为在编解码领域的事实上的行业标准。但音视频filter并没有类似的标准,多个不同的多媒体项目(比如MPlayer、Xine、GStreamer等)都实现了自定义的filter系统。为了统一filter库API接口,FFmpeg提出了参考DirectDraw实现了高质量、高效、灵活的音视频filter接口。详细的文档资料可以参考FFmpeg filter

传统概念上filter是什么?

本部分资料参考filter-def
filter可以翻译成过滤器,滤波器。物理概念上,常见的过滤器跟净化器概念重复,比如滤水器、空气净化器等。

  • 在计算机程序中,filter是指一段代码,可用于检查输入或者输出,按照预定的规则处理并传递这些数据。换种说法,filter是一种传递(pass-through)代码块,将输入数据做特定的变换并输出。通常filter自身不做任何输入/输出。举个例子,linux下的grep可以认为是一个filter,按照正则表达式匹配选择,从输入中选择输出数据。
  • 在电信工程领域,filter通常指的用于信号处理的设备,比如音频处理比较典型的低通滤波器、高通滤波器、带通滤波器、去噪滤波器等。

filter的分类

按照处理数据的类型,通常多媒体的filter分为:

  • 音频filter
  • 视频filter
  • 字幕filter

另一种按照处于编解码器的位置划分:

  • prefilters: used before encoding
  • intrafilters: used while encoding (and are thus an integral part of a video codec)
  • postfilters: used after decoding

FFmpeg中filter分为:

  • source filter (只有输出)
  • audio filter
  • video filter
  • Multimedia filter
  • sink filter (只有输入)

除了source和sink filter,其他filter都至少有一个输入、至少一个输出。

介绍了这么多,下面也是一个例子,使用filter实现宽高减半显示:

ffplay.exe sample.rmvb -vf scale=iw/2:ih/2

下面是使用mptestsrc的source filter作为ffplay输入,直接显示:

ffplay -f lavfi mptestsrc=t=dc_luma
ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_color=#00ff00,scale=1200:800:flags=16

2. 基本原理

FFmpeg filter可以认为是一些预定义的范式,可以实现类似积木的多种功能的自由组合。每个filter都有固定数目的输入和输出,而且实际使用中不允许有空悬的输入输出端。使用文本描述时我们可以通过标识符指定输入和输出端口,将不同filter串联起来,构成更复杂的filter。这就形成了嵌套的filter。当然每个filter可以通过ffmpeg/ffplay命令行实现,但通常filter更方便。

ffmpeg.exe、ffplay.exe能够通过filter处理原始的音视频数据。ffmpeg将filtergraph分为simple filtergraph和complex filtergraph。通常simple filtergraph只有一个输入和输出,ffmpeg命令行中使用-vf-af识别,基本原理图如下:

     _________                        ______________
    |         |                      |              |
    | decoded |                      | encoded data |
    | frames  |\                   _ | packets      |
    |_________| \                  /||______________|
                 \   __________   /
      simple     _\||          | /  encoder
      filtergraph   | filtered |/
                    | frames   |
                    |__________|

complex filtergraph,通常是具有多个输入输出文件,并有多条执行路径;ffmpeg命令行中使用-lavfi-filter_complex,基本原理图如下:

 _________
|         |
| input 0 |\                    __________
|_________| \                  |          |
             \   _________    /| output 0 |
              \ |         |  / |__________|
 _________     \| complex | /
|         |     |         |/
| input 1 |---->| filter  |\
|_________|     |         | \   __________
               /| graph   |  \ |          |
              / |         |   \| output 1 |
 _________   /  |_________|    |__________|
|         | /
| input 2 |/
|_________|

第三部分会介绍filtergraph的基本语法和构成。

在libavfilter, 一个filter可以包含多个输入、多个输出。下图是一个filtergraph的示例:

                [main]
input --> split ---------------------> overlay --> output
            |                             ^
            |[tmp]                  [flip]|
            +-----> crop --> vflip -------+

上图中filtergraph将输入流分成两个流,其中一个通过crop filter和vflip filter,然后通过overlay filter将这两个流合成一个流输出。这个filtergraph可以用下面命令行表示:

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

3. 语法识别

FFmpeg中filter包含三个层次,filter->filterchain->filtergraph。具体可以参考下图:

filter syntax

filter是ffmpeg的libavfilter提供的基础单元。在同一个线性链中的filter使用逗号分隔,在不同线性链中的filter使用分号隔开,比如下面的例子:

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

这里crop、vflip处于同一个线性链,split、overlay位于另一个线性链。二者连接通过命名的label实现(位于中括号中的是label的名字)。在上例中split filter有两个输出,依次命名为[main]和[tmp];[tmp]作为crop filter输入,之后通过vflip filter输出[flip];overlay的输入是[main]和[flilp]。如果filter需要输入参数,多个参数使用冒号分割。
对于没有音频、视频输入的filter称为source filter,没有音频、视频输出的filter称为sink filter。

4. 经典的filter

FFmpeg支持的所有filter可以通过filters查看。
这里选几个相对经典的filter。

音频filter

  • adelay filter
    实现不同声道的延时处理。使用参数如下adelay=1500|0|500,这个例子中实现第一个声道的延迟1.5s,第三个声道延迟0.5s,第二个声道不做调整。
  • aecho filter
    实现回声效果,具体参考http://ffmpeg.org/ffmpeg-filters.html#aecho。
  • amerge filter
    将多个音频流合并成一个多声道音频流。具体参考http://ffmpeg.org/ffmpeg-filters.html#amerge-1。
  • ashowinfo filter
    显示每一个audio frame的信息,比如时间戳、位置、采样格式、采样率、采样点数等。具体参考http://ffmpeg.org/ffmpeg-filters.html#ashowinfo。
  • panfilter
    特定声道处理,比如立体声变为单声道,或者通过特定参数修改声道或交换声道。主要有两大类:
    混音处理,比如下面的例子pan=1c|c0=0.9*c0+0.1*c1,实现立体声到单声道的变换;
    声道变换,比如5.1声道顺序调整,pan="5.1| c0=c1 | c1=c0 | c2=c2 | c3=c3 | c4=c4 | c5=c5"
  • silencedetectsilenceremove filter
    根据特定参数检测静音和移除静音。
  • volumevolumedetect filter
    这两个filter分别实现音量调整和音量检测。
  • audio source filter
    aevalsrc filter按照特定表达式生成音频信号。
    anullsrc filter生成特定的原始音频数据,用于模板或测试。
    anoisesrc filter生成噪声音频信号。
    sine filter生成正弦波音频信号。
  • audio sink filter
    abuffersink filter和anullsink filter,这些filter只是用于特定情况下结束filter chain。

视频filter

  • blendtblend filter
    将两帧视频合并为一帧。具体参数参考http://ffmpeg.org/ffmpeg-filters.html#blend_002c-tblend。
  • crop filter
    按照特定分辨率裁剪输入视频,具体参数参考http://ffmpeg.org/ffmpeg-filters.html#crop。
  • drawboxdrawgriddrawtext filter
    绘制box(对话框)、grid(表格)、text(文本)。
  • edgedetect filter
    边缘检测filter。
  • fps filter
    按照指定帧率输出视频帧(丢帧或者复制)。具体参考http://ffmpeg.org/ffmpeg-filters.html#fps-1。
  • hflipvflip filter
    水平和垂直镜像。
  • histogram filter
    生成每帧的各颜色分量的直方图。
  • noise filter
    在输入视频帧中添加白噪声。
  • overlay filter
    视频叠加。具体参考http://ffmpeg.org/ffmpeg-filters.html#overlay-1。
  • pad filter
    视频边界填充。具体参考http://ffmpeg.org/ffmpeg-filters.html#pad-1。
  • rotate filter
    视频任意角度旋转。具体参考http://ffmpeg.org/ffmpeg-filters.html#rotate。
  • scale filter
    使用libswscale库完成视频缩放的filter。
  • showinfo filter
    显示视频帧的参数信息,比如时间戳、采样格式、帧类型等。
  • subtitles filter
    使用libass库绘制subtitle(字幕)。
  • thumbnail filter
    提取缩略图的filter。
  • transpose filter
    图像转置的filter。参数参考http://ffmpeg.org/ffmpeg-filters.html#transpose。
  • source filter
    主要有cellatuocoreimagesrcmptestsrclife等filter,具体效果建议参考ffmpeg用户手册。
  • source sink
    主要有buffersinknullsink两个filter。

多媒体filter

  • ahistogram filter
    将音频转化为视频输出,并显示为音量的直方图。
  • concat filter
    将音频流、视频流拼接成一个。具体参考http://ffmpeg.org/ffmpeg-filters.html#concat。
  • metadataametadata filter
    操作metadata信息。
  • setptsasetpts filter
    改变输入音频帧或视频帧的pts。
  • showfreqsshowspectrumshowspertrumpicshowvolumeshowwaves filter
    将输入音频转换为视频显示,并显示频谱、音量等信息
  • splitasplit filter
    将输入切分为多个相同的输出。
  • source filter
    主要是movieamovie filter。从movie容器中读取音频或者视频帧。

5. 实例demo

FFmpeg提供了很多有趣的filter实例,详见Fancy Filtering Examples
我们这里先从几个简单的实例开始。

实例一:缩放scale

将输入缩小宽度缩小一半,并保持宽高比。

ffmpeg -i input.jpg -vf scale=iw/2:-1 output.jpg

实例二:filter、filterchain和filtergraph的使用

先将输入去交织,然后减半显示。
以下三个命令是等价的。

# 2 chains form, one filter per chain, chains linked by the [middle] pad
ffmpeg -i input -vf [in]yadif=0:0:0[middle];[middle]scale=iw/2:-1[out] output

# 1 chain form, with 2 filters in the chain, linking implied
ffmpeg -i input -vf [in]yadif=0:0:0,scale=iw/2:-1[out] output

# the input and output are implied without ambiguity
ffmpeg -i input -vf yadif=0:0:0,scale=iw/2:-1 output

实例三:2x2布局画面拼接

这个实例主要说明下filtergraph使用。命令行如下:

./ffmpeg -f lavfi -i testsrc -f lavfi -i testsrc -f lavfi -i testsrc -f lavfi -i testsrc -filter_complex \
"[0:v]pad=iw*2:ih*2[a]; \
 [1:v]negate[b]; \
 [2:v]hflip[c]; \
 [3:v]edgedetect[d]; \
 [a][b]overlay=w[x]; \
 [x][c]overlay=0:h[y]; \
 [y][d]overlay=w:h[out]" -map "[out]" -c:v ffv1 -t 5 multiple_input_grid.avi

6. 参考资料

  1. libavfilter-multimedia
  2. FFmpeg filter HOWTO
  3. FFmpeg Filtering Guide
  4. ffmpeg-filtering
  5. FFmpeg Bug Tracker and Wiki
### FFmpeg Filter Graph 介绍 FFmpeg 的 libavfilter 是一个强大的多媒体滤镜处理框架,允许对音视频流进行各种处理,如缩放、裁剪、格式转换、混音等。Filter Graph 是该框架中的核心概念,它是由多个 Filter 组成的有向无环图(DAG),用于描述音视频处理的流程。每个 Filter 可以看作是一个具有特定功能的处理单元,它们通过输入和输出端口连接在一起,形成一个完整的处理链路 [^1]。 ### FFmpeg Filter Graph 原理 在 Filter Graph 的架构思想中,Graph 可类比为画布。先要有 Graph,然后将 Filter 放置在上面。Filter 身上有 pin 接口,pin 的作用是统一数据接口,还需要通过 link 动作将指定的 2 个 Filter 通过其 pin 接口连接起来,形成完整的 Filter Graph 或 Filter Link List。Filter Graph 本身只是一堆参数数据和代码,需要一个动力引擎来驱动整个过程,FFmpeg 将驱动能力交给了 Filter 框架外部,通过向 Filter Graph 的首个 Filter 推数据和从 Filter Graph 的末尾 Filter 拉数据来驱动数据流 [^2]。 ### FFmpeg Filter Graph 使用方法 代码中关键函数如下: - `avfilter_graph_alloc()`:为 FilterGraph 分配内存。 - `avfilter_graph_create_filter()`:创建并向 FilterGraph 中添加一个 Filter。 - `avfilter_graph_parse_ptr()`:将通过字符串描述的 Graph 添加到 FilterGraph 中。 - `avfilter_graph_config()`:检查 FilterGraph 的配置。 - `av_buffersrc_add_frame()`:向 FilterGraph 中加入一个 AVFrame。 - `av_buffersink_get_frame()`:从 FilterGraph 中取出一个 AVFrame [^4]。 ### FFmpeg Filter Graph 示例 以下是一个简单的使用 Filter Graph 进行视频缩放的示例代码: ```c #include <libavfilter/avfilter.h> #include <libavfilter/buffersrc.h> #include <libavfilter/buffersink.h> #include <libavutil/frame.h> #include <libavutil/opt.h> int main() { AVFilterGraph *graph; AVFilterContext *buffersrc_ctx; AVFilterContext *buffersink_ctx; AVFilter *buffersrc = avfilter_get_by_name("buffer"); AVFilter *buffersink = avfilter_get_by_name("buffersink"); AVFrame *frame_in, *frame_out; char args[512]; // 为 FilterGraph 分配内存 graph = avfilter_graph_alloc(); // 创建输入缓冲区 Filter snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", 640, 480, AV_PIX_FMT_YUV420P, 1, 25, 1, 1); avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, graph); // 创建输出缓冲区 Filter enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }; avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, pix_fmts, graph); // 创建缩放 Filter AVFilterContext *scale_ctx; AVFilter *scale = avfilter_get_by_name("scale"); avfilter_graph_create_filter(&scale_ctx, scale, "scale", "320:240", NULL, graph); // 连接 Filter avfilter_link(buffersrc_ctx, 0, scale_ctx, 0); avfilter_link(scale_ctx, 0, buffersink_ctx, 0); // 检查 FilterGraph 的配置 avfilter_graph_config(graph, NULL); // 模拟输入帧 frame_in = av_frame_alloc(); frame_in->format = AV_PIX_FMT_YUV420P; frame_in->width = 640; frame_in->height = 480; av_frame_get_buffer(frame_in, 0); // 向 FilterGraph 中加入一个 AVFrame av_buffersrc_add_frame(buffersrc_ctx, frame_in); // 从 FilterGraph 中取出一个 AVFrame frame_out = av_frame_alloc(); av_buffersink_get_frame(buffersink_ctx, frame_out); // 释放资源 av_frame_free(&frame_in); av_frame_free(&frame_out); avfilter_graph_free(&graph); return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值