15、视频拼接:使用concat协议和concat滤镜拼接多个视频文件。

视频拼接,说白了就是把几段视频首尾连起来,变成一个长视频。这事儿看起来简单,但实际做起来坑不少。我记得刚入行那会儿,领导扔给我三个不同分辨率的MP4文件,让我合成一个,我直接用cat命令合并——结果播放器直接崩溃。嗯,从那以后我才知道,视频拼接不是简单的文件堆叠。

FFmpeg提供了两种主流方案:concat协议concat滤镜。它们各有各的脾气,选错了,轻则报错,重则音画不同步。今天我就把这两种方案掰开揉碎了讲清楚。

15.1 两种方案的核心区别

先看一张图,你就明白它们各自适合什么场景了。

视频拼接方案决策树 输入文件特征? 参数完全一致(编码/分辨率/码率) 参数不一致(需重新编码) concat 协议 无需重新编码,速度最快 concat 滤镜 需重新编码,灵活性高 ⚠ 核心判断标准:所有输入视频的编码格式、分辨率、帧率、采样率是否完全一致? 是 → 用 concat 协议(无损快速) | 否 → 用 concat 滤镜(统一参数)

说白了,concat协议就是个搬运工,它不碰你的视频内容,直接把数据流拼起来。而concat滤镜是个加工厂,它会重新编码,把不同规格的视频统一成同一个标准。

15.2 concat协议:无损快速拼接

这个方案我最常用。前提是你的视频文件必须「门当户对」——编码格式、分辨率、帧率、采样率都得一模一样。我在项目中遇到过好几次,客户说「我这些视频都是同一个相机拍的,肯定一样」,结果一查,有的帧率是29.97,有的是30,拼接完就出现微妙的音画不同步。

15.2.1 文件列表方式(推荐)

我个人习惯用文件列表的方式,清晰好维护。先创建一个文本文件,比如 mylist.txt

file 'clip1.mp4'
file 'clip2.mp4'
file 'clip3.mp4'

然后执行:

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

这里几个参数我解释一下:

  • -f concat:告诉FFmpeg使用concat协议
  • -safe 0:允许使用绝对路径或特殊字符。如果不加,路径里带空格或中文就会报错
  • -c copy:直接复制流,不重新编码。这是速度最快的模式
💡 我的小技巧: 文件列表里的路径,我建议都用相对路径,或者用正斜杠。Windows用户特别注意,路径里的反斜杠要改成斜杠,否则FFmpeg会翻脸。

15.2.2 直接拼接方式

如果文件不多,也可以直接写在一行:

ffmpeg -i "concat:clip1.mp4|clip2.mp4|clip3.mp4" -c copy output.mp4

这种方式适合临时用一下。但说实话,我很少这么干——一旦文件名里有特殊字符,解析就容易出问题。

⚠ 注意: concat协议只支持MPEG-TS格式或同参数的文件。如果你拿两个不同分辨率的MP4直接拼,FFmpeg会直接报错。我曾经在直播项目中犯过这个错,推流出去画面直接花屏,被运维同事追着骂了一下午。

15.3 concat滤镜:灵活但慢

当你的视频参数不一致时,就得请concat滤镜出场了。它会重新编码,把所有视频统一成你指定的参数。

15.3.1 基础用法

ffmpeg -i clip1.mp4 -i clip2.mp4 -i clip3.mp4 \
-filter_complex "[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[outv][outa]" \
-map "[outv]" -map "[outa]" output.mp4

这个命令看着复杂,其实拆开看很简单:

  • [0:v][0:a]:第一个文件的视频流和音频流
  • concat=n=3:v=1:a=1:拼接3个文件,输出1路视频和1路音频
  • [outv][outa]:给输出流起个名字,方便后面映射

15.3.2 统一分辨率再拼接

我在实际项目中经常遇到这种情况:几个视频分辨率不一样,但又要拼在一起。这时候我会先统一分辨率:

ffmpeg -i clip1.mp4 -i clip2.mp4 \
-filter_complex "[0:v]scale=1920:1080,setsar=1[v0]; \
                 [1:v]scale=1920:1080,setsar=1[v1]; \
                 [v0][0:a][v1][1:a]concat=n=2:v=1:a=1[outv][outa]" \
-map "[outv]" -map "[outa]" output.mp4

你想想看,如果不统一分辨率,拼接出来的视频画面会突然变大或变小,观众看着多难受。

🔑 关键点: concat滤镜要求所有输入的视频流和音频流参数一致。如果分辨率不同,必须先用scale滤镜统一;如果帧率不同,要用fps滤镜统一;如果采样率不同,要用aresample滤镜统一。

15.4 实战对比表

为了方便你选择,我整理了一个对比表:

特性 concat协议 concat滤镜
是否需要重新编码 否(直接复制) 是(重新编码)
拼接速度 极快(秒级) 较慢(取决于视频长度和编码参数)
输出质量 无损 有损(取决于编码参数)
输入参数要求 必须完全一致 可以不一致(但需统一处理)
适用场景 同源视频、监控录像拼接 不同来源视频、需要统一规格

15.5 避坑指南

做视频拼接这几年,我踩过的坑能写满一页纸。挑几个最常见的说说:

  • 音画不同步:我曾经拼接两个帧率不同的视频,结果声音越往后越对不上。后来发现是concat滤镜默认用第一个视频的帧率,后面的视频帧率不一致导致。解决方案是先用fps滤镜统一帧率。
  • 黑屏或绿屏:这通常是编码参数不一致导致的。比如一个视频是yuv420p,另一个是yuv422p,拼接时就会出问题。用format=yuv420p滤镜统一像素格式就好。
  • 文件列表乱码:如果文件路径里有中文,记得把文件列表保存为UTF-8编码,并且加上-safe 0参数。
💡 我的经验: 如果你不确定视频参数是否一致,先用ffprobe查看一下:ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,sample_rate -of default=noprint_wrappers=1 clip1.mp4。把几个视频的参数对比一下,再决定用哪种方案。

15.6 总结

视频拼接这事儿,选对方案就成功了一半。参数一致的视频,用concat协议,又快又好;参数不一致的,用concat滤镜,灵活处理。嗯,就这么简单。

最后提醒一句:生产环境中,能用concat协议就别用concat滤镜。重新编码不仅慢,还会损失画质。除非你确实需要统一参数,否则别给自己找麻烦。


公众号:蓝海资料掘金营,微信deep3321