|
| 1 | +# ADR-013: 实时会话引擎预热与播放暂停驱动(预备线程 + 共享暂停标志) |
| 2 | + |
| 3 | +## 状态 |
| 4 | + |
| 5 | +已接受(2026-08-19 用户需求评审:课堂助手三项优化——停止即时/播放暂停驱动/引擎预热) |
| 6 | + |
| 7 | +## 日期 |
| 8 | + |
| 9 | +2026-08-19 |
| 10 | + |
| 11 | +## 背景 |
| 12 | + |
| 13 | +课堂助手实时捕获存在三处体验缺口: |
| 14 | + |
| 15 | +1. **初始化时机**:`StreamingAsrEngine::load`(SenseVoice + 标点恢复,数秒)在点"开始"后的会话线程内执行,启动延迟全部落在用户点击之后。 |
| 16 | +2. **暂停语义**:REQ-125 已能检测视频播放/暂停(暂停图标颜色统计,落 PlayerBehavior 事件),但只落库不驱动捕获——视频暂停时应用仍持续采集/OCR。 |
| 17 | +3. **停止响应**:停止后 drain 宽限 8s 内捕获线程持续喂块,队列永不空,`stop_active` 5s 有界等待必然超时(detach 日志常态出现)。 |
| 18 | + |
| 19 | +约束:`StreamingAsrEngine` 含 FFI 句柄(非 Send),**不能跨线程移交**;同时期 A1 硬暂停(WASAPI 端点 Stop/Start + 时间戳补偿)已落地共享 `SessionPause` 机制。 |
| 20 | + |
| 21 | +## 决策 |
| 22 | + |
| 23 | +1. **引擎预热 = 预备线程 + channel 交接**(不移动引擎): |
| 24 | + - 新命令 `prepare_live_session`(进课堂助手页即调用)起"预备线程":加载引擎 → 置 `Ready` → park 在**原线程**等 Start/Cancel(500ms 轮询,15min TTL)。 |
| 25 | + - `start_live_session` 优先交接:有界等待就绪 ≤5s(防双引擎内存翻倍)→ channel 发 Start 消息(参数/会话 id/停止标志/暂停状态)→ 预备线程就地转为会话线程,**开始毫秒级**;加载失败/等待超时/交接失败 → 取消预备线程并回退内联加载(现状路径,start 永不因预热缺席失败)。 |
| 26 | + - `release_live_prepare`(离开页面调用)有界 join ≤1s;引擎加载中不可中断时 detach(加载完即退出)。 |
| 27 | +2. **播放暂停驱动捕获 = 复用 A1 共享暂停标志**: |
| 28 | + - 屏幕 worker 的 REQ-125 检测在检测到暂停时置 `SessionPause.paused`(首检基线同置,不写假事件)——音频捕获线程/主循环沿既有 A1 边沿执行真实暂停(端点 Stop、不喂 ASR、不写 WAV、时间戳补偿)。 |
| 29 | + - 自动暂停期间 worker 进入**轻量轮询**(区别于手动暂停的全冻结):1s 一拍仅取帧刷新 `latest_frame`(恢复检测信号源)+ 5s 一拍检测;检测到恢复 → 清标志 + 落 Play 事件,链路沿边沿自动恢复。 |
| 30 | + - 暂停边沿的断句隔离由"喂 100ms 静音"(A1 原方案,不足以触发 sherpa 端点规则)改为 **flush 尾句落库 + 引擎 `reset()`**(flush_tail_and_persist 与停止路径共用,防实现漂移;时间戳统一补偿暂停时长)。 |
| 31 | +3. **停止即时**:主循环观察到停止标志时**先 `audio.stop()`**(捕获线程 ≤100ms 退出 → channel 断开),再 drain 残留块(通常 <1s),`Disconnected` 后退出;8s 宽限保留为理论兜底。`stop_live_session` 补 `spawn_blocking`(此前异步 command 直接阻塞运行时 5s)。 |
| 32 | +4. **A1 补漏**:`SessionPause` 按会话复位(`reset()`)——暂停标志/补偿时长跨会话残留会让新会话起始即暂停、时间戳偏移。 |
| 33 | + |
| 34 | +## 备选方案 |
| 35 | + |
| 36 | +### 方案 A:预备线程 + channel 交接(选定) |
| 37 | +- 优点:引擎不跨线程(规避非 Send 约束);预热与开始解耦(模型加载移出点击路径);TTL/释放命令双保险控内存;失败回退内联零回归。 |
| 38 | +- 缺点:预热线程 park 期间占用内存(~数百 MB);交接路径需处理加载中/失败竞态(有界等待 + 回退 + send 失败取回参数)。 |
| 39 | +- 适用场景:引擎加载耗时且生命周期与 UI 流程解耦的场景。 |
| 40 | + |
| 41 | +### 方案 B:预热结果放共享槽、会话线程自取 |
| 42 | +- 缺点:引擎非 Send 无法存入跨线程共享槽(结构上不可行)。 |
| 43 | +- 适用场景:无。 |
| 44 | + |
| 45 | +### 方案 C:自动暂停独立实现(自有标志 + 循环内丢块) |
| 46 | +- 优点:与 A1 硬暂停解耦。 |
| 47 | +- 缺点:重复实现暂停机制(WAV 间隙/时间戳补偿/事件三处漂移);A1 已有真实端点停采+补偿,叠加自实现违背"复用存量"原则。 |
| 48 | +- 适用场景:无(A1 机制已存在,直接驱动更优)。 |
| 49 | + |
| 50 | +## 选择理由 |
| 51 | + |
| 52 | +- 引擎非 Send 是硬约束——方案 A 是唯一能"提前加载 + 线程内使用"的结构。 |
| 53 | +- 播放暂停自动化的成本集中在检测端(worker 轻量轮询),驱动端完全复用 A1 已验证的硬暂停(端点 Stop/Start 无重连风险、时间戳补偿无跳跃)。 |
| 54 | +- 停止即时化改动最小(一处 `audio.stop()` 前置),同时消除 detach 常态日志并修复 stop 阻塞运行时线程的问题。 |
| 55 | + |
| 56 | +## 影响 |
| 57 | + |
| 58 | +### 正面影响 |
| 59 | +- 点"开始"到录制开始从数秒降至毫秒级(预热就绪时)。 |
| 60 | +- 视频暂停自动停采(省 OCR/磁盘/静音音频),恢复自动续采,时间轴无跳跃。 |
| 61 | +- 停止响应 <1s,detach 日志仅真卡死时出现;暂停前后句子不连句。 |
| 62 | + |
| 63 | +### 负面影响 / 代价 |
| 64 | +- 预热引擎常驻内存(~数百 MB),离开页面/TTL 才释放——受 15min TTL 与页面卸载释放约束。 |
| 65 | +- 自动暂停依赖暂停图标可见(窗口前台、播放器显示图标);遮挡/最小化可能漏检或误判恢复(沿用 REQ-125 保守检测的既有局限)。 |
| 66 | +- 预热后词表变更在首个端点时生效(现状语义,无回归)。 |
| 67 | + |
| 68 | +### 风险 |
| 69 | +- 交接竞态(预备线程在 send 前退出):已用 SendError 取回参数回退内联 + 防御性报错兜底。 |
| 70 | +- 手动恢复时视频仍暂停 → worker 兜底重新自动暂停(语义:捕获跟随视频状态;用户可再次手动继续,5s 内复判)。 |
| 71 | +- 暂停边沿 flush 含 SenseVoice 重打分(有界 3s)——暂停/停止时主循环短暂阻塞,捕获已停无积压,可接受。 |
| 72 | + |
| 73 | +## 合规性验证 |
| 74 | + |
| 75 | +- `cargo test` 全绿(936 例);`cargo check --all-targets` 无新增告警。 |
| 76 | +- 前端 `npm run build`(tsc + vite)通过。 |
| 77 | +- 验收路径(真机):① 预热就绪后点开始 → `live:status recording` 毫秒级;② 视频暂停 → 5s 内 `live:paused` + 端点停采 + 屏幕零分析;恢复 → 自动续采、时间轴连续;③ 停止 → <1.5s 返回、无 detach 日志。 |
| 78 | +- 手动暂停/自动暂停共用同一标志与事件,前端按钮与徽标零改动复用。 |
| 79 | + |
| 80 | +## 相关决策 |
| 81 | + |
| 82 | +- ADR-007: live-session-lifecycle(会话线程编排;本 ADR 在其上加预备交接) |
| 83 | +- ADR-008: 会话纪元统一(A1 时间戳;预备交接的纪元在移交后创建,无偏移) |
| 84 | +- ADR-012: 流式 ASR 质量修复(F1-2 flush 重打分兜底——flush_tail_and_persist 复用) |
| 85 | +- 2026-08 A1 硬暂停设计(docs/archive/2026-08-19/brainstorming-classroom-homepage-controls.md) |
| 86 | + |
| 87 | +## 参考 |
| 88 | + |
| 89 | +- `docs/superpowers/specs/2026-08-19-classroom-live-optimizations-design.md`(本批设计规格) |
| 90 | +- `docs/standards/line-limit-exemptions.md`(相关文件行数登记) |
0 commit comments