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

如何在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);
      }
  }

问题原因分析

  1. 流处理顺序错误:先调用p.waitFor()阻塞主线程,再启动读取输入流的线程,导致FFmpeg的输出缓冲区被填满后无法继续写入,进程阻塞。
  2. 未处理错误流: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:43:15