在 Java 应用中集成视频处理功能时,FFmpeg 是一个强大而灵活的选择。虽然 Java 本身没有内置音视频编解码能力,但我们可以通过调用系统命令的方式,借助 FFmpeg 实现转码、压缩、裁剪等操作。
核心原理 :Java 通过
ProcessBuilder启动外部进程(如ffmpeg.exe),并传递命令行参数来执行具体任务。这本质上是一种"命令行调用",但通过 Java 封装后,可以无缝集成到应用程序中。
一、为什么使用 ProcessBuilder?
ProcessBuilder 是 Java 标准库(java.lang 包)提供的用于创建和管理操作系统进程的工具类。相比旧的 Runtime.exec(),它具有以下优势:
- 更清晰的 API 设计
- 支持设置工作目录、环境变量
- 更安全地处理输入/输出流
- 避免常见的死锁问题(需正确读取 stdout/stderr)
二、示例:用 Java 压缩视频
下面是一个完整的示例,展示如何使用 Java 调用 FFmpeg 对视频进行压缩:
java
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
public class FfmpegCompressor {
/**
* 使用 FFmpeg 压缩视频
*
* @param inputPath 输入视频路径(相对或绝对)
* @param outputPath 输出视频路径
* @param videoBitrate 视频目标码率(如 "3M" 表示 3 Mbps)
* @throws IOException 启动进程或读取流失败
* @throws InterruptedException 进程被中断
*/
public static void compressVideo(String inputPath, String outputPath, String videoBitrate)
throws IOException, InterruptedException {
// 务必指定 ffmpeg 可执行文件的完整路径(不是安装目录!)
String ffmpegPath = "D:\\Develop\\ffmpeg-8.0.1-essentials_build\\bin\\ffmpeg.exe";
ProcessBuilder pb = new ProcessBuilder(
ffmpegPath,
"-i", inputPath,
"-b:v", videoBitrate,
"-bufsize", videoBitrate,
"-c:a", "aac", // 音频编码为 AAC
"-y", // 覆盖输出文件(不提示)
outputPath
);
// 设置工作目录(建议设为视频所在目录,避免路径问题)
pb.directory(new File("C:\\Users\\x\\Videos\\原画"));
System.out.println("正在启动 FFmpeg...");
Process process = pb.start();
// 异步读取 stdout 和 stderr,防止缓冲区阻塞导致死锁
Thread outThread = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
reader.lines().forEach(line -> System.out.println("[FFmpeg OUT] " + line));
} catch (IOException e) {
System.err.println("读取 FFmpeg 标准输出失败: " + e.getMessage());
}
});
Thread errThread = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
reader.lines().forEach(line -> System.err.println("[FFmpeg LOG] " + line));
} catch (IOException e) {
System.err.println("读取 FFmpeg 错误流失败: " + e.getMessage());
}
});
outThread.start();
errThread.start();
// 等待进程结束
int exitCode = process.waitFor();
outThread.join();
errThread.join();
if (exitCode == 0) {
System.out.println("视频压缩成功: " + outputPath);
} else {
System.err.println("FFmpeg 执行失败,退出码: " + exitCode);
throw new RuntimeException("视频压缩失败,请检查输入路径或 FFmpeg 配置");
}
}
public static void main(String[] args) {
try {
compressVideo("1.mp4", "1-compressed.mp4", "3M");
} catch (Exception e) {
System.err.println("程序异常: " + e.getMessage());
e.printStackTrace();
}
}
}
三、关键注意事项
1. ffmpegPath 必须指向 ffmpeg.exe 文件
- 错误:
D:\Develop\ffmpeg-8.0.1-essentials_build - 正确:
D:\Develop\ffmpeg-8.0.1-essentials_build\bin\ffmpeg.exe
2. 工作目录(Working Directory)建议设置
- 若输入/输出路径为相对路径,
pb.directory(...)决定了这些路径的基准。 - 推荐设为视频所在目录,避免路径解析错误。
3. FFmpeg 的日志输出在 stderr
⚠️ 注意:
[FFmpeg ERR]并不表示"错误"!
FFmpeg 遵循 Unix 哲学:
stdout:仅用于真正的数据输出(如-f image2pipe输出图像流)stderr:输出所有日志、进度、警告、统计信息(包括成功信息)
因此,你看到的 [FFmpeg ERR]是正常运行日志。

四、进阶建议
| 方向 | 建议 |
|---|---|
| 路径配置 | 将 ffmpegPath 提取为配置项(如 properties 文件或环境变量),避免硬编码 |
| 异常处理 | 捕获更细粒度的异常,记录日志(如使用 SLF4J) |
| 跨平台支持 | 检测操作系统,自动选择 ffmpeg 或 ffmpeg.exe |
| 资源清理 | 确保 BufferedReader 正确关闭(当前代码已用 try-with-resources) |
| 异步执行 | 若在 Web 应用中使用,建议将压缩任务提交到线程池,避免阻塞主线程 |
五、总结
通过 ProcessBuilder 调用 FFmpeg 是 Java 中实现音视频处理的常用方案。虽然它依赖外部程序,但只要注意路径、流处理和错误判断,就能稳定高效地完成任务。
📌 记住:FFmpeg 强大,但"黑盒"调用需谨慎。建议先在命令行测试命令,再集成到 Java 代码中。