UNPKG

trtc-sdk-v5

Version:
3 lines • 77.3 kB
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e="undefined"!=typeof globalThis?globalThis:e||self).VideoMixer=r()}(this,function(){"use strict";function e(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=Array(r);t<r;t++)n[t]=e[t];return n}function r(e,r,t,n,a,i,o){try{var s=e[i](o),c=s.value}catch(e){return void t(e)}s.done?r(c):Promise.resolve(c).then(n,a)}function t(e){return function(){var t=this,n=arguments;return new Promise(function(a,i){var o=e.apply(t,n);function s(e){r(o,a,i,s,c,"next",e)}function c(e){r(o,a,i,s,c,"throw",e)}s(void 0)})}}function n(e,r){if(!(e instanceof r))throw new TypeError("Cannot call a class as a function")}function a(e,r,t){return r&&function(e,r){for(var t=0;t<r.length;t++){var n=r[t];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,l(n.key),n)}}(e.prototype,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function i(e,r){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!t){if(Array.isArray(e)||(t=u(e))||r){t&&(e=t);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,s=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return o=e.done,e},e:function(e){s=!0,i=e},f:function(){try{o||null==t.return||t.return()}finally{if(s)throw i}}}}function o(){ /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e,r,t="function"==typeof Symbol?Symbol:{},n=t.iterator||"@@iterator",a=t.toStringTag||"@@toStringTag";function i(t,n,a,i){var o=n&&n.prototype instanceof l?n:l,d=Object.create(o.prototype);return s(d,"_invoke",function(t,n,a){var i,o,s,l=0,d=a||[],u=!1,p={p:0,n:0,v:e,a:m,f:m.bind(e,4),d:function(r,t){return i=r,o=0,s=e,p.n=t,c}};function m(t,n){for(o=t,s=n,r=0;!u&&l&&!a&&r<d.length;r++){var a,i=d[r],m=p.p,f=i[2];t>3?(a=f===n)&&(s=i[(o=i[4])?5:(o=3,3)],i[4]=i[5]=e):i[0]<=m&&((a=t<2&&m<i[1])?(o=0,p.v=n,p.n=i[1]):m<f&&(a=t<3||i[0]>n||n>f)&&(i[4]=t,i[5]=n,p.n=f,o=0))}if(a||t>1)return c;throw u=!0,n}return function(a,d,f){if(l>1)throw TypeError("Generator is already running");for(u&&1===d&&m(d,f),o=d,s=f;(r=o<2?e:s)||!u;){i||(o?o<3?(o>1&&(p.n=-1),m(o,s)):p.n=s:p.v=s);try{if(l=2,i){if(o||(a="next"),r=i[a]){if(!(r=r.call(i,s)))throw TypeError("iterator result is not an object");if(!r.done)return r;s=r.value,o<2&&(o=0)}else 1===o&&(r=i.return)&&r.call(i),o<2&&(s=TypeError("The iterator does not provide a '"+a+"' method"),o=1);i=e}else if((r=(u=p.n<0)?s:t.call(n,p))!==c)break}catch(r){i=e,o=1,s=r}finally{l=1}}return{value:r,done:u}}}(t,a,i),!0),d}var c={};function l(){}function d(){}function u(){}r=Object.getPrototypeOf;var p=[][n]?r(r([][n]())):(s(r={},n,function(){return this}),r),m=u.prototype=l.prototype=Object.create(p);function f(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,u):(e.__proto__=u,s(e,a,"GeneratorFunction")),e.prototype=Object.create(m),e}return d.prototype=u,s(m,"constructor",u),s(u,"constructor",d),d.displayName="GeneratorFunction",s(u,a,"GeneratorFunction"),s(m),s(m,a,"Generator"),s(m,n,function(){return this}),s(m,"toString",function(){return"[object Generator]"}),(o=function(){return{w:i,m:f}})()}function s(e,r,t,n){var a=Object.defineProperty;try{a({},"",{})}catch(e){a=0}s=function(e,r,t,n){function i(r,t){s(e,r,function(e){return this._invoke(r,t,e)})}r?a?a(e,r,{value:t,enumerable:!n,configurable:!n,writable:!n}):e[r]=t:(i("next",0),i("throw",1),i("return",2))},s(e,r,t,n)}function c(r){return function(r){if(Array.isArray(r))return e(r)}(r)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(r)||u(r)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(e){var r=function(e,r){if("object"!=typeof e||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,r);if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e,"string");return"symbol"==typeof r?r:r+""}function d(e){return d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},d(e)}function u(r,t){if(r){if("string"==typeof r)return e(r,t);var n={}.toString.call(r).slice(8,-1);return"Object"===n&&r.constructor&&(n=r.constructor.name),"Map"===n||"Set"===n?Array.from(r):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?e(r,t):void 0}}var p=Object.defineProperty,m=Object.defineProperties,f=Object.getOwnPropertyDescriptors,h=Object.getOwnPropertySymbols,k=Object.prototype.hasOwnProperty,g=Object.prototype.propertyIsEnumerable,v=function(e,r,t){return r in e?p(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t},y=function(e,r){for(var t in r||(r={}))k.call(r,t)&&v(e,t,r[t]);if(h){var n,a=i(h(r));try{for(a.s();!(n=a.n()).done;){t=n.value;g.call(r,t)&&v(e,t,r[t])}}catch(e){a.e(e)}finally{a.f()}}return e},w=function(e,r){return m(e,f(r))},S=function(e,r,t){return v(e,"symbol"!==d(r)?r+"":r,t)},b={x:{required:!0,type:"number"},y:{required:!0,type:"number"},width:{required:!0,type:"number",notLessThanZero:!0,min:1,max:3840},height:{required:!0,type:"number",notLessThanZero:!0,min:1,max:3840},zIndex:{required:!0,type:"number"},fillMode:{required:!1,type:"string"},mirror:{required:!1,type:"boolean"},rotation:{required:!1,type:"number"},hidden:{required:!1,type:"boolean"}},T=function(e){return{type:"object",required:arguments.length>1&&void 0!==arguments[1]&&arguments[1],properties:{canvasColor:{required:!1,type:["string",CanvasGradient,CanvasPattern]},width:{required:!0,type:"number",notLessThanZero:!0,min:1,max:3840},height:{required:!0,type:"number",notLessThanZero:!0,min:1,max:3840},frameRate:{required:!1,type:"number",notLessThanZero:!0,min:1,max:60}},validate:function(r,t,n){var a=e.errorModule,i=a.RtcError,o=a.ErrorCode;if(a.ErrorCodeDictionary,r){var s=r.width,c=r.height;if(s&&c&&s*c>8294400)throw new i({code:o.INVALID_PARAMETER,message:"The mix resolution cannot be set higher than 3840 * 2160."})}}}},_=function(e){return{required:!1,type:["string",HTMLElement,null],validate:function(r,t,n){var a=e.errorModule,i=a.RtcError,o=a.ErrorCode,s=a.ErrorCodeDictionary;if(e.utils.isString(r)&&!document.getElementById(r))throw new i({code:o.INVALID_PARAMETER,extraCode:s.INVALID_ELEMENT_ID,fnName:n,messageParams:{key:t}})}}},x=function(e){return{type:"object",required:!(arguments.length>1&&void 0!==arguments[1])||arguments[1],properties:y({},b),validate:function(r,t,n){var a=e.errorModule,i=a.RtcError,o=a.ErrorCode,s=a.ErrorCodeDictionary;if(r){if(r.fillMode&&!["contain","cover","fill"].includes(r.fillMode))throw new i({code:o.INVALID_PARAMETER,extraCode:s.INVALID_PARAMETER_TYPE,message:"The fillMode parameter must be 'contain', 'cover' or 'fill'",fnName:n});if(r.rotation&&![0,90,180,270].includes(r.rotation))throw new i({code:o.INVALID_PARAMETER,extraCode:s.INVALID_PARAMETER_TYPE,message:"The rotation parameter must be 0, 90, 180 or 270",fnName:n})}}}},E=function(e){return{type:"array",required:!1,arrayItem:{type:"object",properties:{id:{required:!0,type:"string"},cameraId:{required:!1,type:"string"},videoTrack:{required:!1,instanceof:MediaStreamTrack},profile:{required:!1,type:["string","object"],properties:{width:{type:"number"},height:{type:"number"},frameRate:{type:"number"},bitrate:{type:"number"}}},layout:y({},x(e))}}}},I=function(e){return{type:"array",required:!1,arrayItem:{type:"object",properties:{id:{required:!0,type:"string"},profile:{required:!1,type:["string","object"],properties:{width:{type:"number"},height:{type:"number"},frameRate:{type:"number"},bitrate:{type:"number"}}},captureElement:{required:!1,type:HTMLElement},preferDisplaySurface:{required:!1,type:"string"},layout:y({},x(e))},validate:function(r,t,n){var a=e.errorModule,i=a.RtcError,o=a.ErrorCode,s=a.ErrorCodeDictionary;if(!e.rtcDectection.isScreenCaptureApiAvailable())throw new i({code:o.ENV_NOT_SUPPORTED,fnName:n,extraCode:s.NOT_SUPPORTED_SCREEN_SHARE})}}}},L=function(e){return{type:"array",required:!1,arrayItem:{type:"object",properties:{id:{required:!0,type:"string"},content:{required:!0,type:"string"},font:{required:!1,type:"string"},color:{required:!1,type:["string",CanvasGradient,CanvasPattern]},layout:y({},x(e))}}}},C=function(e){return{type:"array",required:!1,arrayItem:{type:"object",properties:{id:{required:!0,type:"string"},url:{required:!0,type:"string"},layout:y({},x(e))}}}},M=function(e){return{type:"array",required:!1,arrayItem:{type:"object",properties:{id:{required:!0,type:"string"},url:{required:!0,type:"string"},layout:y({},x(e))}}}};function F(){return"undefined"!=typeof OffscreenCanvas&&"undefined"!=typeof MediaStreamTrackGenerator&&"undefined"!=typeof MediaStreamTrackProcessor&&"undefined"!=typeof Worker}function R(e,r,t,n){return O.apply(this,arguments)}function O(){return(O=t(o().m(function e(r,t,n,a){var i,s,c,l,d,u,p,m,f,h,k;return o().w(function(e){for(;;)switch(e.n){case 0:if(t&&n){e.n=1;break}return e.a(2,null);case 1:if(i=new OffscreenCanvas(t,n),s=i.getContext("2d")){e.n=2;break}return e.a(2,null);case 2:for(s.textBaseline="top",c=(null==a?void 0:a.font)||"16px sans-serif",l=(null==a?void 0:a.color)||"white",s.font=c,s.fillStyle=l,d=/(\d+(?:\.\d+)?)px/.exec(c),u=d?parseFloat(d[1]):16,p=1.2*u,m=r.split("\n"),f=s.measureText(r),h=f.fontBoundingBoxAscent||f.actualBoundingBoxAscent||0,k=0;k<m.length;k++)s.fillText(m[k],0,h+k*p);return e.a(2,i.transferToImageBitmap())}},e)}))).apply(this,arguments)}var W=function(){return a(function e(r){var t,a,i=this;n(this,e),S(this,"_workerScript"),S(this,"_logger"),S(this,"_worker",null),S(this,"_workerUrl",null),S(this,"_generator",null),S(this,"_outputTrack",null),S(this,"_width",0),S(this,"_height",0),S(this,"_fps",15),S(this,"_sources",new Map),S(this,"_listeners",{unrecoverable:new Set}),S(this,"_destroyed",!1),S(this,"_workerFatal",!1),S(this,"_onWorkerMessage",function(e){if(!i._destroyed){var r=e.data;if(r&&"object"===d(r))switch(r.type){case"streamError":case"imageSourceError":i._logger.warn("Worker imageSource error: id=".concat(r.id,", ").concat(r.message));break;case"imageSourceLoaded":i._logger.debug("Worker imageSource loaded: id=".concat(r.id,", ").concat(r.width,"x").concat(r.height));break;case"log":i._forwardWorkerLog(r.level,r.message)}}}),S(this,"_onWorkerError",function(e){if(!i._destroyed&&!i._workerFatal){i._workerFatal=!0;var r=e,t=[];r.message&&t.push("message=".concat(r.message)),r.filename&&t.push("filename=".concat(r.filename)),"number"==typeof r.lineno&&t.push("lineno=".concat(r.lineno)),"number"==typeof r.colno&&t.push("colno=".concat(r.colno)),e.type&&t.push("eventType=".concat(e.type));var n=t.length>0?t.join(", "):"worker error";try{i._logger.error("unrecoverable: ".concat(n))}catch(e){}i._emit("unrecoverable",n)}}),this._workerScript=r.workerScript,this._logger=null!=(t=r.logger)?t:{info:(a=function(e){return function(r){try{e(r)}catch(e){}}})(function(e){return console.info(e)}),warn:a(function(e){return console.warn(e)}),error:a(function(e){return console.error(e)}),debug:a(function(e){return console.debug?console.debug(e):console.log(e)})}},[{key:"init",value:(s=t(o().m(function e(r){var t,n,a,i;return o().w(function(e){for(;;)switch(e.n){case 0:if(F()){e.n=1;break}throw new Error("WorkerMixRenderer: environment does not support OffscreenCanvas + MediaStreamTrackGenerator + Worker");case 1:if(!this._worker){e.n=2;break}throw new Error("WorkerMixRenderer: already initialized");case 2:return this._width=r.width,this._height=r.height,this._fps=null!=(t=r.fps)?t:15,this._workerUrl=URL.createObjectURL(new Blob([this._workerScript],{type:"application/javascript"})),this._worker=new Worker(this._workerUrl),this._worker.addEventListener("message",this._onWorkerMessage),this._worker.addEventListener("error",this._onWorkerError),this._worker.addEventListener("messageerror",this._onWorkerError),n=new OffscreenCanvas(this._width||640,this._height||480),this._generator=new MediaStreamTrackGenerator({kind:"video"}),this._outputTrack=this._generator,a=this._generator.writable,i=this._waitWorkerReady(),this._worker.postMessage({type:"init",canvas:n,writable:a,width:this._width,height:this._height},[n,a]),this._worker.postMessage({type:"start",fps:this._fps}),r.backgroundColor&&this._worker.postMessage({type:"setBackground",color:r.backgroundColor}),e.n=3,i;case 3:return e.a(2)}},e,this)})),function(e){return s.apply(this,arguments)})},{key:"getOutputTrack",value:function(){if(!this._outputTrack)throw new Error("WorkerMixRenderer: getOutputTrack called before init");return this._outputTrack}},{key:"setFps",value:function(e){!this._destroyed&&this._worker&&(this._fps=e,this._worker.postMessage({type:"setFps",fps:e}))}},{key:"destroy",value:(r=t(o().m(function e(){var r,t,n,a,s,c,l,d,u;return o().w(function(e){for(;;)switch(e.n){case 0:if(!this._destroyed){e.n=1;break}return e.a(2);case 1:this._destroyed=!0;try{null==(r=this._worker)||r.postMessage({type:"destroy"})}catch(e){}try{null==(t=this._generator)||t.stop()}catch(e){}this._generator=null,this._outputTrack=null,l=i(this._sources.values());try{for(l.s();!(d=l.n()).done;)if((u=d.value).bitmap&&"close"in u.bitmap)try{u.bitmap.close()}catch(e){}}catch(e){l.e(e)}finally{l.f()}return this._sources.clear(),e.n=2,new Promise(function(e){return setTimeout(e,0)});case 2:try{null==(n=this._worker)||n.removeEventListener("message",this._onWorkerMessage),null==(a=this._worker)||a.removeEventListener("error",this._onWorkerError),null==(s=this._worker)||s.removeEventListener("messageerror",this._onWorkerError),null==(c=this._worker)||c.terminate()}catch(e){}if(this._worker=null,this._workerUrl){try{URL.revokeObjectURL(this._workerUrl)}catch(e){}this._workerUrl=null}this._listeners.unrecoverable.clear();case 3:return e.a(2)}},e,this)})),function(){return r.apply(this,arguments)})},{key:"addSource",value:function(e,r,t,n){if(!this._destroyed&&this._worker){this._sources.has(e)&&this.removeSource(e);var a={kind:t,layout:w(y({},n),{id:e})};if("track"===t){var i=r;try{var o=new MediaStreamTrackProcessor({track:i});a.processor=o;var s=o.readable;this._worker.postMessage({type:"addReadableStream",id:e,readable:s},[s])}catch(r){return void this._logger.warn("addSource(track) failed for id=".concat(e,": ").concat((null==r?void 0:r.message)||r))}}else if("image"===t)if(r instanceof ImageBitmap){var c=r;a.bitmap=c,this._worker.postMessage({type:"addBitmapSource",id:e,bitmap:c},[c])}else{var l=r;a.isUrlImage=!0,this._worker.postMessage({type:"addImageSource",id:e,src:l.src,animated:!!l.animated,mimeType:l.mimeType})}else if("text"===t){var d=r;a.text={content:d,width:n.width,height:n.height,style:{}},this._renderTextAndSend(e,d,n.width,n.height,{},!1)}this._sources.set(e,a),this._sendLayoutSnapshot()}}},{key:"updateSource",value:function(e,r){var t,n,a;if(!this._destroyed&&this._worker){var i=this._sources.get(e);if(i)if("track"===i.kind){try{null==(t=i.processor)||t.readable.cancel()}catch(e){}i.processor=null,this._worker.postMessage({type:"removeReadableStream",id:e});try{var o=new MediaStreamTrackProcessor({track:r});i.processor=o;var s=o.readable;this._worker.postMessage({type:"addReadableStream",id:e,readable:s},[s])}catch(r){this._logger.warn("updateSource(track) failed for id=".concat(e,": ").concat((null==r?void 0:r.message)||r))}}else if("image"===i.kind)if(r instanceof ImageBitmap){i.isUrlImage&&(this._worker.postMessage({type:"removeImageSource",id:e}),i.isUrlImage=!1);var c=r;i.bitmap=c,this._worker.postMessage({type:"updateBitmapSource",id:e,bitmap:c},[c])}else{var l=r;if(!i.isUrlImage&&(this._worker.postMessage({type:"removeBitmapSource",id:e}),i.bitmap)){try{i.bitmap.close()}catch(e){}i.bitmap=null}i.isUrlImage=!0,this._worker.postMessage({type:"updateImageSource",id:e,src:l.src,animated:!!l.animated,mimeType:l.mimeType})}else if("text"===i.kind){var d=r,u=null!=(a=null==(n=i.text)?void 0:n.style)?a:{};i.text={content:d,width:i.layout.width,height:i.layout.height,style:u},this._renderTextAndSend(e,d,i.layout.width,i.layout.height,u,!0)}}}},{key:"addTextSource",value:function(e,r,t,n){if(!this._destroyed&&this._worker){this._sources.has(e)&&this.removeSource(e);var a={kind:"text",layout:w(y({},n),{id:e}),text:{content:r,width:n.width,height:n.height,style:y({},t)}};this._sources.set(e,a),this._renderTextAndSend(e,r,n.width,n.height,y({},t),!1),this._sendLayoutSnapshot()}}},{key:"updateTextSource",value:function(e,r,t){var n,a;if(!this._destroyed&&this._worker){var i=this._sources.get(e);if(i&&"text"===i.kind){var o=y(y({},null!=(a=null==(n=i.text)?void 0:n.style)?a:{}),t);i.text={content:r,width:i.layout.width,height:i.layout.height,style:o},this._renderTextAndSend(e,r,i.layout.width,i.layout.height,o,!0)}}}},{key:"removeSource",value:function(e){var r;if(!this._destroyed&&this._worker){var t=this._sources.get(e);if(t){if("track"===t.kind){try{null==(r=t.processor)||r.readable.cancel()}catch(e){}this._worker.postMessage({type:"removeReadableStream",id:e})}else"image"===t.kind&&t.isUrlImage?this._worker.postMessage({type:"removeImageSource",id:e}):this._worker.postMessage({type:"removeBitmapSource",id:e});if(t.bitmap)try{t.bitmap.close()}catch(e){}this._sources.delete(e),this._sendLayoutSnapshot()}}}},{key:"updateLayout",value:function(e){if(!this._destroyed&&this._worker){var r,t=i(e);try{for(t.s();!(r=t.n()).done;){var n=r.value,a=this._sources.get(n.id);if(a){var o=a.layout;a.layout=y({},n),"text"!==a.kind||!a.text||o.width===n.width&&o.height===n.height||(a.text.width=n.width,a.text.height=n.height,this._renderTextAndSend(n.id,a.text.content,n.width,n.height,a.text.style,!0))}}}catch(e){t.e(e)}finally{t.f()}this._sendLayoutSnapshot()}}},{key:"setBackground",value:function(e){!this._destroyed&&this._worker&&this._worker.postMessage({type:"setBackground",color:e})}},{key:"setBackgroundImage",value:function(e){!this._destroyed&&this._worker&&(e?this._worker.postMessage({type:"setBackgroundImage",bitmap:e},[e]):this._worker.postMessage({type:"setBackground",color:"black"}))}},{key:"resize",value:function(e,r){!this._destroyed&&this._worker&&(this._width=e,this._height=r,this._worker.postMessage({type:"resize",width:e,height:r}))}},{key:"on",value:function(e,r){this._listeners[e].add(r)}},{key:"off",value:function(e,r){this._listeners[e].delete(r)}},{key:"_emit",value:function(e,r){var t,n=i(this._listeners[e]);try{for(n.s();!(t=n.n()).done;){var a=t.value;try{a(r)}catch(e){}}}catch(e){n.e(e)}finally{n.f()}}},{key:"_sendLayoutSnapshot",value:function(){if(this._worker){var e,r=[],t=i(this._sources.values());try{for(t.s();!(e=t.n()).done;){var n=e.value;r.push(y({},n.layout))}}catch(e){t.e(e)}finally{t.f()}this._worker.postMessage({type:"updateLayout",layouts:r})}}},{key:"_renderTextAndSend",value:(e=t(o().m(function e(r,t,n,a,i,s){var c,l,d;return o().w(function(e){for(;;)switch(e.p=e.n){case 0:if(!this._destroyed&&this._worker){e.n=1;break}return e.a(2);case 1:return e.p=1,e.n=2,R(t,n,a,i);case 2:if(c=e.v){e.n=3;break}return e.a(2);case 3:if(!this._destroyed&&this._worker){e.n=4;break}try{c.close()}catch(e){}return e.a(2);case 4:if(l=this._sources.get(r)){e.n=5;break}try{c.close()}catch(e){}return e.a(2);case 5:l.bitmap=null,this._worker.postMessage({type:s?"updateBitmapSource":"addBitmapSource",id:r,bitmap:c},[c]),e.n=7;break;case 6:e.p=6,d=e.v,this._logger.warn("renderText failed for id=".concat(r,": ").concat((null==d?void 0:d.message)||d));case 7:return e.a(2)}},e,this,[[1,6]])})),function(r,t,n,a,i,o){return e.apply(this,arguments)})},{key:"_waitWorkerReady",value:function(){var e=this;return new Promise(function(r,t){if(!e._worker)return t(new Error("worker not created"));var n=e._worker,a=function(e){var o,s;"ready"===(null==(o=e.data)?void 0:o.type)?(n.removeEventListener("message",a),n.removeEventListener("error",i),r()):"error"===(null==(s=e.data)?void 0:s.type)&&(n.removeEventListener("message",a),n.removeEventListener("error",i),t(new Error("worker init error: ".concat(e.data.message))))},i=function(e){n.removeEventListener("message",a),n.removeEventListener("error",i),t(new Error("worker error: ".concat(e.message)))};n.addEventListener("message",a),n.addEventListener("error",i)})}},{key:"_forwardWorkerLog",value:function(e,r){var t="".concat("string"==typeof r?r:JSON.stringify(r));try{switch(e){case"warn":this._logger.warn(t);break;case"error":this._logger.error(t);break;case"debug":this._logger.debug(t);break;default:this._logger.info(t)}}catch(e){}}}]);var e,r,s}(),A=0,V=function(){function e(r){n(this,e),this.core=r,S(this,"seq"),S(this,"log"),S(this,"localMixVideoTrack",null),S(this,"systemAudioTrackList",{}),S(this,"_mixVideoConfig"),S(this,"onScreenShareStop"),S(this,"eventListeners",new Map),A+=1,this.seq=A,this.log=r.log.createChild({id:"".concat(this.getAlias()).concat(A)}),this.log.info("created")}return a(e,[{key:"getName",value:function(){return e.Name}},{key:"getAlias",value:function(){return"vmix"}},{key:"getValidateRule",value:function(e){switch(e){case"start":return r=this.core,{name:"VideoMixerOptions",type:"object",required:!0,allowEmpty:!1,properties:{view:y({},_(r)),canvasInfo:y({},T(r,!0)),camera:y({},E(r)),screen:y({},I(r)),text:y({},L(r)),image:y({},C(r)),video:y({},M(r))},validate:function(e,t,n,a){var i=r.errorModule,o=i.RtcError,s=i.ErrorCode,c=i.ErrorCodeDictionary;if(r.environment.isMobile())throw new o({code:s.ENV_NOT_SUPPORTED,message:"VideoMixer is not supported on mobile devices currently"});var l=e.onScreenShareStop;if(l&&!r.utils.isFunction(l))throw new o({code:s.INVALID_PARAMETER,extraCode:c.INVALID_PARAMETER_TYPE,fnName:n,messageParams:{key:"onScreenShareStop",value:d(l),rule:{type:"Function"}}})}};case"update":return function(e){return{name:"VideoMixerOptions",type:"object",required:!1,allowEmpty:!1,properties:{view:y({},_(e)),canvasInfo:y({},T(e)),camera:y({},E(e)),screen:y({},I(e)),text:y({},L(e)),image:y({},C(e)),video:y({},M(e))}}}(this.core);case"stop":return this.core,{name:"StopVideoMixerOptions",required:!1}}var r}},{key:"getGroup",value:function(){return"vmix"}},{key:"start",value:($=t(o().m(function e(r){var t,n,a,i,s,l,d,u,p,m,f,h,k,g;return o().w(function(e){for(;;)switch(e.n){case 0:return this.localMixVideoTrack||(this.localMixVideoTrack=new this.core.LocalMixVideoTrack(this.core.room.videoManager)),this._mixVideoConfig={canvasInfo:{width:1920,height:1080}},r=this.core.utils.deepCloneBasic(r),n=(t=r).view,a=t.onScreenShareStop,e.n=1,this.parseMixOptionsSyncPhase(r);case 1:return i=e.v,s=i.syncResult,l=i.asyncSourcesInfo,a&&(this.onScreenShareStop=a,this._mixVideoConfig.onScreenShareStop=a),this._updatePreview({view:n,track:this.localMixVideoTrack}),this.core.utils.isUndefined(n)||(this._mixVideoConfig.view=n),e.n=2,this.localMixVideoTrack.startMix({enableWorkerRenderer:!0});case 2:return e.n=3,this._tryAttachWorkerRenderer();case 3:return e.n=4,this.parseMixOptionsAsyncPhase(l);case 4:if(d=e.v,u=y(y({},s.successOptions),d.successOptions),p=[].concat(c(s.failedDetails),c(d.failedDetails)),s.successOptions.screen&&d.successOptions.screen&&(u.screen=[].concat(c(s.successOptions.screen),c(d.successOptions.screen))),m=s.sourceCount+d.sourceCount,!((f=s.allFailCount+d.allFailCount)>0&&f===m)){e.n=5;break}throw h=this.core.errorModule,k=h.RtcError,g=h.ErrorCode,new k({code:g.INVALID_PARAMETER,message:"all sources mix failed",data:{failedDetails:p}});case 5:return e.a(2,{track:this.localMixVideoTrack.outMediaTrack,systemAudioTrackList:this.systemAudioTrackList,result:{successOptions:u,failedDetails:p}})}},e,this)})),function(e){return $.apply(this,arguments)})},{key:"_tryAttachWorkerRenderer",value:(H=t(o().m(function e(){var r,t,n;return o().w(function(e){for(;;)switch(e.p=e.n){case 0:if(this.localMixVideoTrack&&this._mixVideoConfig){e.n=1;break}return e.a(2);case 1:if(F()){e.n=2;break}return this._setFallbackOutputTrack(),e.a(2);case 2:return r=new W({workerScript:'// video-effect/video-mixer/src/mix-render-worker.ts\nfunction isRotate90Or270(rotation) {\n return rotation === 90 || rotation === 270;\n}\nfunction calculateCanvasDrawParams(srcW, srcH, dstW, dstH, fit, swapWH = false) {\n if (swapWH) [dstW, dstH] = [dstH, dstW];\n const res = { sWidth: srcW, sHeight: srcH, dWidth: dstW, dHeight: dstH, sx: 0, sy: 0, dx: 0, dy: 0 };\n if (srcW === 0 || srcH === 0) return res;\n switch (fit) {\n case void 0:\n case "fill":\n break;\n case "contain": {\n const ratio = Math.min(dstW / srcW, dstH / srcH);\n res.dWidth = srcW * ratio;\n res.dHeight = srcH * ratio;\n res.dx = (dstW - res.dWidth) / 2;\n res.dy = (dstH - res.dHeight) / 2;\n break;\n }\n case "cover": {\n const ratio = Math.max(dstW / srcW, dstH / srcH);\n const newSrcW = dstW / ratio;\n const newSrcH = dstH / ratio;\n res.sx = (srcW - newSrcW) / 2;\n res.sy = (srcH - newSrcH) / 2;\n res.sWidth = newSrcW;\n res.sHeight = newSrcH;\n break;\n }\n }\n return res;\n}\nvar drawParamsCache = /* @__PURE__ */ new Map();\nfunction getCachedDrawParams(id, srcW, srcH, dstW, dstH, fillMode, swapWH = false) {\n const key = `${srcW},${srcH},${dstW},${dstH},${fillMode},${swapWH}`;\n const cached = drawParamsCache.get(id);\n if (cached && cached.key === key) {\n return cached.params;\n }\n const params = calculateCanvasDrawParams(srcW, srcH, dstW, dstH, fillMode, swapWH);\n drawParamsCache.set(id, { key, params });\n return params;\n}\nfunction workerLog(level, message) {\n self.postMessage({ type: "log", level, message });\n}\nvar throttleLogTimestamps = /* @__PURE__ */ new Map();\nvar THROTTLE_LOG_MAX_ENTRIES = 50;\nvar THROTTLE_LOG_EXPIRY_MS = 3e4;\nfunction throttledLog(key, level, message, interval = 2e3) {\n const now = performance.now();\n if (throttleLogTimestamps.size > THROTTLE_LOG_MAX_ENTRIES) {\n for (const [k, ts] of throttleLogTimestamps) {\n if (now - ts > THROTTLE_LOG_EXPIRY_MS) {\n throttleLogTimestamps.delete(k);\n }\n }\n if (throttleLogTimestamps.size > THROTTLE_LOG_MAX_ENTRIES) {\n workerLog("warn", `[Worker] throttledLog: map size (${throttleLogTimestamps.size}) still exceeds ${THROTTLE_LOG_MAX_ENTRIES} after eviction, possible dynamic key misuse`);\n }\n }\n const last = throttleLogTimestamps.get(key) || 0;\n if (now - last >= interval) {\n throttleLogTimestamps.set(key, now);\n workerLog(level, message);\n return true;\n }\n return false;\n}\nvar canvas = null;\nvar ctx = null;\nvar layouts = [];\nvar latestFrames = /* @__PURE__ */ new Map();\nvar backgroundColor = "black";\nvar backgroundImage = null;\nvar fps = 15;\nvar renderIntervalId = null;\nvar renderLoopStarted = false;\nvar totalFrames = 0;\nvar dropFrames = 0;\nvar isRendering = false;\nvar dropByBackpressure = 0;\nvar dropByRendering = 0;\nvar dropByCanvasSize = 0;\nvar dropByWriteFail = 0;\nvar dropByOther = 0;\nvar isBackpressure = false;\nvar backpressureStartTime = 0;\nvar backpressureTotalDrops = 0;\nvar isCanvasSizeZero = false;\nvar consecutiveWriteFailures = 0;\nvar trackGeneratorWriter = null;\nvar trackGeneratorFailCount = 0;\nvar TRACK_GENERATOR_FAIL_THRESHOLD = 30;\nvar isFirstFrame = true;\nvar healthLogIntervalId = null;\nvar lastHealthLogTime = 0;\nvar lastHealthLogFrames = 0;\nvar streamControllers = /* @__PURE__ */ new Map();\nvar imageSources = /* @__PURE__ */ new Map();\nasync function handleAddImageSource(id, src, animated, mimeType) {\n const existingState = imageSources.get(id);\n if (existingState && existingState.src === src) {\n workerLog("info", `[Worker] handleAddImageSource: skipping duplicate for ${id} (same src)`);\n return;\n }\n handleRemoveImageSource(id);\n const state = {\n id,\n src,\n animated,\n currentFrameIndex: 0,\n frameCount: 0,\n repetitionCount: 0,\n loaded: false,\n completed: false,\n consecutiveErrors: 0,\n loopCount: 0\n };\n const abortController = new AbortController();\n state.abortController = abortController;\n imageSources.set(id, state);\n try {\n const response = await fetch(src, { signal: abortController.signal });\n if (animated && typeof self.ImageDecoder !== "undefined" && mimeType) {\n const arrayBuffer = await response.arrayBuffer();\n const ImageDecoderClass = self.ImageDecoder;\n const decoder = new ImageDecoderClass({ data: arrayBuffer, type: mimeType });\n state.decoder = decoder;\n state.completed = true;\n await decoder.tracks.ready;\n const track = decoder.tracks.selectedTrack;\n state.frameCount = track.frameCount;\n state.repetitionCount = track.repetitionCount;\n workerLog("info", `[Worker] ImageDecoder ready for ${id}: frameCount=${state.frameCount}, repetitionCount=${state.repetitionCount}, dataMode=arrayBuffer`);\n decodeAndDisplayFrame(state);\n } else {\n const blob = await response.blob();\n const bitmap = await createImageBitmap(blob);\n const prev = latestFrames.get(id);\n if (prev && "close" in prev.frame && typeof prev.frame.close === "function") {\n try {\n prev.frame.close();\n } catch (e) {\n }\n }\n latestFrames.set(id, { id, frame: bitmap, width: bitmap.width, height: bitmap.height });\n self.postMessage({\n type: "imageSourceLoaded",\n id,\n width: bitmap.width,\n height: bitmap.height\n });\n }\n } catch (err) {\n if (err.name === "AbortError") return;\n workerLog("warn", `[Worker] ImageSource load failed for ${id}: ${err.message || err}`);\n self.postMessage({\n type: "imageSourceError",\n id,\n message: err.message || String(err)\n });\n }\n}\nasync function decodeAndDisplayFrame(state) {\n if (!state.decoder || !imageSources.has(state.id)) return;\n try {\n const result = await state.decoder.decode({ frameIndex: state.currentFrameIndex });\n const videoFrame = result.image;\n state.consecutiveErrors = 0;\n const prev = latestFrames.get(state.id);\n if (prev && "close" in prev.frame && typeof prev.frame.close === "function") {\n try {\n prev.frame.close();\n } catch (e) {\n }\n }\n latestFrames.set(state.id, {\n id: state.id,\n frame: videoFrame,\n width: videoFrame.displayWidth,\n height: videoFrame.displayHeight\n });\n if (!state.loaded) {\n state.loaded = true;\n self.postMessage({\n type: "imageSourceLoaded",\n id: state.id,\n width: videoFrame.displayWidth,\n height: videoFrame.displayHeight\n });\n }\n const durationMs = (videoFrame.duration || 1e5) / 1e3;\n state.currentFrameIndex++;\n if (state.currentFrameIndex >= state.frameCount) {\n if (state.repetitionCount === Infinity) {\n state.currentFrameIndex = 0;\n state.loopCount++;\n if (state.loopCount === 1 || state.loopCount % 10 === 0) {\n }\n } else if (!state.completed) {\n state.currentFrameIndex = 0;\n state.loopCount++;\n } else if (state.repetitionCount > 0) {\n state.repetitionCount--;\n state.currentFrameIndex = 0;\n state.loopCount++;\n } else {\n return;\n }\n }\n if (imageSources.has(state.id)) {\n state.timerId = setTimeout(() => decodeAndDisplayFrame(state), durationMs);\n }\n } catch (err) {\n if (err.name === "AbortError") return;\n state.consecutiveErrors++;\n const errMsg = err.message || String(err);\n const errName = err.name || "Error";\n workerLog("warn", `[Worker] ImageDecoder decode failed for ${state.id} frame ${state.currentFrameIndex}: [${errName}] ${errMsg} (consecutive=${state.consecutiveErrors})`);\n if (state.consecutiveErrors >= 3) {\n workerLog("error", `[Worker] ImageSource ${state.id} stopped: ${state.consecutiveErrors} consecutive decode errors`);\n return;\n }\n if (!imageSources.has(state.id)) return;\n if (errName === "RangeError") {\n state.currentFrameIndex = 0;\n } else {\n state.currentFrameIndex++;\n if (state.frameCount > 0 && state.currentFrameIndex >= state.frameCount) {\n state.currentFrameIndex = 0;\n }\n }\n state.timerId = setTimeout(() => decodeAndDisplayFrame(state), 100);\n }\n}\nfunction handleRemoveImageSource(id) {\n const state = imageSources.get(id);\n if (!state) return;\n if (state.abortController) {\n try {\n state.abortController.abort();\n } catch (e) {\n }\n }\n if (state.timerId) {\n clearTimeout(state.timerId);\n state.timerId = void 0;\n }\n if (state.decoder) {\n try {\n state.decoder.close();\n } catch (e) {\n }\n state.decoder = void 0;\n }\n const frameData = latestFrames.get(id);\n if (frameData) {\n if ("close" in frameData.frame && typeof frameData.frame.close === "function") {\n try {\n frameData.frame.close();\n } catch (e) {\n }\n }\n latestFrames.delete(id);\n }\n imageSources.delete(id);\n}\nfunction handleUpdateImageSource(id, src, animated, mimeType) {\n handleRemoveImageSource(id);\n handleAddImageSource(id, src, animated, mimeType);\n}\nfunction drawFrame(layout, frame) {\n if (!ctx || layout.hidden) return;\n const swapWH = isRotate90Or270(layout.rotation);\n const srcW = frame.width;\n const srcH = frame.height;\n const rotation = layout.rotation || 0;\n if (swapWH) {\n const mixSrcW = srcH;\n const mixSrcH = srcW;\n const opt = getCachedDrawParams(\n layout.id,\n mixSrcW,\n mixSrcH,\n layout.width,\n layout.height,\n layout.fillMode,\n swapWH\n );\n const needsTransform = !!layout.mirror || rotation !== 0;\n ctx.save();\n ctx.translate(layout.x + opt.dx, layout.y + opt.dy);\n ctx.scale(opt.dWidth / opt.sWidth, opt.dHeight / opt.sHeight);\n ctx.translate(-opt.sx, -opt.sy);\n if (rotation === 90) {\n if (layout.mirror) {\n ctx.scale(-1, 1);\n } else {\n ctx.translate(srcH, 0);\n }\n ctx.rotate(Math.PI / 2);\n ctx.scale(srcW / srcH, srcH / srcW);\n } else {\n if (layout.mirror) {\n ctx.scale(-1, 1);\n ctx.translate(-srcH, srcW);\n } else {\n ctx.translate(0, srcW);\n }\n ctx.rotate(3 * Math.PI / 2);\n ctx.scale(srcW / srcH, srcH / srcW);\n }\n ctx.drawImage(frame.frame, 0, 0, srcH, srcW);\n ctx.restore();\n } else {\n const opt = getCachedDrawParams(\n layout.id,\n srcW,\n srcH,\n layout.width,\n layout.height,\n layout.fillMode,\n false\n );\n const dw = opt.dWidth;\n const dh = opt.dHeight;\n const needsTransform = !!layout.mirror || rotation !== 0;\n if (needsTransform) {\n ctx.save();\n ctx.translate(layout.x, layout.y);\n if (layout.mirror) {\n ctx.scale(-1, 1);\n ctx.translate(-dw, 0);\n }\n if (rotation === 180) {\n ctx.translate(dw, dh);\n ctx.rotate(Math.PI);\n }\n ctx.drawImage(frame.frame, opt.sx, opt.sy, opt.sWidth, opt.sHeight, opt.dx, opt.dy, dw, dh);\n ctx.restore();\n } else {\n ctx.drawImage(\n frame.frame,\n opt.sx,\n opt.sy,\n opt.sWidth,\n opt.sHeight,\n layout.x + opt.dx,\n layout.y + opt.dy,\n dw,\n dh\n );\n }\n }\n}\nfunction render() {\n if (!canvas || !ctx) {\n dropFrames++;\n dropByOther++;\n return;\n }\n if (isRendering) {\n dropFrames++;\n dropByRendering++;\n return;\n }\n isRendering = true;\n try {\n if (backgroundImage) {\n ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);\n } else {\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n }\n let drawnCount = 0;\n let missedIds = null;\n for (const layout of layouts) {\n if (layout.hidden) continue;\n const frame = latestFrames.get(layout.id);\n if (!frame) {\n if (!missedIds) missedIds = [];\n missedIds.push(layout.id);\n continue;\n }\n drawFrame(layout, frame);\n drawnCount++;\n }\n if (missedIds && missedIds.length > 0) {\n throttledLog("frameMiss", "debug", `[Worker] render: missed frames for layouts=[${missedIds.join(",")}], drawn=${drawnCount}, latestFrames keys=[${[...latestFrames.keys()].join(",")}], layouts=[${layouts.map((l) => l.id).join(",")}], streamControllers=[${[...streamControllers.keys()].join(",")}]`, 3e3);\n }\n if (trackGeneratorWriter) {\n if (trackGeneratorWriter.desiredSize === null || trackGeneratorWriter.desiredSize <= 0) {\n dropFrames++;\n dropByBackpressure++;\n if (!isBackpressure) {\n isBackpressure = true;\n backpressureStartTime = performance.now();\n backpressureTotalDrops = 0;\n }\n backpressureTotalDrops++;\n throttledLog("backpressure", "warn", `[Worker] Writer backpressure, skipping frames. desiredSize=${trackGeneratorWriter.desiredSize}, dropFrames=${dropFrames}`);\n } else {\n if (isBackpressure) {\n const duration = ((performance.now() - backpressureStartTime) / 1e3).toFixed(1);\n workerLog("info", `[Worker] Writer backpressure recovered. duration=${duration}s, droppedDuringBackpressure=${backpressureTotalDrops}`);\n isBackpressure = false;\n backpressureStartTime = 0;\n backpressureTotalDrops = 0;\n }\n if (canvas.width <= 0 || canvas.height <= 0) {\n dropFrames++;\n dropByCanvasSize++;\n if (!isCanvasSizeZero) {\n isCanvasSizeZero = true;\n workerLog("warn", `[Worker] Canvas size is 0 (${canvas.width}x${canvas.height}), skipping VideoFrame creation`);\n }\n } else {\n if (isCanvasSizeZero) {\n isCanvasSizeZero = false;\n workerLog("info", `[Worker] Canvas size recovered to ${canvas.width}x${canvas.height}`);\n }\n let frame = null;\n try {\n frame = new VideoFrame(canvas, { timestamp: performance.now() * 1e3 });\n const frameWidth = frame.displayWidth;\n const frameHeight = frame.displayHeight;\n trackGeneratorWriter.write(frame);\n totalFrames++;\n trackGeneratorFailCount = 0;\n if (consecutiveWriteFailures > 0) {\n workerLog("info", `[Worker] VideoFrame write recovered after ${consecutiveWriteFailures} consecutive failures`);\n consecutiveWriteFailures = 0;\n }\n if (isFirstFrame) {\n isFirstFrame = false;\n workerLog("info", `[Worker] TrackGenerator First VideoFrame written successfully, width=${frameWidth}, height=${frameHeight}`);\n }\n } catch (err) {\n if (frame) {\n try {\n frame.close();\n } catch (e) {\n }\n }\n dropFrames++;\n dropByWriteFail++;\n trackGeneratorFailCount++;\n consecutiveWriteFailures++;\n if (consecutiveWriteFailures <= 3) {\n workerLog("warn", `[Worker] VideoFrame write failed: ${err.message || err}, failCount=${trackGeneratorFailCount}`);\n } else {\n throttledLog("writeFail", "warn", `[Worker] VideoFrame write still failing: ${err.message || err}, consecutiveFailures=${consecutiveWriteFailures}, failCount=${trackGeneratorFailCount}`);\n }\n if (trackGeneratorFailCount >= TRACK_GENERATOR_FAIL_THRESHOLD) {\n workerLog("error", `[Worker] TrackGenerator failed ${trackGeneratorFailCount} times, requesting degradation`);\n self.postMessage({ type: "trackGeneratorFailed", failCount: trackGeneratorFailCount });\n }\n }\n }\n }\n } else {\n if (!renderLoopStarted) {\n return;\n }\n dropFrames++;\n dropByOther++;\n workerLog("error", "[Worker] No TrackGenerator writer available, cannot output frames");\n stopRenderLoop();\n self.postMessage({ type: "error", message: "No TrackGenerator writer available, rendering stopped" });\n return;\n }\n } catch (err) {\n dropFrames++;\n dropByOther++;\n throttledLog("renderError", "error", `[Worker] Render error: ${err.message || err}`);\n } finally {\n isRendering = false;\n }\n}\nfunction startRenderLoop() {\n stopRenderLoop();\n if (fps <= 0) return;\n const interval = 1e3 / fps;\n renderIntervalId = setInterval(render, interval);\n renderLoopStarted = true;\n lastHealthLogTime = performance.now();\n lastHealthLogFrames = totalFrames;\n dropByBackpressure = 0;\n dropByRendering = 0;\n dropByCanvasSize = 0;\n dropByWriteFail = 0;\n dropByOther = 0;\n}\nfunction stopRenderLoop() {\n if (renderIntervalId !== null) {\n clearInterval(renderIntervalId);\n renderIntervalId = null;\n }\n renderLoopStarted = false;\n throttleLogTimestamps.clear();\n}\nfunction closeFrames() {\n latestFrames.forEach((frameData) => {\n try {\n if ("close" in frameData.frame && typeof frameData.frame.close === "function") {\n frameData.frame.close();\n }\n } catch (e) {\n }\n });\n latestFrames.clear();\n}\nfunction abortAllStreams() {\n clearAllStreamStallTimers();\n streamControllers.forEach((controller, id) => {\n try {\n controller.abort();\n } catch (e) {\n }\n });\n streamControllers.clear();\n}\nvar streamFirstFrameReceived = /* @__PURE__ */ new Map();\nvar STREAM_STALL_TIMEOUT_MS = 3e3;\nvar streamStallTimers = /* @__PURE__ */ new Map();\nfunction clearStreamStallTimer(id) {\n const timer = streamStallTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n streamStallTimers.delete(id);\n }\n}\nfunction clearAllStreamStallTimers() {\n streamStallTimers.forEach((timer) => {\n clearTimeout(timer);\n });\n streamStallTimers.clear();\n}\nfunction startReadableStreamPipeline(id, readable) {\n const existing = streamControllers.get(id);\n if (existing) {\n workerLog("debug", `[Worker] startReadableStreamPipeline(${id}): aborting existing pipeline`);\n try {\n existing.abort();\n } catch (e) {\n }\n streamControllers.delete(id);\n }\n const abortController = new AbortController();\n streamControllers.set(id, abortController);\n streamFirstFrameReceived.set(id, false);\n clearStreamStallTimer(id);\n const stallTimer = setTimeout(() => {\n streamStallTimers.delete(id);\n if (!streamFirstFrameReceived.get(id)) {\n workerLog("warn", `[Worker] ReadableStream(${id}): no frames received within ${STREAM_STALL_TIMEOUT_MS}ms, reporting streamStalled`);\n self.postMessage({\n type: "streamStalled",\n id,\n stallDuration: STREAM_STALL_TIMEOUT_MS\n });\n }\n }, STREAM_STALL_TIMEOUT_MS);\n streamStallTimers.set(id, stallTimer);\n workerLog("debug", `[Worker] startReadableStreamPipeline(${id}): pipeline created, stall timer started (${STREAM_STALL_TIMEOUT_MS}ms), waiting for frames...`);\n const writable = new WritableStream({\n write(frame) {\n if (!streamFirstFrameReceived.get(id)) {\n streamFirstFrameReceived.set(id, true);\n clearStreamStallTimer(id);\n workerLog("info", `[Worker] ReadableStream(${id}): first frame received, ${frame.displayWidth}x${frame.displayHeight}, latestFrames.size=${latestFrames.size}, stall timer cancelled`);\n }\n const prev = latestFrames.get(id);\n if (prev) {\n try {\n if ("close" in prev.frame && typeof prev.frame.close === "function") {\n prev.frame.close();\n }\n } catch (e) {\n }\n }\n latestFrames.set(id, {\n id,\n frame,\n width: frame.displayWidth,\n height: frame.displayHeight\n });\n },\n close() {\n workerLog("info", `[Worker] ReadableStream(${id}): stream closed normally`);\n clearStreamStallTimer(id);\n streamControllers.delete(id);\n streamFirstFrameReceived.delete(id);\n },\n abort() {\n workerLog("debug", `[Worker] ReadableStream(${id}): stream aborted`);\n clearStreamStallTimer(id);\n streamControllers.delete(id);\n streamFirstFrameReceived.delete(id);\n }\n });\n readable.pipeTo(writable, { signal: abortController.signal }).catch((err) => {\n if (err.name === "AbortError") {\n workerLog("debug", `[Worker] ReadableStream(${id}): pipeTo aborted (expected)`);\n return;\n }\n workerLog("warn", `[Worker] ReadableStream(${id}): pipeTo error: ${err.message || err}`);\n clearStreamStallTimer(id);\n streamControllers.delete(id);\n streamFirstFrameReceived.delete(id);\n self.postMessage({\n type: "streamError",\n id,\n message: `ReadableStream pipeline error: ${err.message || err}`\n });\n });\n}\nfunction removeReadableStreamPipeline(id) {\n const hadController = streamControllers.has(id);\n const hadFrame = latestFrames.has(id);\n const controller = streamControllers.get(id);\n if (controller) {\n try {\n controller.abort();\n } catch (e) {\n }\n streamControllers.delete(id);\n }\n const frameData = latestFrames.get(id);\n if (frameData) {\n try {\n if ("close" in frameData.frame && typeof frameData.frame.close === "function") {\n frameData.frame.close();\n }\n } catch (e) {\n }\n latestFrames.delete(id);\n }\n clearStreamStallTimer(id);\n streamFirstFrameReceived.delete(id);\n workerLog("debug", `[Worker] removeReadableStreamPipeline(${id}): hadController=${hadController}, hadFrame=${hadFrame}, remaining latestFrames=[${[...latestFrames.keys()].join(",")}]`);\n}\nfunction closeTrackGeneratorWriter() {\n if (trackGeneratorWriter) {\n try {\n trackGeneratorWriter.close();\n } catch (e) {\n }\n trackGeneratorWriter = null;\n }\n}\nfunction startHealthLog() {\n stopHealthLog();\n lastHealthLogTime = performance.now();\n lastHealthLogFrames = totalFrames;\n healthLogIntervalId = setInterval(() => {\n const now = performance.now();\n const elapsed = (now - lastHealthLogTime) / 1e3;\n if (elapsed <= 0) return;\n const framesDelta = totalFrames - lastHealthLogFrames;\n const actualFps = framesDelta / elapsed;\n const writerInfo = trackGeneratorWriter ? `desiredSize=${trackGeneratorWriter.desiredSize}` : "no writer";\n const periodDrops = dropByBackpressure + dropByRendering + dropByCanvasSize + dropByWriteFail + dropByOther;\n if (renderLoopStarted && Math.ceil(actualFps) < 5 || periodDrops > 0) {\n let msg = `[Worker] Health: totalFrames=${totalFrames}, dropFrames=${dropFrames}, fps=${actualFps.toFixed(1)}, ${writerInfo}`;\n if (periodDrops > 0) {\n const parts = [];\n if (dropByBackpressure > 0) parts.push(`backpressure=${dropByBackpressure}`);\n if (dropByRendering > 0) parts.push(`rendering=${dropByRendering}`);\n if (dropByCanvasSize > 0) parts.push(`canvasSize=${dropByCanvasSize}`);\n if (dropByWriteFail > 0) parts.push(`writeFail=${dropByWriteFail}`);\n if (dropByOther > 0) parts.push(`other=${dropByOther}`);\n msg += `, periodDrops={${parts.join(", ")}}`;\n }\n const suggestions = [];\n if (dropByBackpressure > 0) suggestions.push("reduce fps or resolution to alleviate backpressure");\n if (dropByRendering > 0) suggestions.push("reduce fps or layout count to avoid render overlap");\n if (dropByCanvasSize > 0) suggestions.push("check canvas dimensions configuration");\n if (dropByWriteFail > 0) suggestions.push("check encoding parameters or TrackGenerator state");\n if (suggestions.length > 0) {\n msg += `. Suggestion: ${suggestions.join("; ")}`;\n }\n workerLog(actualFps < 5 ? "warn" : "info", msg);\n }\n dropByBackpressure = 0;\n dropByRendering = 0;\n dropByCanvasSize = 0;\n dropByWriteFail = 0;\n dropByOther = 0;\n lastHealthLogTime = now;\n lastHealthLogFrames = totalFrames;\n }, 2e3);\n}\nfunction stopHealthLog() {\n if (healthLogIntervalId !== null) {\n clearInterval(healthLogIntervalId);\n healthLogIntervalId = null;\n }\n}\nself.onmessage = (event) => {\n const { data } = event;\n switch (data.type) {\n case "init": {\n canvas = data.canvas;\n ctx = canvas.getContext("2d");\n if (!ctx) {\n self.postMessage({\n type: "error",\n message: "Failed to get OffscreenCanvas 2d context in Worker"\n });\n return;\n }\n if (data.width) canvas.width = data.width;\n if (data.height) canvas.height = data.height;\n if (data.writable) {\n try {\n trackGeneratorWriter = data.writable.getWriter();\n isFirstFrame = true;\n trackGeneratorFailCount = 0;\n workerLog("info", `[Worker] TrackGenerator writer obtained, desiredSize=${trackGeneratorWriter.desiredSize}`);\n } catch (err) {\n workerLog("warn", `[Worker] Failed to get TrackGenerator writer: ${err.message || err}`);\n trackGeneratorWriter = null;\n }\n }\n startHealthLog();\n self.postMessage({ type: "ready" });\n break;\n }\n case "updateFrames": {\n