@mediafox/core 1.2.7 → 1.2.8
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":"compositor.d.ts","sourceRoot":"","sources":["../../src/compositor/compositor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,KAAK,EAEV,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAElB,iBAAiB,EACjB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACf,MAAM,SAAS,CAAC;AAkBjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,GAAG,CAA6E;IACxF,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,kBAAkB,CAAuC;IACjE,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,aAAa,CAA4E;IACjG,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,oBAAoB,CAAO;IACnC,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,oBAAoB,CAAqB;IACjD,OAAO,CAAC,YAAY,CAIlB;IACF,OAAO,CAAC,sBAAsB,CAAqB;IAEnD;;;OAGG;gBACS,OAAO,EAAE,iBAAiB;IAoEtC;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBnG;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAcxE;;;;;OAKG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBlG;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAwBjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2C1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAOnD;;;OAGG;IACH,aAAa,IAAI,gBAAgB,EAAE;IASnC;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAqEvD,OAAO,CAAC,WAAW;IA4DnB,OAAO,CAAC,kBAAkB;YAyBZ,eAAe;IAgB7B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,oBAAoB;IA4B5B;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAQtC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"compositor.d.ts","sourceRoot":"","sources":["../../src/compositor/compositor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,KAAK,EAEV,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAElB,iBAAiB,EACjB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACf,MAAM,SAAS,CAAC;AAkBjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,GAAG,CAA6E;IACxF,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,kBAAkB,CAAuC;IACjE,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,aAAa,CAA4E;IACjG,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,oBAAoB,CAAO;IACnC,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,oBAAoB,CAAqB;IACjD,OAAO,CAAC,YAAY,CAIlB;IACF,OAAO,CAAC,sBAAsB,CAAqB;IAEnD;;;OAGG;gBACS,OAAO,EAAE,iBAAiB;IAoEtC;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBnG;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAcxE;;;;;OAKG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBlG;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAwBjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2C1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAOnD;;;OAGG;IACH,aAAa,IAAI,gBAAgB,EAAE;IASnC;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAqEvD,OAAO,CAAC,WAAW;IA4DnB,OAAO,CAAC,kBAAkB;YAyBZ,eAAe;IAgB7B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,oBAAoB;IA4B5B;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAQtC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBvC,OAAO,CAAC,eAAe;IAgEvB,OAAO,CAAC,cAAc;IAStB;;;;;OAKG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAiCvF,wCAAwC;IACxC,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,4DAA4D;IAC5D,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,mDAAmD;IACnD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,kDAAkD;IAClD,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,mDAAmD;IACnD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;OAGG;IACH,QAAQ,IAAI,MAAM;IAIlB;;;OAGG;IACH,SAAS,IAAI,MAAM;IAInB;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAe3C;;;;;OAKG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIlG;;;;;OAKG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIpG;;;;OAIG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,IAAI;IAM9F;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B;;;OAGG;IACH,eAAe,IAAI,YAAY;IAS/B,OAAO,CAAC,aAAa;IAMrB;;;OAGG;IACH,OAAO,IAAI,IAAI;CAoBhB"}
|
|
@@ -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)this.workerClient=new _({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:o.worker??!0});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)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 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)this.workerClient=new _({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:o.worker??!0});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})}};
|
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)this.workerClient=new s({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:O.worker??!0});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)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 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)this.workerClient=new s({canvas:this.canvas,width:this.width,height:this.height,backgroundColor:this.backgroundColor,worker:O.worker??!0});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=`
|
|
2
2
|
attribute vec2 a_position;
|
|
3
3
|
attribute vec2 a_texCoord;
|
|
4
4
|
varying vec2 v_texCoord;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mediafox/core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"description": "Framework-agnostic media player library powered by MediaBunny",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./compositor-worker": {
|
|
15
|
+
"import": "./dist/compositor-worker.js"
|
|
13
16
|
}
|
|
14
17
|
},
|
|
15
18
|
"files": [
|