ffmpeg
🎬 FFmpeg 多媒体处理指南
📖 核心概念
💡 什么是 FFmpeg?
FFmpeg 是一套开源的数字音视频记录、转换和流化解决方案。它就像是多媒体处理领域的 “瑞士军刀”,几乎可以处理所有格式的音视频文件。
1. 容器与编码 (Container vs Codec)
- 容器 (Container): 就像一个盒子(如
.mp4,.mkv,.avi),里面装着视频流、音频流和字幕流。 - 编码 (Codec): 就像压缩算法(如
H.264,AAC,H.265),决定了盒子里的内容如何被压缩和存储。
2. 流 (Streams)
一个多媒体文件通常包含多个流:
Stream #0:0 (video): 视频流,负责画面。Stream #0:1 (audio): 音频流,负责声音。- PTS/DTS:
- PTS (Presentation Time Stamp): 告诉播放器在什么时候显示这一帧。
- DTS (Decoding Time Stamp): 告诉解码器在什么时候解码这一帧(主要用于 B 帧)。
graph LR
A[Input File .mp4] --> B[Demuxer 解封装]
B --> C[Encoded Packets]
C --> D[Decoder 解码]
D --> E[Raw Frames YUV/PCM]
E --> F[Filter Graph 滤镜处理]
F --> G[Encoder 编码]
G --> H[Muxer 封装]
H --> I[Output File .mkv]🛠️ 参数速查表
🛠️ 最佳实践
始终显式指定 -pix_fmt yuv420p 以保证最大兼容性(QuickTime, Windows Media Player 等)。
| 参数分类 | 参数 | 含义 | 常见值 / 说明 |
|---|---|---|---|
| 基础 | -i | 输入文件 | input.mp4 |
-y | 自动覆盖 | 默认为询问,脚本中使用很安全 | |
| 视频 | -c:v | 视频编码器 | libx264, libx265, copy (流复制) |
-crf | 恒定质量因子 | 18~28 (值越小质量越高,x264 默认 23) | |
-preset | 编码速度预设 | ultrafast (快/大), medium (默认), veryslow (慢/小) | |
-r | 帧率 | 25, 30, 60 | |
-pix_fmt | 像素格式 | yuv420p (必选以防兼容性问题) | |
-tag:v | 视频标签 | hvc1 (Apple 设备播放 H.265 必需) | |
-bf | B帧数量 | 0 (低延迟), 2 (默认压缩高) | |
| 音频 | -c:a | 音频编码器 | aac, mp3, copy |
-ar | 采样率 | 16000, 44100, 48000 | |
| 色彩 | -color_range | 颜色范围 | tv/1 (Limited 16-235), pc/2 (Full 0-255) |
-colorspace | 颜色空间 (Matrix) | bt709 (HD), bt2020nc (HDR), bt470bg (SD) | |
-color_primaries | 颜色原色 | bt709 (HD), bt470bg (SD),bt2020 (HDR) | |
-color_trc | 传输特性 (Transfer) | bt709 (SDR), smpte2084 (PQ HDR) | |
| 滤镜 | -vf | 视频滤镜 | scale=1920:-2, transpose=1 (旋转) |
💻 命令行实战
1. 格式转换 (Transcoding)
将视频转换为 H.264 编码,设置合理的画质和兼容性:
ffmpeg -i input.mkv -c:v libx264 -crf 23 -preset slow -c:a aac -b:a 128k -pix_fmt yuv420p output.mp42. 视频剪切 (Cutting)
⚠️ seek 原理
-ss 放在 -i 之前是关键!这样 FFmpeg 会使用关键帧快速定位(Input Seeking),速度极快且时间戳会被重置,但是这个方式不是精确剪裁的。
# 从 00:01:30 开始,截取 45 秒
ffmpeg -ss 00:01:30 -i input.mp4 -t 45 -c copy output.mp4精确剪裁
ffmpeg -i input.mp4 \
-vf "trim=start=25:duration=30,setpts=PTS-STARTPTS" \
-af "atrim=start=25:duration=30,asetpts=PTS-STARTPTS" \
-c:v libx264 -crf 23 -preset medium \
-c:a aac -ar 44100 \
output.mp43. 画面旋转 (Rotation)
使用 transpose 滤镜:
1: 顺时针 90°2: 逆时针 90°
# 顺时针旋转 90 度
ffmpeg -i input.mp4 -vf "transpose=1" -c:a copy output.mp44. 视频缩放 (Scaling)
将视频宽度固定为 1080,高度自动计算(保持比例):
ffmpeg -i input.mp4 -vf "scale=1080:-2" output.mp45. 流操作 (Stream Operations)
🛠️ 常用参数
-vn: 禁用视频 (Video None)-an: 禁用音频 (Audio None)-shortest: 以较短的流为准结束 (防止静止画面或无声片段)
提取音频 (Extract Audio)
# 提取为无损 WAV (aac, 16000Hz)
ffmpeg -i input.mp4 -vn -c:a aac -ar 16000 output.wav
# 直接提取音频流 (不转码,速度最快)
ffmpeg -i input.mp4 -vn -c:a copy output.aac
# 指定音频单通道 -ac 1音视频合并 (Muxing)
将无声视频和音频文件合并:
ffmpeg -i video.mp4 -i audio.wav -c:v copy -c:a aac -shortest output.mp46. 帧操作 (Frame Operations)
视频转图片 (Extract Frames)
每秒提取 1 张图片 (-r 1):
# %05d 生成 00001.jpg, 00002.jpg ...
ffmpeg -i input.mp4 -r 1 -f image2 image-%05d.jpg图片合成视频 (Images to Video)
将序列图片合并为视频:
ffmpeg -f image2 -r 25 -i image-%05d.jpg -c:v libx264 -pix_fmt yuv420p output.mp4如果图片中间可能缺帧的话:
ffmpeg -r 25 -pattern_type glob -i "*.jpg" -c:v libx264 -pix_fmt yuv420p output.mp47. 视频信息 (Video Informations)
读取视频基本信息,包括宽、高、平均帧率、帧数、旋转信息:
# windows ps1脚本
ffprobe.exe -v error -select_streams v:0 -show_entries stream=width,height,avg_frame_rate,nb_frames:stream_tags=rotate:stream_side_data_list -of json @args
# 示例输出结果:
## 不带旋转的视频:
{
"programs": [
],
"streams": [
{
"width": 1080,
"height": 1920,
"avg_frame_rate": "25/1",
"nb_frames": "11400",
"tags": {
}
}
]
}
## 带旋转的:
{
"programs": [
],
"streams": [
{
"width": 3840,
"height": 2160,
"avg_frame_rate": "25/1",
"nb_frames": "11400",
"tags": {
},
"side_data_list": [
{
"side_data_type": "Display Matrix",
"displaymatrix": "\n00000000: 0 -65536 0\n00000001: 65536 0 0\n00000002: 0 251658240 1073741824\n",
"rotation": 90
}
]
}
]
}🐍 Python 自动化实战 (ffmpeg-python)
利用 ffmpeg-python 库可以将复杂的滤镜链逻辑化。以下皆为 Production-Ready 的代码片段。
1. 智能分辨率调整 (Smart Resizing)
自动判断长短边,将短边统一缩放到 1080p,适合处理竖屏/横屏混杂的素材。
import ffmpeg
def transcode_to_1080p(input_path, output_path):
probe = ffmpeg.probe(input_path)
video_stream = next(s for s in probe["streams"] if s["codec_type"] == "video")
W, H = int(video_stream["width"]), int(video_stream["height"])
# 逻辑:短边定为 1080,长边自适应 (-2 保证是 2 的倍数)
if W <= H:
scale_filter = {"w": 1080, "h": -2}
else:
scale_filter = {"w": -2, "h": 1080}
(
ffmpeg
.input(input_path)
.filter("scale", **scale_filter)
.output(output_path, vcodec='libx265', crf=24, tag__v='hvc1') # 适配 Apple
.run(overwrite_output=True)
)2. 音画同步校正 (Sync Adjustment)
当视频画面和声音对不上(声画不同步)时,分别对音频和视频流进行 trim (剪切) 和 setpts (时间戳重置) 操作。音画同步帧数可以利用SyncNet计算。
💡 算法逻辑
- 声音比画面早 (frame_shift > 0): 延迟视频 = 剪掉音频开头 + 剪掉视频结尾。
- 画面比声音早 (frame_shift < 0): 延迟音频 = 剪掉视频开头 + 剪掉音频结尾。
- 核心滤镜:
trim/atrim(物理剪切),setpts/asetpts(逻辑时间轴修正)。
def adjust_sync(input_path, output_path, shift_sec):
"""
shift_sec > 0: 声音早, 需延迟视频
shift_sec < 0: 画面早, 需延迟音频
"""
probe = ffmpeg.probe(input_path)
duration = float(probe['format']['duration'])
inp = ffmpeg.input(input_path)
v = inp.video
a = inp.audio
if shift_sec > 0:
# 音频截掉开头 (atrim start),视频截掉结尾 (trim end)
a = a.filter('atrim', start=shift_sec).filter('asetpts', 'PTS-STARTPTS')
v = v.filter('trim', end=duration - shift_sec).filter('setpts', 'PTS-STARTPTS')
else:
# 视频截掉开头,音频截掉结尾
abs_shift = abs(shift_sec)
v = v.filter('trim', start=abs_shift).filter('setpts', 'PTS-STARTPTS')
a = a.filter('atrim', end=duration - abs_shift).filter('asetpts', 'PTS-STARTPTS')
ffmpeg.output(v, a, output_path).run()3. 单图生成视频 (Loop Image)
将某一张特定的帧无限循环,生成一段静止视频,并铺上静音轨道。
def image_to_video(image_path, output_path, duration=5):
# loop=1: 循环输入流
# t=duration: 控制输出时长
v = ffmpeg.input(image_path, loop=1, t=duration).video
# 生成静音音轨
a = ffmpeg.input('anullsrc', f='lavfi', t=duration).audio
ffmpeg.output(v, a, output_path, r=25, pix_fmt='yuv420p').run()⚖️ 深度解析 & FAQ
1. CRF vs Bitrate (码率控制)
在 FFmpeg 中,控制视频质量和文件大小主要有两种模式:CRF (恒定速率因子) 和 Bitrate (指定码率)。
CRF (Constant Rate Factor) - 恒定质量模式
这是 x264 和 x265 编码器的默认推荐模式。
- 原理: 目标是保持整个视频的视觉质量恒定。编码器会根据画面的复杂程度自动分配码率:复杂场景(动作快、细节多)分配更多码率,简单场景(静止、纯色)分配更少码率。
- 参数:
-crf <数值>- 范围: 0-51。
- 0: 无损压缩 (Lossless)。
- 23: x264 的默认值,平衡了画质和体积。
- 28: x265 的默认值。
- 规律: 数值越小,画质越好,文件越大。通常建议在 18-28 之间调整。+/- 6 大概会导致码率翻倍或减半。
- 适用场景: 本地归档、存储,不限制文件最终大小,只关心画质。
-b:v (Video Bitrate) - 指定码率模式
- 原理: 强制编码器试图达到指定的平均码率。
- 参数:
-b:v <码率>(例如-b:v 2M,-b:v 5000k) - 变种:
- CBR (Constant Bitrate): 恒定码率。需要配合
-minrate和-maxrate设置为相同值。适用于流媒体直播,确保带宽稳定。 - VBR (Variable Bitrate): 可变码率。通常配合
-maxrate和-bufsize使用(受限 VBR),限制峰值码率,防止网络拥塞。
- CBR (Constant Bitrate): 恒定码率。需要配合
- 适用场景: 网络流媒体播放(需要控制带宽)、存储空间受限(必须把文件控制在特定大小内)。
对比
| 模式 | 参数 | 场景 | 分析 |
|---|---|---|---|
| CRF (恒定质量) | -crf 23 | 本地归档 | 这里的“恒定”指视觉质量。编码器在复杂场景分配高码率,简单场景分配低码率。文件大小不可控。 |
| Bitrate (指定码率) | -b:v 2M | 直播/推流 | 强制限制带宽。可能导致复杂场景(如爆炸、烟雾)出现马赛克。通常配合 -maxrate 和 -bufsize 使用 (VBR)。 |
2. OpenCV 读取问题
现象: 使用 ffmpeg -hwaccel cuda (NVIDIA 硬编) 生成的视频,OpenCV 的 cv2.CAP_PROP_POS_FRAMES 随机定位经常失败。
原因: 硬件编码器生成的 GOP (Group of Pictures) 结构可能不包含足够的信息供 OpenCV 的 CPU 解码器快速索引。
解决:
- 尽量使用 CPU 编码 (
libx264). - 显式设置较小的 GOP 大小 (
-g 25)。
3. H.265 在 Apple 设备上的黑屏
若使用 libx265 编码后的视频在 Mac/iPhone 上无法播放(或只有声音无画面),通常是因为缺少 tag 信息。
解决: 必须添加参数 -tag:v hvc1。
4. 色彩空间详解 (Color Management)
在处理不同分辨率和动态范围的视频时,必须正确设置色彩参数,否则会出现画面发灰或色彩溢出。经常遇到的 “709” 和 “601” 指的就是色彩标准。
| 标准 | 常用分辨率 | 参数组合 (Matrix/Primaries/Transfer) |
|---|---|---|
| Rec. 601 | SD (PAL/NTSC, <720p) | -colorspace bt470bg -color_primaries bt470bg -color_trc bt709 |
| Rec. 709 | HD (720p, 1080p, SDR 4K) | -colorspace bt709 -color_primaries bt709 -color_trc bt709 |
| Rec. 2020 | UHD (4K HDR) | -colorspace bt2020nc -color_primaries bt2020 -color_trc smpte2084 |
⚠️ 范围 (Range)
- TV (Limited, 16-235): 绝大多数电影/电视内容的标准 (
-color_range tv)。 - PC (Full, 0-255): 游戏录制或计算机生成的图像 (
-color_range pc)。 混用会导致黑色变成深灰色(Full 转 Limited 错误),或者暗部细节丢失变成死黑(Limited 转 Full 错误)。
5. 清空时间码信息
如果用Premier对帧的话,Premier会读取时间码相关的信息,导致视频不是从零开始,这时候需要清空时间码信息:
ffmpeg -i input.mp4 -c copy -map_metadata -1 -write_tmcd 0 output.mp4