Firefox 158 中 decodingInfo 永不返回的 bug

今天遇到一个问题,Firefox 打开 B 站直播,没有画面和声音,而弹幕和聊天一切正常。

控制台有几百行黄色的「Referrer 策略」警告,但查证后确认是无害噪音:Firefox 对跨站请求强制使用更严格的默认 referrer 策略,B 站声明的老策略被忽略,每加载一个跨站资源就记一条日志,请求本身照常发出。

初步排查

  • 同一局域网的 Win10 和 MacBook 上,同版本 Firefox 158.0.1 一切正常;排除 IP 级风控问题。
  • 本机 Edge 正常;排除网络链路和 CDN 问题。
  • B 站点播正常;排除解码、渲染整条媒体管线的问题,故障只在直播播放器的初始化。
  • 本机全新安装、零配置的 Zen-browser(Gecko 内核)一样黑屏;锁定 Gecko 内核。
  • 清空 Cookie 缓存、新建配置文件、关闭跟踪保护,hosts、防火墙、代理逐一核对,均无影响。

至此故障的触发条件已经收得相当窄:要「本机 × Gecko 内核 × 直播」三个条件同时成立,缺一不可。问题从「为什么放不出视频」变成「初始化卡在哪一步」。

深入排查

用调试协议在页面加载前往直播间注入钩子,包装 fetch 和 XMLHttpRequest 记录调用。播放器拿到了播放地址:接口返回 200,avc、hevc 的流列表一项不缺,然后就没有然后了,页面里没有创建任何 video 元素,也没有向 CDN 发出一条拉流请求,初始化卡死。在页面里对照测了一圈媒体 API:

API 结果
requestMediaKeySystemAccess('com.widevine.alpha', …) 1 毫秒正常返回
mediaCapabilities.decodingInfo(…) 永不返回

最简单的复现代码,任意非 B 站网页的控制台粘贴即可,四秒后见分晓:

1
2
3
4
5
6
7
8
9
10
(async () => {
const t = Date.now();
let ok = 0, rej = 0, ms = 0;
navigator.mediaCapabilities.decodingInfo({
type: 'media-source',
video: { contentType: 'video/mp4; codecs="avc1.64001F"', width: 1920, height: 1080, bitrate: 8e6, framerate: 60 }
}).then(() => { ok = 1; ms = Date.now() - t; }, () => { rej = 1; ms = Date.now() - t; });
await new Promise(r => setTimeout(r, 4000));
console.log(ok ? `正常,${ms}ms 返回` : rej ? `${ms}ms 被拒绝` : '挂死,四秒未返回');
})()

钩住 decodingInfo 后发现,播放器初始化时调了它四次,探测 HEVC 和 AV1 的支持情况,四个 Promise 全部悬空。编码探测不返回,播放器就永远停在选编码这一步,弹幕和聊天走另一条路,所以一切如常。写了个 150 毫秒超时保底的垫片脚本注入,直播立刻能放,因果链闭合。

原因

在撰写向 Bugzilla 提交的反馈时,发现已经有相关问题汇报了:这是 Mozilla 自己的回归,见 bug 2082265,Twitch 和 Prime Video 的播放器是同款受害者:

  1. Firefox 158 修 bug 2051510(MediaCapabilities 在远程进程上报编码支持之前抢先回答,把不支持的 HEVC 报成支持)时,把每个视频能力查询都改成了等待所有远程媒体进程上报完毕再回答,等待对象包括 media engine CDM 工具进程。
  2. 在部分 Windows 机器上,这个进程在 LPAC 沙箱下永远启动不完,于是查询永远在等,Promise 永远悬空。
  3. 修复进了 159.0b4 和 160,158 标记 wontfix 不修。

顺带排除了所有本机嫌疑:rdd、utility 进程、硬件解码、WMF 的开关逐个关掉再测,全部照挂;两台机器六千二百多项生效配置全量 diff,零相关差异;进程里没有第三方注入 DLL;显卡驱动从 591.86 换到最新 621.01 也没用。

解决办法

按省事程度排:

  1. about:config 里把 media.wmf.media-engine.enabled 设为 0,实测 27 毫秒恢复。副作用是关闭 WMF Media Engine 播放路径,B 站用不到它,哪天 DRM 场景出问题改回 2 即可。
  2. 升级 Firefox 159.0b4 及以上,根治。
  3. 或者用油猴脚本给 decodingInfo 加超时垫片顶着。我已经写了个脚本:firefox B 站直播无视频声音修复脚本。

至于这台机器为什么落在「部分机器」里,沙箱里那个进程为什么偏偏启动不完,bug 下面也没人给出答案,就这样吧。