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 的选择直接影响各路流的对齐效果。我一般遵循这几个原则:

  1. 所有视频流用同一个 vsync 模式。混用不同模式会导致各路流的时间基准不一致。
  2. 直播场景首选 vsync 1。恒定帧率是直播的基础要求。
  3. 文件转码用 vsync 2。保留原始帧率信息,避免不必要的帧复制。
  4. 调试分析用 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 (-1) 原样传递,不做处理 需要帧率转换 vsync 0 只丢不补 vsync 1 强制 CFR vsync 2 保留 VFR passthrough 原样传递 输出视频流 选择建议 直播用 vsync 1,文件用 vsync 2

从图上可以看得很清楚:-vsync 的核心就是判断输入输出帧率是否匹配。匹配就走 passthrough,不匹配就根据你的需求选一种模式来处理。

好了,-vsync 的四种模式就讲到这里。你只要记住:直播用 1,文件用 2,调试用 passthrough,实时监控用 0。下次遇到音画不同步的问题,先检查一下 -vsync 设对了没有。