Files
ArtPlayer/refactor/changes/2026-09-13-PKG-MASK-02-failures.md
T

5.6 KiB
Raw Blame History

PKG-MASK-02 历史错误与生命周期观察

本任务新增历史测试、专属 helper 和证据,并接入根测试脚本;未修改生产或生成产物,未执行模型, 没有浏览器服务器、推送或发布。PKG-MASK-03 仍依赖 PKG-DANMUKU-07;该依赖尚未 满足,生产重构按任务依赖继续推进。本任务不代表 SDK-08 完成。

输入和可重放接缝

源码来自 b0cfbfe3a09a84a295a257e45e4577847588d1cb;真实 npm 1.0.0/1.1.0 tarball 来自 MASK-01 的 SHA512/SHA256 冻结。源文件与六个 main/legacy/module 成员共七个输入,标识见 机器报告。

两种探针必须区分:

  • MASK-01 已用未改写的归档字节验证 CommonJS/script 导出、注册和 stop,未启动模型。
  • MASK-02 的历史生命周期探针对归档导出工厂内部 五个 SDK 调用表达式的 callee 做受控替换:setBackend(webgl/cpu) 两处、createSegmenter/toBinaryMask/drawMask 各一处。inline SDK 函数表达式作为整体替换,原插件异步流程、生命周期、错误处理、 canvas 操作与旧版 async helper 保留。module 先以 esbuild 转为 CJS,因此不算 原生 ESM 验收。冻结源码仅转换模块形式,通过 require seam 注入 SDK 替身。

helper 首先校验真实归档,再从实际导出函数定位唯一原文字段,用 TypeScript AST 寻找接缝。固定替换数量不是模糊匹配后继续运行:任何数量不符直接失败。报告保存 原成员哈希、转 CJS 后哈希、探针哈希、每个 callee 的范围/旧表达式哈希。首项测试 逐项比较报告,变化必须显式审查。实际 SDK 顶层注册可执行,但没有模型初始化、 模型网络、GPU 或媒体推理。探针 fetch 被拒绝;错误探针不得以网络错误替代目标错误。

实际观察

七个输入都复现以下三个历史缺陷,共 21 项:

触发 历史结果
初始化 pending → stop → 完成初始化 stop 的 maskImage=none 被后续 mask 写入覆盖,重新安排 RAF
推理 pending → destroy → 推理完成 销毁后仍画 mask/排 RAF;segmenter 未 dispose,ready/destroy listener 留存
初始化完成后再次 start → stop 两条 RAF 链,stop 只取消最后记录的一条;剩余链下一轮重新写 mask

详细错误矩阵集中在冻结源码与 npm 1.1.0 main,各 16 项,避免为所有格式机械重复:

触发 历史结果 / 关联风险
初始化 pending 时 stop 或 destroy 后续模型仍存活,不 dispose;MASK-LIFETIME-01
推理 pending 时 stop 晚到结果覆盖 none 并排新 RAF;MASK-LIFETIME-01
同时两次 start 创建两个模型、两条 RAF,destroy 后仍剩一条,两个模型均未 dispose;MASK-START-01
segmentPeople rejection 每帧记一次错误并重试;改为成功后能再画 mask;MASK-BACKEND-01
setBackend(webgl) fulfilled false 不尝试 CPU,继续初始化;MASK-BACKEND-01
WebGL rejection、CPU fulfilled true/false 均尝试 CPU、记录一次 warning、继续初始化,忽略返回 boolean;不是 MediaPipe CPU 推理证据
CPU rejection,经显式 start 调用 Promise 以同一错误拒绝,尚未创建模型或排 RAF
createSegmenter rejection 记录错误,但 start resolve;以后 RAF 只轮询,既不推理也不自动重新初始化
缺少 $danmuku start 在模型创建后以 TypeError 拒绝;stop 也抛错,模型未 dispose;MASK-DOM-01
getContext 返回 null start resolve;读图像时报 TypeError 并排下一帧,旧 mask 不被清除
createElement(canvas) 抛错 start 以同一错误拒绝;已初始化模型在 destroy 后仍未 dispose
getImageData 抛受控 SecurityError 错误被记录并重试,既有 mask 留存;这不是原生 CORS 复现
RAF 返回 0;destroy 后再次 ready truthiness 判断漏取消 0,保留 ready listener 可再启动一条链
ready 内 CPU rejection 被事件调度丢弃的 start Promise 产生未处理拒绝
缺少 $video segmentBody 在 try 之外读 paused,产生未处理拒绝

加上接缝身份验证,共 54 项历史测试。未处理拒绝的四项各启独立 Node 子进程, 使用 --unhandled-rejections=strict,验证子进程 exit=1 和指定错误;父进程没有安装 unhandledRejection handler,子进程也没有吞错 handler。子进程只能以指定错误失败, AssertionError 或禁止网络错误均不作为通过。测试进程本身没有实际 RAF 定时器; 故意留存的历史帧只在该测试的 Map 内,不会泄漏到真实 UI。

验证及后续约束

固定 Node 24.21.0:

node --test test/danmuku-mask-failures.test.js
node node_modules/eslint/bin/eslint.js test/helpers/danmuku-mask.js test/danmuku-mask-failures.test.js
yarn test:danmuku-mask

54 项通过、0 fail、0 skip;专属 lint 通过。日志 refactor/.cache/danmuku-mask-failures-final.log,机器报告记录其哈希和输入身份。 这里只证明旧缺陷可重跑,这些结果不能变成候选验收期望。后续候选测试应独立 验证异步失效、模型单一归属、RAF 单链、适当错误传播/事件处理和销毁清理;不要靠 将本套测试切换到候选 source 来保留已知问题。SDK-08、真实模型/设备/视频/浏览器 与新旧核心组合仍由 05 验收,通知/资源最终分发由 06 核实。

父代理独立复跑54项通过,加入现有test:unit使CI自动执行;专属 test:danmuku-mask同时覆盖01的六项契约。未安装依赖或更改Yarn锁文件。