拼接与合并
本教程共 32 篇 · 第 14 篇 · 更新于 2026-08-09 · 约 7 分钟阅读
本节目标:知道什么时候用哪种拼接方式,能用 concat demuxer 秒拼同编码文件,也能用 concat filter 拼接不同参数的片段。
把多个视频片段连成一个完整视频,是剪辑里的基础操作。FFmpeg 提供了三种拼接机制,适用场景完全不同,选错了要么报错,要么慢得离谱。
三种方式对比
| 方式 | 是否重编码 | 速度 | 适用场景 |
|---|---|---|---|
| concat demuxer | 否(流复制) | 极快 | 所有片段编码参数完全一致 |
| concat filter | 是 | 慢 | 片段编码/分辨率不同,需要统一 |
| concat protocol | 否 | 极快 | 只支持少数格式(MPEG-TS 等) |
concat demuxer:秒拼(推荐首选)
这是最常用的方式。前提是所有片段的编码、分辨率、帧率、像素格式、时间基准都一致——简单说,就是它们是用同样参数生成的。
步骤一:写清单文件
新建一个文本文件,比如 list.txt,每行列出一个要拼接的文件:
file 'part1.mp4'
file 'part2.mp4'
file 'part3.mp4'
- 文件名用单引号包起来。
- 路径是相对于清单文件所在目录的。
- Windows 下路径可以用正斜杠或双反斜杠,避免单反斜杠被当成转义。
带绝对路径的写法:
file 'D:/videos/part1.mp4'
file 'D:/videos/part2.mp4'
步骤二:执行拼接
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
-f concat:指定使用 concat demuxer。-safe 0:允许清单里写绝对路径。如果都是相对路径可以不加。-c copy:所有流直接复制,不重编码,秒出。
Windows PowerShell 生成清单
如果文件按 part-01.mp4、part-02.mp4 命名好,可以直接生成清单:
# PowerShell 6+ 支持 utf8NoBOM
Get-ChildItem part-*.mp4 | Sort-Object Name | ForEach-Object {
"file '$($_.Name)'"
} | Set-Content list.txt -Encoding utf8NoBOM
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
ImportantWindows PowerShell 5.1 的
Set-Content -Encoding UTF8会写 BOM,FFmpeg 的 concat demuxer 读不了带 BOM 的清单。解决方法:用 PowerShell 7+,或者手动用记事本保存为「UTF-8」(不是带 BOM 的),也可以用[System.IO.File]::WriteAllLines()。
Linux/macOS:
for f in part-*.mp4; do echo "file '$f'"; done > list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
片段参数不一致怎么办
如果片段都是同一种编码器(比如都是 H.264 + AAC)但分辨率或帧率不同,concat demuxer 仍然能拼,但结果可能出现花屏、卡顿、音画不同步。这种情况有两种选择:
- 先统一参数:把每个片段都转成同样的分辨率、帧率、GOP,再用 demuxer 拼。
- 用 concat filter:见下一节,一条命令搞定但要重编码。
concat filter:任意拼接(需重编码)
concat filter 会把所有片段先解码,统一后再重新编码。它不要求片段参数一致——分辨率不同会自动按第一段为准,音频声道数也会对齐。
ffmpeg -i part1.mp4 -i part2.mp4 -i part3.mp4 \
-filter_complex "[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
-c:v libx264 -c:a aac \
output.mp4
拆解:
- 有几个输入就写几个
-i。 [0:v][0:a]表示第 0 个输入的视频和音频,按顺序排列。concat=n=3:v=1:a=1:3 段,每段 1 条视频 1 条音频。[v][a]是输出标签,再用-map取出来。- 最后指定编码器。
只有视频没有音频
ffmpeg -i part1.mp4 -i part2.mp4 \
-filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" \
-map "[v]" -c:v libx264 output.mp4
:a=0 表示没有音频。
片段数量多怎么办
手写一长串 [0:v][0:a][1:v][1:a]... 太痛苦。可以写个脚本自动生成。或者先把所有片段转成统一参数的中间文件,再用 concat demuxer 拼——虽然多了一道工序,但总体可能更快,因为 filter 方式必须全部重编码。
Tip想在片段之间加淡入淡出过渡,用
xfade滤镜,见 §27。
concat protocol:特定格式
concat protocol 是最底层的拼接方式,它直接把文件字节流连在一起,不解析容器。只对某些基于数据包、无文件头的格式有效,比如 MPEG-TS、MPEG-PS。
ffmpeg -i "concat:part1.ts|part2.ts|part3.ts" -c copy output.ts
- 用
|分隔文件名。 - 只对 TS 等少数容器可靠。
- 不要对 MP4、MKV 使用,它们有文件头,直接拼会损坏。
实际用得最多的还是 TS 直播切片的拼接。如果你手上是 MP4 文件,老老实实用 demuxer 或 filter。
常见场景
把手机录的多段视频拼起来
手机录的片段参数通常一致(同一台机器、同一个 App),直接 demuxer:
# list.txt
file 'VID_001.mp4'
file 'VID_002.mp4'
file 'VID_003.mp4'
ffmpeg -f concat -safe 0 -i list.txt -c copy merged.mp4
拼不同来源的视频
一个是 1080p30,一个是 720p24,一个是 4K60:
ffmpeg -i a.mp4 -i b.mp4 -i c.mp4 \
-filter_complex \
"[0:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,fps=30,format=yuv420p[v0]; \
[1:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,fps=30,format=yuv420p[v1]; \
[2:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,fps=30,format=yuv420p[v2]; \
[0:a]aresample=48000,aformat=channel_layouts=stereo[a0]; \
[1:a]aresample=48000,aformat=channel_layouts=stereo[a1]; \
[2:a]aresample=48000,aformat=channel_layouts=stereo[a2]; \
[v0][a0][v1][a1][v2][a2]concat=n=3:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" -c:v libx264 -c:a aac output.mp4
这个比较复杂,核心思路是先用 scale/fps/format 把每段统一到 1920×1080 / 30fps / yuv420p,音频统一到 48kHz 立体声,再 concat。滤镜图的语法在 §23 详细讲。
拼接并无损重编码到统一参数
如果想保持画质但必须统一参数,可以用无损中间格式:
# 每段转成无损中间文件
ffmpeg -i part1.mp4 -c:v ffv1 -c:a pcm_s16le tmp1.mkv
ffmpeg -i part2.mp4 -c:v ffv1 -c:a pcm_s16le tmp2.mkv
# 拼接
ffmpeg -i tmp1.mkv -i tmp2.mkv -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a]" -map "[v]" -map "[a]" -c:v ffv1 -c:a pcm_s16le merged.mkv
# 最后压成 H.264
ffmpeg -i merged.mkv -c:v libx264 -c:a aac final.mp4
中间文件巨大,但二次压缩损失最小。
小结
- 同编码、同参数的片段:用 concat demuxer +
-c copy,秒拼。 - 参数不同的片段:用 concat filter,重编码但能统一。
- TS 等无文件头格式:可以用 concat protocol,其他格式别碰。
- 清单文件用 UTF-8 无 BOM 编码,路径用单引号。
- 拼接出问题大多是因为片段参数不一致,先用 ffprobe 检查每段的编码、分辨率、帧率、采样率。
下一章做个有趣的:视频和 GIF 互相转换。