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 代码中。

相关推荐
李慕婉学姐2 小时前
【开题答辩过程】以《基于Java的周边游优选推荐网站的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言
计算机毕设指导62 小时前
基于微信小程序民宿预订管理系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven
学习的学习者2 小时前
CS课程项目设计22:基于Transformer的智能机器翻译算法
人工智能·python·深度学习·transformer·机器翻译
我命由我123452 小时前
Kotlin 开发 - Kotlin Lambda 表达式返回值
android·java·开发语言·java-ee·kotlin·android studio·android-studio
Knight_AL2 小时前
从单例模式说起:Java 常见设计模式的理解与实践
java·单例模式·设计模式
小陈phd2 小时前
langGraph从入门到精通(四)——基于LangGraph的State状态模式设计
python·microsoft·状态模式
a努力。2 小时前
中国电网Java面试被问:Dubbo的服务目录和路由链实现
java·开发语言·jvm·后端·面试·职场和发展·dubbo
爬山算法2 小时前
Hibernate(42)在Hibernate中如何实现分页?
java·后端·hibernate
不平衡的叉叉树2 小时前
我们遇到了正则表达式的灾难性回溯问题
java·正则表达式