文章目录
- [一、FFmpeg 的 concat 合并机制](#一、FFmpeg 的 concat 合并机制)
- [二、核心 Java 代码示例](#二、核心 Java 代码示例)
- 三、代码详解
- 四、大小写混合或排序问题
- 五、何时该用这个合并方案?
- 六、总结
在很多视频处理场景中,我们会将一个录制任务拆成多个小片段(如 seg1.mp4、 seg2.mp4、 seg3.mp4 等)。多个分片合并成一个完整视频是一个常见需求。
Java 调用 FFmpeg 生成用于合并的视频列表文件 list.txt,并执行合并命令生成最终视频。
一、FFmpeg 的 concat 合并机制
FFmpeg 支持一种简洁的合并方式 --- concat demuxer,它要求:
✔ 所有分片编码格式一致
✔ 生成的合并列表文件每行格式:
file 'path/to/seg1.mp4'
file 'path/to/seg2.mp4'
file 'path/to/seg3.mp4'
然后调用:
bash
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
即可将多个文件无损合并成一个文件。([简书][1])
实现思路
实现分为三步:
- 生成合并列表文件(list.txt)
- 调用 FFmpeg 进行合并
- 输出合并结果
二、核心 Java 代码示例
下面是一个完整的 Java 类示例,通过 ProcessBuilder 调用 FFmpeg 完成合并。
java
package com.donglin.skyzlm.event;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
public class MergeVideos {
// ffmpeg 可执行命令(系统 PATH 中必须能执行 ffmpeg)
private static final String FFMPEG = "ffmpeg";
public static void main(String[] args) {
// 1)分片视频列表
List<String> clips = List.of(
"seg1.mp4",
"seg2.mp4",
"seg3.mp4"
);
// 2)生成合并列表文件
String listFilePath = "videos/list.txt";
try {
createConcatListFile(clips, listFilePath);
} catch (IOException e) {
System.err.println("生成 list.txt 失败: " + e.getMessage());
return;
}
// 3)合并输出
String output = "videos/merged.mp4";
boolean success = mergeWithFFmpeg(listFilePath, output);
System.out.println("合并 " + (success ? "成功" : "失败") + ", 输出:" + output);
}
/**
* 生成 concat 的 list.txt
*/
private static void createConcatListFile(List<String> clips, String listFilePath) throws IOException {
File file = new File(listFilePath);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
for (String clip : clips) {
// FFmpeg concat list.txt 格式:file '路径'
writer.write("file '" + clip.replace("\\", "/") + "'");
writer.newLine();
}
}
System.out.println("生成 list.txt: " + file.getAbsolutePath());
}
/**
* 调用 FFmpeg concat 合并
*/
private static boolean mergeWithFFmpeg(String listFilePath, String outputPath) {
try {
ProcessBuilder pb = new ProcessBuilder(
FFMPEG,
"-f", "concat",
"-safe", "0",
"-i", listFilePath,
"-fflags", "+genpts",
"-c:v", "libx264", // 可根据需要调整 codec
"-preset", "veryfast",
"-crf", "20",
"-c:a", "aac",
"-b:a", "128k",
"-movflags", "+faststart",
outputPath
);
// 合并 stdout 和 stderr 输出
pb.redirectErrorStream(true);
Process process = pb.start();
// 打印 FFmpeg 输出(可选)
new Thread(() -> {
try (var is = process.getInputStream()) {
is.transferTo(System.out);
} catch (IOException ignored) {}
}).start();
int exit = process.waitFor();
return exit == 0;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return false;
}
}
}

三、代码详解
1. 生成合并列表文件
java
writer.write("file '" + clip.replace("\\", "/") + "'");
- FFmpeg 要求每行以
file '...'开头 - 用单引号包裹路径
- 替换反斜杠为正斜杠,适配不同 OS 路径规则
这样生成的文件示例可能是:
file 'seg1.mp4'
file 'seg2.mp4'
file 'seg3.mp4'
2. 调用 FFmpeg 合并
bash
ffmpeg -f concat -safe 0 -i list.txt -c:v libx264 -c:a aac ...
这里:
✔ -f concat:指定使用 concat demuxer
✔ -safe 0:允许使用任意路径
✔ -c copy:如果不转码可以直接复制,他们保持原始编码(若需要可自己调整)
✔ -movflags +faststart:让视频可以在网络上边下边播
注意:如果分片编码、分辨率、帧率一致,使用
-c copy是更快/无损的方法。
四、大小写混合或排序问题
如果你的分片文件名不是按顺序排列,FFmpeg 会按照文本列表顺序合并。因此 list.txt 必须:
🟢 按你想合并的顺序写入每个路径
🟢 不要漏掉任何分片项
五、何时该用这个合并方案?
适合场景:
✔ 多个 MP4 分片按时间顺序拼接
✔ 文件编码一致(H.264 + AAC)
✔ 希望无损合并
✔ 使用 Java 批量处理视频任务
六、总结
🔹 FFmpeg 程序必须已安装且可在系统 PATH 中找到
🔹 分片视频参数必须一致(否则会导致合并失败或输出异常)
🔹 如果分片太多,建议动态生成 list.txt 并逐步合并