feat(asr): [PKG-ASR-09] add explicit capture ownership and cancel stale restarts

This commit is contained in:
Harvey Zhao committed 2026-09-13 16:19:49 +08:00
1 parent 5ab7da6e1f
commit 146b78d576
27 files changed
+12951 -64

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,45 @@
# PKG-ASR-09 显式捕获归属与可取消重启
原默认 direct-first 在 Firefox 已有外部音频图时仍可第二次绑定。新增可选
`audioInput: { type: 'capture' }`,不猜测浏览器归属,不用 URL 推断 CORS,完全跳过
createMediaElementSource。捕获失败也不回退到直接绑定,不影响原播放图。缺省调用
继续旧路径。新字段放 RuntimeOption,旧根/legacy 类型精确形状与四个命名运行类型
保持;这是公开配置的加法,不改变已有字段、事件或返回对象。
## 生产实现
工厂把策略转为内部 primitive 快照,外部后续修改嵌套描述不会切换模式。AudioGraph
复用 ASR-08 的 recorder -> gain(0) 静音输出,stop/换源释放自己捕获的轨道和上下文,
pause 保留以便恢复,destroy 最终清理。采集不改主媒体属性,不拥有外部播放图。
未知策略在工厂构造时报错;现有媒体不支持捕获则按初始化失败路径释放资源。
子代理复核又复现延迟 Context.close 的旧竞态:restart 等待期间用户 pause/stop,
旧 continuation 仍重启。先红两例后添加 stop 后 epoch 守卫,后来生命周期操作会
取消旧重启,下一次用户 play 仍有效。此修复同样保护自动 fallback。
## 明确边界
捕获 PCM 不跟随 element 的 mute/volume;它既不混入独立 audio-track,也不包含其他
图的音效处理。不得因此把默认路径改成 capture-first。依据见
[媒体捕获语义](https://www.w3.org/TR/mediacapture-fromelement/#html-media-element-media-capture-extensions)。
显式模式对无授权跨源源:Chromium 原生 SecurityError 后释放自己的 Context;Firefox
可返回 live track 但不提供 PCM。两者零直接绑定、媒体属性不变、原时钟继续。随后
同源切换恢复有效 PCM,stop 与 destroy 清理。没有 CORS 绕过、额外下载或代理。
默认模式跨源限制仍见 [Web Audio](https://www.w3.org/TR/webaudio-1.0/#MediaElementAudioSourceNode-security)。
ASR-CORS-01 保持默认限制记录,不以显式模式测试冒充自动修复。物理扬声器未测。
## 验证
见 [最终验证证据](../baselines/asr-explicit-capture-validation.json)。新增候选13项
包括模式选择、失败清理、两种API、静音图、暂停/换源/stop/销毁、嵌套快照和两项
延迟关闭竞态。最初9项策略失败及后来2项竞态失败分开冻结,历史基线不改写。
类型fixture新增6个拒绝用法,安装矩阵的负例数量断言从12同步到18;首次矩阵仅因
这个旧计数断言失败的报告保留,不能写成旧运行已通过。
最终229项定向通过,main/legacy各43项生命周期通过;原生source/main/legacy共106项
通过、53项能力跳过。正常构建、根/分包严格类型、lint和17配置隔离安装通过。
真实外部 owner 用例包含 Firefox 自然执行、零直接绑定、无强制异常;非零采集、
静音ASR输出、stop/restart/换源与独立清理均已验证。Windows WebKit 无WebAudio明确
跳过;ASR-05 的设备、无音轨/其他不支持媒体及ASR-06发行仍待完成。没有推送、
标签或 npm 发布。
+7 -5
View File
@@ -2,9 +2,9 @@
> 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 227 项,范围 22 个包及工作区/示例。
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 228 项,范围 22 个包及工作区/示例。
状态:todo 81 / doing 17 / blocked 0 / done 129 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
状态:todo 81 / doing 17 / blocked 0 / done 130 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -17,7 +17,7 @@
| artplayer | 5.4.1 | CORE-01, CORE-02, CORE-03, CORE-04, CORE-05, CORE-06, CORE-07, CORE-08, CORE-09, CORE-10, CORE-11, CORE-12, CORE-13, CORE-14, CORE-15, CORE-16, CORE-17, CORE-18, CORE-19, CORE-20, CORE-21, CORE-23, CORE-22, CORE-24, CORE-25, ENG-12 |
| artplayer-plugin-ads | 2.1.0 | PKG-ADS-01, PKG-ADS-02, PKG-ADS-03, PKG-ADS-04, PKG-ADS-05, PKG-ADS-06 |
| artplayer-plugin-ambilight | 1.1.0 | PKG-AMBILIGHT-01, PKG-AMBILIGHT-02, PKG-AMBILIGHT-03, PKG-AMBILIGHT-04, PKG-AMBILIGHT-PROXY-01, PKG-AMBILIGHT-05, PKG-AMBILIGHT-06, PKG-FACTORY-01 |
| artplayer-plugin-asr | 2.1.0 | PKG-ASR-01, PKG-ASR-02, PKG-ASR-03, PKG-ASR-04, PKG-ASR-05, PKG-ASR-06, PKG-ASR-07, PKG-ASR-08 |
| artplayer-plugin-asr | 2.1.0 | PKG-ASR-01, PKG-ASR-02, PKG-ASR-03, PKG-ASR-04, PKG-ASR-05, PKG-ASR-06, PKG-ASR-07, PKG-ASR-08, PKG-ASR-09 |
| artplayer-plugin-audio-track | 1.1.0 | PKG-AUDIO-01, PKG-AUDIO-02, PKG-AUDIO-03, PKG-AUDIO-04, PKG-AUDIO-05, PKG-AUDIO-06 |
| artplayer-plugin-auto-thumbnail | 1.1.0 | PKG-AUTO-THUMB-01, PKG-AUTO-THUMB-02, PKG-AUTO-THUMB-03, PKG-AUTO-THUMB-04, PKG-AUTO-THUMB-05, PKG-AUTO-THUMB-06 |
| artplayer-plugin-chapter | 1.1.0 | PILOT-01, PKG-CHAPTER-01, PKG-CHAPTER-02, PKG-CHAPTER-03, PKG-CHAPTER-04, PKG-CHAPTER-05, PKG-CHAPTER-06 |
@@ -255,10 +255,11 @@
| PKG-ASR-02 | artplayer-plugin-asr<br>建立特有行为与错误测试 | PKG-ASR-01, ENG-03, ENG-05 | PCM/WAV、chunk 时序、回调慢/拒绝、重复初始化与停止 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
| PKG-ASR-03 | artplayer-plugin-asr<br>整理内部职责与资源 | PKG-ASR-02, CORE-10, CORE-18 | AudioContext/Worklet/Stream 生命周期和背压分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done |
| PKG-ASR-04 | artplayer-plugin-asr<br>迁移自有源码和公开类型 | PKG-ASR-03, ENG-04, ENG-06, CORE-07 | 主线程/Worklet/音频 buffer 与异步回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
| PKG-ASR-05 | artplayer-plugin-asr<br>验证新旧核心和组合 | PKG-ASR-04, CORE-22, PKG-ASR-07, PKG-ASR-08 | 真实 WebAudio 分块、播放暂停/切源/销毁,不引入网络 ASR | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing |
| PKG-ASR-05 | artplayer-plugin-asr<br>验证新旧核心和组合 | PKG-ASR-04, CORE-22, PKG-ASR-07, PKG-ASR-08, PKG-ASR-09 | 真实 WebAudio 分块、播放暂停/切源/销毁,不引入网络 ASR | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing |
| PKG-ASR-06 | artplayer-plugin-asr<br>验证分发并同步文档 | PKG-ASR-05, ENG-07 | asr.js、Worklet 资源、输出格式与声明证据 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
| PKG-ASR-07 | artplayer-plugin-asr<br>修复原生播放音量二次衰减 | PKG-ASR-04 | 移除重复音量应用;保留新旧原生音频振幅对照及正式产物验收 | Chromium/Firefox 两核心和 source/main/legacy 输出音量线性、静音和 stop/restart 正确;旧版平方衰减单独保留;单元/类型/真实安装通过,不代替设备验收 | H | done |
| PKG-ASR-08 | artplayer-plugin-asr<br>修复捕获回退重复播放与静音漏声 | PKG-ASR-07 | 捕获回退静音输出图、非零PCM与外部播放归属;真实红绿证据及产物验证 | 已进入回退后不产生额外输出、静音不漏声、外部播放图不被关闭、stop/restart/换源清理正确;明确Firefox受控拒绝不代表自然选择回退 | H | done |
| PKG-ASR-09 | artplayer-plugin-asr<br>提供显式捕获归属并取消过期切源重启 | PKG-ASR-04, PKG-ASR-08 | 保留默认API的capture模式、精确runtime类型、可取消重启和原生外部归属/CORS证据 | 显式模式零直接绑定、不增加输出、拥有资源正确清理;Firefox不强制异常,CORS无绕过;旧工厂类型保持;延迟关闭期间pause/stop取消旧重启 | H | done |
## 5 包迁移:artplayer-plugin-ads
@@ -532,7 +533,7 @@
- PKG-ASR-02: [记录](changes/2026-09-13-PKG-ASR-02-audio-baseline.md) [记录](baselines/asr-audio-validation.json)
- PKG-ASR-03: [记录](changes/2026-09-13-PKG-ASR-03-audio-ownership.md) [记录](baselines/asr-ownership-validation.json)
- PKG-ASR-04: [记录](changes/2026-09-13-PKG-ASR-04-public-types.md) [记录](baselines/asr-types-validation.json) [记录](baselines/asr-type-diagnostics.json)
- PKG-ASR-05: [记录](baselines/asr-volume-validation.json) [记录](baselines/asr-fallback-validation.json) [记录](baselines/asr-core.json) [记录](baselines/asr-local-core-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-local-core-checkpoint.md) [记录](baselines/asr-combinations-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-cors-combinations.md)
- PKG-ASR-05: [记录](baselines/asr-volume-validation.json) [记录](baselines/asr-fallback-validation.json) [记录](baselines/asr-core.json) [记录](baselines/asr-local-core-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-local-core-checkpoint.md) [记录](baselines/asr-combinations-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-cors-combinations.md) [记录](baselines/asr-explicit-capture-validation.json)
- PKG-ADS-01: [记录](baselines/ads-release.json) [记录](baselines/ads-contract.md) [记录](baselines/ads-contract-validation.json) [记录](changes/2026-09-12-PKG-ADS-01-contract.md) [记录](scripts/ads-contract.test.mjs)
- PKG-ADS-02: [记录](changes/2026-09-12-PKG-ADS-02-tests.md) [记录](ads-validation.md) [记录](baselines/ads-validation.json)
- PKG-ADS-03: [记录](changes/2026-09-12-PKG-ADS-03-lifecycle.md) [记录](ads-validation.md) [记录](baselines/ads-lifecycle-validation.json)
@@ -573,3 +574,4 @@
- ENG-12: [记录](changes/2026-09-13-ENG-12-library-public.md) [记录](baselines/library-public-validation.json)
- PKG-ASR-07: [记录](changes/2026-09-13-PKG-ASR-07-playback-volume.md) [记录](baselines/asr-volume-validation.json)
- PKG-ASR-08: [记录](changes/2026-09-13-PKG-ASR-08-capture-output.md) [记录](baselines/asr-fallback-validation.json)
- PKG-ASR-09: [记录](changes/2026-09-13-PKG-ASR-09-explicit-capture.md) [记录](baselines/asr-explicit-capture-validation.json)
+12
View File
@@ -1,5 +1,17 @@
# 进度与证据
## PKG-ASR-09 显式捕获归属完成
新增可选capture策略,旧默认与根类型保留;Firefox无需强制异常,显式模式零直接
绑定、非零PCM、零附加输出,外部Context保持独立。无CORS源遵循原生限制且不
接管播放,同源切换恢复。独立审查发现的延迟close后过期重启已用两项红绿测试修复。
229定向、main/legacy各43项通过;最终source/main/legacy原生106过、53能力跳过;
正常构建、strict/lint、隔离安装17配置及每候选18个负例通过。见[变更](changes/2026-09-13-PKG-ASR-09-explicit-capture.md)
与[证据](baselines/asr-explicit-capture-validation.json)。外部归属按显式模式闭环,默认
CORS静音仍open,不声称自动检测或物理设备已完成。228项:130 done、17 doing、
81 todo;210风险。ASR-05继续,完整发行和整仓重构仍有剩余任务;无推送或发布。
## PKG-ASR-05 CORS 与独立音轨检查点
新增跨源允许/不允许/重定向及同源恢复、audio-track组合生命周期与独立音频归属
+2 -1
View File
@@ -211,5 +211,6 @@
| ASR-LIFE-01 | resolved / 已复现 | ASR partial initialization, pause resume and stale callback cleanup need controlled evidence | PKG-ASR-02, PKG-ASR-03, PKG-ASR-05 |
| ASR-VOLUME-01 | resolved / 已复现 | ASR applies media volume twice and squares playback amplitude | PKG-ASR-07 |
| ASR-FALLBACK-01 | resolved / 已复现 | Captured fallback produces a second audible route even when the video is muted | PKG-ASR-08 |
| ASR-FOREIGN-01 | open / 已复现 | Firefox permits parallel native media-element owners instead of selecting capture fallback | PKG-ASR-05 |
| ASR-FOREIGN-01 | resolved / 已复现 | Firefox permits parallel native media-element owners instead of selecting capture fallback | PKG-ASR-05, PKG-ASR-09 |
| ASR-CORS-01 | open / 已复现 | Default WebAudio binding silences CORS-cross-origin media that otherwise advances normally | PKG-ASR-05 |
| ASR-RESTART-01 | resolved / 已复现 | Delayed captured-source restart resumes recognition after a newer pause or stop | PKG-ASR-09 |
+31 -4
View File
@@ -4662,16 +4662,22 @@
"id": "ASR-FOREIGN-01",
"title": "Firefox permits parallel native media-element owners instead of selecting capture fallback",
"confirmation": "reproduced",
"status": "open",
"status": "resolved",
"owners": [
"PKG-ASR-05"
"PKG-ASR-05",
"PKG-ASR-09"
],
"evidence": [
"refactor/baselines/asr-fallback-validation.json",
"refactor/changes/2026-09-13-PKG-ASR-08-capture-output.md"
],
"compatibleResolution": "Establish an explicit compatible ownership contract for external audio graphs without silently changing default direct-path PCM/mute semantics.",
"closureCriteria": "Natural Firefox external-owner integration has no duplicate output or has a documented, validated explicit ownership mode; controlled binding rejection alone cannot close this finding."
"closureCriteria": "Natural Firefox external-owner integration has no duplicate output or has a documented, validated explicit ownership mode; controlled binding rejection alone cannot close this finding.",
"resolutionEvidence": [
"refactor/baselines/asr-explicit-capture-validation.json",
"refactor/changes/2026-09-13-PKG-ASR-09-explicit-capture.md"
],
"resolutionRationale": "The documented explicit capture mode satisfies the ownership-mode closure branch: native Firefox never calls direct binding and keeps the external route independent. This is not automatic default-mode owner detection."
},
{
"id": "ASR-CORS-01",
@@ -4683,10 +4689,31 @@
],
"evidence": [
"test/browser/asr-cors.spec.js",
"refactor/baselines/asr-combinations-validation.json"
"refactor/baselines/asr-combinations-validation.json",
"refactor/baselines/asr-explicit-capture-validation.json"
],
"compatibleResolution": "Document required CORS setup and provide an explicitly chosen capture path that never takes over media playback; do not bypass browser security or silently change default PCM semantics.",
"closureCriteria": "Authorized capture and same-origin recovery remain valid; an explicit integration avoids direct ownership for restricted input, fails or stays silent without rerouting playback, and is documented with native evidence."
},
{
"id": "ASR-RESTART-01",
"title": "Delayed captured-source restart resumes recognition after a newer pause or stop",
"confirmation": "reproduced",
"status": "resolved",
"owners": [
"PKG-ASR-09"
],
"evidence": [
"test/asr-explicit-capture.test.js",
"refactor/baselines/asr-explicit-capture-validation.json"
],
"compatibleResolution": "Invalidate stale restart continuation using the lifecycle epoch while allowing the next explicit play.",
"closureCriteria": "Pause and stop during delayed context close prevent a new graph and timer; later play works normally.",
"resolutionEvidence": [
"refactor/changes/2026-09-13-PKG-ASR-09-explicit-capture.md",
"refactor/baselines/asr-explicit-capture-validation.json"
],
"resolutionRationale": "Two delayed-close red tests now pass in source/main/legacy, with subsequent play verified; native restart and default regressions pass."
}
]
}
+1 -1
View File
@@ -154,7 +154,7 @@ Promise.all(['artplayer-plugin-asr', 'artplayer-plugin-asr/runtime', 'artplayer-
if (result.plugin === 'candidate') {
const publishedBaseline = result.mode === 'nodenext-esm' ? expected.cases.find(item => item.plugin === 'published-2.1.0' && item.compiler === result.compiler && item.mode === result.mode).diagnostics : []
assert.deepEqual(result.published, publishedBaseline, `Historical direct consumer: ${result.compiler} ${result.mode}`)
assert.equal(result.invalid.length, 12, 'Installed declarations must reject all 12 invalid uses')
assert.equal(result.invalid.length, 18, 'Installed declarations must reject all 18 invalid uses')
}
}
console.log(`ASR installed matrix verified: ${matrix.length} cases; ${output}`)
+24 -2
View File
@@ -2703,7 +2703,8 @@
"PKG-ASR-04",
"CORE-22",
"PKG-ASR-07",
"PKG-ASR-08"
"PKG-ASR-08",
"PKG-ASR-09"
],
"status": "doing",
"risk": "H",
@@ -2716,7 +2717,8 @@
"baselines/asr-local-core-validation.json",
"changes/2026-09-13-PKG-ASR-05-local-core-checkpoint.md",
"baselines/asr-combinations-validation.json",
"changes/2026-09-13-PKG-ASR-05-cors-combinations.md"
"changes/2026-09-13-PKG-ASR-05-cors-combinations.md",
"baselines/asr-explicit-capture-validation.json"
]
},
{
@@ -4584,6 +4586,26 @@
"changes/2026-09-13-PKG-ASR-08-capture-output.md",
"baselines/asr-fallback-validation.json"
]
},
{
"id": "PKG-ASR-09",
"phase": "5 包迁移:artplayer-plugin-asr",
"title": "提供显式捕获归属并取消过期切源重启",
"scope": [
"artplayer-plugin-asr"
],
"dependsOn": [
"PKG-ASR-04",
"PKG-ASR-08"
],
"status": "done",
"risk": "H",
"deliverable": "保留默认API的capture模式、精确runtime类型、可取消重启和原生外部归属/CORS证据",
"acceptance": "显式模式零直接绑定、不增加输出、拥有资源正确清理;Firefox不强制异常,CORS无绕过;旧工厂类型保持;延迟关闭期间pause/stop取消旧重启",
"evidence": [
"changes/2026-09-13-PKG-ASR-09-explicit-capture.md",
"baselines/asr-explicit-capture-validation.json"
]
}
]
}
+1 -1
View File
@@ -1245,7 +1245,7 @@
"docs/assets/example/asr.local.js"
],
"contract": "Core plugin supplies PCM to caller onAudioChunk. Example fetches an external ASR endpoint and opens its returned WebSocket; not an embedded ASR SDK or a verified service.",
"validationStatus": "Native PCM/Worklet, volume and lifecycle verified on Chromium/Firefox with candidate, 5.4.0 and adjacent stable 5.3.0 cores; fallback Firefox selection is explicitly controlled, with foreign ownership still open. Offline docs example verifies local audio only. Windows WebKit, physical devices and actual external recognition service remain unverified.",
"validationStatus": "Native PCM/Worklet, volume, CORS and lifecycle verified on Chromium/Firefox with candidate and published cores. Explicit capture has native Firefox zero-binding external ownership; default fallback still uses controlled rejection there. Offline demo remains local audio only. Default CORS silence, Windows WebKit, physical devices and the external recognition service remain unverified or restricted.",
"demoPathsSource": "refactor/baselines/demo-inventory.json"
},
{