trtc-sdk-v5
Version:
Tencent Cloud RTC SDK for Web
1 lines • 64.8 kB
JavaScript
var __defProp=Object.defineProperty,__defProps=Object.defineProperties,__getOwnPropDescs=Object.getOwnPropertyDescriptors,__getOwnPropSymbols=Object.getOwnPropertySymbols,__hasOwnProp=Object.prototype.hasOwnProperty,__propIsEnum=Object.prototype.propertyIsEnumerable,__defNormalProp=(e,r,t)=>r in e?__defProp(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,__spreadValues=(e,r)=>{for(var t in r||(r={}))__hasOwnProp.call(r,t)&&__defNormalProp(e,t,r[t]);if(__getOwnPropSymbols)for(var t of __getOwnPropSymbols(r))__propIsEnum.call(r,t)&&__defNormalProp(e,t,r[t]);return e},__spreadProps=(e,r)=>__defProps(e,__getOwnPropDescs(r)),__publicField=(e,r,t)=>__defNormalProp(e,"symbol"!=typeof r?r+"":r,t),layoutProperty={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"}},canvasValidateRule=(e,r=!1)=>({type:"object",required:r,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(r,t,a){const{RtcError:n,ErrorCode:s,ErrorCodeDictionary:i}=e.errorModule;if(!r)return;const{width:o,height:c}=r;if(o&&c&&o*c>8294400)throw new n({code:s.INVALID_PARAMETER,message:"The mix resolution cannot be set higher than 3840 * 2160."})}}),viewValidateRule=e=>({required:!1,type:["string",HTMLElement,null],validate(r,t,a){const{RtcError:n,ErrorCode:s,ErrorCodeDictionary:i}=e.errorModule;if(e.utils.isString(r)){if(!document.getElementById(r))throw new n({code:s.INVALID_PARAMETER,extraCode:i.INVALID_ELEMENT_ID,fnName:a,messageParams:{key:t}})}}}),layoutValidateRule=(e,r=!0)=>({type:"object",required:r,properties:__spreadValues({},layoutProperty),validate(r,t,a){const{RtcError:n,ErrorCode:s,ErrorCodeDictionary:i}=e.errorModule;if(r){if(r.fillMode&&!["contain","cover","fill"].includes(r.fillMode))throw new n({code:s.INVALID_PARAMETER,extraCode:i.INVALID_PARAMETER_TYPE,message:"The fillMode parameter must be 'contain', 'cover' or 'fill'",fnName:a});if(r.rotation&&![0,90,180,270].includes(r.rotation))throw new n({code:s.INVALID_PARAMETER,extraCode:i.INVALID_PARAMETER_TYPE,message:"The rotation parameter must be 0, 90, 180 or 270",fnName:a})}}}),cameraValidateRule=e=>({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:__spreadValues({},layoutValidateRule(e))}}}),screenValidateRule=e=>({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:__spreadValues({},layoutValidateRule(e))},validate(r,t,a){const{RtcError:n,ErrorCode:s,ErrorCodeDictionary:i}=e.errorModule;if(!e.rtcDectection.isScreenCaptureApiAvailable())throw new n({code:s.ENV_NOT_SUPPORTED,fnName:a,extraCode:i.NOT_SUPPORTED_SCREEN_SHARE})}}}),textValidateRule=e=>({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:__spreadValues({},layoutValidateRule(e))}}}),imageValidateRule=e=>({type:"array",required:!1,arrayItem:{type:"object",properties:{id:{required:!0,type:"string"},url:{required:!0,type:"string"},layout:__spreadValues({},layoutValidateRule(e))}}}),videoValidateRule=e=>({type:"array",required:!1,arrayItem:{type:"object",properties:{id:{required:!0,type:"string"},url:{required:!0,type:"string"},layout:__spreadValues({},layoutValidateRule(e))}}});function startValidateRule(e){return{name:"VideoMixerOptions",type:"object",required:!0,allowEmpty:!1,properties:{view:__spreadValues({},viewValidateRule(e)),canvasInfo:__spreadValues({},canvasValidateRule(e,!0)),camera:__spreadValues({},cameraValidateRule(e)),screen:__spreadValues({},screenValidateRule(e)),text:__spreadValues({},textValidateRule(e)),image:__spreadValues({},imageValidateRule(e)),video:__spreadValues({},videoValidateRule(e))},validate(r,t,a,n){const{RtcError:s,ErrorCode:i,ErrorCodeDictionary:o}=e.errorModule;if(e.environment.isMobile())throw new s({code:i.ENV_NOT_SUPPORTED,message:"VideoMixer is not supported on mobile devices currently"});const{onScreenShareStop:c}=r;if(c&&!e.utils.isFunction(c))throw new s({code:i.INVALID_PARAMETER,extraCode:o.INVALID_PARAMETER_TYPE,fnName:a,messageParams:{key:"onScreenShareStop",value:typeof c,rule:{type:"Function"}}})}}}function updateValidateRule(e){return{name:"VideoMixerOptions",type:"object",required:!1,allowEmpty:!1,properties:{view:__spreadValues({},viewValidateRule(e)),canvasInfo:__spreadValues({},canvasValidateRule(e)),camera:__spreadValues({},cameraValidateRule(e)),screen:__spreadValues({},screenValidateRule(e)),text:__spreadValues({},textValidateRule(e)),image:__spreadValues({},imageValidateRule(e)),video:__spreadValues({},videoValidateRule(e))}}}function stopValidateRule(e){return{name:"StopVideoMixerOptions",required:!1}}var mix_render_worker_default='// 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 const frames = data.frames;\n for (const frameData of frames) {\n const prev = latestFrames.get(frameData.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(frameData.id, frameData);\n }\n break;\n }\n case "updateLayout": {\n layouts = data.layouts.sort((a, b) => a.zIndex - b.zIndex);\n const layoutIds = new Set(layouts.map((l) => l.id));\n for (const key of drawParamsCache.keys()) {\n if (!layoutIds.has(key)) {\n drawParamsCache.delete(key);\n }\n }\n break;\n }\n case "setFps": {\n fps = data.fps;\n if (renderIntervalId !== null) {\n startRenderLoop();\n }\n break;\n }\n case "setBackground": {\n backgroundColor = data.color || "black";\n backgroundImage = null;\n break;\n }\n case "setBackgroundImage": {\n if (backgroundImage) {\n backgroundImage.close();\n }\n backgroundImage = data.bitmap || null;\n break;\n }\n case "resize": {\n if (canvas) {\n canvas.width = data.width;\n canvas.height = data.height;\n }\n break;\n }\n case "start": {\n fps = data.fps || fps;\n startRenderLoop();\n break;\n }\n case "stop": {\n stopRenderLoop();\n break;\n }\n case "closeWriter": {\n stopRenderLoop();\n closeTrackGeneratorWriter();\n break;\n }\n case "addReadableStream": {\n const { id, readable } = data;\n workerLog("debug", `[Worker] received addReadableStream message: id=${id}, hasReadable=${!!readable}`);\n startReadableStreamPipeline(id, readable);\n break;\n }\n case "removeReadableStream": {\n workerLog("debug", `[Worker] received removeReadableStream message: id=${data.id}`);\n removeReadableStreamPipeline(data.id);\n break;\n }\n case "addImageSource": {\n const { id, src, animated, mimeType } = data;\n handleAddImageSource(id, src, animated, mimeType);\n break;\n }\n case "updateImageSource": {\n const { id, src, animated, mimeType } = data;\n handleUpdateImageSource(id, src, animated, mimeType);\n break;\n }\n case "removeImageSource": {\n handleRemoveImageSource(data.id);\n break;\n }\n // ===== Bitmap source protocol (plugin-pre-rendered bitmaps) =====\n // 与 addImageSource(URL) 并存:URL 路径由 Worker 内部 fetch + 支持动图;\n // bitmap 路径用于承载插件主线程已绘制完成的静态 bitmap(如 text 源)。\n case "addBitmapSource":\n case "updateBitmapSource": {\n const { id, bitmap } = data;\n if (!bitmap) break;\n const prev = latestFrames.get(id);\n if (prev && prev.frame instanceof ImageBitmap && prev.frame !== bitmap) {\n try {\n prev.frame.close();\n } catch (e) {\n }\n }\n latestFrames.set(id, {\n id,\n frame: bitmap,\n width: bitmap.width,\n height: bitmap.height\n });\n break;\n }\n case "removeBitmapSource": {\n const { id } = data;\n const prev = latestFrames.get(id);\n if (prev && prev.frame instanceof ImageBitmap) {\n try {\n prev.frame.close();\n } catch (e) {\n }\n }\n latestFrames.delete(id);\n break;\n }\n case "destroy": {\n stopRenderLoop();\n stopHealthLog();\n closeTrackGeneratorWriter();\n abortAllStreams();\n imageSources.forEach((_, id) => handleRemoveImageSource(id));\n imageSources.clear();\n closeFrames();\n drawParamsCache.clear();\n throttleLogTimestamps.clear();\n if (backgroundImage) {\n backgroundImage.close();\n backgroundImage = null;\n }\n ctx = null;\n canvas = null;\n layouts = [];\n self.postMessage({ type: "destroyed" });\n self.close();\n break;\n }\n default:\n break;\n }\n};\n';function isWorkerRendererSupported(){return"undefined"!=typeof OffscreenCanvas&&"undefined"!=typeof MediaStreamTrackGenerator&&"undefined"!=typeof MediaStreamTrackProcessor&&"undefined"!=typeof Worker}async function renderTextToBitmap(e,r,t,a){if(!r||!t)return null;const n=new OffscreenCanvas(r,t),s=n.getContext("2d");if(!s)return null;s.textBaseline="top";const i=(null==a?void 0:a.font)||"16px sans-serif",o=(null==a?void 0:a.color)||"white";s.font=i,s.fillStyle=o;const c=/(\d+(?:\.\d+)?)px/.exec(i),d=1.2*(c?parseFloat(c[1]):16),l=e.split("\n"),u=s.measureText(e),p=u.fontBoundingBoxAscent||u.actualBoundingBoxAscent||0;for(let e=0;e<l.length;e++)s.fillText(l[e],0,p+e*d);return n.transferToImageBitmap()}function createConsoleLogger(){const e=e=>r=>{try{e(r)}catch(e){}};return{info:e(e=>console.info(e)),warn:e(e=>console.warn(e)),error:e(e=>console.error(e)),debug:e(e=>console.debug?console.debug(e):console.log(e))}}var WorkerMixRenderer=class{constructor(e){var r;__publicField(this,"_workerScript"),__publicField(this,"_logger"),__publicField(this,"_worker",null),__publicField(this,"_workerUrl",null),__publicField(this,"_generator",null),__publicField(this,"_outputTrack",null),__publicField(this,"_width",0),__publicField(this,"_height",0),__publicField(this,"_fps",15),__publicField(this,"_sources",new Map),__publicField(this,"_listeners",{unrecoverable:new Set}),__publicField(this,"_destroyed",!1),__publicField(this,"_workerFatal",!1),__publicField(this,"_onWorkerMessage",e=>{if(this._destroyed)return;const r=e.data;if(r&&"object"==typeof r)switch(r.type){case"streamError":case"imageSourceError":this._logger.warn(`Worker imageSource error: id=${r.id}, ${r.message}`);break;case"imageSourceLoaded":this._logger.debug(`Worker imageSource loaded: id=${r.id}, ${r.width}x${r.height}`);break;case"log":this._forwardWorkerLog(r.level,r.message)}}),__publicField(this,"_onWorkerError",e=>{if(this._destroyed||this._workerFatal)return;this._workerFatal=!0;const r=e,t=[];r.message&&t.push(`message=${r.message}`),r.filename&&t.push(`filename=${r.filename}`),"number"==typeof r.lineno&&t.push(`lineno=${r.lineno}`),"number"==typeof r.colno&&t.push(`colno=${r.colno}`),e.type&&t.push(`eventType=${e.type}`);const a=t.length>0?t.join(", "):"worker error";try{this._logger.error(`unrecoverable: ${a}`)}catch(e){}this._emit("unrecoverable",a)}),this._workerScript=e.workerScript,this._logger=null!=(r=e.logger)?r:createConsoleLogger()}async init(e){var r;if(!isWorkerRendererSupported())throw new Error("WorkerMixRenderer: environment does not support OffscreenCanvas + MediaStreamTrackGenerator + Worker");if(this._worker)throw new Error("WorkerMixRenderer: already initialized");this._width=e.width,this._height=e.height,this._fps=null!=(r=e.fps)?r: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);const t=new OffscreenCanvas(this._width||640,this._height||480);this._generator=new MediaStreamTrackGenerator({kind:"video"}),this._outputTrack=this._generator;const a=this._generator.writable,n=this._waitWorkerReady();this._worker.postMessage({type:"init",canvas:t,writable:a,width:this._width,height:this._height},[t,a]),this._worker.postMessage({type:"start",fps:this._fps}),e.backgroundColor&&this._worker.postMessage({type:"setBackground",color:e.backgroundColor}),await n}getOutputTrack(){if(!this._outputTrack)throw new Error("WorkerMixRenderer: getOutputTrack called before init");return this._outputTrack}setFps(e){!this._destroyed&&this._worker&&(this._fps=e,this._worker.postMessage({type:"setFps",fps:e}))}async destroy(){var e,r,t,a,n,s;if(!this._destroyed){this._destroyed=!0;try{null==(e=this._worker)||e.postMessage({type:"destroy"})}catch(e){}try{null==(r=this._generator)||r.stop()}catch(e){}this._generator=null,this._outputTrack=null;for(const e of this._sources.values())if(e.bitmap&&"close"in e.bitmap)try{e.bitmap.close()}catch(e){}this._sources.clear(),await new Promise(e=>setTimeout(e,0));try{null==(t=this._worker)||t.removeEventListener("message",this._onWorkerMessage),null==(a=this._worker)||a.removeEventListener("error",this._onWorkerError),null==(n=this._worker)||n.removeEventListener("messageerror",this._onWorkerError),null==(s=this._worker)||s.terminate()}catch(e){}if(this._worker=null,this._workerUrl){try{URL.revokeObjectURL(this._workerUrl)}catch(e){}this._workerUrl=null}this._listeners.unrecoverable.clear()}}addSource(e,r,t,a){if(this._destroyed||!this._worker)return;this._sources.has(e)&&this.removeSource(e);const n={kind:t,layout:__spreadProps(__spreadValues({},a),{id:e})};if("track"===t){const t=r;try{const r=new MediaStreamTrackProcessor({track:t});n.processor=r;const{readable:a}=r;this._worker.postMessage({type:"addReadableStream",id:e,readable:a},[a])}catch(r){return void this._logger.warn(`addSource(track) failed for id=${e}: ${(null==r?void 0:r.message)||r}`)}}else if("image"===t)if(r instanceof ImageBitmap){const t=r;n.bitmap=t,this._worker.postMessage({type:"addBitmapSource",id:e,bitmap:t},[t])}else{const t=r;n.isUrlImage=!0,this._worker.postMessage({type:"addImageSource",id:e,src:t.src,animated:!!t.animated,mimeType:t.mimeType})}else if("text"===t){const t=r;n.text={content:t,width:a.width,height:a.height,style:{}},this._renderTextAndSend(e,t,a.width,a.height,{},!1)}this._sources.set(e,n),this._sendLayoutSnapshot()}updateSource(e,r){var t,a,n;if(this._destroyed||!this._worker)return;const s=this._sources.get(e);if(s)if("track"===s.kind){try{null==(t=s.processor)||t.readable.cancel()}catch(e){}s.processor=null,this._worker.postMessage({type:"removeReadableStream",id:e});try{const t=new MediaStreamTrackProcessor({track:r});s.processor=t;const{readable:a}=t;this._worker.postMessage({type:"addReadableStream",id:e,readable:a},[a])}catch(r){this._logger.warn(`updateSource(track) failed for id=${e}: ${(null==r?void 0:r.message)||r}`)}}else if("image"===s.kind)if(r instanceof ImageBitmap){s.isUrlImage&&(this._worker.postMessage({type:"removeImageSource",id:e}),s.isUrlImage=!1);const t=r;s.bitmap=t,this._worker.postMessage({type:"updateBitmapSource",id:e,bitmap:t},[t])}else{const t=r;if(!s.isUrlImage&&(this._worker.postMessage({type:"removeBitmapSource",id:e}),s.bitmap)){try{s.bitmap.close()}catch(e){}s.bitmap=null}s.isUrlImage=!0,this._worker.postMessage({type:"updateImageSource",id:e,src:t.src,animated:!!t.animated,mimeType:t.mimeType})}else if("text"===s.kind){const t=r,i=null!=(n=null==(a=s.text)?void 0:a.style)?n:{};s.text={content:t,width:s.layout.width,height:s.layout.height,style:i},this._renderTextAndSend(e,t,s.layout.width,s.layout.height,i,!0)}}addTextSource(e,r,t,a){if(this._destroyed||!this._worker)return;this._sources.has(e)&&this.removeSource(e);const n={kind:"text",layout:__spreadProps(__spreadValues({},a),{id:e}),text:{content:r,width:a.width,height:a.height,style:__spreadValues({},t)}};this._sources.set(e,n),this._renderTextAndSend(e,r,a.width,a.height,__spreadValues({},t),!1),this._sendLayoutSnapshot()}updateTextSource(e,r,t){var a,n;if(this._destroyed||!this._worker)return;const s=this._sources.get(e);if(!s||"text"!==s.kind)return;const i=__spreadValues(__spreadValues({},null!=(n=null==(a=s.text)?void 0:a.style)?n:{}),t);s.text={content:r,width:s.layout.width,height:s.layout.height,style:i},this._renderTextAndSend(e,r,s.layout.width,s.layout.height,i,!0)}removeSource(e){var r;if(this._destroyed||!this._worker)return;const 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()}}updateLayout(e){if(!this._destroyed&&this._worker){for(const r of e){const e=this._sources.get(r.id);if(!e)continue;const t=e.layout;e.layout=__spreadValues({},r),"text"!==e.kind||!e.text||t.width===r.width&&t.height===r.height||(e.text.width=r.width,e.text.height=r.height,this._renderTextAndSend(r.id,e.text.content,r.width,r.height,e.text.style,!0))}this._sendLayoutSnapshot()}}setBackground(e){!this._destroyed&&this._worker&&this._worker.postMessage({type:"setBackground",color:e})}setBackgroundImage(e){!this._destroyed&&this._worker&&(e?this._worker.postMessage({type:"setBackgroundImage",bitmap:e},[e]):this._worker.postMessage({type:"setBackground",color:"black"}))}resize(e,r){!this._destroyed&&this._worker&&(this._width=e,this._height=r,this._worker.postMessage({type:"resize",width:e,height:r}))}on(e,r){this._listeners[e].add(r)}off(e,r){this._listeners[e].delete(r)}_emit(e,r){for(const t of this._listeners[e])try{t(r)}catch(e){}}_sendLayoutSnapshot(){if(!this._worker)return;const e=[];for(const r of this._sources.values())e.push(__spreadValues({},r.layout));this._worker.postMessage({type:"updateLayout",layouts:e})}async _renderTextAndSend(e,r,t,a,n,s){if(!this._destroyed&&this._worker)try{const i=await renderTextToBitmap(r,t,a,n);if(!i)return;if(this._destroyed||!this._worker){try{i.close()}catch(e){}return}const o=this._sources.get(e);if(!o){try{i.close()}catch(e){}return}o.bitmap=null,this._worker.postMessage({type:s?"updateBitmapSource":"addBitmapSource",id:e,bitmap:i},[i])}catch(r){this._logger.warn(`renderText failed for id=${e}: ${(null==r?void 0:r.message)||r}`)}}_waitWorkerReady(){return new Promise((e,r)=>{if(!this._worker)return r(new Error("worker not created"));const t=this._worker,a=s=>{var i,o;"ready"===(null==(i=s.data)?void 0:i.type)?(t.removeEventListener("message",a),t.removeEventListener("error",n),e()):"error"===(null==(o=s.data)?void 0:o.type)&&(t.removeEventListener("message",a),t.removeEventListener("error",n),r(new Error(`worker init error: ${s.data.message}`)))},n=e=>{t.removeEventListener("message",a),t.removeEventListener("error",n),r(new Error(`worker error: ${e.message}`))};t.addEventListener("message",a),t.addEventListener("error",n)})}_forwardWorkerLog(e,r){const t=`${"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){}}},videoMixSeq=0,_VideoMixer=class e{constructor(e){this.core=e,__publicField(this,"seq"),__publicField(this,"log"),__publicField(this,"localMixVideoTrack",null),__publicField(this,"systemAudioTrackList",{}),__publicField(this,"_mixVideoConfig"),__publicField(this,"onScreenShareStop"),__publicField(this,"eventListeners",new Map),videoMixSeq+=1,this.seq=videoMixSeq,this.log=e.log.createChild({id:`${this.getAlias()}${videoMixSeq}`}),this.log.info("created")}getName(){return e.Name}getAlias(){return"vmix"}getValidateRule(e){switch(e){case"start":return startValidateRule(this.core);case"update":return updateValidateRule(this.core);case"stop":return stopValidateRule(this.core)}}getGroup(){return"vmix"}async start(e){this.localMixVideoTrack||(this.localMixVideoTrack=new this.core.LocalMixVideoTrack(this.core.room.videoManager)),this._mixVideoConfig={canvasInfo:{width:1920,height:1080}},e=this.core.utils.deepCloneBasic(e);const{view:r,onScreenShareStop:t}=e,{syncResult:a,asyncSourcesInfo:n}=await this.parseMixOptionsSyncPhase(e);t&&(this.onScreenShareStop=t,this._mixVideoConfig.onScreenShareStop=t),this._updatePreview({view:r,track:this.localMixVideoTrack}),this.core.utils.isUndefined(r)||(this._mixVideoConfig.view=r),await this.localMixVideoTrack.startMix({enableWorkerRenderer:!0}),await this._tryAttachWorkerRenderer();const s=await this.parseMixOptionsAsyncPhase(n),i=__spreadValues(__spreadValues({},a.successOptions),s.successOptions),o=[...a.failedDetails,...s.failedDetails];a.successOptions.screen&&s.successOptions.screen&&(i.screen=[...a.successOptions.screen,...s.successOptions.screen]);const c=a.sourceCount+s.sourceCount,d=a.allFailCount+s.allFailCount;if(d>0&&d===c){const{RtcError:e,ErrorCode:r}=this.core.errorModule;throw new e({code:r.INVALID_PARAMETER,message:"all sources mix failed",data:{failedDetails:o}})}return{track:this.localMixVideoTrack.outMediaTrack,systemAudioTrackList:this.systemAudioTrackList,result:{successOptions:i,failedDetails:o}}}async _tryAttachWorkerRenderer(){if(!this.localMixVideoTrack||!this._mixVideoConfig)return;if(!isWorkerRendererSupported())return void this._setFallbackOutputTrack();const e=new WorkerMixRenderer({workerScript:mix_render_worker_default,logger:this.log});try{await this.localMixVideoTrack.attachRenderer(e)}catch(r){try{await e.destroy()}catch(e){}const t=(null==r?void 0:r.message)||String(r);this.log.info(`worker renderer attach failed, fallback to main-thread render: ${t}`),this._setFallbackOutputTrack()}}_setFallbackOutputTrack(){var e;if(!this.localMixVideoTrack)return;const r=null==(e=this.localMixVideoTrack.destination)?void 0:e.videoTrack;r&&this.localMixVideoTrack.setOutputMediaStreamTrack(r)}async update(e){const{RtcError:r,ErrorCode:t}=this.core.errorModule;if(!this.localMixVideoTrack)throw new r({code:t.INVALID_OPERATION,message:"mixTrack doesn't initialize!"});e=this.core.utils.deepCloneBasic(e);const{view:a}=e,n=await this.parseMixOptions(e);return await this._updatePreview({view:a,track:this.localMixVideoTrack,prevConfig:this._mixVideoConfig}),this.core.utils.isUndefined(a)||(this._mixVideoConfig.view=a),{track:this.localMixVideoTrack.outMediaTrack,systemAudioTrackList:this.systemAudioTrackList,result:n}}stop(){var e;this.eventListeners.forEach((e,r)=>{this.removeEventListeners(r)}),this.eventListeners.clear(),null==(e=this.localMixVideoTrack)||e.close(),this.localMixVideoTrack=null,Object.values(this.systemAudioTrackList).forEach(e=>e.stop()),this.systemAudioTrackList={},delete this.onScreenShareStop,delete this._mixVideoConfig,void 0!==this.core.room.isUsingVideoMixerInternalTrack&&(this.core.room.isUsingVideoMixerInternalTrack=!1)}isBlockingScreenSource(e){return!0!==e.useInternalTrack&&!!this.core.utils.isUndefined(e.videoTrack)}async parseMixOptionsSyncPhase(e){const r={successOptions:{},failedDetails:[],sourceCount:0,allFailCount:0};if(!this.localMixVideoTrack||!this._mixVideoConfig)return{syncResult:r,asyncSourcesInfo:{blockingScreenSources:[],imageSources:void 0,videoSources:void 0}};cons