FFmpeg 拼接视频-记录我踩过的坑

638 阅读4分钟

我用 FFmpeg 合并过上千个视频小片段,也被“命令太长”报错气得想砸键盘。这篇文章是踩坑后的思考与总结,从 MP4 容器、H.264 编码、FFmpeg 设计哲学,浅浅聊下“为啥拼接老失败”。


一、MP4 不是视频,是“盒子”

很多人以为 MP4 就是视频,其实它是容器,像一个快递箱,里面可以装:

  • 视频流 → 通常是 H.264(AVC)
  • 音频流 → 通常是 AAC
  • 字幕、章节、封面…
┌──────────────┐
│   MP4 容器   │
├──────────────┤
│ H.264 视频流 │
│ AAC 音频流   │
└──────────────┘

关键

拼接 MP4 ≠ 拼接像素,而是拼接“数据流”
只要两个视频的 编码参数一致,FFmpeg 就能“剪刀+胶水”直接粘,不重编,0 损耗。


二、H.264 编码:为啥“一样是 MP4,却拼不了”?

H.264 是目前最常见的视频编码,但看起来一样不代表能直接拼接

参数必须一致才能 -c copy
codec都是 h264
profile都是 HighMain
level都是 4.0
分辨率1920x1080
帧率30fps
像素格式yuv420p(播放器兼容性)

真实案例
我有 10 个手机录的 MP4,肉眼看不出区别,但 ffprobe 一查:

image.png

# 视频1
width=1920, height=1080, r_frame_rate=30/1, pix_fmt=yuv420p

# 视频2
width=1920, height=1080, r_frame_rate=30000/1001, pix_fmt=yuv420p

30/130000/1001-c copy 直接报错!

我的解决思路

能不重编就不重编,但必须先统一参数
推荐:所有视频统一转码一次,再用 -c copy 拼接,效率最高。


三、为什么推荐使用-f concat拼接,如 -f concat -i list.txt

FFmpeg 有 两种拼接方式

方式命令长度支持数量适用场景
-i a.mp4 -i b.mp4 ...有限(终端限制)≤10个快速测试
-f concat -i list.txt无限制几百上千生产环境首选

终端命令长度限制(Linux 约 128KB,Windows 更短,大约8192个字符):

# 50个 -i 就会超限!
ffmpeg -i 1.mp4 -i 2.mp4 ... -i 50.mp4 -filter_complex "concat=n=50..."
# → Argument list too long

list.txt内容,如果是绝对路径,必须添加参数 -safe 0

file '0.mp4'
file '1.mp4'
file '2.mp4'
file '3.mp4'
file '4.mp4'

我的实践经验

任何超过 5 个文件的拼接,必须用 list.txt
这是 FFmpeg 官方推荐,也是工业级流程标准。 拼接前需要进入碎片所在目录下,或者list.txt直接使用绝对路径,但需要在命令行上加参数-safe 0


四、实战:从 5 个到 500 个

场景一:5 个监控片段,编码一致 → 无损拼接

ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4

1 分钟 5 个文件 → 3 秒出片,零损耗。


场景二:50 个手机视频,分辨率乱 → 强制统一 + 无声

# 统一 1080p + 去音频 + H.264
ffmpeg -f concat -safe 0 -i list.txt   -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,format=yuv420p"   -c:v libx264 -preset fast -crf 23 -an final.mp4

如果想保留声音。直接将-an替换为-c:a aac -b:a 192k -ar 48000 -ac 2

-vf 滤镜链:等比缩放 + 黑边补齐 + 转 yuv420p,兼容所有播放器。


场景三:500 个片段 → 一行命令搞定

ffmpeg -f concat -safe 0 -i list.txt   -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,format=yuv420p" -c:v libx264 -preset fast -crf 23 -an "合集.mp4"

如果想保留声音。直接将-an替换为-c:a aac -b:a 192k -ar 48000 -ac 2


五、有声 vs 无声:我选无声的理由

需求推荐
监控合集无声,省空间,避免杂音
Vlog 素材有声,但建议统一 AAC 128k
短视频去 BGM无声 + 后期配乐

无声命令模板

# 有声版
-c:a aac -b:a 192k

# 无声版
-an

六、检查工具:别瞎拼,先 ffprobe

ffprobe -v quiet -select_streams v:0   -show_entries stream=codec_name,width,height,r_frame_rate,pix_fmt   -of csv=p=0 "xxx.mp4"

image.png

运行前先检查,省 90% 失败时间


七、一键命令

预先将每个视频片段名写入list.txt

file '0.mp4'
file '1.mp4'
file '2.mp4'
file '3.mp4'
file '4.mp4'
...

list.txt目录下执行

ffmpeg -f concat -safe 0 -i list.txt   -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,format=yuv420p"   -c:v libx264 -preset fast -crf 23 -an "FINAL.mp4" 

八、写在最后

FFmpeg 不是“会用命令”就行,而是要理解音视频本质
每一次失败,都是参数不匹配;每一次成功,都是流对齐了。

建议

  1. ffprobe 检查
  2. -c copy 就别重编,又快又不降低质量
  3. 超 5 个文件必须 list.txt
  4. 不同分辨率?先缩放统一