Skip to content

[windows] 录音器异常停止后触发 ASR 超时,导致胶囊无响应 #238

@Cooper-X-Oak

Description

@Cooper-X-Oak

问题现象

Windows 平台录音时,如果录音器异常停止,超过 8 秒没有音频数据上传,会触发 ASR 服务超时机制,导致胶囊卡在 processing 状态无响应。

用户界面表现

  • 胶囊显示蓝色加载点点(processing 状态)
  • ✕ 和 ✓ 按钮显示但无响应
  • 无错误提示,用户不知道发生了什么
  • 需要手动取消或重启应用才能恢复

错误日志

2026-05-04T08:01:31.974029Z [ERROR] [asr] error frame code=45000081 
body={"error":"[Timeout waiting next packet] waiting next packet timeout: 8.000000 seconds, session has ended"}

复现步骤

  1. 启动 OpenLess
  2. 按录音键开始录音
  3. 录音器异常停止(麦克风被占用、设备断开、CPAL 崩溃等)
  4. ASR 服务等待 8 秒后超时
  5. 观察胶囊状态:卡在 processing,无法恢复

时间线分析

08:01:21 - 用户按下录音键,开始录音
08:01:22 - ASR 连接成功,开始发送音频
08:01:22 - 录音回调 #1(第一个回调)
08:01:23 - 录音回调 #50(最后一个回调,仅 0.5 秒)
         - 录音器异常停止,无更多回调
08:01:31 - ASR 超时错误(8 秒无数据后)
之后    - 无任何日志,胶囊卡在 processing 状态

根因分析

1. 录音器异常停止(根本原因)

  • 录音开始后只有 50 个回调(约 0.5 秒)
  • 之后录音器完全停止,没有任何回调或错误日志
  • 这不是用户闭麦,而是录音器静默失败

可能的原因:

  • 麦克风设备被其他应用占用
  • 音频设备断开或驱动问题
  • CPAL 音频库回调线程崩溃
  • Windows 音频服务中断
  • 麦克风权限被撤销

2. ASR 服务超时机制(触发因素)

  • Volcengine ASR 是流式识别,要求持续的音频流
  • 如果 8 秒内没有收到新的音频包,服务端认为会话结束
  • 发送错误帧 code=45000081 并关闭连接
  • 这是 ASR provider 的正常机制,不是 bug

3. 错误处理机制缺失(导致无响应)

  • ASR 错误通过 signal_error() 发送到 final_tx
  • 但 coordinator 没有正确处理这个错误
  • 胶囊状态机没有错误恢复分支
  • 前端没有收到状态更新,继续显示 processing
  • 结果:胶囊卡住,用户界面无响应

因果链

录音器异常停止 (0.5秒后)
    ↓
没有音频数据发送到 ASR
    ↓
ASR 等待 8 秒无数据
    ↓
ASR 超时并结束会话
    ↓
错误处理机制缺失
    ↓
胶囊卡在 processing 状态

技术细节

录音器停止的证据

08:01:22.801 - cb#1   (第一个回调)
08:01:23.291 - cb#50  (第 50 个回调,最后一个)
之后无任何回调

当前错误处理流程

// src-tauri/src/asr/volcengine.rs:436
self.signal_error(VolcengineASRError::ConnectionFailed(...));
self.state.lock().is_connected = false;
*self.audio_tx.lock() = None;
// 但没有通知 coordinator 恢复状态

缺失的保护机制

  • ❌ 没有检测录音器是否还在运行
  • ❌ 没有录音器健康检查(如 2 秒无回调则报警)
  • ❌ 没有客户端超时保护(不依赖 ASR 服务端)
  • ❌ 没有错误恢复机制(自动回到 idle)

影响范围

  • 用户体验:严重影响,用户不知道发生了什么,只能重启
  • 平台:Windows 平台(macOS/Linux 可能也有类似问题)
  • 频率:偶发,但一旦发生就无法自动恢复
  • 触发条件:任何导致录音器停止的情况

建议修复

1. 录音器健康检查

// 检测录音回调是否停止
let last_callback_time = Arc::new(Mutex::new(Instant::now()));

// 在回调中更新时间
*last_callback_time.lock() = Instant::now();

// 定期检查(每 2 秒)
tokio::spawn(async move {
    loop {
        tokio::time::sleep(Duration::from_secs(2)).await;
        if last_callback_time.lock().elapsed() > Duration::from_secs(2) {
            log::error!("录音器停止响应,自动取消");
            // 取消录音,恢复到 idle
        }
    }
});

2. 客户端超时保护

// 不依赖 ASR 服务端超时,客户端主动检测
const RECORDING_TIMEOUT: Duration = Duration::from_secs(60);
const PROCESSING_TIMEOUT: Duration = Duration::from_secs(30);
const RECORDER_HEALTH_CHECK: Duration = Duration::from_secs(2);

3. 完善错误处理

// ASR 错误后自动恢复到 idle 状态
match asr_result {
    Err(e) => {
        log::error!("ASR error: {}", e);
        // 清理状态
        self.stop_recording();
        // 通知前端显示错误
        self.emit_capsule_state(CapsuleState::Error { 
            message: "识别超时,请重试".to_string() 
        });
        // 3 秒后自动恢复到 idle
        tokio::time::sleep(Duration::from_secs(3)).await;
        self.emit_capsule_state(CapsuleState::Idle);
    }
}

4. 录音器错误捕获

// 在 CPAL 回调中捕获错误
let error_callback = move |err| {
    log::error!("录音器错误: {}", err);
    // 通知 coordinator
};

临时解决方案

用户遇到此问题时:

  1. 点击 ✕ 按钮尝试取消(可能无效)
  2. 重启应用
  3. 检查麦克风是否被其他应用占用
  4. 检查麦克风权限设置

相关代码

  • src-tauri/src/recorder.rs - 录音器实现
  • src-tauri/src/asr/volcengine.rs:428-442 - ASR 错误处理
  • src-tauri/src/coordinator.rs - 录音状态管理
  • src/components/Capsule.tsx - 胶囊状态显示

环境

  • 平台:Windows 11
  • 版本:1.2.13
  • ASR 服务:Volcengine

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions