diff --git a/docs/compiled/artplayer-plugin-asr.js b/docs/compiled/artplayer-plugin-asr.js index fa6df2967..d3b1b4ace 100644 --- a/docs/compiled/artplayer-plugin-asr.js +++ b/docs/compiled/artplayer-plugin-asr.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const r=this.gain=s||e.createGain();s||(r.gain.value=this.direct?1:0);const n=this.recorder=new AudioWorkletNode(e,"recorder-processor");n.port.onmessage=e=>{this.closed||this.recorder!==n||t(e.data)},i.connect(n),this.direct?i.connect(r):n.connect(r),r.connect(e.destination)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,r=Math.floor(e*s/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const n=this.graph??(this.graph=new t(this.video,e));await n.prepare(),i()?(n.connect(t=>{if(this.running&&this.graph===n)return this.queue.length+t.length>Math.max(60*e,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),s)):this.running||this.graph!==n||n.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:r=16e3,autoHideTimeout:n=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,r=!1;function n(){clearTimeout(s),s=void 0}function o(){r||(t.style.display="none")}return{append:function(a){r||"string"!==typeof a||(n(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){r=!0,n()}}}(a,e,n),c=new i(t.video,{interval:s,sampleRate:r,onAudioChunk:o},h.append),l=()=>c.start(),d=()=>c.pause(),p=()=>c.restart(),u=()=>(h.destroy(),t.off("play",l),t.off("pause",d),t.off("restart",p),t.off("destroy",u),c.stop(!0));return t.on("play",l),t.on("pause",d),t.on("restart",p),t.on("destroy",u),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e,i=!1){this.video=t,this.sampleRate=e,this.captureOnly=i,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source){if(!this.captureOnly)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s)}if(!this.source){const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const r=this.gain=s||e.createGain();s||(r.gain.value=this.direct?1:0);const n=this.recorder=new AudioWorkletNode(e,"recorder-processor");n.port.onmessage=e=>{this.closed||this.recorder!==n||t(e.data)},i.connect(n),this.direct?i.connect(r):n.connect(r),r.connect(e.destination)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it&&!this.terminal&&this.epoch===i?this.start():void 0)}return t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,r=Math.floor(e*s/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const n=this.graph??(this.graph=new t(this.video,e,this.options.captureOnly));await n.prepare(),i()?(n.connect(t=>{if(this.running&&this.graph===n)return this.queue.length+t.length>Math.max(60*e,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),s)):this.running||this.graph!==n||n.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:r=16e3,autoHideTimeout:n=1e4,onAudioChunk:o=()=>null,audioInput:a}=t;if(void 0!==a&&"capture"!==a.type)throw new TypeError("Unsupported ASR audio input");const h="capture"===a?.type;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const c=function(t,e,i){let s,r=!1;function n(){clearTimeout(s),s=void 0}function o(){r||(t.style.display="none")}return{append:function(a){r||"string"!==typeof a||(n(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){r=!0,n()}}}(a,e,n),l=new i(t.video,{interval:s,sampleRate:r,onAudioChunk:o,captureOnly:h},c.append),d=()=>l.start(),p=()=>l.pause(),u=()=>l.restart(),f=()=>(c.destroy(),t.off("play",d),t.off("pause",p),t.off("restart",u),t.off("destroy",f),l.stop(!0));return t.on("play",d),t.on("pause",p),t.on("restart",u),t.on("destroy",f),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:c.hide,append:c.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); diff --git a/docs/compiled/artplayer-plugin-asr.legacy.js b/docs/compiled/artplayer-plugin-asr.legacy.js index 09aaa34ad..53ad308e2 100644 --- a/docs/compiled/artplayer-plugin-asr.legacy.js +++ b/docs/compiled/artplayer-plugin-asr.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=this.direct?1:0);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),this.direct?i.connect(n):r.connect(n),n.connect(e.destination)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),l=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o},h.append),c=()=>l.start(),d=()=>l.pause(),u=()=>l.restart(),p=()=>(h.destroy(),t.off("play",c),t.off("pause",d),t.off("restart",u),t.off("destroy",p),l.stop(!0));return t.on("play",c),t.on("pause",d),t.on("restart",u),t.on("destroy",p),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e,i=!1){this.video=t,this.sampleRate=e,this.captureOnly=i,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source){if(!this.captureOnly)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n)}if(!this.source){const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=this.direct?1:0);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),this.direct?i.connect(n):r.connect(n),n.connect(e.destination)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it&&!this.terminal&&this.epoch===i?this.start():void 0)}return t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i,this.options.captureOnly);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null,audioInput:a}=t;if(void 0!==a&&"capture"!==a.type)throw new TypeError("Unsupported ASR audio input");const h="capture"===(null==a?void 0:a.type);return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const l=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),c=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o,captureOnly:h},l.append),d=()=>c.start(),u=()=>c.pause(),p=()=>c.restart(),f=()=>(l.destroy(),t.off("play",d),t.off("pause",u),t.off("restart",p),t.off("destroy",f),c.stop(!0));return t.on("play",d),t.on("pause",u),t.on("restart",p),t.on("destroy",f),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:l.hide,append:l.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); diff --git a/docs/compiled/artplayer-plugin-asr.mjs b/docs/compiled/artplayer-plugin-asr.mjs index 3fdbb9574..baa8911a1 100644 --- a/docs/compiled/artplayer-plugin-asr.mjs +++ b/docs/compiled/artplayer-plugin-asr.mjs @@ -17,9 +17,10 @@ class RecorderProcessor extends AudioWorkletProcessor { registerProcessor('recorder-processor', RecorderProcessor); `; class AudioGraph { - constructor(video, sampleRate) { + constructor(video, sampleRate, captureOnly = false) { this.video = video; this.sampleRate = sampleRate; + this.captureOnly = captureOnly; this.closed = false; this.direct = false; this.workletLoaded = false; @@ -55,11 +56,15 @@ class AudioGraph { } this.assertOpen(); if (!this.source) { - try { - this.source = context.createMediaElementSource(this.video); - this.direct = true; - } catch (error) { - console.warn("[artplayerPluginAsr] Direct connection failed:", error); + if (!this.captureOnly) { + try { + this.source = context.createMediaElementSource(this.video); + this.direct = true; + } catch (error) { + console.warn("[artplayerPluginAsr] Direct connection failed:", error); + } + } + if (!this.source) { const capture = this.video.captureStream || this.video.mozCaptureStream; if (!capture) throw new Error("Could not establish audio source"); @@ -241,8 +246,11 @@ class Capture { restart() { const active = this.running || Boolean(this.starting); this.pause(); - if (this.graph && !this.graph.ownsMediaConnection) - return this.stop().then(() => active ? this.start() : void 0); + if (this.graph && !this.graph.ownsMediaConnection) { + const stopping = this.stop(); + const epoch = this.epoch; + return stopping.then(() => active && !this.terminal && this.epoch === epoch ? this.start() : void 0); + } return active ? this.start() : Promise.resolve(); } start() { @@ -260,7 +268,7 @@ class Capture { const count = Math.floor(sampleRate * interval / 1e3); if (!Number.isSafeInteger(count) || count < 1 || !Number.isFinite(interval) || interval <= 0) throw new Error("Audio chunk length must be positive and finite"); - const graph = this.graph ?? (this.graph = new AudioGraph(this.video, sampleRate)); + const graph = this.graph ?? (this.graph = new AudioGraph(this.video, sampleRate, this.options.captureOnly)); await graph.prepare(); if (!current()) { if (!this.running && this.graph === graph) @@ -352,13 +360,16 @@ function createSubtitles(layer, length, autoHideTimeout) { }; } function artplayerPluginAsr(option = {}) { - const { length = 3, interval = 100, sampleRate = 16e3, autoHideTimeout = 1e4, onAudioChunk = () => null } = option; + const { length = 3, interval = 100, sampleRate = 16e3, autoHideTimeout = 1e4, onAudioChunk = () => null, audioInput } = option; + if (audioInput !== void 0 && audioInput.type !== "capture") + throw new TypeError("Unsupported ASR audio input"); + const captureOnly = audioInput?.type === "capture"; return (art) => { const layer = art.layers.add({ name: "asr", html: "" }); if (!layer) throw new Error("Could not create ASR subtitle layer"); const subtitles = createSubtitles(layer, length, autoHideTimeout); - const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk }, subtitles.append); + const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk, captureOnly }, subtitles.append); const play = () => capture.start(); const pause = () => capture.pause(); const restart = () => capture.restart(); diff --git a/package.json b/package.json index 9cc1ea159..a5098dae3 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports", "test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js test/asr-distribution.test.js", "typecheck": "node scripts/typecheck.mjs", - "test:unit": "node --test test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js", + "test:unit": "node --test test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js", "test:coverage": "node --test test/coverage.test.js && node scripts/coverage.mjs", "test": "yarn test:node && yarn test:baseline", "test:browser": "playwright test", @@ -94,7 +94,7 @@ "check:ci": "node refactor/scripts/ci-workflow.mjs", "test:ci": "node --test test/ci-summary.test.js refactor/scripts/ci-workflow.test.mjs refactor/scripts/impact.test.mjs test/package-runtime.test.js", "test:package:runtime": "node scripts/package-runtime.mjs --canonical", - "test:asr": "node --test test/asr.test.js test/asr-audio.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-distribution.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js", + "test:asr": "node --test test/asr.test.js test/asr-audio.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-distribution.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js", "test:asr-types-package": "node refactor/scripts/asr-package-types.mjs", "test:asr-demo": "node refactor/scripts/asr-local-demo.mjs" }, diff --git a/packages/artplayer-plugin-asr/ARCHITECTURE.md b/packages/artplayer-plugin-asr/ARCHITECTURE.md index ee3bc3e22..91a055f39 100644 --- a/packages/artplayer-plugin-asr/ARCHITECTURE.md +++ b/packages/artplayer-plugin-asr/ARCHITECTURE.md @@ -70,6 +70,18 @@ This lifetime follows the [Web Audio close semantics](https://www.w3.org/TR/weba and [media-element source behavior](https://www.w3.org/TR/webaudio-1.0/#MediaElementAudioSourceNode). Captured-stream source changes follow [Media Capture from DOM Elements](https://www.w3.org/TR/mediacapture-fromelement/#html-media-element-media-capture-extensions). +The optional `audioInput: { type: 'capture' }` snapshots a capture-only strategy at +factory creation. It skips direct binding entirely and reuses the silent capture +route, with no fallback to media ownership after capture failure. This supports +external owners on Firefox without pretending that default duplicate ownership +is detected automatically. Captured PCM ignores element mute/volume and does not +include processing done in another graph. The browser's CORS checks still apply. + +A captured-source restart snapshots the epoch after stopping its previous graph. +When closing settles, a later pause/stop/restart/destroy must invalidate that +continuation. Otherwise a paused player could silently resume recognition. Keep +the delayed-close regressions when changing this state machine. + Queued audio is limited to the larger of 60 seconds or two configured chunks. A caller that cannot keep up gets an explicit console error and capture pauses, leaving normal playback routed. This avoids an unbounded backlog; it does not claim lossless @@ -112,6 +124,11 @@ From the repository root: - `yarn test:browser test/browser/asr-fallback.spec.js`: native capture/Worklet audio with an external media owner, silent ASR output even while muted, stop, fresh tracks after restart/source change and independent context cleanup. + Explicit capture cases must have zero direct calls and no forced exception on + Firefox; only default fallback coverage uses controlled rejection there. +- `yarn test:browser test/browser/asr-capture-cors.spec.js`: explicit capture + respects native cross-origin restrictions without changing media properties or + binding its playback route, then recovers on same-origin source change. - `yarn test:browser test/browser/asr-cors.spec.js`: CORS-authorized media, opaque direct/redirected media, native zero-output or absent-chunk observations, and restoration after switching to a same-origin source. Default routing keeps diff --git a/packages/artplayer-plugin-asr/README.md b/packages/artplayer-plugin-asr/README.md index ff9b13fdf..ff0bdf353 100644 --- a/packages/artplayer-plugin-asr/README.md +++ b/packages/artplayer-plugin-asr/README.md @@ -52,6 +52,36 @@ independent `audio` element is not mixed into recognition. Muting the main video silences its default ASR input even if a consumer separately unmutes that audio element. Both plugins keep their existing volume and playback APIs. +## Existing audio graphs + +When another library already routes the main video through Web Audio, explicitly +select capture mode to avoid taking ownership of its playback connection: + +```ts +import asr from 'artplayer-plugin-asr/runtime' + +const plugin = asr({ audioInput: { type: 'capture' }, onAudioChunk: ({ pcm }) => { + // Consume main-media PCM without adding a second speaker route. + void pcm +} }) +``` + +Omitting `audioInput` keeps the existing direct-first behavior. Explicit capture +uses `captureStream`/`mozCaptureStream` and never falls back to direct binding, +including on failure. It owns its captured tracks and Context, not the other +library's playback graph. Pause retains that capture graph; stop and source +changes release it, and subsequent play obtains a new stream. An unsupported +capture source reports an initialization error and releases acquired resources. + +Captured PCM is independent of the media element's volume and mute. It is the +main media stream, not the output of another library's effects or an independent +audio-track element. CORS restrictions still apply: Chromium rejects restricted +capture; Firefox may expose a track without delivering PCM. ASR leaves the +original media route alone in either case. This mode does not automatically +detect external owners or repair the default mode's restricted-media silence. +For TypeScript, this additive option is exposed through `/runtime`; historical +root and `/legacy` option declarations remain unchanged. + ## Maintenance See [ARCHITECTURE.md](ARCHITECTURE.md) for the current audio flow, public behavior, diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js index fa6df2967..d3b1b4ace 100644 --- a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const r=this.gain=s||e.createGain();s||(r.gain.value=this.direct?1:0);const n=this.recorder=new AudioWorkletNode(e,"recorder-processor");n.port.onmessage=e=>{this.closed||this.recorder!==n||t(e.data)},i.connect(n),this.direct?i.connect(r):n.connect(r),r.connect(e.destination)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,r=Math.floor(e*s/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const n=this.graph??(this.graph=new t(this.video,e));await n.prepare(),i()?(n.connect(t=>{if(this.running&&this.graph===n)return this.queue.length+t.length>Math.max(60*e,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),s)):this.running||this.graph!==n||n.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:r=16e3,autoHideTimeout:n=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,r=!1;function n(){clearTimeout(s),s=void 0}function o(){r||(t.style.display="none")}return{append:function(a){r||"string"!==typeof a||(n(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){r=!0,n()}}}(a,e,n),c=new i(t.video,{interval:s,sampleRate:r,onAudioChunk:o},h.append),l=()=>c.start(),d=()=>c.pause(),p=()=>c.restart(),u=()=>(h.destroy(),t.off("play",l),t.off("pause",d),t.off("restart",p),t.off("destroy",u),c.stop(!0));return t.on("play",l),t.on("pause",d),t.on("restart",p),t.on("destroy",u),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e,i=!1){this.video=t,this.sampleRate=e,this.captureOnly=i,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source){if(!this.captureOnly)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s)}if(!this.source){const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const r=this.gain=s||e.createGain();s||(r.gain.value=this.direct?1:0);const n=this.recorder=new AudioWorkletNode(e,"recorder-processor");n.port.onmessage=e=>{this.closed||this.recorder!==n||t(e.data)},i.connect(n),this.direct?i.connect(r):n.connect(r),r.connect(e.destination)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it&&!this.terminal&&this.epoch===i?this.start():void 0)}return t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,r=Math.floor(e*s/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const n=this.graph??(this.graph=new t(this.video,e,this.options.captureOnly));await n.prepare(),i()?(n.connect(t=>{if(this.running&&this.graph===n)return this.queue.length+t.length>Math.max(60*e,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),s)):this.running||this.graph!==n||n.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:r=16e3,autoHideTimeout:n=1e4,onAudioChunk:o=()=>null,audioInput:a}=t;if(void 0!==a&&"capture"!==a.type)throw new TypeError("Unsupported ASR audio input");const h="capture"===a?.type;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const c=function(t,e,i){let s,r=!1;function n(){clearTimeout(s),s=void 0}function o(){r||(t.style.display="none")}return{append:function(a){r||"string"!==typeof a||(n(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){r=!0,n()}}}(a,e,n),l=new i(t.video,{interval:s,sampleRate:r,onAudioChunk:o,captureOnly:h},c.append),d=()=>l.start(),p=()=>l.pause(),u=()=>l.restart(),f=()=>(c.destroy(),t.off("play",d),t.off("pause",p),t.off("restart",u),t.off("destroy",f),l.stop(!0));return t.on("play",d),t.on("pause",p),t.on("restart",u),t.on("destroy",f),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:c.hide,append:c.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js index 09aaa34ad..53ad308e2 100644 --- a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=this.direct?1:0);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),this.direct?i.connect(n):r.connect(n),n.connect(e.destination)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),l=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o},h.append),c=()=>l.start(),d=()=>l.pause(),u=()=>l.restart(),p=()=>(h.destroy(),t.off("play",c),t.off("pause",d),t.off("restart",u),t.off("destroy",p),l.stop(!0));return t.on("play",c),t.on("pause",d),t.on("restart",u),t.on("destroy",p),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e,i=!1){this.video=t,this.sampleRate=e,this.captureOnly=i,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source){if(!this.captureOnly)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n)}if(!this.source){const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=this.direct?1:0);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),this.direct?i.connect(n):r.connect(n),n.connect(e.destination)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it&&!this.terminal&&this.epoch===i?this.start():void 0)}return t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i,this.options.captureOnly);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null,audioInput:a}=t;if(void 0!==a&&"capture"!==a.type)throw new TypeError("Unsupported ASR audio input");const h="capture"===(null==a?void 0:a.type);return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const l=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),c=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o,captureOnly:h},l.append),d=()=>c.start(),u=()=>c.pause(),p=()=>c.restart(),f=()=>(l.destroy(),t.off("play",d),t.off("pause",u),t.off("restart",p),t.off("destroy",f),c.stop(!0));return t.on("play",d),t.on("pause",u),t.on("restart",p),t.on("destroy",f),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:l.hide,append:l.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs index 3fdbb9574..baa8911a1 100644 --- a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs @@ -17,9 +17,10 @@ class RecorderProcessor extends AudioWorkletProcessor { registerProcessor('recorder-processor', RecorderProcessor); `; class AudioGraph { - constructor(video, sampleRate) { + constructor(video, sampleRate, captureOnly = false) { this.video = video; this.sampleRate = sampleRate; + this.captureOnly = captureOnly; this.closed = false; this.direct = false; this.workletLoaded = false; @@ -55,11 +56,15 @@ class AudioGraph { } this.assertOpen(); if (!this.source) { - try { - this.source = context.createMediaElementSource(this.video); - this.direct = true; - } catch (error) { - console.warn("[artplayerPluginAsr] Direct connection failed:", error); + if (!this.captureOnly) { + try { + this.source = context.createMediaElementSource(this.video); + this.direct = true; + } catch (error) { + console.warn("[artplayerPluginAsr] Direct connection failed:", error); + } + } + if (!this.source) { const capture = this.video.captureStream || this.video.mozCaptureStream; if (!capture) throw new Error("Could not establish audio source"); @@ -241,8 +246,11 @@ class Capture { restart() { const active = this.running || Boolean(this.starting); this.pause(); - if (this.graph && !this.graph.ownsMediaConnection) - return this.stop().then(() => active ? this.start() : void 0); + if (this.graph && !this.graph.ownsMediaConnection) { + const stopping = this.stop(); + const epoch = this.epoch; + return stopping.then(() => active && !this.terminal && this.epoch === epoch ? this.start() : void 0); + } return active ? this.start() : Promise.resolve(); } start() { @@ -260,7 +268,7 @@ class Capture { const count = Math.floor(sampleRate * interval / 1e3); if (!Number.isSafeInteger(count) || count < 1 || !Number.isFinite(interval) || interval <= 0) throw new Error("Audio chunk length must be positive and finite"); - const graph = this.graph ?? (this.graph = new AudioGraph(this.video, sampleRate)); + const graph = this.graph ?? (this.graph = new AudioGraph(this.video, sampleRate, this.options.captureOnly)); await graph.prepare(); if (!current()) { if (!this.running && this.graph === graph) @@ -352,13 +360,16 @@ function createSubtitles(layer, length, autoHideTimeout) { }; } function artplayerPluginAsr(option = {}) { - const { length = 3, interval = 100, sampleRate = 16e3, autoHideTimeout = 1e4, onAudioChunk = () => null } = option; + const { length = 3, interval = 100, sampleRate = 16e3, autoHideTimeout = 1e4, onAudioChunk = () => null, audioInput } = option; + if (audioInput !== void 0 && audioInput.type !== "capture") + throw new TypeError("Unsupported ASR audio input"); + const captureOnly = audioInput?.type === "capture"; return (art) => { const layer = art.layers.add({ name: "asr", html: "" }); if (!layer) throw new Error("Could not create ASR subtitle layer"); const subtitles = createSubtitles(layer, length, autoHideTimeout); - const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk }, subtitles.append); + const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk, captureOnly }, subtitles.append); const play = () => capture.start(); const pause = () => capture.pause(); const restart = () => capture.restart(); diff --git a/packages/artplayer-plugin-asr/src/audio-graph.ts b/packages/artplayer-plugin-asr/src/audio-graph.ts index c6e3d7eb2..99876f4e8 100644 --- a/packages/artplayer-plugin-asr/src/audio-graph.ts +++ b/packages/artplayer-plugin-asr/src/audio-graph.ts @@ -13,7 +13,7 @@ export class AudioGraph { private direct = false private workletLoaded = false - constructor(private readonly video: CaptureVideo, private readonly sampleRate: number) {} + constructor(private readonly video: CaptureVideo, private readonly sampleRate: number, private readonly captureOnly = false) {} private assertOpen() { if (this.closed) @@ -49,12 +49,16 @@ export class AudioGraph { } this.assertOpen() if (!this.source) { - try { - this.source = context.createMediaElementSource(this.video) - this.direct = true + if (!this.captureOnly) { + try { + this.source = context.createMediaElementSource(this.video) + this.direct = true + } + catch (error) { + console.warn('[artplayerPluginAsr] Direct connection failed:', error) + } } - catch (error) { - console.warn('[artplayerPluginAsr] Direct connection failed:', error) + if (!this.source) { const capture = this.video.captureStream || this.video.mozCaptureStream if (!capture) throw new Error('Could not establish audio source') diff --git a/packages/artplayer-plugin-asr/src/capture.ts b/packages/artplayer-plugin-asr/src/capture.ts index 19037a0c7..b9533d43b 100644 --- a/packages/artplayer-plugin-asr/src/capture.ts +++ b/packages/artplayer-plugin-asr/src/capture.ts @@ -34,8 +34,11 @@ export class Capture { restart() { const active = this.running || Boolean(this.starting) this.pause() - if (this.graph && !this.graph.ownsMediaConnection) - return this.stop().then(() => active ? this.start() : undefined) + if (this.graph && !this.graph.ownsMediaConnection) { + const stopping = this.stop() + const epoch = this.epoch + return stopping.then(() => active && !this.terminal && this.epoch === epoch ? this.start() : undefined) + } return active ? this.start() : Promise.resolve() } @@ -54,7 +57,7 @@ export class Capture { const count = Math.floor(sampleRate * interval / 1000) if (!Number.isSafeInteger(count) || count < 1 || !Number.isFinite(interval) || interval <= 0) throw new Error('Audio chunk length must be positive and finite') - const graph = this.graph ??= new AudioGraph(this.video, sampleRate) + const graph = this.graph ??= new AudioGraph(this.video, sampleRate, this.options.captureOnly) await graph.prepare() if (!current()) { if (!this.running && this.graph === graph) diff --git a/packages/artplayer-plugin-asr/src/index.ts b/packages/artplayer-plugin-asr/src/index.ts index fd05f0a84..cce94e8ff 100644 --- a/packages/artplayer-plugin-asr/src/index.ts +++ b/packages/artplayer-plugin-asr/src/index.ts @@ -5,13 +5,16 @@ import style from './style.less?inline' import { createSubtitles } from './subtitles' export default function artplayerPluginAsr(option: AsrOptions = {}) { - const { length = 3, interval = 100, sampleRate = 16000, autoHideTimeout = 10000, onAudioChunk = () => null } = option + const { length = 3, interval = 100, sampleRate = 16000, autoHideTimeout = 10000, onAudioChunk = () => null, audioInput } = option + if (audioInput !== undefined && audioInput.type !== 'capture') + throw new TypeError('Unsupported ASR audio input') + const captureOnly = audioInput?.type === 'capture' return (art: Artplayer): AsrResult => { const layer = art.layers.add({ name: 'asr', html: '' }) if (!layer) throw new Error('Could not create ASR subtitle layer') const subtitles = createSubtitles(layer, length, autoHideTimeout) - const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk }, subtitles.append) + const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk, captureOnly }, subtitles.append) const play = () => capture.start() const pause = () => capture.pause() const restart = () => capture.restart() diff --git a/packages/artplayer-plugin-asr/src/types.ts b/packages/artplayer-plugin-asr/src/types.ts index 02afd3366..8a4fa6751 100644 --- a/packages/artplayer-plugin-asr/src/types.ts +++ b/packages/artplayer-plugin-asr/src/types.ts @@ -4,6 +4,7 @@ export interface AudioChunk { } export interface AsrOptions { + audioInput?: { type: 'capture' } length?: number interval?: number sampleRate?: number @@ -24,6 +25,7 @@ export interface CaptureVideo extends HTMLVideoElement { } export interface CaptureOptions { + captureOnly?: boolean interval: number sampleRate: number onAudioChunk: (chunk: AudioChunk) => unknown diff --git a/packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts b/packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts index 0cf2329ab..92e21b53f 100644 --- a/packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts +++ b/packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts @@ -25,6 +25,8 @@ export type Factory = (option?: AsrPluginOption) => (art: Artplayer) => AsrPlugi /** Accurate asynchronous view available through the /runtime entry. */ export interface RuntimeOption extends Omit { + /** Capture the media stream without taking ownership of its playback route. */ + audioInput?: { type: 'capture' } onAudioChunk?: (chunk: AudioChunk) => string | void | null | Promise } diff --git a/refactor/baselines/asr-explicit-capture-validation.json b/refactor/baselines/asr-explicit-capture-validation.json new file mode 100644 index 000000000..d503362f8 --- /dev/null +++ b/refactor/baselines/asr-explicit-capture-validation.json @@ -0,0 +1,12227 @@ +{ + "schemaVersion": 1, + "task": "PKG-ASR-09", + "capturedAt": "2026-09-13T08:19:08.736Z", + "baseCommit": "5ab7da6e1f891c5f674033cea38845bae8acb0e8", + "addition": "Optional audioInput: { type: capture } uses native stream capture without any direct media binding or audible ASR route. Runtime types only; historical option and factory declarations unchanged.", + "correction": "An epoch captured after stopping invalidates delayed source restart when later pause/stop/restart/destroy supersedes it.", + "versionChanged": false, + "dependenciesAdded": [], + "red": { + "initialStrategy": { + "passed": 2, + "failed": 9 + }, + "delayedRestart": { + "passed": 11, + "failed": 2 + } + }, + "browsers": [ + { + "mode": "source", + "file": "refactor/.cache/asr09-browser-source/report.json", + "sha256": "c5d966d8c6b56ed5ea0d13240dba33431a3a50eec55f831c268c133b2602ed0a", + "passed": 10, + "skipped": 5, + "rows": [ + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "core": "published", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "plugin": "workspace source", + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + } + ], + "directBindings": 0, + "contexts": [ + "closed" + ], + "streams": [], + "retainedTracks": [], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [ + "[artplayerPluginAsr] Initialization failed: SecurityError: Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data\n at prototype. [as captureStream] (eval at evaluate (:311:30), :29:40)\n at AudioGraph.initialize (:69:33)\n at async :270:9" + ], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + }, + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4215, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4237, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4251, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "plugin": "workspace source", + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + } + ], + "directBindings": 0, + "contexts": [ + "closed" + ], + "streams": [], + "retainedTracks": [], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [ + "[artplayerPluginAsr] Initialization failed: SecurityError: Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data\n at prototype. [as captureStream] (eval at evaluate (:311:30), :29:40)\n at AudioGraph.initialize (:69:33)\n at async :270:9" + ], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + }, + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4168, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4237, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4251, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "core": "published", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "plugin": "workspace source", + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "running" + ], + "streams": [ + { + "active": true, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": true, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4251, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4219, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4248, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "plugin": "workspace source", + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "running" + ], + "streams": [ + { + "active": true, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": true, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4213, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4245, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4190, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "core": "published", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "plugin": "workspace source", + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks AudioContext/AudioWorklet; this is not Safari/device CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-capture-cors.spec.js", + "line": 41, + "column": 12 + } + } + ] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "plugin": "workspace source", + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks AudioContext/AudioWorklet; this is not Safari/device CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-capture-cors.spec.js", + "line": 41, + "column": 12 + } + } + ] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.08772112125105311, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.0889365128761472, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08762481388661633, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08862216379888067, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08779609911638686, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4238, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.0889365128761472, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.0880712446666184, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08866202715264984, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.0880712446666184, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08774571319741767, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08789785569817585, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08789785569817585, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "f5fcf901c7e8c197f72d44ed6d7e4445cbad0c6ce2258d9127a765712d75b010", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + } + ] + }, + { + "mode": "main", + "file": "refactor/.cache/asr09-browser-main/report.json", + "sha256": "c40bb98d35186aa807e8e8a652f5d583d86c339a3cec00005ec342ba80f13938", + "passed": 48, + "skipped": 24, + "rows": [ + { + "title": "published core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": true + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": true + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": true + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": true + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": true + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "published core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "candidate core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": true + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "candidate core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "ASR candidate + published-5.3.0: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "core": "published", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + } + ], + "directBindings": 0, + "contexts": [ + "closed" + ], + "streams": [], + "retainedTracks": [], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [ + "[artplayerPluginAsr] Initialization failed: SecurityError: Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data\n at prototype. [as captureStream] (eval at evaluate (:311:30), :29:40)\n at t.initialize (:7:1749)\n at async :7:4905" + ], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + }, + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4168, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4237, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4251, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + } + ], + "directBindings": 0, + "contexts": [ + "closed" + ], + "streams": [], + "retainedTracks": [], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [ + "[artplayerPluginAsr] Initialization failed: SecurityError: Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data\n at prototype. [as captureStream] (eval at evaluate (:311:30), :29:40)\n at t.initialize (:7:1749)\n at async :7:4905" + ], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + }, + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4249, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4224, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4179, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "core": "published", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "running" + ], + "streams": [ + { + "active": true, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": true, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4242, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4184, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4247, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "running" + ], + "streams": [ + { + "active": true, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": true, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4219, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4248, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4183, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "core": "published", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks AudioContext/AudioWorklet; this is not Safari/device CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-capture-cors.spec.js", + "line": 41, + "column": 12 + } + } + ] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks AudioContext/AudioWorklet; this is not Safari/device CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-capture-cors.spec.js", + "line": 41, + "column": 12 + } + } + ] + }, + { + "title": "ASR candidate + published: anonymous-cors native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "anonymous-cors", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0.12774023413658142, + "chunks": [ + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4250, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4248, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.913627, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12964209914207458, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.392, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + } + ], + "time": 0.906667, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12964209914207458, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.384, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque-redirect native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque-redirect", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + } + ], + "time": 0.886667, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12964209914207458, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.392, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: anonymous-cors native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "anonymous-cors", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0.1273600161075592, + "chunks": [ + { + "max": 4260, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4190, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.892382, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12964209914207458, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.384, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + } + ], + "time": 0.886667, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12970227003097534, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.376, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque-redirect native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque-redirect", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + } + ], + "time": 0.874667, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12970227003097534, + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.373421, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: anonymous-cors native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "anonymous-cors", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0.12736152112483978, + "chunks": [ + { + "max": 4190, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4248, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.868833, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.368, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 1.968, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.368, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque-redirect native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque-redirect", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 1.984, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.368, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: anonymous-cors native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "anonymous-cors", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0.12966568768024445, + "chunks": [ + { + "max": 4190, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4248, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.82902, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.4, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 2.011666, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12976472079753876, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.36, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque-redirect native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque-redirect", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 1.984, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.368, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: anonymous-cors native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + published: opaque native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + published: opaque-redirect native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + candidate: anonymous-cors native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + candidate: opaque native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + candidate: opaque-redirect native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + published-5.3.0: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.08779609911638686, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.0889365128761472, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4158, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4250, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08779609911638686, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4305, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08862216379888067, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4299, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08772112125105311, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08862216379888067, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.08759580548385439, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.0889365128761472, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08759580548385439, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08869046588295236, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4211, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08762481388661633, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08869046588295236, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": true, + "reason": "Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.08791119445452536, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08789785569817585, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": true, + "reason": "Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08778351516353888, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08791736763772941, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": true, + "reason": "Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08906111312585639, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08895963860169126, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.08895963860169126, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08866202715264984, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08808024336011813, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08789785569817585, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08830393306342403, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08778351516353888, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": false, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published-5.3.0: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + } + ] + }, + { + "mode": "legacy", + "file": "refactor/.cache/asr09-browser-legacy/report.json", + "sha256": "78282539f961f4364be29145d9c7fb3178bfdf310c7e892da47c9a6d1ac91c38", + "passed": 48, + "skipped": 24, + "rows": [ + { + "title": "published core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": true + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": true + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": true + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": true + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "candidate core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "published core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": true + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "published core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "published", + "audioFirst": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "candidate core + ASR/audio-track: pause, update, ASR stop and destroy preserve separate ownership", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": true + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "candidate core + ASR/audio-track: capture follows main media, not the independent audible track", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "asr": { + "input": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697" + }, + "audioTrack": { + "input": "workspace source", + "sha256": "c4b37388ecf38c4c33dc172895d2d76d1ffca2587628c01b11b38cfe1852c713" + }, + "media": { + "file": "test/browser/media/audio-tone.m4a", + "sha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "bytes": 132436 + }, + "recognitionService": false, + "scope": "Native media/WebAudio composition; not physical speaker, device, or isolated package acceptance", + "core": "candidate", + "audioFirst": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorklet; Safari and physical-device composition remain unverified", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio-track.spec.js", + "line": 35, + "column": 10 + } + } + ] + }, + { + "title": "ASR candidate + published-5.3.0: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "core": "published", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + } + ], + "directBindings": 0, + "contexts": [ + "closed" + ], + "streams": [], + "retainedTracks": [], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [ + "[artplayerPluginAsr] Initialization failed: SecurityError: Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data\n at prototype. [as captureStream] (eval at evaluate (:311:30), :29:40)\n at e. (:7:1996)\n at Generator.next ()\n at r (:7:364)" + ], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + }, + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4241, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4215, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4237, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + } + ], + "directBindings": 0, + "contexts": [ + "closed" + ], + "streams": [], + "retainedTracks": [], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [ + "[artplayerPluginAsr] Initialization failed: SecurityError: Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data\n at prototype. [as captureStream] (eval at evaluate (:311:30), :29:40)\n at e. (:7:1996)\n at Generator.next ()\n at r (:7:364)" + ], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": { + "name": "SecurityError", + "message": "Failed to execute 'captureStream' on 'HTMLMediaElement': Cannot capture from element with cross-origin data" + } + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + }, + { + "active": false, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4154, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4202, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4251, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "core": "published", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "running" + ], + "streams": [ + { + "active": true, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": true, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4256, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4221, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4213, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "opaque": { + "opaque": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "running" + ], + "streams": [ + { + "active": true, + "tracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "live" + } + ], + "chunks": [] + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "status": 206, + "allowOrigin": null + } + ], + "consoleErrors": [], + "properties": { + "src": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "crossOrigin": null, + "crossOriginAttribute": null, + "muted": false, + "defaultMuted": false, + "volume": 0.6, + "playbackRate": 1, + "autoplay": false, + "controls": false, + "loop": false, + "preload": "metadata", + "playsInline": true + }, + "clockAdvanced": true + }, + "explicitFinal": { + "attempts": [ + { + "source": "http://localhost:8084/test/audio-tone.m4a?capture-cors=opaque", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "error": null + } + ], + "directBindings": 0, + "contexts": [ + "closed", + "closed", + "closed" + ], + "streams": [ + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + }, + { + "active": false, + "tracks": [] + } + ], + "retainedTracks": [ + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": true, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + }, + { + "kind": "audio", + "muted": false, + "enabled": true, + "readyState": "ended" + } + ], + "chunks": [ + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4304, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4204, + "pcmBytes": 3200, + "wavBytes": 3244 + }, + { + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?capture-cors=recovery", + "peak": 4238, + "pcmBytes": 3200, + "wavBytes": 3244 + } + ] + }, + "annotations": [] + }, + { + "title": "ASR explicit capture + published: opaque media keeps native routing and same-origin capture recovers", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "core": "published", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks AudioContext/AudioWorklet; this is not Safari/device CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-capture-cors.spec.js", + "line": 41, + "column": 12 + } + } + ] + }, + { + "title": "ASR explicit capture + candidate: opaque media keeps native routing and same-origin capture recovers", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "core": "candidate", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "plugin": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "nativeCapture": true, + "forcedException": false, + "analyserBinding": false, + "physicalAudioOutput": false, + "scope": "Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output" + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks AudioContext/AudioWorklet; this is not Safari/device CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-capture-cors.spec.js", + "line": 41, + "column": 12 + } + } + ] + }, + { + "title": "ASR candidate + published: anonymous-cors native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "anonymous-cors", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0.1273600161075592, + "chunks": [ + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4250, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4248, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.904151, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12970227003097534, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.376, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + } + ], + "time": 0.914667, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12964209914207458, + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.384, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque-redirect native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque-redirect", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + } + ], + "time": 0.902278, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12970227003097534, + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.374666, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: anonymous-cors native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "anonymous-cors", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0.1273600161075592, + "chunks": [ + { + "max": 4260, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4190, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.89359, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12964209914207458, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.390899, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + }, + { + "max": 0, + "source": "http://localhost:8084/test/audio-tone.m4a" + } + ], + "time": 0.866667, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12970227003097534, + "chunks": [ + { + "max": 4290, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.376, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque-redirect native audio access and source recovery", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque-redirect", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [ + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + }, + { + "max": 0, + "source": "http://127.0.0.1:8084/test/asr-opaque-redirect" + } + ], + "time": 0.913871, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12964209914207458, + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.392, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: anonymous-cors native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "anonymous-cors", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0.12966568768024445, + "chunks": [ + { + "max": 4190, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4248, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.848, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.368, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 1.006958, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.376, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: opaque-redirect native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "scenario": "opaque-redirect", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 1.976, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.376, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: anonymous-cors native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "anonymous-cors", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0.12966568768024445, + "chunks": [ + { + "max": 4190, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4255, + "source": "http://localhost:8084/test/asr-cors.m4a" + }, + { + "max": 4248, + "source": "http://localhost:8084/test/asr-cors.m4a" + } + ], + "time": 0.808, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.36, + "paused": false, + "error": null, + "crossOrigin": "anonymous" + }, + "responses": [ + { + "url": "http://localhost:8084/test/asr-cors.m4a", + "status": 206, + "allowOrigin": "*" + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 1.972333, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.368, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: opaque-redirect native audio access and source recovery", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "scenario": "opaque-redirect", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "physicalAudioOutput": false, + "securityBypass": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "cors": { + "initial": { + "outputMax": 0, + "chunks": [], + "time": 2.001979, + "paused": false, + "error": null, + "crossOrigin": null + }, + "recovery": { + "outputMax": 0.12957505881786346, + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery" + } + ], + "time": 0.376, + "paused": false, + "error": null, + "crossOrigin": null + }, + "responses": [ + { + "url": "http://127.0.0.1:8084/test/asr-opaque-redirect", + "status": 302, + "allowOrigin": null + }, + { + "url": "http://localhost:8084/test/audio-tone.m4a", + "status": 206, + "allowOrigin": null + }, + { + "url": "http://127.0.0.1:8084/test/audio-tone.m4a?asr-cors=recovery", + "status": 206, + "allowOrigin": null + } + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: anonymous-cors native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + published: opaque native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + published: opaque-redirect native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + candidate: anonymous-cors native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + candidate: opaque native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + candidate: opaque-redirect native audio access and source recovery", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native WebAudio; this is not Safari CORS acceptance", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-cors.spec.js", + "line": 26, + "column": 16 + } + } + ] + }, + { + "title": "ASR candidate + published-5.3.0: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.08916705692979744, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4233, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4393, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08869046588295236, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4312, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08864518732424151, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4310, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08869046588295236, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4247, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08759580548385439, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08862216379888067, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4245, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + }, + { + "name": "InvalidStateError", + "message": "Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode." + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.08916705692979744, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4233, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08896275868833134, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4250, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08759580548385439, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08869046588295236, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4251, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08876977731692302, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4157, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08862216379888067, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4192, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4263, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4250, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": true, + "reason": "Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.0880712446666184, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08866202715264984, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": true, + "reason": "Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08830393306342403, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08866202715264984, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": true, + "reason": "Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08791119445452536, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4272, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4280, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4264, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 1 + } + }, + "restart": { + "external": 0.08895963860169126, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [ + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + }, + { + "name": "InvalidStateError", + "message": "Controlled duplicate binding rejection for native capture test" + } + ], + "directCalls": 3 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "fallback": { + "playing": { + "external": 0.0883604767030335, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4215, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4253, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08895963860169126, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4187, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "fallback": { + "playing": { + "external": 0.08866202715264984, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4183, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08789785569817585, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4260, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "function", + "worklet": "function", + "capture": "function", + "mozCapture": "function" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "fallback": { + "playing": { + "external": 0.08866202715264984, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4303, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4252, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "muted": { + "external": 0, + "asr": [ + 0 + ], + "contexts": [ + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + } + }, + "restart": { + "external": 0.08774571319741767, + "asr": [ + 0, + 0, + 0 + ], + "contexts": [ + "closed", + "closed", + "running" + ], + "owner": "running", + "streams": [ + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "ended" + } + ], + [ + { + "kind": "audio", + "state": "live" + } + ] + ], + "chunks": [ + { + "max": 4190, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4255, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + }, + { + "max": 4248, + "source": "http://127.0.0.1:8084/test/audio-tone.m4a?asr=fallback-second", + "samples": 1600 + } + ], + "failures": [], + "directCalls": 0 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": false, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Native duplicate binding rejection", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + published-5.3.0: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + published: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate capture + candidate: native capture fallback does not create a second audible route", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "captureOnly": true, + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "capabilities": { + "context": "undefined", + "worklet": "undefined", + "capture": "undefined", + "mozCapture": "undefined" + }, + "forcedException": false, + "reason": "Explicit capture mode never binds the element; no forced exceptions", + "externalContext": "Native media-element source remains owned and running", + "physicalOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit has no native WebAudio", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-fallback.spec.js", + "line": 26, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published-5.3.0: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published-5.3.0", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.3.0", + "integrity": "sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==", + "member": "package/dist/artplayer.js" + }, + "sha256": "54ffaf7598b910d5a88d2d2eec1aa6faa806583915b99b892dd32129c53e421a", + "bytes": 159238 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "core": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "d9aeee649395b78eb30b38dcaefb3c19c731c21b08e356485bac7cda08ead242", + "bytes": 378117 + }, + "annotations": [] + }, + { + "title": "ASR candidate + published-5.3.0: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + } + ] + } + ], + "totals": { + "passed": 106, + "capabilitySkipped": 53, + "failed": 0, + "flaky": 0 + }, + "unit": { + "focused": 229, + "newExplicitCapture": 13, + "main": 43, + "legacy": 43 + }, + "artifacts": [ + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "bytes": 9034, + "docsCopyIdentical": true + }, + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "bytes": 9527, + "docsCopyIdentical": true + }, + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs", + "sha256": "1b7bac676d2c4976046b08fd7c1ec25724ff24952f5d93fc546e219466d975d8", + "bytes": 13595, + "docsCopyIdentical": true + } + ], + "installed": { + "file": "refactor/.cache/asr-package-types-swwh0u/report.json", + "sha256": "a2736bfb24fa469e2944d0dc1abf761fb4cb26ab6d37f45c1dcaea165f1964b9", + "cases": 17, + "candidateConfigurations": 7, + "invalidPerCandidate": 18, + "packages": [ + { + "name": "artplayer", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\asr-package-types-swwh0u\\artplayer.tgz", + "sha256": "0c0ea52d41575261b836aefac68280733b119c75834ecad6d4bc78edb073f4c1", + "files": { + "package/ARCHITECTURE.md": "bee98fc9d93a3911d58e98ef15f2dbe07e12f2274c444d989094248a22079e46", + "package/README.md": "1e185e65f67fc2081b4182bbc6c4558ea05b6e3fa3acc5dd0c3ac7df1d542b09", + "package/THIRD_PARTY_NOTICES": "b77b5b46b79fa0cda850b0781f78c3b1112c4ed9672fa291956fe8eff53098e7", + "package/dist/artplayer.js": "6dc8ca6d6eb34aa8a056cbc31328efd634530b3c5c60f0014cb7ce719fabc335", + "package/dist/artplayer.legacy.js": "80f74a8a39df4baf64cc00c65d21cd57c011bfcdead0da38eea077fbc67c5c04", + "package/dist/artplayer.mjs": "432e2b0ad92009a09743e020bf7c928c8b8f18a52496622b16259ec9ab20059f", + "package/dist/i18n/ar.js": "bd0bef65f4f7dc3f8ed562904cc6fa9071ff4537fb64c1e0fc3d554141bcd3cb", + "package/dist/i18n/ar.mjs": "59310b7bbeea83bbb7d73aadc5ca82a6ec269c3530d19aa741c94d5c64fc5da8", + "package/dist/i18n/cs.js": "6e2c93daba24c2e48dfa206bcf4f2f2a8a02713b856e404d2e4e4ecd8309ca2f", + "package/dist/i18n/cs.mjs": "c73fba18797d62d7b2581e67fe7340c35fe84510b2250ff1ecaad253d9cabbac", + "package/dist/i18n/es.js": "f4133ac6ea06b69a474b33c4a1809a4066e2376bb9464a3f3aa8330f01f1944a", + "package/dist/i18n/es.mjs": "1390bca3def2be115e811de337df0a0c3f63998e51359f283023246245c552a7", + "package/dist/i18n/fa.js": "8d3ef2e0f0cc0ea480c2167b6fcb87cdf444e90759c3519e8daa139fec576da4", + "package/dist/i18n/fa.mjs": "ab441e8a2d79043b29ceca87c783bf06874bb9d3a149c1604bc167eb481be2a9", + "package/dist/i18n/fr.js": "89dc4194a6c0a962b9ce756b52c90401c1acf3b159f265f3bc0d7d6d7d987f03", + "package/dist/i18n/fr.mjs": "99491878430fb7b6f2b7bf93da0588a831704b412e01116f23ba1d9d45863816", + "package/dist/i18n/id.js": "4c6dc86db741bcc2857df55a9d037e82e91d375fa6fd22e0562809ff621b92db", + "package/dist/i18n/id.mjs": "383e5803a6b6686c6de4e867770241de75b6f517dcca2bdedc6f2004bf6e1fb3", + "package/dist/i18n/pl.js": "3a38828b1c3d719c0aca9906cf6a46c23555a8411f8d57645f0e0ce8147dc664", + "package/dist/i18n/pl.mjs": "079ed96732979035297af387c64a7b99519b5a43107fb86735a654ea4fe8e9b5", + "package/dist/i18n/ru.js": "62fac9dddb302eb16382e6e9301fc4b2c66f5f69d4a358a26b67026a5fa4e787", + "package/dist/i18n/ru.mjs": "a893aeaee1d79409db72e324fbff200e3864f33ed168f04d91d25f7cf2efa765", + "package/dist/i18n/tr.js": "1492c36b069a7d73db4f3913bd18085faff7afd8b3c158138fe264e31600e38c", + "package/dist/i18n/tr.mjs": "a90478cac078836b76c2a98261907610fee07f3e4b28671b8e7dbf533778f918", + "package/dist/i18n/vi.js": "c7216ef7e608a79b7e2c479a07bf310d4ab79577947ac4a62d676faaced0e9cf", + "package/dist/i18n/vi.mjs": "66270fdfa0b5da396322d1769169b7df37a030ba15ac18eb30bd87b3af8074ec", + "package/dist/i18n/zh-tw.js": "11c49857d6d40a5ffd224ffa59c9026e71621b345f63c5a086326db80a22371a", + "package/dist/i18n/zh-tw.mjs": "a38e630cb96de96c15d824686aec750e25cf6c8ada01285a292442a81566314f", + "package/package.json": "82e92699e7c29847f32ac6767ece0401290e7d7a12854722bdbcfd09b90b7954", + "package/types/COMPATIBILITY.md": "a7da9e102a116104b513df4f7451247b7759bc502d3375f30bd5b14a541826e0", + "package/types/README.md": "1bd9868e773610e6312fd48a1c5aa20772d29df0af40c3db66e75b7ab4a1aee0", + "package/types/artplayer.d.cts": "3685065e4263abcfd9d953176e461d5b3eb990b0363b54689a7f55deba770fef", + "package/types/artplayer.d.mts": "b664fec51ee5c5381f2ceab25d99d52aee56feab04a2ee7b4ea5b2145ea5fa0e", + "package/types/artplayer.d.ts": "5d0162adf7ac6d0f2bb9b5cfe361f28657c4d9c540cadbf36eba3cc954ce50f8", + "package/types/component.d.ts": "ae5bc1ddeaa1ee1ebbc62573e32423993727079205c64751a5a314170e588a32", + "package/types/config.d.ts": "36f9a84cd436f17701a0d11d3417fc1fe0414e0c52c80083b94a85dfc329e37e", + "package/types/cssVar.d.ts": "93217860fff2ebf750f606d95ddd22220a58837119e63bdf2f5f78fd7488458b", + "package/types/emitter.d.ts": "7e1f9a2a8ce65feb95734f1ed52d1334e57aa388bbef2a43d8bc53a9b6f6623c", + "package/types/events.d.ts": "f83624c9268a284477e8bd793fe3562a2363b72f2a44d5c64fe8c6a6cede8830", + "package/types/i18n.d.ts": "f4f2f05bdfda73c74fef34767cb18ee8e11b743120b1c4598e9714d7846018e5", + "package/types/icons.d.ts": "326a67ba7bdcf52862c5b2a269e540140aff98d05c7b96196485a77f84fe5960", + "package/types/language.d.cts": "f7928e108cf7b7814c2d75cb0a525333e95873fca6cd6d89a740450656b9c1cd", + "package/types/language.d.mts": "6619a26d604b7cccbe2f596243d87ae37cfaae33b8b52874ec071d9787446f9e", + "package/types/legacy/language.d.ts": "c7abda4d506fd933c350e9030290bf9e64347a921d683a66115da1995ebe0a81", + "package/types/option.d.ts": "ccf2c8ddc537dc65c9682d39f044726e23bff18f476b622512d7e6a4b949b5f6", + "package/types/playback.d.ts": "58705c273df6e76a77a6575e769a9ec6c013785c25080f2551aa22415788d870", + "package/types/player.d.ts": "92b00211b70ac71336e495899df972a0519dd138111846044fe4e0f967966177", + "package/types/plugin.d.ts": "fc3594a41c51d8248713e33c486fb1f6a518ac5c7ce31be6988a6cd8d052c5f3", + "package/types/quality.d.ts": "1ac1a48b4f74ff9ba1ce60443e1463d50d1c6a2e5d87d63dc7d2a8eb7f4c2c34", + "package/types/runtime.d.cts": "7ee988dfea9a0ac71a34a656f96f7b74e78aa92547cfdfab7ff7e1139a77d4a3", + "package/types/runtime.d.mts": "dcb85f59a8b7714918ced0c1d18c2bb24e8b80b56d8c594e019dcbdb5ad88b08", + "package/types/runtime.d.ts": "9c6ccd39a1be293f17ae46dae8ea077053e12635ee79d73a5e3b3a559230fcff", + "package/types/runtime/component.d.ts": "1b4c0cee44568649e80140fe778febfcae88af0ac85515f1cbeff78ab0c6d14f", + "package/types/runtime/construction.d.ts": "7a87bedf6f7ffff0c46078aa5f1551e0077d35574e286516121f5f935e2d9ce4", + "package/types/runtime/events.d.ts": "a1e2f9d387475453eaf95c2a5ee79e228636c0bb859bda3fcc46da46574a0c31", + "package/types/runtime/media.d.ts": "8fc89401be7fe25542c169d07dd5675f10916ed20be8493e19695966cf3864a8", + "package/types/runtime/option.d.ts": "6e334e1a76fdcc40b4665c02523bd31c3994d96078fadeb40e23422aa54c1a1d", + "package/types/runtime/player.d.ts": "8ac6383502e4872e83659838844856903031a81a0adc8e8535feb78453aa042c", + "package/types/runtime/plugin.d.ts": "8528f2b17843149f42f9cc16d6fd70baa238c9dc2c329b67d2c38096799c69bd", + "package/types/runtime/services.d.ts": "604c9f8e4b6c0da818f96576bf9f4b0e96085a9003927b2f7ba30027b1ba237e", + "package/types/runtime/setting.d.ts": "349c1fcb205ad312916bd3ee55e6bf241ea8a569fb2ab6c80822e69fc518f7d7", + "package/types/runtime/subtitle.d.ts": "269ff84678d327b3a9dc2f5bc6cc0d731d1e081b85422e474621d5d792efb153", + "package/types/runtime/template.d.ts": "7a62f968a38b4bf9109a4439557c02074eb1d62ba1f6e21a8eb194e91f0badfe", + "package/types/runtime/utils.d.ts": "6bf731705d86853e066dbaf3b8d4003b6548423fe615b7412b722bd0d3a6e48b", + "package/types/setting.d.ts": "08305a75dff96f06490430e099338b463df471b550c1d0dac3abc8995b39eb09", + "package/types/subtitle.d.ts": "b18314e2d64efdb6045830b6e12f615ae2ed585416130e562329ad3ea337f6ce", + "package/types/template.d.ts": "4792bc0c2cbcb7966e4375118bd34b326166e0831862e94ace4cf6b1bbe6a8c8", + "package/types/utils.d.ts": "fca0d3d814df94a25f9eb067c86fa0cf272e2791bac793b91229d57680797712" + } + }, + { + "name": "artplayer-plugin-asr", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\asr-package-types-swwh0u\\artplayer-plugin-asr.tgz", + "sha256": "a5fbf0b82dcaf1cbbcd490dcba742056982ac5aba6ef972d6a891dd1c5416819", + "files": { + "package/ARCHITECTURE.md": "39d6ab8acc9145467b160973aae93d4067e62b0f353bf2fe8652903c778366f1", + "package/README.md": "0b8f79c5632bb35e68863e2a7b5feb0422401cc875cdbc4f20ee023c64e7fa15", + "package/dist/artplayer-plugin-asr.js": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf", + "package/dist/artplayer-plugin-asr.legacy.js": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697", + "package/dist/artplayer-plugin-asr.mjs": "1b7bac676d2c4976046b08fd7c1ec25724ff24952f5d93fc546e219466d975d8", + "package/package.json": "781b1eebefdff5fdc234b793ff9d6d8263275115ccff4c4d90c55eca942f2444", + "package/types/artplayer-plugin-asr.d.ts": "afa3a72330cdfe7858d6aa3f95c4a77d6f19fd5c34da5adc084c0f83f5e3b99a", + "package/types/runtime.d.cts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606", + "package/types/runtime.d.mts": "ff0769d3e408d66c7d38574171ff2621456afccc50ee8191ca4ee394dc57666e", + "package/types/runtime.d.ts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606" + } + } + ] + }, + "logs": [ + { + "file": "refactor/.cache/asr09-explicit-unit-red.log", + "sha256": "fedd60eb3b78c7ea4e29487f2516b68b1c4e888d674a26c77271ef8188c1aca4" + }, + { + "file": "refactor/.cache/asr09-restart-race-red.log", + "sha256": "34b95af7c0896d378a340de1fb254f386a733be6522ac9f8f4d652bd833d6317" + }, + { + "file": "refactor/.cache/asr09-unit.log", + "sha256": "42d2e800d1673a8e9e692601ce4c8cf3391600676beb2eb7d800cc5b7a3f63f6" + }, + { + "file": "refactor/.cache/asr09-unit-main.log", + "sha256": "096c02dd596746fb1793e1b47dc45abc364150ccbd14fd8421d5828866c02451" + }, + { + "file": "refactor/.cache/asr09-unit-legacy.log", + "sha256": "c4b6ef2de522e33681663326c8080d9e43641f3e597f8ae78e55f5fd6d572cc6" + }, + { + "file": "refactor/.cache/asr09-build.log", + "sha256": "106356a4b62d95bc6b04fee01d138f7ba264e2ddd4a8c88a3d3f90c026bbdcc0" + }, + { + "file": "refactor/.cache/asr09-types.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "refactor/.cache/asr09-strict.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "refactor/.cache/asr09-lint.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "refactor/.cache/asr09-installed.log", + "sha256": "da134c81ec2fae10f5a4d5a95af755ddf968f9680ee7455fb79b774b61593a8a" + }, + { + "file": "refactor/.cache/asr09-installed-final.log", + "sha256": "95c207aa0f09a5c4cbfe60b84cad667d7a1d18428ee714bd988f58c159135f5b" + } + ], + "inputLfSha256": { + "packages/artplayer-plugin-asr/src/audio-graph.ts": "abe87a81af7c3864d7b3255b7d8033fdc8c204b26025939f58338439a858ece4", + "packages/artplayer-plugin-asr/src/capture.ts": "9cec5de6d09e6ce859b166ae4f41f2c4be7754b437a8e228bf4b02ef7bab212a", + "packages/artplayer-plugin-asr/src/index.ts": "283513b118d9f7395d9f8e2423b2192e8fd95e003cb22f27b838a05a22473d0e", + "packages/artplayer-plugin-asr/src/types.ts": "101e1177faf96c4445c7c93712babbc4a1697df3f69c83b6e5c1055bfcf9669b", + "packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts": "6a66680b74e0c4fa3ba8395ecf7abd608f13bfed4e0f6c5cf76f1563f0bae0ec", + "test/asr-explicit-capture.test.js": "eb4bcb7122c8a3e1a79bc5394e262d1e343932cff504c3979b74f78ecaecbde2", + "test/types/asr-public.ts": "8fad3971088bb17795176e2f2a9595b7a033cb6654049fe6f09984510ea50ae7", + "test/browser/asr-fallback.spec.js": "b855563daeb055d7818c8f40e914c8a743c68eeac1a20e2418d888ea89876905", + "test/browser/asr-capture-cors.spec.js": "abbb54d2244c9299568571846932819b82f564541b991338110e740ff1dbbacb" + }, + "limitations": [ + "No automatic external ownership detection; explicit capture must be selected.", + "Default CORS silence remains ASR-CORS-01; explicit capture cannot bypass native restrictions.", + "Captured PCM ignores media mute/volume and does not include external graph effects or independent audio-track media.", + "No physical audio/Safari/device/recognition-service acceptance; ASR-05 and ASR-06 remain unfinished." + ], + "releaseReady": false +} diff --git a/refactor/changes/2026-09-13-PKG-ASR-09-explicit-capture.md b/refactor/changes/2026-09-13-PKG-ASR-09-explicit-capture.md new file mode 100644 index 000000000..3405eee10 --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-ASR-09-explicit-capture.md @@ -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 发布。 diff --git a/refactor/plan.md b/refactor/plan.md index c1669b6e6..990f5aad4 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -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
建立特有行为与错误测试 | PKG-ASR-01, ENG-03, ENG-05 | PCM/WAV、chunk 时序、回调慢/拒绝、重复初始化与停止 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done | | PKG-ASR-03 | artplayer-plugin-asr
整理内部职责与资源 | PKG-ASR-02, CORE-10, CORE-18 | AudioContext/Worklet/Stream 生命周期和背压分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done | | PKG-ASR-04 | artplayer-plugin-asr
迁移自有源码和公开类型 | PKG-ASR-03, ENG-04, ENG-06, CORE-07 | 主线程/Worklet/音频 buffer 与异步回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done | -| PKG-ASR-05 | artplayer-plugin-asr
验证新旧核心和组合 | PKG-ASR-04, CORE-22, PKG-ASR-07, PKG-ASR-08 | 真实 WebAudio 分块、播放暂停/切源/销毁,不引入网络 ASR | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | doing | +| PKG-ASR-05 | artplayer-plugin-asr
验证新旧核心和组合 | 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
验证分发并同步文档 | PKG-ASR-05, ENG-07 | asr.js、Worklet 资源、输出格式与声明证据 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | | PKG-ASR-07 | artplayer-plugin-asr
修复原生播放音量二次衰减 | PKG-ASR-04 | 移除重复音量应用;保留新旧原生音频振幅对照及正式产物验收 | Chromium/Firefox 两核心和 source/main/legacy 输出音量线性、静音和 stop/restart 正确;旧版平方衰减单独保留;单元/类型/真实安装通过,不代替设备验收 | H | done | | PKG-ASR-08 | artplayer-plugin-asr
修复捕获回退重复播放与静音漏声 | PKG-ASR-07 | 捕获回退静音输出图、非零PCM与外部播放归属;真实红绿证据及产物验证 | 已进入回退后不产生额外输出、静音不漏声、外部播放图不被关闭、stop/restart/换源清理正确;明确Firefox受控拒绝不代表自然选择回退 | H | done | +| PKG-ASR-09 | artplayer-plugin-asr
提供显式捕获归属并取消过期切源重启 | 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) diff --git a/refactor/progress.md b/refactor/progress.md index 31b922fed..675cbcd17 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -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组合生命周期与独立音频归属 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 7a7047a1e..8478b7b32 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -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 | diff --git a/refactor/risks.json b/refactor/risks.json index c2ef601e1..a8d32c512 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -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." } ] } diff --git a/refactor/scripts/asr-package-types.mjs b/refactor/scripts/asr-package-types.mjs index 0744b621b..d2d619eaa 100644 --- a/refactor/scripts/asr-package-types.mjs +++ b/refactor/scripts/asr-package-types.mjs @@ -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}`) diff --git a/refactor/tasks.json b/refactor/tasks.json index 47890f977..da31a4836 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -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" + ] } ] } diff --git a/refactor/third-party.json b/refactor/third-party.json index 036156225..4f17b11ac 100644 --- a/refactor/third-party.json +++ b/refactor/third-party.json @@ -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" }, { diff --git a/test/asr-explicit-capture.test.js b/test/asr-explicit-capture.test.js new file mode 100644 index 000000000..513b738a1 --- /dev/null +++ b/test/asr-explicit-capture.test.js @@ -0,0 +1,240 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import process from 'node:process' +// eslint-disable-next-line test/no-import-node-test -- Explicit ASR capture uses the repository runner. +import test from 'node:test' +import { asrAudioEnvironment, deferredAudio } from './helpers/asr-audio.js' +import { compilePackage } from './helpers/load.js' + +const artifact = process.env.ARTPLAYER_ASR_ARTIFACT +const implementation = { + name: artifact || 'candidate-asr', + code: artifact ? fs.readFileSync(artifact, 'utf8') : await compilePackage('artplayer-plugin-asr', 'umd'), +} + +function create(option = {}, capabilities = {}) { + const chunks = [] + const environment = asrAudioEnvironment(implementation, { + sampleRate: 8000, + interval: 1, + audioInput: { type: 'capture' }, + onAudioChunk(chunk) { chunks.push(chunk) }, + ...option, + }, capabilities) + const prototype = environment.context.AudioContext.prototype + const direct = prototype.createMediaElementSource + let directCalls = 0 + prototype.createMediaElementSource = function (...args) { + directCalls++ + return direct.apply(this, args) + } + return { ...environment, chunks, directCalls: () => directCalls } +} + +function assertCapture(environment) { + assert.equal(environment.directCalls(), 0, 'Explicit capture must never attempt to bind the media element') + const source = environment.nodes.filter(node => node.kind === 'stream').at(-1) + const recorder = environment.recorders.at(-1) + assert.ok(source) + assert.deepEqual(source.connections, [recorder]) + assert.equal(recorder.connections.length, 1) + const sink = recorder.connections[0] + assert.equal(sink.kind, 'gain') + assert.equal(sink.gain.value, 0) + assert.deepEqual(sink.connections, [environment.contexts.at(-1).destination]) + return { source, recorder, sink } +} + +async function assertChunk(environment) { + const before = environment.chunks.length + environment.send(new Float32Array(8).fill(0.5)) + await environment.tick() + assert.equal(environment.chunks.length, before + 1) + const chunk = environment.chunks.at(-1) + assert.equal(chunk.pcm.byteLength, 16) + assert.equal(new DataView(chunk.pcm).getInt16(0, true), 16383) + assert.equal(new DataView(chunk.wav).getUint32(24, true), 8000) +} + +function assertReleased(environment) { + assert.equal(environment.directCalls(), 0) + assert.equal(environment.intervals.size, 0) + assert.equal(environment.blobs.size, 0) + assert.ok(environment.contexts.every(context => context.closes === 1)) + assert.ok(environment.nodes.every(node => node.connections.length === 0)) + assert.ok(environment.recorders.every(recorder => recorder.port.onmessage === null)) +} + +for (const moz of [false, true]) { + test(`ASR explicit ${moz ? 'mozCaptureStream' : 'captureStream'} skips a working direct binding`, async () => { + const environment = create({}, { moz }) + await environment.emit('play') + assertCapture(environment) + assert.equal(environment.captures(), 1) + await assertChunk(environment) + await environment.emit('destroy') + assertReleased(environment) + assert.equal(environment.tracks[0].stops, 1) + }) +} + +test('ASR omitted audioInput preserves direct-first acquisition', async () => { + const environment = create({ audioInput: undefined }) + await environment.emit('play') + assert.equal(environment.directCalls(), 1) + assert.equal(environment.captures(), 0) + assert.ok(environment.nodes.some(node => node.kind === 'element')) + await assertChunk(environment) + await environment.emit('destroy') + assert.equal(environment.contexts[0].closes, 1) +}) + +for (const failure of ['unsupported', 'throws']) { + test(`ASR explicit capture ${failure} cleans up without attempting direct fallback`, async () => { + const environment = create({}, { capture: failure !== 'unsupported' }) + let attempts = 0 + if (failure === 'throws') { + environment.art.video.captureStream = () => { + attempts++ + throw new Error('Capture is unavailable for this media') + } + } + await environment.emit('play') + assertReleased(environment) + assert.equal(environment.errors.length, 1) + assert.equal(environment.recorders.length, 0) + assert.equal(environment.tracks[0].stops, 0) + assert.equal(attempts, failure === 'throws' ? 1 : 0) + await environment.emit('destroy') + assertReleased(environment) + }) +} + +test('ASR explicit capture retains a silent sink through media mute and volume changes', async () => { + const environment = create() + await environment.emit('play') + const route = assertCapture(environment) + for (const [muted, volume] of [[true, 0], [false, 0.25], [false, 1]]) { + environment.art.muted = environment.art.video.muted = muted + environment.art.volume = environment.art.video.volume = volume + await environment.emit('video:volumechange') + assert.equal(assertCapture(environment).sink, route.sink) + assert.equal(environment.art.video.muted, muted) + assert.equal(environment.art.video.volume, volume) + await assertChunk(environment) + } + await environment.emit('destroy') + assertReleased(environment) +}) + +test('ASR explicit capture pause retains its stream and resumes with a fresh recorder', async () => { + const environment = create() + await environment.emit('play') + const before = assertCapture(environment) + await environment.emit('pause') + assert.equal(environment.intervals.size, 0) + assert.equal(environment.contexts[0].closes, 0) + assert.equal(environment.tracks[0].stops, 0) + assert.equal(before.recorder.port.onmessage, null) + for (const node of Object.values(before)) + assert.deepEqual(node.connections, []) + await environment.emit('play') + const after = assertCapture(environment) + assert.equal(after.source, before.source) + assert.notEqual(after.recorder, before.recorder) + assert.equal(environment.captures(), 1) + await assertChunk(environment) + await environment.emit('destroy') + assertReleased(environment) +}) + +for (const operation of ['stop/play', 'restart']) { + test(`ASR explicit capture ${operation} releases the old graph and acquires another stream`, async () => { + const environment = create() + await environment.emit('play') + const before = assertCapture(environment) + if (operation === 'stop/play') { + await environment.plugin.stop() + assertReleased(environment) + await environment.emit('play') + } + else { + await environment.emit('restart') + } + const after = assertCapture(environment) + assert.notEqual(after.source, before.source) + assert.equal(environment.captures(), 2) + assert.equal(environment.contexts[0].closes, 1) + assert.equal(environment.contexts[1].closes, 0) + assert.equal(environment.tracks[0].stops, 1) + for (const node of Object.values(before)) + assert.deepEqual(node.connections, []) + await assertChunk(environment) + await environment.emit('destroy') + assertReleased(environment) + assert.equal(environment.tracks[0].stops, 2) + await environment.emit('play') + assert.equal(environment.captures(), 2) + assert.equal(environment.intervals.size, 0) + }) +} + +test('ASR explicit capture snapshots the nested input choice before later caller mutation', async () => { + const input = { type: 'capture' } + const environment = create({ audioInput: input }) + input.type = 'changed by caller' + await environment.emit('play') + assertCapture(environment) + await environment.plugin.stop() + await environment.emit('play') + assertCapture(environment) + assert.equal(environment.captures(), 2) + await environment.emit('destroy') + assertReleased(environment) +}) + +test('ASR explicit capture destroy during worklet loading never binds or captures late', async () => { + const moduleGate = deferredAudio() + const moduleEntered = deferredAudio() + const environment = create({}, { moduleGate, moduleEntered }) + const starting = environment.emit('play') + await moduleEntered.promise + await environment.emit('destroy') + moduleGate.resolve() + await starting + assertReleased(environment) + assert.equal(environment.captures(), 0) + assert.equal(environment.recorders.length, 0) + assert.equal(environment.tracks[0].stops, 0) +}) + +for (const action of ['pause', 'stop']) { + test(`ASR explicit capture ${action} cancels a restart waiting for the old context to close`, async () => { + const environment = create() + await environment.emit('play') + assertCapture(environment) + const closeGate = deferredAudio() + const context = environment.contexts[0] + const close = context.close.bind(context) + context.close = async () => { + await close() + await closeGate.promise + } + const restarting = environment.emit('restart') + const cancelling = action === 'pause' ? environment.emit('pause') : environment.plugin.stop() + assert.equal(environment.intervals.size, 0) + closeGate.resolve() + await Promise.all([restarting, cancelling]) + assert.equal(environment.captures(), 1, 'A superseded restart must not acquire another stream') + assert.equal(environment.contexts.length, 1) + assert.equal(environment.tracks[0].stops, 1) + assertReleased(environment) + await environment.emit('play') + assertCapture(environment) + assert.equal(environment.captures(), 2, 'A subsequent explicit play may start a new capture') + await assertChunk(environment) + await environment.emit('destroy') + assertReleased(environment) + assert.equal(environment.tracks[0].stops, 2) + }) +} diff --git a/test/browser/asr-capture-cors.spec.js b/test/browser/asr-capture-cors.spec.js new file mode 100644 index 000000000..7f39634a4 --- /dev/null +++ b/test/browser/asr-capture-cors.spec.js @@ -0,0 +1,203 @@ +import fs from 'node:fs' +import process from 'node:process' +import { hash } from '../../refactor/scripts/releases.mjs' +import { compilePackage } from '../helpers/load.js' +import { expect, test } from './fixtures.js' + +let code +test.beforeAll(async () => { + expect(process.env.ARTPLAYER_BROWSER_ARTIFACTS, 'Explicit source/artifact tests do not represent installed packages').toBeFalsy() + code = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT, 'utf8') : await compilePackage('artplayer-plugin-asr', 'umd') +}) + +test.afterEach(async ({ page }, testInfo) => { + const observation = await page.evaluate(() => window.captureCorsObservation?.()).catch(error => ({ error: error.message })) + await testInfo.attach('capture-cors-final-observation', { contentType: 'application/json', body: JSON.stringify(observation ?? null) }) + await page.evaluate(() => { + if (window.art && !window.art.isDestroy) + window.art.destroy() + }) +}) + +for (const core of ['published', 'candidate']) { + test(`ASR explicit capture + ${core}: opaque media keeps native routing and same-origin capture recovers`, async ({ page }, testInfo) => { + const responses = [] + const consoleErrors = [] + page.on('response', (response) => { + if (new URL(response.url()).pathname === '/test/audio-tone.m4a') + responses.push({ url: response.url(), status: response.status(), allowOrigin: response.headers()['access-control-allow-origin'] || null }) + }) + page.on('console', (message) => { + if (message.type() === 'error') + consoleErrors.push(message.text()) + }) + await page.goto(`/test/player.html?core=${core}`) + const capabilities = await page.evaluate(() => ({ context: typeof (window.AudioContext || window.webkitAudioContext), worklet: typeof window.AudioWorkletNode, capture: typeof HTMLMediaElement.prototype.captureStream, mozCapture: typeof HTMLMediaElement.prototype.mozCaptureStream })) + await testInfo.attach('capture-cors-inputs', { contentType: 'application/json', body: JSON.stringify({ core, capabilities, plugin: process.env.ARTPLAYER_ASR_ARTIFACT || 'workspace source', pluginSha256: hash(code), mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), nativeCapture: true, forcedException: false, analyserBinding: false, physicalAudioOutput: false, scope: 'Zero direct bindings and native media clock progression preserve routing; no claim about physical speaker output' }) }) + if (capabilities.context === 'undefined') { + expect(process.platform).toBe('win32') + expect(testInfo.project.name).toBe('webkit') + expect(capabilities.worklet).toBe('undefined') + test.skip(true, 'Windows WebKit lacks AudioContext/AudioWorklet; this is not Safari/device CORS acceptance') + } + expect(capabilities.worklet).toBe('function') + expect([capabilities.capture, capabilities.mozCapture]).toContain('function') + await page.addScriptTag({ content: code }) + await page.evaluate(() => { + const NativeContext = window.AudioContext || window.webkitAudioContext + window.captureContexts = [] + window.captureStreams = [] + window.captureTracks = [] + window.captureAttempts = [] + window.directBindings = 0 + window.captureChunks = [] + window.AudioContext = class extends NativeContext { + constructor(options) { + super(options) + window.captureContexts.push(this) + } + + createMediaElementSource(element) { + window.directBindings++ + return super.createMediaElementSource(element) + } + } + const prototype = HTMLMediaElement.prototype + const method = prototype.captureStream ? 'captureStream' : 'mozCaptureStream' + const nativeCapture = prototype[method] + prototype[method] = function (...args) { + const attempt = { source: this.currentSrc, error: null } + window.captureAttempts.push(attempt) + try { + const stream = nativeCapture.apply(this, args) + window.captureStreams.push(stream) + const recordTrack = (track) => { + if (!window.captureTracks.includes(track)) + window.captureTracks.push(track) + } + stream.getTracks().forEach(recordTrack) + stream.addEventListener('addtrack', event => recordTrack(event.track)) + return stream + } + catch (error) { + attempt.error = { name: error.name, message: error.message } + throw error + } + } + const opaque = new URL('/test/audio-tone.m4a?capture-cors=opaque', location.href) + opaque.hostname = 'localhost' + window.art = new window.Artplayer({ + container: '.player', + url: opaque.href, + volume: 0.6, + muted: false, + loop: true, + plugins: [window.artplayerPluginAsr({ + audioInput: { type: 'capture' }, + onAudioChunk({ pcm, wav }) { + let peak = 0 + for (const sample of new Int16Array(pcm)) + peak = Math.max(peak, Math.abs(sample)) + window.captureChunks.push({ source: window.art.video.currentSrc, peak, pcmBytes: pcm.byteLength, wavBytes: wav.byteLength }) + }, + })], + }) + window.captureVideoProperties = () => { + const video = window.art.video + return { + src: video.getAttribute('src'), + crossOrigin: video.crossOrigin, + crossOriginAttribute: video.getAttribute('crossorigin'), + muted: video.muted, + defaultMuted: video.defaultMuted, + volume: video.volume, + playbackRate: video.playbackRate, + autoplay: video.autoplay, + controls: video.controls, + loop: video.loop, + preload: video.preload, + playsInline: video.playsInline, + } + } + window.captureCorsObservation = () => ({ + attempts: window.captureAttempts, + directBindings: window.directBindings, + contexts: window.captureContexts.map(context => context.state), + streams: window.captureStreams.map(stream => ({ active: stream.active, tracks: stream.getTracks().map(track => ({ kind: track.kind, muted: track.muted, enabled: track.enabled, readyState: track.readyState })) })), + retainedTracks: window.captureTracks.map(track => ({ kind: track.kind, muted: track.muted, enabled: track.enabled, readyState: track.readyState })), + chunks: window.captureChunks, + }) + document.querySelector('#play').onclick = () => window.art.play() + document.querySelector('#pause').onclick = () => window.art.pause() + }) + await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true) + const initialProperties = await page.evaluate(() => window.captureVideoProperties()) + expect(initialProperties.crossOrigin).toBeNull() + expect(initialProperties.crossOriginAttribute).toBeNull() + expect(initialProperties.muted).toBe(false) + expect(new URL(initialProperties.src, page.url()).origin).not.toBe(new URL(page.url()).origin) + await page.locator('#play').click() + await expect.poll(() => page.evaluate(() => window.captureAttempts.length)).toBeGreaterThanOrEqual(1) + await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(1) + const opaqueStart = await page.evaluate(() => window.art.currentTime) + await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(opaqueStart + 0.5) + const opaque = await page.evaluate(() => window.captureCorsObservation()) + expect(opaque.directBindings).toBe(0) + expect(opaque.chunks.every(chunk => chunk.peak === 0)).toBe(true) + expect(await page.evaluate(() => window.art.video.error)).toBeNull() + expect(await page.evaluate(() => window.art.video.paused)).toBe(false) + expect(await page.evaluate(() => window.captureVideoProperties())).toEqual(initialProperties) + expect(responses.some(response => new URL(response.url).hostname === 'localhost' && [200, 206].includes(response.status))).toBe(true) + expect(responses.every(response => response.allowOrigin === null)).toBe(true) + expect(opaque.chunks).toEqual([]) + expect(opaque.attempts).toHaveLength(1) + if (testInfo.project.name === 'chromium') { + expect(opaque.attempts[0].error.name).toBe('SecurityError') + expect(opaque.contexts).toEqual(['closed']) + expect(opaque.streams).toEqual([]) + expect(consoleErrors).toHaveLength(1) + expect(consoleErrors[0]).toContain('[artplayerPluginAsr] Initialization failed: SecurityError:') + } + else { + expect(testInfo.project.name).toBe('firefox') + expect(opaque.attempts[0].error).toBeNull() + expect(opaque.contexts).toEqual(['running']) + expect(opaque.streams).toHaveLength(1) + expect(opaque.retainedTracks).toHaveLength(1) + expect(opaque.retainedTracks[0].kind).toBe('audio') + expect(opaque.retainedTracks[0].readyState).toBe('live') + expect(consoleErrors).toEqual([]) + } + await testInfo.attach('opaque-native-capture-observation', { contentType: 'application/json', body: JSON.stringify({ opaque, responses: [...responses], consoleErrors: [...consoleErrors], properties: initialProperties, clockAdvanced: true }) }) + + await page.evaluate(() => { + window.art.url = '/test/audio-tone.m4a?capture-cors=recovery' + }) + await expect.poll(() => page.evaluate(() => window.art.video.currentSrc.includes('capture-cors=recovery') && window.art.video.readyState >= 2)).toBe(true) + const recoveryProperties = await page.evaluate(() => window.captureVideoProperties()) + await page.locator('#play').click() + await expect.poll(() => page.evaluate(() => window.captureChunks.filter(chunk => chunk.source.includes('capture-cors=recovery') && chunk.peak > 0).length)).toBeGreaterThanOrEqual(3) + expect(await page.evaluate(() => window.directBindings)).toBe(0) + expect(await page.evaluate(() => window.captureVideoProperties())).toEqual(recoveryProperties) + expect(await page.evaluate(() => window.captureChunks.filter(chunk => chunk.source.includes('capture-cors=opaque')).every(chunk => chunk.peak === 0))).toBe(true) + expect(await page.evaluate(() => window.captureAttempts.length)).toBeGreaterThanOrEqual(2) + await page.evaluate(() => window.art.plugins.artplayerPluginAsr.stop()) + await expect.poll(() => page.evaluate(() => window.captureContexts.every(context => context.state === 'closed'))).toBe(true) + expect(await page.evaluate(() => window.captureTracks.every(track => track.readyState === 'ended'))).toBe(true) + const stopped = await page.evaluate(() => ({ chunks: window.captureChunks.length, time: window.art.currentTime })) + await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(stopped.time + 0.4) + expect(await page.evaluate(() => window.captureChunks.length)).toBe(stopped.chunks) + expect(await page.evaluate(() => window.art.video.paused)).toBe(false) + expect(await page.evaluate(() => window.captureVideoProperties())).toEqual(recoveryProperties) + await page.locator('#pause').click() + await page.locator('#play').click() + await expect.poll(() => page.evaluate(start => window.captureChunks.slice(start).filter(chunk => chunk.peak > 0).length, stopped.chunks)).toBeGreaterThanOrEqual(3) + expect(await page.evaluate(() => window.directBindings)).toBe(0) + await testInfo.attach('same-origin-capture-recovery', { contentType: 'application/json', body: JSON.stringify(await page.evaluate(() => window.captureCorsObservation())) }) + await page.evaluate(() => window.art.destroy()) + await expect.poll(() => page.evaluate(() => window.captureContexts.every(context => context.state === 'closed'))).toBe(true) + expect(await page.evaluate(() => window.captureTracks.every(track => track.readyState === 'ended'))).toBe(true) + expect(await page.evaluate(() => window.directBindings)).toBe(0) + await expect(page.locator('.art-video-player')).toHaveCount(0) + }) +} diff --git a/test/browser/asr-fallback.spec.js b/test/browser/asr-fallback.spec.js index f18a8a13a..b73215a8b 100644 --- a/test/browser/asr-fallback.spec.js +++ b/test/browser/asr-fallback.spec.js @@ -12,14 +12,14 @@ test.beforeAll(async () => { candidateCode = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT) : await compilePackage('artplayer-plugin-asr', 'umd') }) -for (const version of ['2.1.0', 'candidate']) { +for (const { version, captureOnly } of [{ version: '2.1.0', captureOnly: false }, { version: 'candidate', captureOnly: false }, { version: 'candidate', captureOnly: true }]) { for (const core of ['published-5.3.0', 'published', 'candidate']) { - test(`ASR ${version} + ${core}: native capture fallback does not create a second audible route`, async ({ page }, testInfo) => { + test(`ASR ${version}${captureOnly ? ' capture' : ''} + ${core}: native capture fallback does not create a second audible route`, async ({ page }, testInfo) => { const code = version === 'candidate' ? candidateCode : readMember(contract.archives.get(version), 'package/dist/artplayer-plugin-asr.js') await page.goto(`/test/player.html?core=${core}`) const capabilities = await page.evaluate(() => ({ context: typeof AudioContext, worklet: typeof AudioWorkletNode, capture: typeof HTMLMediaElement.prototype.captureStream, mozCapture: typeof HTMLMediaElement.prototype.mozCaptureStream })) - const forceRejection = testInfo.project.name === 'firefox' - await testInfo.attach('asr-fallback-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, pluginSha256: hash(code), capabilities, forcedException: forceRejection, reason: forceRejection ? 'Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet' : 'Native duplicate binding rejection', externalContext: 'Native media-element source remains owned and running', physicalOutput: false }) }) + const forceRejection = testInfo.project.name === 'firefox' && !captureOnly + await testInfo.attach('asr-fallback-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, captureOnly, pluginSha256: hash(code), capabilities, forcedException: forceRejection, reason: captureOnly ? 'Explicit capture mode never binds the element; no forced exceptions' : forceRejection ? 'Firefox permits a second native media source; force only binding rejection to reach real captureStream/Worklet' : 'Native duplicate binding rejection', externalContext: 'Native media-element source remains owned and running', physicalOutput: false }) }) if (capabilities.context !== 'function') { expect(process.platform).toBe('win32') expect(testInfo.project.name).toBe('webkit') @@ -28,13 +28,14 @@ for (const version of ['2.1.0', 'candidate']) { expect(capabilities.worklet).toBe('function') expect([capabilities.capture, capabilities.mozCapture]).toContain('function') await page.addScriptTag({ content: code.toString() }) - await page.evaluate((forceRejection) => { + await page.evaluate(({ forceRejection, captureOnly }) => { const NativeContext = window.AudioContext window.asrContexts = [] window.asrMeters = [] window.asrStreams = [] window.asrChunks = [] window.asrDirectFailures = [] + window.asrDirectCalls = 0 window.AudioContext = class extends NativeContext { constructor(option) { super(option) @@ -42,6 +43,7 @@ for (const version of ['2.1.0', 'candidate']) { } createMediaElementSource(video) { + window.asrDirectCalls++ try { if (forceRejection) throw new DOMException('Controlled duplicate binding rejection for native capture test', 'InvalidStateError') @@ -80,7 +82,7 @@ for (const version of ['2.1.0', 'candidate']) { url: '/test/audio-tone.m4a', volume: 1, loop: true, - plugins: [window.artplayerPluginAsr({ onAudioChunk({ pcm }) { + plugins: [window.artplayerPluginAsr({ audioInput: captureOnly ? { type: 'capture' } : undefined, onAudioChunk({ pcm }) { const samples = new Int16Array(pcm) const max = samples.reduce((value, sample) => Math.max(value, Math.abs(sample)), 0) window.asrChunks.push({ max, source: window.art.video.currentSrc, samples: samples.length }) @@ -108,9 +110,9 @@ for (const version of ['2.1.0', 'candidate']) { analyser.getFloatTimeDomainData(samples) return Math.sqrt(samples.reduce((sum, value) => sum + value * value, 0) / samples.length) } - return { external: rms(window.externalMeter), asr: window.asrMeters.map(rms), contexts: window.asrContexts.map(c => c.state), owner: window.externalContext.state, streams: window.asrStreams.map(s => s.tracks.map(t => ({ kind: t.kind, state: t.readyState }))), chunks: window.asrChunks.slice(-3), failures: window.asrDirectFailures } + return { external: rms(window.externalMeter), asr: window.asrMeters.map(rms), contexts: window.asrContexts.map(c => c.state), owner: window.externalContext.state, streams: window.asrStreams.map(s => s.tracks.map(t => ({ kind: t.kind, state: t.readyState }))), chunks: window.asrChunks.slice(-3), failures: window.asrDirectFailures, directCalls: window.asrDirectCalls } } - }, forceRejection) + }, { forceRejection, captureOnly }) await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true) await page.locator('#play').click() await expect.poll(() => page.evaluate(() => window.asrChunks.filter(c => c.max > 0).length)).toBeGreaterThanOrEqual(3) @@ -121,7 +123,8 @@ for (const version of ['2.1.0', 'candidate']) { await page.waitForTimeout(450) const muted = await page.evaluate(() => window.readFallback()) await testInfo.attach('asr-fallback-output', { contentType: 'application/json', body: JSON.stringify({ playing, muted }) }) - expect(playing.failures.length).toBe(1) + expect(playing.failures.length).toBe(captureOnly ? 0 : 1) + expect(playing.directCalls).toBe(captureOnly ? 0 : 1) expect(playing.external).toBeGreaterThan(0.01) expect(muted.external).toBe(0) if (version === '2.1.0') { @@ -167,6 +170,8 @@ for (const version of ['2.1.0', 'candidate']) { await page.evaluate(() => window.art.destroy()) await expect.poll(() => page.evaluate(() => window.asrContexts.every(context => context.state === 'closed'))).toBe(true) expect(await page.evaluate(() => window.externalContext.state)).toBe('running') + if (captureOnly) + expect(await page.evaluate(() => window.asrDirectCalls)).toBe(0) await page.evaluate(() => window.externalContext.close()) }) } diff --git a/test/types/asr-public.ts b/test/types/asr-public.ts index 317b826bf..999c30135 100644 --- a/test/types/asr-public.ts +++ b/test/types/asr-public.ts @@ -38,6 +38,8 @@ type PreciseResult = Assert>, Runtim type PreciseStop = Assert, Promise>> type PreciseFactory = Assert> type PreciseCallback = Assert, (chunk: AudioChunk) => string | void | null | Promise>> +type PreciseAudioInput = Assert> +type RootAudioInputAbsent = Assert, never>> const replacement: HistoricalFactory = (_option?: HistoricalOption) => (_art: Artplayer) => ({ name: 'artplayerPluginAsr', @@ -72,6 +74,24 @@ runtime({ onAudioChunk: () => undefined }) runtime({ onAudioChunk: async () => {} }) runtime({ onAudioChunk: async () => null }) runtime(option) +const captureOption: RuntimeOption = { audioInput: { type: 'capture' } } +const captureEntryOption: EntryOption = captureOption +runtime(captureOption) +runtime(captureEntryOption) +runtime({ audioInput: { type: 'capture' }, onAudioChunk: async () => 'Captured main media.' }) + +// @ts-expect-error Only the explicit capture strategy is public. +runtime({ audioInput: { type: 'direct' } }) +// @ts-expect-error The strategy discriminator is required when audioInput is provided. +runtime({ audioInput: {} }) +// @ts-expect-error Audio input is a descriptor, not a string shorthand. +runtime({ audioInput: 'capture' }) +// @ts-expect-error Root options retain their historical fields without the runtime-only strategy. +const rootCaptureOption: AsrPluginOption = { audioInput: { type: 'capture' } } +// @ts-expect-error The historical factory does not expose runtime-only options. +legacy({ audioInput: { type: 'capture' } }) +// @ts-expect-error The explicit legacy entry also retains the historical option shape. +legacyEntry({ audioInput: { type: 'capture' } }) // @ts-expect-error Root options preserve the historical void/Promise callback shape. legacy({ onAudioChunk: () => 'Only the precise entry describes returned subtitles.' }) @@ -97,5 +117,5 @@ runtime()() correct.append(123) // @ts-expect-error PCM is an ArrayBuffer rather than encoded text. const invalidChunk: AudioChunk = { pcm: 'raw audio', wav: new ArrayBuffer(4) } -void [assignToOld, assignFromOld, namespaceFactory, explicitLegacy, oldReturns, stopped, aliasResult, rootPromise, synchronous, wrongStop, invalidChunk] -export type { ChunkShape, PreciseArguments, PreciseCallback, PreciseFactory, PreciseResult, PreciseStop, RootArguments, RootFactory, RootOption, RootResult, RootShape, RootStop, RuntimeEntryTypes } +void [assignToOld, assignFromOld, namespaceFactory, explicitLegacy, oldReturns, stopped, aliasResult, rootCaptureOption, rootPromise, synchronous, wrongStop, invalidChunk] +export type { ChunkShape, PreciseArguments, PreciseAudioInput, PreciseCallback, PreciseFactory, PreciseResult, PreciseStop, RootArguments, RootAudioInputAbsent, RootFactory, RootOption, RootResult, RootShape, RootStop, RuntimeEntryTypes }