@mediafox/core 1.2.13 → 1.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compositor/compositor.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/playback/controller.d.ts +4 -0
- package/dist/playback/controller.d.ts.map +1 -1
- package/dist/playback/renderer.d.ts +5 -1
- package/dist/playback/renderer.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/compositor/compositor.ts +1 -2
- package/src/playback/controller.ts +53 -10
- package/src/playback/renderer.ts +217 -29
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compositor.d.ts","sourceRoot":"","sources":["../../src/compositor/compositor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,KAAK,EAEV,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAElB,iBAAiB,EACjB,gBAAgB,EAChB,uBAAuB,EACvB,OAAO,EACP,kBAAkB,EAClB,cAAc,EACf,MAAM,SAAS,CAAC;AAkBjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,GAAG,CAA6E;IACxF,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,kBAAkB,CAAuC;IACjE,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,aAAa,CAA4E;IACjG,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,oBAAoB,CAAO;IACnC,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,oBAAoB,CAAqB;IACjD,OAAO,CAAC,YAAY,CAIlB;IACF,OAAO,CAAC,sBAAsB,CAAqB;IAGnD,OAAO,CAAC,oBAAoB,CAAS;IAErC;;;OAGG;gBACS,OAAO,EAAE,iBAAiB;IAsEtC;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBnG;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAcxE;;;;;OAKG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBlG;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAwBjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2C1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAOnD;;;OAGG;IACH,aAAa,IAAI,gBAAgB,EAAE;IASnC;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAqEvD,OAAO,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"compositor.d.ts","sourceRoot":"","sources":["../../src/compositor/compositor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,KAAK,EAEV,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAElB,iBAAiB,EACjB,gBAAgB,EAChB,uBAAuB,EACvB,OAAO,EACP,kBAAkB,EAClB,cAAc,EACf,MAAM,SAAS,CAAC;AAkBjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,GAAG,CAA6E;IACxF,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,kBAAkB,CAAuC;IACjE,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,aAAa,CAA4E;IACjG,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,oBAAoB,CAAO;IACnC,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,oBAAoB,CAAqB;IACjD,OAAO,CAAC,YAAY,CAIlB;IACF,OAAO,CAAC,sBAAsB,CAAqB;IAGnD,OAAO,CAAC,oBAAoB,CAAS;IAErC;;;OAGG;gBACS,OAAO,EAAE,iBAAiB;IAsEtC;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBnG;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAcxE;;;;;OAKG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBlG;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAwBjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2C1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAOnD;;;OAGG;IACH,aAAa,IAAI,gBAAgB,EAAE;IASnC;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAqEvD,OAAO,CAAC,WAAW;IAqInB,OAAO,CAAC,kBAAkB;YAyBZ,eAAe;IAgB7B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,oBAAoB;IA6B5B;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAQtC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkC3B;;OAEG;IACH,KAAK,IAAI,IAAI;IAab;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCvC,OAAO,CAAC,eAAe;IAgEvB,OAAO,CAAC,cAAc;IAStB;;;;;OAKG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAiCvF,wCAAwC;IACxC,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,4DAA4D;IAC5D,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,mDAAmD;IACnD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,kDAAkD;IAClD,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,mDAAmD;IACnD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;OAGG;IACH,QAAQ,IAAI,MAAM;IAIlB;;;OAGG;IACH,SAAS,IAAI,MAAM;IAInB;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI;IAuB9D;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAQlC;;;OAGG;IACH,UAAU,IAAI,OAAO;IAMrB;;;;;OAKG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIlG;;;;;OAKG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIpG;;;;OAIG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,IAAI;IAM9F;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B;;;OAGG;IACH,eAAe,IAAI,YAAY;IAS/B,OAAO,CAAC,aAAa;IAMrB;;;OAGG;IACH,OAAO,IAAI,IAAI;CAoBhB"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class I{events=new Map;maxListeners;captureRejections;emitCache=[];constructor(O={}){this.maxListeners=O.maxListeners??10,this.captureRejections=O.captureRejections??!1}on(O,$){if(!this.events.has(O))this.events.set(O,new Set);let K=this.events.get(O);if(!K)return()=>{};if(K.size>=this.maxListeners)console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${K.size} ${String(O)} listeners added. Use emitter.setMaxListeners() to increase limit`);let j=$;return K.add(j),()=>{K.delete(j)}}once(O,$){let K=(j)=>{this.off(O,K),$(j)};return this.on(O,K)}off(O,$){let K=this.events.get(O);if(!K)return;if($){let j=$;K.delete(j)}else K.clear()}emit(O,$){let K=this.events.get(O);if(!K||K.size===0)return;let j=this.emitCache;j.length=0;for(let Q of K)j.push(Q);for(let Q=0;Q<j.length;Q++){let V=j[Q];try{let J=V($);if(this.captureRejections&&YO(J))this.handlePromiseRejection(J)}catch(J){if(this.captureRejections&&this.events.has("error"))this.emit("error",J);else throw J}}}handlePromiseRejection(O){O.catch(($)=>{if(this.events.has("error"))this.emit("error",$);else throw $})}removeAllListeners(O){if(O)this.events.delete(O);else this.events.clear()}setMaxListeners(O){this.maxListeners=O}getMaxListeners(){return this.maxListeners}listeners(O){let $=this.events.get(O);return $?Array.from($):[]}listenerCount(O){let $=this.events.get(O);return $?$.size:0}eventNames(){return Array.from(this.events.keys())}}function YO(O){if(!O||typeof O!=="object"&&typeof O!=="function")return!1;let $=O;return typeof $.then==="function"&&typeof $.catch==="function"}class s{audioContext;masterGain;activeSources=new Map;activeSourceIdsScratch=new Set;playing=!1;disposed=!1;playbackId=0;startContextTime=0;startMediaTime=0;pauseTime=0;masterVolume=1;masterMuted=!1;constructor(O={}){if(O.audioContext)this.audioContext=O.audioContext;else{let $=globalThis,K=$.AudioContext||$.webkitAudioContext;if(!K)throw Error("AudioContext is not supported in this environment");this.audioContext=new K}this.masterGain=this.audioContext.createGain(),this.masterGain.connect(this.audioContext.destination)}registerSource(O,$){if(this.disposed)return;let K=this.audioContext.createGain(),j=this.audioContext.createStereoPanner();K.connect(j),j.connect(this.masterGain),this.activeSources.set(O.id,{sourceId:O.id,bufferSink:$,iterator:null,gainNode:K,panNode:j,queuedNodes:new Set,volume:1,pan:0,muted:!1,startSourceTime:0,currentSourceTime:0,iteratorStartTime:0,lastScheduledTime:0})}unregisterSource(O){let $=this.activeSources.get(O);if(!$)return;this.stopSourceAudio($),$.gainNode.disconnect(),$.panNode.disconnect(),this.activeSources.delete(O)}hasSource(O){return this.activeSources.has(O)}processAudioLayers(O,$){if(this.disposed)return;let K=this.activeSourceIdsScratch;K.clear();for(let j of O){let Q=j.source.id;K.add(Q);let V=this.activeSources.get(Q);if(!V)continue;let J=j.volume??1,U=j.pan??0,Z=j.muted??!1,L=j.sourceTime??$;if(V.volume!==J||V.muted!==Z)V.volume=J,V.muted=Z,V.gainNode.gain.value=Z?0:J*J;if(V.pan!==U)V.pan=U,V.panNode.pan.value=Math.max(-1,Math.min(1,U));if(Math.abs(L-V.currentSourceTime)>0.5&&V.iterator!==null)this.restartSourceIterator(V,L);V.currentSourceTime=L}for(let[j,Q]of this.activeSources)if(!K.has(j)&&Q.iterator!==null)this.stopSourceAudio(Q)}async play(O=this.pauseTime){if(this.playing||this.disposed)return;if(this.audioContext.state==="suspended")await this.audioContext.resume();this.playbackId++,this.playing=!0,this.startContextTime=this.audioContext.currentTime,this.startMediaTime=O,this.pauseTime=O;for(let $ of this.activeSources.values())if($.iterator)$.iterator.return(),$.iterator=null}startSourcePlayback(O,$){let K=this.activeSources.get(O);if(!K||K.iterator!==null)return;this.restartSourceIterator(K,$)}restartSourceIterator(O,$){this.stopSourceAudio(O),O.startSourceTime=$,O.currentSourceTime=$,O.iteratorStartTime=this.audioContext.currentTime,O.iterator=O.bufferSink.buffers($),O.lastScheduledTime=$,this.scheduleSourceBuffers(O,this.playbackId)}async scheduleSourceBuffers(O,$){let K=O.iterator;if(!K)return;try{for await(let{buffer:j,timestamp:Q}of K){if($!==this.playbackId||this.disposed||!this.playing)break;let V=this.audioContext.createBufferSource();V.buffer=j,V.connect(O.gainNode);let J=Q-O.startSourceTime,U=O.iteratorStartTime+J;if(U>=this.audioContext.currentTime)V.start(U);else{let A=this.audioContext.currentTime-U;if(A<j.duration)V.start(this.audioContext.currentTime,A);else continue}O.queuedNodes.add(V),V.onended=()=>{O.queuedNodes.delete(V)},O.lastScheduledTime=Q;let Z=this.audioContext.currentTime-O.iteratorStartTime;if(Q-O.startSourceTime-Z>1)await this.waitForCatchup(O,Q)}}catch{}}async waitForCatchup(O,$){return new Promise((K)=>{let j=setInterval(()=>{if(!this.playing||this.disposed){clearInterval(j),K();return}let Q=this.audioContext.currentTime-O.iteratorStartTime;if($-O.startSourceTime-Q<1)clearInterval(j),K()},100)})}pause(){if(!this.playing)return;this.pauseTime=this.getCurrentTime(),this.playing=!1;for(let O of this.activeSources.values())this.stopSourceAudio(O)}stop(){this.pause(),this.pauseTime=0,this.startContextTime=0,this.startMediaTime=0}async seek(O){let $=this.playing;if(this.pause(),this.pauseTime=O,this.startMediaTime=O,$)await this.play(O)}stopSourceAudio(O){for(let $ of O.queuedNodes)try{$.stop()}catch{}if(O.queuedNodes.clear(),O.iterator)O.iterator.return(),O.iterator=null}getCurrentTime(){if(this.playing)return this.startMediaTime+(this.audioContext.currentTime-this.startContextTime);return this.pauseTime}setMasterVolume(O){this.masterVolume=Math.max(0,Math.min(1,O)),this.updateMasterGain()}setMasterMuted(O){this.masterMuted=O,this.updateMasterGain()}updateMasterGain(){let O=this.masterMuted?0:this.masterVolume;this.masterGain.gain.value=O*O}getAudioContext(){return this.audioContext}isPlaying(){return this.playing}dispose(){if(this.disposed)return;this.disposed=!0,this.playbackId++,this.stop();for(let O of this.activeSources.values())this.stopSourceAudio(O),O.gainNode.disconnect(),O.panNode.disconnect();if(this.activeSources.clear(),this.masterGain.disconnect(),this.audioContext.state!=="closed")this.audioContext.close()}}import{ALL_FORMATS as NO,AudioBufferSink as JO,BlobSource as DO,BufferSource as _O,CanvasSink as EO,FilePathSource as MO,Input as qO,ReadableStreamSource as CO,UrlSource as RO}from"mediabunny";class UO{id;type="video";duration;width;height;data;disposed=!1;constructor(O,$,K,j,Q){this.id=O,this.data=$,this.duration=K,this.width=j,this.height=Q}async getFrameAt(O){if(this.disposed)return null;return this.withLock(async()=>{if(this.disposed)return null;let $=this.data.currentFrame;if($){let V=$.timestamp+($.duration||0);if(O>=$.timestamp&&($.duration?O<V:O===$.timestamp))return this.data.lastRequestedTime=O,$.canvas}let K=this.data.lastRequestedTime;if(this.data.iterator===null||O<K||Math.abs(O-K)>this.data.seekThresholdSeconds)await this.restartIterator(O);let Q=await this.advanceToTime(O);return this.data.lastRequestedTime=O,Q?.canvas??null})}async restartIterator(O){if(this.data.iterator)try{await this.data.iterator.return()}catch{}this.data.iterator=this.data.canvasSink.canvases(O),this.data.currentFrame=null,this.data.nextFrame=null}async advanceToTime(O){let $=this.data.iterator;if(!$)return this.data.canvasSink.getCanvas(O);while(!0){let K=this.data.nextFrame;if(K){if(K.timestamp>O)return this.data.currentFrame;this.data.currentFrame=K,this.data.nextFrame=null;continue}let j;try{j=await $.next()}catch{try{return await this.data.canvasSink.getCanvas(O)}catch{return null}}if(j.done)return this.data.currentFrame;this.data.nextFrame=j.value}}async withLock(O){let $=this.data.lock.catch(()=>{}),K=()=>{},j=new Promise((Q)=>{K=()=>{Q()}});this.data.lock=$.then(()=>j),await $;try{return await O()}finally{K()}}getAudioBufferSink(){return this.data.audioBufferSink}hasAudio(){return this.data.audioBufferSink!==null}clearCache(){}dispose(){if(this.disposed)return;if(this.disposed=!0,this.data.iterator){try{this.data.iterator.return()}catch{}this.data.iterator=null}this.data.currentFrame=null,this.data.nextFrame=null,this.data.input.dispose()}}class LO{id;type="image";duration=1/0;width;height;data;disposed=!1;constructor(O,$){this.id=O,this.data=$,this.width=$.image.width,this.height=$.image.height}async getFrameAt(O){if(this.disposed)return null;return this.data.image}dispose(){if(this.disposed)return;if(this.disposed=!0,"close"in this.data.image)this.data.image.close()}}class ZO{id;type="audio";duration;width=0;height=0;data;disposed=!1;constructor(O,$,K){this.id=O,this.data=$,this.duration=K}async getFrameAt(O){return null}getAudioBufferSink(){return this.data.audioBufferSink}dispose(){if(this.disposed)return;this.disposed=!0,this.data.input.dispose()}}class x{sources=new Map;audioContext=null;nextId=0;constructor(O){this.audioContext=O??null}generateId(){return`source_${this.nextId++}`}async loadVideo(O,$={}){let K=$.id??this.generateId(),j=this.createInput(O),Q=await j.getVideoTracks();if(Q.length===0)throw j.dispose(),Error("Source has no video track");let V=Q[0];if(!await V.canDecode())throw j.dispose(),Error(`Cannot decode video track with codec: ${V.codec}`);let U=new EO(V,{poolSize:4}),Z=await V.computeDuration(),L=0.75,A=null,G=null;try{let N=await j.getAudioTracks();if(N.length>0){if(A=N[0],await A.canDecode())G=new JO(A)}}catch{}let X=new UO(K,{input:j,videoTrack:V,canvasSink:U,iterator:null,currentFrame:null,nextFrame:null,lastRequestedTime:-1/0,seekThresholdSeconds:L,lock:Promise.resolve(),audioTrack:A,audioBufferSink:G},Z,V.displayWidth,V.displayHeight);return this.sources.set(K,X),X}async loadImage(O){let $=this.generateId(),K;if(typeof O!=="string")K=await createImageBitmap(O);else if(typeof Image>"u"){let Q=await fetch(O);if(!Q.ok)throw Error(`Failed to load image: ${O}`);let V=await Q.blob();K=await createImageBitmap(V)}else K=await new Promise((Q,V)=>{let J=new Image;J.onload=()=>Q(J),J.onerror=()=>V(Error(`Failed to load image: ${O}`)),J.crossOrigin="anonymous",J.src=O});let j=new LO($,{image:K});return this.sources.set($,j),j}async loadAudio(O,$={}){let K=$.id??this.generateId(),j=this.createInput(O),Q=await j.getAudioTracks();if(Q.length===0)throw j.dispose(),Error("Source has no audio track");let V=Q[0];if(!await V.canDecode())throw j.dispose(),Error(`Cannot decode audio track with codec: ${V.codec}`);let U=await V.computeDuration(),Z=new JO(V),L=new ZO(K,{input:j,audioTrack:V,audioBufferSink:Z},U);return this.sources.set(K,L),L}createInput(O){let $;if(O instanceof File||O instanceof Blob)$=new DO(O);else if(O instanceof ArrayBuffer||O instanceof Uint8Array)$=new _O(O);else if(typeof O==="string"||O instanceof URL){let K=O instanceof URL?O.href:O;if(typeof window>"u"&&!K.startsWith("http"))$=new MO(K);else $=new RO(K)}else if(typeof ReadableStream<"u"&&O instanceof ReadableStream)$=new CO(O);else throw Error("Unsupported source type");return new qO({source:$,formats:NO})}getSource(O){return this.sources.get(O)}hasSource(O){return this.sources.has(O)}unloadSource(O){let $=this.sources.get(O);if($)return $.dispose(),this.sources.delete(O),!0;return!1}getAllSources(){return Array.from(this.sources.values())}clear(){for(let O of this.sources.values())O.dispose();this.sources.clear()}dispose(){if(this.clear(),this.audioContext&&this.audioContext.state!=="closed")this.audioContext.close();this.audioContext=null}}class i{worker;nextId=1;pending=new Map;ready;constructor(O){let $=typeof O.worker==="boolean"?{}:O.worker??{},K=$.type??"module",j=$.url??new URL("./compositor-worker.js",import.meta.url);this.worker=new Worker(j,{type:K}),this.worker.onmessage=(J)=>{let{id:U,ok:Z,result:L,error:A}=J.data,G=this.pending.get(U);if(!G)return;if(this.pending.delete(U),Z)G.resolve(L);else G.reject(Error(A??"Worker error"))},this.worker.onerror=(J)=>{let U=J.error instanceof Error?J.error:Error("Worker error");for(let Z of this.pending.values())Z.reject(U);this.pending.clear()};let Q=O.canvas.transferControlToOffscreen(),V={canvas:Q,width:O.width,height:O.height,backgroundColor:O.backgroundColor};this.ready=this.call("init",V,[Q])}postMessage(O,$,K){let j=this.nextId++;return this.worker.postMessage({id:j,kind:O,payload:$},K??[]),j}call(O,$,K){let j=this.postMessage(O,$,K);return new Promise((Q,V)=>{this.pending.set(j,{resolve:Q,reject:V})})}async loadSource(O,$){await this.ready;let K={source:O,options:$};return this.call("loadSource",K)}async loadImage(O){await this.ready;let $={source:O};return this.call("loadImage",$)}async loadAudio(O,$){await this.ready;let K={source:O,options:$};return this.call("loadAudio",K)}async unloadSource(O){await this.ready;let $={id:O};return this.call("unloadSource",$)}async render(O){await this.ready;let $={frame:O};return this.call("render",$)}async clear(){return await this.ready,this.call("clear")}async resize(O,$,K){await this.ready;let j={width:O,height:$,fitMode:K};return this.call("resize",j)}async exportFrame(O,$){await this.ready;let K={frame:O,options:$};return this.call("exportFrame",K)}dispose(){try{this.worker.postMessage({id:this.nextId++,kind:"dispose"})}catch{}this.worker.terminate(),this.pending.clear()}}class a{canvas;ctx=null;width;height;backgroundColor;fitMode;sourcePool;audioManager=null;workerClient=null;workerSources=new Map;workerAudioSources=new Map;emitter;state;animationFrameId=null;lastFrameTime=0;lastRenderTime=0;previewOptions=null;disposed=!1;renderBuffers={visibleLayers:[],framePromises:[],frameImages:[]};lastTimeUpdateEmit=0;timeUpdateThrottleMs=100;renderPending=!1;activeAudioSourceIds=new Set;audioScratch={nextActiveSourceIds:new Set,newSourceIds:[],newSourceTimes:[]};registeredAudioSources=new Set;pendingPlayAfterSeek=!1;constructor(O){this.canvas=O.canvas,this.width=O.width??(this.canvas.width||1920),this.height=O.height??(this.canvas.height||1080),this.backgroundColor=O.backgroundColor??"#000000",this.fitMode=O.fitMode??"fill",this.emitter=new I({maxListeners:50}),this.state={playing:!1,currentTime:0,duration:0,seeking:!1},this.canvas.width=this.width,this.canvas.height=this.height;let $=typeof O.worker==="boolean"?O.worker:O.worker?O.worker.enabled??!0:!1,K=$&&typeof Worker<"u"&&typeof OffscreenCanvas<"u"&&typeof this.canvas.transferControlToOffscreen==="function"&&!(this.canvas instanceof OffscreenCanvas);if($&&!K)throw Error("Worker compositor requires HTMLCanvasElement, OffscreenCanvas, and Worker support");if(this.sourcePool=new x,K)try{this.workerClient=new i({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:O.worker??!0})}catch(j){console.warn("[Compositor] Worker initialization failed, falling back to main thread rendering:",j),this.workerClient=null}if(O.enableAudio!==!1)this.audioManager=new s;if(!this.workerClient){if(this.ctx=this.canvas.getContext("2d",{alpha:!1,desynchronized:!0}),!this.ctx)throw Error("Failed to get 2D context for compositor canvas");this.clear()}}async loadSource(O,$){if(this.checkDisposed(),this.workerClient){let j=await this.workerClient.loadSource(O,$),Q=this.createWorkerSource(j);if(this.audioManager&&j.hasAudio)await this.loadWorkerAudio(O,Q.id);return this.emitter.emit("sourceloaded",{id:Q.id,source:Q}),Q}let K=await this.sourcePool.loadVideo(O,$);return this.registerSourceAudio(K),this.emitter.emit("sourceloaded",{id:K.id,source:K}),K}async loadImage(O){if(this.checkDisposed(),this.workerClient){let K=await this.workerClient.loadImage(O),j=this.createWorkerSource(K);return this.emitter.emit("sourceloaded",{id:j.id,source:j}),j}let $=await this.sourcePool.loadImage(O);return this.emitter.emit("sourceloaded",{id:$.id,source:$}),$}async loadAudio(O,$){if(this.checkDisposed(),this.workerClient){let j=await this.workerClient.loadAudio(O,$),Q=this.createWorkerSource(j);if(this.audioManager)await this.loadWorkerAudio(O,Q.id);return this.emitter.emit("sourceloaded",{id:Q.id,source:Q}),Q}let K=await this.sourcePool.loadAudio(O,$);return this.registerSourceAudio(K),this.emitter.emit("sourceloaded",{id:K.id,source:K}),K}unloadSource(O){if(this.workerClient){if(!this.workerSources.get(O))return!1;return this.workerClient.unloadSource(O),this.workerSources.delete(O),this.unloadWorkerAudio(O),this.emitter.emit("sourceunloaded",{id:O}),!0}if(this.registeredAudioSources.has(O))this.audioManager?.unregisterSource(O),this.registeredAudioSources.delete(O);let $=this.sourcePool.unloadSource(O);if($)this.emitter.emit("sourceunloaded",{id:O});return $}registerSourceAudio(O){if(!this.audioManager)return;if(this.registeredAudioSources.has(O.id))return;let $=O.getAudioBufferSink?.();if($)this.audioManager.registerSource(O,$),this.registeredAudioSources.add(O.id)}processAudioLayers(O,$){if(!this.audioManager)return;let K=this.audioScratch.newSourceIds,j=this.audioScratch.newSourceTimes,Q=this.audioScratch.nextActiveSourceIds,V=this.activeAudioSourceIds;K.length=0,j.length=0,Q.clear();for(let J=0;J<O.length;J++){let U=O[J];if(U.muted)continue;let Z=U.source.id;if(!this.audioManager.hasSource(Z))continue;if(Q.add(Z),!V.has(Z))K.push(Z),j.push(U.sourceTime??$)}if(V.size>0)V.clear();for(let J of Q)V.add(J);this.audioManager.processAudioLayers(O,$);for(let J=0;J<K.length;J++)this.audioManager.startSourcePlayback(K[J],j[J])}getSource(O){if(this.workerClient)return this.workerSources.get(O);return this.sourcePool.getSource(O)}getAllSources(){if(this.workerClient)return Array.from(this.workerSources.values());return this.sourcePool.getAllSources()}async render(O){if(this.checkDisposed(),this.workerClient){let L=this.serializeWorkerFrame(O);return this.workerClient.render(L)}let $=this.ctx;if(!$)return!1;let{visibleLayers:K,framePromises:j,frameImages:Q}=this.renderBuffers;K.length=0,j.length=0,Q.length=0;let V=!1,J=-1/0,U=O.layers;for(let L=0;L<U.length;L++){let A=U[L];if(A.visible===!1)continue;let G=A.zIndex??0;if(G<J)V=!0;J=G,K.push(A)}if(K.length===0)return $.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height),!0;if(V)K.sort((L,A)=>(L.zIndex??0)-(A.zIndex??0));for(let L=0;L<K.length;L++){let A=K[L],G=A.sourceTime??O.time;j[L]=A.source.getFrameAt(G)}let Z=await Promise.all(j);for(let L=0;L<Z.length;L++)Q[L]=Z[L]??null;$.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height);for(let L=0;L<K.length;L++){let A=Q[L];if(A)this.renderLayer(A,K[L])}return!0}renderLayer(O,$){let K=this.ctx;if(!K)return;let j=$.transform,Q=$.source.width??this.width,V=$.source.height??this.height,J=$.fitMode===void 0||$.fitMode==="auto"?this.fitMode:$.fitMode,U=Q,Z=V,L=0,A=0;if(Q===0||V===0)U=this.width,Z=this.height;else if(J==="none")U=Q,Z=V,L=(this.width-U)/2,A=(this.height-Z)/2;else{let P=Q/V,z=this.width/this.height;switch(J){case"fill":U=this.width,Z=this.height;break;case"cover":if(P>z)Z=this.height,U=this.height*P,L=(this.width-U)/2;else U=this.width,Z=this.width/P,A=(this.height-Z)/2;break;default:if(P>z)U=this.width,Z=this.width/P,A=(this.height-Z)/2;else Z=this.height,U=this.height*P,L=(this.width-U)/2;break}}if(!j){K.drawImage(O,L,A,U,Z);return}let G=(j.x??0)+L,X=(j.y??0)+A,N=j.width??U,Y=j.height??Z,M=j.rotation??0,q=j.scaleX??1,C=j.scaleY??1,S=j.opacity??1,R=j.filter,W=S!==1,b=M!==0||q!==1||C!==1,w=!!R&&R!=="none";if(!(W||b||w)){K.drawImage(O,G,X,N,Y);return}if(!b){if(K.save(),W)K.globalAlpha=S;if(w)K.filter=R;K.drawImage(O,G,X,N,Y),K.restore();return}let _=j.anchorX??0.5,B=j.anchorY??0.5;if(K.save(),W)K.globalAlpha=S;if(w)K.filter=R;if(K.translate(G+N*_,X+Y*B),M!==0)K.rotate(M*Math.PI/180);if(q!==1||C!==1)K.scale(q,C);K.drawImage(O,-N*_,-Y*B,N,Y),K.restore()}createWorkerSource(O){let $={id:O.id,type:O.type,duration:O.duration,width:O.width,height:O.height,async getFrameAt(){throw Error("getFrameAt is not available when worker rendering is enabled")},getAudioBufferSink(){return null},hasAudio(){return O.hasAudio??!1},dispose(){}};return this.workerSources.set($.id,$),$}async loadWorkerAudio(O,$){if(!this.audioManager)return;if(this.workerAudioSources.has($))this.unloadWorkerAudio($);try{let K=await this.sourcePool.loadAudio(O,{id:$});this.workerAudioSources.set($,K),this.registerSourceAudio(K)}catch{}}unloadWorkerAudio(O){if(!this.audioManager)return;if(this.workerAudioSources.has(O))this.audioManager.unregisterSource(O),this.registeredAudioSources.delete(O),this.sourcePool.unloadSource(O),this.workerAudioSources.delete(O)}serializeWorkerFrame(O){if(!this.workerClient)throw Error("Worker compositor not initialized");let $=O.layers,K=Array($.length);for(let j=0;j<$.length;j++){let Q=$[j],V=Q.source.id;if(!this.workerSources.has(V))throw Error(`Layer source ${V} is not managed by this compositor`);K[j]={sourceId:V,sourceTime:Q.sourceTime,transform:Q.transform,fitMode:Q.fitMode,visible:Q.visible,zIndex:Q.zIndex}}return{time:O.time,layers:K}}clear(){if(this.workerClient){this.workerClient.clear();return}if(!this.ctx)return;this.ctx.fillStyle=this.backgroundColor,this.ctx.fillRect(0,0,this.width,this.height)}preview(O){this.checkDisposed(),this.previewOptions=O,this.state.duration=O.duration,this.lastRenderTime=0,this.emitter.emit("compositionchange",void 0)}async play(){if(this.checkDisposed(),this.state.playing)return;if(!this.previewOptions)throw Error("No preview configured. Call preview() first.");if(this.state.seeking){this.pendingPlayAfterSeek=!0;return}if(this.state.playing=!0,this.lastFrameTime=performance.now(),this.lastRenderTime=this.lastFrameTime,this.emitter.emit("play",void 0),this.audioManager){this.activeAudioSourceIds.clear(),await this.audioManager.play(this.state.currentTime);let O=this.previewOptions.getComposition(this.state.currentTime);this.processAudioLayers(O.audio??[],this.state.currentTime)}this.startRenderLoop()}pause(){if(this.checkDisposed(),!this.state.playing)return;if(this.state.playing=!1,this.pendingPlayAfterSeek=!1,this.stopRenderLoop(),this.audioManager)this.audioManager.pause();this.emitter.emit("pause",void 0)}async seek(O){if(this.checkDisposed(),!this.previewOptions)return;let $=Math.max(0,Math.min(O,this.state.duration));if(this.state.seeking=!0,this.emitter.emit("seeking",{time:$}),this.state.currentTime=$,this.audioManager)await this.audioManager.seek($),this.activeAudioSourceIds.clear();let K=this.previewOptions.getComposition($);if(this.audioManager)this.processAudioLayers(K.audio??[],$);if(await this.render(K),this.state.seeking=!1,this.emitter.emit("seeked",{time:$}),this.emitter.emit("timeupdate",{currentTime:$}),this.pendingPlayAfterSeek)this.pendingPlayAfterSeek=!1,await this.play()}startRenderLoop(){if(this.animationFrameId!==null)return;let O=()=>{if(!this.state.playing||!this.previewOptions)return;this.animationFrameId=requestAnimationFrame(O);let $=performance.now(),K=($-this.lastFrameTime)/1000;if(this.lastFrameTime=$,this.state.currentTime+=K,this.state.currentTime>=this.state.duration)if(this.previewOptions.loop)this.state.currentTime=0;else{this.state.currentTime=this.state.duration,this.pause(),this.emitter.emit("ended",void 0);return}let j=this.previewOptions.fps??0,Q=j>0?1000/j:0;if(!this.renderPending&&(Q===0||$-this.lastRenderTime>=Q)){this.renderPending=!0,this.lastRenderTime=$;let J=this.previewOptions.getComposition(this.state.currentTime);if(this.audioManager)this.processAudioLayers(J.audio??[],this.state.currentTime);this.render(J).catch(()=>{}).finally(()=>{this.renderPending=!1})}if($-this.lastTimeUpdateEmit>=this.timeUpdateThrottleMs)this.lastTimeUpdateEmit=$,this.emitter.emit("timeupdate",{currentTime:this.state.currentTime})};this.animationFrameId=requestAnimationFrame(O)}stopRenderLoop(){if(this.animationFrameId!==null)cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null}async exportFrame(O,$={}){if(this.checkDisposed(),!this.previewOptions)return null;let K=this.previewOptions.getComposition(O);if(this.workerClient){let j=this.serializeWorkerFrame(K);return this.workerClient.exportFrame(j,$)}if(await this.render(K),"toBlob"in this.canvas)return new Promise((j)=>{this.canvas.toBlob((Q)=>j(Q),`image/${$.format??"png"}`,$.quality)});else return this.canvas.convertToBlob({type:`image/${$.format??"png"}`,quality:$.quality})}get currentTime(){return this.state.currentTime}get duration(){return this.state.duration}get playing(){return this.state.playing}get paused(){return!this.state.playing}get seeking(){return this.state.seeking}getWidth(){return this.width}getHeight(){return this.height}resize(O,$,K){if(this.checkDisposed(),this.width=O,this.height=$,K!==void 0)this.fitMode=K;if(this.workerClient){this.workerClient.resize(O,$,this.fitMode);return}this.canvas.width=O,this.canvas.height=$,this.clear()}setFitMode(O){if(this.checkDisposed(),this.fitMode=O,this.workerClient)this.workerClient.resize(this.width,this.height,O)}getFitMode(){return this.fitMode}on(O,$){return this.emitter.on(O,$)}once(O,$){return this.emitter.once(O,$)}off(O,$){this.emitter.off(O,$)}setVolume(O){this.audioManager?.setMasterVolume(O)}setMuted(O){this.audioManager?.setMasterMuted(O)}getAudioContext(){if(!this.audioManager)throw Error("Audio is disabled for this compositor");return this.audioManager.getAudioContext()}checkDisposed(){if(this.disposed)throw Error("Compositor has been disposed")}dispose(){if(this.disposed)return;this.disposed=!0,this.stopRenderLoop(),this.audioManager?.dispose(),this.workerClient?.dispose(),this.workerClient=null,this.sourcePool.dispose(),this.registeredAudioSources.clear(),this.activeAudioSourceIds.clear(),this.audioScratch.nextActiveSourceIds.clear(),this.audioScratch.newSourceIds.length=0,this.audioScratch.newSourceTimes.length=0,this.workerSources.clear(),this.workerAudioSources.clear(),this.emitter.removeAllListeners(),this.ctx=null,this.previewOptions=null}}import{CanvasSink as FO,VideoSampleSink as SO}from"mediabunny";class y{canvas;ctx=null;isInitialized=!1;rotation=0;constructor(O){this.canvas=O.canvas,this.rotation=O.rotation??0,this.initialize()}initialize(){try{if(this.ctx=this.canvas.getContext("2d",{alpha:!1,desynchronized:!0}),!this.ctx)return!1;return this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.isInitialized=!0,!0}catch{return!1}}isReady(){return this.isInitialized&&this.ctx!==null}render(O){if(!this.isReady()||!this.ctx)return!1;try{let{width:$,height:K}=O;if($===0||K===0)return!1;let j=this.canvas.width,Q=this.canvas.height;if(j===0||Q===0)return!1;let V=this.rotation===90||this.rotation===270,J=V?K:$,U=V?$:K,Z=Math.min(j/J,Q/U),L=Math.round(J*Z),A=Math.round(U*Z),G=Math.round((j-L)/2),X=Math.round((Q-A)/2);if(this.ctx.fillStyle="black",this.ctx.fillRect(0,0,j,Q),this.ctx.save(),this.ctx.translate(G+L/2,X+A/2),this.rotation!==0)this.ctx.rotate(this.rotation*Math.PI/180);if(V)this.ctx.drawImage(O,0,0,$,K,-A/2,-L/2,A,L);else this.ctx.drawImage(O,0,0,$,K,-L/2,-A/2,L,A);return this.ctx.restore(),!0}catch{return!1}}clear(){if(!this.isReady()||!this.ctx)return;this.ctx.fillStyle="black",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}setRotation(O){this.rotation=O}getRotation(){return this.rotation}dispose(){this.ctx=null,this.isInitialized=!1}}class r{resources;isInitialized=!1;canvas;textureWidth=0;textureHeight=0;options;boundHandleContextLost=null;boundHandleContextRestored=null;rotation=0;positionsArray=new Float32Array(8);vertexShaderSource=`
|
|
1
|
+
class x{events=new Map;maxListeners;captureRejections;emitCache=[];constructor(O={}){this.maxListeners=O.maxListeners??10,this.captureRejections=O.captureRejections??!1}on(O,$){if(!this.events.has(O))this.events.set(O,new Set);let K=this.events.get(O);if(!K)return()=>{};if(K.size>=this.maxListeners)console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${K.size} ${String(O)} listeners added. Use emitter.setMaxListeners() to increase limit`);let j=$;return K.add(j),()=>{K.delete(j)}}once(O,$){let K=(j)=>{this.off(O,K),$(j)};return this.on(O,K)}off(O,$){let K=this.events.get(O);if(!K)return;if($){let j=$;K.delete(j)}else K.clear()}emit(O,$){let K=this.events.get(O);if(!K||K.size===0)return;let j=this.emitCache;j.length=0;for(let Q of K)j.push(Q);for(let Q=0;Q<j.length;Q++){let V=j[Q];try{let J=V($);if(this.captureRejections&&NO(J))this.handlePromiseRejection(J)}catch(J){if(this.captureRejections&&this.events.has("error"))this.emit("error",J);else throw J}}}handlePromiseRejection(O){O.catch(($)=>{if(this.events.has("error"))this.emit("error",$);else throw $})}removeAllListeners(O){if(O)this.events.delete(O);else this.events.clear()}setMaxListeners(O){this.maxListeners=O}getMaxListeners(){return this.maxListeners}listeners(O){let $=this.events.get(O);return $?Array.from($):[]}listenerCount(O){let $=this.events.get(O);return $?$.size:0}eventNames(){return Array.from(this.events.keys())}}function NO(O){if(!O||typeof O!=="object"&&typeof O!=="function")return!1;let $=O;return typeof $.then==="function"&&typeof $.catch==="function"}class s{audioContext;masterGain;activeSources=new Map;activeSourceIdsScratch=new Set;playing=!1;disposed=!1;playbackId=0;startContextTime=0;startMediaTime=0;pauseTime=0;masterVolume=1;masterMuted=!1;constructor(O={}){if(O.audioContext)this.audioContext=O.audioContext;else{let $=globalThis,K=$.AudioContext||$.webkitAudioContext;if(!K)throw Error("AudioContext is not supported in this environment");this.audioContext=new K}this.masterGain=this.audioContext.createGain(),this.masterGain.connect(this.audioContext.destination)}registerSource(O,$){if(this.disposed)return;let K=this.audioContext.createGain(),j=this.audioContext.createStereoPanner();K.connect(j),j.connect(this.masterGain),this.activeSources.set(O.id,{sourceId:O.id,bufferSink:$,iterator:null,gainNode:K,panNode:j,queuedNodes:new Set,volume:1,pan:0,muted:!1,startSourceTime:0,currentSourceTime:0,iteratorStartTime:0,lastScheduledTime:0})}unregisterSource(O){let $=this.activeSources.get(O);if(!$)return;this.stopSourceAudio($),$.gainNode.disconnect(),$.panNode.disconnect(),this.activeSources.delete(O)}hasSource(O){return this.activeSources.has(O)}processAudioLayers(O,$){if(this.disposed)return;let K=this.activeSourceIdsScratch;K.clear();for(let j of O){let Q=j.source.id;K.add(Q);let V=this.activeSources.get(Q);if(!V)continue;let J=j.volume??1,U=j.pan??0,G=j.muted??!1,Z=j.sourceTime??$;if(V.volume!==J||V.muted!==G)V.volume=J,V.muted=G,V.gainNode.gain.value=G?0:J*J;if(V.pan!==U)V.pan=U,V.panNode.pan.value=Math.max(-1,Math.min(1,U));if(Math.abs(Z-V.currentSourceTime)>0.5&&V.iterator!==null)this.restartSourceIterator(V,Z);V.currentSourceTime=Z}for(let[j,Q]of this.activeSources)if(!K.has(j)&&Q.iterator!==null)this.stopSourceAudio(Q)}async play(O=this.pauseTime){if(this.playing||this.disposed)return;if(this.audioContext.state==="suspended")await this.audioContext.resume();this.playbackId++,this.playing=!0,this.startContextTime=this.audioContext.currentTime,this.startMediaTime=O,this.pauseTime=O;for(let $ of this.activeSources.values())if($.iterator)$.iterator.return(),$.iterator=null}startSourcePlayback(O,$){let K=this.activeSources.get(O);if(!K||K.iterator!==null)return;this.restartSourceIterator(K,$)}restartSourceIterator(O,$){this.stopSourceAudio(O),O.startSourceTime=$,O.currentSourceTime=$,O.iteratorStartTime=this.audioContext.currentTime,O.iterator=O.bufferSink.buffers($),O.lastScheduledTime=$,this.scheduleSourceBuffers(O,this.playbackId)}async scheduleSourceBuffers(O,$){let K=O.iterator;if(!K)return;try{for await(let{buffer:j,timestamp:Q}of K){if($!==this.playbackId||this.disposed||!this.playing)break;let V=this.audioContext.createBufferSource();V.buffer=j,V.connect(O.gainNode);let J=Q-O.startSourceTime,U=O.iteratorStartTime+J;if(U>=this.audioContext.currentTime)V.start(U);else{let L=this.audioContext.currentTime-U;if(L<j.duration)V.start(this.audioContext.currentTime,L);else continue}O.queuedNodes.add(V),V.onended=()=>{O.queuedNodes.delete(V)},O.lastScheduledTime=Q;let G=this.audioContext.currentTime-O.iteratorStartTime;if(Q-O.startSourceTime-G>1)await this.waitForCatchup(O,Q)}}catch{}}async waitForCatchup(O,$){return new Promise((K)=>{let j=setInterval(()=>{if(!this.playing||this.disposed){clearInterval(j),K();return}let Q=this.audioContext.currentTime-O.iteratorStartTime;if($-O.startSourceTime-Q<1)clearInterval(j),K()},100)})}pause(){if(!this.playing)return;this.pauseTime=this.getCurrentTime(),this.playing=!1;for(let O of this.activeSources.values())this.stopSourceAudio(O)}stop(){this.pause(),this.pauseTime=0,this.startContextTime=0,this.startMediaTime=0}async seek(O){let $=this.playing;if(this.pause(),this.pauseTime=O,this.startMediaTime=O,$)await this.play(O)}stopSourceAudio(O){for(let $ of O.queuedNodes)try{$.stop()}catch{}if(O.queuedNodes.clear(),O.iterator)O.iterator.return(),O.iterator=null}getCurrentTime(){if(this.playing)return this.startMediaTime+(this.audioContext.currentTime-this.startContextTime);return this.pauseTime}setMasterVolume(O){this.masterVolume=Math.max(0,Math.min(1,O)),this.updateMasterGain()}setMasterMuted(O){this.masterMuted=O,this.updateMasterGain()}updateMasterGain(){let O=this.masterMuted?0:this.masterVolume;this.masterGain.gain.value=O*O}getAudioContext(){return this.audioContext}isPlaying(){return this.playing}dispose(){if(this.disposed)return;this.disposed=!0,this.playbackId++,this.stop();for(let O of this.activeSources.values())this.stopSourceAudio(O),O.gainNode.disconnect(),O.panNode.disconnect();if(this.activeSources.clear(),this.masterGain.disconnect(),this.audioContext.state!=="closed")this.audioContext.close()}}import{ALL_FORMATS as AO,AudioBufferSink as JO,BlobSource as DO,BufferSource as qO,CanvasSink as MO,FilePathSource as CO,Input as _O,ReadableStreamSource as EO,UrlSource as RO}from"mediabunny";class UO{id;type="video";duration;width;height;data;disposed=!1;constructor(O,$,K,j,Q){this.id=O,this.data=$,this.duration=K,this.width=j,this.height=Q}async getFrameAt(O){if(this.disposed)return null;return this.withLock(async()=>{if(this.disposed)return null;let $=this.data.currentFrame;if($){let V=$.timestamp+($.duration||0);if(O>=$.timestamp&&($.duration?O<V:O===$.timestamp))return this.data.lastRequestedTime=O,$.canvas}let K=this.data.lastRequestedTime;if(this.data.iterator===null||O<K||Math.abs(O-K)>this.data.seekThresholdSeconds)await this.restartIterator(O);let Q=await this.advanceToTime(O);return this.data.lastRequestedTime=O,Q?.canvas??null})}async restartIterator(O){if(this.data.iterator)try{await this.data.iterator.return()}catch{}this.data.iterator=this.data.canvasSink.canvases(O),this.data.currentFrame=null,this.data.nextFrame=null}async advanceToTime(O){let $=this.data.iterator;if(!$)return this.data.canvasSink.getCanvas(O);while(!0){let K=this.data.nextFrame;if(K){if(K.timestamp>O)return this.data.currentFrame;this.data.currentFrame=K,this.data.nextFrame=null;continue}let j;try{j=await $.next()}catch{try{return await this.data.canvasSink.getCanvas(O)}catch{return null}}if(j.done)return this.data.currentFrame;this.data.nextFrame=j.value}}async withLock(O){let $=this.data.lock.catch(()=>{}),K=()=>{},j=new Promise((Q)=>{K=()=>{Q()}});this.data.lock=$.then(()=>j),await $;try{return await O()}finally{K()}}getAudioBufferSink(){return this.data.audioBufferSink}hasAudio(){return this.data.audioBufferSink!==null}clearCache(){}dispose(){if(this.disposed)return;if(this.disposed=!0,this.data.iterator){try{this.data.iterator.return()}catch{}this.data.iterator=null}this.data.currentFrame=null,this.data.nextFrame=null,this.data.input.dispose()}}class ZO{id;type="image";duration=1/0;width;height;data;disposed=!1;constructor(O,$){this.id=O,this.data=$,this.width=$.image.width,this.height=$.image.height}async getFrameAt(O){if(this.disposed)return null;return this.data.image}dispose(){if(this.disposed)return;if(this.disposed=!0,"close"in this.data.image)this.data.image.close()}}class GO{id;type="audio";duration;width=0;height=0;data;disposed=!1;constructor(O,$,K){this.id=O,this.data=$,this.duration=K}async getFrameAt(O){return null}getAudioBufferSink(){return this.data.audioBufferSink}dispose(){if(this.disposed)return;this.disposed=!0,this.data.input.dispose()}}class I{sources=new Map;audioContext=null;nextId=0;constructor(O){this.audioContext=O??null}generateId(){return`source_${this.nextId++}`}async loadVideo(O,$={}){let K=$.id??this.generateId(),j=this.createInput(O),Q=await j.getVideoTracks();if(Q.length===0)throw j.dispose(),Error("Source has no video track");let V=Q[0];if(!await V.canDecode())throw j.dispose(),Error(`Cannot decode video track with codec: ${V.codec}`);let U=new MO(V,{poolSize:4}),G=await V.computeDuration(),Z=0.75,L=null,z=null;try{let A=await j.getAudioTracks();if(A.length>0){if(L=A[0],await L.canDecode())z=new JO(L)}}catch{}let Y=new UO(K,{input:j,videoTrack:V,canvasSink:U,iterator:null,currentFrame:null,nextFrame:null,lastRequestedTime:-1/0,seekThresholdSeconds:Z,lock:Promise.resolve(),audioTrack:L,audioBufferSink:z},G,V.displayWidth,V.displayHeight);return this.sources.set(K,Y),Y}async loadImage(O){let $=this.generateId(),K;if(typeof O!=="string")K=await createImageBitmap(O);else if(typeof Image>"u"){let Q=await fetch(O);if(!Q.ok)throw Error(`Failed to load image: ${O}`);let V=await Q.blob();K=await createImageBitmap(V)}else K=await new Promise((Q,V)=>{let J=new Image;J.onload=()=>Q(J),J.onerror=()=>V(Error(`Failed to load image: ${O}`)),J.crossOrigin="anonymous",J.src=O});let j=new ZO($,{image:K});return this.sources.set($,j),j}async loadAudio(O,$={}){let K=$.id??this.generateId(),j=this.createInput(O),Q=await j.getAudioTracks();if(Q.length===0)throw j.dispose(),Error("Source has no audio track");let V=Q[0];if(!await V.canDecode())throw j.dispose(),Error(`Cannot decode audio track with codec: ${V.codec}`);let U=await V.computeDuration(),G=new JO(V),Z=new GO(K,{input:j,audioTrack:V,audioBufferSink:G},U);return this.sources.set(K,Z),Z}createInput(O){let $;if(O instanceof File||O instanceof Blob)$=new DO(O);else if(O instanceof ArrayBuffer||O instanceof Uint8Array)$=new qO(O);else if(typeof O==="string"||O instanceof URL){let K=O instanceof URL?O.href:O;if(typeof window>"u"&&!K.startsWith("http"))$=new CO(K);else $=new RO(K)}else if(typeof ReadableStream<"u"&&O instanceof ReadableStream)$=new EO(O);else throw Error("Unsupported source type");return new _O({source:$,formats:AO})}getSource(O){return this.sources.get(O)}hasSource(O){return this.sources.has(O)}unloadSource(O){let $=this.sources.get(O);if($)return $.dispose(),this.sources.delete(O),!0;return!1}getAllSources(){return Array.from(this.sources.values())}clear(){for(let O of this.sources.values())O.dispose();this.sources.clear()}dispose(){if(this.clear(),this.audioContext&&this.audioContext.state!=="closed")this.audioContext.close();this.audioContext=null}}class i{worker;nextId=1;pending=new Map;ready;constructor(O){let $=typeof O.worker==="boolean"?{}:O.worker??{},K=$.type??"module",j=$.url??new URL("./compositor-worker.js",import.meta.url);this.worker=new Worker(j,{type:K}),this.worker.onmessage=(J)=>{let{id:U,ok:G,result:Z,error:L}=J.data,z=this.pending.get(U);if(!z)return;if(this.pending.delete(U),G)z.resolve(Z);else z.reject(Error(L??"Worker error"))},this.worker.onerror=(J)=>{let U=J.error instanceof Error?J.error:Error("Worker error");for(let G of this.pending.values())G.reject(U);this.pending.clear()};let Q=O.canvas.transferControlToOffscreen(),V={canvas:Q,width:O.width,height:O.height,backgroundColor:O.backgroundColor};this.ready=this.call("init",V,[Q])}postMessage(O,$,K){let j=this.nextId++;return this.worker.postMessage({id:j,kind:O,payload:$},K??[]),j}call(O,$,K){let j=this.postMessage(O,$,K);return new Promise((Q,V)=>{this.pending.set(j,{resolve:Q,reject:V})})}async loadSource(O,$){await this.ready;let K={source:O,options:$};return this.call("loadSource",K)}async loadImage(O){await this.ready;let $={source:O};return this.call("loadImage",$)}async loadAudio(O,$){await this.ready;let K={source:O,options:$};return this.call("loadAudio",K)}async unloadSource(O){await this.ready;let $={id:O};return this.call("unloadSource",$)}async render(O){await this.ready;let $={frame:O};return this.call("render",$)}async clear(){return await this.ready,this.call("clear")}async resize(O,$,K){await this.ready;let j={width:O,height:$,fitMode:K};return this.call("resize",j)}async exportFrame(O,$){await this.ready;let K={frame:O,options:$};return this.call("exportFrame",K)}dispose(){try{this.worker.postMessage({id:this.nextId++,kind:"dispose"})}catch{}this.worker.terminate(),this.pending.clear()}}class a{canvas;ctx=null;width;height;backgroundColor;fitMode;sourcePool;audioManager=null;workerClient=null;workerSources=new Map;workerAudioSources=new Map;emitter;state;animationFrameId=null;lastFrameTime=0;lastRenderTime=0;previewOptions=null;disposed=!1;renderBuffers={visibleLayers:[],framePromises:[],frameImages:[]};lastTimeUpdateEmit=0;timeUpdateThrottleMs=100;renderPending=!1;activeAudioSourceIds=new Set;audioScratch={nextActiveSourceIds:new Set,newSourceIds:[],newSourceTimes:[]};registeredAudioSources=new Set;pendingPlayAfterSeek=!1;constructor(O){this.canvas=O.canvas,this.width=O.width??(this.canvas.width||1920),this.height=O.height??(this.canvas.height||1080),this.backgroundColor=O.backgroundColor??"#000000",this.fitMode=O.fitMode??"fill",this.emitter=new x({maxListeners:50}),this.state={playing:!1,currentTime:0,duration:0,seeking:!1},this.canvas.width=this.width,this.canvas.height=this.height;let $=typeof O.worker==="boolean"?O.worker:O.worker?O.worker.enabled??!0:!1,K=$&&typeof Worker<"u"&&typeof OffscreenCanvas<"u"&&typeof this.canvas.transferControlToOffscreen==="function"&&!(this.canvas instanceof OffscreenCanvas);if($&&!K)throw Error("Worker compositor requires HTMLCanvasElement, OffscreenCanvas, and Worker support");if(this.sourcePool=new I,K)try{this.workerClient=new i({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:O.worker??!0})}catch(j){console.warn("[Compositor] Worker initialization failed, falling back to main thread rendering:",j),this.workerClient=null}if(O.enableAudio!==!1)this.audioManager=new s;if(!this.workerClient){if(this.ctx=this.canvas.getContext("2d",{alpha:!1,desynchronized:!0}),!this.ctx)throw Error("Failed to get 2D context for compositor canvas");this.clear()}}async loadSource(O,$){if(this.checkDisposed(),this.workerClient){let j=await this.workerClient.loadSource(O,$),Q=this.createWorkerSource(j);if(this.audioManager&&j.hasAudio)await this.loadWorkerAudio(O,Q.id);return this.emitter.emit("sourceloaded",{id:Q.id,source:Q}),Q}let K=await this.sourcePool.loadVideo(O,$);return this.registerSourceAudio(K),this.emitter.emit("sourceloaded",{id:K.id,source:K}),K}async loadImage(O){if(this.checkDisposed(),this.workerClient){let K=await this.workerClient.loadImage(O),j=this.createWorkerSource(K);return this.emitter.emit("sourceloaded",{id:j.id,source:j}),j}let $=await this.sourcePool.loadImage(O);return this.emitter.emit("sourceloaded",{id:$.id,source:$}),$}async loadAudio(O,$){if(this.checkDisposed(),this.workerClient){let j=await this.workerClient.loadAudio(O,$),Q=this.createWorkerSource(j);if(this.audioManager)await this.loadWorkerAudio(O,Q.id);return this.emitter.emit("sourceloaded",{id:Q.id,source:Q}),Q}let K=await this.sourcePool.loadAudio(O,$);return this.registerSourceAudio(K),this.emitter.emit("sourceloaded",{id:K.id,source:K}),K}unloadSource(O){if(this.workerClient){if(!this.workerSources.get(O))return!1;return this.workerClient.unloadSource(O),this.workerSources.delete(O),this.unloadWorkerAudio(O),this.emitter.emit("sourceunloaded",{id:O}),!0}if(this.registeredAudioSources.has(O))this.audioManager?.unregisterSource(O),this.registeredAudioSources.delete(O);let $=this.sourcePool.unloadSource(O);if($)this.emitter.emit("sourceunloaded",{id:O});return $}registerSourceAudio(O){if(!this.audioManager)return;if(this.registeredAudioSources.has(O.id))return;let $=O.getAudioBufferSink?.();if($)this.audioManager.registerSource(O,$),this.registeredAudioSources.add(O.id)}processAudioLayers(O,$){if(!this.audioManager)return;let K=this.audioScratch.newSourceIds,j=this.audioScratch.newSourceTimes,Q=this.audioScratch.nextActiveSourceIds,V=this.activeAudioSourceIds;K.length=0,j.length=0,Q.clear();for(let J=0;J<O.length;J++){let U=O[J];if(U.muted)continue;let G=U.source.id;if(!this.audioManager.hasSource(G))continue;if(Q.add(G),!V.has(G))K.push(G),j.push(U.sourceTime??$)}if(V.size>0)V.clear();for(let J of Q)V.add(J);this.audioManager.processAudioLayers(O,$);for(let J=0;J<K.length;J++)this.audioManager.startSourcePlayback(K[J],j[J])}getSource(O){if(this.workerClient)return this.workerSources.get(O);return this.sourcePool.getSource(O)}getAllSources(){if(this.workerClient)return Array.from(this.workerSources.values());return this.sourcePool.getAllSources()}async render(O){if(this.checkDisposed(),this.workerClient){let Z=this.serializeWorkerFrame(O);return this.workerClient.render(Z)}let $=this.ctx;if(!$)return!1;let{visibleLayers:K,framePromises:j,frameImages:Q}=this.renderBuffers;K.length=0,j.length=0,Q.length=0;let V=!1,J=-1/0,U=O.layers;for(let Z=0;Z<U.length;Z++){let L=U[Z];if(L.visible===!1)continue;let z=L.zIndex??0;if(z<J)V=!0;J=z,K.push(L)}if(K.length===0)return $.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height),!0;if(V)K.sort((Z,L)=>(Z.zIndex??0)-(L.zIndex??0));for(let Z=0;Z<K.length;Z++){let L=K[Z],z=L.sourceTime??O.time;j[Z]=L.source.getFrameAt(z)}let G=await Promise.all(j);for(let Z=0;Z<G.length;Z++)Q[Z]=G[Z]??null;$.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height);for(let Z=0;Z<K.length;Z++){let L=Q[Z];if(L)this.renderLayer(L,K[Z])}return!0}renderLayer(O,$){let K=this.ctx;if(!K)return;let j=$.transform,Q=$.source.width??this.width,V=$.source.height??this.height,J=$.fitMode===void 0||$.fitMode==="auto"?this.fitMode:$.fitMode,U=Q,G=V,Z=0,L=0;if(Q===0||V===0)U=this.width,G=this.height;else if(J==="none")U=Q,G=V,Z=(this.width-U)/2,L=(this.height-G)/2;else{let P=Q/V,B=this.width/this.height;switch(J){case"fill":U=this.width,G=this.height;break;case"cover":if(P>B)G=this.height,U=this.height*P,Z=(this.width-U)/2;else U=this.width,G=this.width/P,L=(this.height-G)/2;break;default:if(P>B)U=this.width,G=this.width/P,L=(this.height-G)/2;else G=this.height,U=this.height*P,Z=(this.width-U)/2;break}}if(!j){K.drawImage(O,Z,L,U,G);return}let z=(j.x??0)+Z,Y=(j.y??0)+L,A=j.width??U,N=j.height??G,C=j.rotation??0,_=j.scaleX??1,E=j.scaleY??1,W=j.opacity??1,R=j.filter,F=W!==1,b=C!==0||_!==1||E!==1,w=!!R&&R!=="none";if(!(F||b||w)){K.drawImage(O,z,Y,A,N);return}if(!b){if(K.save(),F)K.globalAlpha=W;if(w)K.filter=R;K.drawImage(O,z,Y,A,N),K.restore();return}let q=j.anchorX??0.5,X=j.anchorY??0.5;if(K.save(),F)K.globalAlpha=W;if(w)K.filter=R;if(K.translate(z+A*q,Y+N*X),C!==0)K.rotate(C*Math.PI/180);if(_!==1||E!==1)K.scale(_,E);K.drawImage(O,-A*q,-N*X,A,N),K.restore()}createWorkerSource(O){let $={id:O.id,type:O.type,duration:O.duration,width:O.width,height:O.height,async getFrameAt(){throw Error("getFrameAt is not available when worker rendering is enabled")},getAudioBufferSink(){return null},hasAudio(){return O.hasAudio??!1},dispose(){}};return this.workerSources.set($.id,$),$}async loadWorkerAudio(O,$){if(!this.audioManager)return;if(this.workerAudioSources.has($))this.unloadWorkerAudio($);try{let K=await this.sourcePool.loadAudio(O,{id:$});this.workerAudioSources.set($,K),this.registerSourceAudio(K)}catch{}}unloadWorkerAudio(O){if(!this.audioManager)return;if(this.workerAudioSources.has(O))this.audioManager.unregisterSource(O),this.registeredAudioSources.delete(O),this.sourcePool.unloadSource(O),this.workerAudioSources.delete(O)}serializeWorkerFrame(O){if(!this.workerClient)throw Error("Worker compositor not initialized");let $=O.layers,K=Array($.length);for(let j=0;j<$.length;j++){let Q=$[j],V=Q.source.id;if(!this.workerSources.has(V))throw Error(`Layer source ${V} is not managed by this compositor`);K[j]={sourceId:V,sourceTime:Q.sourceTime,transform:Q.transform,fitMode:Q.fitMode,visible:Q.visible,zIndex:Q.zIndex}}return{time:O.time,layers:K}}clear(){if(this.workerClient){this.workerClient.clear();return}if(!this.ctx)return;this.ctx.fillStyle=this.backgroundColor,this.ctx.fillRect(0,0,this.width,this.height)}preview(O){this.checkDisposed(),this.previewOptions=O,this.state.duration=O.duration,this.lastRenderTime=0,this.emitter.emit("compositionchange",void 0)}async play(){if(this.checkDisposed(),this.state.playing)return;if(!this.previewOptions)throw Error("No preview configured. Call preview() first.");if(this.state.seeking){this.pendingPlayAfterSeek=!0;return}if(this.state.playing=!0,this.lastFrameTime=performance.now(),this.lastRenderTime=this.lastFrameTime,this.emitter.emit("play",void 0),this.audioManager){this.activeAudioSourceIds.clear(),await this.audioManager.play(this.state.currentTime);let O=this.previewOptions.getComposition(this.state.currentTime);this.processAudioLayers(O.audio??[],this.state.currentTime)}this.startRenderLoop()}pause(){if(this.checkDisposed(),!this.state.playing)return;if(this.state.playing=!1,this.pendingPlayAfterSeek=!1,this.stopRenderLoop(),this.audioManager)this.audioManager.pause();this.emitter.emit("pause",void 0)}async seek(O){if(this.checkDisposed(),!this.previewOptions)return;let $=Math.max(0,Math.min(O,this.state.duration));if(this.state.seeking=!0,this.emitter.emit("seeking",{time:$}),this.state.currentTime=$,this.audioManager)await this.audioManager.seek($),this.activeAudioSourceIds.clear();let K=this.previewOptions.getComposition($);if(this.audioManager)this.processAudioLayers(K.audio??[],$);if(await this.render(K),this.state.seeking=!1,this.emitter.emit("seeked",{time:$}),this.emitter.emit("timeupdate",{currentTime:$}),this.pendingPlayAfterSeek)this.pendingPlayAfterSeek=!1,await this.play()}startRenderLoop(){if(this.animationFrameId!==null)return;let O=()=>{if(!this.state.playing||!this.previewOptions)return;this.animationFrameId=requestAnimationFrame(O);let $=performance.now(),K=($-this.lastFrameTime)/1000;if(this.lastFrameTime=$,this.state.currentTime+=K,this.state.currentTime>=this.state.duration)if(this.previewOptions.loop)this.state.currentTime=0;else{this.state.currentTime=this.state.duration,this.pause(),this.emitter.emit("ended",void 0);return}let j=this.previewOptions.fps??0,Q=j>0?1000/j:0;if(!this.renderPending&&(Q===0||$-this.lastRenderTime>=Q)){this.renderPending=!0,this.lastRenderTime=$;let J=this.previewOptions.getComposition(this.state.currentTime);if(this.audioManager)this.processAudioLayers(J.audio??[],this.state.currentTime);this.render(J).catch(()=>{}).finally(()=>{this.renderPending=!1})}if($-this.lastTimeUpdateEmit>=this.timeUpdateThrottleMs)this.lastTimeUpdateEmit=$,this.emitter.emit("timeupdate",{currentTime:this.state.currentTime})};this.animationFrameId=requestAnimationFrame(O)}stopRenderLoop(){if(this.animationFrameId!==null)cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null}async exportFrame(O,$={}){if(this.checkDisposed(),!this.previewOptions)return null;let K=this.previewOptions.getComposition(O);if(this.workerClient){let j=this.serializeWorkerFrame(K);return this.workerClient.exportFrame(j,$)}if(await this.render(K),"toBlob"in this.canvas)return new Promise((j)=>{this.canvas.toBlob((Q)=>j(Q),`image/${$.format??"png"}`,$.quality)});else return this.canvas.convertToBlob({type:`image/${$.format??"png"}`,quality:$.quality})}get currentTime(){return this.state.currentTime}get duration(){return this.state.duration}get playing(){return this.state.playing}get paused(){return!this.state.playing}get seeking(){return this.state.seeking}getWidth(){return this.width}getHeight(){return this.height}resize(O,$,K){if(this.checkDisposed(),this.width=O,this.height=$,K!==void 0)this.fitMode=K;if(this.workerClient){this.workerClient.resize(O,$,this.fitMode);return}this.canvas.width=O,this.canvas.height=$,this.clear()}setFitMode(O){if(this.checkDisposed(),this.fitMode=O,this.workerClient)this.workerClient.resize(this.width,this.height,O)}getFitMode(){return this.fitMode}on(O,$){return this.emitter.on(O,$)}once(O,$){return this.emitter.once(O,$)}off(O,$){this.emitter.off(O,$)}setVolume(O){this.audioManager?.setMasterVolume(O)}setMuted(O){this.audioManager?.setMasterMuted(O)}getAudioContext(){if(!this.audioManager)throw Error("Audio is disabled for this compositor");return this.audioManager.getAudioContext()}checkDisposed(){if(this.disposed)throw Error("Compositor has been disposed")}dispose(){if(this.disposed)return;this.disposed=!0,this.stopRenderLoop(),this.audioManager?.dispose(),this.workerClient?.dispose(),this.workerClient=null,this.sourcePool.dispose(),this.registeredAudioSources.clear(),this.activeAudioSourceIds.clear(),this.audioScratch.nextActiveSourceIds.clear(),this.audioScratch.newSourceIds.length=0,this.audioScratch.newSourceTimes.length=0,this.workerSources.clear(),this.workerAudioSources.clear(),this.emitter.removeAllListeners(),this.ctx=null,this.previewOptions=null}}import{CanvasSink as SO,VideoSampleSink as WO}from"mediabunny";class y{canvas;ctx=null;isInitialized=!1;rotation=0;constructor(O){this.canvas=O.canvas,this.rotation=O.rotation??0,this.initialize()}initialize(){try{if(this.ctx=this.canvas.getContext("2d",{alpha:!1,desynchronized:!0}),!this.ctx)return!1;return this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.isInitialized=!0,!0}catch{return!1}}isReady(){return this.isInitialized&&this.ctx!==null}render(O){if(!this.isReady()||!this.ctx)return!1;try{let{width:$,height:K}=O;if($===0||K===0)return!1;let j=this.canvas.width,Q=this.canvas.height;if(j===0||Q===0)return!1;let V=this.rotation===90||this.rotation===270,J=V?K:$,U=V?$:K,G=Math.min(j/J,Q/U),Z=Math.round(J*G),L=Math.round(U*G),z=Math.round((j-Z)/2),Y=Math.round((Q-L)/2);if(this.ctx.fillStyle="black",this.ctx.fillRect(0,0,j,Q),this.ctx.save(),this.ctx.translate(z+Z/2,Y+L/2),this.rotation!==0)this.ctx.rotate(this.rotation*Math.PI/180);if(V)this.ctx.drawImage(O,0,0,$,K,-L/2,-Z/2,L,Z);else this.ctx.drawImage(O,0,0,$,K,-Z/2,-L/2,Z,L);return this.ctx.restore(),!0}catch{return!1}}clear(){if(!this.isReady()||!this.ctx)return;this.ctx.fillStyle="black",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)}setRotation(O){this.rotation=O}getRotation(){return this.rotation}dispose(){this.ctx=null,this.isInitialized=!1}}class r{resources;isInitialized=!1;canvas;textureWidth=0;textureHeight=0;options;boundHandleContextLost=null;boundHandleContextRestored=null;rotation=0;positionsArray=new Float32Array(8);vertexShaderSource=`
|
|
2
2
|
attribute vec2 a_position;
|
|
3
3
|
attribute vec2 a_texCoord;
|
|
4
4
|
varying vec2 v_texCoord;
|
|
@@ -16,7 +16,7 @@ class I{events=new Map;maxListeners;captureRejections;emitCache=[];constructor(O
|
|
|
16
16
|
vec4 color = texture2D(u_texture, v_texCoord);
|
|
17
17
|
gl_FragColor = color;
|
|
18
18
|
}
|
|
19
|
-
`;constructor(O){this.canvas=O.canvas,this.options=O,this.rotation=O.rotation??0,this.resources={gl:null,program:null,texture:null,vertexBuffer:null,texCoordBuffer:null,positionLocation:-1,texCoordLocation:-1,textureLocation:null},this.initialize()}initialize(){try{let O={alpha:this.options.alpha??!1,antialias:this.options.antialias??!1,depth:!1,stencil:!1,preserveDrawingBuffer:this.options.preserveDrawingBuffer??!1,powerPreference:this.options.powerPreference??"high-performance"},$=this.canvas.getContext("webgl",O);if(!$&&"getContext"in this.canvas)$=this.canvas.getContext("experimental-webgl",O);if(!$)return!1;this.resources.gl=$;let K=this.createShader($,$.VERTEX_SHADER,this.vertexShaderSource),j=this.createShader($,$.FRAGMENT_SHADER,this.fragmentShaderSource);if(!K||!j)throw Error("Failed to create shaders");let Q=$.createProgram();if(!Q)throw Error("Failed to create program");if($.attachShader(Q,K),$.attachShader(Q,j),$.linkProgram(Q),!$.getProgramParameter(Q,$.LINK_STATUS)){let J=$.getProgramInfoLog(Q);throw Error(`Failed to link program: ${J}`)}this.resources.program=Q,this.resources.positionLocation=$.getAttribLocation(Q,"a_position"),this.resources.texCoordLocation=$.getAttribLocation(Q,"a_texCoord"),this.resources.textureLocation=$.getUniformLocation(Q,"u_texture"),this.setupQuadBuffers($);let V=$.createTexture();if(!V)throw Error("Failed to create texture");if($.bindTexture($.TEXTURE_2D,V),$.texParameteri($.TEXTURE_2D,$.TEXTURE_WRAP_S,$.CLAMP_TO_EDGE),$.texParameteri($.TEXTURE_2D,$.TEXTURE_WRAP_T,$.CLAMP_TO_EDGE),$.texParameteri($.TEXTURE_2D,$.TEXTURE_MIN_FILTER,$.LINEAR),$.texParameteri($.TEXTURE_2D,$.TEXTURE_MAG_FILTER,$.LINEAR),this.resources.texture=V,$.disable($.DEPTH_TEST),$.disable($.CULL_FACE),$.disable($.BLEND),"addEventListener"in this.canvas)this.boundHandleContextLost=this.handleContextLost.bind(this),this.boundHandleContextRestored=this.handleContextRestored.bind(this),this.canvas.addEventListener("webglcontextlost",this.boundHandleContextLost,!1),this.canvas.addEventListener("webglcontextrestored",this.boundHandleContextRestored,!1);return this.isInitialized=!0,!0}catch{return this.cleanup(),!1}}createShader(O,$,K){let j=O.createShader($);if(!j)return null;if(O.shaderSource(j,K),O.compileShader(j),!O.getShaderParameter(j,O.COMPILE_STATUS))return O.deleteShader(j),null;return j}setupQuadBuffers(O){let $=new Float32Array([-1,-1,1,-1,-1,1,1,1]),K=new Float32Array([0,1,1,1,0,0,1,0]),j=O.createBuffer();O.bindBuffer(O.ARRAY_BUFFER,j),O.bufferData(O.ARRAY_BUFFER,$,O.STATIC_DRAW),this.resources.vertexBuffer=j;let Q=O.createBuffer();O.bindBuffer(O.ARRAY_BUFFER,Q),O.bufferData(O.ARRAY_BUFFER,K,O.STATIC_DRAW),this.resources.texCoordBuffer=Q}isReady(){return this.isInitialized&&this.resources.gl!==null}render(O){if(!this.isInitialized||!this.resources.gl)return!1;let $=this.resources.gl;try{let{width:K,height:j}=O;if(K===0||j===0)return!1;let Q=this.canvas.width,V=this.canvas.height;if(Q===0||V===0)return!1;if($.viewport(0,0,Q,V),$.bindTexture($.TEXTURE_2D,this.resources.texture),K!==this.textureWidth||j!==this.textureHeight)$.texImage2D($.TEXTURE_2D,0,$.RGBA,$.RGBA,$.UNSIGNED_BYTE,O),this.textureWidth=K,this.textureHeight=j;else $.texSubImage2D($.TEXTURE_2D,0,0,0,$.RGBA,$.UNSIGNED_BYTE,O);$.clearColor(0,0,0,1),$.clear($.COLOR_BUFFER_BIT);let J=this.rotation===90||this.rotation===270,U=J?this.textureHeight:this.textureWidth,
|
|
19
|
+
`;constructor(O){this.canvas=O.canvas,this.options=O,this.rotation=O.rotation??0,this.resources={gl:null,program:null,texture:null,vertexBuffer:null,texCoordBuffer:null,positionLocation:-1,texCoordLocation:-1,textureLocation:null},this.initialize()}initialize(){try{let O={alpha:this.options.alpha??!1,antialias:this.options.antialias??!1,depth:!1,stencil:!1,preserveDrawingBuffer:this.options.preserveDrawingBuffer??!1,powerPreference:this.options.powerPreference??"high-performance"},$=this.canvas.getContext("webgl",O);if(!$&&"getContext"in this.canvas)$=this.canvas.getContext("experimental-webgl",O);if(!$)return!1;this.resources.gl=$;let K=this.createShader($,$.VERTEX_SHADER,this.vertexShaderSource),j=this.createShader($,$.FRAGMENT_SHADER,this.fragmentShaderSource);if(!K||!j)throw Error("Failed to create shaders");let Q=$.createProgram();if(!Q)throw Error("Failed to create program");if($.attachShader(Q,K),$.attachShader(Q,j),$.linkProgram(Q),!$.getProgramParameter(Q,$.LINK_STATUS)){let J=$.getProgramInfoLog(Q);throw Error(`Failed to link program: ${J}`)}this.resources.program=Q,this.resources.positionLocation=$.getAttribLocation(Q,"a_position"),this.resources.texCoordLocation=$.getAttribLocation(Q,"a_texCoord"),this.resources.textureLocation=$.getUniformLocation(Q,"u_texture"),this.setupQuadBuffers($);let V=$.createTexture();if(!V)throw Error("Failed to create texture");if($.bindTexture($.TEXTURE_2D,V),$.texParameteri($.TEXTURE_2D,$.TEXTURE_WRAP_S,$.CLAMP_TO_EDGE),$.texParameteri($.TEXTURE_2D,$.TEXTURE_WRAP_T,$.CLAMP_TO_EDGE),$.texParameteri($.TEXTURE_2D,$.TEXTURE_MIN_FILTER,$.LINEAR),$.texParameteri($.TEXTURE_2D,$.TEXTURE_MAG_FILTER,$.LINEAR),this.resources.texture=V,$.disable($.DEPTH_TEST),$.disable($.CULL_FACE),$.disable($.BLEND),"addEventListener"in this.canvas)this.boundHandleContextLost=this.handleContextLost.bind(this),this.boundHandleContextRestored=this.handleContextRestored.bind(this),this.canvas.addEventListener("webglcontextlost",this.boundHandleContextLost,!1),this.canvas.addEventListener("webglcontextrestored",this.boundHandleContextRestored,!1);return this.isInitialized=!0,!0}catch{return this.cleanup(),!1}}createShader(O,$,K){let j=O.createShader($);if(!j)return null;if(O.shaderSource(j,K),O.compileShader(j),!O.getShaderParameter(j,O.COMPILE_STATUS))return O.deleteShader(j),null;return j}setupQuadBuffers(O){let $=new Float32Array([-1,-1,1,-1,-1,1,1,1]),K=new Float32Array([0,1,1,1,0,0,1,0]),j=O.createBuffer();O.bindBuffer(O.ARRAY_BUFFER,j),O.bufferData(O.ARRAY_BUFFER,$,O.STATIC_DRAW),this.resources.vertexBuffer=j;let Q=O.createBuffer();O.bindBuffer(O.ARRAY_BUFFER,Q),O.bufferData(O.ARRAY_BUFFER,K,O.STATIC_DRAW),this.resources.texCoordBuffer=Q}isReady(){return this.isInitialized&&this.resources.gl!==null}render(O){if(!this.isInitialized||!this.resources.gl)return!1;let $=this.resources.gl;try{let{width:K,height:j}=O;if(K===0||j===0)return!1;let Q=this.canvas.width,V=this.canvas.height;if(Q===0||V===0)return!1;if($.viewport(0,0,Q,V),$.bindTexture($.TEXTURE_2D,this.resources.texture),K!==this.textureWidth||j!==this.textureHeight)$.texImage2D($.TEXTURE_2D,0,$.RGBA,$.RGBA,$.UNSIGNED_BYTE,O),this.textureWidth=K,this.textureHeight=j;else $.texSubImage2D($.TEXTURE_2D,0,0,0,$.RGBA,$.UNSIGNED_BYTE,O);$.clearColor(0,0,0,1),$.clear($.COLOR_BUFFER_BIT);let J=this.rotation===90||this.rotation===270,U=J?this.textureHeight:this.textureWidth,G=J?this.textureWidth:this.textureHeight,Z=Math.min(Q/U,V/G),L=Math.round(U*Z),z=Math.round(G*Z),Y=Math.round((Q-L)/2),A=Math.round((V-z)/2),N=Y/Q*2-1,C=(Y+L)/Q*2-1,_=1-A/V*2,E=1-(A+z)/V*2,W=(N+C)/2,R=(_+E)/2,F=(C-N)/2,b=(_-E)/2,w=this.rotation*Math.PI/180,S=Math.cos(w),q=Math.sin(w),X=J?b:F,P=J?F:b,B=this.positionsArray;return B[0]=-X*S- -P*q+W,B[1]=-X*q+-P*S+R,B[2]=X*S- -P*q+W,B[3]=X*q+-P*S+R,B[4]=-X*S-P*q+W,B[5]=-X*q+P*S+R,B[6]=X*S-P*q+W,B[7]=X*q+P*S+R,$.bindBuffer($.ARRAY_BUFFER,this.resources.vertexBuffer),$.bufferData($.ARRAY_BUFFER,B,$.DYNAMIC_DRAW),$.useProgram(this.resources.program),$.bindBuffer($.ARRAY_BUFFER,this.resources.vertexBuffer),$.enableVertexAttribArray(this.resources.positionLocation),$.vertexAttribPointer(this.resources.positionLocation,2,$.FLOAT,!1,0,0),$.bindBuffer($.ARRAY_BUFFER,this.resources.texCoordBuffer),$.enableVertexAttribArray(this.resources.texCoordLocation),$.vertexAttribPointer(this.resources.texCoordLocation,2,$.FLOAT,!1,0,0),$.uniform1i(this.resources.textureLocation,0),$.drawArrays($.TRIANGLE_STRIP,0,4),!0}catch{return!1}}clear(){if(!this.resources.gl)return;let O=this.resources.gl;O.clearColor(0,0,0,1),O.clear(O.COLOR_BUFFER_BIT)}handleContextLost(O){O.preventDefault(),this.isInitialized=!1}handleContextRestored(){this.initialize()}cleanup(){let O=this.resources.gl;if(!O)return;if(this.resources.texture)O.deleteTexture(this.resources.texture);if(this.resources.vertexBuffer)O.deleteBuffer(this.resources.vertexBuffer);if(this.resources.texCoordBuffer)O.deleteBuffer(this.resources.texCoordBuffer);if(this.resources.program)O.deleteProgram(this.resources.program);this.resources={gl:null,program:null,texture:null,vertexBuffer:null,texCoordBuffer:null,positionLocation:-1,texCoordLocation:-1,textureLocation:null},this.isInitialized=!1}setRotation(O){this.rotation=O}getRotation(){return this.rotation}dispose(){if(this.resources.gl){let O=this.resources.gl.getExtension("WEBGL_lose_context");if(O)try{O.loseContext()}catch{}}if(this.cleanup(),"removeEventListener"in this.canvas){if(this.boundHandleContextLost)this.canvas.removeEventListener("webglcontextlost",this.boundHandleContextLost),this.boundHandleContextLost=null;if(this.boundHandleContextRestored)this.canvas.removeEventListener("webglcontextrestored",this.boundHandleContextRestored),this.boundHandleContextRestored=null}}}class t{canvas;device=null;context=null;pipeline=null;texture=null;sampler=null;bindGroup=null;vertexBuffer=null;isInitialized=!1;textureWidth=0;textureHeight=0;powerPreference;rotation=0;quadArray=new Float32Array(16);vertexShaderSource=`
|
|
20
20
|
struct VSOut {
|
|
21
21
|
@builtin(position) pos : vec4f,
|
|
22
22
|
@location(0) uv : vec2f,
|
|
@@ -37,4 +37,4 @@ class I{events=new Map;maxListeners;captureRejections;emitCache=[];constructor(O
|
|
|
37
37
|
fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
|
|
38
38
|
return textureSample(texture_view, texture_sampler, uv);
|
|
39
39
|
}
|
|
40
|
-
`;constructor(O){this.canvas=O.canvas,this.powerPreference=O.powerPreference||"high-performance",this.rotation=O.rotation??0,this.initialize().catch(($)=>{console.error("WebGPU initialization failed:",$)})}async initialize(){try{let O=navigator;if(!O.gpu)return console.log("WebGPU not available in navigator"),!1;let $=await O.gpu.requestAdapter({powerPreference:this.powerPreference});if(!$)return console.log("WebGPU adapter not available"),!1;if(this.device=await $.requestDevice(),!this.device)return console.log("WebGPU device not available"),!1;if("getContext"in this.canvas)this.context=this.canvas.getContext("webgpu");if(!this.context)return console.log("WebGPU context not available on canvas"),!1;let K=O.gpu.getPreferredCanvasFormat();return this.context.configure({device:this.device,format:K,usage:GPUTextureUsage.RENDER_ATTACHMENT,alphaMode:"opaque"}),await this.createRenderPipeline(),this.createVertexBuffer(),this.isInitialized=!0,console.log("WebGPU renderer initialized successfully"),!0}catch(O){return console.error("WebGPU initialization error:",O),!1}}async createRenderPipeline(){if(!this.device)return;let O=navigator;if(!O.gpu)return;let $=this.device.createShaderModule({code:this.vertexShaderSource}),K=this.device.createShaderModule({code:this.fragmentShaderSource});this.pipeline=this.device.createRenderPipeline({layout:"auto",vertex:{module:$,entryPoint:"vs_main",buffers:[{arrayStride:16,attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"}]}]},fragment:{module:K,entryPoint:"fs_main",targets:[{format:O.gpu.getPreferredCanvasFormat()}]},primitive:{topology:"triangle-strip"}})}createVertexBuffer(){if(!this.device)return;this.vertexBuffer=this.device.createBuffer({size:64,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST})}createTexture(O,$){if(!this.device)return;if(this.texture)this.texture.destroy();if(this.texture=this.device.createTexture({size:{width:O,height:$},format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),!this.sampler)this.sampler=this.device.createSampler({magFilter:"linear",minFilter:"linear",addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"});this.createBindGroup()}createBindGroup(){if(!this.device||!this.texture||!this.sampler||!this.pipeline)return;this.bindGroup=this.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:this.sampler},{binding:1,resource:this.texture.createView()}]})}isReady(){return this.isInitialized&&this.device!==null&&this.context!==null&&this.pipeline!==null}render(O){if(!this.isReady()||!this.device||!this.context||!this.pipeline)return!1;try{let{width:$,height:K}=O;if($===0||K===0)return console.warn(`WebGPU: Source canvas has zero dimensions (${$}x${K})`),!1;let j=this.canvas.width,Q=this.canvas.height;if(j===0||Q===0)return console.warn(`WebGPU: Output canvas has zero dimensions (${j}x${Q})`),!1;if($!==this.textureWidth||K!==this.textureHeight)this.createTexture($,K),this.textureWidth=$,this.textureHeight=K;if(!this.texture)return!1;try{this.device.queue.copyExternalImageToTexture({source:O},{texture:this.texture},{width:$,height:K})}catch{let n=O.getContext("2d");if(!n)return!1;if(!("getImageData"in n))return!1;let BO=n.getImageData(0,0,$,K),XO=new Uint8Array(BO.data.buffer);this.device.queue.writeTexture({texture:this.texture,origin:{x:0,y:0,z:0}},XO,{bytesPerRow:$*4,rowsPerImage:K},{width:$,height:K,depthOrArrayLayers:1})}let V=this.device.createCommandEncoder(),J=this.context.getCurrentTexture().createView(),U=V.beginRenderPass({colorAttachments:[{view:J,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]});if(U.setPipeline(this.pipeline),this.bindGroup)U.setBindGroup(0,this.bindGroup);let Z=this.rotation===90||this.rotation===270,L=Z?this.textureHeight:this.textureWidth,A=Z?this.textureWidth:this.textureHeight,G=Math.min(j/L,Q/A),X=Math.round(L*G),N=Math.round(A*G),Y=Math.round((j-X)/2),M=Math.round((Q-N)/2),q=Y/j*2-1,C=(Y+X)/j*2-1,S=1-M/Q*2,R=1-(M+N)/Q*2,W=(q+C)/2,b=(S+R)/2,w=(C-q)/2,F=(S-R)/2,_=this.rotation*Math.PI/180,B=Math.cos(_),P=Math.sin(_),z=Z?F:w,T=Z?w:F,D=this.quadArray;if(D[0]=-z*B- -T*P+W,D[1]=-z*P+-T*B+b,D[2]=0,D[3]=1,D[4]=z*B- -T*P+W,D[5]=z*P+-T*B+b,D[6]=1,D[7]=1,D[8]=-z*B-T*P+W,D[9]=-z*P+T*B+b,D[10]=0,D[11]=0,D[12]=z*B-T*P+W,D[13]=z*P+T*B+b,D[14]=1,D[15]=0,this.vertexBuffer)this.device.queue.writeBuffer(this.vertexBuffer,0,D),U.setVertexBuffer(0,this.vertexBuffer);return U.draw(4,1,0,0),U.end(),this.device.queue.submit([V.finish()]),!0}catch{return!1}}clear(){if(!this.isReady()||!this.device||!this.context)return;try{let O=this.device.createCommandEncoder(),$=this.context.getCurrentTexture().createView();O.beginRenderPass({colorAttachments:[{view:$,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]}).end(),this.device.queue.submit([O.finish()])}catch{}}setRotation(O){this.rotation=O}getRotation(){return this.rotation}dispose(){try{if(this.texture)this.texture.destroy(),this.texture=null;if(this.vertexBuffer)this.vertexBuffer.destroy(),this.vertexBuffer=null;this.device=null,this.context=null,this.pipeline=null,this.sampler=null,this.bindGroup=null,this.isInitialized=!1}catch{}}}class H{canvas;powerPreference;constructor(O){this.canvas=O.canvas,this.powerPreference=O.powerPreference||"high-performance"}async createRenderer(O){try{switch(O){case"webgpu":return await this.createWebGPURenderer();case"webgl":return this.createWebGLRenderer();case"canvas2d":return this.createCanvas2DRenderer();default:return null}}catch{return null}}async createRendererWithFallback(O){let $=[O];if(O!=="webgl")$.push("webgl");if(O!=="canvas2d")$.push("canvas2d");for(let j of $){let Q=await this.createRenderer(j);if(Q?.isReady())return{renderer:Q,actualType:j};if(Q)Q.dispose()}return{renderer:this.createCanvas2DRenderer(),actualType:"canvas2d"}}async createWebGPURenderer(){if(!navigator.gpu)return null;if(!("getContext"in this.canvas))return console.log("WebGPU requires HTMLCanvasElement, not OffscreenCanvas"),null;let $=new t({canvas:this.canvas,powerPreference:this.powerPreference}),K=1000,j=performance.now();if(await(async()=>{while(!$.isReady()){if(performance.now()-j>K)return console.log("WebGPU renderer initialization timed out"),!1;if("requestIdleCallback"in window)await new Promise((J)=>requestIdleCallback(()=>J(void 0)));else await new Promise((J)=>requestAnimationFrame(()=>J(void 0)))}return!0})())return console.log("WebGPU renderer initialized successfully"),$;return $.isReady()?$:null}createWebGLRenderer(){try{let O=new r({canvas:this.canvas,powerPreference:this.powerPreference,preserveDrawingBuffer:!1,antialias:!1,alpha:!1});return O.isReady()?O:null}catch{return null}}createCanvas2DRenderer(){return new y({canvas:this.canvas})}static getSupportedRenderers(){let O=[];if(navigator.gpu)O.push("webgpu");try{let K=document.createElement("canvas");if(K.getContext("webgl")||K.getContext("experimental-webgl"))O.push("webgl")}catch{}return O.push("canvas2d"),O}static getRendererDisplayName(O){switch(O){case"canvas2d":return"Canvas 2D";case"webgl":return"WebGL";case"webgpu":return"WebGPU";default:return"Unknown"}}static isRendererSupported(O){if(O==="canvas2d")return!0;return H.getSupportedRenderers().includes(O)}}var e=new WeakMap;function AO(O,$){e.set(O,$)}function WO(O){let $=e.get(O);if($)e.delete(O);return $}class v{canvas=null;canvasSink=null;sampleSink=null;options;frameIterator=null;currentFrame=null;nextFrame=null;disposed=!1;renderingId=0;renderer=null;rendererType="canvas2d";onRendererChange;onRendererFallback;onRotationChange;initPromise=null;resizeObserver=null;lastObservedWidth=0;lastObservedHeight=0;videoAspectRatio=null;debug=!1;pluginManager=null;overlayCanvas=null;overlayCtx=null;lastOverlayTime=0;rotation=0;sourceWidth=0;sourceHeight=0;updateFrameResult={frameUpdated:!1,isStarving:!1};constructor(O={}){if(this.options={poolSize:O.poolSize??2,rendererType:O.rendererType??"webgpu",...O},this.rendererType=this.options.rendererType??"webgpu",this.debug=O.debug??!1,this.initPromise=null,O.canvas){if(this.canvas=O.canvas,this.options.width!==void 0)O.canvas.width=this.options.width;if(this.options.height!==void 0)O.canvas.height=this.options.height;this.initPromise=this.initializeRenderer(O.canvas,this.rendererType).catch(($)=>{if(this.debug)console.error("Failed to initialize renderer:",$)}),this.setupResizeObserver(O.canvas)}}setupResizeObserver(O){if(!("getBoundingClientRect"in O)||typeof ResizeObserver>"u")return;if(this.options.width!==void 0||this.options.height!==void 0)return;this.cleanupResizeObserver();let $=O;this.resizeObserver=new ResizeObserver((K)=>{if(this.disposed||!this.resizeObserver)return;for(let j of K){let{width:Q,height:V}=this.getCanvasDimensionsFromEntry(j,$);if(Q!==this.lastObservedWidth||V!==this.lastObservedHeight){if(this.lastObservedWidth=Q,this.lastObservedHeight=V,$.width!==Q||$.height!==V){if($.width=Q,$.height=V,this.updateCanvasAspectRatio(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}}}});try{this.resizeObserver.observe($,{box:"device-pixel-content-box"})}catch{try{this.resizeObserver.observe($,{box:"content-box"})}catch{this.resizeObserver.observe($)}}requestAnimationFrame(()=>{if(this.disposed||!this.resizeObserver)return;let{width:K,height:j}=this.getCanvasDimensionsFromCanvas($);if(this.lastObservedWidth=K,this.lastObservedHeight=j,$.width!==K||$.height!==j){if($.width=K,$.height=j,this.updateCanvasAspectRatio(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}})}getCanvasDimensionsFromEntry(O,$){let K=0,j=0,Q=window.devicePixelRatio||1;if(O.devicePixelContentBoxSize?.length)K=O.devicePixelContentBoxSize[0].inlineSize,j=O.devicePixelContentBoxSize[0].blockSize;else if(O.contentBoxSize?.length)K=Math.round(O.contentBoxSize[0].inlineSize*Q),j=Math.round(O.contentBoxSize[0].blockSize*Q);else if(O.contentRect)K=Math.round(O.contentRect.width*Q),j=Math.round(O.contentRect.height*Q);if(K===0||j===0)return this.getCanvasDimensionsFromCanvas($);return{width:Math.max(1,K),height:Math.max(1,j)}}getCanvasDimensionsFromCanvas(O){let $=0,K=0,j=window.devicePixelRatio||1,Q=O.getBoundingClientRect();if($=Math.round(Q.width*j),K=Math.round(Q.height*j),$===0||K===0)$=Math.round(O.clientWidth*j)||$,K=Math.round(O.clientHeight*j)||K;if($===0||K===0)console.warn("Canvas has zero dimensions after all fallbacks, using 1x1");return{width:Math.max(1,$),height:Math.max(1,K)}}cleanupResizeObserver(){if(this.resizeObserver)this.resizeObserver.disconnect(),this.resizeObserver=null,this.lastObservedWidth=0,this.lastObservedHeight=0}retryUntilCanvasReady(O,$,K=60){let j=0,Q=()=>{if(j++,O.canvas.width>0&&O.canvas.height>0)$();else if(j<K)requestAnimationFrame(Q);else{if(this.debug)console.warn("Canvas dimensions timeout, forcing action");$()}};requestAnimationFrame(Q)}updateCanvasAspectRatio(){if(!this.canvas||!this.videoAspectRatio||!("style"in this.canvas))return;this.canvas.style.aspectRatio=this.videoAspectRatio}updateCanvasBackingBuffer(O){let{width:$,height:K}=this.getCanvasDimensionsFromCanvas(O);if(O.width!==$||O.height!==K)return O.width=$,O.height=K,!0;return!1}async initializeRenderer(O,$){if(this.debug)console.log(`Initializing renderer: ${$}`);let j=await new H({canvas:O}).createRendererWithFallback($);if(this.debug)console.log(`Renderer factory result: ${j.actualType}`);if(!j.renderer.isReady()){if(this.debug)console.warn(`VideoRenderer: Renderer (${j.actualType}) not ready`);throw j.renderer.dispose(),Error(`Failed to initialize renderer: ${j.actualType}`)}if(this.renderer=j.renderer,this.rendererType=j.actualType,this.debug)console.log(`Initialized renderer: ${this.rendererType}`);if(j.actualType!==$){if(this.onRendererFallback)this.onRendererFallback($,j.actualType)}if(this.onRendererChange){if(this.debug)console.log(`Emitting renderer change: ${this.rendererType}`);this.onRendererChange(this.rendererType)}if(this.currentFrame&&this.renderer&&this.renderer.isReady()){if(this.debug)console.log(`Rendering initial frame with ${this.rendererType}`);if(this.currentFrame.canvas.width===0||this.currentFrame.canvas.height===0){if(this.debug)console.log("Initial frame has zero dimensions, scheduling render when ready...");this.retryUntilCanvasReady(this.currentFrame,()=>{if(this.currentFrame&&this.debug)console.log(`Canvas ready (${this.currentFrame.canvas.width}x${this.currentFrame.canvas.height}), rendering initial frame`);if(this.currentFrame)this.renderFrame(this.currentFrame)})}else this.renderFrame(this.currentFrame)}}async setCanvas(O){if(this.cleanupOverlayCanvas(),this.canvas=O,this.renderer)this.renderer.dispose(),this.renderer=null;if(this.options.width!==void 0)O.width=this.options.width;if(this.options.height!==void 0)O.height=this.options.height;this.setupResizeObserver(O);try{await this.initializeRenderer(O,this.rendererType)}catch($){if(this.debug)console.error("Failed to initialize renderer:",$);if(!this.renderer){if(this.renderer=new y({canvas:O}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}}async setVideoTrack(O){if(await this.disposeVideoResources(),O.codec===null)throw Error("Unsupported video codec");if(!await O.canDecode())throw Error(`Cannot decode video track with codec: ${O.codec}`);let K=WO(O);if(this.sourceWidth=O.displayWidth,this.sourceHeight=O.displayHeight,!this.videoAspectRatio&&O.displayWidth&&O.displayHeight){let j=(U,Z)=>Z===0?U:j(Z,U%Z),Q=j(O.displayWidth,O.displayHeight),V=O.displayWidth/Q,J=O.displayHeight/Q;this.videoAspectRatio=`${V}/${J}`,this.updateCanvasAspectRatio()}if(this.notifyRotationChange(),this.initPromise)try{await this.initPromise}catch(j){if(this.debug)console.error("Renderer initialization failed:",j)}if(!this.renderer){if(this.debug)console.warn("Renderer not ready, creating Canvas2D fallback");if(this.canvas){if(this.renderer=new y({canvas:this.canvas}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}if(this.canvas){if(this.options.width!==void 0||this.options.height!==void 0){let j=this.options.width??O.displayWidth,Q=this.options.height??O.displayHeight;if(this.canvas.width!==j||this.canvas.height!==Q)this.canvas.width=j,this.canvas.height=Q,this.updateCanvasAspectRatio()}else if(!this.resizeObserver){if(this.canvas.width===0||this.canvas.height===0)this.canvas.width=O.displayWidth,this.canvas.height=O.displayHeight,this.updateCanvasAspectRatio()}}if(K?.canvasSink)this.canvasSink=K.canvasSink;else this.canvasSink=new FO(O,{rotation:this.options.rotation,poolSize:this.options.poolSize});if(this.sampleSink=new SO(O),this.disposed=!1,K?.firstFrame){if(this.currentFrame=K.firstFrame,this.currentFrame.canvas.width>0&&this.currentFrame.canvas.height>0)this.renderFrame(this.currentFrame);else this.retryUntilCanvasReady(this.currentFrame,()=>{if(this.currentFrame)this.renderFrame(this.currentFrame)},30);this.frameIterator=this.canvasSink.canvases(0),this.frameIterator.next().then(()=>{this.fetchNextFrame()})}else try{await this.seek(0)}catch(j){if(this.debug)console.error("Initial seek failed:",j)}requestAnimationFrame(()=>{if(this.resizeObserver&&this.canvas&&"getBoundingClientRect"in this.canvas)this.updateCanvasBackingBuffer(this.canvas);if(this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)})}async seek(O){if(!this.canvasSink)return;this.renderingId++;let $=this.renderingId;if(this.frameIterator){try{await this.frameIterator.return()}catch{}this.frameIterator=null}let K=this.canvasSink.canvases(O);this.frameIterator=K;try{let j=await K.next(),Q=await K.next();if($!==this.renderingId)return;let V=j.value??null,J=Q.value??null;if(V)if(this.currentFrame=V,V.canvas.width>0&&V.canvas.height>0)this.renderFrame(V);else this.retryUntilCanvasReady(V,()=>this.renderFrame(V),30);if(this.nextFrame=J,!this.nextFrame)this.fetchNextFrame()}catch{}}updateFrame(O){let $=this.updateFrameResult;if(this.disposed)return $.frameUpdated=!1,$.isStarving=!1,$;if(!this.nextFrame){if(this.frameIterator)this.fetchNextFrame();return $.frameUpdated=!1,$.isStarving=!0,$}if(this.nextFrame.timestamp<=O){if(this.currentFrame=this.nextFrame,this.nextFrame=null,this.renderFrame(this.currentFrame),this.frameIterator)this.fetchNextFrame();return $.frameUpdated=!0,$.isStarving=!1,$}return $.frameUpdated=!1,$.isStarving=!1,$}async fetchNextFrame(){let O=this.frameIterator;if(!O||this.disposed)return;let $=this.renderingId;try{let j=(await O.next()).value??null;if(!j||$!==this.renderingId||this.disposed)return;this.nextFrame=j}catch{}}renderFrame(O){if(this.currentFrame=O,!this.renderer||!this.canvas){if(this.initPromise)this.initPromise.then(()=>{if(this.currentFrame===O&&this.renderer&&this.renderer.isReady()){if(this.debug)console.log("Rendering frame after renderer initialization");this.renderFrameWithPlugins(O)}});return}if(!this.renderer.isReady()){if(this.debug)console.warn(`VideoRenderer: Renderer (${this.rendererType}) not ready, skipping frame`);return}this.renderFrameWithPlugins(O)}renderFrameWithPlugins(O){if(!this.renderer||!this.canvas)return;let $=O.timestamp;if(this.pluginManager){if(this.pluginManager.executeBeforeRender(O,$)?.skip)return}let K=O;if(this.pluginManager)K=this.pluginManager.executeTransformFrame(O);if(!this.renderer.render(K.canvas)){if(this.debug)console.warn(`Failed to render frame with ${this.rendererType} (canvas: ${K.canvas.width}x${K.canvas.height})`);if(K.canvas.width===0||K.canvas.height===0)this.retryUntilCanvasReady(K,()=>{if(this.currentFrame===O&&this.renderer&&this.renderer.isReady()){if(!this.renderer.render(K.canvas)&&this.debug)console.warn("Retry render also failed")}},1);return}if(this.executeOverlays($),this.pluginManager)this.pluginManager.executeAfterRender(this.canvas)}executeOverlays(O){if(!this.pluginManager||!this.canvas)return;this.lastOverlayTime=O;let $={width:this.canvas.width,height:this.canvas.height};if(this.rendererType==="canvas2d"){let K=this.canvas.getContext("2d");if(!K)return;this.pluginManager.executeOverlays(K,O,$)}else{if(this.ensureOverlayCanvas(),!this.overlayCanvas||!this.overlayCtx)return;this.overlayCtx.clearRect(0,0,this.overlayCanvas.width,this.overlayCanvas.height),this.pluginManager.executeOverlays(this.overlayCtx,O,$)}}refreshOverlays(){if(!this.canvas)return;if(this.rendererType==="canvas2d"){if(this.currentFrame&&this.renderer?.isReady())this.renderer.render(this.currentFrame.canvas),this.executeOverlays(this.lastOverlayTime)}else if(this.overlayCanvas&&this.overlayCtx){if(this.overlayCtx.clearRect(0,0,this.overlayCanvas.width,this.overlayCanvas.height),this.pluginManager){let O={width:this.canvas.width,height:this.canvas.height};this.pluginManager.executeOverlays(this.overlayCtx,this.lastOverlayTime,O)}}}ensureOverlayCanvas(){if(!this.canvas||!(this.canvas instanceof HTMLCanvasElement))return;if(!this.overlayCanvas){this.overlayCanvas=document.createElement("canvas"),this.overlayCanvas.style.position="absolute",this.overlayCanvas.style.top="0",this.overlayCanvas.style.left="0",this.overlayCanvas.style.width="100%",this.overlayCanvas.style.height="100%",this.overlayCanvas.style.pointerEvents="none",this.overlayCanvas.style.zIndex="1",this.overlayCtx=this.overlayCanvas.getContext("2d");let O=this.canvas.parentElement;if(O){if(getComputedStyle(O).position==="static")O.style.position="relative";O.insertBefore(this.overlayCanvas,this.canvas.nextSibling)}}if(this.overlayCanvas.width!==this.canvas.width||this.overlayCanvas.height!==this.canvas.height)this.overlayCanvas.width=this.canvas.width,this.overlayCanvas.height=this.canvas.height}cleanupOverlayCanvas(){if(this.overlayCanvas)this.overlayCanvas.remove(),this.overlayCanvas=null,this.overlayCtx=null}async getFrameAt(O){if(!this.canvasSink)return null;return this.canvasSink.getCanvas(O)}async getSampleAt(O){if(!this.sampleSink)return null;return this.sampleSink.getSample(O)}async extractFrames(O,$,K=1){if(!this.canvasSink)return[];let j=[],Q=[];for(let V=O;V<=$;V+=K)Q.push(V);for await(let V of this.canvasSink.canvasesAtTimestamps(Q))if(V)j.push(V);return j}async screenshot(O,$={}){if(!this.canvas)return null;if(O!==void 0&&this.canvasSink){let K=await this.canvasSink.getCanvas(O);if(K)this.renderFrame(K)}if("toBlob"in this.canvas)return new Promise((K)=>{this.canvas.toBlob((j)=>K(j),`image/${$.format??"png"}`,$.quality)});else return this.canvas.convertToBlob({type:`image/${$.format??"png"}`,quality:$.quality})}getCurrentFrame(){return this.currentFrame}getNextFrame(){return this.nextFrame}getRendererType(){return this.rendererType}getCanvas(){return this.canvas}updateCanvasDimensions(){if(!this.canvas||!("getBoundingClientRect"in this.canvas))return;let O=this.canvas;if(this.updateCanvasBackingBuffer(O)&&this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}async switchRenderer(O){if(!this.canvas)throw Error("Cannot switch renderer: No canvas set");let $=this.rendererType;if(O===$)return;if(this.debug)console.warn(`Switching renderer from ${$} to ${O}. This will recreate the canvas element.`);if(this.canvas instanceof HTMLCanvasElement){let K=this.canvas,j=K.parentElement;if(!j)throw Error("Cannot switch renderer: Canvas has no parent element");let Q=document.createElement("canvas");if(Q.width=K.width,Q.height=K.height,Q.className=K.className,Q.id=K.id,Q.style.cssText=K.style.cssText,Array.from(K.attributes).forEach((V)=>{if(V.name!=="id"&&V.name!=="class"&&V.name!=="style")Q.setAttribute(V.name,V.value)}),this.renderer)this.renderer.dispose(),this.renderer=null;j.replaceChild(Q,K),this.canvas=Q;try{await this.initializeRenderer(Q,O)}catch(V){if(this.debug)console.error(`Failed to switch to ${O}:`,V);if(!this.renderer){if(this.renderer=new y({canvas:Q}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}}else{if(this.debug)console.warn("Runtime switching for OffscreenCanvas may not work if context is already set");if(this.renderer)this.renderer.dispose(),this.renderer=null;try{await this.initializeRenderer(this.canvas,O)}catch(K){if(this.debug)console.error(`Failed to switch to ${O}:`,K);if(!this.renderer){if(this.renderer=new y({canvas:this.canvas}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}}if(this.currentFrame&&this.renderer&&this.renderer.isReady()){if(this.debug)console.log(`Re-rendering after switch to ${this.rendererType}`);queueMicrotask(()=>{if(this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)})}}setRendererChangeCallback(O){if(this.onRendererChange=O,this.renderer&&this.rendererType){if(this.debug)console.log(`Renderer already initialized as ${this.rendererType}, emitting change event`);O(this.rendererType)}}setRendererFallbackCallback(O){this.onRendererFallback=O}setRotationChangeCallback(O){this.onRotationChange=O}setRotation(O){if(this.rotation===O)return;if(this.rotation=O,this.renderer)this.renderer.setRotation(O);if(this.notifyRotationChange(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}getRotation(){return this.rotation}getDisplaySize(){let O=this.rotation===90||this.rotation===270;return{width:O?this.sourceHeight:this.sourceWidth,height:O?this.sourceWidth:this.sourceHeight}}notifyRotationChange(){if(this.onRotationChange&&this.sourceWidth>0&&this.sourceHeight>0)this.onRotationChange(this.rotation,this.getDisplaySize())}setPluginManager(O){this.pluginManager=O}static getSupportedRenderers(){return H.getSupportedRenderers()}async clearIterators(){if(this.renderingId++,this.frameIterator){try{await this.frameIterator.return()}catch{}this.frameIterator=null}this.currentFrame=null,this.nextFrame=null}async disposeVideoResources(){if(this.disposed=!0,this.renderingId++,this.frameIterator){try{await this.frameIterator.return()}catch{}this.frameIterator=null}this.currentFrame=null,this.nextFrame=null,this.canvasSink=null,this.sampleSink=null,this.videoAspectRatio=null}dispose(){if(this.disposed=!0,this.renderingId++,this.frameIterator)this.frameIterator.return(),this.frameIterator=null;if(this.renderer)this.renderer.dispose(),this.renderer=null;this.cleanupResizeObserver(),this.cleanupOverlayCanvas(),this.currentFrame=null,this.nextFrame=null,this.canvasSink=null,this.sampleSink=null,this.onRendererChange=void 0,this.onRendererFallback=void 0}}var f=2,h="[MediaFox]";function HO(O){f=O}function bO(O,...$){if(f<=0)console.debug(`${h} ${O}`,...$)}function wO(O,...$){if(f<=1)console.info(`${h} ${O}`,...$)}function TO(O,...$){if(f<=2)console.warn(`${h} ${O}`,...$)}function yO(O,...$){if(f<=3)console.error(`${h} ${O}`,...$)}var k={setLevel:HO,debug:bO,info:wO,warn:TO,error:yO};class OO{deps;constructor(O){this.deps=O}async load(O,$={}){try{let K=await this.deps.pluginManager.executeBeforeLoad(O);if(K?.cancel)return;if(K?.data!==void 0)O=K.data;await this.deps.playbackController.reset(),this.deps.sourceManager.disposeCurrent();let j=this.deps.state.getState();if(!$.replacePlaylist)this.deps.state.setState({...j,state:"loading",currentTime:$.startTime??0,playing:!1,paused:!0,ended:!1,seeking:!1,error:null,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null,buffered:[],canPlay:!1,canPlayThrough:!1});else this.deps.state.reset(),this.deps.state.updateLoadingState();this.deps.emit("loadstart",void 0);let Q=$.playlistItemId?this.deps.sourceManager.promoteQueuedSource($.playlistItemId):null;if(!Q)Q=await this.deps.sourceManager.createSource(O);let V=Q.input;if(!V)throw Error("Failed to create input from source");await this.deps.trackManager.initialize(V);let[J,U,Z,L]=await Promise.all([V.computeDuration(),V.getFormat(),V.getMimeType(),V.getMetadataTags()]),A={duration:J,format:U.name,mimeType:Z,metadata:L,hasVideo:this.deps.trackManager.hasVideo(),hasAudio:this.deps.trackManager.hasAudio(),hasSubtitles:this.deps.trackManager.hasSubtitles()};this.deps.state.updateDuration(J),this.deps.state.updateMediaInfo(A),this.deps.state.updateTracks(this.deps.trackManager.getVideoTracks(),this.deps.trackManager.getAudioTracks(),this.deps.trackManager.getSubtitleTracks()),this.deps.playbackController.setDuration(J);let G=Q.prefetchedData,X=G?.videoTrack??this.deps.trackManager.getPrimaryVideoTrack(),N=G?.audioTrack??this.deps.trackManager.getPrimaryAudioTrack();if(X&&G?.canvasSink&&G?.firstFrame)AO(X,{canvasSink:G.canvasSink,firstFrame:G.firstFrame});let Y="",M=!1,q=!1;if(X||N){let C=await this.deps.trackSwitcher.setupInitialTracks(X,N);Y+=C.warningMessage,M=C.videoSupported,q=C.audioSupported}if(!M&&!q){if(!Y)Y="No audio or video track found.";throw Error(Y)}if(Y&&(M||q))this.deps.emit("warning",{type:"codec-warning",message:Y.trim(),error:void 0});if(this.deps.state.updateReadyState(!0,!0),this.deps.emit("loadedmetadata",A),this.deps.emit("loadeddata",void 0),this.deps.emit("canplay",void 0),this.deps.emit("canplaythrough",void 0),this.updateCurrentPlaylistItemDuration(J),await this.deps.pluginManager.executeAfterLoad(A),$.autoplay)await this.play();if($.startTime!==void 0)await this.seek($.startTime)}catch(K){throw this.handleError(K),K}}async play(){try{if(this.deps.state.getState().state==="idle")throw Error("No media loaded");if((await this.deps.pluginManager.executeBeforePlay())?.cancel)return;await this.deps.playbackController.play(),this.deps.state.updatePlaybackState(!0),this.deps.emit("play",void 0),this.deps.emit("playing",void 0),this.deps.pluginManager.executeAfterPlay()}catch(O){throw this.handleError(O),O}}async pause(){if((await this.deps.pluginManager.executeBeforePause())?.cancel)return;this.deps.playbackController.pause(),this.deps.state.updatePlaybackState(!1),this.deps.emit("pause",void 0),this.deps.pluginManager.executeAfterPause()}async seek(O){try{if(this.deps.state.getState().state==="idle")throw Error("No media loaded");let K=await this.deps.pluginManager.executeBeforeSeek(O);if(K?.cancel)return;if(K?.data!==void 0)O=K.data;this.deps.state.updateSeekingState(!0),this.deps.emit("seeking",{currentTime:O}),await this.deps.playbackController.seek(O),this.deps.state.updateSeekingState(!1),this.deps.state.updateTime(this.deps.playbackController.getCurrentTime()),this.deps.emit("seeked",{currentTime:this.deps.playbackController.getCurrentTime()}),this.deps.pluginManager.executeAfterSeek(this.deps.playbackController.getCurrentTime())}catch($){throw this.deps.state.updateSeekingState(!1),this.handleError($),$}}async stop(){try{if((await this.deps.pluginManager.executeBeforeStop())?.cancel)return;await this.pause(),await this.seek(0),this.deps.pluginManager.executeAfterStop()}catch(O){throw this.handleError(O),O}}handleError(O){if(this.deps.pluginManager.executeOnError(O))return;this.deps.state.updateError(O),this.deps.emit("error",O),k.error("Player error:",O)}updateCurrentPlaylistItemDuration(O){let $=this.deps.state.getState(),K=$.currentPlaylistIndex;if(K!==null&&$.playlist.length>0){let j=[...$.playlist],Q=j[K];if(Q)j[K]={...Q,duration:O},this.deps.state.updatePlaylist(j,K)}}}class $O{store;constructor(O){this.store=O}getState(){return this.store.getState()}subscribe(O){return this.store.subscribe(O)}setState(O){this.store.setState(O)}reset(){this.store.reset()}applyInitial(O,$,K){this.store.setState({volume:O,muted:$,playbackRate:K})}updateLoadingState(){this.store.updateLoadingState()}updateReadyState(O,$){this.store.updateReadyState(O,$)}updatePlaybackState(O){this.store.updatePlaybackState(O)}updateSeekingState(O){this.store.updateSeekingState(O)}updateWaitingState(O){this.store.updateWaitingState(O)}updateEndedState(O){this.store.updateEndedState(O)}updateTime(O){this.store.updateTime(O)}updateDuration(O){this.store.updateDuration(O)}updateVolume(O,$){this.store.updateVolume(O,$)}updatePlaybackRate(O){this.store.updatePlaybackRate(O)}updateMediaInfo(O){this.store.updateMediaInfo(O)}updateTracks(O,$,K){this.store.updateTracks(O,$,K)}updateSelectedTracks(O,$){this.store.updateSelectedTracks(O,$)}updateError(O){this.store.updateError(O)}updateRendererType(O){this.store.updateRendererType(O)}updatePlaylist(O,$=null){this.store.updatePlaylist(O,$)}}class KO{chains=new Map;async run(O,$){let K=this.chains.get(O)??Promise.resolve(),j,Q=new Promise((V)=>{j=V});this.chains.set(O,K.then(()=>Q));try{return await K,await $()}finally{j?.()}}}class jO{deps;locks=new KO;constructor(O){this.deps=O}async setupInitialTracks(O,$){let K=!1,j=!1,Q="";if(O)try{if(K=await this.locks.run("video",async()=>{if(O.codec!==null&&await O.canDecode())return await this.deps.playbackController.trySetVideoTrack(O);return!1}),!K)Q+="Unsupported video codec. "}catch(V){Q+="Failed to set up video track. ",k.warn("Video track error:",V)}if($)try{if(j=await this.locks.run("audio",async()=>{if($.codec!==null&&await $.canDecode())return await this.deps.playbackController.trySetAudioTrack($);return!1}),!j)Q+="Unsupported audio codec. "}catch(V){Q+="Failed to set up audio track. ",k.warn("Audio track error:",V)}if(!K&&!j&&!Q)Q="No audio or video track found.";return{videoSupported:K,audioSupported:j,warningMessage:Q}}async selectVideoTrack(O,$){if(!O.selectVideoTrack($))throw Error(`Invalid video track ID: ${$}`);let K=O.getSelectedVideoTrack();if(!K){await this.deps.playbackController.setVideoTrack(null);return}if(!await this.locks.run("video",async()=>{if(K.codec!==null&&await K.canDecode())return await this.deps.playbackController.trySetVideoTrack(K);return!1}))this.deps.emit("warning",{type:"video-codec-unsupported",message:"Video codec not supported.",error:void 0}),await this.deps.playbackController.setVideoTrack(null)}async selectAudioTrack(O,$){if(!O.selectAudioTrack($))throw Error(`Invalid audio track ID: ${$}`);let K=O.getSelectedAudioTrack();if(!K){await this.deps.playbackController.setAudioTrack(null);return}if(!await this.locks.run("audio",async()=>{if(K.codec!==null&&await K.canDecode())return await this.deps.playbackController.trySetAudioTrack(K);return!1}))this.deps.emit("warning",{type:"audio-codec-unsupported",message:"Audio codec not supported. Continuing without audio.",error:void 0}),await this.deps.playbackController.setAudioTrack(null)}}import{AudioBufferSink as IO,AudioSampleSink as xO}from"mediabunny";class p{audioContext;gainNode=null;outputNode=null;bufferSink=null;sampleSink=null;bufferIterator=null;queuedNodes=new Set;startContextTime=0;startMediaTime=0;pauseTime=0;playing=!1;volume=1;muted=!1;disposed=!1;playbackId=0;playbackRate=1;pluginManager=null;constructor(O={}){if(O.audioContext)this.audioContext=O.audioContext;else{let $=window,K=$.AudioContext||$.webkitAudioContext;this.audioContext=new K}this.volume=O.volume??1,this.muted=O.muted??!1,this.setupAudioGraph()}setupAudioGraph(){if(this.gainNode=this.audioContext.createGain(),this.pluginManager){let O=this.pluginManager.executeOnAudioNode(this.audioContext,this.gainNode);this.outputNode=O}else this.outputNode=this.gainNode;this.outputNode.connect(this.audioContext.destination),this.updateGain()}setPluginManager(O){this.pluginManager=O,this.rebuildAudioGraph()}rebuildAudioGraph(){if(!this.gainNode)return;if(this.outputNode?.disconnect(),this.pluginManager){let O=this.pluginManager.executeOnAudioNode(this.audioContext,this.gainNode);this.outputNode=O}else this.outputNode=this.gainNode;this.outputNode.connect(this.audioContext.destination)}async setAudioTrack(O){if(this.dispose(),O.codec===null)throw Error("Unsupported audio codec");if(!await O.canDecode())throw Error(`Cannot decode audio track with codec: ${O.codec}`);this.bufferSink=new IO(O),this.sampleSink=new xO(O),this.disposed=!1}async play(O=this.pauseTime){if(this.playing||!this.bufferSink)return;if(this.audioContext.state==="suspended")await this.audioContext.resume();this.playbackId++;let $=this.playbackId;this.playing=!0,this.startContextTime=this.audioContext.currentTime,this.startMediaTime=O,this.pauseTime=O,this.bufferIterator=this.bufferSink.buffers(O),this.scheduleAudioBuffers($)}async scheduleAudioBuffers(O){let $=this.bufferIterator;if(!$||!this.gainNode)return;try{for await(let{buffer:K,timestamp:j}of $){if(O!==this.playbackId||this.disposed||!this.playing)break;let Q=this.audioContext.createBufferSource();Q.buffer=K,Q.connect(this.gainNode),Q.playbackRate.value=this.playbackRate,Q.playbackRate.setValueAtTime(this.playbackRate,this.audioContext.currentTime);let V=this.startContextTime+(j-this.startMediaTime)/this.playbackRate;if(V>=this.audioContext.currentTime)Q.start(V);else{let J=Math.max(0,(this.audioContext.currentTime-V)*this.playbackRate);if(J<K.duration)Q.start(this.audioContext.currentTime,J);else continue}if(this.queuedNodes.add(Q),Q.onended=()=>{this.queuedNodes.delete(Q)},j-this.getCurrentTime()>=1)await this.waitForCatchup(j)}}catch{}}async waitForCatchup(O){return new Promise(($)=>{let K=setInterval(()=>{if(O-this.getCurrentTime()<1||!this.playing)clearInterval(K),$()},100)})}pause(){if(!this.playing)return;let O=this.getCurrentTime();if(this.playing=!1,this.pauseTime=O,this.stopQueuedNodes(),this.bufferIterator)this.bufferIterator.return(),this.bufferIterator=null}stop(){this.pause(),this.pauseTime=0,this.startContextTime=0,this.startMediaTime=0}async seek(O){let $=this.playing;if($)this.pause();if(this.pauseTime=O,$)await this.play(O)}getCurrentTime(){if(this.playing){let O=this.audioContext.currentTime-this.startContextTime;return this.startMediaTime+O*this.playbackRate}return this.pauseTime}setVolume(O){this.volume=Math.max(0,Math.min(1,O)),this.updateGain()}setMuted(O){this.muted=O,this.updateGain()}updateGain(){if(!this.gainNode)return;let O=this.muted?0:this.volume;this.gainNode.gain.value=O*O}getVolume(){return this.volume}isMuted(){return this.muted}isPlaying(){return this.playing}setPlaybackRate(O){let $=Math.max(0.25,Math.min(4,O));if(this.playbackRate===$)return;let K=this.playing,j=this.getCurrentTime();if(this.playbackRate=$,K)this.pause(),this.pauseTime=j,this.play(j)}getAudioContext(){return this.audioContext}async getBufferAt(O){if(!this.bufferSink)return null;return this.bufferSink.getBuffer(O)}async getSampleAt(O){if(!this.sampleSink)return null;return this.sampleSink.getSample(O)}stopQueuedNodes(){for(let O of this.queuedNodes)try{O.stop()}catch{}this.queuedNodes.clear()}async clearIterators(){if(this.playbackId++,this.stop(),this.bufferIterator){try{await this.bufferIterator.return()}catch{}this.bufferIterator=null}}dispose(){if(this.disposed=!0,this.playbackId++,this.stop(),this.bufferIterator)this.bufferIterator.return(),this.bufferIterator=null;this.bufferSink=null,this.sampleSink=null}destroy(){if(this.dispose(),this.gainNode)this.gainNode.disconnect(),this.gainNode=null;if(this.audioContext.state!=="closed")this.audioContext.close()}}var fO=100,kO=250;class g{videoRenderer;audioManager;playing=!1;currentTime=0;duration=0;playbackRate=1;animationFrameId=null;lastFrameTime=0;syncIntervalId=null;renderIntervalId=null;isWaiting=!1;onTimeUpdate;onEnded;onWaiting;onPlaying;constructor(O={}){this.videoRenderer=new v({canvas:O.canvas,rendererType:O.rendererType}),this.audioManager=new p({audioContext:O.audioContext,volume:O.volume,muted:O.muted}),this.playbackRate=O.playbackRate??1}async setVideoTrack(O){if(!O){this.videoRenderer.dispose();return}await this.videoRenderer.setVideoTrack(O);let $=await O.computeDuration();this.duration=Math.max(this.duration,$)}async trySetVideoTrack(O){try{return await this.setVideoTrack(O),!0}catch{return!1}}async setAudioTrack(O){let $=this.playing,K=this.getCurrentTime();if(!O){this.audioManager.dispose();return}let j=await O.computeDuration(),Q=Math.max(0,Math.min(K,j));if(await this.audioManager.setAudioTrack(O),await this.audioManager.seek(Q),$)await this.audioManager.play(Q);this.currentTime=Q,this.duration=Math.max(this.duration,j)}async trySetAudioTrack(O){try{return await this.setAudioTrack(O),!0}catch{return!1}}async setCanvas(O){await this.videoRenderer.setCanvas(O)}async play(){if(this.playing)return;if(this.playing=!0,this.lastFrameTime=performance.now(),this.currentTime>=this.duration)this.currentTime=0,await this.videoRenderer.seek(0);await this.audioManager.play(this.currentTime),this.startRenderLoop(),this.startSyncInterval()}pause(){if(!this.playing)return;if(this.playing=!1,this.isWaiting=!1,this.audioManager.pause(),this.stopRenderLoop(),this.stopSyncInterval(),this.audioManager.isPlaying())this.currentTime=this.audioManager.getCurrentTime()}async seek(O){let $=Math.max(0,Math.min(O,this.duration));if(this.currentTime=$,await this.videoRenderer.seek($),await this.audioManager.seek($),this.onTimeUpdate)this.onTimeUpdate(this.currentTime)}startRenderLoop(){if(this.animationFrameId!==null||this.renderIntervalId!==null)return;let O=($=!0)=>{if(!this.playing)return;if(this.audioManager.isPlaying())this.currentTime=this.audioManager.getCurrentTime();else{let j=performance.now(),Q=(j-this.lastFrameTime)/1000;this.lastFrameTime=j,this.currentTime+=Q*this.playbackRate}if(this.currentTime>=this.duration){this.handleEnded();return}let{isStarving:K}=this.videoRenderer.updateFrame(this.currentTime);if(K&&!this.isWaiting){if(this.isWaiting=!0,this.onWaiting)this.onWaiting()}else if(!K&&this.isWaiting){if(this.isWaiting=!1,this.onPlaying)this.onPlaying()}if($)this.animationFrameId=requestAnimationFrame(()=>O())};if(this.animationFrameId=requestAnimationFrame(()=>O()),this.renderIntervalId===null)this.renderIntervalId=window.setInterval(()=>O(!1),fO)}stopRenderLoop(){if(this.animationFrameId!==null)cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null;if(this.renderIntervalId!==null)clearInterval(this.renderIntervalId),this.renderIntervalId=null}startSyncInterval(){if(this.syncIntervalId!==null)return;this.syncIntervalId=window.setInterval(()=>{if(this.playing&&this.onTimeUpdate)this.onTimeUpdate(this.currentTime)},kO)}stopSyncInterval(){if(this.syncIntervalId!==null)clearInterval(this.syncIntervalId),this.syncIntervalId=null}handleEnded(){if(this.pause(),this.currentTime=this.duration,this.onEnded)this.onEnded()}getCurrentTime(){if(this.playing&&this.audioManager.isPlaying())return this.audioManager.getCurrentTime();return this.currentTime}getDuration(){return this.duration}setDuration(O){this.duration=O}isPlaying(){return this.playing}setVolume(O){this.audioManager.setVolume(O)}getVolume(){return this.audioManager.getVolume()}setMuted(O){this.audioManager.setMuted(O)}isMuted(){return this.audioManager.isMuted()}setPlaybackRate(O){let $=Math.max(0.25,Math.min(4,O));if(this.playbackRate===$)return;this.playbackRate=$,this.audioManager.setPlaybackRate($),this.lastFrameTime=performance.now()}getPlaybackRate(){return this.playbackRate}setTimeUpdateCallback(O){this.onTimeUpdate=O}setEndedCallback(O){this.onEnded=O}setWaitingCallback(O){this.onWaiting=O}setPlayingCallback(O){this.onPlaying=O}isBuffering(){return this.isWaiting}async screenshot(O){return this.videoRenderer.screenshot(this.currentTime,O)}getVideoRenderer(){return this.videoRenderer}getAudioManager(){return this.audioManager}async switchRenderer(O){await this.videoRenderer.switchRenderer(O)}getRendererType(){return this.videoRenderer.getRendererType()}updateCanvasDimensions(){this.videoRenderer.updateCanvasDimensions()}setRendererChangeCallback(O){this.videoRenderer.setRendererChangeCallback(O)}setRendererFallbackCallback(O){this.videoRenderer.setRendererFallbackCallback(O)}setRotationChangeCallback(O){this.videoRenderer.setRotationChangeCallback(O)}setRotation(O){this.videoRenderer.setRotation(O)}getRotation(){return this.videoRenderer.getRotation()}getDisplaySize(){return this.videoRenderer.getDisplaySize()}setPluginManager(O){this.videoRenderer.setPluginManager(O),this.audioManager.setPluginManager(O)}getCanvas(){return this.videoRenderer.getCanvas()}refreshOverlays(){this.videoRenderer.refreshOverlays()}rebuildAudioGraph(){this.audioManager.rebuildAudioGraph()}async reset(){this.pause(),this.stopRenderLoop(),this.stopSyncInterval(),this.currentTime=0,this.duration=0,await Promise.all([this.videoRenderer.clearIterators(),this.audioManager.clearIterators()]),this.playbackRate=1,this.lastFrameTime=0}dispose(){this.pause(),this.videoRenderer.dispose(),this.audioManager.dispose(),this.onTimeUpdate=void 0,this.onEnded=void 0,this.onWaiting=void 0,this.onPlaying=void 0}destroy(){this.dispose(),this.audioManager.destroy()}}function m(){return crypto?.randomUUID?.()??Date.now().toString(36)+Math.random().toString(36).substr(2)}class QO{store;emitter;switchSource;sourceManager;constructor(O,$,K,j){this.store=O,this.emitter=$,this.switchSource=K,this.sourceManager=j}async loadPlaylist(O,$={}){let K=O.map((j)=>{if(j&&typeof j==="object"&&"mediaSource"in j)return{id:m(),mediaSource:j.mediaSource,title:j.title,poster:j.poster,savedPosition:null,duration:null};else return{id:m(),mediaSource:j,savedPosition:null,duration:null}});if(this.store.updatePlaylist(K,K.length>0?0:null),this.emitter.emit("playlistchange",{playlist:K}),K.length>0&&this.switchSource){let j=K[0];if($.startTime!==void 0)j.savedPosition=$.startTime;await this.switchSource(j,$.autoplay??!1)}}addToPlaylist(O,$){let K=this.createPlaylistItem(O);this.store.addToPlaylist(K,$),this.emitter.emit("playlistadd",{item:K,index:$??this.store.getState().playlist.length-1})}async removeFromPlaylist(O){let $=this.store.getState(),K=$.currentPlaylistIndex,j=$.playing;this.store.removeFromPlaylist(O),this.emitter.emit("playlistremove",{index:O}),this.sourceManager?.disposeQueued($.playlist[O]?.id||"");let Q=this.store.getState(),V=Q.currentPlaylistIndex;if(Q.playlist.length===0)this.sourceManager?.disposeAll(),this.emitter.emit("playlistchange",{playlist:Q.playlist}),this.emitter.emit("playlistend",void 0);if(K===O&&V!==null&&V!==K&&this.switchSource){let J=Q.playlist[V];try{await this.switchSource(J,j)}catch(U){this.emitter.emit("playlistitemerror",{index:V,error:U})}}}clearPlaylist(){this.store.clearPlaylist(),this.emitter.emit("playlistchange",{playlist:[]}),this.emitter.emit("playlistend",void 0),this.sourceManager?.disposeAll()}async next(){let O=this.store.getState(),$=O.currentPlaylistIndex??0,K=O.playlist,j=O.playlistMode,Q=null;if(j==="repeat-one")Q=$;else if(j==="sequential"||j==="repeat")if($<K.length-1)Q=$+1;else if(j==="repeat")Q=0;else{this.emitter.emit("playlistend",void 0);return}else if($<K.length-1)Q=$+1;if(Q!==null)await this.switchTo(Q)}async prev(){let $=this.store.getState().currentPlaylistIndex??0;if($>0)await this.switchTo($-1)}async jumpTo(O){let K=this.store.getState().playlist;if(O>=0&&O<K.length)await this.switchTo(O);else if(O>=K.length)this.emitter.emit("playlistend",void 0)}setMode(O){let $=["sequential","manual","repeat","repeat-one",null];if(!$.includes(O))throw Error(`Invalid playlist mode: ${O}. Valid modes: ${$.filter((K)=>K!==null).join(", ")}, null`);this.store.updatePlaylistMode(O)}async switchTo(O){let $=this.store.getState(),K=$.currentPlaylistIndex,j=$.playing,Q=[...$.playlist];if(K!==null&&K!==O){let U=Q[K];Q[K]={...U,savedPosition:$.currentTime}}this.store.updatePlaylist(Q,O);let V=Q[O];if(this.emitter.emit("playlistitemchange",{index:O,item:V,previousIndex:K??void 0}),this.switchSource)await this.switchSource(V,j);if($.playlistMode==="sequential"&&O<Q.length-1){let U=Q[O+1];this.sourceManager?.preloadSource(U.mediaSource,U.id)}}createPlaylistItem(O){if(O&&typeof O==="object"&&"mediaSource"in O)return{id:m(),mediaSource:O.mediaSource,title:O.title,poster:O.poster,savedPosition:null,duration:null};else return{id:m(),mediaSource:O,savedPosition:null,duration:null}}get playlist(){return this.store.getState().playlist}get currentIndex(){return this.store.getState().currentPlaylistIndex}get currentItem(){let O=this.currentIndex;return O!==null?this.playlist[O]:null}get mode(){return this.store.getState().playlistMode}dispose(){if(this.sourceManager)this.sourceManager=void 0;this.switchSource=void 0}}function GO(O,$,K,j){let Q=$.name;return{player:O,getState(){return O.getState()},subscribe(V){let J=O.subscribe(V),U=()=>J.unsubscribe();return K.stateUnsubscribes.push(U),U},getPluginState(){return K.pluginState},setPluginState(V){K.pluginState=V},on(V,J){O.on(V,J);let U=K.eventListeners.get(V);if(!U)U=new Set,K.eventListeners.set(V,U);U.add(J)},off(V,J){O.off(V,J);let U=K.eventListeners.get(V);if(U)U.delete(J)},getCanvas(){return O.getRenderTarget()},getPlugin(V){return j().get(V)?.plugin},hasPlugin(V){return j().has(V)},log(...V){console.log(`[MediaFox:${Q}]`,...V)},warn(...V){console.warn(`[MediaFox:${Q}]`,...V)},error(...V){console.error(`[MediaFox:${Q}]`,...V)}}}class VO{player;plugins=new Map;overlays=[];overlaysSorted=!1;registrationCounter=0;constructor(O){this.player=O}async install(O){let{name:$}=O;if(this.plugins.has($))throw Error(`Plugin "${$}" is already installed`);if(O.dependencies){for(let j of O.dependencies)if(!this.plugins.has(j))throw Error(`Plugin "${$}" requires plugin "${j}" to be installed first`)}let K={plugin:O,context:null,eventListeners:new Map,stateUnsubscribes:[],pluginState:void 0};K.context=GO(this.player,O,K,()=>this.plugins),this.plugins.set($,K);try{await O.install(K.context)}catch(j){throw this.plugins.delete($),j}if(O.hooks?.render?.onOverlay)this.overlays.push({pluginName:$,zIndex:O.hooks.render.onOverlay.zIndex??0,registrationOrder:this.registrationCounter++,render:O.hooks.render.onOverlay}),this.overlaysSorted=!1,this.player.refreshOverlays()}async uninstall(O){let $=this.plugins.get(O);if(!$)throw Error(`Plugin "${O}" is not installed`);try{await $.plugin.uninstall?.()}catch(j){console.error(`Error uninstalling plugin "${O}":`,j)}for(let[j,Q]of $.eventListeners)for(let V of Q)this.player.off(j,V);for(let j of $.stateUnsubscribes)j();let K=this.overlays.some((j)=>j.pluginName===O);if(this.overlays=this.overlays.filter((j)=>j.pluginName!==O),this.plugins.delete(O),K)this.player.refreshOverlays()}has(O){return this.plugins.has(O)}get size(){return this.plugins.size}async executeBeforeLoad(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.beforeLoad;if(!K)continue;try{let j=await K.call($.plugin,O);if(j?.cancel)return j;if(j?.data!==void 0)O=j.data}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in beforeLoad hook:`,j)}}return{data:O}}async executeAfterLoad(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.afterLoad;if(!K)continue;try{await K.call($.plugin,O)}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in afterLoad hook:`,j)}}}async executeBeforePlay(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforePlay;if(!$)continue;try{let K=await $.call(O.plugin);if(K?.cancel)return K}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in beforePlay hook:`,K)}}return}executeAfterPlay(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPlay;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in afterPlay hook:`,K)}}}async executeBeforePause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforePause;if(!$)continue;try{let K=await $.call(O.plugin);if(K?.cancel)return K}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in beforePause hook:`,K)}}return}executeAfterPause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPause;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in afterPause hook:`,K)}}}async executeBeforeSeek(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.beforeSeek;if(!K)continue;try{let j=await K.call($.plugin,O);if(j?.cancel)return j;if(j?.data!==void 0)O=j.data}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in beforeSeek hook:`,j)}}return{data:O}}executeAfterSeek(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.afterSeek;if(!K)continue;try{K.call($.plugin,O)}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in afterSeek hook:`,j)}}}async executeBeforeStop(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforeStop;if(!$)continue;try{let K=await $.call(O.plugin);if(K?.cancel)return K}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in beforeStop hook:`,K)}}return}executeAfterStop(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterStop;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in afterStop hook:`,K)}}}executeOnError(O){let $=!1;for(let[,K]of this.plugins){let j=K.plugin.hooks?.lifecycle?.onError;if(!j)continue;try{if(j.call(K.plugin,O)?.handled)$=!0}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in onError hook:`,Q)}}return $}executeOnEnded(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.onEnded;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in onEnded hook:`,K)}}}executeBeforeRender(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.render?.beforeRender;if(!j)continue;try{let Q=j.call(K.plugin,O,$);if(Q?.skip)return Q}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in beforeRender hook:`,Q)}}return}executeTransformFrame(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.render?.transformFrame;if(!K)continue;try{let j=K.call($.plugin,O);if(j)O=j}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in transformFrame hook:`,j)}}return O}executeAfterRender(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.render?.afterRender;if(!K)continue;try{K.call($.plugin,O)}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in afterRender hook:`,j)}}}executeOverlays(O,$,K){if(this.overlays.length===0)return;if(!this.overlaysSorted)this.overlays.sort((j,Q)=>{if(j.zIndex!==Q.zIndex)return j.zIndex-Q.zIndex;return j.registrationOrder-Q.registrationOrder}),this.overlaysSorted=!0;for(let j of this.overlays)try{j.render?.render(O,$,K)}catch(Q){console.error(`[MediaFox:${j.pluginName}] Error in onOverlay hook:`,Q)}}executeBeforeStateUpdate(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.state?.beforeStateUpdate;if(!K)continue;try{let j=K.call($.plugin,O);if(j===null)return null;if(j!==void 0)O=j}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in beforeStateUpdate hook:`,j)}}return O}executeOnStateChange(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.state?.onStateChange;if(!j)continue;try{j.call(K.plugin,O,$)}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in onStateChange hook:`,Q)}}}executeBeforeEvent(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.event?.beforeEvent;if(!j)continue;try{let Q=j.call(K.plugin,O,$);if(Q?.cancel)return Q;if(Q?.data!==void 0)$=Q.data}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in beforeEvent hook:`,Q)}}return{data:$}}executeAfterEvent(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.event?.afterEvent;if(!j)continue;try{j.call(K.plugin,O,$)}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in afterEvent hook:`,Q)}}}executeOnAudioNode(O,$){let K=$;for(let[,j]of this.plugins){let Q=j.plugin.hooks?.audio?.onAudioNode;if(!Q)continue;try{let V=Q.call(j.plugin,O,$);if(V)K=V}catch(V){console.error(`[MediaFox:${j.plugin.name}] Error in onAudioNode hook:`,V)}}return K}async dispose(){let O=Array.from(this.plugins.keys()).reverse();for(let $ of O)try{await this.uninstall($)}catch(K){console.error(`Error disposing plugin "${$}":`,K)}}}import{ALL_FORMATS as vO,BlobSource as hO,BufferSource as pO,CanvasSink as gO,FilePathSource as mO,Input as uO,ReadableStreamSource as lO,UrlSource as cO}from"mediabunny";class u{currentSource=null;queuedSources=new Map;options;constructor(O={}){this.options={maxCacheSize:O.maxCacheSize??16777216,crossOrigin:O.crossOrigin,requestInit:O.requestInit}}async createSource(O,$){if(this.currentSource&&!$)this.disposeCurrent();let K,j;if(O instanceof File||O instanceof Blob)K=new hO(O,{maxCacheSize:this.options.maxCacheSize}),j="blob";else if(O instanceof ArrayBuffer||O instanceof Uint8Array)K=new pO(O),j="buffer";else if(typeof O==="string"||O instanceof URL){let J=O instanceof URL?O.href:O;if(typeof window>"u"&&!J.startsWith("http"))K=new mO(J,{maxCacheSize:this.options.maxCacheSize}),j="file";else K=new cO(J,{maxCacheSize:this.options.maxCacheSize,requestInit:this.options.requestInit}),j="url"}else if(typeof ReadableStream<"u"&&O instanceof ReadableStream)K=new lO(O,{maxCacheSize:this.options.maxCacheSize}),j="stream";else throw TypeError("Unsupported media source type");let Q=new uO({source:K,formats:vO}),V={source:K,input:Q,type:j,originalSource:O};if($)this.queuedSources.set($,V);else this.currentSource=V;return V}getCurrentSource(){return this.currentSource}getQueuedSource(O){return this.queuedSources.get(O)||null}async preloadSource(O,$){if(this.queuedSources.has($))return;let K=await this.createSource(O,$);if(K.input)try{let j=await this.prefetchTrackData(K.input);K.prefetchedData=j}catch{}}async prefetchTrackData(O){let $=await O.getVideoTracks(),K=await O.getAudioTracks(),j=$.length>0?$[0]:null,Q=K.length>0?K[0]:null,V=null,J=null,U=0;if(j){if(j.codec!==null&&await j.canDecode()){V=new gO(j,{poolSize:2});let Z=V.canvases(0);J=(await Z.next()).value??null,await Z.return(),U=await j.computeDuration()}}if(!U&&Q)U=await Q.computeDuration();return{videoTrack:j,audioTrack:Q,canvasSink:V,firstFrame:J,duration:U}}promoteQueuedSource(O){let $=this.queuedSources.get(O);if(!$)return null;if(this.queuedSources.delete(O),this.currentSource)this.currentSource.input?.dispose();return this.currentSource=$,$}getOriginalSource(){return this.currentSource?.originalSource??null}disposeCurrent(){if(this.currentSource)this.currentSource.input?.dispose(),this.currentSource=null}disposeQueued(O){let $=this.queuedSources.get(O);if($)$.input?.dispose(),this.queuedSources.delete(O)}disposeAll(){this.disposeCurrent(),this.queuedSources.forEach((O,$)=>{this.disposeQueued($)}),this.queuedSources.clear()}dispose(){this.disposeAll()}static isStreamingSource(O){return O instanceof ReadableStream||typeof O==="string"&&O.startsWith("http")}static isLocalSource(O){return O instanceof File||O instanceof Blob||O instanceof ArrayBuffer||O instanceof Uint8Array||typeof O==="string"&&!O.startsWith("http")}static getSourceType(O){if(O instanceof File)return"file";if(O instanceof Blob)return"blob";if(O instanceof ArrayBuffer||O instanceof Uint8Array)return"buffer";if(O instanceof ReadableStream)return"stream";if(typeof O==="string"||O instanceof URL)return(O instanceof URL?O.href:O).startsWith("http")?"url":"file";return"unknown"}static async fromFetch(O,$){let K=await fetch(O,$);if(!K.ok)throw Error(`Failed to fetch: ${K.status} ${K.statusText}`);return K.blob()}static fromStreamingFetch(O,$){return new ReadableStream({async start(K){let j=await fetch(O,$);if(!j.ok){K.error(Error(`Failed to fetch: ${j.status} ${j.statusText}`));return}let Q=j.body?.getReader();if(!Q){K.error(Error("Response body is not readable"));return}try{while(!0){let{done:V,value:J}=await Q.read();if(V)break;K.enqueue(J)}K.close()}catch(V){K.error(V)}}})}}function l(O,$){if(Object.is(O,$))return!0;if(typeof O!==typeof $)return!1;if(O===null||$===null)return O===$;if(Array.isArray(O)&&Array.isArray($)){if(O.length!==$.length)return!1;for(let K=0;K<O.length;K++)if(!l(O[K],$[K]))return!1;return!0}if(PO(O)&&PO($)){let K=Object.keys(O),j=Object.keys($);if(K.length!==j.length)return!1;for(let Q of K){if(!Object.hasOwn($,Q))return!1;if(!l(O[Q],$[Q]))return!1}return!0}return!1}function PO(O){return typeof O==="object"&&O!==null&&O.constructor===Object}class c{state;previousState;listeners=new Set;updateScheduled=!1;pendingUpdates={};pendingKeys=[];pluginManager=null;listenerCache=[];constructor(){this.state=this.getInitialState(),this.previousState={...this.state}}setPluginManager(O){this.pluginManager=O}getInitialState(){let $=H.getSupportedRenderers()[0]||"canvas2d";return{state:"idle",currentTime:0,duration:0,buffered:[],volume:1,muted:!1,playbackRate:1,playing:!1,paused:!0,ended:!1,seeking:!1,waiting:!1,error:null,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null,canPlay:!1,canPlayThrough:!1,isLive:!1,rendererType:$,playlist:[],currentPlaylistIndex:null,playlistMode:null,rotation:0,displaySize:{width:0,height:0}}}getState(){return this.state}setState(O){if(this.pluginManager){let K=this.pluginManager.executeBeforeStateUpdate(O);if(K===null)return;O=K}let $=Object.keys(O);for(let K=0;K<$.length;K++){let j=$[K];if(this.pendingUpdates[j]===void 0)this.pendingKeys.push(j);this.setPendingValue(j,O[j])}if(!this.updateScheduled)this.updateScheduled=!0,queueMicrotask(()=>this.flushUpdates())}setPendingValue(O,$){this.pendingUpdates[O]=$}flushUpdates(){let O=this.pendingKeys;if(O.length===0){this.updateScheduled=!1;return}let $=!1;for(let K=0;K<O.length;K++){let j=O[K];if(!l(this.pendingUpdates[j],this.state[j])){$=!0;break}}for(let K=0;K<O.length;K++){let j=O[K];this.copyStateKey(j)}if(this.pendingUpdates={},this.pendingKeys.length=0,this.updateScheduled=!1,$){if(this.notifyListeners(),this.pluginManager)this.pluginManager.executeOnStateChange(this.state,this.previousState)}}copyStateKey(O){if(this.previousState[O]=this.state[O],O in this.pendingUpdates)this.state[O]=this.pendingUpdates[O]}subscribe(O){return this.listeners.add(O),O(this.getState()),()=>{this.listeners.delete(O)}}reset(){this.state=this.getInitialState(),this.pendingUpdates={},this.pendingKeys.length=0,this.updateScheduled=!1,this.notifyListeners()}notifyListeners(){let O=this.state,$=this.listenerCache;$.length=0;for(let K of this.listeners)$.push(K);for(let K=0;K<$.length;K++)try{$[K](O)}catch(j){console.error("Error in state listener:",j)}}updatePlaybackState(O){let $=O?"playing":"paused";this.setState({state:$,playing:O,paused:!O,ended:!1})}updateTime(O){this.setState({currentTime:O})}updateDuration(O){this.setState({duration:O})}updateBuffered(O){this.setState({buffered:O})}updateVolume(O,$){this.setState({volume:O,muted:$})}updatePlaybackRate(O){this.setState({playbackRate:O})}updateMediaInfo(O){this.setState({mediaInfo:O})}updateTracks(O,$,K){let j={};if(O)j.videoTracks=O;if($)j.audioTracks=$;if(K)j.subtitleTracks=K;this.setState(j)}updateSelectedTracks(O,$){switch(O){case"video":this.setState({selectedVideoTrack:$});break;case"audio":this.setState({selectedAudioTrack:$});break;case"subtitle":this.setState({selectedSubtitleTrack:$});break}}updateError(O){this.setState({error:O,state:O?"error":this.state.state})}updateSeekingState(O){this.setState({seeking:O})}updateWaitingState(O){this.setState({waiting:O})}updateReadyState(O,$){this.setState({canPlay:O,canPlayThrough:$,state:O?"ready":this.state.state})}updateEndedState(O){this.setState({ended:O,playing:!1,paused:!0,state:O?"ended":this.state.state})}updateLoadingState(){this.setState({state:"loading",playing:!1,paused:!0,ended:!1,error:null})}updateRendererType(O){this.setState({rendererType:O})}updateRotation(O,$){this.setState({rotation:O,displaySize:$})}updatePlaylist(O,$=null){this.setState({playlist:O,currentPlaylistIndex:$})}updateCurrentPlaylistIndex(O){this.setState({currentPlaylistIndex:O})}updatePlaylistMode(O){this.setState({playlistMode:O})}addToPlaylist(O,$){let K=this.state.playlist,j,Q=this.state.currentPlaylistIndex;if($!==void 0&&$>=0&&$<=K.length){if(j=[...K.slice(0,$),O,...K.slice($)],Q!==null&&Q>=$)Q+=1}else j=[...K,O];this.setState({playlist:j,currentPlaylistIndex:Q})}removeFromPlaylist(O){let $=this.state.playlist;if(O<0||O>=$.length)return;let K=$.filter((Q,V)=>V!==O),j=this.state.currentPlaylistIndex;if(j===O)j=K.length>0?0:null;else if(j!==null&&j>O)j-=1;if(K.length===0)this.setState({playlist:K,currentPlaylistIndex:null,state:"idle",currentTime:0,duration:0,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null});else this.setState({playlist:K,currentPlaylistIndex:j})}clearPlaylist(){this.setState({playlist:[],currentPlaylistIndex:null,state:"idle",currentTime:0,duration:0,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null})}}class o{input=null;videoTracks=new Map;audioTracks=new Map;videoTrackInfos=[];audioTrackInfos=[];subtitleTrackInfos=[];subtitleProviders=new Map;subtitleTrackResolvers=new Map;selectedVideoTrack=null;selectedAudioTrack=null;selectedSubtitleTrack=null;onTrackChange;async initialize(O){this.videoTracks.clear(),this.audioTracks.clear(),this.videoTrackInfos=[],this.audioTrackInfos=[],this.selectedVideoTrack=null,this.selectedAudioTrack=null,this.input=O,await this.loadTracks()}async loadTracks(){if(!this.input)return;let O=await this.input.getVideoTracks();this.videoTrackInfos=await Promise.all(O.map(async(K)=>{let j=`video-${K.id}`;this.videoTracks.set(j,K);let Q=0,V=0;try{let U=await K.computePacketStats(100);Q=U.averagePacketRate,V=U.averageBitrate}catch{}return{id:j,codec:K.codec,language:K.languageCode,name:K.name,width:K.codedWidth,height:K.codedHeight,frameRate:Q,bitrate:V,rotation:K.rotation,selected:!1,decodable:await K.canDecode()}}));let $=await this.input.getAudioTracks();if(this.audioTrackInfos=await Promise.all($.map(async(K)=>{let j=`audio-${K.id}`;this.audioTracks.set(j,K);let Q=0;try{Q=(await K.computePacketStats(100)).averageBitrate}catch{}return{id:j,codec:K.codec,language:K.languageCode,name:K.name,channels:K.numberOfChannels,sampleRate:K.sampleRate,bitrate:Q,selected:!1,decodable:await K.canDecode()}})),this.videoTrackInfos.length>0){let K=this.videoTrackInfos.find((j)=>j.decodable);if(K)this.selectVideoTrack(K.id)}if(this.audioTrackInfos.length>0){let K=this.audioTrackInfos.find((j)=>j.decodable);if(K)this.selectAudioTrack(K.id)}}getVideoTracks(){return[...this.videoTrackInfos]}getAudioTracks(){return[...this.audioTrackInfos]}getSubtitleTracks(){return[...this.subtitleTrackInfos]}getSelectedVideoTrack(){if(!this.selectedVideoTrack)return null;return this.videoTracks.get(this.selectedVideoTrack)??null}getSelectedAudioTrack(){if(!this.selectedAudioTrack)return null;return this.audioTracks.get(this.selectedAudioTrack)??null}getSelectedVideoTrackInfo(){if(!this.selectedVideoTrack)return null;return this.videoTrackInfos.find((O)=>O.id===this.selectedVideoTrack)??null}getSelectedAudioTrackInfo(){if(!this.selectedAudioTrack)return null;return this.audioTrackInfos.find((O)=>O.id===this.selectedAudioTrack)??null}getSelectedSubtitleTrackInfo(){if(!this.selectedSubtitleTrack)return null;return this.subtitleTrackInfos.find((O)=>O.id===this.selectedSubtitleTrack)??null}selectVideoTrack(O){if(O===this.selectedVideoTrack)return!0;if(O&&!this.videoTracks.has(O))return!1;let $=this.selectedVideoTrack;if(this.videoTrackInfos.forEach((K)=>{K.selected=K.id===O}),this.selectedVideoTrack=O,this.onTrackChange)this.onTrackChange({type:"video",previousTrackId:$,newTrackId:O});return!0}selectAudioTrack(O){if(O===this.selectedAudioTrack)return!0;if(O&&!this.audioTracks.has(O))return!1;let $=this.selectedAudioTrack;if(this.audioTrackInfos.forEach((K)=>{K.selected=K.id===O}),this.selectedAudioTrack=O,this.onTrackChange)this.onTrackChange({type:"audio",previousTrackId:$,newTrackId:O});return!0}selectSubtitleTrack(O){if(O===this.selectedSubtitleTrack)return!0;if(O&&!this.subtitleTrackResolvers.has(O))return!1;let $=this.selectedSubtitleTrack;if(this.subtitleTrackInfos.forEach((K)=>{K.selected=K.id===O}),this.selectedSubtitleTrack=O,this.onTrackChange)this.onTrackChange({type:"subtitle",previousTrackId:$,newTrackId:O});return!0}registerSubtitleTracks(O,$){this.subtitleProviders.set(O,$),this.rebuildSubtitleTracks()}unregisterSubtitleTracks(O){if(!this.subtitleProviders.delete(O))return;this.rebuildSubtitleTracks()}async getSubtitleTrackResource(O){if(!O)return null;let $=this.subtitleTrackResolvers.get(O);if(!$)return null;return $()}rebuildSubtitleTracks(){let O=this.selectedSubtitleTrack;this.subtitleTrackInfos=[],this.subtitleTrackResolvers.clear();for(let K of this.subtitleProviders.values())for(let j of K){let Q={...j.info,selected:!1};this.subtitleTrackInfos.push(Q),this.subtitleTrackResolvers.set(Q.id,j.resolver)}let $=O;if(!$||!this.subtitleTrackResolvers.has($))$=this.subtitleTrackInfos[0]?.id??null;if(this.selectedSubtitleTrack=$,this.subtitleTrackInfos.forEach((K)=>{K.selected=K.id===this.selectedSubtitleTrack}),O!==this.selectedSubtitleTrack&&this.onTrackChange)this.onTrackChange({type:"subtitle",previousTrackId:O,newTrackId:this.selectedSubtitleTrack})}setTrackChangeListener(O){this.onTrackChange=O}getState(){return{videoTracks:this.getVideoTracks(),audioTracks:this.getAudioTracks(),subtitleTracks:this.getSubtitleTracks(),selectedVideoTrack:this.selectedVideoTrack,selectedAudioTrack:this.selectedAudioTrack,selectedSubtitleTrack:this.selectedSubtitleTrack}}getPrimaryVideoTrack(){if(this.videoTracks.size===0)return null;return this.selectedVideoTrack?this.videoTracks.get(this.selectedVideoTrack)??null:this.videoTracks.values().next().value??null}getPrimaryAudioTrack(){if(this.audioTracks.size===0)return null;return this.selectedAudioTrack?this.audioTracks.get(this.selectedAudioTrack)??null:this.audioTracks.values().next().value??null}hasVideo(){return this.videoTrackInfos.length>0}hasAudio(){return this.audioTrackInfos.length>0}hasSubtitles(){return this.subtitleTrackInfos.length>0}dispose(){this.videoTracks.clear(),this.audioTracks.clear(),this.videoTrackInfos=[],this.audioTrackInfos=[],this.subtitleTrackInfos=[],this.subtitleProviders.clear(),this.subtitleTrackResolvers.clear(),this.selectedVideoTrack=null,this.selectedAudioTrack=null,this.selectedSubtitleTrack=null,this.input=null,this.onTrackChange=void 0}async replaceAudioTrackByInputId(O,$){let K=null;for(let[Q,V]of this.audioTracks.entries())if(V.id===O){K=Q;break}if(!K)return;this.audioTracks.set(K,$);let j=this.audioTrackInfos.findIndex((Q)=>Q.id===K);if(j!==-1){let Q=0;try{Q=(await $.computePacketStats(100)).averageBitrate}catch{}this.audioTrackInfos[j]={...this.audioTrackInfos[j],codec:$.codec,channels:$.numberOfChannels,sampleRate:$.sampleRate,bitrate:Q,decodable:await $.canDecode()}}}async replaceVideoTrackByInputId(O,$){let K=null;for(let[Q,V]of this.videoTracks.entries())if(V.id===O){K=Q;break}if(!K)return;this.videoTracks.set(K,$);let j=this.videoTrackInfos.findIndex((Q)=>Q.id===K);if(j!==-1){let Q=0,V=0;try{let J=await $.computePacketStats(100);Q=J.averagePacketRate,V=J.averageBitrate}catch{}this.videoTrackInfos[j]={...this.videoTrackInfos[j],codec:$.codec,width:$.codedWidth,height:$.codedHeight,rotation:$.rotation,frameRate:Q,bitrate:V,decodable:await $.canDecode()}}}}class d{emitter;store;state;sourceManager;playbackController;trackManager;playlistManager;pluginManager;options;disposed=!1;getCurrentInput=()=>this.sourceManager.getCurrentSource()?.input??null;trackSwitcher;core;constructor(O={}){this.options={volume:1,muted:!1,playbackRate:1,autoplay:!1,preload:"metadata",...O},this.emitter=new I({maxListeners:100}),this.store=new c,this.state=new $O(this.store),this.sourceManager=new u({maxCacheSize:O.maxCacheSize,crossOrigin:O.crossOrigin}),this.playbackController=new g({canvas:O.renderTarget,audioContext:O.audioContext,volume:this.options.volume,muted:this.options.muted,playbackRate:this.options.playbackRate,rendererType:this.options.renderer}),this.trackManager=new o,this.playlistManager=new QO(this.store,this.emitter,async($,K)=>{await this.core.load($.mediaSource,{startTime:$.savedPosition??0,autoplay:K,playlistItemId:$.id})},this.sourceManager),this.trackSwitcher=new jO({sourceManager:this.sourceManager,trackManager:this.trackManager,playbackController:this.playbackController,emit:this.emit.bind(this),store:this.store,getCurrentInput:this.getCurrentInput}),this.pluginManager=new VO(this),this.core=new OO({state:this.state,sourceManager:this.sourceManager,trackManager:this.trackManager,playbackController:this.playbackController,trackSwitcher:this.trackSwitcher,emit:this.emit.bind(this),pluginManager:this.pluginManager}),this.playbackController.setPluginManager(this.pluginManager),this.store.setPluginManager(this.pluginManager),this.setupInternalListeners(),this.state.applyInitial(this.options.volume??1,this.options.muted??!1,this.options.playbackRate??1),this.state.updateRendererType(this.options.renderer||"webgpu")}setupInternalListeners(){this.playbackController.setTimeUpdateCallback((O)=>{this.state.updateTime(O),this.emit("timeupdate",{currentTime:O})}),this.playbackController.setEndedCallback(()=>{this.state.updateEndedState(!0),this.emit("ended",void 0);let O=this.getState();if(O.playlist.length>0&&O.currentPlaylistIndex!==null){let{playlistMode:$,currentPlaylistIndex:K}=O;if($==="repeat-one"){let j=K;queueMicrotask(async()=>{try{await this.seek(0),await this.play()}catch(Q){this.emitter.emit("playlistitemerror",{index:j,error:Q})}})}else if($==="repeat"){let j=K<O.playlist.length-1?K+1:0;queueMicrotask(async()=>{try{await this.playlistManager.next()}catch(Q){this.emitter.emit("playlistitemerror",{index:j,error:Q})}})}else if($==="sequential"&&K<O.playlist.length-1){let j=K+1;queueMicrotask(async()=>{try{await this.playlistManager.next()}catch(Q){this.emitter.emit("playlistitemerror",{index:j,error:Q})}})}}}),this.trackManager.setTrackChangeListener((O)=>{this.state.updateSelectedTracks(O.type,O.newTrackId),this.emit("trackchange",{type:O.type,trackId:O.newTrackId})}),this.playbackController.setWaitingCallback(()=>{this.state.updateWaitingState(!0),this.emit("waiting",void 0)}),this.playbackController.setPlayingCallback(()=>{if(this.getState().waiting)this.state.updateWaitingState(!1),this.emit("playing",void 0)}),this.playbackController.setRendererChangeCallback((O)=>{this.state.updateRendererType(O),this.emit("rendererchange",O)}),this.playbackController.setRendererFallbackCallback((O,$)=>{this.emit("rendererfallback",{from:O,to:$})}),this.playbackController.setRotationChangeCallback((O,$)=>{this.store.updateRotation(O,$),this.emit("rotationchange",{rotation:O,displaySize:$})}),this.state.subscribe((O)=>{this.emit("statechange",O)})}async load(O,$={}){this.checkDisposed();let K=this.getState();if(K.playlist.length===0||$.replacePlaylist){await this.playlistManager.loadPlaylist([{mediaSource:O}],{autoplay:$.autoplay??this.options.autoplay,startTime:$.startTime});return}else if(K.currentPlaylistIndex!==null&&K.playlist.length>0){let j=K.currentPlaylistIndex,V={...K.playlist[j],mediaSource:O,savedPosition:0,duration:null},J=[...K.playlist];J[j]=V,this.store.updatePlaylist(J,j),this.emitter.emit("playlistchange",{playlist:J}),await this.core.load(O,{startTime:$.startTime??0,autoplay:$.autoplay??this.options.autoplay});return}await this.core.load(O,{autoplay:$.autoplay??this.options.autoplay,startTime:$.startTime})}async play(){return this.checkDisposed(),this.core.play()}pause(){this.checkDisposed(),this.core.pause()}async seek(O,$={}){return this.checkDisposed(),this.core.seek(O)}async stop(){return this.checkDisposed(),this.core.stop()}get currentTime(){return this.playbackController.getCurrentTime()}set currentTime(O){this.seek(O)}get duration(){return this.state.getState().duration}get volume(){return this.playbackController.getVolume()}set volume(O){this.checkDisposed();let $=Math.max(0,Math.min(1,O));this.playbackController.setVolume($),this.state.updateVolume($,this.muted),this.emit("volumechange",{volume:$,muted:this.muted})}get muted(){return this.playbackController.isMuted()}set muted(O){this.checkDisposed(),this.playbackController.setMuted(O),this.state.updateVolume(this.volume,O),this.emit("volumechange",{volume:this.volume,muted:O})}get playbackRate(){return this.playbackController.getPlaybackRate()}set playbackRate(O){this.checkDisposed();let $=Math.max(0.25,Math.min(4,O));this.playbackController.setPlaybackRate($),this.state.updatePlaybackRate($),this.emit("ratechange",{playbackRate:$})}get paused(){return!this.playbackController.isPlaying()}get ended(){return this.state.getState().ended}get seeking(){return this.state.getState().seeking}get waiting(){return this.state.getState().waiting}get rotation(){return this.playbackController.getRotation()}set rotation(O){this.checkDisposed(),this.playbackController.setRotation(O)}get displaySize(){return this.playbackController.getDisplaySize()}getVideoTracks(){return this.trackManager.getVideoTracks()}getAudioTracks(){return this.trackManager.getAudioTracks()}getSubtitleTracks(){return this.trackManager.getSubtitleTracks()}async selectVideoTrack(O){this.checkDisposed(),await this.trackSwitcher.selectVideoTrack(this.trackManager,O)}async selectAudioTrack(O){this.checkDisposed(),await this.trackSwitcher.selectAudioTrack(this.trackManager,O)}selectSubtitleTrack(O){if(this.checkDisposed(),!this.trackManager.selectSubtitleTrack(O))throw Error(`Invalid subtitle track ID: ${O}`)}registerSubtitleTracks(O,$){this.trackManager.registerSubtitleTracks(O,$),this.state.updateTracks(void 0,void 0,this.trackManager.getSubtitleTracks());let K=this.state.getState().mediaInfo;if(K)this.state.updateMediaInfo({...K,hasSubtitles:this.trackManager.hasSubtitles()})}unregisterSubtitleTracks(O){this.trackManager.unregisterSubtitleTracks(O),this.state.updateTracks(void 0,void 0,this.trackManager.getSubtitleTracks());let $=this.state.getState().mediaInfo;if($)this.state.updateMediaInfo({...$,hasSubtitles:this.trackManager.hasSubtitles()})}async getSubtitleTrackResource(O){return this.trackManager.getSubtitleTrackResource(O)}async screenshot(O={}){return this.checkDisposed(),this.playbackController.screenshot(O)}async setRenderTarget(O){this.checkDisposed(),await this.playbackController.setCanvas(O)}getRenderTarget(){return this.playbackController.getCanvas()}refreshOverlays(){this.playbackController.refreshOverlays()}async loadPlaylist(O,$){this.checkDisposed(),await this.playlistManager.loadPlaylist(O,$)}addToPlaylist(O,$){this.checkDisposed(),this.playlistManager.addToPlaylist(O,$)}async removeFromPlaylist(O){this.checkDisposed(),await this.playlistManager.removeFromPlaylist(O)}clearPlaylist(){this.checkDisposed(),this.playlistManager.clearPlaylist()}async next(){this.checkDisposed(),await this.playlistManager.next()}async prev(){this.checkDisposed(),await this.playlistManager.prev()}async jumpTo(O){this.checkDisposed(),await this.playlistManager.jumpTo(O)}get playlist(){return this.playlistManager.playlist}get playlistIndex(){return this.playlistManager.currentIndex}get nowPlaying(){return this.playlistManager.currentItem}get playlistMode(){return this.playlistManager.mode}set playlistMode(O){this.checkDisposed(),this.playlistManager.setMode(O)}getRendererType(){return this.playbackController.getRendererType()}async switchRenderer(O){this.checkDisposed(),await this.playbackController.switchRenderer(O)}updateCanvasDimensions(){this.checkDisposed(),this.playbackController.updateCanvasDimensions()}static getSupportedRenderers(){return H.getSupportedRenderers()}getState(){return this.state.getState()}subscribe(O){return{unsubscribe:this.state.subscribe(O)}}on(O,$){return this.emitter.on(O,$)}once(O,$){return this.emitter.once(O,$)}off(O,$){this.emitter.off(O,$)}async use(O){if(this.checkDisposed(),await this.pluginManager.install(O),O.hooks?.audio)this.playbackController.rebuildAudioGraph()}async unuse(O){this.checkDisposed(),await this.pluginManager.uninstall(O),this.playbackController.rebuildAudioGraph()}emit(O,$){let K=this.pluginManager.executeBeforeEvent(O,$);if(K?.cancel)return;let j=K?.data??$;this.emitter.emit(O,j),this.pluginManager.executeAfterEvent(O,j)}checkDisposed(){if(this.disposed)throw Error("Player has been disposed")}dispose(){if(this.disposed)return;this.disposed=!0,this.pluginManager.dispose(),this.playbackController.dispose(),this.trackManager.dispose(),this.playlistManager?.dispose(),this.sourceManager.dispose(),this.state.reset(),this.emitter.removeAllListeners()}destroy(){this.dispose(),this.playbackController.destroy()}}var oO={fromUrl(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromFile(O,$={}){return{mediaSource:O,title:$.title||O.name,poster:$.poster}},fromBlob(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromBuffer(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromUint8Array(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromStream(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}}};var zO;((L)=>{L.MEDIA_NOT_SUPPORTED="MEDIA_NOT_SUPPORTED";L.MEDIA_LOAD_FAILED="MEDIA_LOAD_FAILED";L.DECODE_ERROR="DECODE_ERROR";L.NETWORK_ERROR="NETWORK_ERROR";L.PERMISSION_DENIED="PERMISSION_DENIED";L.PLAYBACK_ERROR="PLAYBACK_ERROR";L.TRACK_NOT_FOUND="TRACK_NOT_FOUND";L.INVALID_STATE="INVALID_STATE";L.UNKNOWN_ERROR="UNKNOWN_ERROR"})(zO||={});class E extends Error{code;details;constructor(O,$,K){super($);this.name="MediaFoxError",this.code=O,this.details=K}static mediaNotSupported(O="Media format not supported",$){return new E("MEDIA_NOT_SUPPORTED",O,$)}static mediaLoadFailed(O="Failed to load media",$){return new E("MEDIA_LOAD_FAILED",O,$)}static decodeError(O="Failed to decode media",$){return new E("DECODE_ERROR",O,$)}static networkError(O="Network error occurred",$){return new E("NETWORK_ERROR",O,$)}static permissionDenied(O="Permission denied",$){return new E("PERMISSION_DENIED",O,$)}static playbackError(O="Playback error occurred",$){return new E("PLAYBACK_ERROR",O,$)}static trackNotFound(O="Track not found",$){return new E("TRACK_NOT_FOUND",O,$)}static invalidState(O="Invalid player state",$){return new E("INVALID_STATE",O,$)}static unknownError(O="Unknown error occurred",$){return new E("UNKNOWN_ERROR",O,$)}}function dO(O,$){if(O instanceof E)return O;if(O instanceof Error)return new E("UNKNOWN_ERROR",`${$}: ${O.message}`,{originalError:O});return new E("UNKNOWN_ERROR",`${$}: ${String(O)}`,{originalError:O})}function nO(O,$=!1){let K=Math.abs(O),j=Math.floor(K/3600),Q=Math.floor(K%3600/60),V=Math.floor(K%60),J=Math.floor(K%1*1000),U="";if(O<0)U="-";if(j>0)U+=`${j}:${Q.toString().padStart(2,"0")}:${V.toString().padStart(2,"0")}`;else U+=`${Q}:${V.toString().padStart(2,"0")}`;if($)U+=`.${J.toString().padStart(3,"0")}`;return U}function sO(O){let $=O.trim().split(":").map(Number);if($.some(Number.isNaN))throw Error("Invalid time string");let K=0;if($.length===3)K=$[0]*3600+$[1]*60+$[2];else if($.length===2)K=$[0]*60+$[1];else if($.length===1)K=$[0];else throw Error("Invalid time format");return K}function iO(O,$){return Math.floor(O*$)}function aO(O,$){return O/$}function rO(O,$,K){return Math.max($,Math.min(K,O))}function tO(O,$){return O.start<$.end&&$.start<O.end}function eO(O){if(O.length===0)return[];let $=[...O].sort((j,Q)=>j.start-Q.start),K=[$[0]];for(let j=1;j<$.length;j++){let Q=K[K.length-1],V=$[j];if(V.start<=Q.end)Q.end=Math.max(Q.end,V.end);else K.push(V)}return K}function O$(O){return O.reduce(($,K)=>$+(K.end-K.start),0)}function $$(O,$){for(let K of O)if($>=K.start&&$<K.end)return K;return null}var NK="0.1.0",IK=d;export{dO as wrapError,O$ as totalBufferedDuration,iO as timeToFrame,tO as timeRangesOverlap,sO as parseTime,eO as mergeTimeRanges,aO as frameToTime,nO as formatTime,$$ as findBufferedRange,IK as default,rO as clamp,v as VideoRenderer,NK as VERSION,o as TrackManager,c as Store,x as SourcePool,u as SourceManager,oO as Source,H as RendererFactory,g as PlaybackController,E as MediaFoxError,d as MediaFox,I as EventEmitter,zO as ErrorCode,a as Compositor,p as AudioManager};
|
|
40
|
+
`;constructor(O){this.canvas=O.canvas,this.powerPreference=O.powerPreference||"high-performance",this.rotation=O.rotation??0,this.initialize().catch(($)=>{console.error("WebGPU initialization failed:",$)})}async initialize(){try{let O=navigator;if(!O.gpu)return console.log("WebGPU not available in navigator"),!1;let $=await O.gpu.requestAdapter({powerPreference:this.powerPreference});if(!$)return console.log("WebGPU adapter not available"),!1;if(this.device=await $.requestDevice(),!this.device)return console.log("WebGPU device not available"),!1;if("getContext"in this.canvas)this.context=this.canvas.getContext("webgpu");if(!this.context)return console.log("WebGPU context not available on canvas"),!1;let K=O.gpu.getPreferredCanvasFormat();return this.context.configure({device:this.device,format:K,usage:GPUTextureUsage.RENDER_ATTACHMENT,alphaMode:"opaque"}),await this.createRenderPipeline(),this.createVertexBuffer(),this.isInitialized=!0,console.log("WebGPU renderer initialized successfully"),!0}catch(O){return console.error("WebGPU initialization error:",O),!1}}async createRenderPipeline(){if(!this.device)return;let O=navigator;if(!O.gpu)return;let $=this.device.createShaderModule({code:this.vertexShaderSource}),K=this.device.createShaderModule({code:this.fragmentShaderSource});this.pipeline=this.device.createRenderPipeline({layout:"auto",vertex:{module:$,entryPoint:"vs_main",buffers:[{arrayStride:16,attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"}]}]},fragment:{module:K,entryPoint:"fs_main",targets:[{format:O.gpu.getPreferredCanvasFormat()}]},primitive:{topology:"triangle-strip"}})}createVertexBuffer(){if(!this.device)return;this.vertexBuffer=this.device.createBuffer({size:64,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST})}createTexture(O,$){if(!this.device)return;if(this.texture)this.texture.destroy();if(this.texture=this.device.createTexture({size:{width:O,height:$},format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),!this.sampler)this.sampler=this.device.createSampler({magFilter:"linear",minFilter:"linear",addressModeU:"clamp-to-edge",addressModeV:"clamp-to-edge"});this.createBindGroup()}createBindGroup(){if(!this.device||!this.texture||!this.sampler||!this.pipeline)return;this.bindGroup=this.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:this.sampler},{binding:1,resource:this.texture.createView()}]})}isReady(){return this.isInitialized&&this.device!==null&&this.context!==null&&this.pipeline!==null}render(O){if(!this.isReady()||!this.device||!this.context||!this.pipeline)return!1;try{let{width:$,height:K}=O;if($===0||K===0)return console.warn(`WebGPU: Source canvas has zero dimensions (${$}x${K})`),!1;let j=this.canvas.width,Q=this.canvas.height;if(j===0||Q===0)return console.warn(`WebGPU: Output canvas has zero dimensions (${j}x${Q})`),!1;if($!==this.textureWidth||K!==this.textureHeight)this.createTexture($,K),this.textureWidth=$,this.textureHeight=K;if(!this.texture)return!1;try{this.device.queue.copyExternalImageToTexture({source:O},{texture:this.texture},{width:$,height:K})}catch{let n=O.getContext("2d");if(!n)return!1;if(!("getImageData"in n))return!1;let XO=n.getImageData(0,0,$,K),YO=new Uint8Array(XO.data.buffer);this.device.queue.writeTexture({texture:this.texture,origin:{x:0,y:0,z:0}},YO,{bytesPerRow:$*4,rowsPerImage:K},{width:$,height:K,depthOrArrayLayers:1})}let V=this.device.createCommandEncoder(),J=this.context.getCurrentTexture().createView(),U=V.beginRenderPass({colorAttachments:[{view:J,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]});if(U.setPipeline(this.pipeline),this.bindGroup)U.setBindGroup(0,this.bindGroup);let G=this.rotation===90||this.rotation===270,Z=G?this.textureHeight:this.textureWidth,L=G?this.textureWidth:this.textureHeight,z=Math.min(j/Z,Q/L),Y=Math.round(Z*z),A=Math.round(L*z),N=Math.round((j-Y)/2),C=Math.round((Q-A)/2),_=N/j*2-1,E=(N+Y)/j*2-1,W=1-C/Q*2,R=1-(C+A)/Q*2,F=(_+E)/2,b=(W+R)/2,w=(E-_)/2,S=(W-R)/2,q=this.rotation*Math.PI/180,X=Math.cos(q),P=Math.sin(q),B=G?S:w,T=G?w:S,D=this.quadArray;if(D[0]=-B*X- -T*P+F,D[1]=-B*P+-T*X+b,D[2]=0,D[3]=1,D[4]=B*X- -T*P+F,D[5]=B*P+-T*X+b,D[6]=1,D[7]=1,D[8]=-B*X-T*P+F,D[9]=-B*P+T*X+b,D[10]=0,D[11]=0,D[12]=B*X-T*P+F,D[13]=B*P+T*X+b,D[14]=1,D[15]=0,this.vertexBuffer)this.device.queue.writeBuffer(this.vertexBuffer,0,D),U.setVertexBuffer(0,this.vertexBuffer);return U.draw(4,1,0,0),U.end(),this.device.queue.submit([V.finish()]),!0}catch{return!1}}clear(){if(!this.isReady()||!this.device||!this.context)return;try{let O=this.device.createCommandEncoder(),$=this.context.getCurrentTexture().createView();O.beginRenderPass({colorAttachments:[{view:$,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]}).end(),this.device.queue.submit([O.finish()])}catch{}}setRotation(O){this.rotation=O}getRotation(){return this.rotation}dispose(){try{if(this.texture)this.texture.destroy(),this.texture=null;if(this.vertexBuffer)this.vertexBuffer.destroy(),this.vertexBuffer=null;this.device=null,this.context=null,this.pipeline=null,this.sampler=null,this.bindGroup=null,this.isInitialized=!1}catch{}}}class H{canvas;powerPreference;constructor(O){this.canvas=O.canvas,this.powerPreference=O.powerPreference||"high-performance"}async createRenderer(O){try{switch(O){case"webgpu":return await this.createWebGPURenderer();case"webgl":return this.createWebGLRenderer();case"canvas2d":return this.createCanvas2DRenderer();default:return null}}catch{return null}}async createRendererWithFallback(O){let $=[O];if(O!=="webgl")$.push("webgl");if(O!=="canvas2d")$.push("canvas2d");for(let j of $){let Q=await this.createRenderer(j);if(Q?.isReady())return{renderer:Q,actualType:j};if(Q)Q.dispose()}return{renderer:this.createCanvas2DRenderer(),actualType:"canvas2d"}}async createWebGPURenderer(){if(!navigator.gpu)return null;if(!("getContext"in this.canvas))return console.log("WebGPU requires HTMLCanvasElement, not OffscreenCanvas"),null;let $=new t({canvas:this.canvas,powerPreference:this.powerPreference}),K=1000,j=performance.now();if(await(async()=>{while(!$.isReady()){if(performance.now()-j>K)return console.log("WebGPU renderer initialization timed out"),!1;if("requestIdleCallback"in window)await new Promise((J)=>requestIdleCallback(()=>J(void 0)));else await new Promise((J)=>requestAnimationFrame(()=>J(void 0)))}return!0})())return console.log("WebGPU renderer initialized successfully"),$;return $.isReady()?$:null}createWebGLRenderer(){try{let O=new r({canvas:this.canvas,powerPreference:this.powerPreference,preserveDrawingBuffer:!1,antialias:!1,alpha:!1});return O.isReady()?O:null}catch{return null}}createCanvas2DRenderer(){return new y({canvas:this.canvas})}static getSupportedRenderers(){let O=[];if(navigator.gpu)O.push("webgpu");try{let K=document.createElement("canvas");if(K.getContext("webgl")||K.getContext("experimental-webgl"))O.push("webgl")}catch{}return O.push("canvas2d"),O}static getRendererDisplayName(O){switch(O){case"canvas2d":return"Canvas 2D";case"webgl":return"WebGL";case"webgpu":return"WebGPU";default:return"Unknown"}}static isRendererSupported(O){if(O==="canvas2d")return!0;return H.getSupportedRenderers().includes(O)}}var e=new WeakMap;function LO(O,$){e.set(O,$)}function FO(O){let $=e.get(O);if($)e.delete(O);return $}class v{canvas=null;seekDebounceTimer=null;pendingSeekTime=null;canvasSink=null;sampleSink=null;options;frameIterator=null;currentFrame=null;nextFrame=null;disposed=!1;renderingId=0;currentSeekId=0;renderer=null;rendererType="canvas2d";onRendererChange;onRendererFallback;onRotationChange;initPromise=null;resizeObserver=null;lastObservedWidth=0;lastObservedHeight=0;videoAspectRatio=null;debug=!1;pluginManager=null;overlayCanvas=null;overlayCtx=null;lastOverlayTime=0;rotation=0;sourceWidth=0;sourceHeight=0;updateFrameResult={frameUpdated:!1,isStarving:!1};constructor(O={}){if(this.options={poolSize:O.poolSize??2,rendererType:O.rendererType??"webgpu",...O},this.rendererType=this.options.rendererType??"webgpu",this.debug=O.debug??!1,this.initPromise=null,O.canvas){if(this.canvas=O.canvas,this.options.width!==void 0)O.canvas.width=this.options.width;if(this.options.height!==void 0)O.canvas.height=this.options.height;this.initPromise=this.initializeRenderer(O.canvas,this.rendererType).catch(($)=>{if(this.debug)console.error("Failed to initialize renderer:",$)}),this.setupResizeObserver(O.canvas)}}setupResizeObserver(O){if(!("getBoundingClientRect"in O)||typeof ResizeObserver>"u")return;if(this.options.width!==void 0||this.options.height!==void 0)return;this.cleanupResizeObserver();let $=O;this.resizeObserver=new ResizeObserver((K)=>{if(this.disposed||!this.resizeObserver)return;for(let j of K){let{width:Q,height:V}=this.getCanvasDimensionsFromEntry(j,$);if(Q!==this.lastObservedWidth||V!==this.lastObservedHeight){if(this.lastObservedWidth=Q,this.lastObservedHeight=V,$.width!==Q||$.height!==V){if($.width=Q,$.height=V,this.updateCanvasAspectRatio(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}}}});try{this.resizeObserver.observe($,{box:"device-pixel-content-box"})}catch{try{this.resizeObserver.observe($,{box:"content-box"})}catch{this.resizeObserver.observe($)}}requestAnimationFrame(()=>{if(this.disposed||!this.resizeObserver)return;let{width:K,height:j}=this.getCanvasDimensionsFromCanvas($);if(this.lastObservedWidth=K,this.lastObservedHeight=j,$.width!==K||$.height!==j){if($.width=K,$.height=j,this.updateCanvasAspectRatio(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}})}getCanvasDimensionsFromEntry(O,$){let K=0,j=0,Q=window.devicePixelRatio||1;if(O.devicePixelContentBoxSize?.length)K=O.devicePixelContentBoxSize[0].inlineSize,j=O.devicePixelContentBoxSize[0].blockSize;else if(O.contentBoxSize?.length)K=Math.round(O.contentBoxSize[0].inlineSize*Q),j=Math.round(O.contentBoxSize[0].blockSize*Q);else if(O.contentRect)K=Math.round(O.contentRect.width*Q),j=Math.round(O.contentRect.height*Q);if(K===0||j===0)return this.getCanvasDimensionsFromCanvas($);return{width:Math.max(1,K),height:Math.max(1,j)}}getCanvasDimensionsFromCanvas(O){let $=0,K=0,j=window.devicePixelRatio||1,Q=O.getBoundingClientRect();if($=Math.round(Q.width*j),K=Math.round(Q.height*j),$===0||K===0)$=Math.round(O.clientWidth*j)||$,K=Math.round(O.clientHeight*j)||K;if($===0||K===0)console.warn("Canvas has zero dimensions after all fallbacks, using 1x1");return{width:Math.max(1,$),height:Math.max(1,K)}}cleanupResizeObserver(){if(this.resizeObserver)this.resizeObserver.disconnect(),this.resizeObserver=null,this.lastObservedWidth=0,this.lastObservedHeight=0}retryUntilCanvasReady(O,$,K=60){let j=0,Q=()=>{if(j++,O.canvas.width>0&&O.canvas.height>0)$();else if(j<K)requestAnimationFrame(Q);else{if(this.debug)console.warn("Canvas dimensions timeout, forcing action");$()}};requestAnimationFrame(Q)}updateCanvasAspectRatio(){if(!this.canvas||!this.videoAspectRatio||!("style"in this.canvas))return;this.canvas.style.aspectRatio=this.videoAspectRatio}updateCanvasBackingBuffer(O){let{width:$,height:K}=this.getCanvasDimensionsFromCanvas(O);if(O.width!==$||O.height!==K)return O.width=$,O.height=K,!0;return!1}async initializeRenderer(O,$){if(this.debug)console.log(`Initializing renderer: ${$}`);let j=await new H({canvas:O}).createRendererWithFallback($);if(this.debug)console.log(`Renderer factory result: ${j.actualType}`);if(!j.renderer.isReady()){if(this.debug)console.warn(`VideoRenderer: Renderer (${j.actualType}) not ready`);throw j.renderer.dispose(),Error(`Failed to initialize renderer: ${j.actualType}`)}if(this.renderer=j.renderer,this.rendererType=j.actualType,this.debug)console.log(`Initialized renderer: ${this.rendererType}`);if(j.actualType!==$){if(this.onRendererFallback)this.onRendererFallback($,j.actualType)}if(this.onRendererChange){if(this.debug)console.log(`Emitting renderer change: ${this.rendererType}`);this.onRendererChange(this.rendererType)}if(this.currentFrame&&this.renderer&&this.renderer.isReady()){if(this.debug)console.log(`Rendering initial frame with ${this.rendererType}`);if(this.currentFrame.canvas.width===0||this.currentFrame.canvas.height===0){if(this.debug)console.log("Initial frame has zero dimensions, scheduling render when ready...");this.retryUntilCanvasReady(this.currentFrame,()=>{if(this.currentFrame&&this.debug)console.log(`Canvas ready (${this.currentFrame.canvas.width}x${this.currentFrame.canvas.height}), rendering initial frame`);if(this.currentFrame)this.renderFrame(this.currentFrame)})}else this.renderFrame(this.currentFrame)}}async setCanvas(O){if(this.cleanupOverlayCanvas(),this.canvas=O,this.renderer)this.renderer.dispose(),this.renderer=null;if(this.options.width!==void 0)O.width=this.options.width;if(this.options.height!==void 0)O.height=this.options.height;this.setupResizeObserver(O);try{await this.initializeRenderer(O,this.rendererType)}catch($){if(this.debug)console.error("Failed to initialize renderer:",$);if(!this.renderer){if(this.renderer=new y({canvas:O}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}}async setVideoTrack(O){if(await this.disposeVideoResources(),O.codec===null)throw Error("Unsupported video codec");if(!await O.canDecode())throw Error(`Cannot decode video track with codec: ${O.codec}`);let K=FO(O);if(this.sourceWidth=O.displayWidth,this.sourceHeight=O.displayHeight,!this.videoAspectRatio&&O.displayWidth&&O.displayHeight){let j=(U,G)=>G===0?U:j(G,U%G),Q=j(O.displayWidth,O.displayHeight),V=O.displayWidth/Q,J=O.displayHeight/Q;this.videoAspectRatio=`${V}/${J}`,this.updateCanvasAspectRatio()}if(this.notifyRotationChange(),this.initPromise)try{await this.initPromise}catch(j){if(this.debug)console.error("Renderer initialization failed:",j)}if(!this.renderer){if(this.debug)console.warn("Renderer not ready, creating Canvas2D fallback");if(this.canvas){if(this.renderer=new y({canvas:this.canvas}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}if(this.canvas){if(this.options.width!==void 0||this.options.height!==void 0){let j=this.options.width??O.displayWidth,Q=this.options.height??O.displayHeight;if(this.canvas.width!==j||this.canvas.height!==Q)this.canvas.width=j,this.canvas.height=Q,this.updateCanvasAspectRatio()}else if(!this.resizeObserver){if(this.canvas.width===0||this.canvas.height===0)this.canvas.width=O.displayWidth,this.canvas.height=O.displayHeight,this.updateCanvasAspectRatio()}}if(K?.canvasSink)this.canvasSink=K.canvasSink;else this.canvasSink=new SO(O,{rotation:this.options.rotation,poolSize:this.options.poolSize});if(this.sampleSink=new WO(O),this.disposed=!1,K?.firstFrame){if(this.currentFrame=K.firstFrame,this.currentFrame.canvas.width>0&&this.currentFrame.canvas.height>0)this.renderFrame(this.currentFrame);else this.retryUntilCanvasReady(this.currentFrame,()=>{if(this.currentFrame)this.renderFrame(this.currentFrame)},30);this.frameIterator=this.canvasSink.canvases(0),this.frameIterator.next().then(()=>{this.fetchNextFrame()})}else try{await this.seek(0)}catch(j){if(this.debug)console.error("Initial seek failed:",j)}requestAnimationFrame(()=>{if(this.resizeObserver&&this.canvas&&"getBoundingClientRect"in this.canvas)this.updateCanvasBackingBuffer(this.canvas);if(this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)})}async seek(O){if(this.cancelPendingDebouncedSeek(),!this.canvasSink)return;this.renderingId++;let $=this.renderingId;this.currentSeekId++;let K=this.currentSeekId;if(this.frameIterator){try{await this.frameIterator.return()}catch{}this.frameIterator=null}this.currentFrame=null,this.nextFrame=null;try{let j=null,Q=null,V=0.001,J=Math.max(0,O-2),U=this.canvasSink.canvases(J);try{let G=null;for await(let Z of U){if(K!==this.currentSeekId)return;if(Z.timestamp<=O)G=Z;else{Q=Z;break}if(Z.timestamp>O+5)break}j=G}catch{}finally{try{await U.return()}catch{}}if(K!==this.currentSeekId)return;if(!j){let G=this.canvasSink.canvases(O);try{let Z=await G.next();if(K!==this.currentSeekId)return;if(Z.value)j=Z.value}catch{}finally{try{await G.return()}catch{}}}if($!==this.renderingId||this.disposed)return;if(K!==this.currentSeekId)return;if(j){if(this.currentFrame=j,this.nextFrame=Q,j.canvas.width>0&&j.canvas.height>0)this.renderFrame(j);else this.retryUntilCanvasReady(j,()=>this.renderFrame(j),30);let G=this.canvasSink.canvases(j.timestamp);this.frameIterator=G;let Z=null;try{let L=await G.next();if(K!==this.currentSeekId){try{await G.return()}catch{}if(this.frameIterator===G)this.frameIterator=null;return}Z=L.value??null}catch{}if($!==this.renderingId||this.disposed){try{await G.return()}catch{}if(this.frameIterator===G)this.frameIterator=null;return}if(Z&&Z.timestamp>j.timestamp+V&&(!this.nextFrame||Z.timestamp<this.nextFrame.timestamp))this.nextFrame=Z;if(!this.nextFrame)this.fetchNextFrame()}}catch{}}updateFrame(O,$=!1){let K=this.updateFrameResult;if(this.disposed)return K.frameUpdated=!1,K.isStarving=!1,K;if(this.currentFrame&&this.currentFrame.timestamp>O+0.1)return K.frameUpdated=!1,K.isStarving=!1,K;if(!this.nextFrame){if(this.frameIterator)this.fetchNextFrame();return K.frameUpdated=!1,K.isStarving=!0,K}if(this.nextFrame.timestamp<O-1){if(this.pendingSeekTime=O,this.seekDebounceTimer===null&&!$)this.seekDebounceTimer=window.setTimeout(()=>{let Q=this.pendingSeekTime;if(this.seekDebounceTimer=null,this.pendingSeekTime=null,Q!==null&&!this.disposed)this.seek(Q)},100);return K.frameUpdated=!1,K.isStarving=!1,K}let j=0.016;if(this.nextFrame.timestamp<=O+j){if(this.currentFrame=this.nextFrame,this.nextFrame=null,this.renderFrame(this.currentFrame),this.frameIterator)this.fetchNextFrame();return K.frameUpdated=!0,K.isStarving=!1,K}return K.frameUpdated=!1,K.isStarving=!1,K}async fetchNextFrame(O){let $=this.frameIterator;if(!$||this.disposed)return;let K=this.renderingId;try{let j=null,Q=await $.next();if(j=Q.value??null,!j||K!==this.renderingId||this.disposed)return;if(O!==void 0)while(j&&j.timestamp<O-0.1){Q=await $.next();let J=Q.value??null;if(!J||K!==this.renderingId||this.disposed)break;j=J}this.nextFrame=j}catch{}}renderFrame(O){if(this.currentFrame=O,!this.renderer||!this.canvas){if(this.initPromise)this.initPromise.then(()=>{if(this.currentFrame===O&&this.renderer&&this.renderer.isReady()){if(this.debug)console.log("Rendering frame after renderer initialization");this.renderFrameWithPlugins(O)}});return}if(!this.renderer.isReady()){if(this.debug)console.warn(`VideoRenderer: Renderer (${this.rendererType}) not ready, skipping frame`);return}this.renderFrameWithPlugins(O)}renderFrameWithPlugins(O){if(!this.renderer||!this.canvas)return;let $=O.timestamp;if(this.pluginManager){if(this.pluginManager.executeBeforeRender(O,$)?.skip)return}let K=O;if(this.pluginManager)K=this.pluginManager.executeTransformFrame(O);if(!this.renderer.render(K.canvas)){if(this.debug)console.warn(`Failed to render frame with ${this.rendererType} (canvas: ${K.canvas.width}x${K.canvas.height})`);if(K.canvas.width===0||K.canvas.height===0)this.retryUntilCanvasReady(K,()=>{if(this.currentFrame===O&&this.renderer&&this.renderer.isReady()){if(!this.renderer.render(K.canvas)&&this.debug)console.warn("Retry render also failed")}},1);return}if(this.executeOverlays($),this.pluginManager)this.pluginManager.executeAfterRender(this.canvas)}executeOverlays(O){if(!this.pluginManager||!this.canvas)return;this.lastOverlayTime=O;let $={width:this.canvas.width,height:this.canvas.height};if(this.rendererType==="canvas2d"){let K=this.canvas.getContext("2d");if(!K)return;this.pluginManager.executeOverlays(K,O,$)}else{if(this.ensureOverlayCanvas(),!this.overlayCanvas||!this.overlayCtx)return;this.overlayCtx.clearRect(0,0,this.overlayCanvas.width,this.overlayCanvas.height),this.pluginManager.executeOverlays(this.overlayCtx,O,$)}}refreshOverlays(){if(!this.canvas)return;if(this.rendererType==="canvas2d"){if(this.currentFrame&&this.renderer?.isReady())this.renderer.render(this.currentFrame.canvas),this.executeOverlays(this.lastOverlayTime)}else if(this.overlayCanvas&&this.overlayCtx){if(this.overlayCtx.clearRect(0,0,this.overlayCanvas.width,this.overlayCanvas.height),this.pluginManager){let O={width:this.canvas.width,height:this.canvas.height};this.pluginManager.executeOverlays(this.overlayCtx,this.lastOverlayTime,O)}}}ensureOverlayCanvas(){if(!this.canvas||!(this.canvas instanceof HTMLCanvasElement))return;if(!this.overlayCanvas){this.overlayCanvas=document.createElement("canvas"),this.overlayCanvas.style.position="absolute",this.overlayCanvas.style.top="0",this.overlayCanvas.style.left="0",this.overlayCanvas.style.width="100%",this.overlayCanvas.style.height="100%",this.overlayCanvas.style.pointerEvents="none",this.overlayCanvas.style.zIndex="1",this.overlayCtx=this.overlayCanvas.getContext("2d");let O=this.canvas.parentElement;if(O){if(getComputedStyle(O).position==="static")O.style.position="relative";O.insertBefore(this.overlayCanvas,this.canvas.nextSibling)}}if(this.overlayCanvas.width!==this.canvas.width||this.overlayCanvas.height!==this.canvas.height)this.overlayCanvas.width=this.canvas.width,this.overlayCanvas.height=this.canvas.height}cleanupOverlayCanvas(){if(this.overlayCanvas)this.overlayCanvas.remove(),this.overlayCanvas=null,this.overlayCtx=null}async getFrameAt(O){if(!this.canvasSink)return null;return this.canvasSink.getCanvas(O)}async getSampleAt(O){if(!this.sampleSink)return null;return this.sampleSink.getSample(O)}async extractFrames(O,$,K=1){if(!this.canvasSink)return[];let j=[],Q=[];for(let V=O;V<=$;V+=K)Q.push(V);for await(let V of this.canvasSink.canvasesAtTimestamps(Q))if(V)j.push(V);return j}async screenshot(O,$={}){if(!this.canvas)return null;if(O!==void 0&&this.canvasSink){let K=await this.canvasSink.getCanvas(O);if(K)this.renderFrame(K)}if("toBlob"in this.canvas)return new Promise((K)=>{this.canvas.toBlob((j)=>K(j),`image/${$.format??"png"}`,$.quality)});else return this.canvas.convertToBlob({type:`image/${$.format??"png"}`,quality:$.quality})}getCurrentFrame(){return this.currentFrame}getNextFrame(){return this.nextFrame}getRendererType(){return this.rendererType}getCanvas(){return this.canvas}updateCanvasDimensions(){if(!this.canvas||!("getBoundingClientRect"in this.canvas))return;let O=this.canvas;if(this.updateCanvasBackingBuffer(O)&&this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}async switchRenderer(O){if(!this.canvas)throw Error("Cannot switch renderer: No canvas set");let $=this.rendererType;if(O===$)return;if(this.debug)console.warn(`Switching renderer from ${$} to ${O}. This will recreate the canvas element.`);if(this.canvas instanceof HTMLCanvasElement){let K=this.canvas,j=K.parentElement;if(!j)throw Error("Cannot switch renderer: Canvas has no parent element");let Q=document.createElement("canvas");if(Q.width=K.width,Q.height=K.height,Q.className=K.className,Q.id=K.id,Q.style.cssText=K.style.cssText,Array.from(K.attributes).forEach((V)=>{if(V.name!=="id"&&V.name!=="class"&&V.name!=="style")Q.setAttribute(V.name,V.value)}),this.renderer)this.renderer.dispose(),this.renderer=null;j.replaceChild(Q,K),this.canvas=Q;try{await this.initializeRenderer(Q,O)}catch(V){if(this.debug)console.error(`Failed to switch to ${O}:`,V);if(!this.renderer){if(this.renderer=new y({canvas:Q}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}}else{if(this.debug)console.warn("Runtime switching for OffscreenCanvas may not work if context is already set");if(this.renderer)this.renderer.dispose(),this.renderer=null;try{await this.initializeRenderer(this.canvas,O)}catch(K){if(this.debug)console.error(`Failed to switch to ${O}:`,K);if(!this.renderer){if(this.renderer=new y({canvas:this.canvas}),this.rendererType="canvas2d",this.onRendererChange)this.onRendererChange("canvas2d")}}}if(this.currentFrame&&this.renderer&&this.renderer.isReady()){if(this.debug)console.log(`Re-rendering after switch to ${this.rendererType}`);queueMicrotask(()=>{if(this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)})}}setRendererChangeCallback(O){if(this.onRendererChange=O,this.renderer&&this.rendererType){if(this.debug)console.log(`Renderer already initialized as ${this.rendererType}, emitting change event`);O(this.rendererType)}}setRendererFallbackCallback(O){this.onRendererFallback=O}setRotationChangeCallback(O){this.onRotationChange=O}setRotation(O){if(this.rotation===O)return;if(this.rotation=O,this.renderer)this.renderer.setRotation(O);if(this.notifyRotationChange(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}getRotation(){return this.rotation}getDisplaySize(){let O=this.rotation===90||this.rotation===270;return{width:O?this.sourceHeight:this.sourceWidth,height:O?this.sourceWidth:this.sourceHeight}}notifyRotationChange(){if(this.onRotationChange&&this.sourceWidth>0&&this.sourceHeight>0)this.onRotationChange(this.rotation,this.getDisplaySize())}setPluginManager(O){this.pluginManager=O}static getSupportedRenderers(){return H.getSupportedRenderers()}async clearIterators(){if(this.renderingId++,this.cancelPendingDebouncedSeek(),this.frameIterator){try{await this.frameIterator.return()}catch{}this.frameIterator=null}this.currentFrame=null,this.nextFrame=null}async disposeVideoResources(){if(this.disposed=!0,this.renderingId++,this.cancelPendingDebouncedSeek(),this.frameIterator){try{await this.frameIterator.return()}catch{}this.frameIterator=null}this.currentFrame=null,this.nextFrame=null,this.canvasSink=null,this.sampleSink=null,this.videoAspectRatio=null}dispose(){if(this.disposed=!0,this.renderingId++,this.cancelPendingDebouncedSeek(),this.frameIterator)this.frameIterator.return(),this.frameIterator=null;if(this.renderer)this.renderer.dispose(),this.renderer=null;this.cleanupResizeObserver(),this.cleanupOverlayCanvas(),this.currentFrame=null,this.nextFrame=null,this.canvasSink=null,this.sampleSink=null,this.onRendererChange=void 0,this.onRendererFallback=void 0}cancelPendingDebouncedSeek(){if(this.seekDebounceTimer!==null)clearTimeout(this.seekDebounceTimer);this.seekDebounceTimer=null,this.pendingSeekTime=null}}var f=2,h="[MediaFox]";function HO(O){f=O}function bO(O,...$){if(f<=0)console.debug(`${h} ${O}`,...$)}function wO(O,...$){if(f<=1)console.info(`${h} ${O}`,...$)}function TO(O,...$){if(f<=2)console.warn(`${h} ${O}`,...$)}function yO(O,...$){if(f<=3)console.error(`${h} ${O}`,...$)}var k={setLevel:HO,debug:bO,info:wO,warn:TO,error:yO};class OO{deps;constructor(O){this.deps=O}async load(O,$={}){try{let K=await this.deps.pluginManager.executeBeforeLoad(O);if(K?.cancel)return;if(K?.data!==void 0)O=K.data;await this.deps.playbackController.reset(),this.deps.sourceManager.disposeCurrent();let j=this.deps.state.getState();if(!$.replacePlaylist)this.deps.state.setState({...j,state:"loading",currentTime:$.startTime??0,playing:!1,paused:!0,ended:!1,seeking:!1,error:null,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null,buffered:[],canPlay:!1,canPlayThrough:!1});else this.deps.state.reset(),this.deps.state.updateLoadingState();this.deps.emit("loadstart",void 0);let Q=$.playlistItemId?this.deps.sourceManager.promoteQueuedSource($.playlistItemId):null;if(!Q)Q=await this.deps.sourceManager.createSource(O);let V=Q.input;if(!V)throw Error("Failed to create input from source");await this.deps.trackManager.initialize(V);let[J,U,G,Z]=await Promise.all([V.computeDuration(),V.getFormat(),V.getMimeType(),V.getMetadataTags()]),L={duration:J,format:U.name,mimeType:G,metadata:Z,hasVideo:this.deps.trackManager.hasVideo(),hasAudio:this.deps.trackManager.hasAudio(),hasSubtitles:this.deps.trackManager.hasSubtitles()};this.deps.state.updateDuration(J),this.deps.state.updateMediaInfo(L),this.deps.state.updateTracks(this.deps.trackManager.getVideoTracks(),this.deps.trackManager.getAudioTracks(),this.deps.trackManager.getSubtitleTracks()),this.deps.playbackController.setDuration(J);let z=Q.prefetchedData,Y=z?.videoTrack??this.deps.trackManager.getPrimaryVideoTrack(),A=z?.audioTrack??this.deps.trackManager.getPrimaryAudioTrack();if(Y&&z?.canvasSink&&z?.firstFrame)LO(Y,{canvasSink:z.canvasSink,firstFrame:z.firstFrame});let N="",C=!1,_=!1;if(Y||A){let E=await this.deps.trackSwitcher.setupInitialTracks(Y,A);N+=E.warningMessage,C=E.videoSupported,_=E.audioSupported}if(!C&&!_){if(!N)N="No audio or video track found.";throw Error(N)}if(N&&(C||_))this.deps.emit("warning",{type:"codec-warning",message:N.trim(),error:void 0});if(this.deps.state.updateReadyState(!0,!0),this.deps.emit("loadedmetadata",L),this.deps.emit("loadeddata",void 0),this.deps.emit("canplay",void 0),this.deps.emit("canplaythrough",void 0),this.updateCurrentPlaylistItemDuration(J),await this.deps.pluginManager.executeAfterLoad(L),$.autoplay)await this.play();if($.startTime!==void 0)await this.seek($.startTime)}catch(K){throw this.handleError(K),K}}async play(){try{if(this.deps.state.getState().state==="idle")throw Error("No media loaded");if((await this.deps.pluginManager.executeBeforePlay())?.cancel)return;await this.deps.playbackController.play(),this.deps.state.updatePlaybackState(!0),this.deps.emit("play",void 0),this.deps.emit("playing",void 0),this.deps.pluginManager.executeAfterPlay()}catch(O){throw this.handleError(O),O}}async pause(){if((await this.deps.pluginManager.executeBeforePause())?.cancel)return;this.deps.playbackController.pause(),this.deps.state.updatePlaybackState(!1),this.deps.emit("pause",void 0),this.deps.pluginManager.executeAfterPause()}async seek(O){try{if(this.deps.state.getState().state==="idle")throw Error("No media loaded");let K=await this.deps.pluginManager.executeBeforeSeek(O);if(K?.cancel)return;if(K?.data!==void 0)O=K.data;this.deps.state.updateSeekingState(!0),this.deps.emit("seeking",{currentTime:O}),await this.deps.playbackController.seek(O),this.deps.state.updateSeekingState(!1),this.deps.state.updateTime(this.deps.playbackController.getCurrentTime()),this.deps.emit("seeked",{currentTime:this.deps.playbackController.getCurrentTime()}),this.deps.pluginManager.executeAfterSeek(this.deps.playbackController.getCurrentTime())}catch($){throw this.deps.state.updateSeekingState(!1),this.handleError($),$}}async stop(){try{if((await this.deps.pluginManager.executeBeforeStop())?.cancel)return;await this.pause(),await this.seek(0),this.deps.pluginManager.executeAfterStop()}catch(O){throw this.handleError(O),O}}handleError(O){if(this.deps.pluginManager.executeOnError(O))return;this.deps.state.updateError(O),this.deps.emit("error",O),k.error("Player error:",O)}updateCurrentPlaylistItemDuration(O){let $=this.deps.state.getState(),K=$.currentPlaylistIndex;if(K!==null&&$.playlist.length>0){let j=[...$.playlist],Q=j[K];if(Q)j[K]={...Q,duration:O},this.deps.state.updatePlaylist(j,K)}}}class $O{store;constructor(O){this.store=O}getState(){return this.store.getState()}subscribe(O){return this.store.subscribe(O)}setState(O){this.store.setState(O)}reset(){this.store.reset()}applyInitial(O,$,K){this.store.setState({volume:O,muted:$,playbackRate:K})}updateLoadingState(){this.store.updateLoadingState()}updateReadyState(O,$){this.store.updateReadyState(O,$)}updatePlaybackState(O){this.store.updatePlaybackState(O)}updateSeekingState(O){this.store.updateSeekingState(O)}updateWaitingState(O){this.store.updateWaitingState(O)}updateEndedState(O){this.store.updateEndedState(O)}updateTime(O){this.store.updateTime(O)}updateDuration(O){this.store.updateDuration(O)}updateVolume(O,$){this.store.updateVolume(O,$)}updatePlaybackRate(O){this.store.updatePlaybackRate(O)}updateMediaInfo(O){this.store.updateMediaInfo(O)}updateTracks(O,$,K){this.store.updateTracks(O,$,K)}updateSelectedTracks(O,$){this.store.updateSelectedTracks(O,$)}updateError(O){this.store.updateError(O)}updateRendererType(O){this.store.updateRendererType(O)}updatePlaylist(O,$=null){this.store.updatePlaylist(O,$)}}class KO{chains=new Map;async run(O,$){let K=this.chains.get(O)??Promise.resolve(),j,Q=new Promise((V)=>{j=V});this.chains.set(O,K.then(()=>Q));try{return await K,await $()}finally{j?.()}}}class jO{deps;locks=new KO;constructor(O){this.deps=O}async setupInitialTracks(O,$){let K=!1,j=!1,Q="";if(O)try{if(K=await this.locks.run("video",async()=>{if(O.codec!==null&&await O.canDecode())return await this.deps.playbackController.trySetVideoTrack(O);return!1}),!K)Q+="Unsupported video codec. "}catch(V){Q+="Failed to set up video track. ",k.warn("Video track error:",V)}if($)try{if(j=await this.locks.run("audio",async()=>{if($.codec!==null&&await $.canDecode())return await this.deps.playbackController.trySetAudioTrack($);return!1}),!j)Q+="Unsupported audio codec. "}catch(V){Q+="Failed to set up audio track. ",k.warn("Audio track error:",V)}if(!K&&!j&&!Q)Q="No audio or video track found.";return{videoSupported:K,audioSupported:j,warningMessage:Q}}async selectVideoTrack(O,$){if(!O.selectVideoTrack($))throw Error(`Invalid video track ID: ${$}`);let K=O.getSelectedVideoTrack();if(!K){await this.deps.playbackController.setVideoTrack(null);return}if(!await this.locks.run("video",async()=>{if(K.codec!==null&&await K.canDecode())return await this.deps.playbackController.trySetVideoTrack(K);return!1}))this.deps.emit("warning",{type:"video-codec-unsupported",message:"Video codec not supported.",error:void 0}),await this.deps.playbackController.setVideoTrack(null)}async selectAudioTrack(O,$){if(!O.selectAudioTrack($))throw Error(`Invalid audio track ID: ${$}`);let K=O.getSelectedAudioTrack();if(!K){await this.deps.playbackController.setAudioTrack(null);return}if(!await this.locks.run("audio",async()=>{if(K.codec!==null&&await K.canDecode())return await this.deps.playbackController.trySetAudioTrack(K);return!1}))this.deps.emit("warning",{type:"audio-codec-unsupported",message:"Audio codec not supported. Continuing without audio.",error:void 0}),await this.deps.playbackController.setAudioTrack(null)}}import{AudioBufferSink as xO,AudioSampleSink as IO}from"mediabunny";class p{audioContext;gainNode=null;outputNode=null;bufferSink=null;sampleSink=null;bufferIterator=null;queuedNodes=new Set;startContextTime=0;startMediaTime=0;pauseTime=0;playing=!1;volume=1;muted=!1;disposed=!1;playbackId=0;playbackRate=1;pluginManager=null;constructor(O={}){if(O.audioContext)this.audioContext=O.audioContext;else{let $=window,K=$.AudioContext||$.webkitAudioContext;this.audioContext=new K}this.volume=O.volume??1,this.muted=O.muted??!1,this.setupAudioGraph()}setupAudioGraph(){if(this.gainNode=this.audioContext.createGain(),this.pluginManager){let O=this.pluginManager.executeOnAudioNode(this.audioContext,this.gainNode);this.outputNode=O}else this.outputNode=this.gainNode;this.outputNode.connect(this.audioContext.destination),this.updateGain()}setPluginManager(O){this.pluginManager=O,this.rebuildAudioGraph()}rebuildAudioGraph(){if(!this.gainNode)return;if(this.outputNode?.disconnect(),this.pluginManager){let O=this.pluginManager.executeOnAudioNode(this.audioContext,this.gainNode);this.outputNode=O}else this.outputNode=this.gainNode;this.outputNode.connect(this.audioContext.destination)}async setAudioTrack(O){if(this.dispose(),O.codec===null)throw Error("Unsupported audio codec");if(!await O.canDecode())throw Error(`Cannot decode audio track with codec: ${O.codec}`);this.bufferSink=new xO(O),this.sampleSink=new IO(O),this.disposed=!1}async play(O=this.pauseTime){if(this.playing||!this.bufferSink)return;if(this.audioContext.state==="suspended")await this.audioContext.resume();this.playbackId++;let $=this.playbackId;this.playing=!0,this.startContextTime=this.audioContext.currentTime,this.startMediaTime=O,this.pauseTime=O,this.bufferIterator=this.bufferSink.buffers(O),this.scheduleAudioBuffers($)}async scheduleAudioBuffers(O){let $=this.bufferIterator;if(!$||!this.gainNode)return;try{for await(let{buffer:K,timestamp:j}of $){if(O!==this.playbackId||this.disposed||!this.playing)break;let Q=this.audioContext.createBufferSource();Q.buffer=K,Q.connect(this.gainNode),Q.playbackRate.value=this.playbackRate,Q.playbackRate.setValueAtTime(this.playbackRate,this.audioContext.currentTime);let V=this.startContextTime+(j-this.startMediaTime)/this.playbackRate;if(V>=this.audioContext.currentTime)Q.start(V);else{let J=Math.max(0,(this.audioContext.currentTime-V)*this.playbackRate);if(J<K.duration)Q.start(this.audioContext.currentTime,J);else continue}if(this.queuedNodes.add(Q),Q.onended=()=>{this.queuedNodes.delete(Q)},j-this.getCurrentTime()>=1)await this.waitForCatchup(j)}}catch{}}async waitForCatchup(O){return new Promise(($)=>{let K=setInterval(()=>{if(O-this.getCurrentTime()<1||!this.playing)clearInterval(K),$()},100)})}pause(){if(!this.playing)return;let O=this.getCurrentTime();if(this.playing=!1,this.pauseTime=O,this.stopQueuedNodes(),this.bufferIterator)this.bufferIterator.return(),this.bufferIterator=null}stop(){this.pause(),this.pauseTime=0,this.startContextTime=0,this.startMediaTime=0}async seek(O){let $=this.playing;if($)this.pause();if(this.pauseTime=O,$)await this.play(O)}getCurrentTime(){if(this.playing){let O=this.audioContext.currentTime-this.startContextTime;return this.startMediaTime+O*this.playbackRate}return this.pauseTime}setVolume(O){this.volume=Math.max(0,Math.min(1,O)),this.updateGain()}setMuted(O){this.muted=O,this.updateGain()}updateGain(){if(!this.gainNode)return;let O=this.muted?0:this.volume;this.gainNode.gain.value=O*O}getVolume(){return this.volume}isMuted(){return this.muted}isPlaying(){return this.playing}setPlaybackRate(O){let $=Math.max(0.25,Math.min(4,O));if(this.playbackRate===$)return;let K=this.playing,j=this.getCurrentTime();if(this.playbackRate=$,K)this.pause(),this.pauseTime=j,this.play(j)}getAudioContext(){return this.audioContext}async getBufferAt(O){if(!this.bufferSink)return null;return this.bufferSink.getBuffer(O)}async getSampleAt(O){if(!this.sampleSink)return null;return this.sampleSink.getSample(O)}stopQueuedNodes(){for(let O of this.queuedNodes)try{O.stop()}catch{}this.queuedNodes.clear()}async clearIterators(){if(this.playbackId++,this.stop(),this.bufferIterator){try{await this.bufferIterator.return()}catch{}this.bufferIterator=null}}dispose(){if(this.disposed=!0,this.playbackId++,this.stop(),this.bufferIterator)this.bufferIterator.return(),this.bufferIterator=null;this.bufferSink=null,this.sampleSink=null}destroy(){if(this.dispose(),this.gainNode)this.gainNode.disconnect(),this.gainNode=null;if(this.audioContext.state!=="closed")this.audioContext.close()}}var fO=100,kO=250;class g{videoRenderer;audioManager;playing=!1;currentTime=0;duration=0;playbackRate=1;animationFrameId=null;lastFrameTime=0;syncIntervalId=null;renderIntervalId=null;isWaiting=!1;onTimeUpdate;onEnded;onWaiting;onPlaying;pendingSeekTime=null;isSeeking=!1;seekRequestId=0;constructor(O={}){this.videoRenderer=new v({canvas:O.canvas,rendererType:O.rendererType}),this.audioManager=new p({audioContext:O.audioContext,volume:O.volume,muted:O.muted}),this.playbackRate=O.playbackRate??1}async setVideoTrack(O){if(!O){this.videoRenderer.dispose();return}await this.videoRenderer.setVideoTrack(O);let $=await O.computeDuration();this.duration=Math.max(this.duration,$)}async trySetVideoTrack(O){try{return await this.setVideoTrack(O),!0}catch{return!1}}async setAudioTrack(O){let $=this.playing,K=this.getCurrentTime();if(!O){this.audioManager.dispose();return}let j=await O.computeDuration(),Q=Math.max(0,Math.min(K,j));if(await this.audioManager.setAudioTrack(O),await this.audioManager.seek(Q),$)await this.audioManager.play(Q);this.currentTime=Q,this.duration=Math.max(this.duration,j)}async trySetAudioTrack(O){try{return await this.setAudioTrack(O),!0}catch{return!1}}async setCanvas(O){await this.videoRenderer.setCanvas(O)}async play(){if(this.playing)return;if(this.playing=!0,this.lastFrameTime=performance.now(),this.currentTime>=this.duration)this.currentTime=0,await this.videoRenderer.seek(0);await this.audioManager.play(this.currentTime),this.startRenderLoop(),this.startSyncInterval()}pause(){if(!this.playing)return;if(this.playing=!1,this.isWaiting=!1,this.audioManager.pause(),this.stopRenderLoop(),this.stopSyncInterval(),this.audioManager.isPlaying())this.currentTime=this.audioManager.getCurrentTime()}async seek(O){let $=Math.max(0,Math.min(O,this.duration));if(this.currentTime=$,this.onTimeUpdate)this.onTimeUpdate(this.currentTime);if(this.seekRequestId++,this.pendingSeekTime=$,!this.isSeeking)await this.processPendingSeeks()}async processPendingSeeks(){if(this.isSeeking)return;this.isSeeking=!0;try{while(this.pendingSeekTime!==null){let O=this.pendingSeekTime;this.pendingSeekTime=null;let $=this.seekRequestId;if(await this.videoRenderer.seek(O),$!==this.seekRequestId)continue;await this.audioManager.seek(O)}}finally{this.isSeeking=!1}if(this.pendingSeekTime!==null)await this.processPendingSeeks()}startRenderLoop(){if(this.animationFrameId!==null||this.renderIntervalId!==null)return;let O=($=!0)=>{if(!this.playing)return;if(this.isSeeking);else if(this.audioManager.isPlaying())this.currentTime=this.audioManager.getCurrentTime();else{let j=performance.now(),Q=(j-this.lastFrameTime)/1000;this.lastFrameTime=j,this.currentTime+=Q*this.playbackRate}if(this.currentTime>=this.duration){this.handleEnded();return}let{isStarving:K}=this.videoRenderer.updateFrame(this.currentTime,this.isSeeking);if(K&&!this.isWaiting){if(this.isWaiting=!0,this.onWaiting)this.onWaiting()}else if(!K&&this.isWaiting){if(this.isWaiting=!1,this.onPlaying)this.onPlaying()}if($)this.animationFrameId=requestAnimationFrame(()=>O())};if(this.animationFrameId=requestAnimationFrame(()=>O()),this.renderIntervalId===null)this.renderIntervalId=window.setInterval(()=>O(!1),fO)}stopRenderLoop(){if(this.animationFrameId!==null)cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null;if(this.renderIntervalId!==null)clearInterval(this.renderIntervalId),this.renderIntervalId=null}startSyncInterval(){if(this.syncIntervalId!==null)return;this.syncIntervalId=window.setInterval(()=>{if(this.playing&&this.onTimeUpdate)this.onTimeUpdate(this.currentTime)},kO)}stopSyncInterval(){if(this.syncIntervalId!==null)clearInterval(this.syncIntervalId),this.syncIntervalId=null}handleEnded(){if(this.pause(),this.currentTime=this.duration,this.onEnded)this.onEnded()}getCurrentTime(){if(this.playing&&!this.isSeeking&&this.audioManager.isPlaying())return this.audioManager.getCurrentTime();return this.currentTime}getDuration(){return this.duration}setDuration(O){this.duration=O}isPlaying(){return this.playing}setVolume(O){this.audioManager.setVolume(O)}getVolume(){return this.audioManager.getVolume()}setMuted(O){this.audioManager.setMuted(O)}isMuted(){return this.audioManager.isMuted()}setPlaybackRate(O){let $=Math.max(0.25,Math.min(4,O));if(this.playbackRate===$)return;this.playbackRate=$,this.audioManager.setPlaybackRate($),this.lastFrameTime=performance.now()}getPlaybackRate(){return this.playbackRate}setTimeUpdateCallback(O){this.onTimeUpdate=O}setEndedCallback(O){this.onEnded=O}setWaitingCallback(O){this.onWaiting=O}setPlayingCallback(O){this.onPlaying=O}isBuffering(){return this.isWaiting}async screenshot(O){return this.videoRenderer.screenshot(this.currentTime,O)}getVideoRenderer(){return this.videoRenderer}getAudioManager(){return this.audioManager}async switchRenderer(O){await this.videoRenderer.switchRenderer(O)}getRendererType(){return this.videoRenderer.getRendererType()}updateCanvasDimensions(){this.videoRenderer.updateCanvasDimensions()}setRendererChangeCallback(O){this.videoRenderer.setRendererChangeCallback(O)}setRendererFallbackCallback(O){this.videoRenderer.setRendererFallbackCallback(O)}setRotationChangeCallback(O){this.videoRenderer.setRotationChangeCallback(O)}setRotation(O){this.videoRenderer.setRotation(O)}getRotation(){return this.videoRenderer.getRotation()}getDisplaySize(){return this.videoRenderer.getDisplaySize()}setPluginManager(O){this.videoRenderer.setPluginManager(O),this.audioManager.setPluginManager(O)}getCanvas(){return this.videoRenderer.getCanvas()}refreshOverlays(){this.videoRenderer.refreshOverlays()}rebuildAudioGraph(){this.audioManager.rebuildAudioGraph()}async reset(){this.pause(),this.stopRenderLoop(),this.stopSyncInterval(),this.currentTime=0,this.duration=0,await Promise.all([this.videoRenderer.clearIterators(),this.audioManager.clearIterators()]),this.playbackRate=1,this.lastFrameTime=0,this.seekRequestId++,this.pendingSeekTime=null,this.isSeeking=!1}dispose(){this.pause(),this.seekRequestId++,this.pendingSeekTime=null,this.isSeeking=!1,this.videoRenderer.dispose(),this.audioManager.dispose(),this.onTimeUpdate=void 0,this.onEnded=void 0,this.onWaiting=void 0,this.onPlaying=void 0}destroy(){this.dispose(),this.audioManager.destroy()}}function m(){return crypto?.randomUUID?.()??Date.now().toString(36)+Math.random().toString(36).substr(2)}class QO{store;emitter;switchSource;sourceManager;constructor(O,$,K,j){this.store=O,this.emitter=$,this.switchSource=K,this.sourceManager=j}async loadPlaylist(O,$={}){let K=O.map((j)=>{if(j&&typeof j==="object"&&"mediaSource"in j)return{id:m(),mediaSource:j.mediaSource,title:j.title,poster:j.poster,savedPosition:null,duration:null};else return{id:m(),mediaSource:j,savedPosition:null,duration:null}});if(this.store.updatePlaylist(K,K.length>0?0:null),this.emitter.emit("playlistchange",{playlist:K}),K.length>0&&this.switchSource){let j=K[0];if($.startTime!==void 0)j.savedPosition=$.startTime;await this.switchSource(j,$.autoplay??!1)}}addToPlaylist(O,$){let K=this.createPlaylistItem(O);this.store.addToPlaylist(K,$),this.emitter.emit("playlistadd",{item:K,index:$??this.store.getState().playlist.length-1})}async removeFromPlaylist(O){let $=this.store.getState(),K=$.currentPlaylistIndex,j=$.playing;this.store.removeFromPlaylist(O),this.emitter.emit("playlistremove",{index:O}),this.sourceManager?.disposeQueued($.playlist[O]?.id||"");let Q=this.store.getState(),V=Q.currentPlaylistIndex;if(Q.playlist.length===0)this.sourceManager?.disposeAll(),this.emitter.emit("playlistchange",{playlist:Q.playlist}),this.emitter.emit("playlistend",void 0);if(K===O&&V!==null&&V!==K&&this.switchSource){let J=Q.playlist[V];try{await this.switchSource(J,j)}catch(U){this.emitter.emit("playlistitemerror",{index:V,error:U})}}}clearPlaylist(){this.store.clearPlaylist(),this.emitter.emit("playlistchange",{playlist:[]}),this.emitter.emit("playlistend",void 0),this.sourceManager?.disposeAll()}async next(){let O=this.store.getState(),$=O.currentPlaylistIndex??0,K=O.playlist,j=O.playlistMode,Q=null;if(j==="repeat-one")Q=$;else if(j==="sequential"||j==="repeat")if($<K.length-1)Q=$+1;else if(j==="repeat")Q=0;else{this.emitter.emit("playlistend",void 0);return}else if($<K.length-1)Q=$+1;if(Q!==null)await this.switchTo(Q)}async prev(){let $=this.store.getState().currentPlaylistIndex??0;if($>0)await this.switchTo($-1)}async jumpTo(O){let K=this.store.getState().playlist;if(O>=0&&O<K.length)await this.switchTo(O);else if(O>=K.length)this.emitter.emit("playlistend",void 0)}setMode(O){let $=["sequential","manual","repeat","repeat-one",null];if(!$.includes(O))throw Error(`Invalid playlist mode: ${O}. Valid modes: ${$.filter((K)=>K!==null).join(", ")}, null`);this.store.updatePlaylistMode(O)}async switchTo(O){let $=this.store.getState(),K=$.currentPlaylistIndex,j=$.playing,Q=[...$.playlist];if(K!==null&&K!==O){let U=Q[K];Q[K]={...U,savedPosition:$.currentTime}}this.store.updatePlaylist(Q,O);let V=Q[O];if(this.emitter.emit("playlistitemchange",{index:O,item:V,previousIndex:K??void 0}),this.switchSource)await this.switchSource(V,j);if($.playlistMode==="sequential"&&O<Q.length-1){let U=Q[O+1];this.sourceManager?.preloadSource(U.mediaSource,U.id)}}createPlaylistItem(O){if(O&&typeof O==="object"&&"mediaSource"in O)return{id:m(),mediaSource:O.mediaSource,title:O.title,poster:O.poster,savedPosition:null,duration:null};else return{id:m(),mediaSource:O,savedPosition:null,duration:null}}get playlist(){return this.store.getState().playlist}get currentIndex(){return this.store.getState().currentPlaylistIndex}get currentItem(){let O=this.currentIndex;return O!==null?this.playlist[O]:null}get mode(){return this.store.getState().playlistMode}dispose(){if(this.sourceManager)this.sourceManager=void 0;this.switchSource=void 0}}function zO(O,$,K,j){let Q=$.name;return{player:O,getState(){return O.getState()},subscribe(V){let J=O.subscribe(V),U=()=>J.unsubscribe();return K.stateUnsubscribes.push(U),U},getPluginState(){return K.pluginState},setPluginState(V){K.pluginState=V},on(V,J){O.on(V,J);let U=K.eventListeners.get(V);if(!U)U=new Set,K.eventListeners.set(V,U);U.add(J)},off(V,J){O.off(V,J);let U=K.eventListeners.get(V);if(U)U.delete(J)},getCanvas(){return O.getRenderTarget()},getPlugin(V){return j().get(V)?.plugin},hasPlugin(V){return j().has(V)},log(...V){console.log(`[MediaFox:${Q}]`,...V)},warn(...V){console.warn(`[MediaFox:${Q}]`,...V)},error(...V){console.error(`[MediaFox:${Q}]`,...V)}}}class VO{player;plugins=new Map;overlays=[];overlaysSorted=!1;registrationCounter=0;constructor(O){this.player=O}async install(O){let{name:$}=O;if(this.plugins.has($))throw Error(`Plugin "${$}" is already installed`);if(O.dependencies){for(let j of O.dependencies)if(!this.plugins.has(j))throw Error(`Plugin "${$}" requires plugin "${j}" to be installed first`)}let K={plugin:O,context:null,eventListeners:new Map,stateUnsubscribes:[],pluginState:void 0};K.context=zO(this.player,O,K,()=>this.plugins),this.plugins.set($,K);try{await O.install(K.context)}catch(j){throw this.plugins.delete($),j}if(O.hooks?.render?.onOverlay)this.overlays.push({pluginName:$,zIndex:O.hooks.render.onOverlay.zIndex??0,registrationOrder:this.registrationCounter++,render:O.hooks.render.onOverlay}),this.overlaysSorted=!1,this.player.refreshOverlays()}async uninstall(O){let $=this.plugins.get(O);if(!$)throw Error(`Plugin "${O}" is not installed`);try{await $.plugin.uninstall?.()}catch(j){console.error(`Error uninstalling plugin "${O}":`,j)}for(let[j,Q]of $.eventListeners)for(let V of Q)this.player.off(j,V);for(let j of $.stateUnsubscribes)j();let K=this.overlays.some((j)=>j.pluginName===O);if(this.overlays=this.overlays.filter((j)=>j.pluginName!==O),this.plugins.delete(O),K)this.player.refreshOverlays()}has(O){return this.plugins.has(O)}get size(){return this.plugins.size}async executeBeforeLoad(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.beforeLoad;if(!K)continue;try{let j=await K.call($.plugin,O);if(j?.cancel)return j;if(j?.data!==void 0)O=j.data}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in beforeLoad hook:`,j)}}return{data:O}}async executeAfterLoad(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.afterLoad;if(!K)continue;try{await K.call($.plugin,O)}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in afterLoad hook:`,j)}}}async executeBeforePlay(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforePlay;if(!$)continue;try{let K=await $.call(O.plugin);if(K?.cancel)return K}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in beforePlay hook:`,K)}}return}executeAfterPlay(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPlay;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in afterPlay hook:`,K)}}}async executeBeforePause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforePause;if(!$)continue;try{let K=await $.call(O.plugin);if(K?.cancel)return K}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in beforePause hook:`,K)}}return}executeAfterPause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPause;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in afterPause hook:`,K)}}}async executeBeforeSeek(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.beforeSeek;if(!K)continue;try{let j=await K.call($.plugin,O);if(j?.cancel)return j;if(j?.data!==void 0)O=j.data}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in beforeSeek hook:`,j)}}return{data:O}}executeAfterSeek(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.lifecycle?.afterSeek;if(!K)continue;try{K.call($.plugin,O)}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in afterSeek hook:`,j)}}}async executeBeforeStop(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforeStop;if(!$)continue;try{let K=await $.call(O.plugin);if(K?.cancel)return K}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in beforeStop hook:`,K)}}return}executeAfterStop(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterStop;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in afterStop hook:`,K)}}}executeOnError(O){let $=!1;for(let[,K]of this.plugins){let j=K.plugin.hooks?.lifecycle?.onError;if(!j)continue;try{if(j.call(K.plugin,O)?.handled)$=!0}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in onError hook:`,Q)}}return $}executeOnEnded(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.onEnded;if(!$)continue;try{$.call(O.plugin)}catch(K){console.error(`[MediaFox:${O.plugin.name}] Error in onEnded hook:`,K)}}}executeBeforeRender(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.render?.beforeRender;if(!j)continue;try{let Q=j.call(K.plugin,O,$);if(Q?.skip)return Q}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in beforeRender hook:`,Q)}}return}executeTransformFrame(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.render?.transformFrame;if(!K)continue;try{let j=K.call($.plugin,O);if(j)O=j}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in transformFrame hook:`,j)}}return O}executeAfterRender(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.render?.afterRender;if(!K)continue;try{K.call($.plugin,O)}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in afterRender hook:`,j)}}}executeOverlays(O,$,K){if(this.overlays.length===0)return;if(!this.overlaysSorted)this.overlays.sort((j,Q)=>{if(j.zIndex!==Q.zIndex)return j.zIndex-Q.zIndex;return j.registrationOrder-Q.registrationOrder}),this.overlaysSorted=!0;for(let j of this.overlays)try{j.render?.render(O,$,K)}catch(Q){console.error(`[MediaFox:${j.pluginName}] Error in onOverlay hook:`,Q)}}executeBeforeStateUpdate(O){for(let[,$]of this.plugins){let K=$.plugin.hooks?.state?.beforeStateUpdate;if(!K)continue;try{let j=K.call($.plugin,O);if(j===null)return null;if(j!==void 0)O=j}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in beforeStateUpdate hook:`,j)}}return O}executeOnStateChange(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.state?.onStateChange;if(!j)continue;try{j.call(K.plugin,O,$)}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in onStateChange hook:`,Q)}}}executeBeforeEvent(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.event?.beforeEvent;if(!j)continue;try{let Q=j.call(K.plugin,O,$);if(Q?.cancel)return Q;if(Q?.data!==void 0)$=Q.data}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in beforeEvent hook:`,Q)}}return{data:$}}executeAfterEvent(O,$){for(let[,K]of this.plugins){let j=K.plugin.hooks?.event?.afterEvent;if(!j)continue;try{j.call(K.plugin,O,$)}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in afterEvent hook:`,Q)}}}executeOnAudioNode(O,$){let K=$;for(let[,j]of this.plugins){let Q=j.plugin.hooks?.audio?.onAudioNode;if(!Q)continue;try{let V=Q.call(j.plugin,O,$);if(V)K=V}catch(V){console.error(`[MediaFox:${j.plugin.name}] Error in onAudioNode hook:`,V)}}return K}async dispose(){let O=Array.from(this.plugins.keys()).reverse();for(let $ of O)try{await this.uninstall($)}catch(K){console.error(`Error disposing plugin "${$}":`,K)}}}import{ALL_FORMATS as vO,BlobSource as hO,BufferSource as pO,CanvasSink as gO,FilePathSource as mO,Input as uO,ReadableStreamSource as lO,UrlSource as cO}from"mediabunny";class u{currentSource=null;queuedSources=new Map;options;constructor(O={}){this.options={maxCacheSize:O.maxCacheSize??16777216,crossOrigin:O.crossOrigin,requestInit:O.requestInit}}async createSource(O,$){if(this.currentSource&&!$)this.disposeCurrent();let K,j;if(O instanceof File||O instanceof Blob)K=new hO(O,{maxCacheSize:this.options.maxCacheSize}),j="blob";else if(O instanceof ArrayBuffer||O instanceof Uint8Array)K=new pO(O),j="buffer";else if(typeof O==="string"||O instanceof URL){let J=O instanceof URL?O.href:O;if(typeof window>"u"&&!J.startsWith("http"))K=new mO(J,{maxCacheSize:this.options.maxCacheSize}),j="file";else K=new cO(J,{maxCacheSize:this.options.maxCacheSize,requestInit:this.options.requestInit}),j="url"}else if(typeof ReadableStream<"u"&&O instanceof ReadableStream)K=new lO(O,{maxCacheSize:this.options.maxCacheSize}),j="stream";else throw TypeError("Unsupported media source type");let Q=new uO({source:K,formats:vO}),V={source:K,input:Q,type:j,originalSource:O};if($)this.queuedSources.set($,V);else this.currentSource=V;return V}getCurrentSource(){return this.currentSource}getQueuedSource(O){return this.queuedSources.get(O)||null}async preloadSource(O,$){if(this.queuedSources.has($))return;let K=await this.createSource(O,$);if(K.input)try{let j=await this.prefetchTrackData(K.input);K.prefetchedData=j}catch{}}async prefetchTrackData(O){let $=await O.getVideoTracks(),K=await O.getAudioTracks(),j=$.length>0?$[0]:null,Q=K.length>0?K[0]:null,V=null,J=null,U=0;if(j){if(j.codec!==null&&await j.canDecode()){V=new gO(j,{poolSize:2});let G=V.canvases(0);J=(await G.next()).value??null,await G.return(),U=await j.computeDuration()}}if(!U&&Q)U=await Q.computeDuration();return{videoTrack:j,audioTrack:Q,canvasSink:V,firstFrame:J,duration:U}}promoteQueuedSource(O){let $=this.queuedSources.get(O);if(!$)return null;if(this.queuedSources.delete(O),this.currentSource)this.currentSource.input?.dispose();return this.currentSource=$,$}getOriginalSource(){return this.currentSource?.originalSource??null}disposeCurrent(){if(this.currentSource)this.currentSource.input?.dispose(),this.currentSource=null}disposeQueued(O){let $=this.queuedSources.get(O);if($)$.input?.dispose(),this.queuedSources.delete(O)}disposeAll(){this.disposeCurrent(),this.queuedSources.forEach((O,$)=>{this.disposeQueued($)}),this.queuedSources.clear()}dispose(){this.disposeAll()}static isStreamingSource(O){return O instanceof ReadableStream||typeof O==="string"&&O.startsWith("http")}static isLocalSource(O){return O instanceof File||O instanceof Blob||O instanceof ArrayBuffer||O instanceof Uint8Array||typeof O==="string"&&!O.startsWith("http")}static getSourceType(O){if(O instanceof File)return"file";if(O instanceof Blob)return"blob";if(O instanceof ArrayBuffer||O instanceof Uint8Array)return"buffer";if(O instanceof ReadableStream)return"stream";if(typeof O==="string"||O instanceof URL)return(O instanceof URL?O.href:O).startsWith("http")?"url":"file";return"unknown"}static async fromFetch(O,$){let K=await fetch(O,$);if(!K.ok)throw Error(`Failed to fetch: ${K.status} ${K.statusText}`);return K.blob()}static fromStreamingFetch(O,$){return new ReadableStream({async start(K){let j=await fetch(O,$);if(!j.ok){K.error(Error(`Failed to fetch: ${j.status} ${j.statusText}`));return}let Q=j.body?.getReader();if(!Q){K.error(Error("Response body is not readable"));return}try{while(!0){let{done:V,value:J}=await Q.read();if(V)break;K.enqueue(J)}K.close()}catch(V){K.error(V)}}})}}function l(O,$){if(Object.is(O,$))return!0;if(typeof O!==typeof $)return!1;if(O===null||$===null)return O===$;if(Array.isArray(O)&&Array.isArray($)){if(O.length!==$.length)return!1;for(let K=0;K<O.length;K++)if(!l(O[K],$[K]))return!1;return!0}if(PO(O)&&PO($)){let K=Object.keys(O),j=Object.keys($);if(K.length!==j.length)return!1;for(let Q of K){if(!Object.hasOwn($,Q))return!1;if(!l(O[Q],$[Q]))return!1}return!0}return!1}function PO(O){return typeof O==="object"&&O!==null&&O.constructor===Object}class c{state;previousState;listeners=new Set;updateScheduled=!1;pendingUpdates={};pendingKeys=[];pluginManager=null;listenerCache=[];constructor(){this.state=this.getInitialState(),this.previousState={...this.state}}setPluginManager(O){this.pluginManager=O}getInitialState(){let $=H.getSupportedRenderers()[0]||"canvas2d";return{state:"idle",currentTime:0,duration:0,buffered:[],volume:1,muted:!1,playbackRate:1,playing:!1,paused:!0,ended:!1,seeking:!1,waiting:!1,error:null,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null,canPlay:!1,canPlayThrough:!1,isLive:!1,rendererType:$,playlist:[],currentPlaylistIndex:null,playlistMode:null,rotation:0,displaySize:{width:0,height:0}}}getState(){return this.state}setState(O){if(this.pluginManager){let K=this.pluginManager.executeBeforeStateUpdate(O);if(K===null)return;O=K}let $=Object.keys(O);for(let K=0;K<$.length;K++){let j=$[K];if(this.pendingUpdates[j]===void 0)this.pendingKeys.push(j);this.setPendingValue(j,O[j])}if(!this.updateScheduled)this.updateScheduled=!0,queueMicrotask(()=>this.flushUpdates())}setPendingValue(O,$){this.pendingUpdates[O]=$}flushUpdates(){let O=this.pendingKeys;if(O.length===0){this.updateScheduled=!1;return}let $=!1;for(let K=0;K<O.length;K++){let j=O[K];if(!l(this.pendingUpdates[j],this.state[j])){$=!0;break}}for(let K=0;K<O.length;K++){let j=O[K];this.copyStateKey(j)}if(this.pendingUpdates={},this.pendingKeys.length=0,this.updateScheduled=!1,$){if(this.notifyListeners(),this.pluginManager)this.pluginManager.executeOnStateChange(this.state,this.previousState)}}copyStateKey(O){if(this.previousState[O]=this.state[O],O in this.pendingUpdates)this.state[O]=this.pendingUpdates[O]}subscribe(O){return this.listeners.add(O),O(this.getState()),()=>{this.listeners.delete(O)}}reset(){this.state=this.getInitialState(),this.pendingUpdates={},this.pendingKeys.length=0,this.updateScheduled=!1,this.notifyListeners()}notifyListeners(){let O=this.state,$=this.listenerCache;$.length=0;for(let K of this.listeners)$.push(K);for(let K=0;K<$.length;K++)try{$[K](O)}catch(j){console.error("Error in state listener:",j)}}updatePlaybackState(O){let $=O?"playing":"paused";this.setState({state:$,playing:O,paused:!O,ended:!1})}updateTime(O){this.setState({currentTime:O})}updateDuration(O){this.setState({duration:O})}updateBuffered(O){this.setState({buffered:O})}updateVolume(O,$){this.setState({volume:O,muted:$})}updatePlaybackRate(O){this.setState({playbackRate:O})}updateMediaInfo(O){this.setState({mediaInfo:O})}updateTracks(O,$,K){let j={};if(O)j.videoTracks=O;if($)j.audioTracks=$;if(K)j.subtitleTracks=K;this.setState(j)}updateSelectedTracks(O,$){switch(O){case"video":this.setState({selectedVideoTrack:$});break;case"audio":this.setState({selectedAudioTrack:$});break;case"subtitle":this.setState({selectedSubtitleTrack:$});break}}updateError(O){this.setState({error:O,state:O?"error":this.state.state})}updateSeekingState(O){this.setState({seeking:O})}updateWaitingState(O){this.setState({waiting:O})}updateReadyState(O,$){this.setState({canPlay:O,canPlayThrough:$,state:O?"ready":this.state.state})}updateEndedState(O){this.setState({ended:O,playing:!1,paused:!0,state:O?"ended":this.state.state})}updateLoadingState(){this.setState({state:"loading",playing:!1,paused:!0,ended:!1,error:null})}updateRendererType(O){this.setState({rendererType:O})}updateRotation(O,$){this.setState({rotation:O,displaySize:$})}updatePlaylist(O,$=null){this.setState({playlist:O,currentPlaylistIndex:$})}updateCurrentPlaylistIndex(O){this.setState({currentPlaylistIndex:O})}updatePlaylistMode(O){this.setState({playlistMode:O})}addToPlaylist(O,$){let K=this.state.playlist,j,Q=this.state.currentPlaylistIndex;if($!==void 0&&$>=0&&$<=K.length){if(j=[...K.slice(0,$),O,...K.slice($)],Q!==null&&Q>=$)Q+=1}else j=[...K,O];this.setState({playlist:j,currentPlaylistIndex:Q})}removeFromPlaylist(O){let $=this.state.playlist;if(O<0||O>=$.length)return;let K=$.filter((Q,V)=>V!==O),j=this.state.currentPlaylistIndex;if(j===O)j=K.length>0?0:null;else if(j!==null&&j>O)j-=1;if(K.length===0)this.setState({playlist:K,currentPlaylistIndex:null,state:"idle",currentTime:0,duration:0,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null});else this.setState({playlist:K,currentPlaylistIndex:j})}clearPlaylist(){this.setState({playlist:[],currentPlaylistIndex:null,state:"idle",currentTime:0,duration:0,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null})}}class o{input=null;videoTracks=new Map;audioTracks=new Map;videoTrackInfos=[];audioTrackInfos=[];subtitleTrackInfos=[];subtitleProviders=new Map;subtitleTrackResolvers=new Map;selectedVideoTrack=null;selectedAudioTrack=null;selectedSubtitleTrack=null;onTrackChange;async initialize(O){this.videoTracks.clear(),this.audioTracks.clear(),this.videoTrackInfos=[],this.audioTrackInfos=[],this.selectedVideoTrack=null,this.selectedAudioTrack=null,this.input=O,await this.loadTracks()}async loadTracks(){if(!this.input)return;let O=await this.input.getVideoTracks();this.videoTrackInfos=await Promise.all(O.map(async(K)=>{let j=`video-${K.id}`;this.videoTracks.set(j,K);let Q=0,V=0;try{let U=await K.computePacketStats(100);Q=U.averagePacketRate,V=U.averageBitrate}catch{}return{id:j,codec:K.codec,language:K.languageCode,name:K.name,width:K.codedWidth,height:K.codedHeight,frameRate:Q,bitrate:V,rotation:K.rotation,selected:!1,decodable:await K.canDecode()}}));let $=await this.input.getAudioTracks();if(this.audioTrackInfos=await Promise.all($.map(async(K)=>{let j=`audio-${K.id}`;this.audioTracks.set(j,K);let Q=0;try{Q=(await K.computePacketStats(100)).averageBitrate}catch{}return{id:j,codec:K.codec,language:K.languageCode,name:K.name,channels:K.numberOfChannels,sampleRate:K.sampleRate,bitrate:Q,selected:!1,decodable:await K.canDecode()}})),this.videoTrackInfos.length>0){let K=this.videoTrackInfos.find((j)=>j.decodable);if(K)this.selectVideoTrack(K.id)}if(this.audioTrackInfos.length>0){let K=this.audioTrackInfos.find((j)=>j.decodable);if(K)this.selectAudioTrack(K.id)}}getVideoTracks(){return[...this.videoTrackInfos]}getAudioTracks(){return[...this.audioTrackInfos]}getSubtitleTracks(){return[...this.subtitleTrackInfos]}getSelectedVideoTrack(){if(!this.selectedVideoTrack)return null;return this.videoTracks.get(this.selectedVideoTrack)??null}getSelectedAudioTrack(){if(!this.selectedAudioTrack)return null;return this.audioTracks.get(this.selectedAudioTrack)??null}getSelectedVideoTrackInfo(){if(!this.selectedVideoTrack)return null;return this.videoTrackInfos.find((O)=>O.id===this.selectedVideoTrack)??null}getSelectedAudioTrackInfo(){if(!this.selectedAudioTrack)return null;return this.audioTrackInfos.find((O)=>O.id===this.selectedAudioTrack)??null}getSelectedSubtitleTrackInfo(){if(!this.selectedSubtitleTrack)return null;return this.subtitleTrackInfos.find((O)=>O.id===this.selectedSubtitleTrack)??null}selectVideoTrack(O){if(O===this.selectedVideoTrack)return!0;if(O&&!this.videoTracks.has(O))return!1;let $=this.selectedVideoTrack;if(this.videoTrackInfos.forEach((K)=>{K.selected=K.id===O}),this.selectedVideoTrack=O,this.onTrackChange)this.onTrackChange({type:"video",previousTrackId:$,newTrackId:O});return!0}selectAudioTrack(O){if(O===this.selectedAudioTrack)return!0;if(O&&!this.audioTracks.has(O))return!1;let $=this.selectedAudioTrack;if(this.audioTrackInfos.forEach((K)=>{K.selected=K.id===O}),this.selectedAudioTrack=O,this.onTrackChange)this.onTrackChange({type:"audio",previousTrackId:$,newTrackId:O});return!0}selectSubtitleTrack(O){if(O===this.selectedSubtitleTrack)return!0;if(O&&!this.subtitleTrackResolvers.has(O))return!1;let $=this.selectedSubtitleTrack;if(this.subtitleTrackInfos.forEach((K)=>{K.selected=K.id===O}),this.selectedSubtitleTrack=O,this.onTrackChange)this.onTrackChange({type:"subtitle",previousTrackId:$,newTrackId:O});return!0}registerSubtitleTracks(O,$){this.subtitleProviders.set(O,$),this.rebuildSubtitleTracks()}unregisterSubtitleTracks(O){if(!this.subtitleProviders.delete(O))return;this.rebuildSubtitleTracks()}async getSubtitleTrackResource(O){if(!O)return null;let $=this.subtitleTrackResolvers.get(O);if(!$)return null;return $()}rebuildSubtitleTracks(){let O=this.selectedSubtitleTrack;this.subtitleTrackInfos=[],this.subtitleTrackResolvers.clear();for(let K of this.subtitleProviders.values())for(let j of K){let Q={...j.info,selected:!1};this.subtitleTrackInfos.push(Q),this.subtitleTrackResolvers.set(Q.id,j.resolver)}let $=O;if(!$||!this.subtitleTrackResolvers.has($))$=this.subtitleTrackInfos[0]?.id??null;if(this.selectedSubtitleTrack=$,this.subtitleTrackInfos.forEach((K)=>{K.selected=K.id===this.selectedSubtitleTrack}),O!==this.selectedSubtitleTrack&&this.onTrackChange)this.onTrackChange({type:"subtitle",previousTrackId:O,newTrackId:this.selectedSubtitleTrack})}setTrackChangeListener(O){this.onTrackChange=O}getState(){return{videoTracks:this.getVideoTracks(),audioTracks:this.getAudioTracks(),subtitleTracks:this.getSubtitleTracks(),selectedVideoTrack:this.selectedVideoTrack,selectedAudioTrack:this.selectedAudioTrack,selectedSubtitleTrack:this.selectedSubtitleTrack}}getPrimaryVideoTrack(){if(this.videoTracks.size===0)return null;return this.selectedVideoTrack?this.videoTracks.get(this.selectedVideoTrack)??null:this.videoTracks.values().next().value??null}getPrimaryAudioTrack(){if(this.audioTracks.size===0)return null;return this.selectedAudioTrack?this.audioTracks.get(this.selectedAudioTrack)??null:this.audioTracks.values().next().value??null}hasVideo(){return this.videoTrackInfos.length>0}hasAudio(){return this.audioTrackInfos.length>0}hasSubtitles(){return this.subtitleTrackInfos.length>0}dispose(){this.videoTracks.clear(),this.audioTracks.clear(),this.videoTrackInfos=[],this.audioTrackInfos=[],this.subtitleTrackInfos=[],this.subtitleProviders.clear(),this.subtitleTrackResolvers.clear(),this.selectedVideoTrack=null,this.selectedAudioTrack=null,this.selectedSubtitleTrack=null,this.input=null,this.onTrackChange=void 0}async replaceAudioTrackByInputId(O,$){let K=null;for(let[Q,V]of this.audioTracks.entries())if(V.id===O){K=Q;break}if(!K)return;this.audioTracks.set(K,$);let j=this.audioTrackInfos.findIndex((Q)=>Q.id===K);if(j!==-1){let Q=0;try{Q=(await $.computePacketStats(100)).averageBitrate}catch{}this.audioTrackInfos[j]={...this.audioTrackInfos[j],codec:$.codec,channels:$.numberOfChannels,sampleRate:$.sampleRate,bitrate:Q,decodable:await $.canDecode()}}}async replaceVideoTrackByInputId(O,$){let K=null;for(let[Q,V]of this.videoTracks.entries())if(V.id===O){K=Q;break}if(!K)return;this.videoTracks.set(K,$);let j=this.videoTrackInfos.findIndex((Q)=>Q.id===K);if(j!==-1){let Q=0,V=0;try{let J=await $.computePacketStats(100);Q=J.averagePacketRate,V=J.averageBitrate}catch{}this.videoTrackInfos[j]={...this.videoTrackInfos[j],codec:$.codec,width:$.codedWidth,height:$.codedHeight,rotation:$.rotation,frameRate:Q,bitrate:V,decodable:await $.canDecode()}}}}class d{emitter;store;state;sourceManager;playbackController;trackManager;playlistManager;pluginManager;options;disposed=!1;getCurrentInput=()=>this.sourceManager.getCurrentSource()?.input??null;trackSwitcher;core;constructor(O={}){this.options={volume:1,muted:!1,playbackRate:1,autoplay:!1,preload:"metadata",...O},this.emitter=new x({maxListeners:100}),this.store=new c,this.state=new $O(this.store),this.sourceManager=new u({maxCacheSize:O.maxCacheSize,crossOrigin:O.crossOrigin}),this.playbackController=new g({canvas:O.renderTarget,audioContext:O.audioContext,volume:this.options.volume,muted:this.options.muted,playbackRate:this.options.playbackRate,rendererType:this.options.renderer}),this.trackManager=new o,this.playlistManager=new QO(this.store,this.emitter,async($,K)=>{await this.core.load($.mediaSource,{startTime:$.savedPosition??0,autoplay:K,playlistItemId:$.id})},this.sourceManager),this.trackSwitcher=new jO({sourceManager:this.sourceManager,trackManager:this.trackManager,playbackController:this.playbackController,emit:this.emit.bind(this),store:this.store,getCurrentInput:this.getCurrentInput}),this.pluginManager=new VO(this),this.core=new OO({state:this.state,sourceManager:this.sourceManager,trackManager:this.trackManager,playbackController:this.playbackController,trackSwitcher:this.trackSwitcher,emit:this.emit.bind(this),pluginManager:this.pluginManager}),this.playbackController.setPluginManager(this.pluginManager),this.store.setPluginManager(this.pluginManager),this.setupInternalListeners(),this.state.applyInitial(this.options.volume??1,this.options.muted??!1,this.options.playbackRate??1),this.state.updateRendererType(this.options.renderer||"webgpu")}setupInternalListeners(){this.playbackController.setTimeUpdateCallback((O)=>{this.state.updateTime(O),this.emit("timeupdate",{currentTime:O})}),this.playbackController.setEndedCallback(()=>{this.state.updateEndedState(!0),this.emit("ended",void 0);let O=this.getState();if(O.playlist.length>0&&O.currentPlaylistIndex!==null){let{playlistMode:$,currentPlaylistIndex:K}=O;if($==="repeat-one"){let j=K;queueMicrotask(async()=>{try{await this.seek(0),await this.play()}catch(Q){this.emitter.emit("playlistitemerror",{index:j,error:Q})}})}else if($==="repeat"){let j=K<O.playlist.length-1?K+1:0;queueMicrotask(async()=>{try{await this.playlistManager.next()}catch(Q){this.emitter.emit("playlistitemerror",{index:j,error:Q})}})}else if($==="sequential"&&K<O.playlist.length-1){let j=K+1;queueMicrotask(async()=>{try{await this.playlistManager.next()}catch(Q){this.emitter.emit("playlistitemerror",{index:j,error:Q})}})}}}),this.trackManager.setTrackChangeListener((O)=>{this.state.updateSelectedTracks(O.type,O.newTrackId),this.emit("trackchange",{type:O.type,trackId:O.newTrackId})}),this.playbackController.setWaitingCallback(()=>{this.state.updateWaitingState(!0),this.emit("waiting",void 0)}),this.playbackController.setPlayingCallback(()=>{if(this.getState().waiting)this.state.updateWaitingState(!1),this.emit("playing",void 0)}),this.playbackController.setRendererChangeCallback((O)=>{this.state.updateRendererType(O),this.emit("rendererchange",O)}),this.playbackController.setRendererFallbackCallback((O,$)=>{this.emit("rendererfallback",{from:O,to:$})}),this.playbackController.setRotationChangeCallback((O,$)=>{this.store.updateRotation(O,$),this.emit("rotationchange",{rotation:O,displaySize:$})}),this.state.subscribe((O)=>{this.emit("statechange",O)})}async load(O,$={}){this.checkDisposed();let K=this.getState();if(K.playlist.length===0||$.replacePlaylist){await this.playlistManager.loadPlaylist([{mediaSource:O}],{autoplay:$.autoplay??this.options.autoplay,startTime:$.startTime});return}else if(K.currentPlaylistIndex!==null&&K.playlist.length>0){let j=K.currentPlaylistIndex,V={...K.playlist[j],mediaSource:O,savedPosition:0,duration:null},J=[...K.playlist];J[j]=V,this.store.updatePlaylist(J,j),this.emitter.emit("playlistchange",{playlist:J}),await this.core.load(O,{startTime:$.startTime??0,autoplay:$.autoplay??this.options.autoplay});return}await this.core.load(O,{autoplay:$.autoplay??this.options.autoplay,startTime:$.startTime})}async play(){return this.checkDisposed(),this.core.play()}pause(){this.checkDisposed(),this.core.pause()}async seek(O,$={}){return this.checkDisposed(),this.core.seek(O)}async stop(){return this.checkDisposed(),this.core.stop()}get currentTime(){return this.playbackController.getCurrentTime()}set currentTime(O){this.seek(O)}get duration(){return this.state.getState().duration}get volume(){return this.playbackController.getVolume()}set volume(O){this.checkDisposed();let $=Math.max(0,Math.min(1,O));this.playbackController.setVolume($),this.state.updateVolume($,this.muted),this.emit("volumechange",{volume:$,muted:this.muted})}get muted(){return this.playbackController.isMuted()}set muted(O){this.checkDisposed(),this.playbackController.setMuted(O),this.state.updateVolume(this.volume,O),this.emit("volumechange",{volume:this.volume,muted:O})}get playbackRate(){return this.playbackController.getPlaybackRate()}set playbackRate(O){this.checkDisposed();let $=Math.max(0.25,Math.min(4,O));this.playbackController.setPlaybackRate($),this.state.updatePlaybackRate($),this.emit("ratechange",{playbackRate:$})}get paused(){return!this.playbackController.isPlaying()}get ended(){return this.state.getState().ended}get seeking(){return this.state.getState().seeking}get waiting(){return this.state.getState().waiting}get rotation(){return this.playbackController.getRotation()}set rotation(O){this.checkDisposed(),this.playbackController.setRotation(O)}get displaySize(){return this.playbackController.getDisplaySize()}getVideoTracks(){return this.trackManager.getVideoTracks()}getAudioTracks(){return this.trackManager.getAudioTracks()}getSubtitleTracks(){return this.trackManager.getSubtitleTracks()}async selectVideoTrack(O){this.checkDisposed(),await this.trackSwitcher.selectVideoTrack(this.trackManager,O)}async selectAudioTrack(O){this.checkDisposed(),await this.trackSwitcher.selectAudioTrack(this.trackManager,O)}selectSubtitleTrack(O){if(this.checkDisposed(),!this.trackManager.selectSubtitleTrack(O))throw Error(`Invalid subtitle track ID: ${O}`)}registerSubtitleTracks(O,$){this.trackManager.registerSubtitleTracks(O,$),this.state.updateTracks(void 0,void 0,this.trackManager.getSubtitleTracks());let K=this.state.getState().mediaInfo;if(K)this.state.updateMediaInfo({...K,hasSubtitles:this.trackManager.hasSubtitles()})}unregisterSubtitleTracks(O){this.trackManager.unregisterSubtitleTracks(O),this.state.updateTracks(void 0,void 0,this.trackManager.getSubtitleTracks());let $=this.state.getState().mediaInfo;if($)this.state.updateMediaInfo({...$,hasSubtitles:this.trackManager.hasSubtitles()})}async getSubtitleTrackResource(O){return this.trackManager.getSubtitleTrackResource(O)}async screenshot(O={}){return this.checkDisposed(),this.playbackController.screenshot(O)}async setRenderTarget(O){this.checkDisposed(),await this.playbackController.setCanvas(O)}getRenderTarget(){return this.playbackController.getCanvas()}refreshOverlays(){this.playbackController.refreshOverlays()}async loadPlaylist(O,$){this.checkDisposed(),await this.playlistManager.loadPlaylist(O,$)}addToPlaylist(O,$){this.checkDisposed(),this.playlistManager.addToPlaylist(O,$)}async removeFromPlaylist(O){this.checkDisposed(),await this.playlistManager.removeFromPlaylist(O)}clearPlaylist(){this.checkDisposed(),this.playlistManager.clearPlaylist()}async next(){this.checkDisposed(),await this.playlistManager.next()}async prev(){this.checkDisposed(),await this.playlistManager.prev()}async jumpTo(O){this.checkDisposed(),await this.playlistManager.jumpTo(O)}get playlist(){return this.playlistManager.playlist}get playlistIndex(){return this.playlistManager.currentIndex}get nowPlaying(){return this.playlistManager.currentItem}get playlistMode(){return this.playlistManager.mode}set playlistMode(O){this.checkDisposed(),this.playlistManager.setMode(O)}getRendererType(){return this.playbackController.getRendererType()}async switchRenderer(O){this.checkDisposed(),await this.playbackController.switchRenderer(O)}updateCanvasDimensions(){this.checkDisposed(),this.playbackController.updateCanvasDimensions()}static getSupportedRenderers(){return H.getSupportedRenderers()}getState(){return this.state.getState()}subscribe(O){return{unsubscribe:this.state.subscribe(O)}}on(O,$){return this.emitter.on(O,$)}once(O,$){return this.emitter.once(O,$)}off(O,$){this.emitter.off(O,$)}async use(O){if(this.checkDisposed(),await this.pluginManager.install(O),O.hooks?.audio)this.playbackController.rebuildAudioGraph()}async unuse(O){this.checkDisposed(),await this.pluginManager.uninstall(O),this.playbackController.rebuildAudioGraph()}emit(O,$){let K=this.pluginManager.executeBeforeEvent(O,$);if(K?.cancel)return;let j=K?.data??$;this.emitter.emit(O,j),this.pluginManager.executeAfterEvent(O,j)}checkDisposed(){if(this.disposed)throw Error("Player has been disposed")}dispose(){if(this.disposed)return;this.disposed=!0,this.pluginManager.dispose(),this.playbackController.dispose(),this.trackManager.dispose(),this.playlistManager?.dispose(),this.sourceManager.dispose(),this.state.reset(),this.emitter.removeAllListeners()}destroy(){this.dispose(),this.playbackController.destroy()}}var oO={fromUrl(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromFile(O,$={}){return{mediaSource:O,title:$.title||O.name,poster:$.poster}},fromBlob(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromBuffer(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromUint8Array(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}},fromStream(O,$={}){return{mediaSource:O,title:$.title,poster:$.poster}}};var BO;((Z)=>{Z.MEDIA_NOT_SUPPORTED="MEDIA_NOT_SUPPORTED";Z.MEDIA_LOAD_FAILED="MEDIA_LOAD_FAILED";Z.DECODE_ERROR="DECODE_ERROR";Z.NETWORK_ERROR="NETWORK_ERROR";Z.PERMISSION_DENIED="PERMISSION_DENIED";Z.PLAYBACK_ERROR="PLAYBACK_ERROR";Z.TRACK_NOT_FOUND="TRACK_NOT_FOUND";Z.INVALID_STATE="INVALID_STATE";Z.UNKNOWN_ERROR="UNKNOWN_ERROR"})(BO||={});class M extends Error{code;details;constructor(O,$,K){super($);this.name="MediaFoxError",this.code=O,this.details=K}static mediaNotSupported(O="Media format not supported",$){return new M("MEDIA_NOT_SUPPORTED",O,$)}static mediaLoadFailed(O="Failed to load media",$){return new M("MEDIA_LOAD_FAILED",O,$)}static decodeError(O="Failed to decode media",$){return new M("DECODE_ERROR",O,$)}static networkError(O="Network error occurred",$){return new M("NETWORK_ERROR",O,$)}static permissionDenied(O="Permission denied",$){return new M("PERMISSION_DENIED",O,$)}static playbackError(O="Playback error occurred",$){return new M("PLAYBACK_ERROR",O,$)}static trackNotFound(O="Track not found",$){return new M("TRACK_NOT_FOUND",O,$)}static invalidState(O="Invalid player state",$){return new M("INVALID_STATE",O,$)}static unknownError(O="Unknown error occurred",$){return new M("UNKNOWN_ERROR",O,$)}}function dO(O,$){if(O instanceof M)return O;if(O instanceof Error)return new M("UNKNOWN_ERROR",`${$}: ${O.message}`,{originalError:O});return new M("UNKNOWN_ERROR",`${$}: ${String(O)}`,{originalError:O})}function nO(O,$=!1){let K=Math.abs(O),j=Math.floor(K/3600),Q=Math.floor(K%3600/60),V=Math.floor(K%60),J=Math.floor(K%1*1000),U="";if(O<0)U="-";if(j>0)U+=`${j}:${Q.toString().padStart(2,"0")}:${V.toString().padStart(2,"0")}`;else U+=`${Q}:${V.toString().padStart(2,"0")}`;if($)U+=`.${J.toString().padStart(3,"0")}`;return U}function sO(O){let $=O.trim().split(":").map(Number);if($.some(Number.isNaN))throw Error("Invalid time string");let K=0;if($.length===3)K=$[0]*3600+$[1]*60+$[2];else if($.length===2)K=$[0]*60+$[1];else if($.length===1)K=$[0];else throw Error("Invalid time format");return K}function iO(O,$){return Math.floor(O*$)}function aO(O,$){return O/$}function rO(O,$,K){return Math.max($,Math.min(K,O))}function tO(O,$){return O.start<$.end&&$.start<O.end}function eO(O){if(O.length===0)return[];let $=[...O].sort((j,Q)=>j.start-Q.start),K=[$[0]];for(let j=1;j<$.length;j++){let Q=K[K.length-1],V=$[j];if(V.start<=Q.end)Q.end=Math.max(Q.end,V.end);else K.push(V)}return K}function O$(O){return O.reduce(($,K)=>$+(K.end-K.start),0)}function $$(O,$){for(let K of O)if($>=K.start&&$<K.end)return K;return null}var AK="0.1.0",xK=d;export{dO as wrapError,O$ as totalBufferedDuration,iO as timeToFrame,tO as timeRangesOverlap,sO as parseTime,eO as mergeTimeRanges,aO as frameToTime,nO as formatTime,$$ as findBufferedRange,xK as default,rO as clamp,v as VideoRenderer,AK as VERSION,o as TrackManager,c as Store,I as SourcePool,u as SourceManager,oO as Source,H as RendererFactory,g as PlaybackController,M as MediaFoxError,d as MediaFox,x as EventEmitter,BO as ErrorCode,a as Compositor,p as AudioManager};
|
|
@@ -27,6 +27,9 @@ export declare class PlaybackController {
|
|
|
27
27
|
private onEnded?;
|
|
28
28
|
private onWaiting?;
|
|
29
29
|
private onPlaying?;
|
|
30
|
+
private pendingSeekTime;
|
|
31
|
+
private isSeeking;
|
|
32
|
+
private seekRequestId;
|
|
30
33
|
constructor(options?: PlaybackControllerOptions);
|
|
31
34
|
setVideoTrack(track: InputVideoTrack | null): Promise<void>;
|
|
32
35
|
/**
|
|
@@ -42,6 +45,7 @@ export declare class PlaybackController {
|
|
|
42
45
|
play(): Promise<void>;
|
|
43
46
|
pause(): void;
|
|
44
47
|
seek(time: number): Promise<void>;
|
|
48
|
+
private processPendingSeeks;
|
|
45
49
|
private startRenderLoop;
|
|
46
50
|
private stopRenderLoop;
|
|
47
51
|
private startSyncInterval;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/playback/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAM1D,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAC7C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAC,CAAyB;IAC9C,OAAO,CAAC,OAAO,CAAC,CAAa;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAC,CAAa;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/playback/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAM1D,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAC7C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAC,CAAyB;IAC9C,OAAO,CAAC,OAAO,CAAC,CAAa;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAC,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAK;gBAEd,OAAO,GAAE,yBAA8B;IAe7C,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjE;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IASjE,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBjE;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IASjE,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B,KAAK,IAAI,IAAI;IAuBP,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAkBzB,mBAAmB;IA8BjC,OAAO,CAAC,eAAe;IA2DvB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,WAAW;IASnB,cAAc,IAAI,MAAM;IAOxB,WAAW,IAAI,MAAM;IAIrB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAInC,SAAS,IAAI,OAAO;IAIpB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,MAAM;IAInB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B,OAAO,IAAI,OAAO;IAIlB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAanC,eAAe,IAAI,MAAM;IAIzB,qBAAqB,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI7D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI5C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI9C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI9C,WAAW,IAAI,OAAO;IAIhB,UAAU,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAIxG,gBAAgB,IAAI,aAAa;IAIjC,eAAe,IAAI,YAAY;IAIzB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,eAAe,IAAI,YAAY;IAI/B,sBAAsB,IAAI,IAAI;IAI9B,yBAAyB,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAIvE,2BAA2B,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAI3F,yBAAyB,CACvB,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACrF,IAAI;IAIP,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAIrC,WAAW,IAAI,QAAQ;IAIvB,cAAc,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAInD,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,IAAI;IAKpD,SAAS,IAAI,iBAAiB,GAAG,eAAe,GAAG,IAAI;IAIvD,eAAe,IAAI,IAAI;IAIvB;;;OAGG;IACH,iBAAiB,IAAI,IAAI;IAInB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B,OAAO,IAAI,IAAI;IAaf,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -23,6 +23,8 @@ export interface VideoRendererOptions {
|
|
|
23
23
|
}
|
|
24
24
|
export declare class VideoRenderer {
|
|
25
25
|
private canvas;
|
|
26
|
+
private seekDebounceTimer;
|
|
27
|
+
private pendingSeekTime;
|
|
26
28
|
private canvasSink;
|
|
27
29
|
private sampleSink;
|
|
28
30
|
private options;
|
|
@@ -31,6 +33,7 @@ export declare class VideoRenderer {
|
|
|
31
33
|
private nextFrame;
|
|
32
34
|
private disposed;
|
|
33
35
|
private renderingId;
|
|
36
|
+
private currentSeekId;
|
|
34
37
|
private renderer;
|
|
35
38
|
private rendererType;
|
|
36
39
|
private onRendererChange?;
|
|
@@ -62,7 +65,7 @@ export declare class VideoRenderer {
|
|
|
62
65
|
setCanvas(canvas: HTMLCanvasElement | OffscreenCanvas): Promise<void>;
|
|
63
66
|
setVideoTrack(track: InputVideoTrack): Promise<void>;
|
|
64
67
|
seek(timestamp: number): Promise<void>;
|
|
65
|
-
updateFrame(currentTime: number): {
|
|
68
|
+
updateFrame(currentTime: number, isSeeking?: boolean): {
|
|
66
69
|
frameUpdated: boolean;
|
|
67
70
|
isStarving: boolean;
|
|
68
71
|
};
|
|
@@ -118,6 +121,7 @@ export declare class VideoRenderer {
|
|
|
118
121
|
clearIterators(): Promise<void>;
|
|
119
122
|
private disposeVideoResources;
|
|
120
123
|
dispose(): void;
|
|
124
|
+
private cancelPendingDebouncedSeek;
|
|
121
125
|
}
|
|
122
126
|
export {};
|
|
123
127
|
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/playback/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAmB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACrH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAa,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGrE,gBAAgB;AAChB,UAAU,mBAAmB;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;CAClC;AAKD,gBAAgB;AAChB,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAEnG;AAED,gBAAgB;AAChB,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,eAAe,GAAG,mBAAmB,GAAG,SAAS,CAMlG;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAoD;IAClE,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,aAAa,CAA6D;IAClF,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,gBAAgB,CAAC,CAA+B;IACxD,OAAO,CAAC,kBAAkB,CAAC,CAAiD;IAC5E,OAAO,CAAC,gBAAgB,CAAC,CAA+E;IACxG,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,UAAU,CAAyC;IAC3D,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAK;IAEzB,OAAO,CAAC,iBAAiB,CAA8C;gBAE3D,OAAO,GAAE,oBAAyB;IAkC9C,OAAO,CAAC,mBAAmB;IA0F3B,OAAO,CAAC,4BAA4B;IAoCpC,OAAO,CAAC,6BAA6B;IA0BrC,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,yBAAyB;YAWnB,kBAAkB;IAwD1B,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCrE,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuJpD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/playback/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAmB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACrH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAa,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGrE,gBAAgB;AAChB,UAAU,mBAAmB;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;CAClC;AAKD,gBAAgB;AAChB,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAEnG;AAED,gBAAgB;AAChB,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,eAAe,GAAG,mBAAmB,GAAG,SAAS,CAMlG;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAoD;IAClE,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,aAAa,CAA6D;IAClF,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,gBAAgB,CAAC,CAA+B;IACxD,OAAO,CAAC,kBAAkB,CAAC,CAAiD;IAC5E,OAAO,CAAC,gBAAgB,CAAC,CAA+E;IACxG,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,UAAU,CAAyC;IAC3D,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAK;IAEzB,OAAO,CAAC,iBAAiB,CAA8C;gBAE3D,OAAO,GAAE,oBAAyB;IAkC9C,OAAO,CAAC,mBAAmB;IA0F3B,OAAO,CAAC,4BAA4B;IAoCpC,OAAO,CAAC,6BAA6B;IA0BrC,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,yBAAyB;YAWnB,kBAAkB;IAwD1B,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCrE,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuJpD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsL5C,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,GAAE,OAAe,GAAG;QAAE,YAAY,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE;YA6E9F,cAAc;IAqC5B,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,sBAAsB;IAqD9B,OAAO,CAAC,eAAe;IA0BvB;;;;OAIG;IACH,eAAe,IAAI,IAAI;IAqBvB,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,oBAAoB;IAQtB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK5D,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAK3D,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAiBjG,UAAU,CACd,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;QACjC,OAAO,CAAC,EAAE,MAAM,CAAC;KACb,GACL,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA8BvB,eAAe,IAAI,aAAa,GAAG,IAAI;IAIvC,YAAY,IAAI,aAAa,GAAG,IAAI;IAIpC,eAAe,IAAI,YAAY;IAI/B,SAAS,IAAI,iBAAiB,GAAG,eAAe,GAAG,IAAI;IAIvD;;;;OAIG;IACH,sBAAsB,IAAI,IAAI;IAaxB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAqGvD,yBAAyB,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAUvE,2BAA2B,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAI3F,yBAAyB,CACvB,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACrF,IAAI;IAIP,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAmBrC,WAAW,IAAI,QAAQ;IAIvB,cAAc,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAQnD,OAAO,CAAC,oBAAoB;IAM5B,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,IAAI;IAIpD,MAAM,CAAC,qBAAqB,IAAI,YAAY,EAAE;IAI9C;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;YAiBvB,qBAAqB;IAqBnC,OAAO,IAAI,IAAI;IA+Bf,OAAO,CAAC,0BAA0B;CAQnC"}
|
package/package.json
CHANGED
|
@@ -436,8 +436,7 @@ export class Compositor {
|
|
|
436
436
|
const transform = layer.transform;
|
|
437
437
|
const sourceWidth = layer.source.width ?? this.width;
|
|
438
438
|
const sourceHeight = layer.source.height ?? this.height;
|
|
439
|
-
const effectiveFitMode =
|
|
440
|
-
layer.fitMode === undefined || layer.fitMode === 'auto' ? this.fitMode : layer.fitMode;
|
|
439
|
+
const effectiveFitMode = layer.fitMode === undefined || layer.fitMode === 'auto' ? this.fitMode : layer.fitMode;
|
|
441
440
|
|
|
442
441
|
let fittedWidth = sourceWidth;
|
|
443
442
|
let fittedHeight = sourceHeight;
|
|
@@ -33,6 +33,9 @@ export class PlaybackController {
|
|
|
33
33
|
private onEnded?: () => void;
|
|
34
34
|
private onWaiting?: () => void;
|
|
35
35
|
private onPlaying?: () => void;
|
|
36
|
+
private pendingSeekTime: number | null = null;
|
|
37
|
+
private isSeeking = false;
|
|
38
|
+
private seekRequestId = 0;
|
|
36
39
|
|
|
37
40
|
constructor(options: PlaybackControllerOptions = {}) {
|
|
38
41
|
this.videoRenderer = new VideoRenderer({
|
|
@@ -160,16 +163,48 @@ export class PlaybackController {
|
|
|
160
163
|
const clampedTime = Math.max(0, Math.min(time, this.duration));
|
|
161
164
|
this.currentTime = clampedTime;
|
|
162
165
|
|
|
163
|
-
//
|
|
164
|
-
await this.videoRenderer.seek(clampedTime);
|
|
165
|
-
|
|
166
|
-
// Seek audio
|
|
167
|
-
await this.audioManager.seek(clampedTime);
|
|
168
|
-
|
|
169
|
-
// Notify time update
|
|
166
|
+
// Update UI immediately
|
|
170
167
|
if (this.onTimeUpdate) {
|
|
171
168
|
this.onTimeUpdate(this.currentTime);
|
|
172
169
|
}
|
|
170
|
+
|
|
171
|
+
// Latest-only seek behavior: keep only the newest target.
|
|
172
|
+
this.seekRequestId++;
|
|
173
|
+
this.pendingSeekTime = clampedTime;
|
|
174
|
+
|
|
175
|
+
if (!this.isSeeking) {
|
|
176
|
+
await this.processPendingSeeks();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private async processPendingSeeks(): Promise<void> {
|
|
181
|
+
if (this.isSeeking) return;
|
|
182
|
+
this.isSeeking = true;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
while (this.pendingSeekTime !== null) {
|
|
186
|
+
const targetTime = this.pendingSeekTime;
|
|
187
|
+
this.pendingSeekTime = null;
|
|
188
|
+
const requestIdAtStart = this.seekRequestId;
|
|
189
|
+
|
|
190
|
+
// Seek video first so frame display responds quickly.
|
|
191
|
+
await this.videoRenderer.seek(targetTime);
|
|
192
|
+
|
|
193
|
+
// If a newer seek arrived while video seek was running, skip this audio seek.
|
|
194
|
+
if (requestIdAtStart !== this.seekRequestId) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
await this.audioManager.seek(targetTime);
|
|
199
|
+
}
|
|
200
|
+
} finally {
|
|
201
|
+
this.isSeeking = false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Handle race where a seek arrived after loop exit but before isSeeking was reset.
|
|
205
|
+
if (this.pendingSeekTime !== null) {
|
|
206
|
+
await this.processPendingSeeks();
|
|
207
|
+
}
|
|
173
208
|
}
|
|
174
209
|
|
|
175
210
|
private startRenderLoop(): void {
|
|
@@ -182,7 +217,9 @@ export class PlaybackController {
|
|
|
182
217
|
if (!this.playing) return;
|
|
183
218
|
|
|
184
219
|
// Get accurate time from audio manager if available
|
|
185
|
-
if (this.
|
|
220
|
+
if (this.isSeeking) {
|
|
221
|
+
// Keep requested seek time stable while async seek operations settle.
|
|
222
|
+
} else if (this.audioManager.isPlaying()) {
|
|
186
223
|
this.currentTime = this.audioManager.getCurrentTime();
|
|
187
224
|
} else {
|
|
188
225
|
// Fallback to manual time tracking
|
|
@@ -199,7 +236,7 @@ export class PlaybackController {
|
|
|
199
236
|
}
|
|
200
237
|
|
|
201
238
|
// Update video frame synchronously
|
|
202
|
-
const { isStarving } = this.videoRenderer.updateFrame(this.currentTime);
|
|
239
|
+
const { isStarving } = this.videoRenderer.updateFrame(this.currentTime, this.isSeeking);
|
|
203
240
|
|
|
204
241
|
// Handle buffering state changes
|
|
205
242
|
if (isStarving && !this.isWaiting) {
|
|
@@ -267,7 +304,7 @@ export class PlaybackController {
|
|
|
267
304
|
}
|
|
268
305
|
|
|
269
306
|
getCurrentTime(): number {
|
|
270
|
-
if (this.playing && this.audioManager.isPlaying()) {
|
|
307
|
+
if (this.playing && !this.isSeeking && this.audioManager.isPlaying()) {
|
|
271
308
|
return this.audioManager.getCurrentTime();
|
|
272
309
|
}
|
|
273
310
|
return this.currentTime;
|
|
@@ -427,10 +464,16 @@ export class PlaybackController {
|
|
|
427
464
|
// Reset playback rate to default
|
|
428
465
|
this.playbackRate = 1;
|
|
429
466
|
this.lastFrameTime = 0;
|
|
467
|
+
this.seekRequestId++;
|
|
468
|
+
this.pendingSeekTime = null;
|
|
469
|
+
this.isSeeking = false;
|
|
430
470
|
}
|
|
431
471
|
|
|
432
472
|
dispose(): void {
|
|
433
473
|
this.pause();
|
|
474
|
+
this.seekRequestId++;
|
|
475
|
+
this.pendingSeekTime = null;
|
|
476
|
+
this.isSeeking = false;
|
|
434
477
|
this.videoRenderer.dispose();
|
|
435
478
|
this.audioManager.dispose();
|
|
436
479
|
this.onTimeUpdate = undefined;
|
package/src/playback/renderer.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface VideoRendererOptions {
|
|
|
40
40
|
|
|
41
41
|
export class VideoRenderer {
|
|
42
42
|
private canvas: HTMLCanvasElement | OffscreenCanvas | null = null;
|
|
43
|
+
private seekDebounceTimer: number | null = null;
|
|
44
|
+
private pendingSeekTime: number | null = null;
|
|
43
45
|
private canvasSink: CanvasSink | null = null;
|
|
44
46
|
private sampleSink: VideoSampleSink | null = null;
|
|
45
47
|
private options: VideoRendererOptions;
|
|
@@ -48,6 +50,7 @@ export class VideoRenderer {
|
|
|
48
50
|
private nextFrame: WrappedCanvas | null = null;
|
|
49
51
|
private disposed = false;
|
|
50
52
|
private renderingId = 0;
|
|
53
|
+
private currentSeekId = 0;
|
|
51
54
|
private renderer: IRenderer | null = null;
|
|
52
55
|
private rendererType: RendererType = 'canvas2d';
|
|
53
56
|
private onRendererChange?: (type: RendererType) => void;
|
|
@@ -546,6 +549,8 @@ export class VideoRenderer {
|
|
|
546
549
|
}
|
|
547
550
|
|
|
548
551
|
async seek(timestamp: number): Promise<void> {
|
|
552
|
+
this.cancelPendingDebouncedSeek();
|
|
553
|
+
|
|
549
554
|
if (!this.canvasSink) {
|
|
550
555
|
return;
|
|
551
556
|
}
|
|
@@ -553,6 +558,10 @@ export class VideoRenderer {
|
|
|
553
558
|
this.renderingId++;
|
|
554
559
|
const currentRenderingId = this.renderingId;
|
|
555
560
|
|
|
561
|
+
// Increment seek ID to cancel any in-progress seek
|
|
562
|
+
this.currentSeekId++;
|
|
563
|
+
const seekId = this.currentSeekId;
|
|
564
|
+
|
|
556
565
|
// Dispose current iterator
|
|
557
566
|
if (this.frameIterator) {
|
|
558
567
|
try {
|
|
@@ -563,47 +572,165 @@ export class VideoRenderer {
|
|
|
563
572
|
this.frameIterator = null;
|
|
564
573
|
}
|
|
565
574
|
|
|
566
|
-
//
|
|
567
|
-
|
|
568
|
-
this.
|
|
575
|
+
// Clear current frames to ensure we don't show stale data
|
|
576
|
+
this.currentFrame = null;
|
|
577
|
+
this.nextFrame = null;
|
|
569
578
|
|
|
570
579
|
try {
|
|
571
|
-
//
|
|
572
|
-
|
|
573
|
-
|
|
580
|
+
// Strategy: Look backwards from the seek time to find the frame that should be displayed
|
|
581
|
+
// Video frames are typically decoded from keyframes, so we need to find the frame
|
|
582
|
+
// whose timestamp is at or just before the seek time
|
|
583
|
+
let targetFrame: WrappedCanvas | null = null;
|
|
584
|
+
let nextFrame: WrappedCanvas | null = null;
|
|
585
|
+
const SEEK_FRAME_MATCH_TOLERANCE = 0.001;
|
|
586
|
+
|
|
587
|
+
// First, try to get a frame slightly before the target to find the correct display frame
|
|
588
|
+
const lookbehindTime = Math.max(0, timestamp - 2);
|
|
589
|
+
const lookbehindIterator = this.canvasSink.canvases(lookbehindTime);
|
|
590
|
+
|
|
591
|
+
try {
|
|
592
|
+
// Iterate through frames until we find the one that should be displayed at target time
|
|
593
|
+
// This is the frame with timestamp <= target and next frame has timestamp > target
|
|
594
|
+
let previousFrame: WrappedCanvas | null = null;
|
|
595
|
+
|
|
596
|
+
for await (const frame of lookbehindIterator) {
|
|
597
|
+
// Check if a new seek has been triggered - if so, abort this one
|
|
598
|
+
if (seekId !== this.currentSeekId) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (frame.timestamp <= timestamp) {
|
|
603
|
+
// This frame is at or before the target - it's a candidate
|
|
604
|
+
previousFrame = frame;
|
|
605
|
+
} else {
|
|
606
|
+
// This frame is after the target, so the previous frame is the one we want
|
|
607
|
+
nextFrame = frame;
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Safety limit - don't iterate too far
|
|
612
|
+
if (frame.timestamp > timestamp + 5) {
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
targetFrame = previousFrame;
|
|
618
|
+
} catch {
|
|
619
|
+
// Iterator error
|
|
620
|
+
} finally {
|
|
621
|
+
try {
|
|
622
|
+
await lookbehindIterator.return();
|
|
623
|
+
} catch {
|
|
624
|
+
// Iterator may already be closed
|
|
625
|
+
}
|
|
626
|
+
}
|
|
574
627
|
|
|
575
|
-
if
|
|
628
|
+
// Check again if a new seek was triggered during iteration
|
|
629
|
+
if (seekId !== this.currentSeekId) {
|
|
576
630
|
return;
|
|
577
631
|
}
|
|
578
632
|
|
|
579
|
-
|
|
580
|
-
|
|
633
|
+
// If we didn't find a frame looking backwards, try forward
|
|
634
|
+
if (!targetFrame) {
|
|
635
|
+
const forwardIterator = this.canvasSink.canvases(timestamp);
|
|
636
|
+
try {
|
|
637
|
+
const result = await forwardIterator.next();
|
|
638
|
+
// Check for cancellation
|
|
639
|
+
if (seekId !== this.currentSeekId) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (result.value) {
|
|
643
|
+
targetFrame = result.value;
|
|
644
|
+
}
|
|
645
|
+
} catch {
|
|
646
|
+
// Iterator error
|
|
647
|
+
} finally {
|
|
648
|
+
try {
|
|
649
|
+
await forwardIterator.return();
|
|
650
|
+
} catch {
|
|
651
|
+
// Iterator may already be closed
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
581
655
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
656
|
+
if (currentRenderingId !== this.renderingId || this.disposed) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
585
659
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
660
|
+
// Final cancellation check before setting up the frame
|
|
661
|
+
if (seekId !== this.currentSeekId) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Set up the frame if we found one
|
|
666
|
+
if (targetFrame) {
|
|
667
|
+
this.currentFrame = targetFrame;
|
|
668
|
+
this.nextFrame = nextFrame;
|
|
669
|
+
|
|
670
|
+
// Draw the frame immediately if canvas has dimensions
|
|
671
|
+
if (targetFrame.canvas.width > 0 && targetFrame.canvas.height > 0) {
|
|
672
|
+
this.renderFrame(targetFrame);
|
|
589
673
|
} else {
|
|
590
|
-
this.retryUntilCanvasReady(
|
|
674
|
+
this.retryUntilCanvasReady(targetFrame, () => this.renderFrame(targetFrame), 30);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Set up iterator for continued playback from current frame position
|
|
678
|
+
const playbackIterator = this.canvasSink.canvases(targetFrame.timestamp);
|
|
679
|
+
this.frameIterator = playbackIterator;
|
|
680
|
+
|
|
681
|
+
let firstFrameAfterRestart: WrappedCanvas | null = null;
|
|
682
|
+
try {
|
|
683
|
+
const first = await playbackIterator.next();
|
|
684
|
+
// Check for cancellation
|
|
685
|
+
if (seekId !== this.currentSeekId) {
|
|
686
|
+
try {
|
|
687
|
+
await playbackIterator.return();
|
|
688
|
+
} catch {
|
|
689
|
+
// Iterator may already be closed
|
|
690
|
+
}
|
|
691
|
+
if (this.frameIterator === playbackIterator) {
|
|
692
|
+
this.frameIterator = null;
|
|
693
|
+
}
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
firstFrameAfterRestart = first.value ?? null;
|
|
697
|
+
} catch {
|
|
698
|
+
// Iterator error
|
|
591
699
|
}
|
|
592
|
-
}
|
|
593
700
|
|
|
594
|
-
|
|
595
|
-
|
|
701
|
+
if (currentRenderingId !== this.renderingId || this.disposed) {
|
|
702
|
+
try {
|
|
703
|
+
await playbackIterator.return();
|
|
704
|
+
} catch {
|
|
705
|
+
// Iterator may already be closed
|
|
706
|
+
}
|
|
707
|
+
if (this.frameIterator === playbackIterator) {
|
|
708
|
+
this.frameIterator = null;
|
|
709
|
+
}
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
596
712
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
713
|
+
// Only skip the first iterator frame if it matches the currently rendered frame.
|
|
714
|
+
// If it is already ahead, keep it as the next frame so playback doesn't jump.
|
|
715
|
+
if (
|
|
716
|
+
firstFrameAfterRestart &&
|
|
717
|
+
firstFrameAfterRestart.timestamp > targetFrame.timestamp + SEEK_FRAME_MATCH_TOLERANCE &&
|
|
718
|
+
(!this.nextFrame || firstFrameAfterRestart.timestamp < this.nextFrame.timestamp)
|
|
719
|
+
) {
|
|
720
|
+
this.nextFrame = firstFrameAfterRestart;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Get next frame if we don't have one
|
|
724
|
+
if (!this.nextFrame) {
|
|
725
|
+
void this.fetchNextFrame();
|
|
726
|
+
}
|
|
600
727
|
}
|
|
601
728
|
} catch {
|
|
602
729
|
// Iterator was closed or disposed during seek
|
|
603
730
|
}
|
|
604
731
|
}
|
|
605
732
|
|
|
606
|
-
updateFrame(currentTime: number): { frameUpdated: boolean; isStarving: boolean } {
|
|
733
|
+
updateFrame(currentTime: number, isSeeking: boolean = false): { frameUpdated: boolean; isStarving: boolean } {
|
|
607
734
|
const result = this.updateFrameResult;
|
|
608
735
|
|
|
609
736
|
if (this.disposed) {
|
|
@@ -612,6 +739,16 @@ export class VideoRenderer {
|
|
|
612
739
|
return result;
|
|
613
740
|
}
|
|
614
741
|
|
|
742
|
+
// Special case: If we have a current frame but it's timestamp is far in the future,
|
|
743
|
+
// it means we seeked to a frame that's ahead of playback. Don't advance frames
|
|
744
|
+
// until currentTime catches up to the current frame's timestamp.
|
|
745
|
+
if (this.currentFrame && this.currentFrame.timestamp > currentTime + 0.1) {
|
|
746
|
+
// Current frame is in the future, don't update yet
|
|
747
|
+
result.frameUpdated = false;
|
|
748
|
+
result.isStarving = false;
|
|
749
|
+
return result;
|
|
750
|
+
}
|
|
751
|
+
|
|
615
752
|
// If we don't have a next frame, request one (if iterator exists)
|
|
616
753
|
// This is frame starvation - we're playing but have no frame ready
|
|
617
754
|
if (!this.nextFrame) {
|
|
@@ -623,8 +760,32 @@ export class VideoRenderer {
|
|
|
623
760
|
return result;
|
|
624
761
|
}
|
|
625
762
|
|
|
763
|
+
// Check if we're way behind (>1 second) - trigger debounced seek to catch up
|
|
764
|
+
if (this.nextFrame.timestamp < currentTime - 1.0) {
|
|
765
|
+
// We're more than 1 second behind - need to seek to catch up
|
|
766
|
+
this.pendingSeekTime = currentTime;
|
|
767
|
+
|
|
768
|
+
if (this.seekDebounceTimer === null && !isSeeking) {
|
|
769
|
+
this.seekDebounceTimer = window.setTimeout(() => {
|
|
770
|
+
const seekTime = this.pendingSeekTime;
|
|
771
|
+
this.seekDebounceTimer = null;
|
|
772
|
+
this.pendingSeekTime = null;
|
|
773
|
+
|
|
774
|
+
if (seekTime !== null && !this.disposed) {
|
|
775
|
+
void this.seek(seekTime);
|
|
776
|
+
}
|
|
777
|
+
}, 100); // Wait 100ms before seeking to batch multiple "behind" detections
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
result.frameUpdated = false;
|
|
781
|
+
result.isStarving = false;
|
|
782
|
+
return result;
|
|
783
|
+
}
|
|
784
|
+
|
|
626
785
|
// Check if the current playback time has caught up to the next frame
|
|
627
|
-
|
|
786
|
+
// Use a small tolerance to handle timing precision issues
|
|
787
|
+
const FRAME_DISPLAY_TOLERANCE = 0.016; // ~1 frame at 60fps
|
|
788
|
+
if (this.nextFrame.timestamp <= currentTime + FRAME_DISPLAY_TOLERANCE) {
|
|
628
789
|
// Store the frame first
|
|
629
790
|
this.currentFrame = this.nextFrame;
|
|
630
791
|
this.nextFrame = null;
|
|
@@ -646,22 +807,37 @@ export class VideoRenderer {
|
|
|
646
807
|
return result;
|
|
647
808
|
}
|
|
648
809
|
|
|
649
|
-
private async fetchNextFrame(): Promise<void> {
|
|
810
|
+
private async fetchNextFrame(currentTime?: number): Promise<void> {
|
|
650
811
|
const iterator = this.frameIterator;
|
|
651
812
|
if (!iterator || this.disposed) return;
|
|
652
813
|
|
|
653
814
|
const currentRenderingId = this.renderingId;
|
|
654
815
|
|
|
655
816
|
try {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
817
|
+
let frame: WrappedCanvas | null = null;
|
|
818
|
+
let result = await iterator.next();
|
|
819
|
+
frame = result.value ?? null;
|
|
659
820
|
|
|
660
821
|
if (!frame || currentRenderingId !== this.renderingId || this.disposed) {
|
|
661
822
|
return;
|
|
662
823
|
}
|
|
663
824
|
|
|
664
|
-
//
|
|
825
|
+
// If we have currentTime and this frame is way behind, skip frames
|
|
826
|
+
if (currentTime !== undefined) {
|
|
827
|
+
const SKIP_THRESHOLD = 0.1; // Skip if more than 100ms behind
|
|
828
|
+
|
|
829
|
+
while (frame && frame.timestamp < currentTime - SKIP_THRESHOLD) {
|
|
830
|
+
result = await iterator.next();
|
|
831
|
+
const nextFrame = result.value ?? null;
|
|
832
|
+
|
|
833
|
+
if (!nextFrame || currentRenderingId !== this.renderingId || this.disposed) {
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
frame = nextFrame;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
665
841
|
this.nextFrame = frame;
|
|
666
842
|
} catch {
|
|
667
843
|
// Iterator was closed or disposed during fetch
|
|
@@ -1110,6 +1286,7 @@ export class VideoRenderer {
|
|
|
1110
1286
|
*/
|
|
1111
1287
|
async clearIterators(): Promise<void> {
|
|
1112
1288
|
this.renderingId++;
|
|
1289
|
+
this.cancelPendingDebouncedSeek();
|
|
1113
1290
|
|
|
1114
1291
|
if (this.frameIterator) {
|
|
1115
1292
|
try {
|
|
@@ -1127,6 +1304,7 @@ export class VideoRenderer {
|
|
|
1127
1304
|
private async disposeVideoResources(): Promise<void> {
|
|
1128
1305
|
this.disposed = true;
|
|
1129
1306
|
this.renderingId++;
|
|
1307
|
+
this.cancelPendingDebouncedSeek();
|
|
1130
1308
|
|
|
1131
1309
|
if (this.frameIterator) {
|
|
1132
1310
|
try {
|
|
@@ -1147,6 +1325,7 @@ export class VideoRenderer {
|
|
|
1147
1325
|
dispose(): void {
|
|
1148
1326
|
this.disposed = true;
|
|
1149
1327
|
this.renderingId++;
|
|
1328
|
+
this.cancelPendingDebouncedSeek();
|
|
1150
1329
|
|
|
1151
1330
|
if (this.frameIterator) {
|
|
1152
1331
|
// fire-and-forget – safe cleanup without throwing
|
|
@@ -1173,4 +1352,13 @@ export class VideoRenderer {
|
|
|
1173
1352
|
this.onRendererFallback = undefined;
|
|
1174
1353
|
// Track reference cleared through dispose of iterators
|
|
1175
1354
|
}
|
|
1355
|
+
|
|
1356
|
+
private cancelPendingDebouncedSeek(): void {
|
|
1357
|
+
if (this.seekDebounceTimer !== null) {
|
|
1358
|
+
clearTimeout(this.seekDebounceTimer);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
this.seekDebounceTimer = null;
|
|
1362
|
+
this.pendingSeekTime = null;
|
|
1363
|
+
}
|
|
1176
1364
|
}
|