ffmpeg 裁剪视频踩坑:一个 -t 参数位置导致文件膨胀 50 倍
最近在做视频处理流水线时遇到一个隐蔽的 bug:用 ffmpeg 的 stream copy 模式裁剪一个 18 分钟的视频,本来应该输出几秒到几十秒的小片段,结果最后一个片段膨胀到了 76MB。更诡异的是,片段大小呈严格递增模式——1.7MB、6MB、20MB……一直到 76MB。
排查了很久才定位到根因:-t 参数放在了 -i 前面,而在 Docker 容器里的老版本 ffmpeg(4.4.x)会完全忽略这个参数。
这个 bug 长什么样
原始裁剪命令:
ffmpeg -y -ss 120 -t 5 -i input.mp4 \
-c:v copy -an -avoid_negative_ts make_zero output.mp4
期望行为:从第 120 秒开始,裁剪 5 秒视频。
实际行为(ffmpeg 4.4.x):从第 120 秒开始,一直复制到文件末尾。-t 5 被完全忽略。
用 ffprobe 验证:
# 正常的片段(1.7MB)
ffprobe -v quiet -show_entries stream=codec_type,duration -of csv output_small.mp4
# stream,video,15.0
# stream,audio,12.0
# 异常的片段(76.7MB)
ffprobe -v quiet -show_entries stream=codec_type,duration -of csv output_big.mp4
# stream,video,712.7 ← 视频 12 分钟!
# stream,audio,12.0 ← 音频正常 12 秒
视频流被复制了整个源文件的剩余部分,音频流却正常裁剪了 12 秒。两个流的时长差了 700 秒,全塞在一个文件里。
为什么会有这个问题
ffmpeg 的 -ss 参数在 -i 前后的行为不同,这个很多人知道:
-ss在-i前面:快速 seek,跳到最近的关键帧,时间戳从 0 开始-ss在-i后面:逐帧解码到精确位置,速度慢但精准
但 -t 参数的位置语义,几乎没人提过。实际上:
-t在-i后面(output-side):正确限制输出时长,所有版本都支持-t在-i前面(input-side):4.x 版本直接忽略,8.x 版本部分生效
这在 FFmpeg 官方文档里没有明确说明。Seeking 那篇 Wiki 只说”To extract a segment, combine -ss with -t”,示例里 -t 总是放在 -i 后面,但没有解释放前面会怎样。
Docker 里的版本陷阱
这才是问题变得隐蔽的原因。同样的命令,不同环境表现完全不同:
| 环境 | ffmpeg 版本 | -t 在 -i 前面的表现 |
|---|---|---|
| Ubuntu 24.04 本地 | 8.0.1 | 部分生效,多出 2-12 秒 |
| Docker Ubuntu 22.04 | 4.4.x | 完全忽略,复制到文件末尾 |
| Docker Ubuntu 24.04 | 8.x | 部分生效 |
本地开发用的是 Ubuntu 24.04,ffmpeg 8.0.1,-t 虽然放在 -i 前面,但”大部分时候工作”——输出只比预期多几秒。测了几次没发现问题就上线了。
生产环境跑在 Docker 容器里,基础镜像是 Ubuntu 22.04,ffmpeg 4.4.x。这个版本对 input-side -t 的处理是:直接当它不存在。

问题不是每天都能触发,因为前面的片段”看起来”是正常的——一个几秒的片段 1.7MB、6MB 都很合理。直到仔细看 ffprobe 才发现,即使是”正常”的小片段,视频流也比音频流长了几秒。只是源文件小时膨胀不明显,不会引起注意。
排查过程
发现异常是通过归档日志:同一源视频(18 分钟)裁出的 13 个片段,合计 614MB。这比源文件还大。
第一步,diff 对比代码——本地和仓库的裁剪脚本完全一致,排除代码差异。
第二步,ffprobe 分析。大小文件各取一个,发现大文件的视频流 712 秒、音频流 12 秒。说明 -c:v copy 模式下视频流没有被 -t 截断。
第三步,在本地复现。本地 ffmpeg 8.0.1 下,-t 放 -i 前面时输出确实比预期长,但只长 2-12 秒,不会出现 700 秒的差距。本地”几乎复现”但不够明显。
第四步,进 Docker 容器跑。ffmpeg 4.4.x 下直接复现:-t 完全失效。

修复
把 -t 从 -i 前面移到 -i 后面:
# 修复前(buggy)
ffmpeg -y -ss 120 -t 5 -i input.mp4 \
-c:v copy -an -avoid_negative_ts make_zero output.mp4
# 修复后
ffmpeg -y -ss 120 -i input.mp4 -t 5 \
-c:v copy -an -avoid_negative_ts make_zero output.mp4
-ss 保留在 -i 前面不动——这是刻意选择,input-side seeking 在 stream copy 模式下音视频对齐更好。只需要移动 -t。
修复后,所有 ffmpeg 版本(4.4.x、5.x、6.x、8.x)输出时长都正确。
如果你在 Docker 里跑 ffmpeg
几个实用建议:
1. 检查容器里的 ffmpeg 版本
docker run --rm your-image ffmpeg -version | head -1
Docker 镜像的 ffmpeg 版本取决于基础镜像:
- Ubuntu 22.04 → 大概率 4.4.x
- Ubuntu 24.04 → 6.1+ 或 8.x
- Debian bookworm → 5.1.x
- Alpine → 取决于 apk 源
2. 需要新版 ffmpeg 时,两种方式
# 方式1:从 PPA 安装
RUN add-apt-repository ppa:ubuntuhandbook1/ffmpeg6 && \
apt-get update && apt-get install -y ffmpeg
# 方式2:用静态编译版
ADD https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz /tmp/
RUN tar xf /tmp/ffmpeg-release-*.tar.xz -C /usr/local/bin/ --strip-components=1
3. 永远把 output options 放在 -i 后面
这是一个安全的习惯:-ss 放 -i 前面做快速 seek,-t、-to 这些输出控制参数放 -i 后面。这样不管 ffmpeg 版本怎么变都不会出问题。
小结
这个 bug 的核心是一个参数位置问题。ffmpeg 的 -t 放在 -i 前面时,在 4.x 版本里被静默忽略,在 8.x 里部分生效。因为本地开发环境和 Docker 生产环境的 ffmpeg 版本不同,bug 在本地难以复现、在生产环境才完全暴露。
如果你在用 ffmpeg stream copy 做视频裁剪,尤其是脚本需要跨版本运行的场景,检查一下你的 -t 参数放在哪里。