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

Java调用whisper.cpp读取InputStream陷入无限循环问题排查

问题解答

1. 读取输出最后一行的更好方式

不要定时去读取流,而是单独开一个后台线程持续监听whisper.cpp的stdout输出,把每一行转录文本存入线程安全的缓存容器(比如ConcurrentLinkedQueue<String>或者CopyOnWriteArrayList<String>)。需要获取最后一行时,直接从容器中取最新的元素即可。这种方式既不会阻塞主线程,也能实时捕获所有输出。

2. reader.readLine()永远不为null的原因

whisper.cpp的examples/stream是持续运行的流式转录进程,只要进程没被终止,它的stdout流会一直处于打开状态,用于持续输出实时转录的文本。readLine()方法只有当流被关闭(比如进程退出)时才会返回null,否则会一直阻塞等待新的输出内容,所以你的代码里它永远不会返回null,导致循环停不下来。

3. 代码的错误点

  • Checker的设计逻辑错误:你用定时任务每5秒调用checkText,但checkText里直接调用reader.readLine()——这个方法会一直阻塞,直到有新输出或者流关闭。因为whisper.cpp进程一直在运行,所以readLine()永远不会返回null,导致这个Runnable一直卡在这一行,形成无限循环。
  • 忽略了stderr的读取:如果不读取whisper.cpp的stderr输出,当进程的stderr缓冲区满时,进程会被阻塞,无法继续输出stdout内容,这也是常见的坑。
改进方案示例

改进后的Transcriber类

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.ConcurrentLinkedQueue;

public class Transcriber {
    private Process whisperProcess;
    private final ConcurrentLinkedQueue<String> transcriptLines = new ConcurrentLinkedQueue<>();
    private volatile boolean running = false;

    public void start() throws IOException {
        ProcessBuilder pb = new ProcessBuilder("./path/to/stream", "--model", "./models/ggml-base.en.bin");
        // 合并stderr到stdout,或者单独处理stderr
        pb.redirectErrorStream(true);
        whisperProcess = pb.start();
        running = true;

        // 后台线程持续读取输出
        new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(whisperProcess.getInputStream()))) {
                String line;
                while (running && (line = reader.readLine()) != null) {
                    transcriptLines.add(line);
                    // 可选:如果只需要保留最后N行,超出时移除最早的
                    if (transcriptLines.size() > 100) {
                        transcriptLines.poll();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                running = false;
            }
        }).start();
    }

    // 获取最新的转录行
    public String getLastTranscriptLine() {
        return transcriptLines.peekLast();
    }

    public void stop() {
        running = false;
        if (whisperProcess != null) {
            whisperProcess.destroy();
        }
    }
}

改进后的Checker类

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Checker {
    private final Transcriber transcriber;
    private final String targetWord;
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    public Checker(Transcriber transcriber, String targetWord) {
        this.transcriber = transcriber;
        this.targetWord = targetWord;
    }

    public void startChecking() {
        // 每5秒检查一次最新的转录内容
        scheduler.scheduleAtFixedRate(() -> {
            String lastLine = transcriber.getLastTranscriptLine();
            if (lastLine != null && lastLine.contains(targetWord)) {
                System.out.println("检测到目标词汇:" + targetWord);
                // 执行你的业务逻辑
            }
        }, 0, 5, TimeUnit.SECONDS);
    }

    public void stopChecking() {
        scheduler.shutdown();
    }
}

使用示例

public class Main {
    public static void main(String[] args) throws IOException {
        Transcriber transcriber = new Transcriber();
        transcriber.start();

        Checker checker = new Checker(transcriber, "hello");
        checker.startChecking();

        // 程序运行一段时间后停止
        // Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        //     checker.stopChecking();
        //     transcriber.stop();
        // }));
    }
}

内容的提问来源于stack exchange,提问作者Elijah Crum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 18:42:38