Python(十八):第十七章 音视频处理

第十七章 音视频处理

本章节我们将学习如何使用 Python 来调用强大的 FFmpeg 工具包,以处理常见的音视频文件操作。FFmpeg 功能极其丰富,虽然它本身是 C 语言开发的,但通过 Python 调用其命令行工具是实现自动化处理、集成到 Web 后端或脚本中的常用且高效的方法。我们的重点是学习如何“处理”音视频,而不是从零“实现”编解码器。我们将主要使用 ffmpeg-python 这个库来简化调用过程,让代码更符合 Python 的风格。


17.1 FFmpeg 介绍、核心概念、安装与 Python 交互方式

目标:

  • 深入理解 FFmpeg 是什么,它的重要性以及核心组成。
  • 掌握音视频处理中的基本术语(容器、流、编解码器等)。
  • 在你的操作系统上成功安装 FFmpeg 命令行工具并配置好环境变量。
  • 了解 Python 与 FFmpeg 交互的几种方式,并安装好我们将主要使用的 ffmpeg-python 库。

17.1.1 FFmpeg:音视频领域的“瑞士军刀”

什么是 FFmpeg?

FFmpeg 不仅仅是一个简单的程序,它是一个完整的、跨平台的、开源的音视频处理解决方案。如果你需要对音频或视频文件进行几乎任何你能想到的操作——包括但不限于:录制、转换格式(转码)、编辑(裁剪、合并、旋转)、提取信息、添加滤镜/特效(水印、调色、降噪)、压缩、以及进行网络流传输(推流/拉流)——FFmpeg 都是这个领域内最强大、最通用、使用最广泛的工具集。从大型视频网站的后台转码系统,到你电脑上的视频播放器、剪辑软件,再到各种流媒体服务,背后都极有可能有 FFmpeg 的身影。

FFmpeg 的重要性:

  • 功能全面: 支持海量的音视频格式(容器)和编解码器(算法),无论是常见的 MP4/H.264/AAC,还是较新的 AV1/Opus,甚至很多生僻格式,FFmpeg 大多都能处理。
  • 跨平台: 无缝运行在 Windows、macOS、Linux 等主流操作系统上。
  • 开源免费: 遵循 LGPL 或 GPL 等开源协议(取决于编译选项),可以自由使用、修改和分发。
  • 性能优异: 核心代码使用 C 语言编写,并针对性能进行了深度优化,利用了多核 CPU 和 SIMD 指令集等技术,处理效率高。
  • 命令行驱动: 提供了极其灵活和强大的命令行接口,可以通过参数组合实现复杂的操作流程,非常适合编写脚本进行自动化处理或在服务器后端调用。

FFmpeg 的核心组成:

FFmpeg是一整个“庞大”的项目,他其中含有大量的工具,虽然我们最常用的是 ffmpeg 这个命令,但了解其主要组成部分有助于理解它的工作方式:

ffmpeg (命令行工具)

这是最核心、最常用的工具。它负责读取输入文件/流,根据用户指定的参数进行解码、滤镜处理、编码、封装等一系列操作,最终输出结果文件/流。我们后续的大部分操作都是通过构建不同的 ffmpeg 命令来实现的。

ffprobe (命令行工具)

这是一个多媒体流分析工具。它可以读取音视频文件或流,提取并显示其中非常详细的元数据和技术信息,例如:

  • 文件格式(容器类型)。
  • 包含的数据流(视频、音频、字幕等)。
  • 每个流的编解码器、比特率、时长、帧率(视频)、分辨率(视频)、采样率(音频)、声道数(音频)等。
  • 文件的元数据标签(标题、作者、专辑等)。在进行任何处理之前,使用 ffprobe 了解输入文件的属性往往是必要的第一步,这对于编写健壮的处理脚本至关重要。
ffplay (命令行工具)

一个基于 FFmpeg 库和 SDL(一个跨平台多媒体库)的简单媒体播放器。它主要用于:

  • 快速预览音视频文件。
  • 测试 FFmpeg 的解码能力。
  • 实时查看应用滤镜的效果。它功能相对简单,不如专门的播放器强大,但在开发调试时非常方便。
核心库 (libav*)

这些是以 libav 开头的 C 语言库,是 FFmpeg 所有功能的基石,也是许多其他播放器、编辑器、转码软件(甚至包括 Chrome 浏览器)使用的底层库:

  • libavcodec: 包含了海量的音频/视频编解码器 (CODEC)
  • libavformat: 负责处理各种多媒体容器格式 (Format)解复用 (Demuxing)复用 (Muxing)
  • libavfilter: 提供了丰富的音频和视频滤镜 (Filter),用于实现各种特效和处理。
  • libavutil: 包含了一些基础的通用工具函数(如数学运算、数据结构、日志等)。
  • libswscale: 用于进行高效的图像缩放 (Scaling)色彩空间转换
  • libswresample: 用于进行音频重采样 (Resampling)格式转换声道布局处理。
  • libavdevice: 用于访问操作系统提供的多媒体设备(如摄像头、麦克风、屏幕录制)。

17.1.2 音视频基础概念

理解以下术语对于使用 FFmpeg 至关重要:

术语英文比喻作用与解释常见示例
容器格式Container / Format文件盒子定义文件结构,规定如何将音视频流、元数据打包存储。决定文件扩展名。.mp4, .mkv, .mov, .avi, .mp3, .aac
数据流Stream文件内容容器内实际的、连续的数据序列,如视频数据、音频数据、字幕数据。H.264 视频流, AAC 音频流, SRT 字幕流
编解码器Codec (Coder/Decoder)压缩/解压算法用于压缩(编码)原始音视频数据以减小体积,或解压缩(解码)数据以便播放或编辑。H.264, H.265(HEVC), VP9, AV1, AAC, MP3, Opus

相关处理过程:

  • 编码 (Encoding): 将未压缩或已解码的音视频数据,使用特定的编解码器压缩成目标格式的数据。这是有损或无损压缩的过程。
  • 解码 (Decoding): 将已编码的音视频数据,使用对应的编解码器解压缩成原始的、可播放或可处理的数据(如图像帧序列、音频样本)。
  • 转码 (Transcoding): 先将输入文件解码,然后对解码后的数据进行处理(可选,如加滤镜),最后重新编码成目标格式/参数。这是一个解码+编码的过程,通常比较耗时和消耗 CPU 资源。
  • 封装转换/重封装 (Remuxing): 不经过解码和重新编码,直接将一个容器中的音视频流原封不动地提取出来,再放入另一个类型的容器中。例如,将 MKV 文件中的 H.264 视频和 AAC 音频直接放入 MP4 文件。这个过程非常快,几乎不损失质量,但前提是目标容器必须支持源文件中的音视频编码格式。
  • 复用/封装 (Muxing):多个单独的编码后的流(如一个视频流、一个音频流)合并到一个容器文件中。
  • 解复用/分离 (Demuxing): 从一个容器文件中提取出单个的流(如只提取音频流)。

17.1.3 安装 FFmpeg 命令行工具 (重要前提!)

ffmpeg-python 库本身不包含 FFmpeg 的可执行程序。你必须首先独立地在你的操作系统中安装 FFmpeg,并确保 ffmpegffprobe 命令可以在你的终端或命令行中直接运行。

Windows 安装与配置
  1. 下载: 访问 FFmpeg 官网下载页面 ([下载链接](Download FFmpeg))。推荐从 gyan.devBtbN 下载预编译的 Windows 版本。建议选择 “release” 版本下的 “full” 构建(如 ffmpeg-release-full.zip),它包含了较全面的编解码器支持。

image-20250502095638177

  1. 解压: 将下载的 .zip 文件解压到一个固定且路径不含中文或特殊字符的位置,例如 C:\ffmpegD:\tools\ffmpeg\。解压后你会看到 bin, doc, licenses 等文件夹。关键的可执行文件在 bin 目录中。

  2. 配置环境变量 (关键步骤):

    • 在 Windows 搜索栏搜索“环境变量”并打开“编辑系统环境变量”。
    • 在弹出的“系统属性”窗口中,点击“高级”选项卡下的“环境变量…”按钮。
    • 在下方的“系统变量”列表中,找到名为 Path 的变量,选中它,点击“编辑…”。
    • 在“编辑环境变量”窗口中,点击“新建”,然后将你刚才解压后 ffmpeg 文件夹下 bin 目录的完整路径(例如 C:\ffmpeg\bin)粘贴进去。
    • 点击“确定”关闭所有打开的设置窗口。
  3. 验证: 必须重新打开一个新的 CMD 或 PowerShell 窗口(旧窗口不会加载新的环境变量)。输入以下命令并回车:

    1
    2
    ffmpeg -version
    ffprobe -version

    如果能成功显示 FFmpeg 和 ffprobe 的版本信息,则安装和配置成功。如果提示“不是内部或外部命令”,请仔细检查你的 PATH 环境变量设置是否正确,路径是否包含 bin 目录,以及是否重新打开了命令行窗口。有时需要重启电脑才能使环境变量完全生效。

macOS 安装

使用 Homebrew 是最推荐的方式:

1
brew install ffmpeg

验证:打开新的终端窗口,输入 ffmpeg -version

Linux 安装
  • Debian/Ubuntu:
    1
    sudo apt update && sudo apt install ffmpeg
  • CentOS/RHEL/Fedora:
    通常需要先启用 EPEL 和 RPM Fusion 仓库(如果尚未启用):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # (根据你的具体发行版和版本,启用仓库的命令可能略有不同)
    # 例如 CentOS 7/8:
    # sudo yum install epel-release -y
    # sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm https://download1.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-$(rpm -E %rhel).noarch.rpm -y
    # sudo yum install ffmpeg ffmpeg-devel -y

    # 例如 Fedora:
    # sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm -y
    # sudo dnf install ffmpeg --allowerasing -y
    验证:输入 ffmpeg -version

17.1.4 Python 交互方式的选择与 ffmpeg-python 安装

在 Python 中与 FFmpeg 交互主要有两种方式:

subprocess 模块 (基础方式)

这是 Python 内置的标准库,可以用来启动和管理子进程,执行任何命令行程序,包括 ffmpeg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import subprocess
import shlex # 用于安全处理带空格或特殊字符的参数

input_file = 'input.mp4'
output_file = 'output.avi'
# 推荐将命令构造成列表,避免 shell 注入风险
cmd = ['ffmpeg', '-i', input_file, '-c:v', 'copy', '-c:a', 'copy', output_file]

print(f"将要执行: {' '.join(cmd)}") # 打印命令方便查看
try:
# check=True: 如果 ffmpeg 返回非 0 退出码(表示错误),则抛出 CalledProcessError
# capture_output=True: 捕获标准输出和标准错误
# text=True, encoding='utf-8': 将输出解码为文本
result = subprocess.run(cmd, capture_output=True, text=True, check=True, encoding='utf-8', errors='ignore')
print("FFmpeg 命令成功执行。")
# FFmpeg 的详细进度和信息通常输出到 stderr
print("FFmpeg 输出 (stderr):")
print(result.stderr)
except subprocess.CalledProcessError as e:
# 命令执行失败
print(f"FFmpeg 命令执行失败,返回码: {e.returncode}")
print("FFmpeg 错误输出 (stderr):")
print(e.stderr)
except FileNotFoundError:
print("错误:找不到 ffmpeg 命令。请确认 FFmpeg 已安装并添加到系统 PATH。")
except Exception as e:
print(f"执行命令时发生未知错误: {e}")

优点: 无需额外库,灵活,可以直接执行任何 FFmpeg 命令。
缺点: 当命令参数复杂(尤其是涉及滤镜 -vf-filter_complex 时,包含大量引号、逗号、分号)时,手动构建命令列表非常繁琐且极易出错。需要手动解析 FFmpeg 的 stderr 输出以获取进度或判断具体错误。

ffmpeg-python 库 (推荐)

这是一个非常流行的 Python 库,它封装了调用 FFmpeg 命令行的过程。你使用 Python 的函数和链式调用来定义输入、输出、滤镜等操作,ffmpeg-python自动为你生成对应的、复杂的 FFmpeg 命令行参数,并替你调用 subprocess 来执行。

需要注意的是,下载需要下载的是这个库,而不是单pip install ffmpeg

1
pip install ffmpeg-python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import ffmpeg  # 使用 python-ffmpeg 库

input_file = "input.mp4"
output_file = "output.avi"

print(f"将要使用 python-ffmpeg 库处理:{input_file} -> {output_file}")

try:
# 使用 ffmpeg-python 库的流式 API
# 创建输入流
stream = ffmpeg.input(input_file)

# 设置输出选项,使用相同的视频编解码器设置
stream = ffmpeg.output(stream, output_file, vcodec='copy')

# 打印将要执行的 FFmpeg 命令
print(f"等效的 FFmpeg 命令: {' '.join(ffmpeg.compile(stream))}")

# 执行 FFmpeg 处理
ffmpeg.run(stream, capture_stdout=True, capture_stderr=True)

print("FFmpeg 处理成功完成!")
print("注意:使用 python-ffmpeg 库时,输出信息被库内部处理")

except ffmpeg.Error as e:
# 处理 FFmpeg 错误
print(f"FFmpeg 处理失败")
print("FFmpeg 错误输出:")
print(e.stderr.decode('utf-8', errors='ignore'))
except ImportError:
print("错误:找不到 ffmpeg-python 库。请使用 'pip install ffmpeg-python' 安装。")
print("注意:这个库需要系统中已安装 FFmpeg 命令行工具。")
except Exception as e:
print(f"处理视频时发生未知错误: {e}")

优点:

  • 代码更 Pythonic、更易读: 使用函数调用和链式操作构建流程,比拼接字符串或列表清晰。
  • 简化复杂命令: 特别是对于包含多个滤镜、输入、输出的复杂操作,能大大简化命令构建过程。
  • 减少引用/转义错误: 库会自动处理参数的引用和转义问题。

缺点:

  • 仍然依赖外部 FFmpeg: 它只是一个包装器,不是 FFmpeg 的 Python 重新实现,所以一定要保证自己的环境变量存在ffmpeg!
  • 可能无法覆盖所有参数: 对于 FFmpeg 非常规或最新的参数,库可能没有直接对应的函数或关键字参数(但通常提供传递原生参数的方式)。
  • 错误调试: 虽然它捕获了 stderr,但有时理解错误原因仍需结合 FFmpeg 本身的知识。

17.2 FFmpeg 命令行结构

目标:

  • 深入理解 FFmpeg 命令行的组成、选项类型、作用顺序和流指定符。
  • 了解如何使用 ffprobe 获取媒体文件的详细信息。
  • 全面掌握 ffmpeg-python 库的核心语法,包括如何定义输入、输出、滤镜,以及如何执行处理和获取信息。
  • 建立 FFmpeg 命令行参数与 ffmpeg-python 函数/参数之间的对应关系。

17.2.1 FFmpeg 命令行结构详解

要精确控制 FFmpeg,理解其命令行结构至关重要。其基本模式为:

1
ffmpeg [全局选项] [输入选项] -i 输入文件1 ... [输出选项] 输出文件1 ...
选项的作用域与顺序

理解选项的作用域和顺序是正确使用 FFmpeg 的关键。选项通常会影响紧随其后的文件或操作。

全局选项 (Global Options)

这些选项位于 ffmpeg 命令之后、第一个 -i 输入文件之前,它们控制整个 FFmpeg 进程的行为。

  • -y: 自动覆盖同名输出文件,无需确认。
  • -n: 如果输出文件已存在则不执行,防止覆盖。
  • -stats: (通常默认开启) 显示处理进度统计信息。
  • -v <level>-loglevel <level>: 设置日志记录的详细程度。常用的 <level> 包括:
    • quiet: 静默,几乎不输出。
    • panic, fatal, error: 只输出严重错误。
    • warning: 输出警告和错误。
    • info: 输出标准信息(默认)。
    • verbose, debug, trace: 输出更详细的调试信息。
  • -hide_banner: 隐藏 FFmpeg 启动时打印的版本号、库信息和编译配置等横幅。
1
2
3
4
5
6
7
8
# 示例 1: 覆盖输出文件并隐藏版本信息
ffmpeg -y -hide_banner -i input.mp4 output.avi
# -y: 自动覆盖 output.avi
# -hide_banner: 不显示版本等信息

# 示例 2: 只显示警告及以上级别的日志
ffmpeg -v warning -i input.mp4 output_warn.mp4
# -v warning: 只输出警告和错误信息

输入选项 (Input Options)

这些选项放在特定的 -i 之前,并且只对紧随其后的那个输入文件生效。

  • -f <格式>: 强制 FFmpeg 以指定的容器格式来解析输入文件(通常 FFmpeg 能自动检测,但在处理原始数据流或特殊格式时需要)。
  • -r <帧率>: 强制指定输入视频的帧率(例如,用于帧率信息丢失的原始视频流或图像序列)。
  • -s <宽x高>: 强制指定输入视频的分辨率(例如 640x480,主要用于原始视频流)。
  • -ss <时间>: 放在 -i 之前,表示输入定位 (Input Seeking)。FFmpeg 会尝试快速跳转到接近指定时间点(可以使用秒 15.5HH:MM:SS.ms 00:01:05.500 格式)的关键帧开始处理。优点是速度快,缺点是定位可能不完全精确到帧
  • -stream_loop <次数>: 设置输入流循环次数。-1 表示无限循环(常用于将单张图片或短 GIF 做成循环背景视频)。0 表示不循环(默认)。
  • -re: 要求 FFmpeg 以源文件固有的帧率读取输入。如果不加此参数,FFmpeg 会尽可能快地读取文件。该选项主要用于模拟直播推流或处理实时输入设备,以匹配源的自然速率。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 示例 1:提取视频的前五秒转换为图片序列
ffmpeg -i input.mp4 -t 5 -vf fps=30 D:/video_frames/frame_%04d.png
#-i my_video.mp4: 指定你的输入视频文件。
#-t 5: 告诉 FFmpeg 只处理输入视频的前 5 秒。
#-vf fps=30: 使用 fps 视频滤镜,设置输出图片的帧率为 30 帧/秒。这样 5 秒的视频会生成 5 * 30 = 150 张图片。这个帧率与你后面要测试的命令中的 -framerate 30 相匹配。
#D:/video_frames/frame_%04d.png: 指定输出图片的位置和命名规则。
# frame_%04d.png: 图片的命名规则(frame_0001.png, frame_0002.png, ..., frame_0150.png)。




# 示例 2: 将图片序列合成为视频,指定输入帧率和开始时间点对应的图片
ffmpeg -framerate 30 -ss 2 -f image2 -i frame_%04d.png output.mp4
# -framerate 30: 假设图片序列是 30fps
# -ss 2: 从时间点 2 秒对应的帧开始 (即大约第 60 帧,frame_0060.png)
# -f image2: 明确输入是图像序列
# -i frame_%04d.png: 输入文件模式 (frame_0001.png, frame_0002.png...)

代码实现与最佳实践如下:

  • 1.使用函数封装命令、定义形参以便后续调整值

  • 2.使用列表构造命令,防止注入攻击

    当你用 subprocess.run() 运行一个外部命令(比如 ffmpeg)时,如果不特别指定 stdoutstderr 参数,那么这个外部命令(子进程)的标准输出标准错误会直接连接到你的 Python 脚本(父进程)的标准输出和标准错误。简单来说,就是 ffmpeg 打印的任何信息(无论是正常信息还是错误信息)都会直接显示在你的 Python 脚本运行的那个终端窗口里

    当你给 subprocess.run() 传递 stdout=subprocess.PIPE 这个参数时,你是在告诉 Python: “请不要让子进程 (ffmpeg) 的标准输出直接打印到终端。而是,创建一个管道,把子进程的标准输出重定向到这个管道里。这样一来,我这个父进程(Python 脚本)后续就可以从这个管道里读取子进程的输出了。”

    在 Python 3.7 及更高版本中,subprocess.run() 提供了一个更方便的参数 capture_output=True设置 capture_output=True 等同于同时设置 stdout=subprocess.PIPEstderr=subprocess.PIPE,这也是我们第一个示例使用的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import subprocess
import os


def extract_frames_with_ffmpeg(input_video, output_pattern, duration=5, fps=30):
try:
# 确保输出目录存在
output_dir = os.path.dirname(output_pattern)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"创建输出目录: {output_dir}")

# 构建ffmpeg命令
command = [
'ffmpeg',
'-i', input_video,
'-t', str(duration),
'-vf', f'fps={fps}',
output_pattern
]

# 执行命令
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)

print("视频帧提取成功!")
return True
except subprocess.CalledProcessError as e:
print(f"执行ffmpeg命令失败: {e}")
print(f"错误输出: {e.stderr}")
return False
except Exception as e:
print(f"发生错误: {e}")
return False


def create_video_from_frames(input_pattern, output_video, framerate=30, start_time=0):
"""
将图片序列转换回视频文件

Args:
input_pattern: 输入图片序列的模式,例如 "frame_%04d.png"
output_video: 输出视频文件路径
framerate: 帧率,默认为30fps
start_time: 开始时间(秒),默认为0

Returns:
bool: 操作是否成功
"""
try:
# 确保输出目录存在
output_dir = os.path.dirname(output_video)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"创建输出目录: {output_dir}")

# 构建ffmpeg命令
command = [
'ffmpeg',
'-framerate', str(framerate),
'-ss', str(start_time),
'-f', 'image2',
'-i', input_pattern,
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
output_video
]

# 执行命令
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)

print("从图片序列创建视频成功!")
return True
except subprocess.CalledProcessError as e:
print(f"执行ffmpeg命令失败: {e}")
print(f"错误输出: {e.stderr}")
return False
except Exception as e:
print(f"发生错误: {e}")
return False


# 示例使用
if __name__ == "__main__":
# 从视频提取帧
# input_video = "input.mp4" # 输入视频文件路径
# output_pattern = "D:/video_frames/frame_%04d.png" # 输出图片路径模式
# extract_frames_with_ffmpeg(input_video, output_pattern, duration=5, fps=30)

# 从帧创建视频
input_frames = "D:/video_frames/frame_%04d.png" # 输入图片序列
output_video = "D:/video_frames/output.mp4" # 输出视频文件
create_video_from_frames(input_frames, output_video, framerate=30, start_time=2)


输出选项 (Output Options)

这些选项位于某个输出文件名之前,并且只对紧随其后的那个输出文件生效。这是控制输出文件特性最关键、最丰富的一类选项。

视频编码器选择 (-c:v-vcodec)

选择合适的视频编码器是平衡画质、文件大小、编码速度和兼容性的关键。

编码器 (值)常用库名 (FFmpeg)类型压缩效率编码速度兼容性主要用途/备注
H.264 / AVClibx264有损良好快-中等极好最通用,Web、移动端、流媒体广泛支持。质量和效率良好。
H.265 / HEVClibx265有损优异中等-慢压缩率比 H.264 高约 30-50%,但编码更耗时,部分旧设备不支持。
VP9libvpx-vp9有损优异中等-慢Google 开发,开源免费,WebM 容器常用,YouTube 使用。效率类似 H.265。
AV1libaom-av1有损顶尖极慢增长中最新一代开放标准,压缩率最高,但编码非常非常慢,解码要求也高。
MPEG-4 Visualmpeg4有损一般较好较旧的标准,兼容性尚可,效率不如 H.264。
copycopy无损(复制)N/A极快取决于容器特殊值: 不重新编码,直接复制原始视频流。仅用于改变容器或截取。
… 其他特定编码器prores, dnxhd (编辑用), h264_nvenc (Nvidia 硬编) 等。

选择建议:

  • 追求兼容性/通用性: 首选 libx264 (H.264)。
  • 追求最高压缩率 (不计时间):libaom-av1 (AV1),其次 libx265 (H.265) 或 libvpx-vp9 (VP9)。
  • 仅改变容器或截取: 优先尝试 copy 以获得最快速度和无损质量。
  • 硬件加速: 如果你的硬件支持且 FFmpeg 编译时包含支持(如 Nvidia GPU 的 h264_nvenc, hevc_nvenc),可以尝试使用,通常能大幅提高编码速度,但质量控制方式和效果可能与软编码略有不同。

音频编码器选择 (-c:a-acodec)

编码器 (值)常用库名 (FFmpeg)类型压缩效率兼容性主要用途/备注
AACaac有损良好极好最通用,MP4/MOV/流媒体常用,是 Apple 平台标准。质量良好。
Opuslibopus有损优异开放免费,特别适合语音和网络传输(低延迟、低码率下表现好),WebRTC 标准。
MP3libmp3lame有损一般极好历史悠久,兼容性最好,但同等码率下效率不如 AAC/Opus。
Vorbislibvorbis有损良好开放免费,常用于 Ogg, WebM 容器。
FLACflac无损较高较好无损压缩,保留原始音频质量,文件较大。适合存档或对音质要求极高。
copycopy无损 (复制)N/A取决于容器特殊值: 直接复制原始音频流。
… 其他pcm_s16le (未压缩 PCM), alac (Apple 无损) 等。

选择建议:

  • 通用、兼容性好: 首选 aac
  • 网络传输、语音、低码率高质量: 首选 libopus
  • 需要无损压缩:flac
  • 仅改变容器或截取: 尝试 copy

字幕编码器选择 (-c:s-scodec)
字幕相对简单,常用选项:

  • srt: 输出为 SubRip (.srt) 格式(通常作为单独文件,或封装在 MKV 中)。
  • mov_text: 输出为 MP4/MOV 兼容的内嵌文本字幕。
  • copy: 直接复制原始字幕流(如果目标容器支持)。

质量与比特率控制

这是影响输出文件大小和视觉/听觉感受的关键。主要有两种策略:

比特率就是每秒钟用多少数据来记录或传输声音和图像,这个数值直接决定了音视频的清晰程度和文件的大小

控制方式对应参数 (常用)描述优点缺点
恒定质量-crf <值> (视频)推荐用于视频。 让编码器根据画面复杂度动态调整比特率以维持感知质量恒定。值越低,质量越好,文件越大。对 x264 常用 18(高)-28(低);x265 类似,但相同值压缩率更高。文件大小更可控,质量相对稳定。最终比特率和文件大小不固定。
平均比特率-b:v <值> (视频) -b:a <值> (音频)推荐用于音频,也可用于视频。 指定输出文件的目标平均比特率(如 ‘2M’, ‘128k’)。编码器会尽量使最终平均速率接近此值。文件大小更容易预测和控制。简单画面可能浪费码率,复杂画面可能码率不足导致质量下降。
最大/最小比特率-maxrate <值> -minrate <值> -bufsize <值>通常与 -b:v 配合,限制比特率波动范围,用于流媒体兼容性。-bufsize 定义解码器缓冲区大小。兼容性好,码率波动可控。配置复杂,可能影响质量。

选择建议:

  • 视频: 优先使用 -crf 控制质量,选择一个你能接受的质量和文件大小平衡点 (如 22-24)。只有在对输出文件大小有严格限制时才使用 -b:v
  • 音频: 通常使用 -b:a 指定目标比特率 (如 128k, 192k, 256k for AAC/Opus)。

编码速度 (-preset)

编码器通常提供预设参数集,用于在编码速度压缩效率(同等质量下文件大小)之间做权衡。

Preset 值速度压缩效率/质量CPU 消耗
ultrafast最快最低最低
superfast很快较低较低
veryfast较快较低较低
faster稍快一般一般
fast较好较高
medium默认值中等
slow更好
slower较慢优异较高
veryslow最慢最佳最高
placebo(极慢)(几乎无提升)(极高)

选择建议:mediumfast 开始尝试。如果追求极致压缩率且不介意时间,可选 slowslower。如果速度是首要考虑,可选 veryfastultrafast,但文件会大很多。

时间控制 (-ss, -t, -to)

选项位置作用精度速度
-ss <时间>输入前输入定位 (Seeking): 从输入源的指定时间点开始读取。不精确
-ss <时间>输出前输出定位 (Seeking): 解码到指定时间点才开始输出。精确
-t <时长>输出前限制输出的总时长。N/AN/A
-to <时间>输出前指定输出的结束时间点 (相对于输入开始时间)。N/AN/A

选择建议:

  • 快速截取大致片段: -ss 放在 -i 前。
  • 精确截取片段: -ss 放在输出文件前(会解码前面所有内容),或者先用 -ss (输入前) 定位到大概位置,再用 -t-to (输出前) 精确控制结束点。
  • 只截取开头部分: 只用 -t-to

场景 1: 快速截取大致片段

目标: 快速从视频大约 1 分钟(60 秒)处开始,截取 15 秒长度的片段。优先考虑速度,不强求帧级别的精确。

方法:-ss 放在 -i 输入文件之前(输入定位),并使用 -t 指定输出时长。可以尝试使用 -c copy 以获得最快速度。

1
2
3
4
5
ffmpeg -ss 60 -i input.mp4 -t 15 -c copy output_fast_cut.mp4
# -ss 60: (输入定位) 快速跳转到 input.mp4 中接近第 60 秒的位置 (通常是前面的关键帧)
# -i input.mp4: 输入文件
# -t 15: (输出选项) 从定位到的点开始,截取 15 秒的时长
# -c copy: (输出选项) 直接复制音视频流,不重新编码,速度最快 (如果格式兼容)

场景 2: 精确截取片段

目标: 精确地从视频的第 1 分 05 秒 (00:01:05) 开始,截取到第 1 分 15 秒 (00:01:15) 结束(总时长 10 秒)。

-ss 放在输出文件之前,并配合 -to-t。FFmpeg 会解码 -ss 之前的所有内容。通常不能使用 -c copy

1
2
3
4
5
ffmpeg -i input.mp4 -ss 00:01:05 -to 00:01:15 -c:v libx264 -c:a aac output.mp4
# -i input.mp4: 输入文件
# -ss 00:01:05: (输出定位) 精确地从解码后的第 1分05秒 开始编码输出
# -to 00:01:15: (输出定位) 精确地在解码后的第 1分15秒 结束编码输出 (注意是绝对时间点)
# -c:v libx264 -c:a aac: 需要重新编码视频和音频以保证精确切割

注意: 这种方式非常精确,但因为需要解码前面的内容,所以速度很慢,尤其当 -ss 的时间点靠后时。

若我们一个视频很长的情况下,而我们需要截取的片段而又在非常的末尾,这时候选择这种精确片段的语法就不好了,所以我们换一种思路:

先用 -ss (输入前) 快速定位到大概位置,再用 -t 精确控制截取时长

1
2
3
4
5
ffmpeg -ss 00:01:00 -i input.mp4 -t 10 -c:v libx264 -c:a aac output.mp4
# -ss 00:01:00: (输入定位) 快速跳转到接近 1 分钟的位置
# -i input.mp4: 输入文件
# -t 10: (输出选项) 从实际定位到的点开始,精确地截取 10 秒的**时长**
# 这种方式下,起始点可能略早于 1 分钟,但输出时长是准确的 10 秒,速度比纯输出定位快得多。

场景 3: 只截取开头部分

目标: 只保留视频的前 20 秒。

1
2
3
4
5
6
7
8
9
# 使用 -t 指定时长
ffmpeg -i input.mp4 -t 20 -c copy output_first_20s_t.mp4
# -t 20: 从开头截取 20 秒时长

# 使用 -to 指定结束时间点
ffmpeg -i input.mp4 -to 20 -c copy output_first_20s_to.mp4
# -to 20: 从开头截取到第 20 秒结束

# 注意: -t 和 -to 在只截取开头时效果类似,都比较快,且通常可以使用 -c copy。
复杂滤镜(-filter_complex)

-filter_complex 在我们接下来需要介绍的滤镜篇十分重要!他是实现复杂的滤镜效果的核心关键点

为什么需要 -filter_complex

我们用的 -vf (视频滤镜) 和 -af (音频滤镜) 适用于简单的、线性的处理流程:一个输入流 -> 经过一个或多个滤镜(用逗号分隔) -> 一个输出流。

但如果你需要做更复杂的操作,比如:

  • 处理多个输入流: 例如,把一个 logo 图片叠加到视频上(视频是输入1,logo 是输入2)。
  • 一个滤镜产生多个输出: 例如,split 滤镜可以把一个视频流复制成两份,分别进行不同处理。
  • 合并多个流: 例如,把两个视频左右拼接 (hstack),或者把处理过的视频流和处理过的音频流合并输出。

这些情况就需要用到 -filter_complex 来定义一个滤镜图 (filtergraph)

-filter_complex 的基本语法结构:

可以把它想象成用文字描述一个“数据流管道系统”:

1
2
3
4
5
6
7
-filter_complex "[输入流标签1][输入流标签2]... 滤镜1=参数[输出流标签A]; [输入流标签C]滤镜2=参数[输出流标签B]; [标签A][标签B]滤镜3[最终输出标签]"

# 对应如下命令行
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[0:v][1:v] overlay=10:10 [outv]" \
-map "[outv]" -map 0:a? \
output.mp4

核心组成部分解释:

  1. [...] (方括号 - 流标签 / Stream Labels):

    • 这就像是给在滤镜图内部流动的临时数据流(视频流、音频流)起名字。

    • 引用输入文件流:

      通常用

      1
      [文件索引:流标识符]

      的形式。例如:

      • [0:v] 指的是第一个输入文件 (-i 后面第一个) 的视频流 (通常是第一个视频流)。
      • [0:a] 指的是第一个输入文件的第一个音频流。
      • [1:v] 指的是第二个输入文件的视频流。
    • 自定义标签: 你可以给一个滤镜处理后的输出流起一个自定义的名字,比如 [scaled_video], [watermarked], [outv]。这个名字可以被后续的滤镜或最后的 -map 指令引用。

-map 的配合:

因为 -filter_complex 处理后的输出流是带有自定义标签的(比如上面例子中的 [outv]),FFmpeg 不会自动将它映射到最终的输出文件。你需要使用 -map 选项来明确告诉 FFmpeg 使用哪个标签的流作为输出文件的视频流、哪个作为音频流等。

  • -map "[outv]": 将标签为 outv 的流映射为输出文件的第一个视频流(通常是这样)。
  • -map "[outa]": 将标签为 outa 的流映射为输出文件的第一个音频流
  • 你仍然可以用 -map 0:a? 这样的方式来映射未经滤镜图处理的原始输入流(比如直接复制原始音频)。

简单示例:叠加 Logo (回顾)

我们再看一下这个例子,体会一下标签和分号的作用:

1
2
3
4
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[0:v][1:v] overlay=10:10 [outv]" \
-map "[outv]" -map 0:a? \
output.mp4
  • -filter_complex 内:
    • [0:v][1:v]输入标签,分别代表 input.mp4 的视频流和 logo.png 的视频流。
    • overlay=10:10 是作用于这两个输入流的滤镜
    • [outv]overlay 滤镜处理完成后的输出标签
    • 这里只有一个滤镜链,所以没有用到分号 ;
  • -map "[outv]": 将带有 outv 标签的视频流(叠加结果)指定为输出视频。
  • -map 0:a?: 将原始视频的音频(如果存在)也指定到输出。

17.2.2 **常用滤镜 (-vf, -af) **

FFmpeg 的滤镜系统是其最强大的功能之一,它允许你对音视频流进行各种精密的修改和处理。你可以将滤镜想象成一个个处理单元,音视频数据流过这些单元时会被修改,像 Photoshop (PS), Premiere Pro, DaVinci Resolve 这些图形界面工具非常强大,对于单张图片或单个视频的精细、创意性编辑,它们通常更直观、效率更高,因为你可以实时看到效果并进行微调。

但假设思考这么一些场景你就能够理解FFmpeg的强大之处:

网站后台自动处理用户上传的图片(统一尺寸、加水印)、视频网站自动生成不同清晰度的版本等。

用户上传视频到你的网站,后端服务器自动调用 FFmpeg 进行转码、截图、添加片头片尾等。

一个监控系统,当检测到特定事件时,自动调用 FFmpeg 截取相关录像片段并进行压缩或格式转换。

  • 作用: 修改或分析音视频流的内容。常见的操作包括:调整尺寸/裁剪、旋转/翻转、叠加图像/文字、调色、降噪、改变速度、调整音量、混音等等。
  • 分类:
    • 视频滤镜 (-vf): 处理视频帧数据。
    • 音频滤镜 (-af): 处理音频采样数据。
  • 数量庞大: FFmpeg 内置了数百种滤镜,覆盖了绝大部分常见的处理需求。你可以通过 ffmpeg -filters 命令查看所有支持的滤镜。

在进行滤镜处理之前,我们给出三张好看的原图作为参考

Image 1 Image 2 Image 3

(1) 模糊 (boxblur)

参数: boxblur。用于实现均匀模糊效果。

命令行示例:

1
2
## ./ffmpeg -i 1.jpg -vf boxblur=2 blur1.jpg
ffmpeg -i 1.png -vf boxblur=2 blur1.png

(注:示例命令中的 ./ffmpeg 通常在 Linux/macOS 下表示当前目录的可执行文件,Windows 下或配置好 PATH 后直接用 ffmpeg 即可。以下示例统一使用 ffmpeg。)

说明: 值越大越模糊。以下是值分别为 2、4、8 的效果:

Image 1 Image 2 Image 3

(2) 变色

FFmpeg 提供了多种变色方法。

(a) colorbalance

通过调整 RGB 三个颜色通道在不同亮度区域(阴影、中间调、高光)的权重来实现色彩平衡调整。

参数说明:
选项有三组,分别对应阴影(shadows/s)、中间调(midtones/m)、高光(highlights/h):

  • rs, gs, bs: 调整阴影部分的红/绿/蓝。
  • rm, gm, bm: 调整中间调的红/绿/蓝。
  • rh, gh, bh: 调整高光的红/绿/蓝。

每个选项的值范围为 [-1, 1]。正数表示增强该颜色在该区域的影响力(偏向该色),负数表示减弱(远离该色)。

命令行示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 增强阴影中的红色
ffmpeg -i 3.png -vf colorbalance=rs=1 colorbalance_rs1.jpg

## 增强中间调的红色
ffmpeg -i 3.png -vf colorbalance=rm=1 colorbalance_rm2.jpg

## 增强高光中的红色
ffmpeg -i 3.png -vf colorbalance=rh=1 colorbalance_rh3.jpg

# 稍微增加阴影和中间调的红色和绿色,略微减少蓝色,使整体偏暖
ffmpeg -i 3.png -vf colorbalance=rs=0.2:gs=0.1:rm=0.3:gm=0.2:bm=-0.1 colorbalance_warm.jpg
# rs=0.2, gs=0.1: 轻微增加阴影中的红、绿色
# rm=0.3, gm=0.2: 稍多增加中间调的红、绿色
# bm=-0.1: 轻微减少中间调的蓝色

效果图:

Image 1 Image 2 Image 3 Image 4
(b) colorchannelmixer

通过一个 3x4 (或 4x4) 矩阵对 RGBA 四个通道进行线性组合,重新计算每个输出通道的值。可以实现灰度、各种色调(如棕褐色)等复杂效果。

参数说明:
需要提供 12 个(用于 RGB 输出)或 16 个(用于 RGBA 输出)用冒号 : 分隔的权重值。顺序为:rr:rg:rb:ra:gr:gg:gb:ga:br:bg:bb:ba[:ar:ag:ab:aa]。计算公式为(以红色输出为例):R_out = R_in*rr + G_in*rg + B_in*rb + A_in*ra
(Gout, Bout, Aout 同理)权重值通常在 [0, 1] 范围内。

命令行示例:

1
2
3
4
5
6
7
## 灰度效果 (一种近似计算)
## R_out = G_out = B_out = 0.3*R_in + 0.4*G_in + 0.3*B_in + 0*A_in
ffmpeg -i 2.png -vf colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3:0 colorchannelmixer_gray.jpg


## 棕褐色调 (Sepia)
ffmpeg -i 2.png -vf colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131:0 colorchannelmixer_sepia.jpg

效果图:

Image 1 Image 2
######(c) `hue` 与 Saturation

调整图像的色相 (Hue)饱和度 (Saturation)

PS中关于HSB色彩模式详解 - 知乎

参数说明:

  • h=<角度>: 色相角度 (单位:度),在色轮上旋转颜色。范围 [0, 360] 循环,默认 0。
  • s=<数值>: 饱和度。范围 [-10, 10],默认 1。大于 1 增强饱和度,小于 1 降低,0 为灰度,负数为补色效果。
  • H=<弧度>: 同 h,但单位是弧度。
  • b=<数值>: 亮度。范围 [-10, 10],默认 0。

有关色相、饱和度、亮度请详见色彩三要素

命令行示例:

1
2
3
4
5
## 改变色相到 180 度,饱和度不变
ffmpeg -i 3.png -vf hue=h=180:s=1 hue_h180.jpg

## 降低饱和度 (接近黑白但略带反色)
ffmpeg -i 3.png -vf hue=h=0:s=-5 hue_s-5.jpg
Image 1 Image 2
-----
(d) lutyuv / lutrgb

这两个滤镜应用查找表 (LUT) 的概念来进行颜色转换,但它们的主要工作方式是基于你提供的数学表达式来计算每个颜色分量的输出值。

  • lutyuv: 在 YUV 颜色空间操作(Y: 亮度, U/V: 色度)。
  • lutrgb: 在 RGB 颜色空间操作(R: 红, G: 绿, B: 蓝)。

Y = 亮度 (有多亮)

U = 蓝色色度差 (颜色有多偏蓝/黄)

V = 红色色度差 (颜色有多偏红/青)

视频压缩和传输常常使用 YUV (或类似的 YCbCr) 而不是我们屏幕显示常用的 RGB(红绿蓝),主要是符合人眼特性、与压缩效率高两个优点

它们的核心是通过表达式,根据输入像素各分量的当前值 (val) 来计算输出像素各分量的新值

参数说明

你需要为想要修改的颜色分量指定一个计算表达式。例如,对于 lutyuv,你可以指定 y='<表达式>', u='<表达式>', v='<表达式>'

表达式中可用的常用变量包括:

变量名含义示例用法
val当前正在处理的颜色分量的输入值。y=2*val (亮度翻倍)
negval输入值的反转值,等于 maxval + minval - valy=negval (亮度反转)
clipvalval,但其值会被自动限制在对应分量的有效范围内。
minval当前颜色分量允许的最小值 (例如 YUV 中的 16 或 RGB 中的 0)。y=maxval+minval-val
maxval当前颜色分量允许的最大值 (例如 YUV 中的 235/240 或 RGB 中的 255)。y=maxval-val
w, h图像/视频的宽度和高度 (像素)。x/w (归一化坐标)
x, y当前正在处理的像素的坐标 (从 0 开始)。y=val+x/10
t当前帧的时间戳 (秒)。y=val*sin(t)

表达式支持常见的数学运算符 (+, -, *, /, ^) 和函数 (sin, cos, max, min, clip, if 等)。

命令行示例 (lutyuv)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 底片效果 (反转 Y, U, V 三个分量)
# 使用 negval 变量更简洁
ffmpeg -i 1.png -vf lutyuv="y=negval:u=negval:v=negval" lutyuv_negate.jpg

# 黑白效果 (移除色度信息,将 U, V 设为中间值 128)
# Y (亮度) 分量保持不变 (y=val 是默认值)
ffmpeg -i 1.png -vf lutyuv="u=128:v=128" lutyuv_bw.jpg

# 提升亮度 (Y 值翻倍,注意可能过曝导致细节丢失)
# 计算结果会自动 clip 到有效范围 [minval, maxval]
ffmpeg -i 1.png -vf lutyuv="y=2*val" lutyuv_brighten.jpg

# 调整色度 (示例:增加 U 分量,略微增加 V 分量)
ffmpeg -i 1.png -vf lutyuv='u=1.2*val:v=1.1*val' lutyuv_adjust_uv.jpg
效果图
Image 1 Image 2 Image 3 Image 4

重要说明:基于表达式的 LUT vs 加载 LUT 预设文件

常识来说LUT 被描述为一种可以保存和加载的“滤镜效果文件”(通常是 .cube, .3dl, .look 等格式)。这种用法在视频编辑和调色领域非常普遍。

这两种关于 LUT 的描述都是正确的,但指向的是不同的概念和 FFmpeg 功能:

  1. LUT 预设文件 (.cube 等):

    • 存储了一个预先计算好的、庞大的颜色映射表,定义了各种输入颜色如何精确地转换为输出颜色。
    • 常用于应用复杂的色彩风格(如电影感、复古色调)或进行色彩空间转换(如 Log 转 Rec.709)。
    • 在 FFmpeg 中,加载和应用这类文件 主要使用 lut3d 滤镜 (对于 3D LUTs,如 .cube) 或 lut1d 滤镜 (对于 1D LUTs)。
  2. FFmpeg 的 lutyuv/lutrgb/lut 滤镜 (基于表达式):

    • 如上所述,这些滤镜允许你使用数学表达式来实时定义颜色转换规则
    • 它们非常灵活,适合进行程序化的、基于像素值或坐标的颜色调整(例如上面示例中的反相、去色、亮度调整)。
    • 但它们不直接加载 .cube 等外部预设文件。

如何使用 FFmpeg 加载常见的 .cube LUT 文件?

你需要使用 lut3d 滤镜:

1
2
# 示例:将 "my_film_look.cube" 这个 LUT 文件应用到视频上
ffmpeg -i input.mp4 -vf lut3d="path/to/my_film_look.cube" output_lut_applied.mp4
  • -vf lut3d="<你的LUT文件路径>": 这里的参数就是你的 .cube (或其他支持的 LUT 格式) 文件的路径。

附:100个LUT文件模板下载


(3) 裁剪 (crop)

从视频或图像中裁剪出指定尺寸和位置的矩形区域(基于图片大小)

参数说明: crop=width:height:x:ycrop=w=W:h=H:x=X:y=Y

  • w, h: 输出宽度和高度。可用 iw, ih (输入宽高)。
  • x, y: 裁剪区域左上角坐标。默认居中。

命令行示例:

1
2
3
4
5
## 从 (100, 100) 裁剪 200x300
ffmpeg -i 1.png -vf crop=w=200:h=300:x=100:y=100 crop_1.jpg

## 从中心裁剪 400x500 (省略 x, y)
ffmpeg -i 1.png -vf crop=400:500 crop_center.jpg
Image 1 Image 2

在指定矩形区域内应用像素插值或模糊,尝试去除静态 Logo。效果有限,如下图,此图片携带b站的水印

image-20250502170301166

参数说明: delogo=x=X:y=Y:w=W:h=H[:band=B][:show=S]

左上角: x = 534, y = 22

右下角 : x = 763, y = 76

我们可以计算出 delogo 滤镜需要的宽度 (w) 和高度 (h):

宽度 w = 右下角 x - 左上角 x = 763 - 534 = 229

高度 h = 右下角 y - 左上角 y = 76 - 22 = 54

命令行示例:

1
ffmpeg -i 4.png -vf "delogo=x=534:y=22:w=229:h=54" output_delogo.jpg

效果图如下:请注意: delogo 的实际效果取决于水印周围的背景复杂度,它主要是基于边缘像素进行插值填充,可能不会完美去除或产生理想的模糊效果,有时看起来会有些“涂抹”感。

output_delogo


(5) 加边框 (drawbox)

在图像或视频上绘制矩形边框。

参数说明: drawbox=x=X:y=Y:w=W:h=H:color=C[@A][:t=T]

  • x,y,w,h: 边框位置和尺寸。默认 x=0, y=0, w=iw, h=ih (整个画面)。
  • colorc: 边框颜色,可以是颜色名 (red, blue) 或十六进制 (0xFF0000),可以用 @透明度 指定透明度 (如 red@0.5)。默认黑色。
  • thicknesst: 边框线宽(像素)。fill 表示填充整个矩形。默认 3。

命令行示例:

1
2
3
4
5
6
7
8
## 绘制默认黑色实心边框 (充满边界)
ffmpeg -i 1.png -vf drawbox drawbox_default.jpg

## 在 (10,10) 绘制 200x100 的半透明红色边框,线宽默认
ffmpeg -i 1.png -vf drawbox=x=10:y=10:w=200:h=100:color=red@0.5 drawbox_rect.jpg

## 在 (10,10) 绘制 300x230 的半透明粉色边框,线宽 10
ffmpeg -i 1.png -vf drawbox=x=10:y=10:w=300:h=230:color=pink@0.5:t=10 drawbox_thick.jpg

效果图

Image 1 Image 2 Image 3

(6) 画网格 (drawgrid)

在视频上绘制网格线。

参数说明: drawgrid=width=W:height=H:thickness=T:color=C[@A]

  • widthw: 网格单元的宽度。可用 iw (输入宽)。
  • heighth: 网格单元的高度。可用 ih (输入高)。
  • thicknesst: 网格线的厚度。
  • colorc: 网格线颜色和透明度。

命令行示例:

1
2
3
4
5
## 绘制 3x3 九宫格 (单元宽高为输入宽/高除以3),白色半透明,线宽 2
ffmpeg -i 1.png -vf drawgrid=w=iw/3:h=ih/3:t=2:c=white@0.5 drawgrid_3x3.jpg

## 绘制单元格为 400x100 的红色半透明网格,线宽 2
ffmpeg -i 1.png -vf drawgrid=w=400:h=100:t=2:c=red@0.5 drawgrid_400x100.jpg

效果图:

Image 1 Image 2

(7) 添加字幕 (subtitlesdrawtext)

对于视频在线转SRT字幕,推荐离线语音转文字WhisperDesktop1.7魔改版,自带100种语言,安装包4G

  • subtitles 滤镜: 用于烧录(硬编码)ASS 或 SRT 等格式的字幕文件到视频帧上。

    1
    2
    3
    4
    # 烧录 SRT 字幕,使用默认样式
    ffmpeg -i input.mp4 -vf subtitles=subtitle.srt output_hardsub.mp4
    # 烧录 ASS 字幕,并强制使用 ASS 文件内定义的样式
    ffmpeg -i input.mp4 -vf "subtitles=subtitle.ass:force_style='PrimaryColour=&H00FFFFFF&,Fontsize=24'" output_hardsub_styled.mp4
  • drawtext 滤镜: 用于在视频上绘制动态或静态文本,可以设置字体、大小、颜色、位置、滚动等,非常灵活,但配置复杂。

    1
    2
    # 在左上角显示时间码
    ffmpeg -i input.mp4 -vf "drawtext=fontfile=/path/to/font.ttf:text='%{pts\:hms}':x=10:y=10:fontsize=24:fontcolor=yellow" output_timecode.mp4

(8) 画边缘 (edgedetect)

作用:
此滤idge]用于检测并绘制图像或视频中的边缘(即像素亮度/颜色发生剧烈变化的地方)。它通常基于 Canny 边缘检测算法。

参数说明:
edgedetect=low=L:high=H[:mode=M]

  • low, high: Canny 算法的低阈值和高阈值,范围 [0, 1],且 low <= high。这两个阈值共同决定了哪些变化被识别为边缘。阈值越高,检测到的边缘越少,只有非常明显的边缘才会被保留;阈值越低,检测到的边缘越多,可能会包含一些噪音或不重要的细节。需要根据具体图像和需求调整。
  • mode: 控制输出的模式,常用的有:
    • wires (默认): 输出一个黑色背景、白色线条表示边缘的图像。这就是你看到的“线稿”效果。
    • colormix: 将检测到的边缘(通常为白色)与原始图像的颜色进行混合,产生一种带有彩色边缘描边的效果。
    • canny: 输出 Canny 算法中间过程产生的灰度边缘强度图。

edgedetect 的实际用途:

你可能会问,只是得到一个“线稿”有什么用?实际上,边缘检测是许多更复杂任务的基础或辅助步骤:

  1. 计算机视觉预处理: 在进行目标识别、特征提取、图像分割等高级计算机视觉任务前,边缘检测可以提取图像的关键结构信息,减少需要处理的数据量,并突出对象的轮廓。
  2. 艺术/风格化效果: 可以故意使用 wires 模式来创造素描、卡通、技术绘图等艺术风格。结合其他滤镜(如与原图叠加、调整颜色等)可以产生更多样的视觉效果。

命令行示例 (不同模式):

  • 示例 1: wires 模式 (默认 - 线稿效果)
    使用不同的阈值组合,观察边缘检测的敏感度变化。

    1
    2
    3
    4
    5
    # 较低阈值,检测更多边缘细节
    ffmpeg -i 1.png -vf edgedetect=low=0.1:high=0.2 edge_py_1_01_02.jpg

    # 较高阈值,只保留较强边缘
    ffmpeg -i 1.png -vf edgedetect=low=0.5:high=0.8 edge_py_2_05_08.jpg

    效果图:

    Image 1 Image 2
  • 示例 2: colormix 模式 (彩色边缘描边)
    将边缘信息(白色)与原图颜色混合。

    1
    ffmpeg -i 1.png -vf edgedetect=low=0.1:high=0.4:mode=colormix edge_py_2_colormix.jpg

    这个命令会生成一个保留了原图大概颜色,但在边缘处有明显白色(或受原色影响的亮色)描边的图像,视觉效果与 wires 完全不同。

edge_py_2_colormix


(9) EQ 效果 (eq)

调整视频的亮度 (Brightness)、对比度 (Contrast)、饱和度 (Saturation) 和 Gamma。

参数说明:

  • brightness: 亮度,范围 [-1.0, 1.0],默认 0。
  • contrast: 对比度,范围 [-2.0, 2.0],默认 1。
  • saturation: 饱和度,范围 [0.0, 3.0],默认 1。
  • gamma: Gamma 值 (整体亮度调整),范围 [0.1, 10.0],默认 1。
  • gamma_r, gamma_g, gamma_b: 分别调整红绿蓝通道的 Gamma。
  • gamma_weight: Gamma 应用的权重。

命令行示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 增加亮度 (增加 0.3)
ffmpeg -i 1.png -vf "eq=brightness=0.3" output_brightness_p0.3.jpg

# 降低亮度 (降低 0.2)
ffmpeg -i 1.png -vf "eq=brightness=-0.2" output_brightness_n0.2.jpg

# 增加对比度 (设置为 1.5)
ffmpeg -i 1.png -vf "eq=contrast=1.5" output_contrast_p1.5.jpg

# 降低对比度 (设置为 0.7)
ffmpeg -i 1.png -vf "eq=contrast=0.7" output_contrast_p0.7.jpg

# 增加饱和度 (设置为 1.8,颜色更鲜艳)
ffmpeg -i 1.png -vf "eq=saturation=1.8" output_saturation_p1.8.jpg

# 降低饱和度 (设置为 0.3,颜色变淡)
ffmpeg -i 1.png -vf "eq=saturation=0.3" output_saturation_p0.3.jpg

# 增加 Gamma (设置为 1.6,中间调变亮)
ffmpeg -i 1.png -vf "eq=gamma=1.6" output_gamma_p1.6.jpg

# 降低红色通道 Gamma (减少红色在高光和中间调的影响,画面偏青/蓝)
ffmpeg -i 1.png -vf "eq=gamma_r=0.5" output_gamma_r_p0.5.jpg

# 增加蓝色通道 Gamma (增加蓝色在高光和中间调的影响,画面偏蓝/冷)
ffmpeg -i 1.png -vf "eq=gamma_b=1.5" output_gamma_b_p1.5.jpg

效果图:

增加亮度 (brightness=0.3)

增加亮度

降低亮度 (brightness=-0.2)

降低亮度

增加对比度 (contrast=1.5)

增加对比度

降低对比度 (contrast=0.7)

降低对比度

增加饱和度 (saturation=1.8)

增加饱和度

降低饱和度 (saturation=0.3)

降低饱和度

增加Gamma (gamma=1.6)

增加Gamma

降低红色通道 (gamma_r=0.5)

降低红色通道Gamma

增加蓝色通道 (gamma_b=1.5)

增加蓝色通道Gamma

(10) 缩放 (scale)

调整视频或图像的分辨率(尺寸)。

参数说明: scale=width:height[:flags=F]

  • width, height: 目标宽度和高度。可以使用 iw, ih (输入宽高) 进行计算,-1-2 表示按比例自动计算(-2 保证结果为偶数)。
  • flags: 缩放算法(如 bilinear, bicubic, lanczos 等),影响速度和质量。

命令行示例:

1
2
3
4
5
## 缩放到固定 200x200 (可能变形)
ffmpeg -i 3.png -vf scale=200:200 scale_200x200.jpg

## 缩放到宽度 640,高度按比例自动计算
ffmpeg -i 3.png -vf scale=640:-2 scale_640w.jpg

效果图:

Image 1 Image 2

(11) 等比放大 (hqx)

使用 hqx 算法进行高质量的整数倍放大(2x, 3x, 4x)。

参数说明: hqx=n=N

  • n: 放大倍数,必须是 2, 3 或 4。

命令行示例:

1
2
## 放大 4 倍
ffmpeg -i 1.png -vf hqx=n=4 hqx4.jpg

image-20250502214022903


(12) 横向倒置 (hflip)

水平翻转图像或视频(镜像效果)。

参数说明: 无。

命令行示例:

1
ffmpeg -i 3.png -vf hflip hflip.jpg

效果图:

hflip


(13) 纵向倒置 (vflip)

垂直翻转图像或视频。

参数说明: 无。

命令行示例:

1
ffmpeg -i 3.png -vf vflip vflip.jpg

效果图:

vflip


(14) 加噪音 (noise)

为视频添加不同类型的噪音。

其中Y/U/V/S分别指的是☞ Y(亮度)、U(蓝色度差)、V(红色度差)、A(透明度)分量的噪音强度,一般会使用alls或allf,指全部色彩

参数说明: noise=alls=S[:allf=F] 或指定分量 ys, us, vs, asyf, uf, vf, af

  • alls (或 ys, us…): 噪音强度,范围 [0, 100],默认 0。
  • allf (或 yf, uf…): 噪音标志/类型,可以是:
    • t (temporal): 随时间变化的噪音。
    • u (uniform): 空间上均匀但不随时间变化的噪音。
    • p (pattern): 随时间重复的图案噪音。
    • a (averaged): 时间上平滑(平均)的图案噪音。
    • 可以组合,如 t+u

命令行示例:

1
2
3
4
5
6
7
8
## 添加强度为 100 的临时+统一噪音
ffmpeg -i 2.png -vf noise=alls=100:allf=t+u noise_tu.jpg

## 添加强度为 100 的平滑平均噪音
ffmpeg -i 2.png -vf noise=alls=100:allf=a noise_a.jpg

## 添加强度为 100 的随机图案噪音
ffmpeg -i 2.png -vf noise=alls=100:allf=p noise_p.jpg

效果图:

Image 1 Image 2 Image 3

(15) 加水印 (overlay)

作用:

overlay 滤镜是 FFmpeg 中非常常用的一个功能,它的作用是将一个视频流(或图片流,图片会被当作单帧视频)叠加到另一个视频流的上方,就像给视频添加“图层”一样。这常用于添加 logo、水印、画中画 (PiP) 等效果。

核心概念:

  • 至少需要两个输入: 一个是主视频流 (背景),另一个是要叠加的视频/图片流 (前景/水印)。
  • 复杂滤镜 (-filter_complex): 因为涉及多个输入流,overlay 通常在 -filter_complex 中使用。
  • 坐标定位: 你可以精确控制叠加层(水印)在背景视频上的位置。
  • 时间控制: 可以控制叠加层何时出现、何时消失。

主要参数 (overlay=...):

参数名 (命令行)描述常用值/表达式示例
x=<表达式>叠加层左上角的 水平 (X) 坐标。10 (左边距 10), main_w-overlay_w-10 (右边距 10), (main_w-overlay_w)/2 (水平居中)
y=<表达式>叠加层左上角的 垂直 (Y) 坐标。10 (上边距 10), main_h-overlay_h-10 (下边距 10), (main_h-overlay_h)/2 (垂直居中)
eof_action较短的那个输入流结束时,如何处理?repeat (默认, 对静态图片水印常用,会保持最后一帧), endall (结束整个处理), pass (移除叠加层,保留主视频流)
shortest(布尔值 01) 注意: 这个通常作为输出选项 (-shortest) 使用。-shortest (命令行)
format指定内部处理时使用的像素格式,影响是否支持透明度。rgb, rgba, yuv420, yuva420p

表达式中可用的变量:

  • main_w, W: 主视频流(背景)的宽度。
  • main_h, H: 主视频流(背景)的高度。
  • overlay_w, w: 叠加层(水印)的宽度。
  • overlay_h, h: 叠加层(水印)的高度。
  • t: 当前时间戳 (秒)。

命令行示例:

示例 1: 添加静态 PNG 图片水印 (带透明度) 到右下角

假设 input.mp4 是主视频,logo.png 是带透明背景的水印图片。

1
2
3
4
5
6
7
8
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[0:v][1:v]overlay=x=main_w-overlay_w-10:y=main_h-overlay_h-10[outv]" \
-map "[outv]" -map 0:a? \
-c:v libx264 -preset fast -c:a copy \
output_watermarked.mp4

# 对于Windows来说,无法使用 \换行符执行指令,所以只能混在一行,在这里我们还限制了一下图片的大小,避免图片太大
ffmpeg -i input.mp4 -i logo.png -filter_complex "[1:v]scale=w=-2:h=60[scaled_logo]; [0:v][scaled_logo]overlay=x=main_w-overlay_w-10:y=main_h-overlay_h-10[outv]" -map "[outv]" -map 0:a? -c:v libx264 -preset fast -c:a copy output_watermarked_scaled.mp4
  • -i input.mp4 -i logo.png: 指定两个输入文件。
  • -filter_complex "[0:v][1:v]overlay=x=main_w-overlay_w-10:y=main_h-overlay_h-10[outv]":
    • [0:v] 代表第一个输入(视频)的视频流。
    • [1:v] 代表第二个输入(logo)的视频流(图片被当作单帧视频)。
    • overlay=...: 应用 overlay 滤镜。
    • x=main_w-overlay_w-10: 设置 x 坐标为 主视频宽度 - logo宽度 - 10像素(右边距10)。
    • y=main_h-overlay_h-10: 设置 y 坐标为 主视频高度 - logo高度 - 10像素(下边距10)。
    • [outv]: 将处理后的视频流标记为 outv
  • -map "[outv]": 将标记为 outv 的视频流映射到输出。
  • -map 0:a?: 将第一个输入文件中的音频流(如果存在 ?)映射到输出。
  • -c:v libx264 -preset fast: 对视频进行 H.264 重新编码(烧录水印需要重编码),使用较快的预设。
  • -c:a copy: 直接复制原始音频流。
  • output_watermarked.mp4: 输出文件名。

示例 2: 添加动态 GIF 水印 (循环播放) 到左上角

假设 input.mp4 是主视频,animated_logo.gif 是动态水印。

1
2
3
4
5
6
7
8
9
# 注意:GIF 作为输入时,需要特殊的输入选项来控制循环
ffmpeg -i input.mp4 -ignore_loop 0 -stream_loop -1 -i animated_logo.gif \
-filter_complex "[0:v][1:v]overlay=x=10:y=10:eof_action=repeat[outv]" \
-map "[outv]" -map 0:a? \
-c:v libx264 -preset fast -c:a copy \
output_animated_watermark.mp4

# 对于Windows来说,无法使用 \换行符执行指令,所以只能混在一行
ffmpeg -i input.mp4 -ignore_loop 0 -stream_loop -1 -i animated_logo.gif -filter_complex "[0:v][1:v]overlay=x=10:y=10:eof_action=repeat[outv]" -map "[outv]" -map 0:a? -c:v libx264 -preset fast -c:a copy -shortest output_animated_watermark.mp4
  • -ignore_loop 0: 输入选项 (针对 GIF),告诉 FFmpeg 不要忽略 GIF 文件内部定义的循环次数(如果 GIF 本身只循环几次,这个选项会让它按原样播放)。如果 GIF 本身是无限循环的,此选项影响不大。
  • -stream_loop -1: 输入选项 (针对 GIF),告诉 FFmpeg 在 GIF 播放完一次后,无限循环地重新读取 GIF 输入流。这确保了即使 GIF 本身时长很短,它也会在整个视频时长内持续显示。如果想让 GIF 只播放一次然后消失,可以将 eof_action 设为 pass 并且不使用 -stream_loop -1 (或者结合 -shortest 输出选项)。
  • -filter_complex "[0:v][1:v]overlay=x=10:y=10:eof_action=repeat[outv]":
    • x=10:y=10: 将 GIF 放在左上角(边距 10)。
    • eof_action=repeat: 当 GIF 流结束时(如果 -stream_loop 不是 -1),保持其最后一帧。由于我们用了 -stream_loop -1,这个参数在这里影响不大。
  • 其他 -map, -c:v, -c:a 参数同上。
  • -shortest:这个选项告诉 FFmpeg:“当所有输入流中,最短的那个流结束时,就结束整个输出文件。” 在你的例子中,input.mp4 是有限时长的(比如 1 分多钟),而无限循环的 GIF 被视为无限时长,所以 -shortest 会让输出在 input.mp4 播放完毕时自动停止。

示例三:添加固定尺寸 (64x64) 且位置随机飘动的动态水印

1
2
ffmpeg -i input.mp4 -ignore_loop 0 -stream_loop -1 -i animated_logo.gif -filter_complex "[1:v]scale=h=64:w=-2[scaled_logo];[0:v][scaled_logo]overlay=x='if(eq(mod(trunc(t*50/(main_w-overlay_w))\,2)\,0)\,mod(t*50\,main_w-overlay_w)\,main_w-overlay_w-mod(t*50\,main_w-overlay_w))':y='if(eq(mod(trunc(t*50/(main_h-overlay_h))\,2)\,0)\,mod(t*50\,main_h-overlay_h)\,main_h-overlay_h-mod(t*50\,main_h-overlay_h))'[outv]" -map "[outv]" -map 0:a? -c:v libx264 -preset fast -c:a copy -
shortest output_bouncing_escaped.mp4

(16) 加底板/画布扩展 (pad)

扩展视频或图像的画布尺寸,并可以用指定颜色填充新增区域。常用于需要固定输出尺寸或添加边框背景的场景。

参数说明: pad=width=W:height=H[:x=X][:y=Y][:color=C]

  • width, height: 输出画布的总宽度总高度。必须大于等于输入尺寸。
  • x, y: 输入图像在输出画布上的左上角坐标。默认居中 (ow-iw)/2:(oh-ih)/2
  • color: 填充扩展区域的颜色,默认黑色。

命令行示例:

1
2
## 将 2.jpg 放到一个 800x800 的紫色底板上,原图放在 (40, 40) 的位置
ffmpeg -i 3.png -vf pad=width=800:height=800:x=40:y=40:color=violet pad_violet.jpg

效果图:

pad_violet


(17) 旋转 (rotate)

按指定角度旋转视频(围绕中心点)。

参数说明: rotate=angle=A[:out_w=OW][:out_h=OH][:bilinear=B]

  • anglea: 旋转角度。注意:单位是弧度 (Radians)。可以使用 PI 常量和数学表达式。顺时针为负,逆时针为正。例如 PI/6 (30度), -PI/2 (-90度)。要使用角度需转换:angle=45*PI/180
  • out_w, out_h: (可选) 输出的宽度和高度,默认与输入相同。旋转后图像可能会超出原边界,可以用 ow='hypot(iw,ih)' 来设置足够大的画布。
  • bilinear: 是否使用双线性插值 (0 或 1),默认 1 (使用)。

命令行示例:

1
2
3
4
5
## 逆时针旋转 PI/6 弧度 (30度)
ffmpeg -i 1.png -vf rotate=angle=PI/6 rotate_30deg.jpg

## 顺时针旋转 90 度
ffmpeg -i 1.png -vf rotate=angle=-PI/2 rotate_neg90deg.jpg

效果图:

Image 1 Image 2

(18) 光晕/晕影 (vignette)

在图像或视频边缘添加暗角(或亮角)效果。

参数说明: vignette=angle=A[:x0=X][:y0=Y][:mode=M][:eval=E]

  • anglea: 控制光晕形状的角度表达式(单位:弧度)。常用 PI/4 (圆形) 或 PI/5
  • x0, y0: 光晕中心的相对坐标(0 到 1),默认 0.5 (中心)。
  • mode: 模式 (backward, forward)。
  • eval: 评估模式 (init, frame)。

命令行示例:

1
2
## 添加一个圆形暗角
ffmpeg -i 1.png -vf vignette=angle=PI/4 vignette_circle.jpg

效果图:

vignette_circle


(19) 淡入淡出 (fade, afade)

作用:

为视频 (fade) 或音频 (afade) 添加淡入 (Fade In) 或淡出 (Fade Out) 效果。这在视频开头、结尾或者场景切换时非常常用。

主要参数:

  • type或 t: 指定淡入淡出类型。

    • in: 淡入效果。
    • out: 淡出效果。
  • start_timest: 效果开始的时间点(单位:秒)。对于淡入,通常是 0;对于淡出,需要计算好(视频总时长 - 淡出时长)。

  • durationd: 效果持续的时长(单位:秒)。

  • colorc (仅 fade): 指定淡入/淡出的目标颜色,默认是黑色。例如 color=white 可以实现淡入自白色或淡出至白色。

  • alpha: 值为 1 时,只对 alpha 透明通道应用淡入淡出,可用于实现透明度的渐变。默认 0

命令行示例:

1
2
3
4
5
6
7
8
9
10
11
12
# 示例 1: 视频前 2 秒从黑色淡入,最后 3 秒淡出到黑色 (假设视频总长 35 秒)
ffmpeg -i input.mp4 -vf "fade=type=in:start_time=0:duration=2, fade=type=out:start_time=32:duration=3" -c:a copy output_video_fade.mp4
# -vf "...": 应用视频滤镜链。
# fade=type=in:start_time=0:duration=2: 第一个 fade 滤镜,类型为 in (淡入),从第 0 秒开始,持续 2 秒。
# , : 滤镜链分隔符。
# fade=type=out:start_time=32:duration=3: 第二个 fade 滤镜,类型为 out (淡出),从第 32 秒开始 (35-3=32),持续 3 秒。
# -c:a copy: 音频直接复制。

# 示例 2: 音频在最后 5 秒淡出 (假设音频总长 60 秒)
ffmpeg -i input.mp3 -af "afade=type=out:start_time=55:duration=5" output_audio_fade.mp3
# -af "...": 应用音频滤镜。
# afade=type=out:start_time=55:duration=5: 音频淡出,从 55 秒开始,持续 5 秒。

(20) 旋转 90/180/270 度 (transpose)

作用:

对视频进行精确的 90 度倍数旋转或翻转。相比于 rotate 滤镜(它允许任意角度旋转但可能涉及复杂的插值和画布调整),transpose 对于标准的 90 度旋转更简单、更常用,且通常效率更高。

主要参数:

1
transpose=<方向值>
  • 0: 逆时针旋转 90 度并垂直翻转。
  • 1clock: 顺时针旋转 90 度。 (常用)
  • 2cclock: 逆时针旋转 90 度。 (常用)
  • 3clock_flip: 顺时针旋转 90 度并垂直翻转。
  • (还有 cclock_flip 等)

命令行示例:

1
2
3
4
5
6
7
# 将视频顺时针旋转 90 度 (例如,竖屏拍的视频转成横屏)
ffmpeg -i portrait_video.mp4 -vf "transpose=1" -c:a copy output_rotated_cw.mp4
# -vf "transpose=1": 应用 transpose 滤镜,参数 1 表示顺时针旋转 90 度。

# 将视频逆时针旋转 90 度
ffmpeg -i portrait_video.mp4 -vf "transpose=2" -c:a copy output_rotated_ccw.mp4
# -vf "transpose=2": 参数 2 表示逆时针旋转 90 度。

(21) 改变播放速度 (setpts, atempo)

作用:

改变视频 (setpts) 或音频 (atempo) 的播放速度,实现快放或慢放效果。

  • setpts=<表达式> (视频): 通过修改视频帧的显示时间戳 (Presentation Timestamp) 来改变速度。
  • atempo=<倍数> (音频): 调整音频的播放速度,同时尽量保持音高不变(这是它与简单地改变采样率或 asetpts 的主要区别)。

主要参数/表达式:

  • setpts 表达式:
    • PTS/N: 将播放速度提高 N 倍 (例如 PTS/2 是 2 倍速)。
    • PTS*N: 将播放速度减慢 N 倍 (例如 PTS*2 是 0.5 倍速)。
    • PTS-STARTPTS: 让时间戳从 0 开始(有时用于修复时间码问题)。
  • atempo 倍数:
    • 1.0 是原速。
    • 2.0 是 2 倍速。
    • 0.5 是 0.5 倍速 (慢放)。
    • 注意: atempo 滤镜接受的倍数范围有限制 (通常在 0.5 到 100.0 之间)。对于超出范围的速度调整,可以链式调用多个 atempo 滤镜 (例如 atempo=2.0,atempo=2.0 实现 4 倍速)。

重要: 改变视频速度时,通常需要同时改变音频速度以保持音画同步。因此 setptsatempo 常常在 -filter_complex 中配合使用。

命令行示例:

1
2
3
4
5
6
7
8
9
10
11
# 示例 1: 将视频和音频都加速到 1.5 倍速
ffmpeg -i input.mp4 -filter_complex "[0:v]setpts=PTS/1.5[v];[0:a]atempo=1.5[a]" -map "[v]" -map "[a]" output_fast_1.5x.mp4
# -filter_complex "...": 定义复杂滤镜图。
# [0:v]setpts=PTS/1.5[v]: 将输入视频流 [0:v] 的 PTS 除以 1.5 (加速),标记输出为 [v]。
# [0:a]atempo=1.5[a]: 将输入音频流 [0:a] 的速度提高 1.5 倍,标记输出为 [a]。
# -map "[v]" -map "[a]": 将处理后的音视频流映射到输出。

# 示例 2: 将视频和音频都减慢到 0.75 倍速
ffmpeg -i input.mp4 -filter_complex "[0:v]setpts=PTS*(1/0.75)[v];[0:a]atempo=0.75[a]" -map "[v]" -map "[a]" output_slow_0.75x.mp4
# setpts=PTS*(1/0.75): 将 PTS 乘以约 1.333,实现 0.75 倍慢放。
# atempo=0.75: 音频速度变为 0.75 倍。

(22) 绘制文本 (drawtext) 详解

作用:

在视频帧上绘制静态或动态文本,功能非常强大,可以控制字体、大小、颜色、位置、背景框、滚动、时间显示等。

主要参数 (非常多,仅列举常用):

  • fontfile=<字体文件路径>: 必需 (除非系统配置了默认字体且 FFmpeg 能找到),指定 TrueType (.ttf) 或 OpenType (.otf) 字体文件的路径。路径中若有特殊字符或空格,需要恰当引用或转义。
  • text=<文本内容>: 要绘制的文本字符串。可以包含变量(如时间码、帧号、元数据)和表达式。例如 text='Frame %{frame_num}'text='%{pts\:hms}' (显示 HH:MM:SS.ms 格式时间码)。特殊字符需要转义。
  • x=<表达式>: 文本左上角的水平 (X) 坐标。可以使用 w, h (视频宽高), tw, th (文本宽高), line_h (行高) 等变量。例如 (w-tw)/2 (水平居中)。
  • y=<表达式>: 文本左上角的垂直 (Y) 坐标。例如 h-th-10 (下边距 10)。
  • fontsize=<大小>: 字体大小(像素)。
  • fontcolor=<颜色>: 字体颜色 (颜色名如 white, yellow, 或十六进制 0xFF0000, 或表达式)。
  • box=<1或0>: 是否绘制背景框,1 表示绘制,0 (默认) 不绘制。
  • boxcolor=<颜色>[@透明度]: 背景框颜色和可选透明度 (如 black@0.5)。
  • boxborderw=<宽度>: 背景框边框的宽度。
  • shadowx, shadowy: 文字阴影的 X, Y 偏移。
  • shadowcolor: 阴影颜色。
  • enable='<表达式>': 条件渲染!只有当表达式求值为非零时,才绘制文本。例如 enable='between(t,5,10)' 表示只在视频时间 5 到 10 秒之间显示文本。

命令行示例 (来自你的笔记):

1
2
3
4
5
6
7
8
# 示例 1: 左右滚动的字幕 (需要根据你的字体路径修改 fontfile)
# 这个 x 表达式实现了从右向左滚动的效果,50 控制速度
ffmpeg -i input.mp4 -vf "drawtext=fontfile=/path/to/your/font.ttf:fontcolor=0xaaff00:fontsize=18:shadowy=0:x='if(gte(t,2), (main_w-mod(t*50,main_w+text_w)), NAN)':y=(main_h-line_h-10):text='关注广州小程,提升专业技能。'" -b:v 500k -c:a copy output_scrolling.mp4
# 注:x 表达式中的 NAN 表示在 t<2 时不绘制 (移出画面)

# 示例 2: 固定位置的两行字幕 (需要根据你的字体路径修改 fontfile)
ffmpeg -i input.mp4 -vf "drawtext=fontfile=/path/to/your/HeiTi.ttf:fontcolor=yellow:fontsize=20:shadowy=0:x=(w-tw)/2:y=main_h-line_h-50:text='关注广州小程', drawtext=fontfile=/path/to/your/YaHei.ttf:fontcolor=0xaaff00:fontsize=18:shadowy=0:x=(w-tw)/2:y=main_h-line_h-20:text='提升专业技能。'" -b:v 500k -c:a copy output_static_lines.mp4
# 注:用逗号分隔两个 drawtext 滤镜实例来实现多行/多样式文本

(23) 视频堆叠 (hstack, vstack, xstack)

作用:

将多个视频输入水平 (hstack)、垂直 (vstack) 或按自定义网格 (xstack) 布局拼接在一起,形成一个更大的视频画面。这在制作对比视频、画中画(另一种方式)或多画面监控展示时非常有用。这需要使用 -filter_complex。

主要参数:

  • hstack, vstack:

    • inputs=<N>: 指定要堆叠的视频输入流的数量。必需
    • shortest=<0|1>: 设为 1 时,输出时长以最短的那个输入流为准。默认 0
  • xstack:

    (更复杂,用于网格布局)

    • inputs=<N>: 输入流数量。
    • layout=<布局字符串>: 定义网格布局,例如 0_0|w0_0|0_h0|w0_h0 表示 2x2 网格。
    • shortest=<0|1>: 同上。
    • fill=<颜色>: 当布局尺寸大于输入流总尺寸时,用指定颜色填充空白区域。

注意:

  • 默认情况下,hstack/vstack 要求所有输入视频具有相同的高度(对于 hstack)或相同的宽度(对于 vstack)。如果尺寸不同,你需要先用 scale 滤镜统一尺寸。
  • xstack 则会自动将所有输入缩放到网格单元允许的最大尺寸。

命令行示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 示例 1: 水平并排拼接两个视频 (左右排列)
# 假设 left.mp4 和 right.mp4 分辨率相同
ffmpeg -i left.mp4 -i right.mp4 \
-filter_complex "[0:v][1:v]hstack=inputs=2[outv]" \
-map "[outv]" -map 0:a? -c:a copy \
output_hstack.mp4
# [0:v][1:v]: 选择两个输入的视频流。
# hstack=inputs=2: 应用水平堆叠滤镜,指明有两个输入。
# [outv]: 标记输出视频流。
# -map "[outv]" -map 0:a?: 映射处理后的视频和第一个输入的音频。

# 示例 2: 垂直上下拼接两个视频 (需要先统一宽度)
ffmpeg -i top.mp4 -i bottom.mp4 \
-filter_complex \
"[0:v]scale=w=640:h=-2[top_scaled]; \
[1:v]scale=w=640:h=-2[bottom_scaled]; \
[top_scaled][bottom_scaled]vstack=inputs=2[outv]" \
-map "[outv]" -map 0:a? -c:a copy \
output_vstack.mp4
# 先用 scale 将两个视频宽度统一为 640 (高度自适应偶数)。
# 再用 vstack 将缩放后的流垂直堆叠。

# 示例 3: 将四个视频排列成 2x2 网格
ffmpeg -i input1.mp4 -i input2.mp4 -i input3.mp4 -i input4.mp4 \
-filter_complex \
"xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[outv]" \
-map "[outv]" -map 0:a? -c:a copy \
output_xstack_2x2.mp4
# xstack=inputs=4: 指定 4 个输入。
# layout=0_0|w0_0|0_h0|w0_h0: 定义 2x2 布局。
# 0_0: 第一个视频放在左上角 (0,0)。
# w0_0: 第二个视频放在第一个视频右边 (x=第一个视频宽度, y=0)。
# 0_h0: 第三个视频放在第一个视频下方 (x=0, y=第一个视频高度)。
# w0_h0: 第四个视频放在右下角。

(24) 锐化 (unsharp, smartblur)

作用:

提高图像或视频的清晰度,突出细节。有多种锐化滤镜,unsharp (反锐化掩模) 是其中常用的一种,效果比较自然。smartblur 则可以在模糊的同时保留边缘(也可用于轻微锐化)。

主要参数 (unsharp):

  • luma_msize_xlx: Luma (亮度) 矩阵的水平大小 (奇数, 3-63, 默认 5)。
  • luma_msize_yly: Luma 矩阵的垂直大小 (奇数, 3-63, 默认 5)。
  • luma_amountla: Luma 锐化/模糊的强度。正值锐化,负值模糊。范围 [-1.5, 1.5],默认 1.0 (轻微锐化)。值越大锐化越强。
  • chroma_msize_x/cx, chroma_msize_y/cy, chroma_amount/ca: 对应 Chroma (色度) 通道的参数,用法同 Luma。通常调整 Luma 即可。

命令行示例:

1
2
3
4
5
6
7
8
9
# 示例 1: 应用中等强度的锐化 (使用 5x5 矩阵,亮度强度 0.8)
ffmpeg -i input.mp4 -vf "unsharp=lx=5:ly=5:la=0.8" -c:a copy output_unsharp_medium.mp4
# lx=5:ly=5: 使用 5x5 的分析矩阵。
# la=0.8: 设置亮度锐化强度为 0.8 (比默认 1.0 稍弱)。

# 示例 2: 应用更强的锐化 (使用 7x7 矩阵,亮度强度 1.2)
ffmpeg -i input.mp4 -vf "unsharp=lx=7:ly=7:la=1.2" -c:a copy output_unsharp_strong.mp4
# lx=7:ly=7: 使用更大的分析矩阵,可能影响更多细节。
# la=1.2: 设置更高的锐化强度。
  • 注意: 过度锐化会导致图像出现噪点、光晕(halos)和不自然的边缘,需要适度调整参数。

17.2.3 流标识符 (Stream Specifiers) 详解

为了精确地对多媒体文件中的特定流进行操作,需要使用流标识符。

标识符含义示例用法ffmpeg-python 对应 (概念)
v文件中所有的视频流-vn (禁用视频), -map vin_node['v'] (通常指第一个)
a文件中所有的音频流-an (禁用音频), -map ain_node['a'] (通常指第一个)
s文件中所有的字幕流-sn (禁用字幕), -map s?in_node['s'] (通常指第一个)
N (数字)文件中绝对索引为 N 的流 (从 0 开始)-map 0:1 (映射文件 0 的流 1)in_node[1]
v:N文件中第 N+1 个视频流 (索引从 0 开始)-c:v:0 libx264 (设置第 1 个视频流编码)in_node['v:0']in_node.video
a:N文件中第 N+1 个音频流 (索引从 0 开始)-b:a:1 192k (设置第 2 个音频流码率)in_node['a:1']
s:N文件中第 N+1 个字幕流 (索引从 0 开始)-map 0:s:0 (映射文件 0 的第 1 个字幕)in_node['s:0']
i:STREAM_ID(较新版本) 根据流的唯一 ID 选择-map i:137(需手动过滤)
p:PROGRAM_ID[:STREAM_SPEC](较新版本) 根据节目 ID 选择 (用于 TS 流等)-map p:101-map p:101:v(需手动过滤)
m:key[:value]根据流的元数据选择-map m:language:eng(需手动过滤)

ffmpeg-python 中,我们主要通过字典访问方式(如 in_node['a:1'])来选择流。

想象一下,一个多媒体文件(比如一个 .mkv.mp4 文件)就像一个大包裹。这个包裹里可能装了很多东西,这些东西就是流 (Stream)

  • 可能有一段视频画面 (Video Stream)
  • 可能不止一段音频:比如一条是电影原声普通话 (Audio Stream 0),一条是粤语配音 (Audio Stream 1),还有一条是导演评论音轨 (Audio Stream 2)。
  • 可能还有几条字幕:一条是中文字幕 (Subtitle Stream 0),一条是英文字幕 (Subtitle Stream 1)。

FFmpeg 的默认行为 (没有流标识符时):

如果你只给 FFmpeg 一个简单的命令,比如 ffmpeg -i input.mkv output.mp4,它会尝试自己做决定,从 input.mkv 里选一些流放到 output.mp4 里。它通常会:

  • 一个它认为“最好”的视频流(比如分辨率最高的那个)。
  • 一个它认为“最好”的音频流(比如声道数最多的那个)。
  • 可能还会选一个字幕流(比如第一个)。

问题来了:

  • 如果 input.mkv 里有国语和粤语两条音轨,但 FFmpeg 默认选了粤语,而你其实想要国语的,怎么办?
  • 如果你想把国语和粤语两条音轨都保留在输出文件 output.mp4 里,怎么办?
  • 如果你只想提取视频,完全不要任何音频和字幕,怎么办?
  • 如果你有两个输入文件,想用第一个文件的视频配上第二个文件的音频,怎么办?

流标识符的作用:精确的“指名道姓”

这时候,流标识符就派上用场了!它就像给包裹里的每一样东西(每个流)都编上号或者打上标签,让你能够精确地告诉 FFmpeg 你要对哪个具体的东西进行操作

它最常用的地方是配合 -map 输出选项

  • -map 的作用: 这个选项用来手动指定哪些流应该被包含在输出文件中。一旦你使用了 -map,FFmpeg 就不再使用默认规则去猜了,只有你明确 -map 指定的流才会被输出

  • 配合流标识符:

    • ffmpeg -i input.mkv -map 0:v:0 -map 0:a:1 -c copy output.mp4
      
      1
      2
      3
      4
      5
      6
      7
      8

      - `-map 0:v:0`: 告诉 FFmpeg,选择第一个输入文件 (`0`) 的第一个视频流 (`v:0`)。
      - `-map 0:a:1`: 告诉 FFmpeg,选择第一个输入文件 (`0`) 的第二个音频流 (`a:1`)(假设这是粤语音轨)。
      - `-c copy`: 直接复制选中的流。
      - 结果 `output.mp4` 就只包含指定的那个视频流和粤语音轨。

      - ```bash
      ffmpeg -i input.mkv -map 0:v -map 0:a -c copy output_va.mkv
      - `-map 0:v`: 选择第一个输入的所有视频流。 - `-map 0:a`: 选择第一个输入的所有音频流。 - 结果 `output_va.mkv` 会包含原文件所有的视频和音频流。
    • ffmpeg -i video.mp4 -i audio.mp3 -map 0:v -map 1:a -c copy output_merged.mp4
      
      1
      2
      3
      4
      5
      6
      7

      - `-map 0:v`: 选择第一个输入 (`video.mp4`) 的视频流。
      - `-map 1:a`: 选择第二个输入 (`audio.mp3`) 的音频流。
      - 结果 `output_merged.mp4` 就是用第一个视频配上第二个音频。

      - ```bash
      ffmpeg -i input.mkv -map 0 -map -0:s -c copy output_no_subs.mkv
      - `-map 0`: 先选择第一个输入的所有流。 - `-map -0:s`: 然后**取消选择**第一个输入的所有字幕流 (`-` 号表示取消)。 - 结果 `output_no_subs.mkv` 包含原视频和音频,但不含字幕。

流标识符的其他用途:

除了配合 -map,流标识符还可以用来给特定的流指定选项

  • -c:a:0 aac -b:a:0 128k: 对第一个音频流 (a:0) 使用 AAC 编码,比特率为 128k。
  • -c:a:1 libopus: 对第二个音频流 (a:1) 使用 Opus 编码。
  • -metadata:s:a:0 language=eng: 为第一个音频流 (a:0) 设置语言元数据为英语。
  • -filter_complex 中,像 [0:v] 这样的标签实际上也是基于流标识符来引用特定的输入流。

ffmpeg-python 中的对应:

使用ffmpeg-python 库时,选择流的方式更 Pythonic:

  • input_node['v']input_node.video 通常选择第一个视频流。
  • input_node['a:1'] 选择第二个音频流。
  • input_node[0] 选择第一个流(不区分类型)。 库在底层会将这些选择转换成合适的 FFmpeg 命令行 -map 参数或滤镜图标签。

17.2.4 FFmpeg 自身能力查询命令

为何要查询 FFmpeg 的能力?

在你编写任何调用 FFmpeg 的脚本或程序之前,了解你所使用的 FFmpeg 版本究竟能做什么至关重要。FFmpeg 功能极其强大,但并非所有安装版本都包含全部功能。例如,某些编解码器(特别是需要额外授权或有专利问题的)可能在默认编译中未被包含;或者你可能需要确认是否支持特定的网络协议或滤镜。

因此,执行信息查询命令的主要作用和价值在于:

  1. 避免运行时错误: 提前确认 FFmpeg 支持你将要使用的编码器、格式、滤镜或协议,可以避免在脚本执行到一半时才发现功能缺失而导致失败。例如,你想编码为 libfdk_aac,但查询 ffmpeg -encoders 后发现它不在列表中,你就需要换用 FFmpeg 内置的 aac 编码器。
  2. 确保兼容性与选择最佳方案: 查看支持列表可以帮助你选择最适合你需求的、且当前环境支持的最佳工具。比如,检查是否有硬件加速编码器(如 h264_nvenc)可用,或者对比几种可选的音频编码器。
  3. 辅助调试: 特别是当你自行编译 FFmpeg 或遇到奇怪问题时,检查编译时链接的库和启用的功能(可以通过 -version 或查询相关组件)可以帮助诊断问题根源。
  4. 发现新功能: 浏览支持列表也是学习 FFmpeg 强大功能、寻找特定处理方案的途径。

常用查询命令详解:

查看编解码器 (-encoders, -decoders)

命令 ffmpeg -encoders 会列出所有当前 FFmpeg 版本编译支持的编码器。编码器用于将原始音视频数据压缩。输出会标明编码器名称(如 libx264)、类型(V=视频, A=音频, S=字幕)和简短描述。类似地,ffmpeg -decoders 列出所有支持的解码器,用于解压缩。了解这些列表可以让你确定:

  • 输出兼容性: 你能否使用 -c:v libx265-c:a libopus 来编码你的输出文件?
  • 输入兼容性: 当你拿到一个不常见的视频文件时,FFmpeg 是否内置了对应的解码器来读取它?
  • 硬件加速: 列表中是否包含如 h264_nvenc, hevc_qsv 等硬件加速编解码器?
查看协议 (-protocols)

命令 ffmpeg -protocols 显示 FFmpeg 支持的输入和输出协议。这决定了 FFmpeg 能从哪些来源读取数据以及能将数据发送到哪些目的地。常见的协议包括 file (本地文件), http, https, ftp, tcp, udp, 以及流媒体常用的 rtmp, rtsp, hls, pipe (管道) 等。在你需要处理网络流(如拉取 RTSP 流进行处理,或将处理结果推送到 RTMP 服务器)或与其他进程通过管道交互时,确认协议支持是前提。

查看格式 (-formats)

命令 ffmpeg -formats 列出 FFmpeg 支持的所有容器格式 (Muxers/Demuxers)。容器格式定义了音视频流如何打包存储。输出中 D 列表示支持解复用(Demuxing,读取这种格式的文件),E 列表示支持复用(Muxing,写入这种格式的文件)。常见的格式如 mp4, mov, mkv, webm, avi, flv, mpegts, mp3, aac, wav, ogg, image2 (用于图像序列) 等。在你需要进行格式转换,或者使用 -f 参数强制指定格式时,需要确保目标格式是被支持的(有 E 标记)。

查看和学习滤镜 (-filters, -h filter=<滤镜名>)

命令 ffmpeg -filters 会打印出当前版本支持的所有滤镜的列表,这个列表通常非常长!它对于快速浏览有哪些可用的处理功能很有帮助。但更实用的是 ffmpeg -h filter=<滤镜名> 命令。例如,你想了解如何精确地缩放视频,可以运行 ffmpeg -h filter=scale。这个命令会输出 scale 滤镜的详细文档,包括它接受的所有参数(如 width, height, flags, interl 等)、每个参数的含义、可接受的值范围、默认值,甚至可能有使用示例。在你使用任何不熟悉的滤镜之前,通过 -h filter= 来查阅其用法是避免参数错误的关键步骤。

获取完整帮助 (-h full)

命令 ffmpeg -h full 会输出 FFmpeg 所有可用的命令行选项的极其详尽的帮助信息。内容非常多,适合在需要查找非常规或底层参数时作为最终参考。


17.2.5 ffprobe 获取文件信息详解 (深度解读)

为何要探测文件信息?

ffprobe 是 FFmpeg 套件中的媒体文件侦探。在你对音视频文件进行任何修改或处理之前,使用 ffprobe 深入了解它的“底细”至关重要,其作用和价值体现在:

  1. 制定处理策略:

    获取文件的精确属性(如分辨率、帧率、时长、编码格式、比特率、音频声道数、采样率、像素格式等)是决定后续操作的基础。例如:

    • 只有当视频分辨率大于 1920x1080 时,才执行缩小操作。
    • 只有当音频编码不是 AAC 时,才进行音频转码。
    • 根据获取的视频时长来计算截取片段的起止时间或判断是否需要处理。
    • 根据帧率来设置输出帧率或进行帧率转换。
    • 检查像素格式以确保后续滤镜或编码器兼容。
    • 检测是否有音频流来决定是否需要处理或复制音频。
    • 读取旋转 (Rotation) 元数据,以便在必要时进行反向旋转校正。
  2. 动态参数计算: 在脚本中,你可以用 ffprobe 获取的值来动态计算 FFmpeg 命令的参数。例如,使用探测到的宽度 iw 和高度 ih 来计算缩放或裁剪的目标尺寸,或者使用探测到的时长 duration 来设置滤镜效果的时间参数。

  3. 错误预检查与调试: 可以用来检查输入文件是否有效、是否损坏、是否包含预期的流类型。如果一个文件处理失败,ffprobe 的输出可以帮助判断问题是出在文件本身还是处理命令上。

  4. 内容分析与质量评估: 查看文件的编码器、Profile/Level、比特率等信息,可以大致评估源文件的质量,并据此选择合适的处理参数。

推荐的 ffprobe 命令及其详解:

对于脚本化处理,获取 JSON 格式的输出是最方便的:

Bash

1
ffprobe -v quiet -print_format json -show_format -show_streams your_video.mp4
  • -v quiet: 设置日志级别为 quiet,这样 ffprobe 在执行时不会打印版本信息、库信息等额外的日志,只输出纯净的 JSON 数据(或错误信息),便于程序解析。
  • -print_format json: 明确指定输出格式为 JSON。JSON 是一种结构化的数据格式,Python 的 json 模块可以轻松地将其解析为字典和列表。
  • -show_format: 指示 ffprobe 在输出中包含关于容器格式 (Format) 的信息。这部分信息描述了整个文件的属性。
  • -show_streams: 指示 ffprobe 在输出中包含文件中每一个数据流 (Stream) 的详细信息(包括视频、音频、字幕等)。

深入理解 JSON 输出结构:

ffprobe 输出的 JSON 是一个 Python 字典,其中最重要的两个键是 "format""streams"

"format" 对象 (容器/文件级别信息)

这个字典包含了文件的整体信息:

  • filename: 文件的完整路径。
  • nb_streams: 文件中包含的流的总数。
  • format_name: 容器格式的名称(通常是逗号分隔的列表,如 ‘mov,mp4,m4a,3gp,3g2,mj2’)。
  • format_long_name: 容器格式的更详细名称。
  • start_time: 文件播放的起始时间戳(通常是 “0.000000”)。
  • duration: 文件的总时长(单位:秒,字符串形式)。极其常用!
  • size: 文件总大小(单位:字节,字符串形式)。
  • bit_rate: 文件的平均总比特率(单位:bps,字符串形式)。
  • probe_score: FFmpeg 对探测到的格式的置信度(通常不用太关心)。
  • tags: 一个字典,包含了容器级别的元数据标签,例如 major_brand, minor_version, compatible_brands, title, artist, album, encoder (编码这个文件的软件信息), comment 等。
"streams" 列表 (流级别信息)

这是一个列表,列表中的每个元素都是一个字典,详细描述了文件中的一个流。你需要遍历这个列表,并通过 codec_type 来判断是视频流、音频流还是其他类型的流。

  • 通用关键字段:
    • index: 流的索引号(从 0 开始)。
    • codec_type: 流类型,值为 ‘video’, ‘audio’, ‘subtitle’, ‘data’ 等。用于区分不同流
    • codec_name: 编解码器的短名称(如 ‘h264’, ‘aac’, ‘vp9’)。
    • codec_long_name: 编解码器的完整名称。
    • profile: 编码时使用的配置文件(如 H.264 的 ‘High’, ‘Main’)。
    • time_base: 时间戳的基本单位(分数形式,如 “1/16000”,表示时间戳的单位是 1/16000 秒)。
    • start_time: 此流的起始时间(秒)。
    • duration: 此流的持续时间(秒,可能与 format 中的总时长略有不同)。
    • bit_rate: 此流的平均比特率(bps)。
    • nb_frames: 此流的总帧数(对某些编码可能是估计值)。
    • disposition: 一个字典,包含布尔标志,指示流的用途,如 default (是否为默认流), dub (是否为配音), original, comment, forced (强制字幕)等。
    • tags: 此流特有的元数据,如 language (‘eng’, ‘chi’), handler_name, rotate (视频旋转角度)等。
  • 视频流 (codec_type == 'video') 特有关键字段:
    • width, height: 视频解码后的宽度和高度(像素)。
    • coded_width, coded_height: 视频编码时的宽度和高度(可能因编码要求而与 width/height 不同)。
    • pix_fmt: 像素格式(如 ‘yuv420p’, ‘rgb24’)。
    • level: 编码级别(如 H.264 的 41 代表 Level 4.1)。
    • color_range, color_space, color_transfer, color_primaries: 详细的色彩空间信息。
    • sample_aspect_ratio (SAR), display_aspect_ratio (DAR): 像素宽高比和显示宽高比。
    • r_frame_rate: 容器中标称的基础帧率(分数,如 “25/1”)。
    • avg_frame_rate: 平均帧率(分数,如 “30000/1001” 表示 29.97fps)。常用!
  • 音频流 (codec_type == 'audio') 特有关键字段:
    • sample_fmt: 音频采样格式(如 ‘fltp’ - 浮点平面)。
    • sample_rate: 采样率(Hz,如 ‘44100’, ‘48000’)。常用!
    • channels: 声道数(如 1, 2, 6)。常用!
    • channel_layout: 声道布局描述(如 ‘mono’, ‘stereo’, ‘5.1(side)’)。常用!
    • bits_per_sample: 每个采样点的位数(对于某些格式可能为 0)。
Python 解析 ffprobe JSON 示例 (增强版)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import subprocess
import json
import sys
from pathlib import Path # 使用 pathlib 处理路径,更健壮

def get_media_info(filepath: str) -> dict | None:
"""使用 ffprobe 获取媒体文件的详细信息 (JSON 格式)"""
ffprobe_cmd = 'ffprobe' # 假设 ffprobe 在系统 PATH 中
filepath_obj = Path(filepath)
# 检查文件是否存在
if not filepath_obj.is_file():
print(f"错误: 输入文件不存在 '{filepath}'")
return None

# 构建 ffprobe 命令列表
cmd = [
ffprobe_cmd,
'-v', 'quiet', # 抑制日志输出,只输出 JSON 或错误
'-print_format', 'json', # 指定输出格式为 JSON
'-show_format', # 要求包含容器格式信息
'-show_streams', # 要求包含所有流的信息
str(filepath_obj) # 将 Path 对象转换为字符串路径
]
print(f"执行命令: {' '.join(cmd)}") # 打印将要执行的命令,方便调试

try:
# 执行命令并捕获输出
result = subprocess.run(
cmd,
capture_output=True, # 捕获 stdout 和 stderr
text=True, # 将输出解码为文本
check=True, # 如果 ffprobe 返回非零退出码则抛出异常
encoding='utf-8', # 指定编码
errors='ignore' # 忽略解码中的小错误
)
# 解析 JSON 输出
media_info = json.loads(result.stdout)
return media_info
except subprocess.CalledProcessError as e:
# 处理命令执行失败的情况
print(f"ffprobe 执行失败 for '{filepath}':")
print(f" 返回码: {e.returncode}")
print(f" 命令: {' '.join(e.cmd)}") # 打印执行的命令
print(f" 错误输出 (stderr): {e.stderr}") # 打印错误信息
return None
except FileNotFoundError:
# 处理 ffprobe 命令未找到的情况
print(f"错误: ffprobe 命令 '{ffprobe_cmd}' 未找到。请确保已安装 FFmpeg 并将其添加到系统 PATH。")
return None
except json.JSONDecodeError as e:
# 处理 JSON 解析失败的情况
print(f"错误: 解析 ffprobe 输出失败。错误: {e}")
# 打印原始输出可能有助于诊断 ffprobe 的问题
# print("原始输出:", result.stdout if 'result' in locals() else 'N/A')
return None
except Exception as e:
# 捕获其他未知错误
print(f"获取媒体信息时发生未知错误: {e}")
return None

# 使用示例:
if __name__ == "__main__":
# 替换为你需要分析的文件路径
target_file = 'input.mp4' # 或者你电脑上的其他音视频文件

info = get_media_info(target_file)

if info:
print(f"\n--- 文件 '{target_file}' 的详细信息 ---")

# 安全地访问 format 信息
format_info = info.get('format', {})
print("\n[容器信息]")
print(f" 格式: {format_info.get('format_long_name', 'N/A')} ({format_info.get('format_name', 'N/A')})")
try:
duration_sec = float(format_info.get('duration', 0))
print(f" 时长: {duration_sec:.2f} 秒")
except (ValueError, TypeError):
print(f" 时长: N/A")
try:
size_mb = int(format_info.get('size', 0)) / (1024*1024)
print(f" 大小: {size_mb:.2f} MB")
except (ValueError, TypeError):
print(f" 大小: N/A")
try:
bitrate_kbps = int(format_info.get('bit_rate', 0)) / 1000
print(f" 总比特率: {bitrate_kbps:.1f} kbps")
except (ValueError, TypeError):
print(f" 总比特率: N/A")
print(f" 流数量: {format_info.get('nb_streams', 0)}")
if format_info.get('tags'):
print(f" 元数据标签: {format_info.get('tags')}")

print("\n[流信息]")
# 安全地访问 streams 列表
for stream in info.get('streams', []):
stream_index = stream.get('index', 'N/A')
codec_type = stream.get('codec_type', 'N/A')
print(f" --- 流 #{stream_index} ({codec_type}) ---")
print(f" 编码: {stream.get('codec_long_name', 'N/A')} ({stream.get('codec_name', 'N/A')})")
if codec_type == 'video':
print(f" 分辨率: {stream.get('width')}x{stream.get('height')}")
print(f" 像素格式: {stream.get('pix_fmt', 'N/A')}")
print(f" 帧率 (avg): {stream.get('avg_frame_rate', 'N/A')}")
elif codec_type == 'audio':
print(f" 采样率: {stream.get('sample_rate')} Hz")
print(f" 声道: {stream.get('channels')} ({stream.get('channel_layout', 'N/A')})")
try:
stream_duration = float(stream.get('duration', 0))
print(f" 流时长: {stream_duration:.2f} 秒")
except (ValueError, TypeError):
print(f" 流时长: N/A")
try:
stream_bitrate = int(stream.get('bit_rate', 0)) / 1000
print(f" 流比特率: {stream_bitrate:.1f} kbps")
except (ValueError, TypeError):
print(f" 流比特率: N/A")
if stream.get('tags'):
print(f" 流标签: {stream.get('tags')}")
else:
print(f"\n未能获取文件 '{target_file}' 的信息。请检查文件路径和 FFmpeg 安装。")



详细信息流如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
--- 文件 'input.mp4' 的详细信息 ---

[容器信息]
格式: QuickTime / MOV (mov,mp4,m4a,3gp,3g2,mj2)
时长: 35.33
大小: 1.92 MB
总比特率: 456.8 kbps
流数量: 2
元数据标签: {'major_brand': 'isom', 'minor_version': '512', 'compatible_brands': 'isomiso2avc1mp41', 'encoder': 'Lavf59.27.100', 'description': 'Packed by Bilibili XCoder v2.0.2'}

[流信息]
--- 流 #0 (video) ---
编码: H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (h264)
分辨率: 720x996
像素格式: yuv420p
帧率 (avg): 1060000/35333
流时长: 35.33
流比特率: 341.0 kbps
流标签: {'language': 'und', 'handler_name': 'VideoHandler', 'vendor_id': '[0][0][0][0]'}
--- 流 #1 (audio) ---
编码: AAC (Advanced Audio Coding) (aac)
采样率: 44100 Hz
声道: 2 (stereo)
流时长: 35.32
流比特率: 105.5 kbps
流标签: {'language': 'und', 'handler_name': 'SoundHandler', 'vendor_id': '[0][0][0][0]'}

17.3 ffmpeg-python 核心 API

ffmpeg-python 的核心思想与其他 Python 库直接绑定 FFmpeg 的 C API 不同。它并不直接执行编解码或滤镜操作,而是提供了一套 Pythonic 的接口,让你像用 Python 代码描述流程一样来构建一个媒体处理的有向无环图 (DAG - Directed Acyclic Graph)

在这个图中:

  • 节点 (Node): 代表一个处理单元,比如一个输入文件、一个滤镜操作、或者一个输出文件。
  • 边 (Edge / Stream): 代表在节点之间流动的媒体流 (Stream),可以是视频流、音频流等。

你通过调用 ffmpeg-python 的函数(如 ffmpeg.input, ffmpeg.filter, ffmpeg.output)来创建节点,并通过链式调用或将流作为参数传递来连接这些节点,形成处理图。这个图仅仅是一个处理流程的描述

只有当你调用最终输出节点的 .run().run_async() 方法时,ffmpeg-python 才会将这个图编译 (compile) 成一个等效的、可能非常复杂的 FFmpeg 命令行参数列表,然后调用你系统上安装的 ffmpeg 可执行程序(通过 Python 的 subprocess 模块)来实际执行这个命令。

这种方式的主要优点是代码可读性高、符合 Python 习惯、并且能优雅地处理复杂的滤镜链和多输入多输出场景,避免了手动拼接命令行字符串的痛苦和易错性。

17.3.1 API: ffmpeg.probe() - 探测媒体文件信息

作用:
调用系统中的 ffprobe 命令行工具,分析指定的媒体文件,并将其详细信息解析为一个结构化的 Python 字典返回。这是在进行任何处理之前了解文件属性的关键步骤。

语法:

1
ffmpeg.probe(filename, cmd='ffprobe', **kwargs)

参数详解:

  • filename (字符串, 必需): 要分析的媒体文件的路径或 URL。建议使用 pathlib 处理路径,并传入 str(path_obj)
  • cmd (字符串, 可选): 指定 ffprobe 可执行文件的路径。如果 ffprobe 已经在系统 PATH 环境变量中,则保持默认值 'ffprobe' 即可。
  • **kwargs (可选关键字参数): 对应传递给 ffprobe 命令行的额外选项(需要去掉选项前的 -)。例如:
    • select_streams='v': 对应 -select_streams v,表示只探测视频流的信息。
    • show_entries='stream=codec_name,duration:format=bit_rate': 对应 -show_entries stream=codec_name,duration:format=bit_rate,表示只在输出中包含指定的条目,减少输出量。
    • count_frames=None: 对应 -count_frames 标志,用于计算流的总帧数(可能较慢)。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import ffmpeg
import json
from pathlib import Path
import sys # 用于退出

# --- 配置 ffprobe 路径 (如果需要) ---
FFPROBE_PATH = "ffprobe"

def probe_and_display(filepath: str):
"""调用 ffmpeg.probe 并打印关键信息"""
filepath_obj = Path(filepath)
if not filepath_obj.is_file():
print(f"错误: 文件不存在 '{filepath}'")
return

print(f"\n--- 使用 ffmpeg.probe 探测: {filepath} ---")
try:
# 调用 probe 函数
info = ffmpeg.probe(str(filepath_obj), cmd=FFPROBE_PATH)
# 探测成功,info 是一个包含详细信息的字典

print("[探测成功,解析信息如下]")

# --- 解析容器信息 ---
format_info = info.get('format', {})
print(" [容器 Format]")
print(f" 时长 (秒): {float(format_info.get('duration', 0)):.3f}") # 更高精度
print(f" 格式: {format_info.get('format_long_name', 'N/A')}")
print(f" 总比特率 (kbps): {int(format_info.get('bit_rate', 0)) / 1000:.1f}")
print(f" 流数量: {format_info.get('nb_streams', 0)}")

# --- 解析流信息 ---
print(" [数据流 Streams]")
streams_list = info.get('streams', [])
for stream in streams_list:
index = stream.get('index', 'N/A')
codec_type = stream.get('codec_type', 'N/A')
print(f" --- 流 #{index} ({codec_type}) ---")
print(f" 编码: {stream.get('codec_name', 'N/A')} (Profile: {stream.get('profile', 'N/A')})")
if codec_type == 'video':
# 获取视频信息
width = stream.get('width')
height = stream.get('height')
pix_fmt = stream.get('pix_fmt', 'N/A')
frame_rate = stream.get('avg_frame_rate', 'N/A') # 平均帧率更常用
# 计算帧率的数值 (分数形式转小数)
fps_num = 0
if frame_rate != 'N/A' and '/' in frame_rate:
num, den = map(int, frame_rate.split('/'))
if den != 0: fps_num = num / den
print(f" 分辨率: {width}x{height}")
print(f" 像素格式: {pix_fmt}")
print(f" 帧率: {frame_rate} (~{fps_num:.2f} fps)")
elif codec_type == 'audio':
# 获取音频信息
sample_rate = stream.get('sample_rate')
channels = stream.get('channels')
channel_layout = stream.get('channel_layout', 'N/A')
print(f" 采样率: {sample_rate} Hz")
print(f" 声道: {channels} ({channel_layout})")
# 打印流的比特率和时长
stream_bitrate_str = stream.get('bit_rate')
stream_duration_str = stream.get('duration')
try: print(f" 流比特率: {int(stream_bitrate_str) / 1000:.1f} kbps")
except: print(f" 流比特率: N/A")
try: print(f" 流时长: {float(stream_duration_str):.3f} 秒")
except: print(f" 流时长: N/A")

except ffmpeg.Error as e:
print(f"ffprobe 执行出错 (ffmpeg.Error):")
print(e.stderr.decode(errors='ignore')) # 打印原始错误输出
except FileNotFoundError:
print(f"错误: ffprobe 命令 '{FFPROBE_PATH}' 未找到。")
except Exception as e:
print(f"探测过程中发生未知错误: {e}")

# --- 使用示例 ---
if __name__ == "__main__":
# 确保存在一个可供探测的文件
test_file = Path("input.mp4")
if not test_file.is_file():
print(f"警告: 测试文件 '{test_file}' 不存在。请准备一个视频文件或运行之前的示例代码创建它。")
# 如果需要,可以在这里添加创建测试文件的逻辑
else:
probe_and_display(str(test_file))

返回值:

  • 成功时: 返回一个 Python 字典 (dict),包含了从 ffprobe 获取并解析的媒体信息。
  • 失败时: 抛出 ffmpeg.Error 或其他异常。

知识点表格: ffmpeg.probe()

API / 参数类型作用备注
ffmpeg.probe()Function调用 ffprobe 分析媒体文件并返回信息字典。是进行处理前获取属性的关键。
filenamestr(必需) 文件路径。
cmdstr(可选) ffprobe 可执行文件路径。默认 'ffprobe'
**kwargsdict(可选) 传递给 ffprobe 的额外选项。select_streams, show_entries
返回值dict媒体信息字典 (format, streams)。
异常ffmpeg.Errorffprobe 执行失败时抛出。可通过 .stderr 获取原始错误信息。

17.3.2 输入节点 (ffmpeg.input)

作用:
ffmpeg-python 构建的处理图中创建输入节点,代表一个媒体文件、设备或网络流来源。这是所有处理流程的起点。

语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
import ffmpeg
import glob

# 获取1~4.png的文件列表
file_pattern = "%d.png" # 使用ffmpeg的图像序列模式
file_list = glob.glob("[1-4].png") # 用于验证文件是否存在

if file_list:
print(f"找到以下图像文件: {file_list}")
# 使用ffmpeg的图像序列模式
input_node = ffmpeg.input(file_pattern, pattern_type='sequence')
else:
print("未找到1~4.png的文件")

参数详解:

  • filename (字符串, 必需): 输入来源的标识符。可以是:

    • 本地文件路径: 'input.mp4', 'images/logo.png', str(Path('video.mkv'))
    • URL: 'http://server/stream.m3u8', 'rtmp://server/live'
    • 设备名称 (依赖 FFmpeg 编译和系统支持): 例如 Linux 上的 /dev/video0 (需要 -f v4l2) 或 Windows 上的 'video=Integrated Camera' (需要 -f dshow)。
    • 图像序列模式: 'img%04d.png' (匹配 img0001.png, img0002.png…)
    • Lavfi 滤镜源: 'color=c=black:s=1280x720:d=10' (创建一个 10 秒的黑色视频源)
  • **kwargs (可选关键字参数): 对应 FFmpeg 命令行中放在 -i 之前输入选项。键名通常与命令行选项相同(去掉 -)。

    常用输入选项 (kwargs):

    • ss=<time>: 输入定位。从指定时间点开始读取输入。time 可以是秒数或 HH:MM:SS.ms 格式。比输出定位 -ss 快,但不精确到帧。
    • t=<duration>: 从输入源最多读取指定 duration 时长的数据。
    • to=<time>: 从输入源读取数据,直到达到指定的时间点 time
    • f=<format>: 强制使用指定的输入格式/解复用器 (Demuxer)。例如,读取摄像头时可能需要 f='v4l2' (Linux) 或 f='dshow' (Windows);读取图像序列时用 f='image2';读取 Lavfi 源时用 f='lavfi'
    • framerate=<rate>: 强制指定输入的帧率。主要用于本身没有帧率信息的输入,如图形序列。
    • s='<WxH>': 强制指定输入的分辨率。主要用于本身没有分辨率信息的原始视频流。
    • pix_fmt=<format>: 强制指定输入的像素格式。主要用于原始视频流。
    • loop=1: (用于 image2 demuxer) 无限循环单个输入图像。通常和 -t 配合生成持续时间的视频。
    • stream_loop=<count>: (用于视频/GIF等) 循环读取整个输入流指定的次数。-1 表示无限循环。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import ffmpeg

# 1. 基本文件输入
in1 = ffmpeg.input('input.mp4')
print("输入节点 1 (视频文件):", repr(in1)) # repr() 显示节点信息

# 2. 输入文件的一部分 (从第 5 秒开始,处理 10 秒)
in2 = ffmpeg.input('input.mp4', ss=5, t=10)
print("输入节点 2 (视频片段):", repr(in2))

# 3. 输入图像序列 (假设文件名为 1.png, 2.png...)
# framerate=24 参数的意义:
# 决定了将图像序列转换为视频时的播放速度(每秒显示24张图片)
# 影响输出视频的流畅度和文件大小(帧率越高越流畅,但文件更大)
# 确定了时间轴的刻度(例如:第100张图片将在第100/24≈4.17秒出现)
# 对于动画或视觉效果,24fps是电影行业的标准帧率,给人流畅的视觉体验
in_images = ffmpeg.input('%d.png', framerate=24, f='image2')
print("输入节点 3 (图像序列):", repr(in_images))


# 4. 循环输入一个 GIF
in_gif_loop = ffmpeg.input('animation.gif', stream_loop=-1)
print("输入节点 4 (循环 GIF):", repr(in_gif_loop))


返回值:
ffmpeg.input() 函数返回一个代表输入源的节点对象 (Node)。这个对象包含了关于输入流的信息,并且可以用流选择器来获取具体的 Stream 对象用于后续处理。

知识点表格: ffmpeg.input()

API / 参数类型作用备注
ffmpeg.input()Function在处理图中创建输入节点。处理流程的起点。
filenamestr(必需) 输入源路径、URL 或标识符。
**kwargsdict(可选) 对应 FFmpeg 的输入选项。ss, t, f, framerate 等。
返回值Node代表输入源的节点对象。可用于选择流 node['v'] 等。

17.3.3 流 (Stream) 的概念与选择

作用:

一个媒体文件(如 MKV, MP4)通常包含多个数据流 (Stream),例如一个视频流、一个或多个音频流(不同语言)、一个或多个字幕流等。ffmpeg.input() 函数返回的是一个代表整个输入源的节点 (Node) 对象。为了对特定的数据流(比如只对视频进行缩放,或只选择英语音轨)进行操作,你需要从输入节点中精确地选择出你想要的那个流。

选择流会得到一个 Stream 对象,这个 Stream 对象是后续应用滤镜 (.filter()) 的主体。

语法:

主要使用类似字典的方括号访问方式:

1
stream = input_node[stream_specifier]

或者使用便捷属性(只适用于第一个默认流):

1
2
3
video_stream = input_node.video # 等同于 input_node['v'] 或 input_node['v:0']
audio_stream = input_node.audio # 等同于 input_node['a'] 或 input_node['a:0']
subtitle_stream = input_node.subtitle # 等同于 input_node['s'] 或 input_node['s:0']

参数详解 (stream_specifier):

stream_specifier 是一个字符串,其格式与 FFmpeg 命令行中的流标识符非常相似,用于指定要选择哪个流。

ffmpeg-python specifierFFmpeg CLI 等效含义
'v''v:0'vv:0第一个视频流
'a''a:0'aa:0第一个音频流
's''s:0'ss:0第一个字幕流
N (整数)N第 N+1 个流(绝对索引,从 0 开始,不区分类型)
'v:N'v:N第 N+1 个视频流 (索引从 0 开始)
'a:N'a:N第 N+1 个音频流 (索引从 0 开始)
's:N's:N第 N+1 个字幕流 (索引从 0 开始)
其他其他标识符ffmpeg-python 对更复杂的标识符 (如 i:ID, p:ID, m:key:value) 的直接支持可能有限,通常通过先 probe 获取信息再按 index 选择更可靠。

代码示例: 选择不同的流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import ffmpeg

# 假设 input_multi.mkv 文件包含:
# 流 0: 视频 (1080p)
# 流 1: 音频 (英语, 立体声)
# 流 2: 音频 (日语, 5.1声道)
# 流 3: 字幕 (英语 SRT)
# 流 4: 字幕 (中文字幕 ASS)

try:
# 创建输入节点
input_node = ffmpeg.input('input_multi.mkv') # 替换为你的多流文件路径

# --- 选择流 ---

# 1. 选择第一个视频流 (最常用)
video_default = input_node['v']
# video_default = input_node.video # 效果相同
print("选择的默认视频流:", repr(video_default))

# 2. 选择第一个音频流 (英语)
audio_eng = input_node['a']
# audio_eng = input_node['a:0'] # 效果相同
print("选择的默认音频流 (英语):", repr(audio_eng))

# 3. 按类型和索引选择第二个音频流 (日语 5.1)
audio_jpn_51 = input_node['a:1']
print("选择的第二个音频流 (日语):", repr(audio_jpn_51))

# 4. 按绝对索引选择第四个流 (这里是第一个字幕流)
# 注意:索引从 0 开始,所以第 4 个流的索引是 3
subtitle_eng = input_node[3]
print("选择的第 4 个流 (索引3, 英语字幕):", repr(subtitle_eng))

# 5. 按类型和索引选择第二个字幕流 (中文 ASS)
subtitle_chn = input_node['s:1']
print("选择的第二个字幕流 (中文):", repr(subtitle_chn))

# --- 后续可以将选择的流用于处理 ---
# 例如,只输出视频和日语 5.1 音频
# output_node = ffmpeg.output(video_default, audio_jpn_51, 'output_jpn_audio.mp4')
# output_node.run() # 执行 (需要 ffmpeg)

except ffmpeg.Error as e:
print(f"处理出错: {e.stderr.decode(errors='ignore')}")
except FileNotFoundError:
print(f"错误: 输入文件 'input_multi.mkv' 或 ffmpeg/ffprobe 命令未找到。")
except IndexError:
print("错误:尝试选择的流索引不存在。请用 ffprobe 确认文件流信息。")
except Exception as e:
print(f"发生未知错误: {e}")

返回值:

成功选择流会返回一个 ffmpeg.Stream 对象。这个对象代表了处理图中的一个特定数据流,可以对其调用 .filter() 方法或将其传递给 ffmpeg.output()。如果指定的流不存在(例如,文件只有一个音频流,但你尝试选择 a:1),通常会在后续构建图或执行时(取决于库的实现细节)引发错误。

知识点: 流选择

语法/属性作用返回值备注
node[specifier]主要方式: 使用流标识符选择流。ffmpeg.Streamspecifier'v', 'a:1', 0 等。
node.video便捷属性,选择第一个视频流。ffmpeg.Stream等同于 node['v']node['v:0']
node.audio便捷属性,选择第一个音频流。ffmpeg.Stream等同于 node['a']node['a:0']
node.subtitle便捷属性,选择第一个字幕流。ffmpeg.Stream等同于 node['s']node['s:0']

17.3.4 应用滤镜 (Stream.filter)

作用:

ffmpeg-python 中,获取到一个代表特定流的 Stream 对象后(例如通过 input_node['v']input_node['a']),你可以对这个流应用一个或多个 FFmpeg 滤镜来进行处理。这是修改音视频内容的核心步骤,比如调整大小、裁剪、调色、调整音量等。

每次调用 .filter() 都会在处理图中添加一个滤镜节点,并返回一个代表滤镜处理后输出的新 Stream 对象。

语法:

1
filtered_stream = stream_object.filter(filter_name, *args, **kwargs)

参数详解:

  • filter_name (字符串, 必需): 你想要应用的 FFmpeg 滤镜的名称。例如 'scale', 'crop', 'volume', 'hflip', 'drawtext' 等。你可以通过 ffmpeg -filters 查看所有可用滤镜。
  • *args (可选位置参数): 传递给滤镜的位置参数。某些简单的滤镜可以直接按顺序接收参数值。例如,scale 滤镜可以接受宽度和高度作为前两个位置参数:filter('scale', 640, 360)
  • **kwargs (可选关键字参数): (推荐使用) 使用 键=值 的形式传递滤镜的选项。键名通常与 FFmpeg 滤镜文档中定义的选项名一致。使用关键字参数通常更清晰易读,不易出错。例如:filter('scale', width=640, height=-2)
    • 注意: 如果 FFmpeg 滤镜选项名包含特殊字符或与 Python 关键字冲突(虽然不常见),你可能需要用字典解包的方式传递:filter('filter_name', **{'option-with-hyphen': 'value'})

链式调用 (Chaining):

由于 .filter() 返回一个新的 Stream 对象,你可以方便地进行链式调用,将多个滤镜按顺序应用到同一个流上,形成一个简单的滤镜链 (filterchain)(对应命令行中用逗号分隔多个滤镜)。

代码示例 1: 缩放视频流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import ffmpeg
import subprocess
import re
import sys
import time

input_file = "input.mp4"
output_file = "output.mp4"
FFMPEG_PATH = 'ffmpeg' # 在系统变量中


def get_video_duration(input_file):
"""获取视频时长(秒)"""
try:
probe = ffmpeg.probe(input_file)
duration = float(probe['format']['duration'])
return duration
except Exception as e:
print(f"获取视频时长失败: {e}")
return 0


def print_progress_bar(current, total, bar_length=50):
"""打印进度条"""
progress = min(1.0, current / total) if total > 0 else 0
arrow = '=' * int(round(progress * bar_length))
spaces = ' ' * (bar_length - len(arrow))
percent = round(progress * 100, 2)

sys.stdout.write(f"\r[{arrow}{spaces}] {percent}%")
sys.stdout.flush()


def scaleVedio(input_file: str, output_file: str, width: int, height: int) -> None:
try:
# 获取视频时长
duration = get_video_duration(input_file)

# 1.创建输入节点
input_node = ffmpeg.input(input_file)
# 2.选择视频流
video_stream = input_node['v']

# 3.对视频流应用 scale 滤镜
scaled_video_stream = video_stream.filter('scale',width=width,height=height)

# 4.创建输出节点
# a? 表示如果存在银平流则选择第一个,不存在也不会报错
output_node = ffmpeg.output(scaled_video_stream, input_node['a?'], output_file,
acodec='copy') # 音频直接复制

# 5. 查看命令 (调试)
cmd = output_node.compile(cmd=FFMPEG_PATH)
print("生成的命令:", cmd)

# 6. 执行并显示进度条
print("开始执行缩放...")

# 使用subprocess代替直接run,以便捕获实时输出
process = subprocess.Popen(
cmd,
stderr=subprocess.PIPE,
universal_newlines=True
)

# 正则表达式用于从ffmpeg输出中提取时间信息
time_pattern = re.compile(r"time=(\d+):(\d+):(\d+)\.\d+")

# 初始化进度条
print_progress_bar(0, duration)

# 读取ffmpeg的输出并更新进度条
for line in process.stderr:
matches = time_pattern.search(line)
if matches:
hours, minutes, seconds = map(int, matches.groups())
current_time = hours * 3600 + minutes * 60 + seconds
print_progress_bar(current_time, duration)

# 等待进程完成
process.wait()

# 完成进度条
print_progress_bar(duration, duration)
print("\n缩放完成!")

except ffmpeg.Error as e:
print("\n缩放失败:", e.stderr.decode(errors='ignore'))
except Exception as e:
print(f"\n发生错误: {e}")


if __name__ == '__main__':
scaleVedio(input_file, output_file, 1600, -2)

代码示例 2: 链式调用 - 调整音量并改变速度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import ffmpeg

input_file = "input.mp4"
output_file = "output.mp4"
FFMPEG_PATH = 'ffmpeg'


def adjust_video(input_file: str, output_file: str, speed: float, volume: float) -> None:
try:
# 1. 输入并选择音频流
input_stream = ffmpeg.input(input_file)
audio_stream = input_stream['a']
video_stream = input_stream['v']

# 2.链式调用滤镜
process_audio_stream = (
audio_stream
.filter('volume', volume) # 调整音量
.filter('atempo', speed) # 调整播放速度
)

# 2.1 处理视频流,添加速度调整
process_video_stream = (
video_stream
.filter('setpts', f'PTS/{speed}') # 调整视频播放速度
)

# 3. 定义输出,视频不再使用copy编码,因为需要处理视频速度
output_node = ffmpeg.output(
process_video_stream, # 使用处理后的视频流
process_audio_stream,
output_file
# 不再使用vcodec='copy',让ffmpeg自动选择合适的编码器
)

# 4.查看命令
print("生成的命令:", output_node.compile(cmd=FFMPEG_PATH))

# 5. 执行命令
print("开始执行命令...")
output_node.run(cmd=FFMPEG_PATH, capture_stdout=True, capture_stderr=True)
print("执行完成!")
except ffmpeg.Error as e:
print("执行出错:", e.stderr.decode('utf8'))


if __name__ == '__main__':
adjust_video(input_file, output_file, speed=5.0, volume=3.0)

返回值:
.filter() 方法返回一个新的 ffmpeg.Stream 对象,代表应用了该滤镜之后的输出流。你可以继续对这个新的 Stream 对象调用 .filter() 或将它传递给 ffmpeg.output()

知识点表格: Stream.filter()

API / 参数类型作用备注
.filter()Method on Stream obj对当前流应用一个 FFmpeg 滤镜。返回一个新的 Stream 对象,允许链式调用。
filter_namestr(必需) FFmpeg 滤镜名称。'scale', 'volume'
*args(可选) 滤镜的位置参数。顺序必须与 FFmpeg 文档一致。
**kwargsdict(可选/推荐) 滤镜的关键字参数。键名通常同 FFmpeg 选项名。
返回值ffmpeg.Stream代表应用滤镜后的输出流。

17.3.5 输出节点 (ffmpeg.output) 与输出选项详解 (表格优化版)

作用:

ffmpeg.output() 函数用于在处理图中定义一个输出节点。它指定了处理流程的终点(即生成的输出文件),并且包含了将输入流编码、封装到这个输出文件时所需的所有配置,如使用的编码器、比特率、格式、时长限制等。

语法:

1
2
3
output_node = ffmpeg.output(*streams_and_filename, **kwargs)
# 或者更清晰地分开写:
# output_node = ffmpeg.output(stream1, stream2, ..., filename, **kwargs)

参数详解:

  • *streams_and_filename (位置参数):

    • *streams (必需): 一个或多个 ffmpeg.Stream 对象,代表要包含在输出文件中的数据流。
    • filename (字符串, 必需): 最后一个位置参数,指定输出文件的路径和名称。扩展名(如 .mp4)有助于 FFmpeg 推断默认格式。
  • **kwargs (可选关键字参数): (核心) 这些参数是控制输出文件属性的关键,它们直接映射到 FFmpeg 命令中放在输出文件名前的输出选项。下面表格列出了最常用的 kwargs

    常用 ffmpeg.output() 关键字参数 (kwargs)

    ffmpeg-python kwargFFmpeg CLI Option描述示例值 ('' 内为字符串)
    f-f <format>强制指定输出文件的容器格式 (Muxer)。'mp4', 'flv', 'hls', 'mp3'
    vcodecc:v-c:v <codec>指定视频编码器'libx264', 'copy', 'h264_nvenc'
    acodecc:a-c:a <codec>指定音频编码器'aac', 'libopus', 'copy'
    scodecc:s-c:s <codec>指定字幕编码器'srt', 'mov_text', 'copy'
    video_bitrateb:v-b:v <rate>设置视频平均比特率。与 crf 通常不同时使用。'2M', '1500k'
    audio_bitrateb:a-b:a <rate>设置音频平均比特率'128k', '192k'
    crf-crf <value>恒定速率因子 (用于 x264/x265 等),控制视频质量。值越低质量越好。推荐使用23, 28
    preset-preset <value>编码器预设,平衡速度与压缩率。'fast', 'medium', 'slow'
    pix_fmt-pix_fmt <format>指定输出视频的像素格式,影响颜色和兼容性。'yuv420p' (常用)
    vf-vf <filter_string>应用简单的视频滤镜链 (字符串形式)。注意:推荐使用 .filter() 方法构建。'scale=640:-1,hflip'
    af-af <filter_string>应用简单的音频滤镜链 (字符串形式)。注意:推荐使用 .filter() 方法构建。'volume=0.8,atempo=1.1'
    t-t <duration>限制输出文件的持续时长(秒)。60, '00:01:30.5'
    to-to <time>指定输出文件的结束时间点(相对于输入开始时间)。120, '00:02:00.0'
    movflags-movflags <flags>(主要用于 MP4) 设置 MP4 的标志。'+faststart' 强烈推荐用于网络播放。'+faststart'
    shortest-shortest(布尔值 True) 当有多个输入流时长不同时,使输出时长等于最短的输入流。True
    map-map <specifier>(字符串列表) 手动指定流映射。注意:通常直接传递 Stream 对象给 output() 更 Pythonic,一般无需此参数。['0:v:0', '1:a']
    **{'option': value}-option value传递 ffmpeg-python 没有直接关键字参数对应的原生 FFmpeg 选项。**{'q:v': 2}

代码示例 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import ffmpeg

# 假设已经有输入节点和处理后的流
# input_node = ffmpeg.input('input.mp4')
# video_processed = input_node['v'].filter('scale', 640, -1) # 假设处理过
# audio_original = input_node['a']

FFMPEG_PATH = 'ffmpeg' # 假设在 PATH 中

# --- 示例 1: 输出为 MP4,指定编码器和质量 (CRF) ---
# try:
# print("\n--- 示例 1: 输出 MP4 (H.264/AAC, CRF) ---")
# out1 = ffmpeg.output(
# video_processed, # 处理后的视频流
# audio_original, # 原始音频流
# 'output_h264_crf.mp4', # 输出文件名
# vcodec='libx264', # 视频编码器
# crf=24, # 视频质量控制
# preset='fast', # 编码速度
# acodec='aac', # 音频编码器
# audio_bitrate='128k', # 音频比特率
# pix_fmt='yuv420p', # 像素格式
# movflags='+faststart' # MP4 优化
# )
# print("命令:", out1.compile(cmd=FFMPEG_PATH))
# # out1.run(cmd=FFMPEG_PATH, capture_stderr=True, overwrite_output=True)
# print("输出 1 完成 (注释掉了执行)")
# except ffmpeg.Error as e: print("错误:", e.stderr.decode())
# except Exception as e: print(f"发生错误: {e}")

# --- 示例 2: 输出为 WebM,指定比特率 ---
# try:
# print("\n--- 示例 2: 输出 WebM (VP9/Opus, Bitrate) ---")
# out2 = ffmpeg.output(
# video_processed,
# audio_original,
# 'output_vp9_opus.webm',
# # f='webm', # 通常根据扩展名可推断
# vcodec='libvpx-vp9', # VP9 视频编码
# video_bitrate='1M', # 视频目标比特率 1Mbps
# acodec='libopus', # Opus 音频编码
# audio_bitrate='96k' # 音频目标比特率 96kbps
# )
# print("命令:", out2.compile(cmd=FFMPEG_PATH))
# # out2.run(cmd=FFMPEG_PATH, capture_stderr=True, overwrite_output=True)
# print("输出 2 完成 (注释掉了执行)")
# except ffmpeg.Error as e: print("错误:", e.stderr.decode())
# except Exception as e: print(f"发生错误: {e}")

# --- 示例 3: 仅提取音频并转换为 MP3,设置时长 ---
# try:
# print("\n--- 示例 3: 提取 MP3 音频 (前 30 秒) ---")
# out3 = ffmpeg.output(
# audio_original, # 只传入音频流
# 'output_audio_30s.mp3',
# acodec='libmp3lame', # MP3 编码器
# audio_bitrate='192k',
# t=30 # 只输出前 30 秒
# )
# print("命令:", out3.compile(cmd=FFMPEG_PATH))
# # out3.run(cmd=FFMPEG_PATH, capture_stderr=True, overwrite_output=True)
# print("输出 3 完成 (注释掉了执行)")
# except ffmpeg.Error as e: print("错误:", e.stderr.decode())
# except Exception as e: print(f"发生错误: {e}")

# --- 示例 4: 改变容器格式,直接复制流 (Remuxing) ---
# try:
# print("\n--- 示例 4: 转换容器 MKV -> MP4 (流复制) ---")
# in_mkv = ffmpeg.input('input.mkv') # 假设此文件存在
# out4 = ffmpeg.output(
# in_mkv, # 直接传入输入节点,自动选择流
# 'output_remux.mp4',
# codec='copy', # 对所有流使用 copy (vcodec='copy', acodec='copy')
# movflags='+faststart'
# )
# print("命令:", out4.compile(cmd=FFMPEG_PATH))
# # out4.run(cmd=FFMPEG_PATH, capture_stderr=True, overwrite_output=True)
# print("输出 4 完成 (注释掉了执行)")
# except ffmpeg.Error as e: print("错误:", e.stderr.decode())
# except FileNotFoundError: print("错误: input.mkv 未找到")
# except Exception as e: print(f"发生错误: {e}")

返回值:
ffmpeg.output() 函数返回一个代表输出操作的 OutputNode 对象。你需要调用这个对象的 .run().run_async() 方法来最终执行整个处理流程。

知识点表格: ffmpeg.output()

API / 参数类型作用备注
ffmpeg.output()Function创建输出节点,定义输出文件和编码/格式选项。处理流程的终点。
*streamsffmpeg.Stream(必需) 一个或多个要写入的流对象。来自 inputfilter 的结果。
filenamestr(必需) 输出文件的路径和名称。扩展名用于推断格式。
**kwargsdict(可选) 对应 FFmpeg 的输出选项。控制编码、质量、格式、时长等。
返回值OutputNode代表输出操作的节点对象。可以调用 .run(), .compile() 等。

17.3.6 全局选项与调试工具 (.compile, .view, global_args)

当你构建的 FFmpeg 处理流程变得复杂,或者执行结果不符合预期时,ffmpeg-python 提供了一些工具来帮助你理解和调试发生了什么。

API: Node.compile() - 查看生成的命令

作用:
这个方法不会执行任何 FFmpeg 命令。它的唯一作用是将你通过 ffmpeg-python 构建的处理图(从输入到输出)编译成最终将要传递给 ffmpeg 可执行程序的命令行参数列表。这对于以下情况极其有用

  • 调试:.run() 报错时,你可以先 .compile() 看看生成的具体命令是什么,然后将这个命令手动复制到你的终端里执行,这样可以更直接地看到 FFmpeg 的原始输出和错误信息,更容易定位问题。
  • 学习: 通过观察 ffmpeg-python 代码如何转换为命令行参数,可以加深对 FFmpeg 命令和 ffmpeg-python 库本身的理解。
  • 集成: 在某些特殊情况下,你可能只想用 ffmpeg-python 来构建命令参数列表,然后用自己定制的 subprocess 调用或其他方式来执行它。

语法:

1
arguments_list = output_node.compile(cmd='ffmpeg')

参数详解:

  • cmd (字符串, 可选): 指定 ffmpeg 可执行文件的路径。这应该与你传递给 .run()cmd 参数保持一致。默认是 'ffmpeg'

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import ffmpeg

# 构建一个稍微复杂点的处理流程 (仅用于演示 compile)
try:
in_file = ffmpeg.input('input.mp4', ss=5) # 输入,从第5秒开始
logo = ffmpeg.input('logo.png')

processed_video = (
in_file['v']
.filter('scale', 640, -1) # 缩放
.overlay(logo['v'], x=10, y=10) # 叠加 logo
)
output_node = ffmpeg.output(
processed_video, # 处理后的视频
in_file['a'], # 原始音频
'output_compiled.mp4', # 输出文件
t=15, # 输出时长 15 秒
vcodec='libx264',
acodec='aac'
)

# 调用 compile 获取参数列表
ffmpeg_args = output_node.compile()

# 打印生成的参数列表
print("--- Generated FFmpeg Command Arguments (List) ---")
print(ffmpeg_args)

# 为了更直观,可以尝试将其拼接成命令行字符串(注意空格和引号)
# 注意:直接 join 可能对包含空格的参数处理不当,仅作演示
print("\n--- Equivalent Command Line (Approximation) ---")
# 在实际执行时,推荐直接将列表传递给 subprocess.run
print(f"ffmpeg {' '.join(ffmpeg_args)}")

except Exception as e:
print(f"构建图或编译时出错: {e}")

运行上述代码(假设输入文件存在),你将在控制台看到一个 Python 列表,其中包含了 ffmpeg-python 为你生成的所有命令行参数,例如 ['-i', 'input.mp4', '-i', 'logo.png', '-filter_complex', '[1:v]...[outv]', '-map', '[outv]', ...]。你可以把这个列表(去掉 Python 的方括号和引号,加上开头的 ffmpeg)组合成一个完整的命令行字符串,在终端里运行来验证。

返回值:
compile() 方法返回一个字符串列表 (list[str]),代表将要传递给 ffmpeg 可执行程序的参数。

知识点表格: Node.compile()

API / 参数类型作用备注
.compile()Method on Node将处理图编译为 FFmpeg 命令行参数列表,不执行主要用于调试和学习。
cmdstr(可选) ffmpeg 可执行文件路径(通常不需要指定)。
返回值list[str]代表命令行参数的字符串列表。
API: Node.view() - 可视化处理图

作用:
对于非常复杂的滤镜图,文本描述可能不够直观。.view() 方法可以利用 Graphviz 工具自动生成处理图的可视化流程图(通常是 PNG, PDF 或 SVG 格式),并尝试用系统默认程序打开它。这有助于你理解数据流是如何在不同的滤镜节点之间传递和连接的。

重要前提:
要使用 .view(),你必须

  1. 安装 graphviz Python 库: pip install graphviz
  2. 安装 Graphviz 软件本身: 这是一个独立的开源图形可视化软件。你需要根据你的操作系统进行安装,并确保其 dot 命令可以在系统的 PATH 环境变量中被找到。
    • Windows: 从 Graphviz 官网 (https://graphviz.org/download/) 下载 msi 安装包并安装,务必在安装过程中勾选 “Add Graphviz to the system PATH for all users” 或类似选项。
    • macOS: brew install graphviz
    • Linux (Debian/Ubuntu): sudo apt install graphviz
    • Linux (Fedora/CentOS): sudo yum install graphvizsudo dnf install graphviz

语法:

1
output_node.view(filename='graph', format='png')

参数详解:

  • filename (字符串, 可选): 生成的图形文件的基本名称(不含扩展名),默认为 'graph'
  • format (字符串, 可选): 输出图形的文件格式,默认为 'png'。可以是 Graphviz dot 命令支持的任何格式,如 'pdf', 'svg'

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import ffmpeg

# 假设 output_node 是之前构建好的复杂输出节点
# output_node = ...

# 尝试生成并查看处理图 (需要正确安装 Graphviz)
try:
print("\n--- 尝试生成并显示处理流程图 ---")
# 这会生成 graph.gv 和 graph.gv.png (或你指定的格式) 文件
# 并尝试用系统默认程序打开 png 文件
output_node.view(filename='my_ffmpeg_graph', format='png')
print("图形文件已生成 (或尝试打开)。请查找 my_ffmpeg_graph.gv 和 my_ffmpeg_graph.gv.png")
except FileNotFoundError:
print("错误:无法执行 'dot' 命令。请确保 Graphviz 已正确安装并已添加到系统 PATH。")
except ImportError:
print("错误:需要安装 'graphviz' Python 库 (pip install graphviz)。")
except Exception as e:
print(f"生成图形时发生错误: {e}")

返回值:
.view() 方法通常返回 None,它的主要作用是产生副作用(生成文件和尝试打开查看器)。

知识点表格: Node.view()

API / 参数类型作用备注
.view()Method on Node使用 Graphviz 生成处理流程图的可视化文件。依赖 Graphviz 软件和 Python 库。
filenamestr(可选) 输出图形文件的基本名称。默认 'graph'
formatstr(可选) 输出图形的文件格式。默认 'png'。支持 ‘pdf’, ‘svg’ 等。
返回值None-主要产生副作用(生成文件/打开查看器)。
API: 全局选项 (global_args in .run() / .run_async())

作用:
有时候你需要传递 FFmpeg 的全局选项(即放在 ffmpeg 命令之后、第一个 -i 之前的选项),例如 -progress 来输出进度信息,或者 -report 生成详细日志文件,或者 -v 覆盖默认日志级别。ffmpeg-python 允许你在执行 .run().run_async() 时通过 global_args 参数来传递这些选项。

语法:

1
output_node.run(..., global_args=['-option1', 'value1', '-option2', ...])

参数详解:

  • global_args (列表 list[str], 可选): 一个包含全局选项及其值的字符串列表。每个选项和它的值(如果需要值的话)应该作为列表中的独立元素

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import ffmpeg
import os

output_node = ffmpeg.input('input.mp4').output('output.avi')
FFMPEG_PATH = 'ffmpeg'
PROGRESS_FILE = 'ffmpeg_progress.txt'

# 确保进度文件不存在,以免混淆
if os.path.exists(PROGRESS_FILE):
os.remove(PROGRESS_FILE)

try:
print("\n--- 示例:使用 global_args 传递 -progress ---")
# 使用 -progress 选项将详细进度写入文件
# 注意:-progress 需要一个目标,这里是 file: 协议
output_node.run(
cmd=FFMPEG_PATH,
capture_stderr=True,
overwrite_output=True,
global_args=['-progress', f'file:{PROGRESS_FILE}'] # 传递全局选项
)
print(f"FFmpeg 执行完成。进度信息已写入 {PROGRESS_FILE} (如果成功)。")
# 你可以在这里读取并解析 PROGRESS_FILE 的内容
# with open(PROGRESS_FILE, 'r') as f:
# progress_data = f.read()
# print("进度文件内容:\n", progress_data[:500] + "...") # 只打印部分

except ffmpeg.Error as e:
print("FFmpeg 执行失败:")
print(e.stderr.decode(errors='ignore'))
except Exception as e:
print(f"执行时发生错误: {e}")

知识点表格: global_args (用于 .run() / .run_async())

参数名类型作用备注
global_argslist[str](可选) 传递给 FFmpeg 的全局选项列表。每个选项及其值是列表中的独立字符串元素。