文章

升级 Electron 之后视频冻在第一帧,最后改的是命令解析

Emby Theater Enhanced 从 Electron 18 升级到 44 之后,启动视频停在第一帧。记录两个被否决的假设、一次 2×2 矩阵实验,以及落在启动命令规范化上的修复。

Emby Theater Enhanced 是我在维护的一个 Windows 客户端,基于早期的 Carnival 版本继续维护,播放靠内嵌的 mpv。9 月下半月,这个客户端的 Electron 运行时从 18.3.15 换到了 44.4.2。旧运行时的 Chromium 停在 100,Node 停在 16,安全修复和兼容性支持都已经停更,升级的目标是在不动播放链的前提下把运行时整个换掉。

运行时以固定输入的方式接入。官方发行包带归档和完整目录树的哈希校验,构建时整个 electron 目录被完整替换,旧运行时的 DLL、locale 和资源不会残留。需要改的兼容点只有两处,被移除的 new-window 事件换成 setWindowOpenHandler,客户端内部的六个自定义协议要在应用就绪前注册为 standard + supportFetchAPI + corsEnabled,否则渲染进程对它们发起的 XHR 会失败。再删掉一个未使用的 BrowserView 引用,这轮升级在代码层面就这些。安全选项没有被放宽,也没有把视图迁移混进来。

第一个候选构建的自动化门槛全部通过。两百多条测试是绿的,合成媒体、STRM 和网盘直链这些链路也都过了,隐藏窗口下的启动检查同样通过。隐藏检查按设计不截图,画面呈现本来就不在它的证明范围里。换到可见窗口做前台检查,问题出现了。视频只出第一帧,之后画面不再变化;同一时间 mpv 的 time-pos 在推进,服务器侧的播放位置和音频正常,界面和 OSD 也都正常。解码和播放时钟是活的,停住的只有画面呈现。

先排除遮挡与窗口合成

第一个假设是视频表面被系统当成了被遮挡的窗口。实验用同一份候选运行时、同一个用户配置、同一个媒体和同样的窗口位置尺寸做对照,A 组不加开关,B 组在应用就绪前加 disable-backgrounding-occluded-windows。两组都在启动后的固定时间点截取视频区域,比较 PNG 的 SHA-256。结果是两组的截图序列各自完全不变,拖动进度、改变窗口尺寸、把窗口遮住再露出来,画面都没有变化;同一时间两组里 mpv 的播放时钟、服务器位置、音频和播放状态都正常推进。遮挡假设被否决,这个开关没有写进产品,也没有提交。

窗口合成和窗口激活一类方向在这一轮保持关闭。修复里没有出现重绘定时器、Chromium 开关、焦点处理或者窗口位置调整之类的绕行方案,也没有对外重新开放这些假设。

命令 token 的形态变了

真正的线索来自另一个现象。Electron 44 对标准协议的命令 URL 做了一次规范化,渲染进程传给主进程的命令词形态变了,最直观的例子是 electronapphost://loaded/ 和 electronapphost://windowstate-Maximized/。旧解析器对大小写和结尾的斜杠敏感,loaded/ 没有匹配上,启动时下面这段链就没有执行。

setWindowState(windowStateOnLoad)
mainWindow.focus()
hasAppLoaded = true
onLoaded()

第一次注意到这个问题是从全屏切换开始的。修好解析之后,真实 Electron 44 环境里的探针确认被规范化的窗口状态命令重新进入了既有分派,窗口正常进入全屏。与此对应,启动时的视频冻结在修复后不再出现。

修复只动命令词的规范化。只有 electronapphost 的命令词会做同一化处理,把结尾斜杠和大小写归一;openurl 的原始 URL 和查询字节保持原样;未知命令直接拒绝。记录里没有声称这条链里哪一条语句单独导致了冻结,最后一层机制没有继续隔离,也写明这一层不影响发布。

用 2×2 矩阵钉住边界

为了确认冻结跟什么走,后面做了一组矩阵实验,两个源码版本各配两种启动入口,每种组合跑五次。

源码版本 入口一 入口二
修复前(9168d08) 冻结 5/5 冻结 5/5
修复后(725d4c2) 通过 5/5 通过 5/5

四组里的 Electron、宿主、辅助进程和 mpv 身份完全一致。结论是冻结跟着源码版本走,跟入口无关。这个结论和修复互相印证,也把之前的遮挡和合成假设归入历史证据。

修复范围与验收

修复后的候选重新跑了完整门槛。233 项自动化测试全部通过;安装包解包内容与运行目录逐文件比对,2137 对 2137,缺失、多余和哈希不一致都是 0;安装后的程序又做了一轮完整的前台人工验收,连续播放、音频、OSD、设置、暂停恢复、拖动、全屏进出、Alt-Tab、最小化恢复、缩放、停止和正常退出全部确认通过,冻结没有再出现,拖动和停止时的黑帧也没有观察到。这台机器上的播放日志证据仍然标为不可用,验收按人工前台口径记录,没有写成机器证据。

发布

这些改动合入 main 之后发布了 v0.2.2,运行时为 Electron 44.4.2,Chromium 152.0.7977.130。安装包随 Release 提供。

SHA256: 238FA813C5F7BFC1EFE5D76ABD76848C749D0011C588111FA24710BFA72438BA

已知问题清单里还留着 Windows 混合 DPI 场景的窗口尺寸问题,这一项和这次修复无关,继续保持记录。

回过头看,这次排查里最有用的部分是两次实验的设计。遮挡对照给出了一个干脆的否定,矩阵实验把边界钉在源码版本上,两次都没有引入产品改动,也没有把没有验证过的结论写进发布说明。最后落地的一行修复只做命令词的规范化,其余部分保持原样。

Sources

[1] https://github.com/hope140/EmbyTheaterEnhanced Emby Theater Enhanced 仓库 [2] https://github.com/hope140/EmbyTheaterEnhanced/releases/tag/v0.2.2 v0.2.2 Release [3] https://github.com/hope140/EmbyTheaterEnhanced/commit/8be3b6b8fdce9295f73acd7aa6b6507eb5d6c27c 启动命令规范化修复 [4] https://github.com/hope140/EmbyTheaterEnhanced/blob/main/docs/ELECTRON_44_UPGRADE.md Electron 44 升级记录