Java 程序调用 FFmpeg 教程

在 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)
跨平台支持 检测操作系统,自动选择 ffmpegffmpeg.exe
资源清理 确保 BufferedReader 正确关闭(当前代码已用 try-with-resources)
异步执行 若在 Web 应用中使用,建议将压缩任务提交到线程池,避免阻塞主线程

五、总结

通过 ProcessBuilder 调用 FFmpeg 是 Java 中实现音视频处理的常用方案。虽然它依赖外部程序,但只要注意路径、流处理和错误判断,就能稳定高效地完成任务。

📌 记住:FFmpeg 强大,但"黑盒"调用需谨慎。建议先在命令行测试命令,再集成到 Java 代码中。

相关推荐
xcl09252 小时前
幼儿托育系统开发实战:从需求分析到上线全流程指南
java·大数据·需求分析
吴声子夜歌2 小时前
ApacheCommons——commons-cli(命令行参数解析)
java·开发语言·apache
小小猪的春天2 小时前
Java 手写第一个 MCP Server:Spring AI MCP 半小时跑通
java·人工智能·spring boot·ai编程
find1star2 小时前
LeetCode 141:环形链表
java·算法·leetcode·链表
今天AI了吗3 小时前
DeepSeek Harness 深度解析:从评测架构到实战落地
java·网络·数据库·人工智能·架构·java-ee
benchmark_cc3 小时前
REST API 和 Python SDK 应该怎么选?量化交易数据接口选型实战
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
许彰午3 小时前
27-AuthService登录链路
java·低代码·架构
泡海椒4 小时前
JQuick-Curl 性能分析:并发场景下的性能表现与调优,第三方接口调用不只要快写也要稳跑
java·开发语言·okhttp
2601_962382434 小时前
python快乐编程网络爬虫课后答案
python·网络爬虫·学习工具·课后答案·刷题app
Rain的Java大神之路5 小时前
JavaWeb开发如何解决跨域问题
java·前端·后端·nginx·web安全·面试·运维开发