You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Runtime.exec()执行Windows控制台可用的FFmpeg音频替换命令时冻结

解决Java调用FFmpeg合并视频时进程冻结的问题

问题根源

代码冻结的核心原因是子进程的标准输出/错误流缓冲区被填满,导致FFmpeg进程阻塞,进而Java的waitFor()永久等待:

  • 音频合并命令的FFmpeg输出日志量小,缓冲区未被填满,所以能正常执行;但视频合并的日志输出多,很快撑满缓冲区,FFmpeg会暂停执行等待缓冲区释放,Java代码也就跟着卡住了。
  • 改用命令数组确实解决了路径带空格的问题,但没触碰到流缓冲区阻塞的核心矛盾。

解决方案

必须主动处理FFmpeg进程的stdout和stderr,可以通过单独开启线程读取这两个流,或者直接将输出重定向到文件/空设备。推荐使用ProcessBuilder替代Runtime.exec,它的API更清晰灵活。

修正后的代码示例

1. 改进FFmpeg执行方法

protected void runFFMPEG(Multimap<String,String> args, String outputFile, String fileFormat) throws IOException, InterruptedException {
    ProcessBuilder pb = new ProcessBuilder(setCommand(args, outputFile, fileFormat));
    
    // 可选:将输出重定向到文件,方便调试排查问题
    // pb.redirectError(new File("ffmpeg_error.log"));
    // pb.redirectOutput(new File("ffmpeg_output.log"));
    
    Process process = pb.start();

    // 开启线程读取stdout,避免缓冲区阻塞
    new Thread(() -> {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                // 可根据需求打印日志或写入文件
                System.out.println("[FFmpeg stdout] " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }).start();

    // 开启线程读取stderr,避免缓冲区阻塞
    new Thread(() -> {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                // 可根据需求打印日志或写入文件
                System.err.println("[FFmpeg stderr] " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }).start();

    // 等待进程执行完成
    int exitCode = process.waitFor();
    System.out.println("FFmpeg执行完成,退出码:" + exitCode);
}

2. 保留原命令生成方法(已验证正确可继续使用)

private String[] setCommand(Multimap<String, String> args, String outputFile, String fileFormat) {
    String[] command = new String[args.size()*2+2];
    command[0] = ffmpegLocation;
    int[] i = new int[1];
    i[0] = 1;
    args.forEach((key, value) -> { 
        command[i[0]++] = key;
        command[i[0]++] = value;
    });
    command[i[0]] = outputFile + fileFormat;
    return command;
}

额外注意事项

  • 如果不需要查看FFmpeg的日志,可以直接将输出重定向到空设备,简化代码(Windows系统用NUL,Linux/macOS用/dev/null):
    pb.redirectError(ProcessBuilder.Redirect.to(new File("NUL")));
    pb.redirectOutput(ProcessBuilder.Redirect.to(new File("NUL")));
    
  • 后续添加ASS字幕和PNG图片的命令,只要生成格式正确,同样适用此方法,不会再出现冻结问题。

内容的提问来源于stack exchange,提问作者Finn Andre Worm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:05:34