Skip to content

Latest commit

 

History

History
122 lines (88 loc) · 8.13 KB

File metadata and controls

122 lines (88 loc) · 8.13 KB

ADR-008: 文件导入与字幕优先转写方案(含采集链路质量优化)

状态

已接受(Accepted,2026-08-18)

日期

2026-08-18

背景

v0.3.0 需要打开"视频文件导入"第二入口(REQ-015:ffmpeg 提取音轨 + 关键帧)与字幕优先转写(REQ-016/017/018:外挂/内嵌字幕探测,命中则免 ASR 零成本 100% 准确)。同时 课堂采集链路技术审查([ ] 已归档) 发现 3 个真实缺陷(时间戳三基准不一致、ASR 句起时间戳近似、引擎池串行阻塞)与 3 个可即取优化(头脑风暴 T2 多帧投票 / P4 OCR 输入缩小 / P3 语音画面互斥调度简化版)。

约束:

  • 本地优先:字幕解析/提取必须纯本地;AI 仅作可选增强(本版无 AI)
  • 环境限制:本机无 ffmpeg;存在 TLS 拦截(rustls 下载失败、PowerShell 走 Windows 证书库正常)——预编译二进制下载须走 PowerShell 脚本(与 sherpa-archive/ort 同方案)
  • 不新增第三方 crate(在线依赖风险);GBK 解码复用已有 windows crate(补 Win32_Globalization feature)
  • 数据模型变更最小化:本版不迁移 sessions 表(融合状态用内存标记)

决策

1. ffmpeg 分发:捆绑二进制优先 + PATH 探测回退

  • 解析顺序:ENTROPY_FFMPEG_DIR 环境变量(测试注入)→ 捆绑目录 src-tauri/ffmpeg/(ffmpeg.exe + ffprobe.exe,gitignore 不入库)→ 系统 PATH
  • 捆绑下载:scripts/download-ffmpeg.ps1(PowerShell Invoke-WebRequest,绕 TLS 拦截;用户手动执行,与模型下载脚本同模式)
  • 缺失降级:音轨提取/关键帧/内嵌字幕轨(L2)报可操作错误(引导下载或安装);外挂字幕(L1)纯文本解析不依赖 ffmpeg,始终可用
  • 子进程调用安全:命令参数全走 Command 数组(不经 shell,防注入);有界等待(超时 kill);输出目录限定应用数据目录

2. 字幕探测与优先策略(L1 → L2 → ASR 三级降级)

  • L1 外挂字幕:视频同目录同名 .srt/.ass/.vtt 探测(多命中按扩展名优先级 vtt>srt>ass 取一);编码探测:UTF-8 严格解码成功即用,失败回退 GBK(CP936,Windows API 解码);纯文本解析为 SubtitleSegment(毫秒时间轴)
  • L2 内嵌字幕轨:ffprobe -select_streams s 探测 → 命中则 ffmpeg -map 0:s:0 -f srt - 解出为标准 SRT 流 → 复用 L1 解析器;ffmpeg 缺失时报可操作错误
  • 优先策略:L1 命中 → 字幕直出转写(免 ASR);否则 L2;再否则 ASR fallback(分窗转写,见决策 4);关键帧 OCR 始终执行(画面要点与转写独立)
  • 混合模式(字幕 + ASR 同存时融合)由既有 merge_transcript 支持,本版不主动混跑(命中即免 ASR,REQ-016 验收口径)

3. 融合状态标记:内存方案(不迁移表)

  • REQ-031 异步融合的"融合中"状态由 LiveSessionManager 内存 HashSet<i64> 维护,不加 sessions 表字段
  • 理由:融合任务随应用运行期存在,跨重启无意义;ADR-006 派生表(V1.0)落地时自然取代,避免本版 schema 迁移
  • 事件契约:session:fusing(开始)→ session:fused(成功)→ session:fusion-failed(失败,原段保留)

4. 采集链路质量优化(技术审查 A/B1-B3 档,本版并入)

项 方案
A1 时间戳统一 会话纪元 epoch: Instant 在 run_session 起点创建,注入音频捕获线程/屏幕 worker/flush 三处,全部 epoch.elapsed()
A2 句起时间戳 编排层跟踪 Final 后首个非静音块时间戳作为句起,替代 end-2000ms 近似
A3 引擎池拆分 EnginePool 内部拆 ASR/OCR 双 worker 线程(各持单一模型,API 不变,请求并行)
A4 分窗转写 导入音频按 30s 窗切分逐窗离线转写,产真实时间戳段 + 进度事件
B1 多帧投票(T2) SubtitleTracker 改造为样本累积 + 字符级多数投票,字幕切换/停止时输出校正文本(替换"编辑距离 ≤1 合并跳过"语义)
B2 OCR 输入缩小(P4) 字幕裁剪区宽 >960px 时最近邻缩小后再送 OCR
B3 语音/画面互斥(P3 简化) 会话线程按 RMS 写共享 speech_active 标志;静音期全帧采样提频(每 2 tick),语音期维持字幕区为主

备选方案

方案 A:ffmpeg 走系统安装(用户自装 + PATH)

  • 优点:实现最简单
  • 缺点:目标用户(技能自学者)多数不装 ffmpeg,导入功能不可用即失败;与"开箱即用"产品承诺冲突
  • 决策:否决,采用捆绑 + 探测双路径

方案 B:纯 Rust 音视频解复用(symphonia 等)

  • 优点:零外部二进制
  • 缺点:无关键帧提取/内嵌字幕能力;新增依赖在 TLS 拦截环境有下载风险;能力与 REQ-015/017 验收不符
  • 决策:否决(未来若捆绑体积成为问题再评估)

方案 C:融合状态落库(sessions 表加字段)

  • 优点:跨重启可见
  • 缺点:schema 迁移成本;语义与 ADR-006 派生表重叠;V1.0 即废弃
  • 决策:否决,内存标记足够(本版事件驱动刷新)

方案 D:字幕去重保持现状(不投票)

  • 优点:零改动
  • 缺点:权威源单帧错误无法纠正,融合/笔记全链路污染
  • 决策:否决(T2 收益明确且纯逻辑可单测)

选择理由

  • ffmpeg 双路径(捆绑+PATH)在"开箱即用"与"体积/分发可控"间取平衡;L1 纯文本解析保证无 ffmpeg 时核心字幕能力仍可用(本地优先降级原则)
  • 字幕优先三级降级是市场主流方案(通义听悟等)且成本为零(命中免 ASR)
  • 内存融合标记避免本版 schema 变更,与 ADR-006 演进路径一致
  • 技术审查 A/B1-B3 七项优化全部低风险可单测,直接改善权威源质量与实时链路性能,符合"自底向上、小步快跑"节奏

影响

正面影响

  • 视频导入成为实时捕获之后的第二入口;有字幕视频零成本 100% 准确转写
  • 时间轴统一后融合 gap 判断与详情页对齐准确(历史会话不追溯)
  • 引擎池并行后实时链路 OCR 不再阻塞重打分;导入长转写不阻塞实时 OCR
  • 字幕 OCR 质量提升(投票纠错)+ 性能提升(输入缩小)

负面影响 / 代价

  • 捆绑 ffmpeg 需新增下载脚本与 gitignore 条目(体积 ~80MB,用户按需执行)
  • T2 投票延迟字幕段落库最多一个去重窗(3s)——实时落库语义弱化为近实时
  • A1 统一纪元涉及 audio_loopback / dxgi 采样 / live_session 三处签名变更(测试面扩大)

风险

  • ffmpeg 静态版下载源稳定性(BtbN/gyan.dev)——脚本内置多镜像回退
  • GBK 解码仅 Windows 生效(本应用 Windows-only,无影响)
  • 静音误判(低音量课程)→ 全帧提频成本:由变化检测兜底,成本可控

合规性验证

  • cargo test:字幕三格式解析、编码探测(UTF-8/GBK)、投票函数、调度器自适应、命令构建(防注入)、分窗规划、无字幕短路、融合状态流转
  • 无 ffmpeg 机器:L1 字幕导入可用;音轨提取报可操作错误
  • 有字幕视频导入:字幕直出转写(source=subtitle),免 ASR(日志验证)
  • 无字幕视频导入:分窗 ASR 段带真实时间戳(无 -2000ms 近似)
  • 实时会话停止:停止 <100ms 返回;无字幕会话短路(日志验证);有字幕会话 session:fused 后详情刷新为融合轴
  • 导入进行中实时捕获不卡顿(引擎池并行验证)

相关决策

  • ADR-001/002/003/004/005/007: 实时捕获链路既有决策(本 ADR 在其上扩展文件链路与质量优化)
  • ADR-006: 派生视图(V1.0;融合状态内存标记届时被派生表取代)
  • REQ-015/016/017/018/031/034: 需求池(v0.3.0)

参考