浏览器本地语音转写的兼容性实战
作者复盘用 transformers.js、Whisper 和 ffmpeg.wasm 构建本地转写工具时遇到的跨设备问题。素材列出不同模型在 WASM 与 WebGPU 下的下载体积,并说明缺少 COOP/COEP 头会让 WASM 只能单线程运行。
AI 深度解读
Whisper 在自己的电脑上跑通,离一个别人能用的浏览器转录工具还有多远?作者遇到的麻烦集中在模型之外:大视频撑爆内存,手机 GPU 中途崩溃,下载耗尽超时预算,还有用户等不到结果就离开。
这篇实战记录的结论很明确:端侧 AI 的可用性,取决于文件处理、推理后端、失败恢复和等待体验能否配合起来。它没有给出完整的性能评测,但提供了一批值得借鉴的故障处理办法。
大视频还没开始转录,内存为什么先爆了
作者的工具默认在本机处理文件:ffmpeg.wasm 提取音频,转换为 16 kHz、单声道、float32 PCM;transformers.js 4.3 在 Web Worker 中运行 OpenAI 的 Whisper,输出文本或 SRT/VTT 字幕。
模型有 tiny、base、small 三档,界面分别标为 Fast、Accurate、Best。可选的 Instant 模式使用 Web Speech API,会把音频发送给 Google 或 Microsoft,默认关闭,不属于这次端侧排障的范围。
第一处问题来自常见的 ffmpeg.wasm 示例:先把整个文件读进 JavaScript,再写入 ffmpeg 的内存文件系统。同一个视频因此存在两份完整副本。作者遇到过一个 300 MB 的手机视频,仅提取音频就让内存占用超过 1 GB,手机直接撑不住。
解决办法是用 WORKERFS 挂载 File 对象,让 ffmpeg 按块读取。内存里主要留下提取后的音频,float32 PCM 每小时约占 230 MB。作者后来测试过 2.1 GB 文件;旧浏览器挂载失败时,仍回退到复制方案。
处理大文件时,先排查重复复制,往往比调整模型更有效。
不过,挂载解决的是输入视频的复制成本,音频和模型仍然占内存,不能据此推断任意长度的文件都能处理。
为什么有 WebGPU,也不能直接用
作者最初把 GPU 当成优先选项,随后在手机和笔记本上分别碰壁。
Android Chrome 能暴露 navigator.gpu,适配器看起来也正常,但转录中的 Worker 仍会死亡。一位印度用户使用最小模型连续失败 7 次。作者怀疑与共享内存或驱动不稳定有关,并未证实原因;最终把手机统一切到 WASM,使用 q8 权重。
笔记本则是性能问题。在一台 2021 年的 Core i5-11300H、Intel Iris Xe 电脑上,作者用 60 秒语音测试,只计转录耗时。第一次使用启用 Vulkan 后端的 headless Chrome,每个模型只跑一次,small 的 CPU 路径快了 3.5 倍。
第二次用默认设置的 headless Chrome,按「处理 10 分钟音频需要几分钟」折算,tiny 是 CPU 2.4、GPU 3.4;base 是 CPU 4.7、GPU 8.2。small 没有复测。
据此,作者让旧 Intel 核显走 WASM:检查 adapter.info.vendor 是否为 intel,架构是否以 gen- 开头。未测过的新 Intel 芯片和独显仍走 WebGPU。网站没有设置 COOP/COEP 响应头,因此这里的 WASM 还是单线程。
检测适配器时,还有一处容易漏掉的细节。transformers.js 请求高性能适配器,检测也必须保持一致:
requestAdapter({ powerPreference: 'high-performance' })
要看的是 powerPreference。双显卡电脑上,普通 requestAdapter() 可能拿到核显,而真正推理用的是另一块 GPU,导致判断对象错位。
权重格式同样不能放任默认值。不显式指定 dtype 时,WebGPU 路径下载了 fp32 权重,small 达到 923 MB。作者最终选择 GPU 用 fp16、WASM 用 q8:
- 在同一台电脑、同一模型的较早测试里,35 秒音频用 fp16 约需 25 秒,q8 却要 196 秒。
- fp16 下载量约为 q8 的两倍,但 GPU 推理更快;q8 慢是否因为 int8 缺少加速,仍是作者的推测。
- small 的 q4f16 比 q8 更小,却在作者测试中输出不可读文本,因此被排除。仓库还有更小的 int8 文件,作者尚未测试。
能自动恢复的故障,就别只弹一个错误框
作者给常见故障安排了明确退路。大模型报出带内存分配错误的 failed to call OrtRun() 时,自动改用最小模型,并在界面说明;模型下载卡住时重试一次,已下载完成的文件保留在浏览器缓存,再次失败便降到下一档;GPU 中途死亡,则新建 Worker,用 CPU 重新开始,本次会话不再使用 GPU。
这里的重新开始,并没有声称支持断点续转录。
超时也从「耗时太长」改成「长时间没有进展」。旧逻辑固定 10 分钟,而且在约 31 MB 的 ffmpeg.wasm 引擎下载前就开始计时。一周里有 4 人收到音频读取超时,作者推测慢网下载吃掉了处理预算。
新逻辑给不报告进度的引擎下载留出 15 分钟;读取音频时,只有连续 3 分钟没有进度事件才放弃。手机切换应用会冻结标签页,因此计时器若晚触发超过 5 秒,或页面处于隐藏状态,就重新计时。
等待体验也有实际损失。一周内 26 人启动转录,9 人报错,7 人没有报错却中途离开。作者补上了处理期间的 Screen Wake Lock、标签页标题进度、完成提示,以及任务运行时的 beforeunload 提醒。页面重新可见时还要再次申请 Wake Lock,因为隐藏页面会让它被释放。
更关键的是修正预期:small 原先承诺每 10 分钟音频耗时 5–15 分钟,测量结果却是 25 分钟,页面随后改成实测数字。
我的判断
我最认可的是把后端选择、权重格式和恢复策略一起考虑。只检测浏览器是否支持 WebGPU,无法回答设备能否稳定跑完,也无法回答它是否更快。
但这些性能结论有明确边界:主要来自一台 Intel 笔记本,首轮每个模型只跑一次,第二轮没有复测 small。原文也没有提供完整的下载体积表和性能表,不能据此替所有设备选最优配置。旧 Intel 核显走 WASM,是一条有样本支持的经验规则。
用户离开同样不能全归因于等待。26 人的记录足以暴露问题,却不足以证明 Wake Lock、标题进度等改动降低了流失,作者没有给出改动后的对照数据。
与服务端转录相比,本地模式减少了上传音视频的需要,但首次要下载数十到数百 MB,速度也受设备限制。small 在手机上很慢,Hindi、Tamil、Urdu 等语言又可能需要它才能得到可用结果。普通 Whisper 还不提供说话人区分。产品需要把这些边界提前讲清。
能拿来做什么
- 做浏览器音视频工具时,先检查是否把完整 File 读入 JavaScript 后又复制到 WASM 文件系统;可用时采用 WORKERFS,并保留兼容回退。
- 接入 transformers.js 时,显式设置后端和
dtype,用目标用户的真实设备测试耗时、稳定性与下载量。 - 把内存不足、下载停滞、GPU 崩溃分别映射到降模型、重试和切换 Worker,让用户看到恢复动作及其代价。
- 给长任务区分下载、读取和推理阶段,用无进展时间判断卡死,并结合页面可见性处理计时器,提前展示实测等待时间。