~/blog/webm-libvpx-wasm-crash
WASM2026-10-14 · 9 min

WebM 在 ffmpeg.wasm 上为什么必崩:三层排查实录

VP9 首帧即 OOB、VP8 低 cpu-used 内容相关越界、同机 headed 通过 headless 也通过——三层根因逐层剥,最后只剩诚实的下线。

格式转换页给 WebM 输出兜底的过程中,我们撞上了一个排查了三天、最后只剩诚实下线的 bug。它的特别之处在于:崩溃完全确定(同一文件、同一帧数、重建引擎也一样),但穷尽所有可控变量之后,仍然有一部分无法定位。记录全过程。

第一层:WebM 默认编码器在 wasm 上必崩

现象很直接:格式转换选 WebM 输出,RuntimeError: memory access out of bounds,用户的第一反应是"是不是不支持?删掉吧"。

先用最小 fixture 验证:320×240、2 秒的 WebM,也崩。说明和文件大小无关。再定位到容器默认编码器:webm 的默认视频编码器是 libvpx-vp9,在 @ffmpeg/core@0.12.10(ffmpeg 5.1.4,x86_32 单线程 wasm)上,任何内容第一帧就 OOB。VP9 的编码路径在这个 core 上根本不能用。

修法是显式改走 VP8:-c:v libvpx。顺带发现一个附带问题:-preset veryfast 是 x264 的私有选项,之前无条件附加在所有编码器命令后面,对其他编码器只产生一行无害警告——这种"无害"的噪音会在排障时干扰判断,顺手清了。

第二层:VP8 也有内容相关的确定性越界

改 VP8 之后,用户那边仍然崩。我们自己合成了 720p、1080p、4K、WMV、H.264 各种样本,全部通过,始终无法复现。直到拿到用户的真实文件(5,485,044 字节,1080×1920 的竖屏):稳定崩在第 4 帧,重建 core 重试也崩在同一位置——是确定性 bug,不是随机内存问题。

跑参数矩阵找到了分界线:

cpu-used结果
3第 4 帧崩
4第 4 帧崩
5稳定通过

libvpx 的 good deadline 模式下 cpu-used 合法域是 0~5,5 就是上限。结论是:VP8 低 cpu-used 的高质量编码路径在这个 wasm core 上存在内容相关的确定性越界——某些画面内容会触发,合成样本没踩中,用户的真实文件踩中了。

定案防线写在参数构建函数里,一份参数、命令展示和执行共用,防止两处漂移:

-c:v libvpx -b:v 1M -cpu-used 5

对抗审查还抓到两个残留出口:视频裁剪页手拼参数会裸奔 VP9;高级参数里显式填 libvpx 会绕过 cpu-used 5 防线。都补了。

第三层:穷尽排除之后,只剩"用户浏览器"

到这里故事本该结束,但用户换了另一个文件(576×1024),在 cpu-used 5 之下 frame=3 仍然确定性崩。我们开始做排除法,做到比较狠:

  • 同一文件、同一参数、同一台机器:headless Chrome for Testing 通过、headed Chrome for Testing 通过、headless Edge 通过、headed Edge 通过——注意 headless 和 headed 跑的是同一个 Edge 二进制;
  • 原始中文文件名 ✅、同会话连跑 3 次 ✅;
  • CDN core 和本地 core 的 wasm 文件字节级哈希一致(core.js 的差异只是 CRLF,规范化后也一致)——排除"用户下到坏的 core";
  • 用户浏览器(Edge 153,64 位)的 wasm 内存上限实测 2000MB,正常。

所有可控变量穷尽,只剩用户浏览器 profile 层——扩展、缓存之类的因素——无法在我们这边复现。用户做了决定:下线 WebM 输出选项(格式转换和裁剪两页 UI 移除),exec 参数保留为休眠防线;webm 作为输入转 MP4/GIF 不受影响。

复盘

三个可以带走的判断顺序:

  1. 排查 wasm OOB 先看编码器和参数矩阵,再怀疑堆大小。 这是 libvpx 编码路径的 bug,加内存没用。合成样本全过不代表没问题——拿真实用户文件复现是关键一步。
  2. 确定性崩溃(同一帧数)说明是内容触发,不是资源耗尽。 重建、重试、加内存都不会改变结果,方向应该直接转到"哪个参数路径绕开它"。
  3. 排除法要留证据链。 wasm 字节级哈希、同二进制 headless/headed 对比、真实文件复现——每一环都堵死一个"可能是这个原因"的退路,最后剩下的那个解释哪怕无法验证,也只能是它。
RELATED TOOLS · 把文章里的东西用起来