📚 写给开发者的音视频处理工程手册
🎯 目标 :深入解析 FFmpeg 的核心引擎------滤镜图 (Filtergraph) 。本章将从信号流(Signal Flow)的角度,阐述如何通过有向图(Directed Graph)结构实现对原始视音频数据的精确操控。
🛠️ 核心问题:简单滤镜与复杂滤镜的架构区别是什么?如何在笛卡尔坐标系中精确控制图层叠加?如何处理多路流的时空对齐与合成?
📋 目录 (Part 2)
- 1. 滤镜系统架构:简单滤镜 vs 复杂滤镜
- 2. 语法范式:链路、节点与标签
- 3. 空间域处理:坐标系与图层叠加 (Overlay)
- 4. 多流合成:拼接与堆叠 (Concatenation & Stacking)
- 5. 时间域处理:时间戳重置与修剪
- 6. 实战工程:构建一个复杂的 Filtergraph
1. 滤镜系统架构:简单滤镜 vs 复杂滤镜
在 FFmpeg 的内部管线中,滤镜(Filter)位于解码器(Decoder)之后、编码器(Encoder)之前。它们直接操作未压缩的 Raw Data(原始帧)。根据输入输出流的数量和逻辑复杂度,FFmpeg 将滤镜处理分为两类。
1.1 简单滤镜 (Simple Filtergraph)
- 标志 :
-vf(Video Filter) 或-af(Audio Filter)。 - 拓扑结构 :线性链表 (Linear Chain)。
- 特征 :仅支持 1个输入流 和 1个输出流。输入流的数据类型(视频/音频)必须与输出一致。
- 应用场景 :调整分辨率 (
scale)、裁剪 (crop)、色彩转换 (format)。
bash
# 线性处理:先去噪,再缩放
ffmpeg -i input.mp4 -vf "hqdn3d,scale=1280:720" output.mp4
1.2 复杂滤镜 (Complex Filtergraph)
- 标志 :
-filter_complex或-lavfi。 - 拓扑结构 :有向无环图 (Directed Acyclic Graph, DAG)。
- 特征 :支持 N个输入流 和 M个输出流 (N>=0, M>=0)。支持不同类型流的混合处理。
- 应用场景:画中画、多画面宫格、加水印、音视频混流。
2. 语法范式:链路、节点与标签
要编写复杂滤镜,必须掌握描述 图 (Graph) 的语法。
2.1 基础语法结构
一个完整的 Filtergraph 由多个 滤镜链 (Filterchain) 组成,链之间用分号 ; 分隔。
一个滤镜链由多个 滤镜 (Filter) 组成,滤镜之间用逗号 , 分隔。
2.2 输入/输出标签 (Pad Labels)
在复杂图中,数据流不再自动传递,必须显式地进行"路由"。我们使用方括号 [label_name] 来标记输入和输出端口。
标准格式:
text
[输入标签1][输入标签2] 滤镜名=参数1=值1:参数2=值2 [输出标签]
示例解析:
bash
ffmpeg -i video.mp4 -i logo.png -filter_complex "[0:v][1:v]overlay=10:10[outv]" -map "[outv]" output.mp4
[0:v]:引用第 0 个输入文件的视频流。[1:v]:引用第 1 个输入文件的视频流。overlay:滤镜名称(叠加)。10:10:滤镜参数。[outv]:自定义的输出标签,用于后续步骤或最终输出 (-map)。
3. 空间域处理:坐标系与图层叠加 (Overlay)
图像合成是视频处理中最常见的需求,其核心是 overlay 滤镜。理解它需要建立严格的坐标系概念。
3.1 计算机图形坐标系
FFmpeg 使用标准的 2D 计算机图形坐标系:
- 原点 (0, 0) :背景画布(主视频)的 左上角。
- X 轴:向右为正方向。
- Y 轴:向下为正方向。
3.2 Overlay 滤镜的参数变量
overlay 滤镜允许使用内置变量进行数学计算,实现动态布局。
| 变量 | 含义 | 物理意义 |
|---|---|---|
W / H |
Main Width/Height | 背景视频的宽度和高度 |
w / h |
Overlay Width/Height | 前景素材(水印/Logo)的宽度和高度 |
t |
Timestamp | 当前帧的时间戳(秒) |
n |
Frame Number | 当前帧的序号 |
3.3 典型场景的坐标计算
场景 A:右上角水印 (留出 10px 边距)
text
x = W - w - 10
y = 10
场景 B:正中心水印
text
x = (W - w) / 2
y = (H - h) / 2
场景 C:跑马灯效果 (Logo 从左向右移动)
text
x = t * 50 (每秒向右移动 50 像素)
y = 10
完整命令示例:
bash
ffmpeg -i main.mp4 -i logo.png -filter_complex "overlay=x=W-w-10:y=10" output.mp4
4. 多流合成:拼接与堆叠 (Concatenation & Stacking)
除了层叠 (Overlay),我们经常需要将多个视频流在空间上并排,或在时间上首尾相连。
4.1 空间堆叠 (Stacking)
用于制作"视频墙"或对比视频。
- hstack (Horizontal Stack) :水平拼接。要求所有输入流的 高度 (Height) 一致。
- vstack (Vertical Stack) :垂直拼接。要求所有输入流的 宽度 (Width) 一致。
示例:左右分屏对比
bash
ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v][1:v]hstack=inputs=2[v]" -map "[v]" output.mp4
4.2 时间拼接 (Concat Filter)
注意区别于 Demuxer Concat(直接合并文件包)。Concat Filter 是在解码后的原始帧层级进行拼接,因此它支持编码参数完全不同的视频源。
语法:
concat=n=片段数:v=视频流数:a=音频流数
示例:拼接三个片段
bash
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -filter_complex "[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[vv][aa]" -map "[vv]" -map "[aa]" out.mp4
5. 时间域处理:时间戳重置与修剪
在滤镜图中处理时间轴时,必须理解 PTS (Presentation Time Stamp) 的概念。
5.1 精确修剪 (Trim / Atrim)
trim 滤镜通过解码后的帧进行剪切,精度高于直接用 -ss (Seek) 参数。
start=10:从第 10 秒开始。duration=5:持续 5 秒。
5.2 时间戳重置 (SetPTS)
当你使用 trim 截取了视频的中间一段(例如 10s~15s),该片段的原始 PTS 依然是从 10s 开始的。如果直接播放,播放器会在前 10s 显示黑屏。
因此,trim 通常必须配合 setpts 使用,将时间戳归零。
标准组合公式:
text
trim=start=10:duration=5,setpts=PTS-STARTPTS
PTS:当前帧的原始时间戳。STARTPTS:截取片段起始帧的时间戳。PTS-STARTPTS:计算相对时间,实现从 0 秒开始播放。
6. 实战工程:构建一个复杂的 Filtergraph
任务目标:
- 截取视频 A 的前 5 秒。
- 将视频 A 缩小为 320x240。
- 将缩小后的视频 A 作为"画中画"叠加在视频 B 的右下角。
构建逻辑流程 (Topology):
Trim+SetPTS
Scale
Input A
5s Fragment
Small Video
Input B
Overlay
Output
完整命令:
bash
ffmpeg -i video_A.mp4 -i video_B.mp4 -filter_complex \
"[0:v]trim=duration=5,setpts=PTS-STARTPTS,scale=320:240[small_a]; \
[1:v][small_a]overlay=x=W-w-10:y=H-h-10[out]" \
-map "[out]" output.mp4
代码解析:
[0:v]...[small_a]:第一条链。处理输入 A,经过修剪、重置时间戳、缩放后,输出到临时标签[small_a]。[1:v][small_a]overlay...:第二条链。以输入 B 为背景,以[small_a]为前景,进行坐标叠加。-map "[out]":将最终处理结果编码输出。
🎉祝你天天开心,我将更新更多有意思的内容,欢迎关注!
最后更新:2026年2月
作者:Echo