@mediafox/core 1.2.10 → 1.2.11
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-pool.d.ts","sourceRoot":"","sources":["../../src/compositor/source-pool.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAc,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"source-pool.d.ts","sourceRoot":"","sources":["../../src/compositor/source-pool.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAc,MAAM,SAAS,CAAC;AAyPrF,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,MAAM,CAAK;gBAEP,YAAY,CAAC,EAAE,YAAY;IAIvC,OAAO,CAAC,UAAU;IAIZ,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAyEhG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkClE,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAyCtG,OAAO,CAAC,WAAW;IA0BnB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAInD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI9B,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAUjC,aAAa,IAAI,gBAAgB,EAAE;IAInC,KAAK,IAAI,IAAI;IAOb,OAAO,IAAI,IAAI;CAOhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
class O{events=new Map;maxListeners;captureRejections;emitCache=[];constructor(o={}){this.maxListeners=o.maxListeners??10,this.captureRejections=o.captureRejections??!1}on(o,C){if(!this.events.has(o))this.events.set(o,new Set);let W=this.events.get(o);if(!W)return()=>{};if(W.size>=this.maxListeners)console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${W.size} ${String(o)} listeners added. Use emitter.setMaxListeners() to increase limit`);let w=C;return W.add(w),()=>{W.delete(w)}}once(o,C){let W=(w)=>{this.off(o,W),C(w)};return this.on(o,W)}off(o,C){let W=this.events.get(o);if(!W)return;if(C){let w=C;W.delete(w)}else W.clear()}emit(o,C){let W=this.events.get(o);if(!W||W.size===0)return;let w=this.emitCache;w.length=0;for(let k of W)w.push(k);for(let k=0;k<w.length;k++){let P=w[k];try{let r=P(C);if(this.captureRejections&&N(r))this.handlePromiseRejection(r)}catch(r){if(this.captureRejections&&this.events.has("error"))this.emit("error",r);else throw r}}}handlePromiseRejection(o){o.catch((C)=>{if(this.events.has("error"))this.emit("error",C);else throw C})}removeAllListeners(o){if(o)this.events.delete(o);else this.events.clear()}setMaxListeners(o){this.maxListeners=o}getMaxListeners(){return this.maxListeners}listeners(o){let C=this.events.get(o);return C?Array.from(C):[]}listenerCount(o){let C=this.events.get(o);return C?C.size:0}eventNames(){return Array.from(this.events.keys())}}function N(o){if(!o||typeof o!=="object"&&typeof o!=="function")return!1;let C=o;return typeof C.then==="function"&&typeof C.catch==="function"}class J{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 C=globalThis,W=C.AudioContext||C.webkitAudioContext;if(!W)throw Error("AudioContext is not supported in this environment");this.audioContext=new W}this.masterGain=this.audioContext.createGain(),this.masterGain.connect(this.audioContext.destination)}registerSource(o,C){if(this.disposed)return;let W=this.audioContext.createGain(),w=this.audioContext.createStereoPanner();W.connect(w),w.connect(this.masterGain),this.activeSources.set(o.id,{sourceId:o.id,bufferSink:C,iterator:null,gainNode:W,panNode:w,queuedNodes:new Set,volume:1,pan:0,muted:!1,startSourceTime:0,currentSourceTime:0,iteratorStartTime:0,lastScheduledTime:0})}unregisterSource(o){let C=this.activeSources.get(o);if(!C)return;this.stopSourceAudio(C),C.gainNode.disconnect(),C.panNode.disconnect(),this.activeSources.delete(o)}hasSource(o){return this.activeSources.has(o)}processAudioLayers(o,C){if(this.disposed||!this.playing)return;let W=this.activeSourceIdsScratch;W.clear();for(let w of o){let k=w.source.id;W.add(k);let P=this.activeSources.get(k);if(!P)continue;let r=w.volume??1,p=w.pan??0,n=w.muted??!1,t=w.sourceTime??C;if(P.volume!==r||P.muted!==n)P.volume=r,P.muted=n,P.gainNode.gain.value=n?0:r*r;if(P.pan!==p)P.pan=p,P.panNode.pan.value=Math.max(-1,Math.min(1,p));if(Math.abs(t-P.currentSourceTime)>0.5&&P.iterator!==null)this.restartSourceIterator(P,t);P.currentSourceTime=t}for(let[w,k]of this.activeSources)if(!W.has(w)&&k.iterator!==null)this.stopSourceAudio(k)}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}startSourcePlayback(o,C){let W=this.activeSources.get(o);if(!W||W.iterator!==null)return;this.restartSourceIterator(W,C)}restartSourceIterator(o,C){this.stopSourceAudio(o),o.startSourceTime=C,o.currentSourceTime=C,o.iteratorStartTime=this.audioContext.currentTime,o.iterator=o.bufferSink.buffers(C),o.lastScheduledTime=C,this.scheduleSourceBuffers(o,this.playbackId)}async scheduleSourceBuffers(o,C){let W=o.iterator;if(!W)return;try{for await(let{buffer:w,timestamp:k}of W){if(C!==this.playbackId||this.disposed||!this.playing)break;let P=this.audioContext.createBufferSource();P.buffer=w,P.connect(o.gainNode);let r=k-o.startSourceTime,p=o.iteratorStartTime+r;if(p>=this.audioContext.currentTime)P.start(p);else{let E=this.audioContext.currentTime-p;if(E<w.duration)P.start(this.audioContext.currentTime,E);else continue}o.queuedNodes.add(P),P.onended=()=>{o.queuedNodes.delete(P)},o.lastScheduledTime=k;let n=this.audioContext.currentTime-o.iteratorStartTime;if(k-o.startSourceTime-n>1)await this.waitForCatchup(o,k)}}catch{}}async waitForCatchup(o,C){return new Promise((W)=>{let w=setInterval(()=>{if(!this.playing||this.disposed){clearInterval(w),W();return}let k=this.audioContext.currentTime-o.iteratorStartTime;if(C-o.startSourceTime-k<1)clearInterval(w),W()},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 C=this.playing;if(this.pause(),this.pauseTime=o,this.startMediaTime=o,C)await this.play(o)}stopSourceAudio(o){for(let C of o.queuedNodes)try{C.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 X,AudioBufferSink as Z,BlobSource as Y,BufferSource as j,CanvasSink as L,FilePathSource as A,Input as I,ReadableStreamSource as a,UrlSource as S}from"mediabunny";class b{cache=new Map;maxSize;constructor(o){this.maxSize=o}get(o){let C=this.cache.get(o);if(C!==void 0)this.cache.delete(o),this.cache.set(o,C);return C}set(o,C){if(this.cache.has(o))this.cache.delete(o);else if(this.cache.size>=this.maxSize){let W=this.cache.keys().next().value;if(W!==void 0)this.cache.delete(W)}this.cache.set(o,C)}clear(){this.cache.clear()}get size(){return this.cache.size}}class B{id;type="video";duration;width;height;data;disposed=!1;constructor(o,C,W,w,k){this.id=o,this.data=C,this.duration=W,this.width=w,this.height=k}async getFrameAt(o){if(this.disposed)return null;let C=this.data.frameIntervalMs,W=Math.floor(o*1000/C)*C,w=this.data.frameCache.get(W);if(w)return w.canvas;try{let k=await this.data.canvasSink.getCanvas(o);if(!k)return null;return this.data.frameCache.set(W,k),k.canvas}catch{return null}}getAudioBufferSink(){return this.data.audioBufferSink}hasAudio(){return this.data.audioBufferSink!==null}clearCache(){this.data.frameCache.clear()}dispose(){if(this.disposed)return;this.disposed=!0,this.data.frameCache.clear(),this.data.input.dispose()}}class K{id;type="image";duration=1/0;width;height;data;disposed=!1;constructor(o,C){this.id=o,this.data=C,this.width=C.image.width,this.height=C.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 V{id;type="audio";duration;width=0;height=0;data;disposed=!1;constructor(o,C,W){this.id=o,this.data=C,this.duration=W}async getFrameAt(o){return null}getAudioBufferSink(){return this.data.audioBufferSink}dispose(){if(this.disposed)return;this.disposed=!0,this.data.input.dispose()}}class Q{sources=new Map;audioContext=null;nextId=0;constructor(o){this.audioContext=o??null}generateId(){return`source_${this.nextId++}`}async loadVideo(o,C={}){let W=C.id??this.generateId(),w=this.createInput(o),k=await w.getVideoTracks();if(k.length===0)throw w.dispose(),Error("Source has no video track");let P=k[0];if(!await P.canDecode())throw w.dispose(),Error(`Cannot decode video track with codec: ${P.codec}`);let p=new L(P,{poolSize:4}),n=await P.computeDuration(),t=30;try{let F=await P.computePacketStats(100);if(F.averagePacketRate>0)t=F.averagePacketRate}catch{}let E=1000/t,h=P.displayWidth*P.displayHeight,q=h>2073600?15:h>921600?30:60,g=null,M=null;try{let F=await w.getAudioTracks();if(F.length>0){if(g=F[0],await g.canDecode())M=new Z(g)}}catch{}let $=new B(W,{input:w,videoTrack:P,canvasSink:p,frameCache:new b(q),frameIntervalMs:E,audioTrack:g,audioBufferSink:M},n,P.displayWidth,P.displayHeight);return this.sources.set(W,$),$}async loadImage(o){let C=this.generateId(),W;if(typeof o!=="string")W=await createImageBitmap(o);else if(typeof Image>"u"){let k=await fetch(o);if(!k.ok)throw Error(`Failed to load image: ${o}`);let P=await k.blob();W=await createImageBitmap(P)}else W=await new Promise((k,P)=>{let r=new Image;r.onload=()=>k(r),r.onerror=()=>P(Error(`Failed to load image: ${o}`)),r.crossOrigin="anonymous",r.src=o});let w=new K(C,{image:W});return this.sources.set(C,w),w}async loadAudio(o,C={}){let W=C.id??this.generateId(),w=this.createInput(o),k=await w.getAudioTracks();if(k.length===0)throw w.dispose(),Error("Source has no audio track");let P=k[0];if(!await P.canDecode())throw w.dispose(),Error(`Cannot decode audio track with codec: ${P.codec}`);let p=await P.computeDuration(),n=new Z(P),t=new V(W,{input:w,audioTrack:P,audioBufferSink:n},p);return this.sources.set(W,t),t}createInput(o){let C;if(o instanceof File||o instanceof Blob)C=new Y(o);else if(o instanceof ArrayBuffer||o instanceof Uint8Array)C=new j(o);else if(typeof o==="string"||o instanceof URL){let W=o instanceof URL?o.href:o;if(typeof window>"u"&&!W.startsWith("http"))C=new A(W);else C=new S(W)}else if(typeof ReadableStream<"u"&&o instanceof ReadableStream)C=new a(o);else throw Error("Unsupported source type");return new I({source:C,formats:X})}getSource(o){return this.sources.get(o)}hasSource(o){return this.sources.has(o)}unloadSource(o){let C=this.sources.get(o);if(C)return C.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 _{worker;nextId=1;pending=new Map;ready;constructor(o){let C=typeof o.worker==="boolean"?{}:o.worker??{},W=C.type??"module",w=C.url??new URL("./compositor-worker.js",import.meta.url);this.worker=new Worker(w,{type:W}),this.worker.onmessage=(r)=>{let{id:p,ok:n,result:t,error:E}=r.data,h=this.pending.get(p);if(!h)return;if(this.pending.delete(p),n)h.resolve(t);else h.reject(Error(E??"Worker error"))},this.worker.onerror=(r)=>{let p=r.error instanceof Error?r.error:Error("Worker error");for(let n of this.pending.values())n.reject(p);this.pending.clear()};let k=o.canvas.transferControlToOffscreen(),P={canvas:k,width:o.width,height:o.height,backgroundColor:o.backgroundColor};this.ready=this.call("init",P,[k])}postMessage(o,C,W){let w=this.nextId++;return this.worker.postMessage({id:w,kind:o,payload:C},W??[]),w}call(o,C,W){let w=this.postMessage(o,C,W);return new Promise((k,P)=>{this.pending.set(w,{resolve:k,reject:P})})}async loadSource(o,C){await this.ready;let W={source:o,options:C};return this.call("loadSource",W)}async loadImage(o){await this.ready;let C={source:o};return this.call("loadImage",C)}async loadAudio(o,C){await this.ready;let W={source:o,options:C};return this.call("loadAudio",W)}async unloadSource(o){await this.ready;let C={id:o};return this.call("unloadSource",C)}async render(o){await this.ready;let C={frame:o};return this.call("render",C)}async clear(){return await this.ready,this.call("clear")}async resize(o,C){await this.ready;let W={width:o,height:C};return this.call("resize",W)}async exportFrame(o,C){await this.ready;let W={frame:o,options:C};return this.call("exportFrame",W)}dispose(){try{this.worker.postMessage({id:this.nextId++,kind:"dispose"})}catch{}this.worker.terminate(),this.pending.clear()}}class D{canvas;ctx=null;width;height;backgroundColor;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;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.emitter=new O({maxListeners:50}),this.state={playing:!1,currentTime:0,duration:0,seeking:!1},this.canvas.width=this.width,this.canvas.height=this.height;let C=typeof o.worker==="boolean"?o.worker:o.worker?o.worker.enabled??!0:!1,W=C&&typeof Worker<"u"&&typeof OffscreenCanvas<"u"&&typeof this.canvas.transferControlToOffscreen==="function"&&!(this.canvas instanceof OffscreenCanvas);if(C&&!W)throw Error("Worker compositor requires HTMLCanvasElement, OffscreenCanvas, and Worker support");if(this.sourcePool=new Q,W)try{this.workerClient=new _({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:o.worker??!0})}catch(w){console.warn("[Compositor] Worker initialization failed, falling back to main thread rendering:",w),this.workerClient=null}if(o.enableAudio!==!1)this.audioManager=new J;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,C){if(this.checkDisposed(),this.workerClient){let w=await this.workerClient.loadSource(o,C),k=this.createWorkerSource(w);if(this.audioManager&&w.hasAudio)await this.loadWorkerAudio(o,k.id);return this.emitter.emit("sourceloaded",{id:k.id,source:k}),k}let W=await this.sourcePool.loadVideo(o,C);return this.registerSourceAudio(W),this.emitter.emit("sourceloaded",{id:W.id,source:W}),W}async loadImage(o){if(this.checkDisposed(),this.workerClient){let W=await this.workerClient.loadImage(o),w=this.createWorkerSource(W);return this.emitter.emit("sourceloaded",{id:w.id,source:w}),w}let C=await this.sourcePool.loadImage(o);return this.emitter.emit("sourceloaded",{id:C.id,source:C}),C}async loadAudio(o,C){if(this.checkDisposed(),this.workerClient){let w=await this.workerClient.loadAudio(o,C),k=this.createWorkerSource(w);if(this.audioManager)await this.loadWorkerAudio(o,k.id);return this.emitter.emit("sourceloaded",{id:k.id,source:k}),k}let W=await this.sourcePool.loadAudio(o,C);return this.registerSourceAudio(W),this.emitter.emit("sourceloaded",{id:W.id,source:W}),W}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 C=this.sourcePool.unloadSource(o);if(C)this.emitter.emit("sourceunloaded",{id:o});return C}registerSourceAudio(o){if(!this.audioManager)return;if(this.registeredAudioSources.has(o.id))return;let C=o.getAudioBufferSink?.();if(C)this.audioManager.registerSource(o,C),this.registeredAudioSources.add(o.id)}processAudioLayers(o,C){if(!this.audioManager)return;let W=this.audioScratch.newSourceIds,w=this.audioScratch.newSourceTimes,k=this.audioScratch.nextActiveSourceIds,P=this.activeAudioSourceIds;W.length=0,w.length=0,k.clear();for(let r=0;r<o.length;r++){let p=o[r];if(p.muted)continue;let n=p.source.id;if(!this.audioManager.hasSource(n))continue;if(k.add(n),!P.has(n))W.push(n),w.push(p.sourceTime??C)}if(P.size>0)P.clear();for(let r of k)P.add(r);this.audioManager.processAudioLayers(o,C);for(let r=0;r<W.length;r++)this.audioManager.startSourcePlayback(W[r],w[r])}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 t=this.serializeWorkerFrame(o);return this.workerClient.render(t)}let C=this.ctx;if(!C)return!1;let{visibleLayers:W,framePromises:w,frameImages:k}=this.renderBuffers;W.length=0,w.length=0,k.length=0;let P=!1,r=-1/0,p=o.layers;for(let t=0;t<p.length;t++){let E=p[t];if(E.visible===!1)continue;let h=E.zIndex??0;if(h<r)P=!0;r=h,W.push(E)}if(W.length===0)return C.fillStyle=this.backgroundColor,C.fillRect(0,0,this.width,this.height),!0;if(P)W.sort((t,E)=>(t.zIndex??0)-(E.zIndex??0));for(let t=0;t<W.length;t++){let E=W[t],h=E.sourceTime??o.time;w[t]=E.source.getFrameAt(h)}let n=await Promise.all(w);for(let t=0;t<n.length;t++)k[t]=n[t]??null;C.fillStyle=this.backgroundColor,C.fillRect(0,0,this.width,this.height);for(let t=0;t<W.length;t++){let E=k[t];if(E)this.renderLayer(E,W[t])}return!0}renderLayer(o,C){let W=this.ctx;if(!W)return;let w=C.transform,k=C.source.width??this.width,P=C.source.height??this.height;if(!w){W.drawImage(o,0,0,k,P);return}let r=w.width??k,p=w.height??P,n=w.x??0,t=w.y??0,E=w.rotation??0,h=w.scaleX??1,q=w.scaleY??1,g=w.opacity??1,M=g!==1;if(!M&&!(E!==0||h!==1||q!==1)){W.drawImage(o,n,t,r,p);return}let F=w.anchorX??0.5,l=w.anchorY??0.5;if(W.save(),M)W.globalAlpha=g;if(W.translate(n+r*F,t+p*l),E!==0)W.rotate(E*Math.PI/180);if(h!==1||q!==1)W.scale(h,q);W.drawImage(o,-r*F,-p*l,r,p),W.restore()}createWorkerSource(o){let C={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(C.id,C),C}async loadWorkerAudio(o,C){if(!this.audioManager)return;if(this.workerAudioSources.has(C))this.unloadWorkerAudio(C);try{let W=await this.sourcePool.loadAudio(o,{id:C});this.workerAudioSources.set(C,W),this.registerSourceAudio(W)}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 C=o.layers,W=Array(C.length);for(let w=0;w<C.length;w++){let k=C[w],P=k.source.id;if(!this.workerSources.has(P))throw Error(`Layer source ${P} is not managed by this compositor`);W[w]={sourceId:P,sourceTime:k.sourceTime,transform:k.transform,visible:k.visible,zIndex:k.zIndex}}return{time:o.time,layers:W}}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.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);this.startRenderLoop()}pause(){if(this.checkDisposed(),!this.state.playing)return;if(this.state.playing=!1,this.stopRenderLoop(),this.audioManager)this.audioManager.pause();this.emitter.emit("pause",void 0)}async seek(o){if(this.checkDisposed(),!this.previewOptions)return;let C=Math.max(0,Math.min(o,this.state.duration));if(this.state.seeking=!0,this.emitter.emit("seeking",{time:C}),this.state.currentTime=C,this.audioManager)await this.audioManager.seek(C);let W=this.previewOptions.getComposition(C);await this.render(W),this.state.seeking=!1,this.emitter.emit("seeked",{time:C}),this.emitter.emit("timeupdate",{currentTime:C})}startRenderLoop(){if(this.animationFrameId!==null)return;let o=()=>{if(!this.state.playing||!this.previewOptions)return;this.animationFrameId=requestAnimationFrame(o);let C=performance.now(),W=(C-this.lastFrameTime)/1000;if(this.lastFrameTime=C,this.state.currentTime+=W,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 w=this.previewOptions.fps??0,k=w>0?1000/w:0;if(!this.renderPending&&(k===0||C-this.lastRenderTime>=k)){this.renderPending=!0,this.lastRenderTime=C;let r=this.previewOptions.getComposition(this.state.currentTime);if(this.audioManager)this.processAudioLayers(r.audio??[],this.state.currentTime);this.render(r).catch(()=>{}).finally(()=>{this.renderPending=!1})}if(C-this.lastTimeUpdateEmit>=this.timeUpdateThrottleMs)this.lastTimeUpdateEmit=C,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,C={}){if(this.checkDisposed(),!this.previewOptions)return null;let W=this.previewOptions.getComposition(o);if(this.workerClient){let w=this.serializeWorkerFrame(W);return this.workerClient.exportFrame(w,C)}if(await this.render(W),"toBlob"in this.canvas)return new Promise((w)=>{this.canvas.toBlob((k)=>w(k),`image/${C.format??"png"}`,C.quality)});else return this.canvas.convertToBlob({type:`image/${C.format??"png"}`,quality:C.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,C){if(this.checkDisposed(),this.width=o,this.height=C,this.canvas.width=o,this.canvas.height=C,this.workerClient){this.workerClient.resize(o,C);return}this.clear()}on(o,C){return this.emitter.on(o,C)}once(o,C){return this.emitter.once(o,C)}off(o,C){this.emitter.off(o,C)}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}}var H=self,R=null,U=null,G=(o)=>({id:o.id,type:o.type,duration:o.duration,width:o.width,height:o.height,hasAudio:o.type==="audio"?!0:o.hasAudio?o.hasAudio():!1}),y=(o)=>{if(!R)throw Error("Compositor not initialized");let C=Array(o.layers.length);for(let W=0;W<o.layers.length;W++){let w=o.layers[W],k=R.getSource(w.sourceId);if(!k)throw Error(`Unknown source: ${w.sourceId}`);C[W]={source:k,sourceTime:w.sourceTime,transform:w.transform,visible:w.visible,zIndex:w.zIndex}}return{time:o.time,layers:C}},z=(o,C)=>{H.postMessage(o,C??[])};H.onmessage=async(o)=>{let{id:C,kind:W,payload:w}=o.data;try{switch(W){case"init":{let k=w;U=k.canvas,R=new D({canvas:U,width:k.width,height:k.height,backgroundColor:k.backgroundColor,enableAudio:!1}),z({id:C,ok:!0});return}case"loadSource":{let{source:k,options:P}=w;if(!R)throw Error("Compositor not initialized");let r=await R.loadSource(k,P);z({id:C,ok:!0,result:G(r)});return}case"loadImage":{let{source:k}=w;if(!R)throw Error("Compositor not initialized");let P=await R.loadImage(k);z({id:C,ok:!0,result:G(P)});return}case"loadAudio":{let{source:k,options:P}=w;if(!R)throw Error("Compositor not initialized");let r=await R.loadAudio(k,P);z({id:C,ok:!0,result:G(r)});return}case"unloadSource":{if(!R)throw Error("Compositor not initialized");let{id:k}=w,P=R.unloadSource(k);z({id:C,ok:!0,result:P});return}case"render":{if(!R)throw Error("Compositor not initialized");let{frame:k}=w,P=y(k),r=await R.render(P);z({id:C,ok:!0,result:r});return}case"clear":{if(!R)throw Error("Compositor not initialized");R.clear(),z({id:C,ok:!0,result:!0});return}case"resize":{if(!R)throw Error("Compositor not initialized");let{width:k,height:P}=w;R.resize(k,P),z({id:C,ok:!0,result:!0});return}case"exportFrame":{if(!R||!U)throw Error("Compositor not initialized");let{frame:k,options:P}=w,r=y(k);await R.render(r);let p=`image/${P?.format??"png"}`,n=await U.convertToBlob({type:p,quality:P?.quality});z({id:C,ok:!0,result:n});return}case"dispose":{R?.dispose(),R=null,U=null,z({id:C,ok:!0,result:!0});return}default:throw Error(`Unknown worker command: ${W}`)}}catch(k){let P=k instanceof Error?k.message:"Worker error";z({id:C,ok:!1,error:P})}};
|
|
1
|
+
class U{events=new Map;maxListeners;captureRejections;emitCache=[];constructor(o={}){this.maxListeners=o.maxListeners??10,this.captureRejections=o.captureRejections??!1}on(o,C){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 W=C;return k.add(W),()=>{k.delete(W)}}once(o,C){let k=(W)=>{this.off(o,k),C(W)};return this.on(o,k)}off(o,C){let k=this.events.get(o);if(!k)return;if(C){let W=C;k.delete(W)}else k.clear()}emit(o,C){let k=this.events.get(o);if(!k||k.size===0)return;let W=this.emitCache;W.length=0;for(let w of k)W.push(w);for(let w=0;w<W.length;w++){let P=W[w];try{let p=P(C);if(this.captureRejections&&V(p))this.handlePromiseRejection(p)}catch(p){if(this.captureRejections&&this.events.has("error"))this.emit("error",p);else throw p}}}handlePromiseRejection(o){o.catch((C)=>{if(this.events.has("error"))this.emit("error",C);else throw C})}removeAllListeners(o){if(o)this.events.delete(o);else this.events.clear()}setMaxListeners(o){this.maxListeners=o}getMaxListeners(){return this.maxListeners}listeners(o){let C=this.events.get(o);return C?Array.from(C):[]}listenerCount(o){let C=this.events.get(o);return C?C.size:0}eventNames(){return Array.from(this.events.keys())}}function V(o){if(!o||typeof o!=="object"&&typeof o!=="function")return!1;let C=o;return typeof C.then==="function"&&typeof C.catch==="function"}class ${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 C=globalThis,k=C.AudioContext||C.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,C){if(this.disposed)return;let k=this.audioContext.createGain(),W=this.audioContext.createStereoPanner();k.connect(W),W.connect(this.masterGain),this.activeSources.set(o.id,{sourceId:o.id,bufferSink:C,iterator:null,gainNode:k,panNode:W,queuedNodes:new Set,volume:1,pan:0,muted:!1,startSourceTime:0,currentSourceTime:0,iteratorStartTime:0,lastScheduledTime:0})}unregisterSource(o){let C=this.activeSources.get(o);if(!C)return;this.stopSourceAudio(C),C.gainNode.disconnect(),C.panNode.disconnect(),this.activeSources.delete(o)}hasSource(o){return this.activeSources.has(o)}processAudioLayers(o,C){if(this.disposed||!this.playing)return;let k=this.activeSourceIdsScratch;k.clear();for(let W of o){let w=W.source.id;k.add(w);let P=this.activeSources.get(w);if(!P)continue;let p=W.volume??1,h=W.pan??0,R=W.muted??!1,E=W.sourceTime??C;if(P.volume!==p||P.muted!==R)P.volume=p,P.muted=R,P.gainNode.gain.value=R?0:p*p;if(P.pan!==h)P.pan=h,P.panNode.pan.value=Math.max(-1,Math.min(1,h));if(Math.abs(E-P.currentSourceTime)>0.5&&P.iterator!==null)this.restartSourceIterator(P,E);P.currentSourceTime=E}for(let[W,w]of this.activeSources)if(!k.has(W)&&w.iterator!==null)this.stopSourceAudio(w)}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}startSourcePlayback(o,C){let k=this.activeSources.get(o);if(!k||k.iterator!==null)return;this.restartSourceIterator(k,C)}restartSourceIterator(o,C){this.stopSourceAudio(o),o.startSourceTime=C,o.currentSourceTime=C,o.iteratorStartTime=this.audioContext.currentTime,o.iterator=o.bufferSink.buffers(C),o.lastScheduledTime=C,this.scheduleSourceBuffers(o,this.playbackId)}async scheduleSourceBuffers(o,C){let k=o.iterator;if(!k)return;try{for await(let{buffer:W,timestamp:w}of k){if(C!==this.playbackId||this.disposed||!this.playing)break;let P=this.audioContext.createBufferSource();P.buffer=W,P.connect(o.gainNode);let p=w-o.startSourceTime,h=o.iteratorStartTime+p;if(h>=this.audioContext.currentTime)P.start(h);else{let r=this.audioContext.currentTime-h;if(r<W.duration)P.start(this.audioContext.currentTime,r);else continue}o.queuedNodes.add(P),P.onended=()=>{o.queuedNodes.delete(P)},o.lastScheduledTime=w;let R=this.audioContext.currentTime-o.iteratorStartTime;if(w-o.startSourceTime-R>1)await this.waitForCatchup(o,w)}}catch{}}async waitForCatchup(o,C){return new Promise((k)=>{let W=setInterval(()=>{if(!this.playing||this.disposed){clearInterval(W),k();return}let w=this.audioContext.currentTime-o.iteratorStartTime;if(C-o.startSourceTime-w<1)clearInterval(W),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 C=this.playing;if(this.pause(),this.pauseTime=o,this.startMediaTime=o,C)await this.play(o)}stopSourceAudio(o){for(let C of o.queuedNodes)try{C.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 H,AudioBufferSink as D,BlobSource as L,BufferSource as N,CanvasSink as X,FilePathSource as Y,Input as j,ReadableStreamSource as A,UrlSource as a}from"mediabunny";class G{id;type="video";duration;width;height;data;disposed=!1;constructor(o,C,k,W,w){this.id=o,this.data=C,this.duration=k,this.width=W,this.height=w}async getFrameAt(o){if(this.disposed)return null;return this.withLock(async()=>{if(this.disposed)return null;let C=this.data.currentFrame;if(C){let P=C.timestamp+(C.duration||0);if(o>=C.timestamp&&(C.duration?o<P:o===C.timestamp))return this.data.lastRequestedTime=o,C.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 w=await this.advanceToTime(o);return this.data.lastRequestedTime=o,w?.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 C=this.data.iterator;if(!C)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 W;try{W=await C.next()}catch{try{return await this.data.canvasSink.getCanvas(o)}catch{return null}}if(W.done)return this.data.currentFrame;this.data.nextFrame=W.value}}async withLock(o){let C=this.data.lock.catch(()=>{}),k=()=>{},W=new Promise((w)=>{k=()=>{w()}});this.data.lock=C.then(()=>W),await C;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 Z{id;type="image";duration=1/0;width;height;data;disposed=!1;constructor(o,C){this.id=o,this.data=C,this.width=C.image.width,this.height=C.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 t{id;type="audio";duration;width=0;height=0;data;disposed=!1;constructor(o,C,k){this.id=o,this.data=C,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 l{sources=new Map;audioContext=null;nextId=0;constructor(o){this.audioContext=o??null}generateId(){return`source_${this.nextId++}`}async loadVideo(o,C={}){let k=C.id??this.generateId(),W=this.createInput(o),w=await W.getVideoTracks();if(w.length===0)throw W.dispose(),Error("Source has no video track");let P=w[0];if(!await P.canDecode())throw W.dispose(),Error(`Cannot decode video track with codec: ${P.codec}`);let h=new X(P,{poolSize:4}),R=await P.computeDuration(),E=0.75,r=null,F=null;try{let M=await W.getAudioTracks();if(M.length>0){if(r=M[0],await r.canDecode())F=new D(r)}}catch{}let y=new G(k,{input:W,videoTrack:P,canvasSink:h,iterator:null,currentFrame:null,nextFrame:null,lastRequestedTime:-1/0,seekThresholdSeconds:E,lock:Promise.resolve(),audioTrack:r,audioBufferSink:F},R,P.displayWidth,P.displayHeight);return this.sources.set(k,y),y}async loadImage(o){let C=this.generateId(),k;if(typeof o!=="string")k=await createImageBitmap(o);else if(typeof Image>"u"){let w=await fetch(o);if(!w.ok)throw Error(`Failed to load image: ${o}`);let P=await w.blob();k=await createImageBitmap(P)}else k=await new Promise((w,P)=>{let p=new Image;p.onload=()=>w(p),p.onerror=()=>P(Error(`Failed to load image: ${o}`)),p.crossOrigin="anonymous",p.src=o});let W=new Z(C,{image:k});return this.sources.set(C,W),W}async loadAudio(o,C={}){let k=C.id??this.generateId(),W=this.createInput(o),w=await W.getAudioTracks();if(w.length===0)throw W.dispose(),Error("Source has no audio track");let P=w[0];if(!await P.canDecode())throw W.dispose(),Error(`Cannot decode audio track with codec: ${P.codec}`);let h=await P.computeDuration(),R=new D(P),E=new t(k,{input:W,audioTrack:P,audioBufferSink:R},h);return this.sources.set(k,E),E}createInput(o){let C;if(o instanceof File||o instanceof Blob)C=new L(o);else if(o instanceof ArrayBuffer||o instanceof Uint8Array)C=new N(o);else if(typeof o==="string"||o instanceof URL){let k=o instanceof URL?o.href:o;if(typeof window>"u"&&!k.startsWith("http"))C=new Y(k);else C=new a(k)}else if(typeof ReadableStream<"u"&&o instanceof ReadableStream)C=new A(o);else throw Error("Unsupported source type");return new j({source:C,formats:H})}getSource(o){return this.sources.get(o)}hasSource(o){return this.sources.has(o)}unloadSource(o){let C=this.sources.get(o);if(C)return C.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 O{worker;nextId=1;pending=new Map;ready;constructor(o){let C=typeof o.worker==="boolean"?{}:o.worker??{},k=C.type??"module",W=C.url??new URL("./compositor-worker.js",import.meta.url);this.worker=new Worker(W,{type:k}),this.worker.onmessage=(p)=>{let{id:h,ok:R,result:E,error:r}=p.data,F=this.pending.get(h);if(!F)return;if(this.pending.delete(h),R)F.resolve(E);else F.reject(Error(r??"Worker error"))},this.worker.onerror=(p)=>{let h=p.error instanceof Error?p.error:Error("Worker error");for(let R of this.pending.values())R.reject(h);this.pending.clear()};let w=o.canvas.transferControlToOffscreen(),P={canvas:w,width:o.width,height:o.height,backgroundColor:o.backgroundColor};this.ready=this.call("init",P,[w])}postMessage(o,C,k){let W=this.nextId++;return this.worker.postMessage({id:W,kind:o,payload:C},k??[]),W}call(o,C,k){let W=this.postMessage(o,C,k);return new Promise((w,P)=>{this.pending.set(W,{resolve:w,reject:P})})}async loadSource(o,C){await this.ready;let k={source:o,options:C};return this.call("loadSource",k)}async loadImage(o){await this.ready;let C={source:o};return this.call("loadImage",C)}async loadAudio(o,C){await this.ready;let k={source:o,options:C};return this.call("loadAudio",k)}async unloadSource(o){await this.ready;let C={id:o};return this.call("unloadSource",C)}async render(o){await this.ready;let C={frame:o};return this.call("render",C)}async clear(){return await this.ready,this.call("clear")}async resize(o,C){await this.ready;let k={width:o,height:C};return this.call("resize",k)}async exportFrame(o,C){await this.ready;let k={frame:o,options:C};return this.call("exportFrame",k)}dispose(){try{this.worker.postMessage({id:this.nextId++,kind:"dispose"})}catch{}this.worker.terminate(),this.pending.clear()}}class J{canvas;ctx=null;width;height;backgroundColor;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;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.emitter=new U({maxListeners:50}),this.state={playing:!1,currentTime:0,duration:0,seeking:!1},this.canvas.width=this.width,this.canvas.height=this.height;let C=typeof o.worker==="boolean"?o.worker:o.worker?o.worker.enabled??!0:!1,k=C&&typeof Worker<"u"&&typeof OffscreenCanvas<"u"&&typeof this.canvas.transferControlToOffscreen==="function"&&!(this.canvas instanceof OffscreenCanvas);if(C&&!k)throw Error("Worker compositor requires HTMLCanvasElement, OffscreenCanvas, and Worker support");if(this.sourcePool=new l,k)try{this.workerClient=new O({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:o.worker??!0})}catch(W){console.warn("[Compositor] Worker initialization failed, falling back to main thread rendering:",W),this.workerClient=null}if(o.enableAudio!==!1)this.audioManager=new $;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,C){if(this.checkDisposed(),this.workerClient){let W=await this.workerClient.loadSource(o,C),w=this.createWorkerSource(W);if(this.audioManager&&W.hasAudio)await this.loadWorkerAudio(o,w.id);return this.emitter.emit("sourceloaded",{id:w.id,source:w}),w}let k=await this.sourcePool.loadVideo(o,C);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),W=this.createWorkerSource(k);return this.emitter.emit("sourceloaded",{id:W.id,source:W}),W}let C=await this.sourcePool.loadImage(o);return this.emitter.emit("sourceloaded",{id:C.id,source:C}),C}async loadAudio(o,C){if(this.checkDisposed(),this.workerClient){let W=await this.workerClient.loadAudio(o,C),w=this.createWorkerSource(W);if(this.audioManager)await this.loadWorkerAudio(o,w.id);return this.emitter.emit("sourceloaded",{id:w.id,source:w}),w}let k=await this.sourcePool.loadAudio(o,C);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 C=this.sourcePool.unloadSource(o);if(C)this.emitter.emit("sourceunloaded",{id:o});return C}registerSourceAudio(o){if(!this.audioManager)return;if(this.registeredAudioSources.has(o.id))return;let C=o.getAudioBufferSink?.();if(C)this.audioManager.registerSource(o,C),this.registeredAudioSources.add(o.id)}processAudioLayers(o,C){if(!this.audioManager)return;let k=this.audioScratch.newSourceIds,W=this.audioScratch.newSourceTimes,w=this.audioScratch.nextActiveSourceIds,P=this.activeAudioSourceIds;k.length=0,W.length=0,w.clear();for(let p=0;p<o.length;p++){let h=o[p];if(h.muted)continue;let R=h.source.id;if(!this.audioManager.hasSource(R))continue;if(w.add(R),!P.has(R))k.push(R),W.push(h.sourceTime??C)}if(P.size>0)P.clear();for(let p of w)P.add(p);this.audioManager.processAudioLayers(o,C);for(let p=0;p<k.length;p++)this.audioManager.startSourcePlayback(k[p],W[p])}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 E=this.serializeWorkerFrame(o);return this.workerClient.render(E)}let C=this.ctx;if(!C)return!1;let{visibleLayers:k,framePromises:W,frameImages:w}=this.renderBuffers;k.length=0,W.length=0,w.length=0;let P=!1,p=-1/0,h=o.layers;for(let E=0;E<h.length;E++){let r=h[E];if(r.visible===!1)continue;let F=r.zIndex??0;if(F<p)P=!0;p=F,k.push(r)}if(k.length===0)return C.fillStyle=this.backgroundColor,C.fillRect(0,0,this.width,this.height),!0;if(P)k.sort((E,r)=>(E.zIndex??0)-(r.zIndex??0));for(let E=0;E<k.length;E++){let r=k[E],F=r.sourceTime??o.time;W[E]=r.source.getFrameAt(F)}let R=await Promise.all(W);for(let E=0;E<R.length;E++)w[E]=R[E]??null;C.fillStyle=this.backgroundColor,C.fillRect(0,0,this.width,this.height);for(let E=0;E<k.length;E++){let r=w[E];if(r)this.renderLayer(r,k[E])}return!0}renderLayer(o,C){let k=this.ctx;if(!k)return;let W=C.transform,w=C.source.width??this.width,P=C.source.height??this.height;if(!W){k.drawImage(o,0,0,w,P);return}let p=W.width??w,h=W.height??P,R=W.x??0,E=W.y??0,r=W.rotation??0,F=W.scaleX??1,y=W.scaleY??1,M=W.opacity??1,q=M!==1;if(!q&&!(r!==0||F!==1||y!==1)){k.drawImage(o,R,E,p,h);return}let Q=W.anchorX??0.5,_=W.anchorY??0.5;if(k.save(),q)k.globalAlpha=M;if(k.translate(R+p*Q,E+h*_),r!==0)k.rotate(r*Math.PI/180);if(F!==1||y!==1)k.scale(F,y);k.drawImage(o,-p*Q,-h*_,p,h),k.restore()}createWorkerSource(o){let C={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(C.id,C),C}async loadWorkerAudio(o,C){if(!this.audioManager)return;if(this.workerAudioSources.has(C))this.unloadWorkerAudio(C);try{let k=await this.sourcePool.loadAudio(o,{id:C});this.workerAudioSources.set(C,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 C=o.layers,k=Array(C.length);for(let W=0;W<C.length;W++){let w=C[W],P=w.source.id;if(!this.workerSources.has(P))throw Error(`Layer source ${P} is not managed by this compositor`);k[W]={sourceId:P,sourceTime:w.sourceTime,transform:w.transform,visible:w.visible,zIndex:w.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.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);this.startRenderLoop()}pause(){if(this.checkDisposed(),!this.state.playing)return;if(this.state.playing=!1,this.stopRenderLoop(),this.audioManager)this.audioManager.pause();this.emitter.emit("pause",void 0)}async seek(o){if(this.checkDisposed(),!this.previewOptions)return;let C=Math.max(0,Math.min(o,this.state.duration));if(this.state.seeking=!0,this.emitter.emit("seeking",{time:C}),this.state.currentTime=C,this.audioManager)await this.audioManager.seek(C);let k=this.previewOptions.getComposition(C);await this.render(k),this.state.seeking=!1,this.emitter.emit("seeked",{time:C}),this.emitter.emit("timeupdate",{currentTime:C})}startRenderLoop(){if(this.animationFrameId!==null)return;let o=()=>{if(!this.state.playing||!this.previewOptions)return;this.animationFrameId=requestAnimationFrame(o);let C=performance.now(),k=(C-this.lastFrameTime)/1000;if(this.lastFrameTime=C,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 W=this.previewOptions.fps??0,w=W>0?1000/W:0;if(!this.renderPending&&(w===0||C-this.lastRenderTime>=w)){this.renderPending=!0,this.lastRenderTime=C;let p=this.previewOptions.getComposition(this.state.currentTime);if(this.audioManager)this.processAudioLayers(p.audio??[],this.state.currentTime);this.render(p).catch(()=>{}).finally(()=>{this.renderPending=!1})}if(C-this.lastTimeUpdateEmit>=this.timeUpdateThrottleMs)this.lastTimeUpdateEmit=C,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,C={}){if(this.checkDisposed(),!this.previewOptions)return null;let k=this.previewOptions.getComposition(o);if(this.workerClient){let W=this.serializeWorkerFrame(k);return this.workerClient.exportFrame(W,C)}if(await this.render(k),"toBlob"in this.canvas)return new Promise((W)=>{this.canvas.toBlob((w)=>W(w),`image/${C.format??"png"}`,C.quality)});else return this.canvas.convertToBlob({type:`image/${C.format??"png"}`,quality:C.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,C){if(this.checkDisposed(),this.width=o,this.height=C,this.canvas.width=o,this.canvas.height=C,this.workerClient){this.workerClient.resize(o,C);return}this.clear()}on(o,C){return this.emitter.on(o,C)}once(o,C){return this.emitter.once(o,C)}off(o,C){this.emitter.off(o,C)}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}}var B=self,z=null,g=null,K=(o)=>({id:o.id,type:o.type,duration:o.duration,width:o.width,height:o.height,hasAudio:o.type==="audio"?!0:o.hasAudio?o.hasAudio():!1}),b=(o)=>{if(!z)throw Error("Compositor not initialized");let C=Array(o.layers.length);for(let k=0;k<o.layers.length;k++){let W=o.layers[k],w=z.getSource(W.sourceId);if(!w)throw Error(`Unknown source: ${W.sourceId}`);C[k]={source:w,sourceTime:W.sourceTime,transform:W.transform,visible:W.visible,zIndex:W.zIndex}}return{time:o.time,layers:C}},n=(o,C)=>{B.postMessage(o,C??[])};B.onmessage=async(o)=>{let{id:C,kind:k,payload:W}=o.data;try{switch(k){case"init":{let w=W;g=w.canvas,z=new J({canvas:g,width:w.width,height:w.height,backgroundColor:w.backgroundColor,enableAudio:!1}),n({id:C,ok:!0});return}case"loadSource":{let{source:w,options:P}=W;if(!z)throw Error("Compositor not initialized");let p=await z.loadSource(w,P);n({id:C,ok:!0,result:K(p)});return}case"loadImage":{let{source:w}=W;if(!z)throw Error("Compositor not initialized");let P=await z.loadImage(w);n({id:C,ok:!0,result:K(P)});return}case"loadAudio":{let{source:w,options:P}=W;if(!z)throw Error("Compositor not initialized");let p=await z.loadAudio(w,P);n({id:C,ok:!0,result:K(p)});return}case"unloadSource":{if(!z)throw Error("Compositor not initialized");let{id:w}=W,P=z.unloadSource(w);n({id:C,ok:!0,result:P});return}case"render":{if(!z)throw Error("Compositor not initialized");let{frame:w}=W,P=b(w),p=await z.render(P);n({id:C,ok:!0,result:p});return}case"clear":{if(!z)throw Error("Compositor not initialized");z.clear(),n({id:C,ok:!0,result:!0});return}case"resize":{if(!z)throw Error("Compositor not initialized");let{width:w,height:P}=W;z.resize(w,P),n({id:C,ok:!0,result:!0});return}case"exportFrame":{if(!z||!g)throw Error("Compositor not initialized");let{frame:w,options:P}=W,p=b(w);await z.render(p);let h=`image/${P?.format??"png"}`,R=await g.convertToBlob({type:h,quality:P?.quality});n({id:C,ok:!0,result:R});return}case"dispose":{z?.dispose(),z=null,g=null,n({id:C,ok:!0,result:!0});return}default:throw Error(`Unknown worker command: ${k}`)}}catch(w){let P=w instanceof Error?w.message:"Worker error";n({id:C,ok:!1,error:P})}};
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class y{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 A=this.events.get(O);if(!A)return()=>{};if(A.size>=this.maxListeners)console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${A.size} ${String(O)} listeners added. Use emitter.setMaxListeners() to increase limit`);let j=$;return A.add(j),()=>{A.delete(j)}}once(O,$){let A=(j)=>{this.off(O,A),$(j)};return this.on(O,A)}off(O,$){let A=this.events.get(O);if(!A)return;if($){let j=$;A.delete(j)}else A.clear()}emit(O,$){let A=this.events.get(O);if(!A||A.size===0)return;let j=this.emitCache;j.length=0;for(let Q of A)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 n{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,A=$.AudioContext||$.webkitAudioContext;if(!A)throw Error("AudioContext is not supported in this environment");this.audioContext=new A}this.masterGain=this.audioContext.createGain(),this.masterGain.connect(this.audioContext.destination)}registerSource(O,$){if(this.disposed)return;let A=this.audioContext.createGain(),j=this.audioContext.createStereoPanner();A.connect(j),j.connect(this.masterGain),this.activeSources.set(O.id,{sourceId:O.id,bufferSink:$,iterator:null,gainNode:A,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||!this.playing)return;let A=this.activeSourceIdsScratch;A.clear();for(let j of O){let Q=j.source.id;A.add(Q);let V=this.activeSources.get(Q);if(!V)continue;let J=j.volume??1,K=j.pan??0,Z=j.muted??!1,U=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!==K)V.pan=K,V.panNode.pan.value=Math.max(-1,Math.min(1,K));if(Math.abs(U-V.currentSourceTime)>0.5&&V.iterator!==null)this.restartSourceIterator(V,U);V.currentSourceTime=U}for(let[j,Q]of this.activeSources)if(!A.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}startSourcePlayback(O,$){let A=this.activeSources.get(O);if(!A||A.iterator!==null)return;this.restartSourceIterator(A,$)}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 A=O.iterator;if(!A)return;try{for await(let{buffer:j,timestamp:Q}of A){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,K=O.iteratorStartTime+J;if(K>=this.audioContext.currentTime)V.start(K);else{let G=this.audioContext.currentTime-K;if(G<j.duration)V.start(this.audioContext.currentTime,G);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((A)=>{let j=setInterval(()=>{if(!this.playing||this.disposed){clearInterval(j),A();return}let Q=this.audioContext.currentTime-O.iteratorStartTime;if($-O.startSourceTime-Q<1)clearInterval(j),A()},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 DO,AudioBufferSink as JO,BlobSource as MO,BufferSource as _O,CanvasSink as EO,FilePathSource as qO,Input as CO,ReadableStreamSource as RO,UrlSource as WO}from"mediabunny";class KO{cache=new Map;maxSize;constructor(O){this.maxSize=O}get(O){let $=this.cache.get(O);if($!==void 0)this.cache.delete(O),this.cache.set(O,$);return $}set(O,$){if(this.cache.has(O))this.cache.delete(O);else if(this.cache.size>=this.maxSize){let A=this.cache.keys().next().value;if(A!==void 0)this.cache.delete(A)}this.cache.set(O,$)}clear(){this.cache.clear()}get size(){return this.cache.size}}class UO{id;type="video";duration;width;height;data;disposed=!1;constructor(O,$,A,j,Q){this.id=O,this.data=$,this.duration=A,this.width=j,this.height=Q}async getFrameAt(O){if(this.disposed)return null;let $=this.data.frameIntervalMs,A=Math.floor(O*1000/$)*$,j=this.data.frameCache.get(A);if(j)return j.canvas;try{let Q=await this.data.canvasSink.getCanvas(O);if(!Q)return null;return this.data.frameCache.set(A,Q),Q.canvas}catch{return null}}getAudioBufferSink(){return this.data.audioBufferSink}hasAudio(){return this.data.audioBufferSink!==null}clearCache(){this.data.frameCache.clear()}dispose(){if(this.disposed)return;this.disposed=!0,this.data.frameCache.clear(),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,$,A){this.id=O,this.data=$,this.duration=A}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 A=$.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 K=new EO(V,{poolSize:4}),Z=await V.computeDuration(),U=30;try{let Y=await V.computePacketStats(100);if(Y.averagePacketRate>0)U=Y.averagePacketRate}catch{}let G=1000/U,L=V.displayWidth*V.displayHeight,P=L>2073600?15:L>921600?30:60,M=null,X=null;try{let Y=await j.getAudioTracks();if(Y.length>0){if(M=Y[0],await M.canDecode())X=new JO(M)}}catch{}let E=new UO(A,{input:j,videoTrack:V,canvasSink:K,frameCache:new KO(P),frameIntervalMs:G,audioTrack:M,audioBufferSink:X},Z,V.displayWidth,V.displayHeight);return this.sources.set(A,E),E}async loadImage(O){let $=this.generateId(),A;if(typeof O!=="string")A=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();A=await createImageBitmap(V)}else A=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:A});return this.sources.set($,j),j}async loadAudio(O,$={}){let A=$.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 K=await V.computeDuration(),Z=new JO(V),U=new GO(A,{input:j,audioTrack:V,audioBufferSink:Z},K);return this.sources.set(A,U),U}createInput(O){let $;if(O instanceof File||O instanceof Blob)$=new MO(O);else if(O instanceof ArrayBuffer||O instanceof Uint8Array)$=new _O(O);else if(typeof O==="string"||O instanceof URL){let A=O instanceof URL?O.href:O;if(typeof window>"u"&&!A.startsWith("http"))$=new qO(A);else $=new WO(A)}else if(typeof ReadableStream<"u"&&O instanceof ReadableStream)$=new RO(O);else throw Error("Unsupported source type");return new CO({source:$,formats:DO})}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 s{worker;nextId=1;pending=new Map;ready;constructor(O){let $=typeof O.worker==="boolean"?{}:O.worker??{},A=$.type??"module",j=$.url??new URL("./compositor-worker.js",import.meta.url);this.worker=new Worker(j,{type:A}),this.worker.onmessage=(J)=>{let{id:K,ok:Z,result:U,error:G}=J.data,L=this.pending.get(K);if(!L)return;if(this.pending.delete(K),Z)L.resolve(U);else L.reject(Error(G??"Worker error"))},this.worker.onerror=(J)=>{let K=J.error instanceof Error?J.error:Error("Worker error");for(let Z of this.pending.values())Z.reject(K);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,$,A){let j=this.nextId++;return this.worker.postMessage({id:j,kind:O,payload:$},A??[]),j}call(O,$,A){let j=this.postMessage(O,$,A);return new Promise((Q,V)=>{this.pending.set(j,{resolve:Q,reject:V})})}async loadSource(O,$){await this.ready;let A={source:O,options:$};return this.call("loadSource",A)}async loadImage(O){await this.ready;let $={source:O};return this.call("loadImage",$)}async loadAudio(O,$){await this.ready;let A={source:O,options:$};return this.call("loadAudio",A)}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,$){await this.ready;let A={width:O,height:$};return this.call("resize",A)}async exportFrame(O,$){await this.ready;let A={frame:O,options:$};return this.call("exportFrame",A)}dispose(){try{this.worker.postMessage({id:this.nextId++,kind:"dispose"})}catch{}this.worker.terminate(),this.pending.clear()}}class i{canvas;ctx=null;width;height;backgroundColor;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;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.emitter=new y({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,A=$&&typeof Worker<"u"&&typeof OffscreenCanvas<"u"&&typeof this.canvas.transferControlToOffscreen==="function"&&!(this.canvas instanceof OffscreenCanvas);if($&&!A)throw Error("Worker compositor requires HTMLCanvasElement, OffscreenCanvas, and Worker support");if(this.sourcePool=new x,A)try{this.workerClient=new s({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 n;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 A=await this.sourcePool.loadVideo(O,$);return this.registerSourceAudio(A),this.emitter.emit("sourceloaded",{id:A.id,source:A}),A}async loadImage(O){if(this.checkDisposed(),this.workerClient){let A=await this.workerClient.loadImage(O),j=this.createWorkerSource(A);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 A=await this.sourcePool.loadAudio(O,$);return this.registerSourceAudio(A),this.emitter.emit("sourceloaded",{id:A.id,source:A}),A}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 A=this.audioScratch.newSourceIds,j=this.audioScratch.newSourceTimes,Q=this.audioScratch.nextActiveSourceIds,V=this.activeAudioSourceIds;A.length=0,j.length=0,Q.clear();for(let J=0;J<O.length;J++){let K=O[J];if(K.muted)continue;let Z=K.source.id;if(!this.audioManager.hasSource(Z))continue;if(Q.add(Z),!V.has(Z))A.push(Z),j.push(K.sourceTime??$)}if(V.size>0)V.clear();for(let J of Q)V.add(J);this.audioManager.processAudioLayers(O,$);for(let J=0;J<A.length;J++)this.audioManager.startSourcePlayback(A[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 U=this.serializeWorkerFrame(O);return this.workerClient.render(U)}let $=this.ctx;if(!$)return!1;let{visibleLayers:A,framePromises:j,frameImages:Q}=this.renderBuffers;A.length=0,j.length=0,Q.length=0;let V=!1,J=-1/0,K=O.layers;for(let U=0;U<K.length;U++){let G=K[U];if(G.visible===!1)continue;let L=G.zIndex??0;if(L<J)V=!0;J=L,A.push(G)}if(A.length===0)return $.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height),!0;if(V)A.sort((U,G)=>(U.zIndex??0)-(G.zIndex??0));for(let U=0;U<A.length;U++){let G=A[U],L=G.sourceTime??O.time;j[U]=G.source.getFrameAt(L)}let Z=await Promise.all(j);for(let U=0;U<Z.length;U++)Q[U]=Z[U]??null;$.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height);for(let U=0;U<A.length;U++){let G=Q[U];if(G)this.renderLayer(G,A[U])}return!0}renderLayer(O,$){let A=this.ctx;if(!A)return;let j=$.transform,Q=$.source.width??this.width,V=$.source.height??this.height;if(!j){A.drawImage(O,0,0,Q,V);return}let J=j.width??Q,K=j.height??V,Z=j.x??0,U=j.y??0,G=j.rotation??0,L=j.scaleX??1,P=j.scaleY??1,M=j.opacity??1,X=M!==1;if(!X&&!(G!==0||L!==1||P!==1)){A.drawImage(O,Z,U,J,K);return}let Y=j.anchorX??0.5,q=j.anchorY??0.5;if(A.save(),X)A.globalAlpha=M;if(A.translate(Z+J*Y,U+K*q),G!==0)A.rotate(G*Math.PI/180);if(L!==1||P!==1)A.scale(L,P);A.drawImage(O,-J*Y,-K*q,J,K),A.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 A=await this.sourcePool.loadAudio(O,{id:$});this.workerAudioSources.set($,A),this.registerSourceAudio(A)}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,A=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`);A[j]={sourceId:V,sourceTime:Q.sourceTime,transform:Q.transform,visible:Q.visible,zIndex:Q.zIndex}}return{time:O.time,layers:A}}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.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);this.startRenderLoop()}pause(){if(this.checkDisposed(),!this.state.playing)return;if(this.state.playing=!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($);let A=this.previewOptions.getComposition($);await this.render(A),this.state.seeking=!1,this.emitter.emit("seeked",{time:$}),this.emitter.emit("timeupdate",{currentTime:$})}startRenderLoop(){if(this.animationFrameId!==null)return;let O=()=>{if(!this.state.playing||!this.previewOptions)return;this.animationFrameId=requestAnimationFrame(O);let $=performance.now(),A=($-this.lastFrameTime)/1000;if(this.lastFrameTime=$,this.state.currentTime+=A,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 A=this.previewOptions.getComposition(O);if(this.workerClient){let j=this.serializeWorkerFrame(A);return this.workerClient.exportFrame(j,$)}if(await this.render(A),"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,$){if(this.checkDisposed(),this.width=O,this.height=$,this.canvas.width=O,this.canvas.height=$,this.workerClient){this.workerClient.resize(O,$);return}this.clear()}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 S{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:A}=O;if($===0||A===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?A:$,K=V?$:A,Z=Math.min(j/J,Q/K),U=Math.round(J*Z),G=Math.round(K*Z),L=Math.round((j-U)/2),P=Math.round((Q-G)/2);if(this.ctx.fillStyle="black",this.ctx.fillRect(0,0,j,Q),this.ctx.save(),this.ctx.translate(L+U/2,P+G/2),this.rotation!==0)this.ctx.rotate(this.rotation*Math.PI/180);if(V)this.ctx.drawImage(O,0,0,$,A,-G/2,-U/2,G,U);else this.ctx.drawImage(O,0,0,$,A,-U/2,-G/2,U,G);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 t{resources;isInitialized=!1;canvas;textureWidth=0;textureHeight=0;options;boundHandleContextLost=null;boundHandleContextRestored=null;rotation=0;positionsArray=new Float32Array(8);vertexShaderSource=`
|
|
1
|
+
class y{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 A=this.events.get(O);if(!A)return()=>{};if(A.size>=this.maxListeners)console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${A.size} ${String(O)} listeners added. Use emitter.setMaxListeners() to increase limit`);let K=$;return A.add(K),()=>{A.delete(K)}}once(O,$){let A=(K)=>{this.off(O,A),$(K)};return this.on(O,A)}off(O,$){let A=this.events.get(O);if(!A)return;if($){let K=$;A.delete(K)}else A.clear()}emit(O,$){let A=this.events.get(O);if(!A||A.size===0)return;let K=this.emitCache;K.length=0;for(let j of A)K.push(j);for(let j=0;j<K.length;j++){let Q=K[j];try{let V=Q($);if(this.captureRejections&&BO(V))this.handlePromiseRejection(V)}catch(V){if(this.captureRejections&&this.events.has("error"))this.emit("error",V);else throw V}}}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 BO(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,A=$.AudioContext||$.webkitAudioContext;if(!A)throw Error("AudioContext is not supported in this environment");this.audioContext=new A}this.masterGain=this.audioContext.createGain(),this.masterGain.connect(this.audioContext.destination)}registerSource(O,$){if(this.disposed)return;let A=this.audioContext.createGain(),K=this.audioContext.createStereoPanner();A.connect(K),K.connect(this.masterGain),this.activeSources.set(O.id,{sourceId:O.id,bufferSink:$,iterator:null,gainNode:A,panNode:K,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||!this.playing)return;let A=this.activeSourceIdsScratch;A.clear();for(let K of O){let j=K.source.id;A.add(j);let Q=this.activeSources.get(j);if(!Q)continue;let V=K.volume??1,J=K.pan??0,Z=K.muted??!1,U=K.sourceTime??$;if(Q.volume!==V||Q.muted!==Z)Q.volume=V,Q.muted=Z,Q.gainNode.gain.value=Z?0:V*V;if(Q.pan!==J)Q.pan=J,Q.panNode.pan.value=Math.max(-1,Math.min(1,J));if(Math.abs(U-Q.currentSourceTime)>0.5&&Q.iterator!==null)this.restartSourceIterator(Q,U);Q.currentSourceTime=U}for(let[K,j]of this.activeSources)if(!A.has(K)&&j.iterator!==null)this.stopSourceAudio(j)}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}startSourcePlayback(O,$){let A=this.activeSources.get(O);if(!A||A.iterator!==null)return;this.restartSourceIterator(A,$)}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 A=O.iterator;if(!A)return;try{for await(let{buffer:K,timestamp:j}of A){if($!==this.playbackId||this.disposed||!this.playing)break;let Q=this.audioContext.createBufferSource();Q.buffer=K,Q.connect(O.gainNode);let V=j-O.startSourceTime,J=O.iteratorStartTime+V;if(J>=this.audioContext.currentTime)Q.start(J);else{let L=this.audioContext.currentTime-J;if(L<K.duration)Q.start(this.audioContext.currentTime,L);else continue}O.queuedNodes.add(Q),Q.onended=()=>{O.queuedNodes.delete(Q)},O.lastScheduledTime=j;let Z=this.audioContext.currentTime-O.iteratorStartTime;if(j-O.startSourceTime-Z>1)await this.waitForCatchup(O,j)}}catch{}}async waitForCatchup(O,$){return new Promise((A)=>{let K=setInterval(()=>{if(!this.playing||this.disposed){clearInterval(K),A();return}let j=this.audioContext.currentTime-O.iteratorStartTime;if($-O.startSourceTime-j<1)clearInterval(K),A()},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 VO,BlobSource as MO,BufferSource as DO,CanvasSink as _O,FilePathSource as EO,Input as qO,ReadableStreamSource as CO,UrlSource as RO}from"mediabunny";class JO{id;type="video";duration;width;height;data;disposed=!1;constructor(O,$,A,K,j){this.id=O,this.data=$,this.duration=A,this.width=K,this.height=j}async getFrameAt(O){if(this.disposed)return null;return this.withLock(async()=>{if(this.disposed)return null;let $=this.data.currentFrame;if($){let Q=$.timestamp+($.duration||0);if(O>=$.timestamp&&($.duration?O<Q:O===$.timestamp))return this.data.lastRequestedTime=O,$.canvas}let A=this.data.lastRequestedTime;if(this.data.iterator===null||O<A||Math.abs(O-A)>this.data.seekThresholdSeconds)await this.restartIterator(O);let j=await this.advanceToTime(O);return this.data.lastRequestedTime=O,j?.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 A=this.data.nextFrame;if(A){if(A.timestamp>O)return this.data.currentFrame;this.data.currentFrame=A,this.data.nextFrame=null;continue}let K;try{K=await $.next()}catch{try{return await this.data.canvasSink.getCanvas(O)}catch{return null}}if(K.done)return this.data.currentFrame;this.data.nextFrame=K.value}}async withLock(O){let $=this.data.lock.catch(()=>{}),A=()=>{},K=new Promise((j)=>{A=()=>{j()}});this.data.lock=$.then(()=>K),await $;try{return await O()}finally{A()}}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 UO{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,$,A){this.id=O,this.data=$,this.duration=A}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 A=$.id??this.generateId(),K=this.createInput(O),j=await K.getVideoTracks();if(j.length===0)throw K.dispose(),Error("Source has no video track");let Q=j[0];if(!await Q.canDecode())throw K.dispose(),Error(`Cannot decode video track with codec: ${Q.codec}`);let J=new _O(Q,{poolSize:4}),Z=await Q.computeDuration(),U=0.75,L=null,G=null;try{let M=await K.getAudioTracks();if(M.length>0){if(L=M[0],await L.canDecode())G=new VO(L)}}catch{}let P=new JO(A,{input:K,videoTrack:Q,canvasSink:J,iterator:null,currentFrame:null,nextFrame:null,lastRequestedTime:-1/0,seekThresholdSeconds:U,lock:Promise.resolve(),audioTrack:L,audioBufferSink:G},Z,Q.displayWidth,Q.displayHeight);return this.sources.set(A,P),P}async loadImage(O){let $=this.generateId(),A;if(typeof O!=="string")A=await createImageBitmap(O);else if(typeof Image>"u"){let j=await fetch(O);if(!j.ok)throw Error(`Failed to load image: ${O}`);let Q=await j.blob();A=await createImageBitmap(Q)}else A=await new Promise((j,Q)=>{let V=new Image;V.onload=()=>j(V),V.onerror=()=>Q(Error(`Failed to load image: ${O}`)),V.crossOrigin="anonymous",V.src=O});let K=new UO($,{image:A});return this.sources.set($,K),K}async loadAudio(O,$={}){let A=$.id??this.generateId(),K=this.createInput(O),j=await K.getAudioTracks();if(j.length===0)throw K.dispose(),Error("Source has no audio track");let Q=j[0];if(!await Q.canDecode())throw K.dispose(),Error(`Cannot decode audio track with codec: ${Q.codec}`);let J=await Q.computeDuration(),Z=new VO(Q),U=new ZO(A,{input:K,audioTrack:Q,audioBufferSink:Z},J);return this.sources.set(A,U),U}createInput(O){let $;if(O instanceof File||O instanceof Blob)$=new MO(O);else if(O instanceof ArrayBuffer||O instanceof Uint8Array)$=new DO(O);else if(typeof O==="string"||O instanceof URL){let A=O instanceof URL?O.href:O;if(typeof window>"u"&&!A.startsWith("http"))$=new EO(A);else $=new RO(A)}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??{},A=$.type??"module",K=$.url??new URL("./compositor-worker.js",import.meta.url);this.worker=new Worker(K,{type:A}),this.worker.onmessage=(V)=>{let{id:J,ok:Z,result:U,error:L}=V.data,G=this.pending.get(J);if(!G)return;if(this.pending.delete(J),Z)G.resolve(U);else G.reject(Error(L??"Worker error"))},this.worker.onerror=(V)=>{let J=V.error instanceof Error?V.error:Error("Worker error");for(let Z of this.pending.values())Z.reject(J);this.pending.clear()};let j=O.canvas.transferControlToOffscreen(),Q={canvas:j,width:O.width,height:O.height,backgroundColor:O.backgroundColor};this.ready=this.call("init",Q,[j])}postMessage(O,$,A){let K=this.nextId++;return this.worker.postMessage({id:K,kind:O,payload:$},A??[]),K}call(O,$,A){let K=this.postMessage(O,$,A);return new Promise((j,Q)=>{this.pending.set(K,{resolve:j,reject:Q})})}async loadSource(O,$){await this.ready;let A={source:O,options:$};return this.call("loadSource",A)}async loadImage(O){await this.ready;let $={source:O};return this.call("loadImage",$)}async loadAudio(O,$){await this.ready;let A={source:O,options:$};return this.call("loadAudio",A)}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,$){await this.ready;let A={width:O,height:$};return this.call("resize",A)}async exportFrame(O,$){await this.ready;let A={frame:O,options:$};return this.call("exportFrame",A)}dispose(){try{this.worker.postMessage({id:this.nextId++,kind:"dispose"})}catch{}this.worker.terminate(),this.pending.clear()}}class t{canvas;ctx=null;width;height;backgroundColor;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;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.emitter=new y({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,A=$&&typeof Worker<"u"&&typeof OffscreenCanvas<"u"&&typeof this.canvas.transferControlToOffscreen==="function"&&!(this.canvas instanceof OffscreenCanvas);if($&&!A)throw Error("Worker compositor requires HTMLCanvasElement, OffscreenCanvas, and Worker support");if(this.sourcePool=new I,A)try{this.workerClient=new i({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:O.worker??!0})}catch(K){console.warn("[Compositor] Worker initialization failed, falling back to main thread rendering:",K),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 K=await this.workerClient.loadSource(O,$),j=this.createWorkerSource(K);if(this.audioManager&&K.hasAudio)await this.loadWorkerAudio(O,j.id);return this.emitter.emit("sourceloaded",{id:j.id,source:j}),j}let A=await this.sourcePool.loadVideo(O,$);return this.registerSourceAudio(A),this.emitter.emit("sourceloaded",{id:A.id,source:A}),A}async loadImage(O){if(this.checkDisposed(),this.workerClient){let A=await this.workerClient.loadImage(O),K=this.createWorkerSource(A);return this.emitter.emit("sourceloaded",{id:K.id,source:K}),K}let $=await this.sourcePool.loadImage(O);return this.emitter.emit("sourceloaded",{id:$.id,source:$}),$}async loadAudio(O,$){if(this.checkDisposed(),this.workerClient){let K=await this.workerClient.loadAudio(O,$),j=this.createWorkerSource(K);if(this.audioManager)await this.loadWorkerAudio(O,j.id);return this.emitter.emit("sourceloaded",{id:j.id,source:j}),j}let A=await this.sourcePool.loadAudio(O,$);return this.registerSourceAudio(A),this.emitter.emit("sourceloaded",{id:A.id,source:A}),A}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 A=this.audioScratch.newSourceIds,K=this.audioScratch.newSourceTimes,j=this.audioScratch.nextActiveSourceIds,Q=this.activeAudioSourceIds;A.length=0,K.length=0,j.clear();for(let V=0;V<O.length;V++){let J=O[V];if(J.muted)continue;let Z=J.source.id;if(!this.audioManager.hasSource(Z))continue;if(j.add(Z),!Q.has(Z))A.push(Z),K.push(J.sourceTime??$)}if(Q.size>0)Q.clear();for(let V of j)Q.add(V);this.audioManager.processAudioLayers(O,$);for(let V=0;V<A.length;V++)this.audioManager.startSourcePlayback(A[V],K[V])}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 U=this.serializeWorkerFrame(O);return this.workerClient.render(U)}let $=this.ctx;if(!$)return!1;let{visibleLayers:A,framePromises:K,frameImages:j}=this.renderBuffers;A.length=0,K.length=0,j.length=0;let Q=!1,V=-1/0,J=O.layers;for(let U=0;U<J.length;U++){let L=J[U];if(L.visible===!1)continue;let G=L.zIndex??0;if(G<V)Q=!0;V=G,A.push(L)}if(A.length===0)return $.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height),!0;if(Q)A.sort((U,L)=>(U.zIndex??0)-(L.zIndex??0));for(let U=0;U<A.length;U++){let L=A[U],G=L.sourceTime??O.time;K[U]=L.source.getFrameAt(G)}let Z=await Promise.all(K);for(let U=0;U<Z.length;U++)j[U]=Z[U]??null;$.fillStyle=this.backgroundColor,$.fillRect(0,0,this.width,this.height);for(let U=0;U<A.length;U++){let L=j[U];if(L)this.renderLayer(L,A[U])}return!0}renderLayer(O,$){let A=this.ctx;if(!A)return;let K=$.transform,j=$.source.width??this.width,Q=$.source.height??this.height;if(!K){A.drawImage(O,0,0,j,Q);return}let V=K.width??j,J=K.height??Q,Z=K.x??0,U=K.y??0,L=K.rotation??0,G=K.scaleX??1,P=K.scaleY??1,M=K.opacity??1,N=M!==1;if(!N&&!(L!==0||G!==1||P!==1)){A.drawImage(O,Z,U,V,J);return}let _=K.anchorX??0.5,E=K.anchorY??0.5;if(A.save(),N)A.globalAlpha=M;if(A.translate(Z+V*_,U+J*E),L!==0)A.rotate(L*Math.PI/180);if(G!==1||P!==1)A.scale(G,P);A.drawImage(O,-V*_,-J*E,V,J),A.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 A=await this.sourcePool.loadAudio(O,{id:$});this.workerAudioSources.set($,A),this.registerSourceAudio(A)}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,A=Array($.length);for(let K=0;K<$.length;K++){let j=$[K],Q=j.source.id;if(!this.workerSources.has(Q))throw Error(`Layer source ${Q} is not managed by this compositor`);A[K]={sourceId:Q,sourceTime:j.sourceTime,transform:j.transform,visible:j.visible,zIndex:j.zIndex}}return{time:O.time,layers:A}}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.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);this.startRenderLoop()}pause(){if(this.checkDisposed(),!this.state.playing)return;if(this.state.playing=!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($);let A=this.previewOptions.getComposition($);await this.render(A),this.state.seeking=!1,this.emitter.emit("seeked",{time:$}),this.emitter.emit("timeupdate",{currentTime:$})}startRenderLoop(){if(this.animationFrameId!==null)return;let O=()=>{if(!this.state.playing||!this.previewOptions)return;this.animationFrameId=requestAnimationFrame(O);let $=performance.now(),A=($-this.lastFrameTime)/1000;if(this.lastFrameTime=$,this.state.currentTime+=A,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 K=this.previewOptions.fps??0,j=K>0?1000/K:0;if(!this.renderPending&&(j===0||$-this.lastRenderTime>=j)){this.renderPending=!0,this.lastRenderTime=$;let V=this.previewOptions.getComposition(this.state.currentTime);if(this.audioManager)this.processAudioLayers(V.audio??[],this.state.currentTime);this.render(V).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 A=this.previewOptions.getComposition(O);if(this.workerClient){let K=this.serializeWorkerFrame(A);return this.workerClient.exportFrame(K,$)}if(await this.render(A),"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})}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,$){if(this.checkDisposed(),this.width=O,this.height=$,this.canvas.width=O,this.canvas.height=$,this.workerClient){this.workerClient.resize(O,$);return}this.clear()}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 WO,VideoSampleSink as FO}from"mediabunny";class S{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:A}=O;if($===0||A===0)return!1;let K=this.canvas.width,j=this.canvas.height;if(K===0||j===0)return!1;let Q=this.rotation===90||this.rotation===270,V=Q?A:$,J=Q?$:A,Z=Math.min(K/V,j/J),U=Math.round(V*Z),L=Math.round(J*Z),G=Math.round((K-U)/2),P=Math.round((j-L)/2);if(this.ctx.fillStyle="black",this.ctx.fillRect(0,0,K,j),this.ctx.save(),this.ctx.translate(G+U/2,P+L/2),this.rotation!==0)this.ctx.rotate(this.rotation*Math.PI/180);if(Q)this.ctx.drawImage(O,0,0,$,A,-L/2,-U/2,L,U);else this.ctx.drawImage(O,0,0,$,A,-U/2,-L/2,U,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 a{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 y{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 A=this.createShader($,$.VERTEX_SHADER,this.vertexShaderSource),
|
|
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 A=this.createShader($,$.VERTEX_SHADER,this.vertexShaderSource),K=this.createShader($,$.FRAGMENT_SHADER,this.fragmentShaderSource);if(!A||!K)throw Error("Failed to create shaders");let j=$.createProgram();if(!j)throw Error("Failed to create program");if($.attachShader(j,A),$.attachShader(j,K),$.linkProgram(j),!$.getProgramParameter(j,$.LINK_STATUS)){let V=$.getProgramInfoLog(j);throw Error(`Failed to link program: ${V}`)}this.resources.program=j,this.resources.positionLocation=$.getAttribLocation(j,"a_position"),this.resources.texCoordLocation=$.getAttribLocation(j,"a_texCoord"),this.resources.textureLocation=$.getUniformLocation(j,"u_texture"),this.setupQuadBuffers($);let Q=$.createTexture();if(!Q)throw Error("Failed to create texture");if($.bindTexture($.TEXTURE_2D,Q),$.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=Q,$.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,$,A){let K=O.createShader($);if(!K)return null;if(O.shaderSource(K,A),O.compileShader(K),!O.getShaderParameter(K,O.COMPILE_STATUS))return O.deleteShader(K),null;return K}setupQuadBuffers(O){let $=new Float32Array([-1,-1,1,-1,-1,1,1,1]),A=new Float32Array([0,1,1,1,0,0,1,0]),K=O.createBuffer();O.bindBuffer(O.ARRAY_BUFFER,K),O.bufferData(O.ARRAY_BUFFER,$,O.STATIC_DRAW),this.resources.vertexBuffer=K;let j=O.createBuffer();O.bindBuffer(O.ARRAY_BUFFER,j),O.bufferData(O.ARRAY_BUFFER,A,O.STATIC_DRAW),this.resources.texCoordBuffer=j}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:A,height:K}=O;if(A===0||K===0)return!1;let j=this.canvas.width,Q=this.canvas.height;if(j===0||Q===0)return!1;if($.viewport(0,0,j,Q),$.bindTexture($.TEXTURE_2D,this.resources.texture),A!==this.textureWidth||K!==this.textureHeight)$.texImage2D($.TEXTURE_2D,0,$.RGBA,$.RGBA,$.UNSIGNED_BYTE,O),this.textureWidth=A,this.textureHeight=K;else $.texSubImage2D($.TEXTURE_2D,0,0,0,$.RGBA,$.UNSIGNED_BYTE,O);$.clearColor(0,0,0,1),$.clear($.COLOR_BUFFER_BIT);let V=this.rotation===90||this.rotation===270,J=V?this.textureHeight:this.textureWidth,Z=V?this.textureWidth:this.textureHeight,U=Math.min(j/J,Q/Z),L=Math.round(J*U),G=Math.round(Z*U),P=Math.round((j-L)/2),M=Math.round((Q-G)/2),N=P/j*2-1,W=(P+L)/j*2-1,_=1-M/Q*2,E=1-(M+G)/Q*2,H=(N+W)/2,b=(_+E)/2,w=(W-N)/2,T=(_-E)/2,f=this.rotation*Math.PI/180,q=Math.cos(f),C=Math.sin(f),z=V?T:w,Y=V?w:T,X=this.positionsArray;return X[0]=-z*q- -Y*C+H,X[1]=-z*C+-Y*q+b,X[2]=z*q- -Y*C+H,X[3]=z*C+-Y*q+b,X[4]=-z*q-Y*C+H,X[5]=-z*C+Y*q+b,X[6]=z*q-Y*C+H,X[7]=z*C+Y*q+b,$.bindBuffer($.ARRAY_BUFFER,this.resources.vertexBuffer),$.bufferData($.ARRAY_BUFFER,X,$.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 r{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 y{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 A=O.gpu.getPreferredCanvasFormat();return this.context.configure({device:this.device,format:A,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}),A=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:A,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:A}=O;if($===0||A===0)return console.warn(`WebGPU: Source canvas has zero dimensions (${$}x${A})`),!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||A!==this.textureHeight)this.createTexture($,A),this.textureWidth=$,this.textureHeight=A;if(!this.texture)return!1;try{this.device.queue.copyExternalImageToTexture({source:O},{texture:this.texture},{width:$,height:A})}catch{let VO=O.getContext("2d");if(!VO)return!1;let zO=VO.getImageData(0,0,$,A),BO=new Uint8Array(zO.data.buffer);this.device.queue.writeTexture({texture:this.texture,origin:{x:0,y:0,z:0}},BO,{bytesPerRow:$*4,rowsPerImage:A},{width:$,height:A,depthOrArrayLayers:1})}let V=this.device.createCommandEncoder(),J=this.context.getCurrentTexture().createView(),K=V.beginRenderPass({colorAttachments:[{view:J,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]});if(K.setPipeline(this.pipeline),this.bindGroup)K.setBindGroup(0,this.bindGroup);let Z=this.rotation===90||this.rotation===270,U=Z?this.textureHeight:this.textureWidth,G=Z?this.textureWidth:this.textureHeight,L=Math.min(j/U,Q/G),P=Math.round(U*L),M=Math.round(G*L),X=Math.round((j-P)/2),E=Math.round((Q-M)/2),Y=X/j*2-1,q=(X+P)/j*2-1,H=1-E/Q*2,b=1-(E+M)/Q*2,w=(Y+q)/2,T=(H+b)/2,f=(q-Y)/2,C=(H-b)/2,R=this.rotation*Math.PI/180,B=Math.cos(R),N=Math.sin(R),z=Z?C:f,F=Z?f:C,D=this.quadArray;if(D[0]=-z*B- -F*N+w,D[1]=-z*N+-F*B+T,D[2]=0,D[3]=1,D[4]=z*B- -F*N+w,D[5]=z*N+-F*B+T,D[6]=1,D[7]=1,D[8]=-z*B-F*N+w,D[9]=-z*N+F*B+T,D[10]=0,D[11]=0,D[12]=z*B-F*N+w,D[13]=z*N+F*B+T,D[14]=1,D[15]=0,this.vertexBuffer)this.device.queue.writeBuffer(this.vertexBuffer,0,D),K.setVertexBuffer(0,this.vertexBuffer);return K.draw(4,1,0,0),K.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 W{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 r({canvas:this.canvas,powerPreference:this.powerPreference}),A=1000,j=performance.now();if(await(async()=>{while(!$.isReady()){if(performance.now()-j>A)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 t({canvas:this.canvas,powerPreference:this.powerPreference,preserveDrawingBuffer:!1,antialias:!1,alpha:!1});return O.isReady()?O:null}catch{return null}}createCanvas2DRenderer(){return new S({canvas:this.canvas})}static getSupportedRenderers(){let O=[];if(navigator.gpu)O.push("webgpu");try{let A=document.createElement("canvas");if(A.getContext("webgl")||A.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 W.getSupportedRenderers().includes(O)}}var a=new WeakMap;function LO(O,$){a.set(O,$)}function HO(O){let $=a.get(O);if($)a.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((A)=>{if(this.disposed||!this.resizeObserver)return;for(let j of A){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:A,height:j}=this.getCanvasDimensionsFromCanvas($);if(this.lastObservedWidth=A,this.lastObservedHeight=j,$.width!==A||$.height!==j){if($.width=A,$.height=j,this.updateCanvasAspectRatio(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}})}getCanvasDimensionsFromEntry(O,$){let A=0,j=0,Q=window.devicePixelRatio||1;if(O.devicePixelContentBoxSize?.length)A=O.devicePixelContentBoxSize[0].inlineSize,j=O.devicePixelContentBoxSize[0].blockSize;else if(O.contentBoxSize?.length)A=Math.round(O.contentBoxSize[0].inlineSize*Q),j=Math.round(O.contentBoxSize[0].blockSize*Q);else if(O.contentRect)A=Math.round(O.contentRect.width*Q),j=Math.round(O.contentRect.height*Q);if(A===0||j===0)return this.getCanvasDimensionsFromCanvas($);return{width:Math.max(1,A),height:Math.max(1,j)}}getCanvasDimensionsFromCanvas(O){let $=0,A=0,j=window.devicePixelRatio||1,Q=O.getBoundingClientRect();if($=Math.round(Q.width*j),A=Math.round(Q.height*j),$===0||A===0)$=Math.round(O.clientWidth*j)||$,A=Math.round(O.clientHeight*j)||A;if($===0||A===0)console.warn("Canvas has zero dimensions after all fallbacks, using 1x1");return{width:Math.max(1,$),height:Math.max(1,A)}}cleanupResizeObserver(){if(this.resizeObserver)this.resizeObserver.disconnect(),this.resizeObserver=null,this.lastObservedWidth=0,this.lastObservedHeight=0}retryUntilCanvasReady(O,$,A=60){let j=0,Q=()=>{if(j++,O.canvas.width>0&&O.canvas.height>0)$();else if(j<A)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:A}=this.getCanvasDimensionsFromCanvas(O);if(O.width!==$||O.height!==A)return O.width=$,O.height=A,!0;return!1}async initializeRenderer(O,$){if(this.debug)console.log(`Initializing renderer: ${$}`);let j=await new W({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 S({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 A=HO(O);if(this.sourceWidth=O.displayWidth,this.sourceHeight=O.displayHeight,!this.videoAspectRatio&&O.displayWidth&&O.displayHeight){let j=(K,Z)=>Z===0?K:j(Z,K%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 S({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(A?.canvasSink)this.canvasSink=A.canvasSink;else this.canvasSink=new FO(O,{rotation:this.options.rotation,poolSize:this.options.poolSize});if(this.sampleSink=new SO(O),this.disposed=!1,A?.firstFrame){if(this.currentFrame=A.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 A=this.canvasSink.canvases(O);this.frameIterator=A;try{let j=await A.next(),Q=await A.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 A=O;if(this.pluginManager)A=this.pluginManager.executeTransformFrame(O);if(!this.renderer.render(A.canvas)){if(this.debug)console.warn(`Failed to render frame with ${this.rendererType} (canvas: ${A.canvas.width}x${A.canvas.height})`);if(A.canvas.width===0||A.canvas.height===0)this.retryUntilCanvasReady(A,()=>{if(this.currentFrame===O&&this.renderer&&this.renderer.isReady()){if(!this.renderer.render(A.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 A=this.canvas.getContext("2d");if(!A)return;this.pluginManager.executeOverlays(A,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,$,A=1){if(!this.canvasSink)return[];let j=[],Q=[];for(let V=O;V<=$;V+=A)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 A=await this.canvasSink.getCanvas(O);if(A)this.renderFrame(A)}if("toBlob"in this.canvas)return new Promise((A)=>{this.canvas.toBlob((j)=>A(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 A=this.canvas,j=A.parentElement;if(!j)throw Error("Cannot switch renderer: Canvas has no parent element");let Q=document.createElement("canvas");if(Q.width=A.width,Q.height=A.height,Q.className=A.className,Q.id=A.id,Q.style.cssText=A.style.cssText,Array.from(A.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,A),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 S({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(A){if(this.debug)console.error(`Failed to switch to ${O}:`,A);if(!this.renderer){if(this.renderer=new S({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 W.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 I=2,h="[MediaFox]";function bO(O){I=O}function wO(O,...$){if(I<=0)console.debug(`${h} ${O}`,...$)}function TO(O,...$){if(I<=1)console.info(`${h} ${O}`,...$)}function yO(O,...$){if(I<=2)console.warn(`${h} ${O}`,...$)}function fO(O,...$){if(I<=3)console.error(`${h} ${O}`,...$)}var k={setLevel:bO,debug:wO,info:TO,warn:yO,error:fO};class e{deps;constructor(O){this.deps=O}async load(O,$={}){try{let A=await this.deps.pluginManager.executeBeforeLoad(O);if(A?.cancel)return;if(A?.data!==void 0)O=A.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,K,Z,U]=await Promise.all([V.computeDuration(),V.getFormat(),V.getMimeType(),V.getMetadataTags()]),G={duration:J,format:K.name,mimeType:Z,metadata:U,hasVideo:this.deps.trackManager.hasVideo(),hasAudio:this.deps.trackManager.hasAudio(),hasSubtitles:this.deps.trackManager.hasSubtitles()};this.deps.state.updateDuration(J),this.deps.state.updateMediaInfo(G),this.deps.state.updateTracks(this.deps.trackManager.getVideoTracks(),this.deps.trackManager.getAudioTracks(),this.deps.trackManager.getSubtitleTracks()),this.deps.playbackController.setDuration(J);let L=Q.prefetchedData,P=L?.videoTrack??this.deps.trackManager.getPrimaryVideoTrack(),M=L?.audioTrack??this.deps.trackManager.getPrimaryAudioTrack();if(P&&L?.canvasSink&&L?.firstFrame)LO(P,{canvasSink:L.canvasSink,firstFrame:L.firstFrame});let X="",E=!1,Y=!1;if(P||M){let q=await this.deps.trackSwitcher.setupInitialTracks(P,M);X+=q.warningMessage,E=q.videoSupported,Y=q.audioSupported}if(!E&&!Y){if(!X)X="No audio or video track found.";throw Error(X)}if(X&&(E||Y))this.deps.emit("warning",{type:"codec-warning",message:X.trim(),error:void 0});if(this.deps.state.updateReadyState(!0,!0),this.deps.emit("loadedmetadata",G),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(G),$.autoplay)await this.play();if($.startTime!==void 0)await this.seek($.startTime)}catch(A){throw this.handleError(A),A}}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 A=await this.deps.pluginManager.executeBeforeSeek(O);if(A?.cancel)return;if(A?.data!==void 0)O=A.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(),A=$.currentPlaylistIndex;if(A!==null&&$.playlist.length>0){let j=[...$.playlist],Q=j[A];if(Q)j[A]={...Q,duration:O},this.deps.state.updatePlaylist(j,A)}}}class OO{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,$,A){this.store.setState({volume:O,muted:$,playbackRate:A})}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,$,A){this.store.updateTracks(O,$,A)}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 $O{chains=new Map;async run(O,$){let A=this.chains.get(O)??Promise.resolve(),j,Q=new Promise((V)=>{j=V});this.chains.set(O,A.then(()=>Q));try{return await A,await $()}finally{j?.()}}}class AO{deps;locks=new $O;constructor(O){this.deps=O}async setupInitialTracks(O,$){let A=!1,j=!1,Q="";if(O)try{if(A=await this.locks.run("video",async()=>{if(O.codec!==null&&await O.canDecode())return await this.deps.playbackController.trySetVideoTrack(O);return!1}),!A)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(!A&&!j&&!Q)Q="No audio or video track found.";return{videoSupported:A,audioSupported:j,warningMessage:Q}}async selectVideoTrack(O,$){if(!O.selectVideoTrack($))throw Error(`Invalid video track ID: ${$}`);let A=O.getSelectedVideoTrack();if(!A){await this.deps.playbackController.setVideoTrack(null);return}if(!await this.locks.run("video",async()=>{if(A.codec!==null&&await A.canDecode())return await this.deps.playbackController.trySetVideoTrack(A);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 A=O.getSelectedAudioTrack();if(!A){await this.deps.playbackController.setAudioTrack(null);return}if(!await this.locks.run("audio",async()=>{if(A.codec!==null&&await A.canDecode())return await this.deps.playbackController.trySetAudioTrack(A);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,A=$.AudioContext||$.webkitAudioContext;this.audioContext=new A}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:A,timestamp:j}of $){if(O!==this.playbackId||this.disposed||!this.playing)break;let Q=this.audioContext.createBufferSource();Q.buffer=A,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<A.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 A=setInterval(()=>{if(O-this.getCurrentTime()<1||!this.playing)clearInterval(A),$()},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 A=this.playing,j=this.getCurrentTime();if(this.playbackRate=$,A)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 kO=100,vO=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,A=this.getCurrentTime();if(!O){this.audioManager.dispose();return}let j=await O.computeDuration(),Q=Math.max(0,Math.min(A,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:A}=this.videoRenderer.updateFrame(this.currentTime);if(A&&!this.isWaiting){if(this.isWaiting=!0,this.onWaiting)this.onWaiting()}else if(!A&&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),kO)}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)},vO)}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 jO{store;emitter;switchSource;sourceManager;constructor(O,$,A,j){this.store=O,this.emitter=$,this.switchSource=A,this.sourceManager=j}async loadPlaylist(O,$={}){let A=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(A,A.length>0?0:null),this.emitter.emit("playlistchange",{playlist:A}),A.length>0&&this.switchSource){let j=A[0];if($.startTime!==void 0)j.savedPosition=$.startTime;await this.switchSource(j,$.autoplay??!1)}}addToPlaylist(O,$){let A=this.createPlaylistItem(O);this.store.addToPlaylist(A,$),this.emitter.emit("playlistadd",{item:A,index:$??this.store.getState().playlist.length-1})}async removeFromPlaylist(O){let $=this.store.getState(),A=$.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(A===O&&V!==null&&V!==A&&this.switchSource){let J=Q.playlist[V];try{await this.switchSource(J,j)}catch(K){this.emitter.emit("playlistitemerror",{index:V,error:K})}}}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,A=O.playlist,j=O.playlistMode,Q=null;if(j==="repeat-one")Q=$;else if(j==="sequential"||j==="repeat")if($<A.length-1)Q=$+1;else if(j==="repeat")Q=0;else{this.emitter.emit("playlistend",void 0);return}else if($<A.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 A=this.store.getState().playlist;if(O>=0&&O<A.length)await this.switchTo(O);else if(O>=A.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((A)=>A!==null).join(", ")}, null`);this.store.updatePlaylistMode(O)}async switchTo(O){let $=this.store.getState(),A=$.currentPlaylistIndex,j=$.playing,Q=[...$.playlist];if(A!==null&&A!==O){let K=Q[A];Q[A]={...K,savedPosition:$.currentTime}}this.store.updatePlaylist(Q,O);let V=Q[O];if(this.emitter.emit("playlistitemchange",{index:O,item:V,previousIndex:A??void 0}),this.switchSource)await this.switchSource(V,j);if($.playlistMode==="sequential"&&O<Q.length-1){let K=Q[O+1];this.sourceManager?.preloadSource(K.mediaSource,K.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 PO(O,$,A,j){let Q=$.name;return{player:O,getState(){return O.getState()},subscribe(V){let J=O.subscribe(V),K=()=>J.unsubscribe();return A.stateUnsubscribes.push(K),K},getPluginState(){return A.pluginState},setPluginState(V){A.pluginState=V},on(V,J){O.on(V,J);let K=A.eventListeners.get(V);if(!K)K=new Set,A.eventListeners.set(V,K);K.add(J)},off(V,J){O.off(V,J);let K=A.eventListeners.get(V);if(K)K.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 QO{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 A={plugin:O,context:null,eventListeners:new Map,stateUnsubscribes:[],pluginState:void 0};A.context=PO(this.player,O,A,()=>this.plugins),this.plugins.set($,A);try{await O.install(A.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 A=this.overlays.some((j)=>j.pluginName===O);if(this.overlays=this.overlays.filter((j)=>j.pluginName!==O),this.plugins.delete(O),A)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 A=$.plugin.hooks?.lifecycle?.beforeLoad;if(!A)continue;try{let j=await A.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 A=$.plugin.hooks?.lifecycle?.afterLoad;if(!A)continue;try{await A.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 A=await $.call(O.plugin);if(A?.cancel)return A}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in beforePlay hook:`,A)}}return}executeAfterPlay(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPlay;if(!$)continue;try{$.call(O.plugin)}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in afterPlay hook:`,A)}}}async executeBeforePause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforePause;if(!$)continue;try{let A=await $.call(O.plugin);if(A?.cancel)return A}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in beforePause hook:`,A)}}return}executeAfterPause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPause;if(!$)continue;try{$.call(O.plugin)}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in afterPause hook:`,A)}}}async executeBeforeSeek(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.lifecycle?.beforeSeek;if(!A)continue;try{let j=await A.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 A=$.plugin.hooks?.lifecycle?.afterSeek;if(!A)continue;try{A.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 A=await $.call(O.plugin);if(A?.cancel)return A}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in beforeStop hook:`,A)}}return}executeAfterStop(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterStop;if(!$)continue;try{$.call(O.plugin)}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in afterStop hook:`,A)}}}executeOnError(O){let $=!1;for(let[,A]of this.plugins){let j=A.plugin.hooks?.lifecycle?.onError;if(!j)continue;try{if(j.call(A.plugin,O)?.handled)$=!0}catch(Q){console.error(`[MediaFox:${A.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(A){console.error(`[MediaFox:${O.plugin.name}] Error in onEnded hook:`,A)}}}executeBeforeRender(O,$){for(let[,A]of this.plugins){let j=A.plugin.hooks?.render?.beforeRender;if(!j)continue;try{let Q=j.call(A.plugin,O,$);if(Q?.skip)return Q}catch(Q){console.error(`[MediaFox:${A.plugin.name}] Error in beforeRender hook:`,Q)}}return}executeTransformFrame(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.render?.transformFrame;if(!A)continue;try{let j=A.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 A=$.plugin.hooks?.render?.afterRender;if(!A)continue;try{A.call($.plugin,O)}catch(j){console.error(`[MediaFox:${$.plugin.name}] Error in afterRender hook:`,j)}}}executeOverlays(O,$,A){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,$,A)}catch(Q){console.error(`[MediaFox:${j.pluginName}] Error in onOverlay hook:`,Q)}}executeBeforeStateUpdate(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.state?.beforeStateUpdate;if(!A)continue;try{let j=A.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[,A]of this.plugins){let j=A.plugin.hooks?.state?.onStateChange;if(!j)continue;try{j.call(A.plugin,O,$)}catch(Q){console.error(`[MediaFox:${A.plugin.name}] Error in onStateChange hook:`,Q)}}}executeBeforeEvent(O,$){for(let[,A]of this.plugins){let j=A.plugin.hooks?.event?.beforeEvent;if(!j)continue;try{let Q=j.call(A.plugin,O,$);if(Q?.cancel)return Q;if(Q?.data!==void 0)$=Q.data}catch(Q){console.error(`[MediaFox:${A.plugin.name}] Error in beforeEvent hook:`,Q)}}return{data:$}}executeAfterEvent(O,$){for(let[,A]of this.plugins){let j=A.plugin.hooks?.event?.afterEvent;if(!j)continue;try{j.call(A.plugin,O,$)}catch(Q){console.error(`[MediaFox:${A.plugin.name}] Error in afterEvent hook:`,Q)}}}executeOnAudioNode(O,$){let A=$;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)A=V}catch(V){console.error(`[MediaFox:${j.plugin.name}] Error in onAudioNode hook:`,V)}}return A}async dispose(){let O=Array.from(this.plugins.keys()).reverse();for(let $ of O)try{await this.uninstall($)}catch(A){console.error(`Error disposing plugin "${$}":`,A)}}}import{ALL_FORMATS as hO,BlobSource as pO,BufferSource as gO,CanvasSink as mO,FilePathSource as uO,Input as lO,ReadableStreamSource as cO,UrlSource as oO}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 A,j;if(O instanceof File||O instanceof Blob)A=new pO(O,{maxCacheSize:this.options.maxCacheSize}),j="blob";else if(O instanceof ArrayBuffer||O instanceof Uint8Array)A=new gO(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"))A=new uO(J,{maxCacheSize:this.options.maxCacheSize}),j="file";else A=new oO(J,{maxCacheSize:this.options.maxCacheSize,requestInit:this.options.requestInit}),j="url"}else if(typeof ReadableStream<"u"&&O instanceof ReadableStream)A=new cO(O,{maxCacheSize:this.options.maxCacheSize}),j="stream";else throw TypeError("Unsupported media source type");let Q=new lO({source:A,formats:hO}),V={source:A,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 A=await this.createSource(O,$);if(A.input)try{let j=await this.prefetchTrackData(A.input);A.prefetchedData=j}catch{}}async prefetchTrackData(O){let $=await O.getVideoTracks(),A=await O.getAudioTracks(),j=$.length>0?$[0]:null,Q=A.length>0?A[0]:null,V=null,J=null,K=0;if(j){if(j.codec!==null&&await j.canDecode()){V=new mO(j,{poolSize:2});let Z=V.canvases(0);J=(await Z.next()).value??null,await Z.return(),K=await j.computeDuration()}}if(!K&&Q)K=await Q.computeDuration();return{videoTrack:j,audioTrack:Q,canvasSink:V,firstFrame:J,duration:K}}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 A=await fetch(O,$);if(!A.ok)throw Error(`Failed to fetch: ${A.status} ${A.statusText}`);return A.blob()}static fromStreamingFetch(O,$){return new ReadableStream({async start(A){let j=await fetch(O,$);if(!j.ok){A.error(Error(`Failed to fetch: ${j.status} ${j.statusText}`));return}let Q=j.body?.getReader();if(!Q){A.error(Error("Response body is not readable"));return}try{while(!0){let{done:V,value:J}=await Q.read();if(V)break;A.enqueue(J)}A.close()}catch(V){A.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 A=0;A<O.length;A++)if(!l(O[A],$[A]))return!1;return!0}if(XO(O)&&XO($)){let A=Object.keys(O),j=Object.keys($);if(A.length!==j.length)return!1;for(let Q of A){if(!Object.hasOwn($,Q))return!1;if(!l(O[Q],$[Q]))return!1}return!0}return!1}function XO(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 $=W.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 A=this.pluginManager.executeBeforeStateUpdate(O);if(A===null)return;O=A}let $=Object.keys(O);for(let A=0;A<$.length;A++){let j=$[A];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 A=0;A<O.length;A++){let j=O[A];if(!l(this.pendingUpdates[j],this.state[j])){$=!0;break}}for(let A=0;A<O.length;A++){let j=O[A];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 A of this.listeners)$.push(A);for(let A=0;A<$.length;A++)try{$[A](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,$,A){let j={};if(O)j.videoTracks=O;if($)j.audioTracks=$;if(A)j.subtitleTracks=A;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 A=this.state.playlist,j,Q=this.state.currentPlaylistIndex;if($!==void 0&&$>=0&&$<=A.length){if(j=[...A.slice(0,$),O,...A.slice($)],Q!==null&&Q>=$)Q+=1}else j=[...A,O];this.setState({playlist:j,currentPlaylistIndex:Q})}removeFromPlaylist(O){let $=this.state.playlist;if(O<0||O>=$.length)return;let A=$.filter((Q,V)=>V!==O),j=this.state.currentPlaylistIndex;if(j===O)j=A.length>0?0:null;else if(j!==null&&j>O)j-=1;if(A.length===0)this.setState({playlist:A,currentPlaylistIndex:null,state:"idle",currentTime:0,duration:0,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null});else this.setState({playlist:A,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(A)=>{let j=`video-${A.id}`;this.videoTracks.set(j,A);let Q=0,V=0;try{let K=await A.computePacketStats(100);Q=K.averagePacketRate,V=K.averageBitrate}catch{}return{id:j,codec:A.codec,language:A.languageCode,name:A.name,width:A.codedWidth,height:A.codedHeight,frameRate:Q,bitrate:V,rotation:A.rotation,selected:!1,decodable:await A.canDecode()}}));let $=await this.input.getAudioTracks();if(this.audioTrackInfos=await Promise.all($.map(async(A)=>{let j=`audio-${A.id}`;this.audioTracks.set(j,A);let Q=0;try{Q=(await A.computePacketStats(100)).averageBitrate}catch{}return{id:j,codec:A.codec,language:A.languageCode,name:A.name,channels:A.numberOfChannels,sampleRate:A.sampleRate,bitrate:Q,selected:!1,decodable:await A.canDecode()}})),this.videoTrackInfos.length>0){let A=this.videoTrackInfos.find((j)=>j.decodable);if(A)this.selectVideoTrack(A.id)}if(this.audioTrackInfos.length>0){let A=this.audioTrackInfos.find((j)=>j.decodable);if(A)this.selectAudioTrack(A.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((A)=>{A.selected=A.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((A)=>{A.selected=A.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((A)=>{A.selected=A.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 A of this.subtitleProviders.values())for(let j of A){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((A)=>{A.selected=A.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 A=null;for(let[Q,V]of this.audioTracks.entries())if(V.id===O){A=Q;break}if(!A)return;this.audioTracks.set(A,$);let j=this.audioTrackInfos.findIndex((Q)=>Q.id===A);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 A=null;for(let[Q,V]of this.videoTracks.entries())if(V.id===O){A=Q;break}if(!A)return;this.videoTracks.set(A,$);let j=this.videoTrackInfos.findIndex((Q)=>Q.id===A);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 y({maxListeners:100}),this.store=new c,this.state=new OO(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 jO(this.store,this.emitter,async($,A)=>{await this.core.load($.mediaSource,{startTime:$.savedPosition??0,autoplay:A,playlistItemId:$.id})},this.sourceManager),this.trackSwitcher=new AO({sourceManager:this.sourceManager,trackManager:this.trackManager,playbackController:this.playbackController,emit:this.emit.bind(this),store:this.store,getCurrentInput:this.getCurrentInput}),this.pluginManager=new QO(this),this.core=new e({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:A}=O;if($==="repeat-one"){let j=A;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=A<O.playlist.length-1?A+1:0;queueMicrotask(async()=>{try{await this.playlistManager.next()}catch(Q){this.emitter.emit("playlistitemerror",{index:j,error:Q})}})}else if($==="sequential"&&A<O.playlist.length-1){let j=A+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 A=this.getState();if(A.playlist.length===0||$.replacePlaylist){await this.playlistManager.loadPlaylist([{mediaSource:O}],{autoplay:$.autoplay??this.options.autoplay,startTime:$.startTime});return}else if(A.currentPlaylistIndex!==null&&A.playlist.length>0){let j=A.currentPlaylistIndex,V={...A.playlist[j],mediaSource:O,savedPosition:0,duration:null},J=[...A.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 A=this.state.getState().mediaInfo;if(A)this.state.updateMediaInfo({...A,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 W.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 A=this.pluginManager.executeBeforeEvent(O,$);if(A?.cancel)return;let j=A?.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 dO={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 YO;((U)=>{U.MEDIA_NOT_SUPPORTED="MEDIA_NOT_SUPPORTED";U.MEDIA_LOAD_FAILED="MEDIA_LOAD_FAILED";U.DECODE_ERROR="DECODE_ERROR";U.NETWORK_ERROR="NETWORK_ERROR";U.PERMISSION_DENIED="PERMISSION_DENIED";U.PLAYBACK_ERROR="PLAYBACK_ERROR";U.TRACK_NOT_FOUND="TRACK_NOT_FOUND";U.INVALID_STATE="INVALID_STATE";U.UNKNOWN_ERROR="UNKNOWN_ERROR"})(YO||={});class _ extends Error{code;details;constructor(O,$,A){super($);this.name="MediaFoxError",this.code=O,this.details=A}static mediaNotSupported(O="Media format not supported",$){return new _("MEDIA_NOT_SUPPORTED",O,$)}static mediaLoadFailed(O="Failed to load media",$){return new _("MEDIA_LOAD_FAILED",O,$)}static decodeError(O="Failed to decode media",$){return new _("DECODE_ERROR",O,$)}static networkError(O="Network error occurred",$){return new _("NETWORK_ERROR",O,$)}static permissionDenied(O="Permission denied",$){return new _("PERMISSION_DENIED",O,$)}static playbackError(O="Playback error occurred",$){return new _("PLAYBACK_ERROR",O,$)}static trackNotFound(O="Track not found",$){return new _("TRACK_NOT_FOUND",O,$)}static invalidState(O="Invalid player state",$){return new _("INVALID_STATE",O,$)}static unknownError(O="Unknown error occurred",$){return new _("UNKNOWN_ERROR",O,$)}}function nO(O,$){if(O instanceof _)return O;if(O instanceof Error)return new _("UNKNOWN_ERROR",`${$}: ${O.message}`,{originalError:O});return new _("UNKNOWN_ERROR",`${$}: ${String(O)}`,{originalError:O})}function sO(O,$=!1){let A=Math.abs(O),j=Math.floor(A/3600),Q=Math.floor(A%3600/60),V=Math.floor(A%60),J=Math.floor(A%1*1000),K="";if(O<0)K="-";if(j>0)K+=`${j}:${Q.toString().padStart(2,"0")}:${V.toString().padStart(2,"0")}`;else K+=`${Q}:${V.toString().padStart(2,"0")}`;if($)K+=`.${J.toString().padStart(3,"0")}`;return K}function iO(O){let $=O.trim().split(":").map(Number);if($.some(Number.isNaN))throw Error("Invalid time string");let A=0;if($.length===3)A=$[0]*3600+$[1]*60+$[2];else if($.length===2)A=$[0]*60+$[1];else if($.length===1)A=$[0];else throw Error("Invalid time format");return A}function tO(O,$){return Math.floor(O*$)}function rO(O,$){return O/$}function aO(O,$,A){return Math.max($,Math.min(A,O))}function eO(O,$){return O.start<$.end&&$.start<O.end}function O$(O){if(O.length===0)return[];let $=[...O].sort((j,Q)=>j.start-Q.start),A=[$[0]];for(let j=1;j<$.length;j++){let Q=A[A.length-1],V=$[j];if(V.start<=Q.end)Q.end=Math.max(Q.end,V.end);else A.push(V)}return A}function $$(O){return O.reduce(($,A)=>$+(A.end-A.start),0)}function A$(O,$){for(let A of O)if($>=A.start&&$<A.end)return A;return null}var DA="0.1.0",xA=d;export{nO as wrapError,$$ as totalBufferedDuration,tO as timeToFrame,eO as timeRangesOverlap,iO as parseTime,O$ as mergeTimeRanges,rO as frameToTime,sO as formatTime,A$ as findBufferedRange,xA as default,aO as clamp,v as VideoRenderer,DA as VERSION,o as TrackManager,c as Store,x as SourcePool,u as SourceManager,dO as Source,W as RendererFactory,g as PlaybackController,_ as MediaFoxError,d as MediaFox,y as EventEmitter,YO as ErrorCode,i 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 A=O.gpu.getPreferredCanvasFormat();return this.context.configure({device:this.device,format:A,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}),A=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:A,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:A}=O;if($===0||A===0)return console.warn(`WebGPU: Source canvas has zero dimensions (${$}x${A})`),!1;let K=this.canvas.width,j=this.canvas.height;if(K===0||j===0)return console.warn(`WebGPU: Output canvas has zero dimensions (${K}x${j})`),!1;if($!==this.textureWidth||A!==this.textureHeight)this.createTexture($,A),this.textureWidth=$,this.textureHeight=A;if(!this.texture)return!1;try{this.device.queue.copyExternalImageToTexture({source:O},{texture:this.texture},{width:$,height:A})}catch{let n=O.getContext("2d");if(!n)return!1;if(!("getImageData"in n))return!1;let zO=n.getImageData(0,0,$,A),YO=new Uint8Array(zO.data.buffer);this.device.queue.writeTexture({texture:this.texture,origin:{x:0,y:0,z:0}},YO,{bytesPerRow:$*4,rowsPerImage:A},{width:$,height:A,depthOrArrayLayers:1})}let Q=this.device.createCommandEncoder(),V=this.context.getCurrentTexture().createView(),J=Q.beginRenderPass({colorAttachments:[{view:V,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]});if(J.setPipeline(this.pipeline),this.bindGroup)J.setBindGroup(0,this.bindGroup);let Z=this.rotation===90||this.rotation===270,U=Z?this.textureHeight:this.textureWidth,L=Z?this.textureWidth:this.textureHeight,G=Math.min(K/U,j/L),P=Math.round(U*G),M=Math.round(L*G),N=Math.round((K-P)/2),W=Math.round((j-M)/2),_=N/K*2-1,E=(N+P)/K*2-1,H=1-W/j*2,b=1-(W+M)/j*2,w=(_+E)/2,T=(H+b)/2,f=(E-_)/2,q=(H-b)/2,C=this.rotation*Math.PI/180,z=Math.cos(C),Y=Math.sin(C),X=Z?q:f,F=Z?f:q,B=this.quadArray;if(B[0]=-X*z- -F*Y+w,B[1]=-X*Y+-F*z+T,B[2]=0,B[3]=1,B[4]=X*z- -F*Y+w,B[5]=X*Y+-F*z+T,B[6]=1,B[7]=1,B[8]=-X*z-F*Y+w,B[9]=-X*Y+F*z+T,B[10]=0,B[11]=0,B[12]=X*z-F*Y+w,B[13]=X*Y+F*z+T,B[14]=1,B[15]=0,this.vertexBuffer)this.device.queue.writeBuffer(this.vertexBuffer,0,B),J.setVertexBuffer(0,this.vertexBuffer);return J.draw(4,1,0,0),J.end(),this.device.queue.submit([Q.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 R{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 K of $){let j=await this.createRenderer(K);if(j?.isReady())return{renderer:j,actualType:K};if(j)j.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 r({canvas:this.canvas,powerPreference:this.powerPreference}),A=1000,K=performance.now();if(await(async()=>{while(!$.isReady()){if(performance.now()-K>A)return console.log("WebGPU renderer initialization timed out"),!1;if("requestIdleCallback"in window)await new Promise((V)=>requestIdleCallback(()=>V(void 0)));else await new Promise((V)=>requestAnimationFrame(()=>V(void 0)))}return!0})())return console.log("WebGPU renderer initialized successfully"),$;return $.isReady()?$:null}createWebGLRenderer(){try{let O=new a({canvas:this.canvas,powerPreference:this.powerPreference,preserveDrawingBuffer:!1,antialias:!1,alpha:!1});return O.isReady()?O:null}catch{return null}}createCanvas2DRenderer(){return new S({canvas:this.canvas})}static getSupportedRenderers(){let O=[];if(navigator.gpu)O.push("webgpu");try{let A=document.createElement("canvas");if(A.getContext("webgl")||A.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 R.getSupportedRenderers().includes(O)}}var e=new WeakMap;function LO(O,$){e.set(O,$)}function SO(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((A)=>{if(this.disposed||!this.resizeObserver)return;for(let K of A){let{width:j,height:Q}=this.getCanvasDimensionsFromEntry(K,$);if(j!==this.lastObservedWidth||Q!==this.lastObservedHeight){if(this.lastObservedWidth=j,this.lastObservedHeight=Q,$.width!==j||$.height!==Q){if($.width=j,$.height=Q,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:A,height:K}=this.getCanvasDimensionsFromCanvas($);if(this.lastObservedWidth=A,this.lastObservedHeight=K,$.width!==A||$.height!==K){if($.width=A,$.height=K,this.updateCanvasAspectRatio(),this.currentFrame&&this.renderer&&this.renderer.isReady())this.renderFrame(this.currentFrame)}})}getCanvasDimensionsFromEntry(O,$){let A=0,K=0,j=window.devicePixelRatio||1;if(O.devicePixelContentBoxSize?.length)A=O.devicePixelContentBoxSize[0].inlineSize,K=O.devicePixelContentBoxSize[0].blockSize;else if(O.contentBoxSize?.length)A=Math.round(O.contentBoxSize[0].inlineSize*j),K=Math.round(O.contentBoxSize[0].blockSize*j);else if(O.contentRect)A=Math.round(O.contentRect.width*j),K=Math.round(O.contentRect.height*j);if(A===0||K===0)return this.getCanvasDimensionsFromCanvas($);return{width:Math.max(1,A),height:Math.max(1,K)}}getCanvasDimensionsFromCanvas(O){let $=0,A=0,K=window.devicePixelRatio||1,j=O.getBoundingClientRect();if($=Math.round(j.width*K),A=Math.round(j.height*K),$===0||A===0)$=Math.round(O.clientWidth*K)||$,A=Math.round(O.clientHeight*K)||A;if($===0||A===0)console.warn("Canvas has zero dimensions after all fallbacks, using 1x1");return{width:Math.max(1,$),height:Math.max(1,A)}}cleanupResizeObserver(){if(this.resizeObserver)this.resizeObserver.disconnect(),this.resizeObserver=null,this.lastObservedWidth=0,this.lastObservedHeight=0}retryUntilCanvasReady(O,$,A=60){let K=0,j=()=>{if(K++,O.canvas.width>0&&O.canvas.height>0)$();else if(K<A)requestAnimationFrame(j);else{if(this.debug)console.warn("Canvas dimensions timeout, forcing action");$()}};requestAnimationFrame(j)}updateCanvasAspectRatio(){if(!this.canvas||!this.videoAspectRatio||!("style"in this.canvas))return;this.canvas.style.aspectRatio=this.videoAspectRatio}updateCanvasBackingBuffer(O){let{width:$,height:A}=this.getCanvasDimensionsFromCanvas(O);if(O.width!==$||O.height!==A)return O.width=$,O.height=A,!0;return!1}async initializeRenderer(O,$){if(this.debug)console.log(`Initializing renderer: ${$}`);let K=await new R({canvas:O}).createRendererWithFallback($);if(this.debug)console.log(`Renderer factory result: ${K.actualType}`);if(!K.renderer.isReady()){if(this.debug)console.warn(`VideoRenderer: Renderer (${K.actualType}) not ready`);throw K.renderer.dispose(),Error(`Failed to initialize renderer: ${K.actualType}`)}if(this.renderer=K.renderer,this.rendererType=K.actualType,this.debug)console.log(`Initialized renderer: ${this.rendererType}`);if(K.actualType!==$){if(this.onRendererFallback)this.onRendererFallback($,K.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 S({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 A=SO(O);if(this.sourceWidth=O.displayWidth,this.sourceHeight=O.displayHeight,!this.videoAspectRatio&&O.displayWidth&&O.displayHeight){let K=(J,Z)=>Z===0?J:K(Z,J%Z),j=K(O.displayWidth,O.displayHeight),Q=O.displayWidth/j,V=O.displayHeight/j;this.videoAspectRatio=`${Q}/${V}`,this.updateCanvasAspectRatio()}if(this.notifyRotationChange(),this.initPromise)try{await this.initPromise}catch(K){if(this.debug)console.error("Renderer initialization failed:",K)}if(!this.renderer){if(this.debug)console.warn("Renderer not ready, creating Canvas2D fallback");if(this.canvas){if(this.renderer=new S({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 K=this.options.width??O.displayWidth,j=this.options.height??O.displayHeight;if(this.canvas.width!==K||this.canvas.height!==j)this.canvas.width=K,this.canvas.height=j,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(A?.canvasSink)this.canvasSink=A.canvasSink;else this.canvasSink=new WO(O,{rotation:this.options.rotation,poolSize:this.options.poolSize});if(this.sampleSink=new FO(O),this.disposed=!1,A?.firstFrame){if(this.currentFrame=A.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(K){if(this.debug)console.error("Initial seek failed:",K)}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 A=this.canvasSink.canvases(O);this.frameIterator=A;try{let K=await A.next(),j=await A.next();if($!==this.renderingId)return;let Q=K.value??null,V=j.value??null;if(Q)if(this.currentFrame=Q,Q.canvas.width>0&&Q.canvas.height>0)this.renderFrame(Q);else this.retryUntilCanvasReady(Q,()=>this.renderFrame(Q),30);if(this.nextFrame=V,!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 K=(await O.next()).value??null;if(!K||$!==this.renderingId||this.disposed)return;this.nextFrame=K}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 A=O;if(this.pluginManager)A=this.pluginManager.executeTransformFrame(O);if(!this.renderer.render(A.canvas)){if(this.debug)console.warn(`Failed to render frame with ${this.rendererType} (canvas: ${A.canvas.width}x${A.canvas.height})`);if(A.canvas.width===0||A.canvas.height===0)this.retryUntilCanvasReady(A,()=>{if(this.currentFrame===O&&this.renderer&&this.renderer.isReady()){if(!this.renderer.render(A.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 A=this.canvas.getContext("2d");if(!A)return;this.pluginManager.executeOverlays(A,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,$,A=1){if(!this.canvasSink)return[];let K=[],j=[];for(let Q=O;Q<=$;Q+=A)j.push(Q);for await(let Q of this.canvasSink.canvasesAtTimestamps(j))if(Q)K.push(Q);return K}async screenshot(O,$={}){if(!this.canvas)return null;if(O!==void 0&&this.canvasSink){let A=await this.canvasSink.getCanvas(O);if(A)this.renderFrame(A)}if("toBlob"in this.canvas)return new Promise((A)=>{this.canvas.toBlob((K)=>A(K),`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 A=this.canvas,K=A.parentElement;if(!K)throw Error("Cannot switch renderer: Canvas has no parent element");let j=document.createElement("canvas");if(j.width=A.width,j.height=A.height,j.className=A.className,j.id=A.id,j.style.cssText=A.style.cssText,Array.from(A.attributes).forEach((Q)=>{if(Q.name!=="id"&&Q.name!=="class"&&Q.name!=="style")j.setAttribute(Q.name,Q.value)}),this.renderer)this.renderer.dispose(),this.renderer=null;K.replaceChild(j,A),this.canvas=j;try{await this.initializeRenderer(j,O)}catch(Q){if(this.debug)console.error(`Failed to switch to ${O}:`,Q);if(!this.renderer){if(this.renderer=new S({canvas:j}),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(A){if(this.debug)console.error(`Failed to switch to ${O}:`,A);if(!this.renderer){if(this.renderer=new S({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 R.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 x=2,h="[MediaFox]";function HO(O){x=O}function bO(O,...$){if(x<=0)console.debug(`${h} ${O}`,...$)}function wO(O,...$){if(x<=1)console.info(`${h} ${O}`,...$)}function TO(O,...$){if(x<=2)console.warn(`${h} ${O}`,...$)}function yO(O,...$){if(x<=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 A=await this.deps.pluginManager.executeBeforeLoad(O);if(A?.cancel)return;if(A?.data!==void 0)O=A.data;await this.deps.playbackController.reset(),this.deps.sourceManager.disposeCurrent();let K=this.deps.state.getState();if(!$.replacePlaylist)this.deps.state.setState({...K,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 j=$.playlistItemId?this.deps.sourceManager.promoteQueuedSource($.playlistItemId):null;if(!j)j=await this.deps.sourceManager.createSource(O);let Q=j.input;if(!Q)throw Error("Failed to create input from source");await this.deps.trackManager.initialize(Q);let[V,J,Z,U]=await Promise.all([Q.computeDuration(),Q.getFormat(),Q.getMimeType(),Q.getMetadataTags()]),L={duration:V,format:J.name,mimeType:Z,metadata:U,hasVideo:this.deps.trackManager.hasVideo(),hasAudio:this.deps.trackManager.hasAudio(),hasSubtitles:this.deps.trackManager.hasSubtitles()};this.deps.state.updateDuration(V),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(V);let G=j.prefetchedData,P=G?.videoTrack??this.deps.trackManager.getPrimaryVideoTrack(),M=G?.audioTrack??this.deps.trackManager.getPrimaryAudioTrack();if(P&&G?.canvasSink&&G?.firstFrame)LO(P,{canvasSink:G.canvasSink,firstFrame:G.firstFrame});let N="",W=!1,_=!1;if(P||M){let E=await this.deps.trackSwitcher.setupInitialTracks(P,M);N+=E.warningMessage,W=E.videoSupported,_=E.audioSupported}if(!W&&!_){if(!N)N="No audio or video track found.";throw Error(N)}if(N&&(W||_))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(V),await this.deps.pluginManager.executeAfterLoad(L),$.autoplay)await this.play();if($.startTime!==void 0)await this.seek($.startTime)}catch(A){throw this.handleError(A),A}}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 A=await this.deps.pluginManager.executeBeforeSeek(O);if(A?.cancel)return;if(A?.data!==void 0)O=A.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(),A=$.currentPlaylistIndex;if(A!==null&&$.playlist.length>0){let K=[...$.playlist],j=K[A];if(j)K[A]={...j,duration:O},this.deps.state.updatePlaylist(K,A)}}}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,$,A){this.store.setState({volume:O,muted:$,playbackRate:A})}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,$,A){this.store.updateTracks(O,$,A)}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 AO{chains=new Map;async run(O,$){let A=this.chains.get(O)??Promise.resolve(),K,j=new Promise((Q)=>{K=Q});this.chains.set(O,A.then(()=>j));try{return await A,await $()}finally{K?.()}}}class KO{deps;locks=new AO;constructor(O){this.deps=O}async setupInitialTracks(O,$){let A=!1,K=!1,j="";if(O)try{if(A=await this.locks.run("video",async()=>{if(O.codec!==null&&await O.canDecode())return await this.deps.playbackController.trySetVideoTrack(O);return!1}),!A)j+="Unsupported video codec. "}catch(Q){j+="Failed to set up video track. ",k.warn("Video track error:",Q)}if($)try{if(K=await this.locks.run("audio",async()=>{if($.codec!==null&&await $.canDecode())return await this.deps.playbackController.trySetAudioTrack($);return!1}),!K)j+="Unsupported audio codec. "}catch(Q){j+="Failed to set up audio track. ",k.warn("Audio track error:",Q)}if(!A&&!K&&!j)j="No audio or video track found.";return{videoSupported:A,audioSupported:K,warningMessage:j}}async selectVideoTrack(O,$){if(!O.selectVideoTrack($))throw Error(`Invalid video track ID: ${$}`);let A=O.getSelectedVideoTrack();if(!A){await this.deps.playbackController.setVideoTrack(null);return}if(!await this.locks.run("video",async()=>{if(A.codec!==null&&await A.canDecode())return await this.deps.playbackController.trySetVideoTrack(A);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 A=O.getSelectedAudioTrack();if(!A){await this.deps.playbackController.setAudioTrack(null);return}if(!await this.locks.run("audio",async()=>{if(A.codec!==null&&await A.canDecode())return await this.deps.playbackController.trySetAudioTrack(A);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 fO,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,A=$.AudioContext||$.webkitAudioContext;this.audioContext=new A}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 fO(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:A,timestamp:K}of $){if(O!==this.playbackId||this.disposed||!this.playing)break;let j=this.audioContext.createBufferSource();j.buffer=A,j.connect(this.gainNode),j.playbackRate.value=this.playbackRate,j.playbackRate.setValueAtTime(this.playbackRate,this.audioContext.currentTime);let Q=this.startContextTime+(K-this.startMediaTime)/this.playbackRate;if(Q>=this.audioContext.currentTime)j.start(Q);else{let V=Math.max(0,(this.audioContext.currentTime-Q)*this.playbackRate);if(V<A.duration)j.start(this.audioContext.currentTime,V);else continue}if(this.queuedNodes.add(j),j.onended=()=>{this.queuedNodes.delete(j)},K-this.getCurrentTime()>=1)await this.waitForCatchup(K)}}catch{}}async waitForCatchup(O){return new Promise(($)=>{let A=setInterval(()=>{if(O-this.getCurrentTime()<1||!this.playing)clearInterval(A),$()},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 A=this.playing,K=this.getCurrentTime();if(this.playbackRate=$,A)this.pause(),this.pauseTime=K,this.play(K)}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 xO=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,A=this.getCurrentTime();if(!O){this.audioManager.dispose();return}let K=await O.computeDuration(),j=Math.max(0,Math.min(A,K));if(await this.audioManager.setAudioTrack(O),await this.audioManager.seek(j),$)await this.audioManager.play(j);this.currentTime=j,this.duration=Math.max(this.duration,K)}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 K=performance.now(),j=(K-this.lastFrameTime)/1000;this.lastFrameTime=K,this.currentTime+=j*this.playbackRate}if(this.currentTime>=this.duration){this.handleEnded();return}let{isStarving:A}=this.videoRenderer.updateFrame(this.currentTime);if(A&&!this.isWaiting){if(this.isWaiting=!0,this.onWaiting)this.onWaiting()}else if(!A&&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),xO)}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 jO{store;emitter;switchSource;sourceManager;constructor(O,$,A,K){this.store=O,this.emitter=$,this.switchSource=A,this.sourceManager=K}async loadPlaylist(O,$={}){let A=O.map((K)=>{if(K&&typeof K==="object"&&"mediaSource"in K)return{id:m(),mediaSource:K.mediaSource,title:K.title,poster:K.poster,savedPosition:null,duration:null};else return{id:m(),mediaSource:K,savedPosition:null,duration:null}});if(this.store.updatePlaylist(A,A.length>0?0:null),this.emitter.emit("playlistchange",{playlist:A}),A.length>0&&this.switchSource){let K=A[0];if($.startTime!==void 0)K.savedPosition=$.startTime;await this.switchSource(K,$.autoplay??!1)}}addToPlaylist(O,$){let A=this.createPlaylistItem(O);this.store.addToPlaylist(A,$),this.emitter.emit("playlistadd",{item:A,index:$??this.store.getState().playlist.length-1})}async removeFromPlaylist(O){let $=this.store.getState(),A=$.currentPlaylistIndex,K=$.playing;this.store.removeFromPlaylist(O),this.emitter.emit("playlistremove",{index:O}),this.sourceManager?.disposeQueued($.playlist[O]?.id||"");let j=this.store.getState(),Q=j.currentPlaylistIndex;if(j.playlist.length===0)this.sourceManager?.disposeAll(),this.emitter.emit("playlistchange",{playlist:j.playlist}),this.emitter.emit("playlistend",void 0);if(A===O&&Q!==null&&Q!==A&&this.switchSource){let V=j.playlist[Q];try{await this.switchSource(V,K)}catch(J){this.emitter.emit("playlistitemerror",{index:Q,error:J})}}}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,A=O.playlist,K=O.playlistMode,j=null;if(K==="repeat-one")j=$;else if(K==="sequential"||K==="repeat")if($<A.length-1)j=$+1;else if(K==="repeat")j=0;else{this.emitter.emit("playlistend",void 0);return}else if($<A.length-1)j=$+1;if(j!==null)await this.switchTo(j)}async prev(){let $=this.store.getState().currentPlaylistIndex??0;if($>0)await this.switchTo($-1)}async jumpTo(O){let A=this.store.getState().playlist;if(O>=0&&O<A.length)await this.switchTo(O);else if(O>=A.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((A)=>A!==null).join(", ")}, null`);this.store.updatePlaylistMode(O)}async switchTo(O){let $=this.store.getState(),A=$.currentPlaylistIndex,K=$.playing,j=[...$.playlist];if(A!==null&&A!==O){let J=j[A];j[A]={...J,savedPosition:$.currentTime}}this.store.updatePlaylist(j,O);let Q=j[O];if(this.emitter.emit("playlistitemchange",{index:O,item:Q,previousIndex:A??void 0}),this.switchSource)await this.switchSource(Q,K);if($.playlistMode==="sequential"&&O<j.length-1){let J=j[O+1];this.sourceManager?.preloadSource(J.mediaSource,J.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,$,A,K){let j=$.name;return{player:O,getState(){return O.getState()},subscribe(Q){let V=O.subscribe(Q),J=()=>V.unsubscribe();return A.stateUnsubscribes.push(J),J},getPluginState(){return A.pluginState},setPluginState(Q){A.pluginState=Q},on(Q,V){O.on(Q,V);let J=A.eventListeners.get(Q);if(!J)J=new Set,A.eventListeners.set(Q,J);J.add(V)},off(Q,V){O.off(Q,V);let J=A.eventListeners.get(Q);if(J)J.delete(V)},getCanvas(){return O.getRenderTarget()},getPlugin(Q){return K().get(Q)?.plugin},hasPlugin(Q){return K().has(Q)},log(...Q){console.log(`[MediaFox:${j}]`,...Q)},warn(...Q){console.warn(`[MediaFox:${j}]`,...Q)},error(...Q){console.error(`[MediaFox:${j}]`,...Q)}}}class QO{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 K of O.dependencies)if(!this.plugins.has(K))throw Error(`Plugin "${$}" requires plugin "${K}" to be installed first`)}let A={plugin:O,context:null,eventListeners:new Map,stateUnsubscribes:[],pluginState:void 0};A.context=GO(this.player,O,A,()=>this.plugins),this.plugins.set($,A);try{await O.install(A.context)}catch(K){throw this.plugins.delete($),K}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(K){console.error(`Error uninstalling plugin "${O}":`,K)}for(let[K,j]of $.eventListeners)for(let Q of j)this.player.off(K,Q);for(let K of $.stateUnsubscribes)K();let A=this.overlays.some((K)=>K.pluginName===O);if(this.overlays=this.overlays.filter((K)=>K.pluginName!==O),this.plugins.delete(O),A)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 A=$.plugin.hooks?.lifecycle?.beforeLoad;if(!A)continue;try{let K=await A.call($.plugin,O);if(K?.cancel)return K;if(K?.data!==void 0)O=K.data}catch(K){console.error(`[MediaFox:${$.plugin.name}] Error in beforeLoad hook:`,K)}}return{data:O}}async executeAfterLoad(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.lifecycle?.afterLoad;if(!A)continue;try{await A.call($.plugin,O)}catch(K){console.error(`[MediaFox:${$.plugin.name}] Error in afterLoad hook:`,K)}}}async executeBeforePlay(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforePlay;if(!$)continue;try{let A=await $.call(O.plugin);if(A?.cancel)return A}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in beforePlay hook:`,A)}}return}executeAfterPlay(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPlay;if(!$)continue;try{$.call(O.plugin)}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in afterPlay hook:`,A)}}}async executeBeforePause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforePause;if(!$)continue;try{let A=await $.call(O.plugin);if(A?.cancel)return A}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in beforePause hook:`,A)}}return}executeAfterPause(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterPause;if(!$)continue;try{$.call(O.plugin)}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in afterPause hook:`,A)}}}async executeBeforeSeek(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.lifecycle?.beforeSeek;if(!A)continue;try{let K=await A.call($.plugin,O);if(K?.cancel)return K;if(K?.data!==void 0)O=K.data}catch(K){console.error(`[MediaFox:${$.plugin.name}] Error in beforeSeek hook:`,K)}}return{data:O}}executeAfterSeek(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.lifecycle?.afterSeek;if(!A)continue;try{A.call($.plugin,O)}catch(K){console.error(`[MediaFox:${$.plugin.name}] Error in afterSeek hook:`,K)}}}async executeBeforeStop(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.beforeStop;if(!$)continue;try{let A=await $.call(O.plugin);if(A?.cancel)return A}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in beforeStop hook:`,A)}}return}executeAfterStop(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.afterStop;if(!$)continue;try{$.call(O.plugin)}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in afterStop hook:`,A)}}}executeOnError(O){let $=!1;for(let[,A]of this.plugins){let K=A.plugin.hooks?.lifecycle?.onError;if(!K)continue;try{if(K.call(A.plugin,O)?.handled)$=!0}catch(j){console.error(`[MediaFox:${A.plugin.name}] Error in onError hook:`,j)}}return $}executeOnEnded(){for(let[,O]of this.plugins){let $=O.plugin.hooks?.lifecycle?.onEnded;if(!$)continue;try{$.call(O.plugin)}catch(A){console.error(`[MediaFox:${O.plugin.name}] Error in onEnded hook:`,A)}}}executeBeforeRender(O,$){for(let[,A]of this.plugins){let K=A.plugin.hooks?.render?.beforeRender;if(!K)continue;try{let j=K.call(A.plugin,O,$);if(j?.skip)return j}catch(j){console.error(`[MediaFox:${A.plugin.name}] Error in beforeRender hook:`,j)}}return}executeTransformFrame(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.render?.transformFrame;if(!A)continue;try{let K=A.call($.plugin,O);if(K)O=K}catch(K){console.error(`[MediaFox:${$.plugin.name}] Error in transformFrame hook:`,K)}}return O}executeAfterRender(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.render?.afterRender;if(!A)continue;try{A.call($.plugin,O)}catch(K){console.error(`[MediaFox:${$.plugin.name}] Error in afterRender hook:`,K)}}}executeOverlays(O,$,A){if(this.overlays.length===0)return;if(!this.overlaysSorted)this.overlays.sort((K,j)=>{if(K.zIndex!==j.zIndex)return K.zIndex-j.zIndex;return K.registrationOrder-j.registrationOrder}),this.overlaysSorted=!0;for(let K of this.overlays)try{K.render?.render(O,$,A)}catch(j){console.error(`[MediaFox:${K.pluginName}] Error in onOverlay hook:`,j)}}executeBeforeStateUpdate(O){for(let[,$]of this.plugins){let A=$.plugin.hooks?.state?.beforeStateUpdate;if(!A)continue;try{let K=A.call($.plugin,O);if(K===null)return null;if(K!==void 0)O=K}catch(K){console.error(`[MediaFox:${$.plugin.name}] Error in beforeStateUpdate hook:`,K)}}return O}executeOnStateChange(O,$){for(let[,A]of this.plugins){let K=A.plugin.hooks?.state?.onStateChange;if(!K)continue;try{K.call(A.plugin,O,$)}catch(j){console.error(`[MediaFox:${A.plugin.name}] Error in onStateChange hook:`,j)}}}executeBeforeEvent(O,$){for(let[,A]of this.plugins){let K=A.plugin.hooks?.event?.beforeEvent;if(!K)continue;try{let j=K.call(A.plugin,O,$);if(j?.cancel)return j;if(j?.data!==void 0)$=j.data}catch(j){console.error(`[MediaFox:${A.plugin.name}] Error in beforeEvent hook:`,j)}}return{data:$}}executeAfterEvent(O,$){for(let[,A]of this.plugins){let K=A.plugin.hooks?.event?.afterEvent;if(!K)continue;try{K.call(A.plugin,O,$)}catch(j){console.error(`[MediaFox:${A.plugin.name}] Error in afterEvent hook:`,j)}}}executeOnAudioNode(O,$){let A=$;for(let[,K]of this.plugins){let j=K.plugin.hooks?.audio?.onAudioNode;if(!j)continue;try{let Q=j.call(K.plugin,O,$);if(Q)A=Q}catch(Q){console.error(`[MediaFox:${K.plugin.name}] Error in onAudioNode hook:`,Q)}}return A}async dispose(){let O=Array.from(this.plugins.keys()).reverse();for(let $ of O)try{await this.uninstall($)}catch(A){console.error(`Error disposing plugin "${$}":`,A)}}}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 A,K;if(O instanceof File||O instanceof Blob)A=new hO(O,{maxCacheSize:this.options.maxCacheSize}),K="blob";else if(O instanceof ArrayBuffer||O instanceof Uint8Array)A=new pO(O),K="buffer";else if(typeof O==="string"||O instanceof URL){let V=O instanceof URL?O.href:O;if(typeof window>"u"&&!V.startsWith("http"))A=new mO(V,{maxCacheSize:this.options.maxCacheSize}),K="file";else A=new cO(V,{maxCacheSize:this.options.maxCacheSize,requestInit:this.options.requestInit}),K="url"}else if(typeof ReadableStream<"u"&&O instanceof ReadableStream)A=new lO(O,{maxCacheSize:this.options.maxCacheSize}),K="stream";else throw TypeError("Unsupported media source type");let j=new uO({source:A,formats:vO}),Q={source:A,input:j,type:K,originalSource:O};if($)this.queuedSources.set($,Q);else this.currentSource=Q;return Q}getCurrentSource(){return this.currentSource}getQueuedSource(O){return this.queuedSources.get(O)||null}async preloadSource(O,$){if(this.queuedSources.has($))return;let A=await this.createSource(O,$);if(A.input)try{let K=await this.prefetchTrackData(A.input);A.prefetchedData=K}catch{}}async prefetchTrackData(O){let $=await O.getVideoTracks(),A=await O.getAudioTracks(),K=$.length>0?$[0]:null,j=A.length>0?A[0]:null,Q=null,V=null,J=0;if(K){if(K.codec!==null&&await K.canDecode()){Q=new gO(K,{poolSize:2});let Z=Q.canvases(0);V=(await Z.next()).value??null,await Z.return(),J=await K.computeDuration()}}if(!J&&j)J=await j.computeDuration();return{videoTrack:K,audioTrack:j,canvasSink:Q,firstFrame:V,duration:J}}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 A=await fetch(O,$);if(!A.ok)throw Error(`Failed to fetch: ${A.status} ${A.statusText}`);return A.blob()}static fromStreamingFetch(O,$){return new ReadableStream({async start(A){let K=await fetch(O,$);if(!K.ok){A.error(Error(`Failed to fetch: ${K.status} ${K.statusText}`));return}let j=K.body?.getReader();if(!j){A.error(Error("Response body is not readable"));return}try{while(!0){let{done:Q,value:V}=await j.read();if(Q)break;A.enqueue(V)}A.close()}catch(Q){A.error(Q)}}})}}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 A=0;A<O.length;A++)if(!l(O[A],$[A]))return!1;return!0}if(PO(O)&&PO($)){let A=Object.keys(O),K=Object.keys($);if(A.length!==K.length)return!1;for(let j of A){if(!Object.hasOwn($,j))return!1;if(!l(O[j],$[j]))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 $=R.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 A=this.pluginManager.executeBeforeStateUpdate(O);if(A===null)return;O=A}let $=Object.keys(O);for(let A=0;A<$.length;A++){let K=$[A];if(this.pendingUpdates[K]===void 0)this.pendingKeys.push(K);this.setPendingValue(K,O[K])}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 A=0;A<O.length;A++){let K=O[A];if(!l(this.pendingUpdates[K],this.state[K])){$=!0;break}}for(let A=0;A<O.length;A++){let K=O[A];this.copyStateKey(K)}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 A of this.listeners)$.push(A);for(let A=0;A<$.length;A++)try{$[A](O)}catch(K){console.error("Error in state listener:",K)}}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,$,A){let K={};if(O)K.videoTracks=O;if($)K.audioTracks=$;if(A)K.subtitleTracks=A;this.setState(K)}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 A=this.state.playlist,K,j=this.state.currentPlaylistIndex;if($!==void 0&&$>=0&&$<=A.length){if(K=[...A.slice(0,$),O,...A.slice($)],j!==null&&j>=$)j+=1}else K=[...A,O];this.setState({playlist:K,currentPlaylistIndex:j})}removeFromPlaylist(O){let $=this.state.playlist;if(O<0||O>=$.length)return;let A=$.filter((j,Q)=>Q!==O),K=this.state.currentPlaylistIndex;if(K===O)K=A.length>0?0:null;else if(K!==null&&K>O)K-=1;if(A.length===0)this.setState({playlist:A,currentPlaylistIndex:null,state:"idle",currentTime:0,duration:0,mediaInfo:null,videoTracks:[],audioTracks:[],subtitleTracks:[],selectedVideoTrack:null,selectedAudioTrack:null,selectedSubtitleTrack:null});else this.setState({playlist:A,currentPlaylistIndex:K})}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(A)=>{let K=`video-${A.id}`;this.videoTracks.set(K,A);let j=0,Q=0;try{let J=await A.computePacketStats(100);j=J.averagePacketRate,Q=J.averageBitrate}catch{}return{id:K,codec:A.codec,language:A.languageCode,name:A.name,width:A.codedWidth,height:A.codedHeight,frameRate:j,bitrate:Q,rotation:A.rotation,selected:!1,decodable:await A.canDecode()}}));let $=await this.input.getAudioTracks();if(this.audioTrackInfos=await Promise.all($.map(async(A)=>{let K=`audio-${A.id}`;this.audioTracks.set(K,A);let j=0;try{j=(await A.computePacketStats(100)).averageBitrate}catch{}return{id:K,codec:A.codec,language:A.languageCode,name:A.name,channels:A.numberOfChannels,sampleRate:A.sampleRate,bitrate:j,selected:!1,decodable:await A.canDecode()}})),this.videoTrackInfos.length>0){let A=this.videoTrackInfos.find((K)=>K.decodable);if(A)this.selectVideoTrack(A.id)}if(this.audioTrackInfos.length>0){let A=this.audioTrackInfos.find((K)=>K.decodable);if(A)this.selectAudioTrack(A.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((A)=>{A.selected=A.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((A)=>{A.selected=A.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((A)=>{A.selected=A.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 A of this.subtitleProviders.values())for(let K of A){let j={...K.info,selected:!1};this.subtitleTrackInfos.push(j),this.subtitleTrackResolvers.set(j.id,K.resolver)}let $=O;if(!$||!this.subtitleTrackResolvers.has($))$=this.subtitleTrackInfos[0]?.id??null;if(this.selectedSubtitleTrack=$,this.subtitleTrackInfos.forEach((A)=>{A.selected=A.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 A=null;for(let[j,Q]of this.audioTracks.entries())if(Q.id===O){A=j;break}if(!A)return;this.audioTracks.set(A,$);let K=this.audioTrackInfos.findIndex((j)=>j.id===A);if(K!==-1){let j=0;try{j=(await $.computePacketStats(100)).averageBitrate}catch{}this.audioTrackInfos[K]={...this.audioTrackInfos[K],codec:$.codec,channels:$.numberOfChannels,sampleRate:$.sampleRate,bitrate:j,decodable:await $.canDecode()}}}async replaceVideoTrackByInputId(O,$){let A=null;for(let[j,Q]of this.videoTracks.entries())if(Q.id===O){A=j;break}if(!A)return;this.videoTracks.set(A,$);let K=this.videoTrackInfos.findIndex((j)=>j.id===A);if(K!==-1){let j=0,Q=0;try{let V=await $.computePacketStats(100);j=V.averagePacketRate,Q=V.averageBitrate}catch{}this.videoTrackInfos[K]={...this.videoTrackInfos[K],codec:$.codec,width:$.codedWidth,height:$.codedHeight,rotation:$.rotation,frameRate:j,bitrate:Q,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 y({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 jO(this.store,this.emitter,async($,A)=>{await this.core.load($.mediaSource,{startTime:$.savedPosition??0,autoplay:A,playlistItemId:$.id})},this.sourceManager),this.trackSwitcher=new KO({sourceManager:this.sourceManager,trackManager:this.trackManager,playbackController:this.playbackController,emit:this.emit.bind(this),store:this.store,getCurrentInput:this.getCurrentInput}),this.pluginManager=new QO(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:A}=O;if($==="repeat-one"){let K=A;queueMicrotask(async()=>{try{await this.seek(0),await this.play()}catch(j){this.emitter.emit("playlistitemerror",{index:K,error:j})}})}else if($==="repeat"){let K=A<O.playlist.length-1?A+1:0;queueMicrotask(async()=>{try{await this.playlistManager.next()}catch(j){this.emitter.emit("playlistitemerror",{index:K,error:j})}})}else if($==="sequential"&&A<O.playlist.length-1){let K=A+1;queueMicrotask(async()=>{try{await this.playlistManager.next()}catch(j){this.emitter.emit("playlistitemerror",{index:K,error:j})}})}}}),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 A=this.getState();if(A.playlist.length===0||$.replacePlaylist){await this.playlistManager.loadPlaylist([{mediaSource:O}],{autoplay:$.autoplay??this.options.autoplay,startTime:$.startTime});return}else if(A.currentPlaylistIndex!==null&&A.playlist.length>0){let K=A.currentPlaylistIndex,Q={...A.playlist[K],mediaSource:O,savedPosition:0,duration:null},V=[...A.playlist];V[K]=Q,this.store.updatePlaylist(V,K),this.emitter.emit("playlistchange",{playlist:V}),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 A=this.state.getState().mediaInfo;if(A)this.state.updateMediaInfo({...A,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 R.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 A=this.pluginManager.executeBeforeEvent(O,$);if(A?.cancel)return;let K=A?.data??$;this.emitter.emit(O,K),this.pluginManager.executeAfterEvent(O,K)}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 XO;((U)=>{U.MEDIA_NOT_SUPPORTED="MEDIA_NOT_SUPPORTED";U.MEDIA_LOAD_FAILED="MEDIA_LOAD_FAILED";U.DECODE_ERROR="DECODE_ERROR";U.NETWORK_ERROR="NETWORK_ERROR";U.PERMISSION_DENIED="PERMISSION_DENIED";U.PLAYBACK_ERROR="PLAYBACK_ERROR";U.TRACK_NOT_FOUND="TRACK_NOT_FOUND";U.INVALID_STATE="INVALID_STATE";U.UNKNOWN_ERROR="UNKNOWN_ERROR"})(XO||={});class D extends Error{code;details;constructor(O,$,A){super($);this.name="MediaFoxError",this.code=O,this.details=A}static mediaNotSupported(O="Media format not supported",$){return new D("MEDIA_NOT_SUPPORTED",O,$)}static mediaLoadFailed(O="Failed to load media",$){return new D("MEDIA_LOAD_FAILED",O,$)}static decodeError(O="Failed to decode media",$){return new D("DECODE_ERROR",O,$)}static networkError(O="Network error occurred",$){return new D("NETWORK_ERROR",O,$)}static permissionDenied(O="Permission denied",$){return new D("PERMISSION_DENIED",O,$)}static playbackError(O="Playback error occurred",$){return new D("PLAYBACK_ERROR",O,$)}static trackNotFound(O="Track not found",$){return new D("TRACK_NOT_FOUND",O,$)}static invalidState(O="Invalid player state",$){return new D("INVALID_STATE",O,$)}static unknownError(O="Unknown error occurred",$){return new D("UNKNOWN_ERROR",O,$)}}function dO(O,$){if(O instanceof D)return O;if(O instanceof Error)return new D("UNKNOWN_ERROR",`${$}: ${O.message}`,{originalError:O});return new D("UNKNOWN_ERROR",`${$}: ${String(O)}`,{originalError:O})}function nO(O,$=!1){let A=Math.abs(O),K=Math.floor(A/3600),j=Math.floor(A%3600/60),Q=Math.floor(A%60),V=Math.floor(A%1*1000),J="";if(O<0)J="-";if(K>0)J+=`${K}:${j.toString().padStart(2,"0")}:${Q.toString().padStart(2,"0")}`;else J+=`${j}:${Q.toString().padStart(2,"0")}`;if($)J+=`.${V.toString().padStart(3,"0")}`;return J}function sO(O){let $=O.trim().split(":").map(Number);if($.some(Number.isNaN))throw Error("Invalid time string");let A=0;if($.length===3)A=$[0]*3600+$[1]*60+$[2];else if($.length===2)A=$[0]*60+$[1];else if($.length===1)A=$[0];else throw Error("Invalid time format");return A}function iO(O,$){return Math.floor(O*$)}function tO(O,$){return O/$}function aO(O,$,A){return Math.max($,Math.min(A,O))}function rO(O,$){return O.start<$.end&&$.start<O.end}function eO(O){if(O.length===0)return[];let $=[...O].sort((K,j)=>K.start-j.start),A=[$[0]];for(let K=1;K<$.length;K++){let j=A[A.length-1],Q=$[K];if(Q.start<=j.end)j.end=Math.max(j.end,Q.end);else A.push(Q)}return A}function O$(O){return O.reduce(($,A)=>$+(A.end-A.start),0)}function $$(O,$){for(let A of O)if($>=A.start&&$<A.end)return A;return null}var NA="0.1.0",fA=d;export{dO as wrapError,O$ as totalBufferedDuration,iO as timeToFrame,rO as timeRangesOverlap,sO as parseTime,eO as mergeTimeRanges,tO as frameToTime,nO as formatTime,$$ as findBufferedRange,fA as default,aO as clamp,v as VideoRenderer,NA as VERSION,o as TrackManager,c as Store,I as SourcePool,u as SourceManager,oO as Source,R as RendererFactory,g as PlaybackController,D as MediaFoxError,d as MediaFox,y as EventEmitter,XO as ErrorCode,t as Compositor,p as AudioManager};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webgpu.d.ts","sourceRoot":"","sources":["../../../src/playback/renderers/webgpu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAC5C,eAAe,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAAC;IACnD,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAe;IAE/B,OAAO,CAAC,SAAS,CAAwB;IAEzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAajC;IAEF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAQnC;gBAEU,OAAO,EAAE,qBAAqB;YAS5B,UAAU;YAoDV,oBAAoB;IAsClC,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,eAAe;IAYhB,OAAO,IAAI,OAAO;IAIlB,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"webgpu.d.ts","sourceRoot":"","sources":["../../../src/playback/renderers/webgpu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAC5C,eAAe,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAAC;IACnD,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAe;IAE/B,OAAO,CAAC,SAAS,CAAwB;IAEzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAajC;IAEF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAQnC;gBAEU,OAAO,EAAE,qBAAqB;YAS5B,UAAU;YAoDV,oBAAoB;IAsClC,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,eAAe;IAYhB,OAAO,IAAI,OAAO;IAIlB,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,GAAG,OAAO;IAwJ5D,KAAK,IAAI,IAAI;IAuBb,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAIrC,WAAW,IAAI,QAAQ;IAIvB,OAAO,IAAI,IAAI;CAoBvB"}
|
package/package.json
CHANGED
|
@@ -16,57 +16,16 @@ import {
|
|
|
16
16
|
import type { MediaSource } from '../types';
|
|
17
17
|
import type { CompositorSource, CompositorSourceOptions, SourceType } from './types';
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* LRU cache for video frames.
|
|
21
|
-
* Uses Map's insertion order + move-to-end on access for O(1) LRU eviction.
|
|
22
|
-
*/
|
|
23
|
-
class LRUFrameCache {
|
|
24
|
-
private cache = new Map<number, WrappedCanvas>();
|
|
25
|
-
private maxSize: number;
|
|
26
|
-
|
|
27
|
-
constructor(maxSize: number) {
|
|
28
|
-
this.maxSize = maxSize;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get(key: number): WrappedCanvas | undefined {
|
|
32
|
-
const value = this.cache.get(key);
|
|
33
|
-
if (value !== undefined) {
|
|
34
|
-
// Move to end (most recently used)
|
|
35
|
-
this.cache.delete(key);
|
|
36
|
-
this.cache.set(key, value);
|
|
37
|
-
}
|
|
38
|
-
return value;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
set(key: number, value: WrappedCanvas): void {
|
|
42
|
-
// If key exists, delete first to update insertion order
|
|
43
|
-
if (this.cache.has(key)) {
|
|
44
|
-
this.cache.delete(key);
|
|
45
|
-
} else if (this.cache.size >= this.maxSize) {
|
|
46
|
-
// Evict least recently used (first item)
|
|
47
|
-
const firstKey = this.cache.keys().next().value;
|
|
48
|
-
if (firstKey !== undefined) {
|
|
49
|
-
this.cache.delete(firstKey);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
this.cache.set(key, value);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
clear(): void {
|
|
56
|
-
this.cache.clear();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get size(): number {
|
|
60
|
-
return this.cache.size;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
19
|
interface VideoSourceData {
|
|
65
20
|
input: Input<Source>;
|
|
66
21
|
videoTrack: InputVideoTrack;
|
|
67
22
|
canvasSink: CanvasSink;
|
|
68
|
-
|
|
69
|
-
|
|
23
|
+
iterator: AsyncGenerator<WrappedCanvas, void, unknown> | null;
|
|
24
|
+
currentFrame: WrappedCanvas | null;
|
|
25
|
+
nextFrame: WrappedCanvas | null;
|
|
26
|
+
lastRequestedTime: number;
|
|
27
|
+
seekThresholdSeconds: number;
|
|
28
|
+
lock: Promise<void>;
|
|
70
29
|
audioTrack: InputAudioTrack | null;
|
|
71
30
|
audioBufferSink: AudioBufferSink | null;
|
|
72
31
|
}
|
|
@@ -101,27 +60,108 @@ class VideoSource implements CompositorSource {
|
|
|
101
60
|
async getFrameAt(time: number): Promise<CanvasImageSource | null> {
|
|
102
61
|
if (this.disposed) return null;
|
|
103
62
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
63
|
+
// CanvasSink.getCanvas(timestamp) is optimized for random access, but calling it for every preview
|
|
64
|
+
// frame turns playback into repeated seek+decode, which can stall/freeze on high-res sources.
|
|
65
|
+
// Instead, keep a single iterator open and advance it as time increases.
|
|
66
|
+
return this.withLock(async () => {
|
|
67
|
+
if (this.disposed) return null;
|
|
68
|
+
|
|
69
|
+
// Fast path: time is within current frame
|
|
70
|
+
const current = this.data.currentFrame;
|
|
71
|
+
if (current) {
|
|
72
|
+
const end = current.timestamp + (current.duration || 0);
|
|
73
|
+
if (time >= current.timestamp && (current.duration ? time < end : time === current.timestamp)) {
|
|
74
|
+
this.data.lastRequestedTime = time;
|
|
75
|
+
return current.canvas;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const lastTime = this.data.lastRequestedTime;
|
|
80
|
+
const needsSeek =
|
|
81
|
+
this.data.iterator === null || time < lastTime || Math.abs(time - lastTime) > this.data.seekThresholdSeconds;
|
|
82
|
+
|
|
83
|
+
if (needsSeek) {
|
|
84
|
+
await this.restartIterator(time);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const advanced = await this.advanceToTime(time);
|
|
88
|
+
this.data.lastRequestedTime = time;
|
|
89
|
+
return advanced?.canvas ?? null;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
108
92
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
93
|
+
private async restartIterator(startTime: number): Promise<void> {
|
|
94
|
+
// Close any existing iterator to release decoder resources.
|
|
95
|
+
if (this.data.iterator) {
|
|
96
|
+
try {
|
|
97
|
+
await this.data.iterator.return();
|
|
98
|
+
} catch {
|
|
99
|
+
// ignore
|
|
100
|
+
}
|
|
113
101
|
}
|
|
114
102
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
103
|
+
this.data.iterator = this.data.canvasSink.canvases(startTime);
|
|
104
|
+
this.data.currentFrame = null;
|
|
105
|
+
this.data.nextFrame = null;
|
|
106
|
+
}
|
|
118
107
|
|
|
119
|
-
|
|
120
|
-
|
|
108
|
+
private async advanceToTime(time: number): Promise<WrappedCanvas | null> {
|
|
109
|
+
const iterator = this.data.iterator;
|
|
110
|
+
if (!iterator) {
|
|
111
|
+
// Shouldn't happen, but keep behavior safe.
|
|
112
|
+
return this.data.canvasSink.getCanvas(time);
|
|
113
|
+
}
|
|
121
114
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
115
|
+
// Use a one-item lookahead buffer so we can stop once we pass the requested time.
|
|
116
|
+
while (true) {
|
|
117
|
+
const next = this.data.nextFrame;
|
|
118
|
+
if (next) {
|
|
119
|
+
if (next.timestamp > time) {
|
|
120
|
+
return this.data.currentFrame;
|
|
121
|
+
}
|
|
122
|
+
this.data.currentFrame = next;
|
|
123
|
+
this.data.nextFrame = null;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let result: IteratorResult<WrappedCanvas, void>;
|
|
128
|
+
try {
|
|
129
|
+
result = await iterator.next();
|
|
130
|
+
} catch {
|
|
131
|
+
// Decoder/iterator failed; fall back to random access.
|
|
132
|
+
try {
|
|
133
|
+
return await this.data.canvasSink.getCanvas(time);
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (result.done) {
|
|
140
|
+
return this.data.currentFrame;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.data.nextFrame = result.value;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async withLock<T>(fn: () => Promise<T>): Promise<T> {
|
|
148
|
+
// NOTE: Assignments inside Promise executors aren't tracked by TS control flow,
|
|
149
|
+
// so keep `release` always-callable to avoid it narrowing to `never`.
|
|
150
|
+
const previous = this.data.lock.catch(() => {
|
|
151
|
+
// Ignore previous failures; keep the mutex usable.
|
|
152
|
+
});
|
|
153
|
+
let release: () => void = () => {};
|
|
154
|
+
const current = new Promise<void>((resolve) => {
|
|
155
|
+
release = () => {
|
|
156
|
+
resolve();
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
this.data.lock = previous.then(() => current);
|
|
160
|
+
await previous;
|
|
161
|
+
try {
|
|
162
|
+
return await fn();
|
|
163
|
+
} finally {
|
|
164
|
+
release();
|
|
125
165
|
}
|
|
126
166
|
}
|
|
127
167
|
|
|
@@ -140,13 +180,22 @@ class VideoSource implements CompositorSource {
|
|
|
140
180
|
}
|
|
141
181
|
|
|
142
182
|
clearCache(): void {
|
|
143
|
-
|
|
183
|
+
// No-op: sequential iterator keeps bounded memory.
|
|
144
184
|
}
|
|
145
185
|
|
|
146
186
|
dispose(): void {
|
|
147
187
|
if (this.disposed) return;
|
|
148
188
|
this.disposed = true;
|
|
149
|
-
this.data.
|
|
189
|
+
if (this.data.iterator) {
|
|
190
|
+
try {
|
|
191
|
+
void this.data.iterator.return();
|
|
192
|
+
} catch {
|
|
193
|
+
// ignore
|
|
194
|
+
}
|
|
195
|
+
this.data.iterator = null;
|
|
196
|
+
}
|
|
197
|
+
this.data.currentFrame = null;
|
|
198
|
+
this.data.nextFrame = null;
|
|
150
199
|
this.data.input.dispose();
|
|
151
200
|
}
|
|
152
201
|
}
|
|
@@ -256,23 +305,9 @@ export class SourcePool {
|
|
|
256
305
|
// Get duration
|
|
257
306
|
const duration = await videoTrack.computeDuration();
|
|
258
307
|
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
const stats = await videoTrack.computePacketStats(100);
|
|
264
|
-
if (stats.averagePacketRate > 0) {
|
|
265
|
-
fps = stats.averagePacketRate;
|
|
266
|
-
}
|
|
267
|
-
} catch {
|
|
268
|
-
// Ignore errors in stats computation
|
|
269
|
-
}
|
|
270
|
-
const frameIntervalMs = 1000 / fps;
|
|
271
|
-
|
|
272
|
-
// Adaptive cache size based on resolution
|
|
273
|
-
// Higher resolution = fewer cached frames to limit memory usage
|
|
274
|
-
const pixelCount = videoTrack.displayWidth * videoTrack.displayHeight;
|
|
275
|
-
const cacheSize = pixelCount > 2073600 ? 15 : pixelCount > 921600 ? 30 : 60; // 1080p: 15, 720p: 30, smaller: 60
|
|
308
|
+
// If the caller is scrubbing aggressively, we restart decoding from the requested time.
|
|
309
|
+
// Keeping this threshold low avoids iterating through huge gaps in a single request.
|
|
310
|
+
const seekThresholdSeconds = 0.75;
|
|
276
311
|
|
|
277
312
|
// Try to get audio track if available
|
|
278
313
|
let audioTrack: InputAudioTrack | null = null;
|
|
@@ -296,8 +331,12 @@ export class SourcePool {
|
|
|
296
331
|
input,
|
|
297
332
|
videoTrack,
|
|
298
333
|
canvasSink,
|
|
299
|
-
|
|
300
|
-
|
|
334
|
+
iterator: null,
|
|
335
|
+
currentFrame: null,
|
|
336
|
+
nextFrame: null,
|
|
337
|
+
lastRequestedTime: -Infinity,
|
|
338
|
+
seekThresholdSeconds,
|
|
339
|
+
lock: Promise.resolve(),
|
|
301
340
|
audioTrack,
|
|
302
341
|
audioBufferSink,
|
|
303
342
|
},
|
|
@@ -234,8 +234,14 @@ export class WebGPURenderer implements IRenderer {
|
|
|
234
234
|
// Fallback to getImageData if copyExternalImageToTexture fails
|
|
235
235
|
const sourceCtx = source.getContext('2d');
|
|
236
236
|
if (!sourceCtx) return false;
|
|
237
|
+
if (!('getImageData' in sourceCtx)) return false;
|
|
237
238
|
|
|
238
|
-
const imageData = sourceCtx
|
|
239
|
+
const imageData = (sourceCtx as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D).getImageData(
|
|
240
|
+
0,
|
|
241
|
+
0,
|
|
242
|
+
sourceWidth,
|
|
243
|
+
sourceHeight
|
|
244
|
+
);
|
|
239
245
|
const data = new Uint8Array(imageData.data.buffer);
|
|
240
246
|
|
|
241
247
|
this.device.queue.writeTexture(
|