trtc-sdk-v5 5.18.0-beta.5 → 5.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/audio-player/package.json +1 -1
- package/plugins/cdn-streaming/package.json +1 -1
- package/plugins/chorus/package.json +1 -1
- package/plugins/cross-room/package.json +1 -1
- package/plugins/custom-encryption/package.json +1 -1
- package/plugins/device-detector/package.json +1 -1
- package/plugins/lebplayer/package.json +1 -1
- package/plugins/realtime-transcriber/package.json +1 -1
- package/plugins/small-stream-auto-switcher/package.json +1 -1
- package/plugins/video-decoder/package.json +1 -1
- package/plugins/video-effect/basic-beauty/package.json +1 -1
- package/plugins/video-effect/beauty/package.json +1 -1
- package/plugins/video-effect/face-detection/package.json +1 -1
- package/plugins/video-effect/video-mixer/package.json +1 -1
- package/plugins/video-effect/video-mixer/video-mixer.esm.js +1 -1
- package/plugins/video-effect/video-mixer/video-mixer.umd.js +1 -1
- package/plugins/video-effect/virtual-background/package.json +1 -1
- package/plugins/video-effect/watermark/package.json +1 -1
- package/plugins/voice-changer/package.json +1 -1
- package/trtc.esm.js +29 -29
- package/trtc.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtc-plugin/audio-player",
|
|
3
|
-
"version": "5.18.0
|
|
3
|
+
"version": "5.18.0",
|
|
4
4
|
"description": "TRTC Web SDK AudioPlayer plugin - unified audio player supporting PCM, URL, and Track sources",
|
|
5
5
|
"main": "./audio-player.esm.js",
|
|
6
6
|
"module": "./audio-player.esm.js",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
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;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}`)}}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}};const{canvasInfo:t,camera:a,screen:n,text:s,image:i,video:o}=e;let c=!1;a&&(c=a.some(e=>!0===e.useInternalTrack)),!c&&n&&(c=n.some(e=>!0===e.useInternalTrack)),void 0!==this.core.room.isUsingVideoMixerInternalTrack&&(this.core.room.isUsingVideoMixerInternalTrack=c,c&&this.log.debug("isUsingVideoMixerInternalTrack",c)),t&&this.parseCanvasOptions(t);let d=[],l=[];if(n)for(const e of n)this.isBlockingScreenSource(e)?l.push(e):d.push(e);const u=[{key:"camera",options:a,parser:this.parseCameraOptions.bind(this)},{key:"screen",options:d.length>0?d:void 0,parser:this.parseScreenOptions.bind(this)},{key:"text",options:s,parser:this.parseTextOptions.bind(this)}],p=__spreadValues({},e);for(const{key:e,options:t,parser:a}of u)if(t){r.sourceCount++;const n=await a(this.localMixVideoTrack,t,this._mixVideoConfig[e]||[]);this._mixVideoConfig[e]=n.finalOptions,p[e]=n.finalOptions,n.errors.length>0&&(r.failedDetails.push(...n.errors),n.errors.length===t.length&&r.allFailCount++)}return r.successOptions=p,{syncResult:r,asyncSourcesInfo:{blockingScreenSources:l,imageSources:i,videoSources:o}}}async parseMixOptionsAsyncPhase(e){const r={successOptions:{},failedDetails:[],sourceCount:0,allFailCount:0};if(!this.localMixVideoTrack||!this._mixVideoConfig)return r;const{blockingScreenSources:t,imageSources:a,videoSources:n}=e,s=[{key:"screen",options:t.length>0?t:void 0,parser:this.parseScreenOptions.bind(this)},{key:"image",options:a,parser:this.parseImageOptions.bind(this)},{key:"video",options:n,parser:this.parseVideoOptions.bind(this)}];for(const{key:e,options:t,parser:a}of s)if(t){r.sourceCount++;const n=await a(this.localMixVideoTrack,t,this._mixVideoConfig[e]||[]);if("screen"===e){const r=this._mixVideoConfig[e]||[];this._mixVideoConfig[e]=[...r,...n.finalOptions]}else this._mixVideoConfig[e]=n.finalOptions;r.successOptions[e]=n.finalOptions,n.errors.length>0&&(r.failedDetails.push(...n.errors),n.errors.length===t.length&&r.allFailCount++)}return r}async parseMixOptions(e){const{RtcError:r,ErrorCode:t}=this.core.errorModule;if(!this.localMixVideoTrack||!this._mixVideoConfig)return{successOptions:{},failedDetails:[]};const a=[],n=__spreadValues({},e),{canvasInfo:s,camera:i,screen:o,text:c,image:d,video:l}=e;let u=!1;i&&(u=i.some(e=>!0===e.useInternalTrack)),!u&&o&&(u=o.some(e=>!0===e.useInternalTrack)),void 0!==this.core.room.isUsingVideoMixerInternalTrack&&(this.core.room.isUsingVideoMixerInternalTrack=u,u&&this.log.debug("isUsingVideoMixerInternalTrack",u)),s&&this.parseCanvasOptions(s);let p=0,m=0;const h=[{key:"camera",options:i,parser:this.parseCameraOptions.bind(this)},{key:"screen",options:o,parser:this.parseScreenOptions.bind(this)},{key:"text",options:c,parser:this.parseTextOptions.bind(this)},{key:"image",options:d,parser:this.parseImageOptions.bind(this)},{key:"video",options:l,parser:this.parseVideoOptions.bind(this)}];for(const{key:e,options:r,parser:t}of h)if(r){p++;const s=await t(this.localMixVideoTrack,r,this._mixVideoConfig[e]||[]);this._mixVideoConfig[e]=s.finalOptions,n[e]=s.finalOptions,s.errors.length>0&&(a.push(...s.errors),s.errors.length===r.length&&m++)}if(m>0&&m===p)throw new r({code:t.INVALID_PARAMETER,message:"all sources mix failed",data:{failedDetails:a}});return{successOptions:n,failedDetails:a}}parseCanvasOptions(e){if(!this.localMixVideoTrack||!this._mixVideoConfig)return;const{canvasColor:r,width:t,height:a,frameRate:n}=e;r&&this.localMixVideoTrack.setMixBackground(r),n&&this.localMixVideoTrack.setFps(n),this.localMixVideoTrack.resizeMixCanvas(t,a),this._mixVideoConfig.canvasInfo=e}prepareSourceOptions(e,r){const t=new Set(e.map(e=>e.id));return{removeIdList:r.filter(e=>!t.has(e.id)).map(e=>e.id),preOptionsMap:new Map(r.map(e=>[e.id,e]))}}recordSourceError(e,r,t,a,n){n.push({id:e,error:r}),t.has(e)&&a.push(t.get(e))}async parseCameraOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeCameraSource(r),this.removeEventListeners(r);const s=[],i=[];for(const t of r)try{await this.processSingleCameraSource(e,t),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleCameraSource(e,r){const{id:t}=r;this.resolveCameraInternalTrack(e,r),e.inputLocalVideoTracks.has(t)?await this.updateExistingCameraSource(e,r):await this.addNewCameraSource(e,r)}async updateExistingCameraSource(e,r){var t,a;const{id:n,layout:s,profile:i}=r,o=null==(t=e.inputLocalVideoTracks.get(n))?void 0:t.mediaTrack;await this.updateCameraProfile(r);const c=null==(a=e.inputLocalVideoTracks.get(n))?void 0:a.mediaTrack,d=this.resolveVideoProfile(i);c!==o?e.updateCameraSource(n,s,c,d):e.updateCameraSource(n,s,null,d)}resolveCameraInternalTrack(e,r){var t;const{id:a,layout:n,profile:s,useInternalTrack:i}=r;if(i){if(e.inputLocalVideoTracks.get(a))return r;this.log.debug("resolve camera internal track",r),(null==(t=this.core.trtc.localVideoTrack)?void 0:t.sourceTrack)?(this.log.debug("resolve camera internal track outMediaTrack:",this.core.trtc.localVideoTrack.outMediaTrack,"sourceTrack:",this.core.trtc.localVideoTrack.sourceTrack),r.videoTrack=this.core.trtc.localVideoTrack.outMediaTrack,r.profile=this.core.trtc.localVideoTrack.profile):r.videoTrack=this.createPlaceholderVideoTrack(),this.removeEventListeners(a);const s=r=>{var t,s;const i=e.inputLocalVideoTracks.get(a);this.log.debug(`camera internal track preprocessed event from ${null==(t=r.room)?void 0:t.userId} to ${this.core.room.userId}, is same instance:${r.room===this.core.room} ,new track:`,r.mediaTrack,null==i?void 0:i.mediaTrack,e.outMediaTrack),r.room===this.core.room&&i&&r.mediaTrack&&(null==(s=r.mediaTrack)?void 0:s.kind)===this.core.constants.NAME.VIDEO&&r.mediaTrack!==(null==i?void 0:i.mediaTrack)&&r.mediaTrack!==e.outMediaTrack&&e.updateCameraSource(a,n,r.mediaTrack)},i=r=>{var t,s,i,o,c,d,l,u;const p=e.inputLocalVideoTracks.get(a);this.log.debug(`camera internal track stopped ${(null==(t=r.track)?void 0:t.mediaTrack)===(null==p?void 0:p.mediaTrack)||(null==(s=r.track)?void 0:s.outMediaTrack)===(null==p?void 0:p.mediaTrack)||(null==(i=r.track)?void 0:i.outMediaTrack)===e.outMediaTrack}`,null==(o=r.track)?void 0:o.mediaTrack,null==(c=r.track)?void 0:c.outMediaTrack,null==p?void 0:p.mediaTrack,e.outMediaTrack),!p||(null==(d=r.track)?void 0:d.mediaTrack)!==(null==p?void 0:p.mediaTrack)&&(null==(l=r.track)?void 0:l.outMediaTrack)!==(null==p?void 0:p.mediaTrack)&&(null==(u=r.track)?void 0:u.outMediaTrack)!==e.outMediaTrack||e.updateCameraSource(a,n,this.createPlaceholderVideoTrack())};this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_VIDEO_TRACK_PREPROCESSED,s),this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,i),this.eventListeners.has(a)||this.eventListeners.set(a,{}),this.eventListeners.get(a).captureSuccess=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_VIDEO_TRACK_PREPROCESSED,s)},this.eventListeners.get(a).trackStop=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,i)}}return r}async addNewCameraSource(e,r){const{id:t,layout:a,useInternalTrack:n}=r,s=await this.captureCamera(r);try{e.addCameraSource(t,s,a)}catch(e){throw s.close(),e}}resolveVideoProfile(e){if(!this.core.utils.isUndefined(e))return this.core.utils.isString(e)?this.core.constants.videoProfileMap[e]:e}async parseScreenOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeScreenSource(r),this.removeSystemAudioTrack(r),this.removeEventListeners(r);const s=[],i=[];for(const t of r)try{await this.processSingleScreenSource(e,t,n),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleScreenSource(e,r,t){const{id:a,layout:n,useInternalTrack:s}=r;this.resolveScreenInternalTrack(e,r);const i=t.get(a),o=e.inputLocalScreenTracks.has(a),c=!(null==i?void 0:i.systemAudio)&&r.systemAudio;o&&!c?this.updateExistingScreenSource(e,a,n,i,r):await this.addNewScreenSource(e,r,i)}updateExistingScreenSource(e,r,t,a,n){e.updateScreenSource(r,t),(null==a?void 0:a.systemAudio)&&!n.systemAudio&&this.removeSystemAudioTrack(r)}resolveScreenInternalTrack(e,r){var t,a;const{id:n,layout:s,useInternalTrack:i}=r;if(i){if(e.inputLocalScreenTracks.get(n))return r;this.log.debug("resolve screen internal track",r,r.id,r.videoTrack),(null==(t=this.core.trtc.localScreenTrack)?void 0:t.sourceTrack)?(r.videoTrack=this.core.trtc.localScreenTrack.sourceTrack,r.profile=this.core.trtc.localScreenTrack.profile,(null==(a=this.core.trtc.localScreenAudioTrack)?void 0:a.mediaTrack)&&(r.audioTrack=this.core.trtc.localScreenAudioTrack.mediaTrack),delete r.captureElement,delete r.preferDisplaySurface,delete r.systemAudio):r.videoTrack=this.createPlaceholderVideoTrack(),this.removeEventListeners(n);const i=r=>{var t,a,i;const o=e.inputLocalScreenTracks.get(n);this.log.debug(`screen internal track capture success event, is same instance:${r.room===this.core.room}, isScreen:${null==(t=r.track)?void 0:t.isScreen} kind:${null==(a=r.track)?void 0:a.kind}`,o,r.track.sourceTrack),r.track.isScreen&&(null==(i=r.track)?void 0:i.kind)===this.core.constants.NAME.VIDEO&&r.room===this.core.room&&r.track.sourceTrack&&o&&e.updateScreenSource(n,s,r.track.sourceTrack)},o=r=>{var t,a;const i=e.inputLocalScreenTracks.get(n);this.log.debug(`screen internal track stopped, is same track:${(null==(t=r.track)?void 0:t.sourceTrack)===(null==i?void 0:i.mediaTrack)}, isScreen:${r.track.isScreen}`),r.track.isScreen&&(null==(a=r.track)?void 0:a.sourceTrack)===(null==i?void 0:i.mediaTrack)&&e.updateScreenSource(n,s,this.createPlaceholderVideoTrack())};this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_TRACK_CAPTURE_SUCCESS,i),this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,o),this.eventListeners.has(n)||this.eventListeners.set(n,{}),this.eventListeners.get(n).captureSuccess=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_TRACK_CAPTURE_SUCCESS,i)},this.eventListeners.get(n).trackStop=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,o)}}return r}async addNewScreenSource(e,r,t){const{id:a,layout:n}=r,s=await this.captureScreen(r);!(null==t?void 0:t.systemAudio)&&r.systemAudio&&e.inputLocalScreenTracks.has(a)&&e.removeScreenSource(a);try{e.addScreenSource(a,s,n)}catch(e){throw s.close(),e}}async parseTextOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeTextSource(r);const s=[],i=[];for(const t of r)try{n.has(t.id)?e.updateTextSource(t):e.addTextSource(t),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async parseImageOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeImageSource(r);const s=[],i=[];for(const t of r)try{await this.processSingleImageSource(e,t,n),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleImageSource(e,r,t){const{id:a,url:n,layout:s}=r,i=t.get(a);if(i){let r;i.url!==n&&(r=await this.core.utils.loadImage(n)),e.updateImageSource(a,s,r)}else{const r=await this.core.utils.loadImage(n);e.addImageSource(a,r,s)}}async parseVideoOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeVideoSource(r);const s=[],i=[];for(const t of r)try{await this.processSingleVideoSource(e,t,n),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleVideoSource(e,r,t){const{id:a,url:n,layout:s}=r,i=t.get(a);if(i){let r;i.url!==n&&(r=await this.core.utils.loadVideo(n)),e.updateVideoSource(a,s,r)}else{const r=await this.core.utils.loadVideo(n);e.addVideoSource(a,r,s)}}createPlaceholderVideoTrack(){const e=document.createElement("canvas");e.width=1,e.height=1;const r=e.getContext("2d");if(!r){return e.captureStream(30).getVideoTracks()[0]}let t=null;const a=1e3/30,n=e.captureStream(30).getVideoTracks()[0],s=()=>{r.fillStyle="rgba(0, 0, 0, 0)",r.fillRect(0,0,e.width,e.height),"live"===n.readyState&&(t=setTimeout(s,a))};s();const i=n.stop.bind(n);return n.stop=()=>{t&&(clearTimeout(t),t=null),i()},n}removeEventListeners(e){const r=this.eventListeners.get(e);r&&(r.captureSuccess&&r.captureSuccess(),r.trackStop&&r.trackStop(),this.eventListeners.delete(e))}async captureCamera(e){const{id:r,cameraId:t,videoTrack:a,profile:n}=e,s=new this.core.LocalVideoTrack;s.log.id+=`-${r}`;const i={};if(t?i.deviceId=t:this.core.utils.isUndefined(a)||(i.customSource=a),!this.core.utils.isUndefined(n)){const e=this.resolveVideoProfile(n);e&&s.setProfile(e)}return await s.capture(i),s}async updateCameraProfile(e){var r;const{id:t,cameraId:a,videoTrack:n,profile:s}=e,i=null==(r=this.localMixVideoTrack)?void 0:r.inputLocalVideoTracks.get(t);if(i&&(a?await i.switchDevice(a):this.core.utils.isUndefined(n)||await i.setInputMediaStreamTrack(n),!this.core.utils.isUndefined(s))){const e=this.resolveVideoProfile(s);e&&i.setProfile(e),a&&i.isNeedToSwitchDevice(a)||await i.applyProfile()}}async captureScreen(e){const{id:r,profile:t,captureElement:a,preferDisplaySurface:n,systemAudio:s,videoTrack:i,audioTrack:o}=e,c=new this.core.LocalScreenTrack;c.log.id+=`-${r}`;const d={captureElement:a,preferDisplaySurface:n,systemAudio:s,videoTrack:i,audioTrack:o};if(!this.core.utils.isUndefined(t))if(this.core.utils.isString(t)){const e=this.core.constants.screenProfileMap[t];e&&c.setProfile(e)}else c.setProfile(t);const l=await c.capture(d);return s&&l.getAudioTracks().length>0?(this.systemAudioTrackList[r]=l.getAudioTracks()[0],this.log.info(`${r} system audio track captured`)):this.removeSystemAudioTrack(r),c.mediaTrack.addEventListener(this.core.constants.NAME.ENDED,()=>{this.handleScreenShareEnded(r)}),c}handleScreenShareEnded(e){var r,t,a;null==(r=this.localMixVideoTrack)||r.removeScreenSource(e),(null==(t=this._mixVideoConfig)?void 0:t.screen)&&(this._mixVideoConfig.screen=this._mixVideoConfig.screen.filter(r=>r.id!==e)),null==(a=this.onScreenShareStop)||a.call(this,e)}async _updatePreview({view:e,track:r,prevConfig:t}){if(this.core.utils.isUndefined(e)&&(null==t?void 0:t.view)){const e=this.core.utils.getViewListFromView(t.view);return void(e.length>0&&await r.play(e))}if(!this.core.utils.isUndefined(e)){const t=this.core.utils.getViewListFromView(e);t.length>0?await r.play(t):r.stop()}}removeSystemAudioTrack(e){const r=this.systemAudioTrackList[e];r&&(r.stop(),this.log.info(`${e} system audio track stop`),delete this.systemAudioTrackList[e])}};__publicField(_VideoMixer,"Name","VideoMixer");var VideoMixer=_VideoMixer,index_default=VideoMixer;export{index_default as default};export{VideoMixer};
|
|
1
|
+
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}};const{canvasInfo:t,camera:a,screen:n,text:s,image:i,video:o}=e;let c=!1;a&&(c=a.some(e=>!0===e.useInternalTrack)),!c&&n&&(c=n.some(e=>!0===e.useInternalTrack)),void 0!==this.core.room.isUsingVideoMixerInternalTrack&&(this.core.room.isUsingVideoMixerInternalTrack=c,c&&this.log.debug("isUsingVideoMixerInternalTrack",c)),t&&this.parseCanvasOptions(t);let d=[],l=[];if(n)for(const e of n)this.isBlockingScreenSource(e)?l.push(e):d.push(e);const u=[{key:"camera",options:a,parser:this.parseCameraOptions.bind(this)},{key:"screen",options:d.length>0?d:void 0,parser:this.parseScreenOptions.bind(this)},{key:"text",options:s,parser:this.parseTextOptions.bind(this)}],p=__spreadValues({},e);for(const{key:e,options:t,parser:a}of u)if(t){r.sourceCount++;const n=await a(this.localMixVideoTrack,t,this._mixVideoConfig[e]||[]);this._mixVideoConfig[e]=n.finalOptions,p[e]=n.finalOptions,n.errors.length>0&&(r.failedDetails.push(...n.errors),n.errors.length===t.length&&r.allFailCount++)}return r.successOptions=p,{syncResult:r,asyncSourcesInfo:{blockingScreenSources:l,imageSources:i,videoSources:o}}}async parseMixOptionsAsyncPhase(e){const r={successOptions:{},failedDetails:[],sourceCount:0,allFailCount:0};if(!this.localMixVideoTrack||!this._mixVideoConfig)return r;const{blockingScreenSources:t,imageSources:a,videoSources:n}=e,s=[{key:"screen",options:t.length>0?t:void 0,parser:this.parseScreenOptions.bind(this)},{key:"image",options:a,parser:this.parseImageOptions.bind(this)},{key:"video",options:n,parser:this.parseVideoOptions.bind(this)}];for(const{key:e,options:t,parser:a}of s)if(t){r.sourceCount++;const n=await a(this.localMixVideoTrack,t,this._mixVideoConfig[e]||[]);if("screen"===e){const r=this._mixVideoConfig[e]||[];this._mixVideoConfig[e]=[...r,...n.finalOptions]}else this._mixVideoConfig[e]=n.finalOptions;r.successOptions[e]=n.finalOptions,n.errors.length>0&&(r.failedDetails.push(...n.errors),n.errors.length===t.length&&r.allFailCount++)}return r}async parseMixOptions(e){const{RtcError:r,ErrorCode:t}=this.core.errorModule;if(!this.localMixVideoTrack||!this._mixVideoConfig)return{successOptions:{},failedDetails:[]};const a=[],n=__spreadValues({},e),{canvasInfo:s,camera:i,screen:o,text:c,image:d,video:l}=e;let u=!1;i&&(u=i.some(e=>!0===e.useInternalTrack)),!u&&o&&(u=o.some(e=>!0===e.useInternalTrack)),void 0!==this.core.room.isUsingVideoMixerInternalTrack&&(this.core.room.isUsingVideoMixerInternalTrack=u,u&&this.log.debug("isUsingVideoMixerInternalTrack",u)),s&&this.parseCanvasOptions(s);let p=0,m=0;const h=[{key:"camera",options:i,parser:this.parseCameraOptions.bind(this)},{key:"screen",options:o,parser:this.parseScreenOptions.bind(this)},{key:"text",options:c,parser:this.parseTextOptions.bind(this)},{key:"image",options:d,parser:this.parseImageOptions.bind(this)},{key:"video",options:l,parser:this.parseVideoOptions.bind(this)}];for(const{key:e,options:r,parser:t}of h)if(r){p++;const s=await t(this.localMixVideoTrack,r,this._mixVideoConfig[e]||[]);this._mixVideoConfig[e]=s.finalOptions,n[e]=s.finalOptions,s.errors.length>0&&(a.push(...s.errors),s.errors.length===r.length&&m++)}if(m>0&&m===p)throw new r({code:t.INVALID_PARAMETER,message:"all sources mix failed",data:{failedDetails:a}});return{successOptions:n,failedDetails:a}}parseCanvasOptions(e){if(!this.localMixVideoTrack||!this._mixVideoConfig)return;const{canvasColor:r,width:t,height:a,frameRate:n}=e;r&&this.localMixVideoTrack.setMixBackground(r),n&&this.localMixVideoTrack.setFps(n),this.localMixVideoTrack.resizeMixCanvas(t,a),this._mixVideoConfig.canvasInfo=e}prepareSourceOptions(e,r){const t=new Set(e.map(e=>e.id));return{removeIdList:r.filter(e=>!t.has(e.id)).map(e=>e.id),preOptionsMap:new Map(r.map(e=>[e.id,e]))}}recordSourceError(e,r,t,a,n){n.push({id:e,error:r}),t.has(e)&&a.push(t.get(e))}async parseCameraOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeCameraSource(r),this.removeEventListeners(r);const s=[],i=[];for(const t of r)try{await this.processSingleCameraSource(e,t),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleCameraSource(e,r){const{id:t}=r;this.resolveCameraInternalTrack(e,r),e.inputLocalVideoTracks.has(t)?await this.updateExistingCameraSource(e,r):await this.addNewCameraSource(e,r)}async updateExistingCameraSource(e,r){var t,a;const{id:n,layout:s,profile:i}=r,o=null==(t=e.inputLocalVideoTracks.get(n))?void 0:t.mediaTrack;await this.updateCameraProfile(r);const c=null==(a=e.inputLocalVideoTracks.get(n))?void 0:a.mediaTrack,d=this.resolveVideoProfile(i);c!==o?e.updateCameraSource(n,s,c,d):e.updateCameraSource(n,s,null,d)}resolveCameraInternalTrack(e,r){var t;const{id:a,layout:n,profile:s,useInternalTrack:i}=r;if(i){if(e.inputLocalVideoTracks.get(a))return r;this.log.debug("resolve camera internal track",r),(null==(t=this.core.trtc.localVideoTrack)?void 0:t.sourceTrack)?(this.log.debug("resolve camera internal track outMediaTrack:",this.core.trtc.localVideoTrack.outMediaTrack,"sourceTrack:",this.core.trtc.localVideoTrack.sourceTrack),r.videoTrack=this.core.trtc.localVideoTrack.outMediaTrack,r.profile=this.core.trtc.localVideoTrack.profile):r.videoTrack=this.createPlaceholderVideoTrack(),this.removeEventListeners(a);const s=r=>{var t,s;const i=e.inputLocalVideoTracks.get(a);this.log.debug(`camera internal track preprocessed event from ${null==(t=r.room)?void 0:t.userId} to ${this.core.room.userId}, is same instance:${r.room===this.core.room} ,new track:`,r.mediaTrack,null==i?void 0:i.mediaTrack,e.outMediaTrack),r.room===this.core.room&&i&&r.mediaTrack&&(null==(s=r.mediaTrack)?void 0:s.kind)===this.core.constants.NAME.VIDEO&&r.mediaTrack!==(null==i?void 0:i.mediaTrack)&&r.mediaTrack!==e.outMediaTrack&&e.updateCameraSource(a,n,r.mediaTrack)},i=r=>{var t,s,i,o,c,d,l,u;const p=e.inputLocalVideoTracks.get(a);this.log.debug(`camera internal track stopped ${(null==(t=r.track)?void 0:t.mediaTrack)===(null==p?void 0:p.mediaTrack)||(null==(s=r.track)?void 0:s.outMediaTrack)===(null==p?void 0:p.mediaTrack)||(null==(i=r.track)?void 0:i.outMediaTrack)===e.outMediaTrack}`,null==(o=r.track)?void 0:o.mediaTrack,null==(c=r.track)?void 0:c.outMediaTrack,null==p?void 0:p.mediaTrack,e.outMediaTrack),!p||(null==(d=r.track)?void 0:d.mediaTrack)!==(null==p?void 0:p.mediaTrack)&&(null==(l=r.track)?void 0:l.outMediaTrack)!==(null==p?void 0:p.mediaTrack)&&(null==(u=r.track)?void 0:u.outMediaTrack)!==e.outMediaTrack||e.updateCameraSource(a,n,this.createPlaceholderVideoTrack())};this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_VIDEO_TRACK_PREPROCESSED,s),this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,i),this.eventListeners.has(a)||this.eventListeners.set(a,{}),this.eventListeners.get(a).captureSuccess=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_VIDEO_TRACK_PREPROCESSED,s)},this.eventListeners.get(a).trackStop=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,i)}}return r}async addNewCameraSource(e,r){const{id:t,layout:a,useInternalTrack:n}=r,s=await this.captureCamera(r);try{e.addCameraSource(t,s,a)}catch(e){throw s.close(),e}}resolveVideoProfile(e){if(!this.core.utils.isUndefined(e))return this.core.utils.isString(e)?this.core.constants.videoProfileMap[e]:e}async parseScreenOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeScreenSource(r),this.removeSystemAudioTrack(r),this.removeEventListeners(r);const s=[],i=[];for(const t of r)try{await this.processSingleScreenSource(e,t,n),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleScreenSource(e,r,t){const{id:a,layout:n,useInternalTrack:s}=r;this.resolveScreenInternalTrack(e,r);const i=t.get(a),o=e.inputLocalScreenTracks.has(a),c=!(null==i?void 0:i.systemAudio)&&r.systemAudio;o&&!c?this.updateExistingScreenSource(e,a,n,i,r):await this.addNewScreenSource(e,r,i)}updateExistingScreenSource(e,r,t,a,n){e.updateScreenSource(r,t),(null==a?void 0:a.systemAudio)&&!n.systemAudio&&this.removeSystemAudioTrack(r)}resolveScreenInternalTrack(e,r){var t,a;const{id:n,layout:s,useInternalTrack:i}=r;if(i){if(e.inputLocalScreenTracks.get(n))return r;this.log.debug("resolve screen internal track",r,r.id,r.videoTrack),(null==(t=this.core.trtc.localScreenTrack)?void 0:t.sourceTrack)?(r.videoTrack=this.core.trtc.localScreenTrack.sourceTrack,r.profile=this.core.trtc.localScreenTrack.profile,(null==(a=this.core.trtc.localScreenAudioTrack)?void 0:a.mediaTrack)&&(r.audioTrack=this.core.trtc.localScreenAudioTrack.mediaTrack),delete r.captureElement,delete r.preferDisplaySurface,delete r.systemAudio):r.videoTrack=this.createPlaceholderVideoTrack(),this.removeEventListeners(n);const i=r=>{var t,a,i;const o=e.inputLocalScreenTracks.get(n);this.log.debug(`screen internal track capture success event, is same instance:${r.room===this.core.room}, isScreen:${null==(t=r.track)?void 0:t.isScreen} kind:${null==(a=r.track)?void 0:a.kind}`,o,r.track.sourceTrack),r.track.isScreen&&(null==(i=r.track)?void 0:i.kind)===this.core.constants.NAME.VIDEO&&r.room===this.core.room&&r.track.sourceTrack&&o&&e.updateScreenSource(n,s,r.track.sourceTrack)},o=r=>{var t,a;const i=e.inputLocalScreenTracks.get(n);this.log.debug(`screen internal track stopped, is same track:${(null==(t=r.track)?void 0:t.sourceTrack)===(null==i?void 0:i.mediaTrack)}, isScreen:${r.track.isScreen}`),r.track.isScreen&&(null==(a=r.track)?void 0:a.sourceTrack)===(null==i?void 0:i.mediaTrack)&&e.updateScreenSource(n,s,this.createPlaceholderVideoTrack())};this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_TRACK_CAPTURE_SUCCESS,i),this.core.innerEmitter.on(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,o),this.eventListeners.has(n)||this.eventListeners.set(n,{}),this.eventListeners.get(n).captureSuccess=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_TRACK_CAPTURE_SUCCESS,i)},this.eventListeners.get(n).trackStop=()=>{this.core.innerEmitter.off(this.core.INNER_EVENT.LOCAL_TRACK_STOPPED,o)}}return r}async addNewScreenSource(e,r,t){const{id:a,layout:n}=r,s=await this.captureScreen(r);!(null==t?void 0:t.systemAudio)&&r.systemAudio&&e.inputLocalScreenTracks.has(a)&&e.removeScreenSource(a);try{e.addScreenSource(a,s,n)}catch(e){throw s.close(),e}}async parseTextOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeTextSource(r);const s=[],i=[];for(const t of r)try{n.has(t.id)?e.updateTextSource(t):e.addTextSource(t),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async parseImageOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeImageSource(r);const s=[],i=[];for(const t of r)try{await this.processSingleImageSource(e,t,n),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleImageSource(e,r,t){const{id:a,url:n,layout:s}=r,i=t.get(a);if(i){let r;i.url!==n&&(r=await this.core.utils.loadImage(n)),e.updateImageSource(a,s,r)}else{const r=await this.core.utils.loadImage(n);e.addImageSource(a,r,s)}}async parseVideoOptions(e,r,t=[]){const{removeIdList:a,preOptionsMap:n}=this.prepareSourceOptions(r,t);for(const r of a)e.removeVideoSource(r);const s=[],i=[];for(const t of r)try{await this.processSingleVideoSource(e,t,n),s.push(t)}catch(e){this.recordSourceError(t.id,e,n,s,i)}return{finalOptions:s,errors:i}}async processSingleVideoSource(e,r,t){const{id:a,url:n,layout:s}=r,i=t.get(a);if(i){let r;i.url!==n&&(r=await this.core.utils.loadVideo(n)),e.updateVideoSource(a,s,r)}else{const r=await this.core.utils.loadVideo(n);e.addVideoSource(a,r,s)}}createPlaceholderVideoTrack(){const e=document.createElement("canvas");e.width=1,e.height=1;const r=e.getContext("2d");if(!r){return e.captureStream(30).getVideoTracks()[0]}let t=null;const a=1e3/30,n=e.captureStream(30).getVideoTracks()[0],s=()=>{r.fillStyle="rgba(0, 0, 0, 0)",r.fillRect(0,0,e.width,e.height),"live"===n.readyState&&(t=setTimeout(s,a))};s();const i=n.stop.bind(n);return n.stop=()=>{t&&(clearTimeout(t),t=null),i()},n}removeEventListeners(e){const r=this.eventListeners.get(e);r&&(r.captureSuccess&&r.captureSuccess(),r.trackStop&&r.trackStop(),this.eventListeners.delete(e))}async captureCamera(e){const{id:r,cameraId:t,videoTrack:a,profile:n}=e,s=new this.core.LocalVideoTrack;s.log.id+=`-${r}`;const i={};if(t?i.deviceId=t:this.core.utils.isUndefined(a)||(i.customSource=a),!this.core.utils.isUndefined(n)){const e=this.resolveVideoProfile(n);e&&s.setProfile(e)}return await s.capture(i),s}async updateCameraProfile(e){var r;const{id:t,cameraId:a,videoTrack:n,profile:s}=e,i=null==(r=this.localMixVideoTrack)?void 0:r.inputLocalVideoTracks.get(t);if(i&&(a?await i.switchDevice(a):this.core.utils.isUndefined(n)||await i.setInputMediaStreamTrack(n),!this.core.utils.isUndefined(s))){const e=this.resolveVideoProfile(s);e&&i.setProfile(e),a&&i.isNeedToSwitchDevice(a)||await i.applyProfile()}}async captureScreen(e){const{id:r,profile:t,captureElement:a,preferDisplaySurface:n,systemAudio:s,videoTrack:i,audioTrack:o}=e,c=new this.core.LocalScreenTrack;c.log.id+=`-${r}`;const d={captureElement:a,preferDisplaySurface:n,systemAudio:s,videoTrack:i,audioTrack:o};if(!this.core.utils.isUndefined(t))if(this.core.utils.isString(t)){const e=this.core.constants.screenProfileMap[t];e&&c.setProfile(e)}else c.setProfile(t);const l=await c.capture(d);return s&&l.getAudioTracks().length>0?(this.systemAudioTrackList[r]=l.getAudioTracks()[0],this.log.info(`${r} system audio track captured`)):this.removeSystemAudioTrack(r),c.mediaTrack.addEventListener(this.core.constants.NAME.ENDED,()=>{this.handleScreenShareEnded(r)}),c}handleScreenShareEnded(e){var r,t,a;null==(r=this.localMixVideoTrack)||r.removeScreenSource(e),(null==(t=this._mixVideoConfig)?void 0:t.screen)&&(this._mixVideoConfig.screen=this._mixVideoConfig.screen.filter(r=>r.id!==e)),null==(a=this.onScreenShareStop)||a.call(this,e)}async _updatePreview({view:e,track:r,prevConfig:t}){if(this.core.utils.isUndefined(e)&&(null==t?void 0:t.view)){const e=this.core.utils.getViewListFromView(t.view);return void(e.length>0&&await r.play(e))}if(!this.core.utils.isUndefined(e)){const t=this.core.utils.getViewListFromView(e);t.length>0?await r.play(t):r.stop()}}removeSystemAudioTrack(e){const r=this.systemAudioTrackList[e];r&&(r.stop(),this.log.info(`${e} system audio track stop`),delete this.systemAudioTrackList[e])}};__publicField(_VideoMixer,"Name","VideoMixer");var VideoMixer=_VideoMixer,index_default=VideoMixer;export{index_default as default};export{VideoMixer};
|