首页 / FFmpeg 入门教程 / 拼接与合并

FFmpeg 入门教程

拼接与合并

本教程共 32 篇 · 第 14 篇 · 更新于 2026-08-09 · 约 7 分钟阅读

FFmpegconcat拼接合并concat demuxerconcat filterconcat protocol

本节目标:知道什么时候用哪种拼接方式,能用 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.mp4part-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
Important

Windows 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 仍然能拼,但结果可能出现花屏、卡顿、音画不同步。这种情况有两种选择:

  1. 先统一参数:把每个片段都转成同样的分辨率、帧率、GOP,再用 demuxer 拼。
  2. 用 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 互相转换。