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
相关产品推荐
相关产品推荐

