3. FFmpeg 核心同步机制:-vsync 参数详解
做流媒体的人都知道,音视频同步是个绕不开的坎。我刚开始接触 FFmpeg 转码时,最头疼的就是视频帧率跟音频采样率对不上,出来的片子要么音画不同步,要么视频卡顿。后来我才发现,-vsync 这个参数才是控制同步的核心开关。
说白了,-vsync 就是告诉 FFmpeg:当输入视频的帧率跟输出要求的帧率不一致时,你该怎么处理那些多出来的或者缺少的帧?是扔掉?是重复?还是想办法凑合?
今天我就把这几个模式掰开揉碎了讲清楚。你想想看,搞懂了 -vsync,多路流同步转码就成功了一半。
3.1 -vsync 的四种模式
FFmpeg 提供了四种 -vsync 模式,分别是 0、1、2 和 passthrough。每种模式对应一种帧处理策略。
| 模式 | 数值 | 行为描述 | 适用场景 |
|---|---|---|---|
| passthrough | -1 | 不修改时间戳,原样传递所有帧 | 无损分析、帧精确处理 |
| cfr | 1 | 自动插帧或丢帧,强制输出恒定帧率 | 通用转码、直播推流 |
| vfr | 2 | 保留原始时间戳,只做必要调整 | 可变帧率源、录屏文件 |
| drop | 0 | 只丢帧不插帧,保持 PTS 递增 | 实时监控、低延迟场景 |
核心要点:默认模式是 -vsync 1(cfr)。如果你不指定,FFmpeg 会按恒定帧率处理。但很多情况下这并不是你想要的。
3.2 vsync 0(drop)—— 只丢不补
这个模式很直接。当输入帧率高于输出帧率时,FFmpeg 会直接丢弃多余的帧。它不会去复制帧来凑数。
我在做实时监控流接入时经常用这个模式。监控摄像头通常是 25fps,但推流平台只要求 15fps。用 -vsync 0,每两秒丢掉 20 帧,CPU 开销极小。
# 将 25fps 的输入降为 15fps,只丢帧不插帧
ffmpeg -i input.mp4 -vsync 0 -r 15 output.mp4
注意:vsync 0 模式下,输出文件的帧率可能不稳定。因为丢帧策略只保证 PTS 递增,不保证帧间隔均匀。如果你需要严格恒定帧率,别用这个模式。
3.3 vsync 1(cfr)—— 强制恒定帧率
这是默认模式,也是大多数场景下的安全选择。它的逻辑是:不管输入帧率怎么变,输出必须严格按目标帧率来。
具体怎么做呢?
- 如果输入帧太多,就丢掉一些帧
- 如果输入帧太少,就复制最近的帧来填充
举个例子。输入是 30fps 的视频,你指定输出 25fps。FFmpeg 会每秒钟丢掉 5 帧,同时保证剩下的 25 帧时间戳均匀分布。
# 强制输出 25fps,自动处理帧率转换
ffmpeg -i input.mp4 -vsync 1 -r 25 output.mp4
我个人习惯在直播推流时用 -vsync 1。因为直播要求恒定帧率,否则播放器会出问题。我曾经遇到过推流帧率忽高忽低,导致观众端画面一顿一顿的,后来发现就是 -vsync 没设对。
小技巧:如果你不确定输入源的帧率,可以用 ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate input.mp4 先查一下。
3.4 vsync 2(vfr)—— 保留可变帧率
这个模式很有意思。它不会强制改变帧率,而是尽量保留原始时间戳。只有当时间戳出现严重异常时,才会做微调。
录屏文件、手机拍摄的视频,很多都是可变帧率。比如你录屏时,鼠标不动的那几秒可能只有 1fps,动起来又变成 60fps。如果用 -vsync 1 强制转成恒定帧率,FFmpeg 会复制大量重复帧,文件体积暴增。
# 保留原始可变帧率,不做插帧
ffmpeg -i screen_recording.mkv -vsync 2 -c:v libx264 output.mp4
嗯,这里要注意。有些播放器不支持可变帧率,播放时反而会卡。所以用 -vsync 2 之前,先确认你的目标平台能不能接得住。
3.5 vsync passthrough(-1)—— 原样传递
这个模式最简单,也最暴力。它不做任何帧率转换,直接把输入帧的时间戳原封不动传给编码器。
什么时候用?
- 做帧精确的剪辑操作时
- 分析原始时间戳信息时
- 输入输出帧率完全一致时
# 原样传递所有帧,不修改时间戳
ffmpeg -i input.mp4 -vsync passthrough -c copy output.mp4
警告:passthrough 模式不会修复任何时间戳问题。如果输入源本身就有 PTS 乱序或跳变,输出也会跟着乱。我曾经接手过一个项目,上游推流时间戳乱跳,用 passthrough 直接转码,结果下游全部音画不同步。后来改成 -vsync 1 才解决。
3.6 四种模式对比总结
为了让你一眼看明白,我画了张对比表。
| 特性 | vsync 0 (drop) | vsync 1 (cfr) | vsync 2 (vfr) | vsync passthrough |
|---|---|---|---|---|
| 丢帧 | 是 | 是 | 否 | 否 |
| 插帧 | 否 | 是 | 否 | 否 |
| 输出帧率恒定 | 否 | 是 | 否 | 否 |
| 保留原始 PTS | 部分 | 否 | 是 | 完全 |
| CPU 开销 | 低 | 中 | 低 | 极低 |
| 推荐场景 | 实时监控 | 直播推流 | 录屏文件 | 帧精确分析 |
3.7 多路流同步中的 vsync 选择
做多路流同步转码时,-vsync 的选择直接影响各路流的对齐效果。我一般遵循这几个原则:
- 所有视频流用同一个 vsync 模式。混用不同模式会导致各路流的时间基准不一致。
- 直播场景首选 vsync 1。恒定帧率是直播的基础要求。
- 文件转码用 vsync 2。保留原始帧率信息,避免不必要的帧复制。
- 调试分析用 passthrough。先看原始时间戳有没有问题,再决定怎么处理。
# 多路流同步转码示例:两路视频 + 一路音频
ffmpeg -i input1.mp4 -i input2.mp4 -i audio.wav \
-filter_complex "[0:v][1:v]hstack=inputs=2[v]" \
-map "[v]" -map 2:a \
-vsync 1 -r 30 \
-c:v libx264 -c:a aac \
output.mp4
实战建议:多路流同步时,除了 -vsync,还要配合 -async(音频同步)和 -muxdelay(封装延迟)一起调。我习惯先固定 -vsync 1,再根据实际输出调整音频同步参数。
3.8 知识体系图
下面这张图帮你理清 -vsync 的核心逻辑。
从图上可以看得很清楚:-vsync 的核心就是判断输入输出帧率是否匹配。匹配就走 passthrough,不匹配就根据你的需求选一种模式来处理。
好了,-vsync 的四种模式就讲到这里。你只要记住:直播用 1,文件用 2,调试用 passthrough,实时监控用 0。下次遇到音画不同步的问题,先检查一下 -vsync 设对了没有。