如何在Java中稳定调用FFmpeg进行分屏录制而不中断?
Java调用FFmpeg屏幕录制片段异常问题排查与解决
问题描述
尝试在Java中调用FFmpeg进行屏幕录制并分割为1分钟片段,终端运行命令完全正常,但Java调用时最多生成3个片段后停止,FFmpeg进程仍在运行但无输出。停止Java程序后,FFmpeg能恢复正常录制,但丢失停止期间的内容。
原执行代码:
public static void execute(int frameRate, int width, int height, String windowTitle) { String[] args = new String[]{ path, "-hide_banner", "-f", "gdigrab", "-thread_queue_size", "1024", "-rtbufsize", "256M", "-framerate", "" + frameRate, "-offset_x", "0", "-offset_y", "0", "-video_size", "" + width + "x" + height, "-draw_mouse", "1", "-i", "title=" + windowTitle, "-c:v", "libx264", "-r", "" + frameRate, "-preset", "ultrafast", "-tune", "zerolatency", "-crf", "28", "-pix_fmt", "yuv420p", "-movflags", "+faststart", "-y", "-f", "segment", "-reset_timestamps", "1", "-segment_time", "1", "output%06d.mp4" }; try { Process p = Runtime.getRuntime().exec(args); Thread thread = new Thread(() -> { String line; BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream())); System.out.println("TEST"); try { while ((line = input.readLine()) != null) System.out.println(line); System.out.println("Stopping with reading"); input.close(); } catch (IOException e) { e.printStackTrace(); } }); int exitCode = p.waitFor(); thread.start(); thread.join(); if (exitCode != 0) { throw new RuntimeException("FFmpeg exited with code " + exitCode); } } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } }
问题原因分析
- 流处理顺序错误:先调用
p.waitFor()阻塞主线程,再启动读取输入流的线程,导致FFmpeg的输出缓冲区被填满后无法继续写入,进程阻塞。 - 未处理错误流:FFmpeg的日志信息默认输出到**标准错误流(stderr)**而非标准输出流(stdout),原代码只读取stdout,导致错误流缓冲区满也会阻塞进程。
修复后的代码
public static void execute(int frameRate, int width, int height, String windowTitle) { String[] args = new String[]{ path, "-hide_banner", "-f", "gdigrab", "-thread_queue_size", "1024", "-rtbufsize", "256M", "-framerate", "" + frameRate, "-offset_x", "0", "-offset_y", "0", "-video_size", "" + width + "x" + height, "-draw_mouse", "1", "-i", "title=" + windowTitle, "-c:v", "libx264", "-r", "" + frameRate, "-preset", "ultrafast", "-tune", "zerolatency", "-crf", "28", "-pix_fmt", "yuv420p", "-movflags", "+faststart", "-y", "-f", "segment", "-reset_timestamps", "1", "-segment_time", "1", "output%06d.mp4" }; try { Process p = Runtime.getRuntime().exec(args); // 处理标准输出流 Thread stdoutThread = new Thread(() -> { try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()))) { String line; while ((line = input.readLine()) != null) { System.out.println("[STDOUT] " + line); } } catch (IOException e) { e.printStackTrace(); } }); // 处理标准错误流(FFmpeg日志主要输出到这里) Thread stderrThread = new Thread(() -> { try (BufferedReader errorInput = new BufferedReader(new InputStreamReader(p.getErrorStream()))) { String line; while ((line = errorInput.readLine()) != null) { System.out.println("[STDERR] " + line); } } catch (IOException e) { e.printStackTrace(); } }); // 先启动流读取线程,再等待进程结束 stdoutThread.start(); stderrThread.start(); int exitCode = p.waitFor(); // 等待流读取线程完成 stdoutThread.join(); stderrThread.join(); if (exitCode != 0) { throw new RuntimeException("FFmpeg exited with code " + exitCode); } } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } }
关键修复点说明
- 调整线程启动顺序:先启动两个流读取线程,再调用
waitFor(),确保FFmpeg的输出能被及时消费,不会因为缓冲区满而阻塞。 - 新增错误流处理:专门读取FFmpeg的标准错误流,这样就能看到终端里的日志输出,方便排查问题。
- 使用try-with-resources:自动关闭流,避免资源泄漏。
内容的提问来源于stack exchange,提问作者WinnieTheDampoeh
相关产品推荐
相关产品推荐

