@webex/ladon-ts 5.7.1 → 5.9.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.
@@ -1,76 +1,122 @@
1
- import { WorkerGenerator as n } from "./worker-generator.js";
2
- import { BlurRenderer as o } from "./renderer/blur-renderer.js";
3
- import { PassthroughRenderer as h } from "./renderer/passthrough-renderer.js";
4
- import { ReplacementRenderer as d } from "./renderer/replacement-renderer.js";
5
- import { resizeImage as u } from "./util.js";
6
- class w {
7
- constructor(t, e, a, r) {
8
- this.inputCanvas = t, this.outputCanvas = e, this.config = a, this.videoElement = r, this.loaded = !1;
9
- const s = this.inputCanvas.getContext("2d", {
1
+ import { WorkerGenerator as g } from "./worker-generator.js";
2
+ import { BlurRenderer as f } from "./renderer/blur-renderer.js";
3
+ import { PassthroughRenderer as w } from "./renderer/passthrough-renderer.js";
4
+ import { ReplacementRenderer as k } from "./renderer/replacement-renderer.js";
5
+ import { resizeImage as F } from "./util.js";
6
+ class M {
7
+ /**
8
+ * Create a new pipeline instance.
9
+ *
10
+ * @param inputCanvas - Canvas element for input frame processing.
11
+ * @param outputCanvas - Canvas element where the final rendered output will be displayed.
12
+ * @param config - Pipeline configuration containing processing and rendering settings.
13
+ * @param inputVideoElement - Video element serving as the input source.
14
+ * @throws Error if the input canvas 2D context cannot be obtained.
15
+ */
16
+ constructor(t, e, s, n) {
17
+ this.inputCanvas = t, this.outputCanvas = e, this.config = s, this.inputVideoElement = n, this.loaded = !1, this.isProcessingFrame = !1, this.pendingFramePromise = null;
18
+ const r = this.inputCanvas.getContext("2d", {
10
19
  alpha: !1,
11
20
  willReadFrequently: !0
12
21
  });
13
- if (!s)
22
+ if (!r)
14
23
  throw new Error("[ladon-ts] Error getting input context 2d");
15
- this.inputCanvasCtx = s;
24
+ this.inputCanvasContext = r;
16
25
  }
17
26
  /**
18
- * Processes a single frame and returns the inference result.
19
- * This method handles the frame data, performs inference if needed, and renders the output.
27
+ * Process a single frame and return the inference result.
28
+ * This method handles frame data, performs inference if needed, and renders the output.
29
+ * Includes frame processing serialization to prevent overlapping operations.
20
30
  *
21
- * @param timestamp - The timestamp of the frame.
22
- * @param performInference - Flag to perform mask inference.
23
- * @returns The result of the frame processing.
24
- * @throws If the pipeline (generator or renderer) is not loaded.
31
+ * @param timestamp - The timestamp of the frame in milliseconds.
32
+ * @param performInference - Whether to perform mask inference or reuse the last result.
33
+ * @param frameData - Optional frame data to process instead of capturing from input.
34
+ * @returns Promise resolving to the frame processing result with timing statistics.
35
+ * @throws Error if the pipeline (generator or renderer) is not loaded.
25
36
  */
26
- async onFrame(t, e = !0) {
37
+ async onFrame(t, e = !0, s) {
38
+ this.isProcessingFrame && this.pendingFramePromise && await this.pendingFramePromise, this.isProcessingFrame = !0;
39
+ try {
40
+ return this.pendingFramePromise = this.processFrameInternal(
41
+ t,
42
+ e,
43
+ s
44
+ ), await this.pendingFramePromise;
45
+ } finally {
46
+ this.isProcessingFrame = !1, this.pendingFramePromise = null;
47
+ }
48
+ }
49
+ /**
50
+ * Internal frame processing implementation.
51
+ * Handles the actual frame processing logic including timing measurements.
52
+ *
53
+ * @param timestamp - The timestamp of the frame in milliseconds.
54
+ * @param performInference - Whether to perform mask inference or reuse the last result.
55
+ * @param frameData - Optional frame data to process instead of capturing from input.
56
+ * @returns Promise resolving to the frame processing result with timing statistics.
57
+ * @throws Error if the pipeline (generator or renderer) is not loaded.
58
+ */
59
+ async processFrameInternal(t, e = !0, s) {
60
+ const n = performance.now();
27
61
  if (!this.generator || !this.renderer)
28
62
  throw new Error("[ladon-ts] Pipeline is not loaded.");
29
- const a = this.getInputFrameData(e);
30
- let r;
31
- if (a instanceof Uint8ClampedArray ? ((!this.frameDataBuffer || this.frameDataBuffer.length !== a.length) && (this.frameDataBuffer = new Uint8ClampedArray(a.length)), this.frameDataBuffer.set(a), r = this.frameDataBuffer) : r = a, e || !this.lastMaskResult) {
32
- const s = a, i = this.getMaskImageData(s);
33
- await this.generator.postRender(i, t), this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(t);
63
+ let r = 0, a = 0;
64
+ if (e || !this.lastMaskResult) {
65
+ const c = performance.now();
66
+ s ?? (s = this.getInputFrameData()), r = performance.now() - c;
67
+ const m = performance.now(), u = s, p = this.getMaskImageData(u);
68
+ await this.generator.process(p, t), this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(t), a = performance.now() - m;
34
69
  }
35
- return await this.renderer.render(r, this.lastMaskResult), this.createInferenceResult(t, e);
70
+ const h = performance.now();
71
+ await this.renderer.render(this.inputVideoElement, this.lastMaskResult);
72
+ const d = performance.now() - h, i = performance.now(), l = i - n, o = this.createInferenceResult(t, e);
73
+ return o.stats = {
74
+ totalFrameTime: l,
75
+ modelInferenceTime: a,
76
+ preprocessingTime: r,
77
+ postprocessingTime: d,
78
+ frameStartTime: n,
79
+ frameEndTime: i,
80
+ inferenceSkipped: !e
81
+ }, o;
36
82
  }
37
83
  /**
38
- * Gets the raw image data from the input canvas context.
39
- *
40
- * @param needFreshImageData - Flag to indicate if fresh image data is needed.
84
+ * Get the raw image data from the input canvas context.
41
85
  *
42
- * @returns The raw image data.
86
+ * @returns The frame data as a Uint8ClampedArray.
43
87
  */
44
- getInputFrameData(t) {
45
- return t || !this.videoElement ? this.inputCanvasCtx.getImageData(
88
+ getInputFrameData() {
89
+ return this.inputCanvasContext.getImageData(
46
90
  0,
47
91
  0,
48
92
  this.config.input.width,
49
93
  this.config.input.height
50
- ).data : this.videoElement;
94
+ ).data;
51
95
  }
52
96
  /**
53
- * Resizes the image data if the model expects a fixed size input.
97
+ * Resize the image data if the model expects a different input size.
98
+ * Handles scaling between input dimensions and model requirements.
54
99
  *
55
- * @param {Uint8ClampedArray} rawImageData - The raw image data.
56
- * @returns {Uint8ClampedArray} - The processed mask image data.
100
+ * @param rawImageData - The raw image data to potentially resize.
101
+ * @returns The processed mask image data, resized if necessary.
57
102
  */
58
103
  getMaskImageData(t) {
59
- const e = this.config.mask.inputSize, a = this.config.input;
60
- return e.width === a.width && e.height === a.height ? t : u(
104
+ const e = this.config.mask.inputSize, s = this.config.input;
105
+ return e.width === s.width && e.height === s.height ? t : F(
61
106
  t,
62
- a.width,
63
- a.height,
107
+ s.width,
108
+ s.height,
64
109
  e.width,
65
110
  e.height
66
111
  );
67
112
  }
68
113
  /**
69
- * Creates the inference result object.
114
+ * Create the inference result object with appropriate data.
115
+ * Handles both successful inference results and fallback cases.
70
116
  *
71
- * @param {number} timestamp - The timestamp of the frame.
72
- * @param {boolean} performedInference - Indicates if the inference was performed.
73
- * @returns {IFrameInferenceResult} - The result object containing inference details.
117
+ * @param timestamp - The timestamp of the frame in milliseconds.
118
+ * @param performedInference - Whether inference was actually performed for this frame.
119
+ * @returns The inference result object containing mask data and metadata.
74
120
  */
75
121
  createInferenceResult(t, e) {
76
122
  return this.lastMaskResult ? {
@@ -79,53 +125,66 @@ class w {
79
125
  motion: this.lastMaskResult.motion,
80
126
  skipped: !e,
81
127
  timestamp: t
128
+ // Stats will be added by the caller if needed
82
129
  } : {
83
130
  facesAndLandmarks: { data: new Float32Array(0), dims: [] },
84
131
  gesture: new Float32Array(0),
85
132
  motion: 0,
86
133
  skipped: !0,
87
134
  timestamp: t
135
+ // Stats will be added by the caller if needed
88
136
  };
89
137
  }
90
138
  /**
91
- * Checks if the pipeline is loaded.
139
+ * Check if the pipeline is fully loaded and ready for processing.
92
140
  *
93
- * @returns True if the pipeline is loaded, false otherwise.
141
+ * @returns True if the pipeline is loaded and ready, false otherwise.
94
142
  */
95
143
  isLoaded() {
96
144
  return this.loaded;
97
145
  }
98
146
  /**
99
- * Loads the pipeline components such as the generator and renderer.
147
+ * Load and initialize the pipeline components.
148
+ * Creates the appropriate renderer and generator based on configuration.
149
+ *
150
+ * @returns Promise that resolves when all components are loaded and ready.
151
+ * @throws Error if an unsupported renderer type is specified in the configuration.
100
152
  */
101
153
  async load() {
102
154
  switch (this.config.render.type) {
103
155
  case "blur":
104
- this.renderer = new o(this.outputCanvas, this.config);
156
+ this.renderer = new f(this.outputCanvas, this.config);
105
157
  break;
106
158
  case "passthrough":
107
- this.renderer = new h(this.outputCanvas, this.config);
159
+ this.renderer = new w(this.outputCanvas, this.config);
108
160
  break;
109
161
  case "replacement":
110
- this.renderer = new d(this.outputCanvas, this.config);
162
+ this.renderer = new k(this.outputCanvas, this.config);
111
163
  break;
112
164
  default:
113
165
  throw new Error("[ladon-ts] Renderer not implemented.");
114
166
  }
115
- await this.renderer.load(), this.generator = new n(this.config), await this.generator.load(), this.loaded = !0;
167
+ await this.renderer.load(), this.generator = new g(this.config), await this.generator.load(), this.loaded = !0;
116
168
  }
169
+ /**
170
+ * Reset the pipeline state by clearing cached mask results.
171
+ * Useful when switching between different processing modes or sources.
172
+ */
117
173
  reset() {
118
174
  this.lastMaskResult = void 0;
119
175
  }
120
176
  /**
121
- * Destroys the pipeline and cleans up resources.
177
+ * Destroy the pipeline and clean up all resources.
178
+ * Properly disposes of the renderer and generator to prevent memory leaks.
179
+ *
180
+ * @returns Promise that resolves when cleanup is complete.
122
181
  */
123
182
  async destroy() {
124
183
  var t, e;
125
- this.reset(), (t = this.renderer) == null || t.destroy(), await ((e = this.generator) == null ? void 0 : e.destroy()), this.frameDataBuffer = void 0, this.loaded = !1;
184
+ this.reset(), (t = this.renderer) == null || t.destroy(), await ((e = this.generator) == null ? void 0 : e.destroy()), this.loaded = !1;
126
185
  }
127
186
  }
128
187
  export {
129
- w as Pipeline
188
+ M as Pipeline
130
189
  };
131
190
  //# sourceMappingURL=pipeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport {\n FacesAndLandmarks,\n FrameData,\n LadonOutput,\n MaskGenerator,\n WorkerGenerator,\n} from './generator';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\nexport interface IFrameInferenceResult {\n facesAndLandmarks: FacesAndLandmarks;\n gesture: Float32Array;\n motion: number;\n skipped: boolean;\n timestamp: number;\n}\n\nexport interface IPipeline {\n onFrame(timestamp: number, maskNeeded?: boolean): Promise<IFrameInferenceResult>;\n isLoaded(): boolean;\n load(): Promise<void>;\n destroy(): Promise<void>;\n reset(): void;\n}\n\nexport class Pipeline implements IPipeline {\n private generator?: MaskGenerator;\n private renderer?: LadonRenderer;\n private loaded = false;\n\n private inputCanvasCtx: CanvasRenderingContext2D;\n private lastMaskResult?: LadonOutput;\n\n private frameDataBuffer?: Uint8ClampedArray;\n\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly videoElement?: HTMLVideoElement\n ) {\n const inputCanvasCtx = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasCtx) {\n throw new Error('[ladon-ts] Error getting input context 2d');\n }\n this.inputCanvasCtx = inputCanvasCtx;\n }\n\n /**\n * Processes a single frame and returns the inference result.\n * This method handles the frame data, performs inference if needed, and renders the output.\n *\n * @param timestamp - The timestamp of the frame.\n * @param performInference - Flag to perform mask inference.\n * @returns The result of the frame processing.\n * @throws If the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true\n ): Promise<IFrameInferenceResult> {\n if (!this.generator || !this.renderer) {\n throw new Error('[ladon-ts] Pipeline is not loaded.');\n }\n\n // Get the current frame (raw image data or video element). Use the video element if skipping\n // inference to avoid unnecessary processing when calling canvas getImageData.\n const frameData = this.getInputFrameData(performInference);\n\n // Reuse or create buffer for frame data copies, which is needed to ensure the data is not modified\n // or garbage collected before the inference is done.\n let frameDataCopy: FrameData;\n if (frameData instanceof Uint8ClampedArray) {\n if (!this.frameDataBuffer || this.frameDataBuffer.length !== frameData.length) {\n this.frameDataBuffer = new Uint8ClampedArray(frameData.length);\n }\n this.frameDataBuffer.set(frameData);\n frameDataCopy = this.frameDataBuffer;\n } else {\n frameDataCopy = frameData;\n }\n\n if (performInference || !this.lastMaskResult) {\n // Send frame for inference and wait for the mask for this timestamp\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n await this.generator.postRender(maskImageData, timestamp);\n this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(timestamp);\n }\n\n // Render the frame and mask together (mask may be stale if skipping inference)\n await this.renderer.render(frameDataCopy, this.lastMaskResult);\n\n return this.createInferenceResult(timestamp, performInference);\n }\n\n /**\n * Gets the raw image data from the input canvas context.\n *\n * @param needFreshImageData - Flag to indicate if fresh image data is needed.\n *\n * @returns The raw image data.\n */\n private getInputFrameData(needFreshImageData: boolean): FrameData {\n if (needFreshImageData || !this.videoElement) {\n return this.inputCanvasCtx.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n return this.videoElement;\n }\n\n /**\n * Resizes the image data if the model expects a fixed size input.\n *\n * @param {Uint8ClampedArray} rawImageData - The raw image data.\n * @returns {Uint8ClampedArray} - The processed mask image data.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n const modelInput = this.config.mask.inputSize;\n const inputImage = this.config.input;\n\n // Check if resizing is needed based on configuration.\n if (modelInput.width === inputImage.width && modelInput.height === inputImage.height) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n inputImage.width,\n inputImage.height,\n modelInput.width,\n modelInput.height\n );\n }\n\n /**\n * Creates the inference result object.\n *\n * @param {number} timestamp - The timestamp of the frame.\n * @param {boolean} performedInference - Indicates if the inference was performed.\n * @returns {IFrameInferenceResult} - The result object containing inference details.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n };\n }\n\n /**\n * Checks if the pipeline is loaded.\n *\n * @returns True if the pipeline is loaded, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Loads the pipeline components such as the generator and renderer.\n */\n public async load(): Promise<void> {\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n throw new Error('[ladon-ts] Renderer not implemented.');\n }\n\n await this.renderer.load();\n\n this.generator = new WorkerGenerator(this.config);\n await this.generator.load();\n\n this.loaded = true;\n }\n\n public reset(): void {\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroys the pipeline and cleans up resources.\n */\n public async destroy(): Promise<void> {\n this.reset();\n this.renderer?.destroy();\n await this.generator?.destroy();\n this.frameDataBuffer = undefined;\n this.loaded = false;\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","videoElement","inputCanvasCtx","timestamp","performInference","frameData","frameDataCopy","rawImageData","maskImageData","needFreshImageData","modelInput","inputImage","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","WorkerGenerator","_a","_b"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport { FrameData, LadonOutput, MaskGenerator, WorkerGenerator } from './generator';\nimport { IFrameInferenceResult, IPipeline } from './pipeline-types';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\n/**\n * Core pipeline implementation for processing video frames with ML inference and rendering.\n * Handles frame processing, mask generation, and output rendering with proper timing controls.\n */\nexport class Pipeline implements IPipeline {\n /** The mask generator responsible for ML inference. */\n private generator?: MaskGenerator;\n\n /** The renderer responsible for compositing the final output. */\n private renderer?: LadonRenderer;\n\n /** Flag indicating whether the pipeline has been fully loaded and initialized. */\n private loaded = false;\n\n /** 2D rendering context for the input canvas used for frame processing. */\n private inputCanvasContext: CanvasRenderingContext2D;\n\n /** The most recent mask result from ML inference, used when skipping inference. */\n private lastMaskResult?: LadonOutput;\n\n /** Flag indicating whether a frame is currently being processed. */\n private isProcessingFrame = false;\n\n /** Promise for the currently pending frame processing operation. */\n private pendingFramePromise: Promise<IFrameInferenceResult> | null = null;\n\n /**\n * Create a new pipeline instance.\n *\n * @param inputCanvas - Canvas element for input frame processing.\n * @param outputCanvas - Canvas element where the final rendered output will be displayed.\n * @param config - Pipeline configuration containing processing and rendering settings.\n * @param inputVideoElement - Video element serving as the input source.\n * @throws Error if the input canvas 2D context cannot be obtained.\n */\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly inputVideoElement: HTMLVideoElement\n ) {\n const inputCanvasContext = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasContext) {\n throw new Error('[ladon-ts] Error getting input context 2d');\n }\n this.inputCanvasContext = inputCanvasContext;\n }\n\n /**\n * Process a single frame and return the inference result.\n * This method handles frame data, performs inference if needed, and renders the output.\n * Includes frame processing serialization to prevent overlapping operations.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performInference - Whether to perform mask inference or reuse the last result.\n * @param frameData - Optional frame data to process instead of capturing from input.\n * @returns Promise resolving to the frame processing result with timing statistics.\n * @throws Error if the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true,\n frameData?: FrameData\n ): Promise<IFrameInferenceResult> {\n // If already processing, wait for the current frame to finish\n if (this.isProcessingFrame && this.pendingFramePromise) {\n await this.pendingFramePromise;\n }\n\n // Set processing flag\n this.isProcessingFrame = true;\n\n try {\n this.pendingFramePromise = this.processFrameInternal(\n timestamp,\n performInference,\n frameData\n );\n return await this.pendingFramePromise;\n } finally {\n this.isProcessingFrame = false;\n this.pendingFramePromise = null;\n }\n }\n\n /**\n * Internal frame processing implementation.\n * Handles the actual frame processing logic including timing measurements.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performInference - Whether to perform mask inference or reuse the last result.\n * @param frameData - Optional frame data to process instead of capturing from input.\n * @returns Promise resolving to the frame processing result with timing statistics.\n * @throws Error if the pipeline (generator or renderer) is not loaded.\n */\n private async processFrameInternal(\n timestamp: number,\n performInference = true,\n frameData?: FrameData\n ): Promise<IFrameInferenceResult> {\n const frameStartTime = performance.now();\n\n if (!this.generator || !this.renderer) {\n throw new Error('[ladon-ts] Pipeline is not loaded.');\n }\n\n let preprocessingTime = 0;\n let modelInferenceTime = 0;\n\n if (performInference || !this.lastMaskResult) {\n // Pre-processing timing\n const preprocessStart = performance.now();\n\n // Get the current frame (raw image data or video element). Use the video element if skipping\n // inference to avoid unnecessary processing when calling canvas getImageData.\n frameData ??= this.getInputFrameData();\n\n preprocessingTime = performance.now() - preprocessStart;\n\n // Model inference timing\n const inferenceStart = performance.now();\n\n // Send frame for inference and wait for the mask for this timestamp\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n await this.generator.process(maskImageData, timestamp);\n this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(timestamp);\n\n modelInferenceTime = performance.now() - inferenceStart;\n }\n\n // Post-processing timing (rendering)\n const postprocessStart = performance.now();\n\n // Render the frame and mask together (mask may be stale if skipping inference)\n await this.renderer.render(this.inputVideoElement, this.lastMaskResult);\n\n const postprocessingTime = performance.now() - postprocessStart;\n\n const frameEndTime = performance.now();\n const totalFrameTime = frameEndTime - frameStartTime;\n\n const result = this.createInferenceResult(timestamp, performInference);\n\n // Add timing stats to the result\n result.stats = {\n totalFrameTime,\n modelInferenceTime,\n preprocessingTime,\n postprocessingTime,\n frameStartTime,\n frameEndTime,\n inferenceSkipped: !performInference,\n };\n\n return result;\n }\n\n /**\n * Get the raw image data from the input canvas context.\n *\n * @returns The frame data as a Uint8ClampedArray.\n */\n private getInputFrameData(): FrameData {\n return this.inputCanvasContext.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n /**\n * Resize the image data if the model expects a different input size.\n * Handles scaling between input dimensions and model requirements.\n *\n * @param rawImageData - The raw image data to potentially resize.\n * @returns The processed mask image data, resized if necessary.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n const modelInput = this.config.mask.inputSize;\n const inputImage = this.config.input;\n\n // Check if resizing is needed based on configuration.\n if (modelInput.width === inputImage.width && modelInput.height === inputImage.height) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n inputImage.width,\n inputImage.height,\n modelInput.width,\n modelInput.height\n );\n }\n\n /**\n * Create the inference result object with appropriate data.\n * Handles both successful inference results and fallback cases.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performedInference - Whether inference was actually performed for this frame.\n * @returns The inference result object containing mask data and metadata.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n /**\n * Check if the pipeline is fully loaded and ready for processing.\n *\n * @returns True if the pipeline is loaded and ready, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Load and initialize the pipeline components.\n * Creates the appropriate renderer and generator based on configuration.\n *\n * @returns Promise that resolves when all components are loaded and ready.\n * @throws Error if an unsupported renderer type is specified in the configuration.\n */\n public async load(): Promise<void> {\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n throw new Error('[ladon-ts] Renderer not implemented.');\n }\n\n await this.renderer.load();\n\n this.generator = new WorkerGenerator(this.config);\n await this.generator.load();\n\n this.loaded = true;\n }\n\n /**\n * Reset the pipeline state by clearing cached mask results.\n * Useful when switching between different processing modes or sources.\n */\n public reset(): void {\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroy the pipeline and clean up all resources.\n * Properly disposes of the renderer and generator to prevent memory leaks.\n *\n * @returns Promise that resolves when cleanup is complete.\n */\n public async destroy(): Promise<void> {\n this.reset();\n this.renderer?.destroy();\n await this.generator?.destroy();\n this.loaded = false;\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","inputVideoElement","inputCanvasContext","timestamp","performInference","frameData","frameStartTime","preprocessingTime","modelInferenceTime","preprocessStart","inferenceStart","rawImageData","maskImageData","postprocessStart","postprocessingTime","frameEndTime","totalFrameTime","result","modelInput","inputImage","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","WorkerGenerator","_a","_b"],"mappings":";;;;;AAUO,MAAMA,EAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BvC,YACYC,GACAC,GACQC,GACCC,GACnB;AAJU,SAAA,cAAAH,GACA,KAAA,eAAAC,GACQ,KAAA,SAAAC,GACC,KAAA,oBAAAC,GA3BrB,KAAQ,SAAS,IASjB,KAAQ,oBAAoB,IAG5B,KAAQ,sBAA6D;AAiBjE,UAAMC,IAAqB,KAAK,YAAY,WAAW,MAAM;AAAA,MACzD,OAAO;AAAA,MACP,oBAAoB;AAAA,IAAA,CACvB;AAED,QAAI,CAACA;AACK,YAAA,IAAI,MAAM,2CAA2C;AAE/D,SAAK,qBAAqBA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc9B,MAAa,QACTC,GACAC,IAAmB,IACnBC,GAC8B;AAE1B,IAAA,KAAK,qBAAqB,KAAK,uBAC/B,MAAM,KAAK,qBAIf,KAAK,oBAAoB;AAErB,QAAA;AACA,kBAAK,sBAAsB,KAAK;AAAA,QAC5BF;AAAA,QACAC;AAAA,QACAC;AAAA,MACJ,GACO,MAAM,KAAK;AAAA,IAAA,UACpB;AACE,WAAK,oBAAoB,IACzB,KAAK,sBAAsB;AAAA,IAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaJ,MAAc,qBACVF,GACAC,IAAmB,IACnBC,GAC8B;AACxB,UAAAC,IAAiB,YAAY,IAAI;AAEvC,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK;AACnB,YAAA,IAAI,MAAM,oCAAoC;AAGxD,QAAIC,IAAoB,GACpBC,IAAqB;AAErB,QAAAJ,KAAoB,CAAC,KAAK,gBAAgB;AAEpC,YAAAK,IAAkB,YAAY,IAAI;AAIxC,MAAAJ,UAAc,KAAK,kBAAkB,IAEjBE,IAAA,YAAY,QAAQE;AAGlC,YAAAC,IAAiB,YAAY,IAAI,GAGjCC,IAAeN,GACfO,IAAgB,KAAK,iBAAiBD,CAAY;AACxD,YAAM,KAAK,UAAU,QAAQC,GAAeT,CAAS,GACrD,KAAK,iBAAiB,MAAM,KAAK,UAAU,2BAA2BA,CAAS,GAE1DK,IAAA,YAAY,QAAQE;AAAA,IAAA;AAIvC,UAAAG,IAAmB,YAAY,IAAI;AAGzC,UAAM,KAAK,SAAS,OAAO,KAAK,mBAAmB,KAAK,cAAc;AAEhE,UAAAC,IAAqB,YAAY,IAAA,IAAQD,GAEzCE,IAAe,YAAY,IAAI,GAC/BC,IAAiBD,IAAeT,GAEhCW,IAAS,KAAK,sBAAsBd,GAAWC,CAAgB;AAGrE,WAAAa,EAAO,QAAQ;AAAA,MACX,gBAAAD;AAAA,MACA,oBAAAR;AAAA,MACA,mBAAAD;AAAA,MACA,oBAAAO;AAAA,MACA,gBAAAR;AAAA,MACA,cAAAS;AAAA,MACA,kBAAkB,CAACX;AAAA,IACvB,GAEOa;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQH,oBAA+B;AACnC,WAAO,KAAK,mBAAmB;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,KAAK,OAAO,MAAM;AAAA,MAClB,KAAK,OAAO,MAAM;AAAA,IAAA,EACpB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUE,iBAAiBN,GAAoD;AACnE,UAAAO,IAAa,KAAK,OAAO,KAAK,WAC9BC,IAAa,KAAK,OAAO;AAG/B,WAAID,EAAW,UAAUC,EAAW,SAASD,EAAW,WAAWC,EAAW,SACnER,IAIJS;AAAA,MACHT;AAAA,MACAQ,EAAW;AAAA,MACXA,EAAW;AAAA,MACXD,EAAW;AAAA,MACXA,EAAW;AAAA,IACf;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWI,sBACJf,GACAkB,GACqB;AACjB,WAAC,KAAK,iBAWH;AAAA,MACH,mBAAmB,KAAK,eAAe;AAAA,MACvC,SAAS,KAAK,eAAe;AAAA,MAC7B,QAAQ,KAAK,eAAe;AAAA,MAC5B,SAAS,CAACA;AAAA,MACV,WAAAlB;AAAA;AAAA,IAEJ,IAjBW;AAAA,MACH,mBAAmB,EAAE,MAAM,IAAI,aAAa,CAAC,GAAG,MAAM,GAAG;AAAA,MACzD,SAAS,IAAI,aAAa,CAAC;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,WAAAA;AAAA;AAAA,IAEJ;AAAA,EAUJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQG,WAAoB;AACvB,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUhB,MAAa,OAAsB;AACvB,YAAA,KAAK,OAAO,OAAO,MAAM;AAAA,MAC7B,KAAK;AACD,aAAK,WAAW,IAAImB,EAAa,KAAK,cAAc,KAAK,MAAM;AAC/D;AAAA,MACJ,KAAK;AACD,aAAK,WAAW,IAAIC,EAAoB,KAAK,cAAc,KAAK,MAAM;AACtE;AAAA,MACJ,KAAK;AACD,aAAK,WAAW,IAAIC,EAAoB,KAAK,cAAc,KAAK,MAAM;AACtE;AAAA,MACJ;AACU,cAAA,IAAI,MAAM,sCAAsC;AAAA,IAAA;AAGxD,UAAA,KAAK,SAAS,KAAK,GAEzB,KAAK,YAAY,IAAIC,EAAgB,KAAK,MAAM,GAC1C,MAAA,KAAK,UAAU,KAAK,GAE1B,KAAK,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOX,QAAc;AACjB,SAAK,iBAAiB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS1B,MAAa,UAAyB;;AAClC,SAAK,MAAM,IACXC,IAAA,KAAK,aAAL,QAAAA,EAAe,WACT,QAAAC,IAAA,KAAK,cAAL,gBAAAA,EAAgB,YACtB,KAAK,SAAS;AAAA,EAAA;AAEtB;"}
@@ -2,17 +2,79 @@ import { PipelineConfig } from '../config-types';
2
2
  import { FrameData, LadonOutput } from '../generator';
3
3
  import { LadonRenderer } from './ladon-renderer';
4
4
  import { WebGLRenderer } from './webgl-renderer';
5
+ /**
6
+ * WebGL renderer implementation for virtual background replacement effects.
7
+ * Handles rendering of video backgrounds with proper frame rate throttling.
8
+ */
5
9
  export declare class ReplacementRenderer extends WebGLRenderer implements LadonRenderer {
6
- output_canvas: HTMLCanvasElement;
10
+ outputCanvas: HTMLCanvasElement;
7
11
  readonly config: PipelineConfig;
12
+ /** The compiled WebGL shader program used for rendering. */
8
13
  private program;
9
- private background_canvas_ctx;
10
- protected background_texture: WebGLTexture;
11
- protected fragment_shader: string;
12
- constructor(output_canvas: HTMLCanvasElement, config: PipelineConfig);
14
+ /** Timestamp of the last video frame update in milliseconds. */
15
+ private lastVideoFrameTime;
16
+ /** Interval between video frame updates in milliseconds. */
17
+ private videoFrameInterval;
18
+ /** WebGL texture containing the background image or video frame. */
19
+ protected backgroundTexture: WebGLTexture;
20
+ /** Fragment shader source code for the replacement effect. */
21
+ protected fragmentShader: string;
22
+ /**
23
+ * Create a new replacement renderer instance.
24
+ *
25
+ * @param outputCanvas - The canvas element where the rendered output will be displayed.
26
+ * @param config - Pipeline configuration containing render settings and background source.
27
+ */
28
+ constructor(outputCanvas: HTMLCanvasElement, config: PipelineConfig);
29
+ /**
30
+ * Build the fragment shader source code for the replacement effect.
31
+ * Creates a shader that blends the background with the input video based on the mask.
32
+ *
33
+ * @returns The complete fragment shader source code.
34
+ */
13
35
  protected buildFragmentShader(): string;
36
+ /**
37
+ * Set up the WebGL texture for the background image or video.
38
+ * Creates and configures the texture that will be used as the replacement background.
39
+ */
14
40
  protected setupBackgroundTexture(): void;
41
+ /**
42
+ * Render a frame with the replacement background effect.
43
+ * Combines the input video stream with the background based on the provided mask.
44
+ * Includes frame rate throttling for video backgrounds to prevent stuttering.
45
+ * Canvas backgrounds are updated every frame to support dynamic content.
46
+ *
47
+ * @param input - The input frame data (video element or image data).
48
+ * @param mask - Optional mask data for determining foreground/background blend.
49
+ * @returns Promise that resolves when rendering is complete.
50
+ */
15
51
  render(input: FrameData, mask?: LadonOutput): Promise<void>;
52
+ /**
53
+ * Throttle video background updates and upload texture only when needed.
54
+ * This prevents stuttering and unnecessary GPU uploads when inference is skipped.
55
+ *
56
+ * @param backgroundSource - The video element to potentially upload.
57
+ * @returns Promise that resolves after appropriate throttling and upload.
58
+ */
59
+ private updateVideoBackgroundTexture;
60
+ /**
61
+ * Attempt to determine the frame rate of a video element.
62
+ * Tries to extract frame rate from video track settings if available.
63
+ *
64
+ * @param video - The video element to analyze.
65
+ * @returns The video's frame rate in frames per second, or undefined if not determinable.
66
+ */
67
+ private getVideoFrameRate;
68
+ /**
69
+ * Load and initialize the renderer.
70
+ * Currently a no-op for this renderer implementation.
71
+ *
72
+ * @returns Promise that resolves when loading is complete.
73
+ */
16
74
  load(): Promise<void>;
75
+ /**
76
+ * Clean up resources and destroy the renderer.
77
+ * Currently a no-op for this renderer implementation.
78
+ */
17
79
  destroy(): void;
18
80
  }
@@ -1,19 +1,30 @@
1
- import { compileShader as i, linkProgram as g } from "../util.js";
1
+ import { compileShader as a, linkProgram as g } from "../util.js";
2
2
  import { WebGLRenderer as h } from "./webgl-renderer.js";
3
- class c extends h {
4
- constructor(t, r) {
5
- super(t, r), this.output_canvas = t, this.config = r;
6
- const e = this.config.render.background.getContext("2d", { willReadFrequently: !0 });
7
- if (!e)
8
- throw new Error("[ladon-ts] Could not get background canvas context 2d");
9
- this.background_canvas_ctx = e, this.fragment_shader = this.buildFragmentShader();
10
- const s = i(this.gl, this.gl.VERTEX_SHADER, this.vertex_shader_src), a = i(
11
- this.gl,
12
- this.gl.FRAGMENT_SHADER,
13
- this.fragment_shader
14
- );
15
- this.program = g(this.gl, s, a), this.gl.useProgram(this.program), this.setSharedUniforms(this.program), this.setupBackgroundTexture();
3
+ class l extends h {
4
+ /**
5
+ * Create a new replacement renderer instance.
6
+ *
7
+ * @param outputCanvas - The canvas element where the rendered output will be displayed.
8
+ * @param config - Pipeline configuration containing render settings and background source.
9
+ */
10
+ constructor(t, e) {
11
+ super(t, e), this.outputCanvas = t, this.config = e, this.lastVideoFrameTime = 0;
12
+ const s = this.config.render.background;
13
+ if (s instanceof HTMLVideoElement) {
14
+ const n = this.getVideoFrameRate(s) || 30;
15
+ this.videoFrameInterval = 1e3 / n;
16
+ } else
17
+ this.videoFrameInterval = 0;
18
+ this.fragmentShader = this.buildFragmentShader();
19
+ const i = a(this.gl, this.gl.VERTEX_SHADER, this.vertex_shader_src), o = a(this.gl, this.gl.FRAGMENT_SHADER, this.fragmentShader);
20
+ this.program = g(this.gl, i, o), this.gl.useProgram(this.program), this.setSharedUniforms(this.program), this.setupBackgroundTexture();
16
21
  }
22
+ /**
23
+ * Build the fragment shader source code for the replacement effect.
24
+ * Creates a shader that blends the background with the input video based on the mask.
25
+ *
26
+ * @returns The complete fragment shader source code.
27
+ */
17
28
  buildFragmentShader() {
18
29
  return `${this.fragment_shader_header}
19
30
 
@@ -32,64 +43,103 @@ void main() {
32
43
  outColor = vec4(color, 1.0);
33
44
  }`;
34
45
  }
46
+ /**
47
+ * Set up the WebGL texture for the background image or video.
48
+ * Creates and configures the texture that will be used as the replacement background.
49
+ */
35
50
  setupBackgroundTexture() {
36
- const t = this.background_canvas_ctx.getImageData(
37
- 0,
38
- 0,
39
- this.config.render.background.width,
40
- this.config.render.background.height
41
- );
42
- this.background_texture = this.createTexture(
51
+ const e = this.config.render.background;
52
+ this.backgroundTexture = this.createTexture(
43
53
  this.gl.RGBA,
44
- this.config.render.background.width,
45
- this.config.render.background.height,
54
+ e.width,
55
+ e.height,
46
56
  () => {
47
57
  this.gl.texImage2D(
48
58
  this.gl.TEXTURE_2D,
49
59
  0,
50
60
  this.gl.RGBA,
51
- t.width,
52
- t.height,
53
- 0,
54
61
  this.gl.RGBA,
55
62
  this.gl.UNSIGNED_BYTE,
56
- t.data
63
+ e
57
64
  );
58
65
  }
59
66
  );
60
67
  const r = this.gl.getUniformLocation(this.program, "u_background");
61
68
  if (!r)
62
69
  throw new Error('[ladon-ts] Error finding "u_background" uniform');
63
- this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.background_texture), this.gl.uniform1i(r, 2);
70
+ this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture), this.gl.uniform1i(r, 2);
71
+ }
72
+ /**
73
+ * Render a frame with the replacement background effect.
74
+ * Combines the input video stream with the background based on the provided mask.
75
+ * Includes frame rate throttling for video backgrounds to prevent stuttering.
76
+ * Canvas backgrounds are updated every frame to support dynamic content.
77
+ *
78
+ * @param input - The input frame data (video element or image data).
79
+ * @param mask - Optional mask data for determining foreground/background blend.
80
+ * @returns Promise that resolves when rendering is complete.
81
+ */
82
+ async render(t, e) {
83
+ this.uploadVideoStream(t), this.uploadMask(e);
84
+ const r = this.config.render, s = !r.static, i = r.background;
85
+ s && (i instanceof HTMLVideoElement ? await this.updateVideoBackgroundTexture(i) : (this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture), this.gl.texImage2D(
86
+ this.gl.TEXTURE_2D,
87
+ 0,
88
+ this.gl.RGBA,
89
+ this.gl.RGBA,
90
+ this.gl.UNSIGNED_BYTE,
91
+ i
92
+ ))), this.gl.useProgram(this.program), this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3), this.postDraw();
93
+ }
94
+ /**
95
+ * Throttle video background updates and upload texture only when needed.
96
+ * This prevents stuttering and unnecessary GPU uploads when inference is skipped.
97
+ *
98
+ * @param backgroundSource - The video element to potentially upload.
99
+ * @returns Promise that resolves after appropriate throttling and upload.
100
+ */
101
+ async updateVideoBackgroundTexture(t) {
102
+ if (this.videoFrameInterval <= 0) return;
103
+ const e = performance.now();
104
+ e - this.lastVideoFrameTime >= this.videoFrameInterval && (this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture), this.gl.texImage2D(
105
+ this.gl.TEXTURE_2D,
106
+ 0,
107
+ this.gl.RGBA,
108
+ this.gl.RGBA,
109
+ this.gl.UNSIGNED_BYTE,
110
+ t
111
+ ), this.lastVideoFrameTime = e);
64
112
  }
65
- async render(t, r) {
66
- if (this.uploadVideoStream(t), this.uploadMask(r), !this.config.render.static) {
67
- const e = this.background_canvas_ctx.getImageData(
68
- 0,
69
- 0,
70
- this.config.render.background.width,
71
- this.config.render.background.height
72
- ).data;
73
- this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.background_texture), this.gl.texImage2D(
74
- this.gl.TEXTURE_2D,
75
- 0,
76
- this.gl.RGBA,
77
- this.config.render.background.width,
78
- this.config.render.background.height,
79
- 0,
80
- this.gl.RGBA,
81
- this.gl.UNSIGNED_BYTE,
82
- e
83
- );
113
+ /**
114
+ * Attempt to determine the frame rate of a video element.
115
+ * Tries to extract frame rate from video track settings if available.
116
+ *
117
+ * @param video - The video element to analyze.
118
+ * @returns The video's frame rate in frames per second, or undefined if not determinable.
119
+ */
120
+ getVideoFrameRate(t) {
121
+ if (t.srcObject instanceof MediaStream) {
122
+ const e = t.srcObject.getVideoTracks()[0];
123
+ if (e != null && e.getSettings)
124
+ return e.getSettings().frameRate;
84
125
  }
85
- this.gl.useProgram(this.program), this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3), this.postDraw();
86
126
  }
127
+ /**
128
+ * Load and initialize the renderer.
129
+ * Currently a no-op for this renderer implementation.
130
+ *
131
+ * @returns Promise that resolves when loading is complete.
132
+ */
87
133
  async load() {
88
134
  }
135
+ /**
136
+ * Clean up resources and destroy the renderer.
137
+ * Currently a no-op for this renderer implementation.
138
+ */
89
139
  destroy() {
90
140
  }
91
141
  }
92
142
  export {
93
- c as ReplacementRenderer
143
+ l as ReplacementRenderer
94
144
  };
95
145
  //# sourceMappingURL=replacement-renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"replacement-renderer.js","sources":["../../../../src/lib/renderer/replacement-renderer.ts"],"sourcesContent":["// replacement-renderer.ts\n\nimport { PipelineConfig, ReplacementConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { compileShader, linkProgram } from '../util';\n\nimport { LadonRenderer } from './ladon-renderer';\nimport { WebGLRenderer } from './webgl-renderer';\n\nexport class ReplacementRenderer extends WebGLRenderer implements LadonRenderer {\n private program: WebGLProgram;\n private background_canvas_ctx: CanvasRenderingContext2D;\n\n protected background_texture!: WebGLTexture;\n protected fragment_shader!: string;\n\n constructor(public output_canvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n super(output_canvas, config);\n\n const background_canvas_ctx = (\n this.config.render as ReplacementConfig\n ).background.getContext('2d', { willReadFrequently: true });\n if (!background_canvas_ctx) {\n throw new Error('[ladon-ts] Could not get background canvas context 2d');\n }\n this.background_canvas_ctx = background_canvas_ctx;\n\n // Build the fragment shader\n this.fragment_shader = this.buildFragmentShader();\n\n // Compile and link shaders\n const vertex_shader = compileShader(this.gl, this.gl.VERTEX_SHADER, this.vertex_shader_src);\n const fragment_shader = compileShader(\n this.gl,\n this.gl.FRAGMENT_SHADER,\n this.fragment_shader\n );\n\n this.program = linkProgram(this.gl, vertex_shader, fragment_shader);\n this.gl.useProgram(this.program);\n\n this.setSharedUniforms(this.program);\n\n // Set up background texture\n this.setupBackgroundTexture();\n }\n\n protected buildFragmentShader(): string {\n return `${this.fragment_shader_header}\n \nuniform sampler2D u_background;\n\n${this.shared_shader_functions}\n\nvoid main() {\n ${this.getUVCalculation()}\n\n vec3 background = texture(u_background, st).rgb;\n vec3 image = texture(u_image, st).rgb;\n float mask = get_mask(st);\n\n vec3 color = mix(background, image, mask);\n outColor = vec4(color, 1.0);\n}`;\n }\n\n protected setupBackgroundTexture() {\n const backgroundData = this.background_canvas_ctx.getImageData(\n 0,\n 0,\n (this.config.render as ReplacementConfig).background.width,\n (this.config.render as ReplacementConfig).background.height\n );\n\n this.background_texture = this.createTexture(\n this.gl.RGBA,\n (this.config.render as ReplacementConfig).background.width,\n (this.config.render as ReplacementConfig).background.height,\n () => {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n backgroundData.width,\n backgroundData.height,\n 0,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundData.data\n );\n }\n );\n\n const u_background = this.gl.getUniformLocation(this.program, 'u_background');\n if (!u_background) {\n throw new Error('[ladon-ts] Error finding \"u_background\" uniform');\n }\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.background_texture);\n this.gl.uniform1i(u_background, 2);\n }\n\n public async render(input: FrameData, mask?: LadonOutput): Promise<void> {\n this.uploadVideoStream(input);\n this.uploadMask(mask);\n\n if (!(this.config.render as ReplacementConfig).static) {\n const backgroundData = this.background_canvas_ctx.getImageData(\n 0,\n 0,\n (this.config.render as ReplacementConfig).background.width,\n (this.config.render as ReplacementConfig).background.height\n ).data;\n\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.background_texture);\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n (this.config.render as ReplacementConfig).background.width,\n (this.config.render as ReplacementConfig).background.height,\n 0,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundData\n );\n }\n\n this.gl.useProgram(this.program);\n\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n\n this.postDraw();\n }\n\n public async load(): Promise<void> {\n // empty\n }\n\n public destroy(): void {\n // empty\n }\n}\n"],"names":["ReplacementRenderer","WebGLRenderer","output_canvas","config","background_canvas_ctx","vertex_shader","compileShader","fragment_shader","linkProgram","backgroundData","u_background","input","mask"],"mappings":"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"}
1
+ {"version":3,"file":"replacement-renderer.js","sources":["../../../../src/lib/renderer/replacement-renderer.ts"],"sourcesContent":["import { PipelineConfig, ReplacementConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { compileShader, linkProgram } from '../util';\n\nimport { LadonRenderer } from './ladon-renderer';\nimport { WebGLRenderer } from './webgl-renderer';\n\n/**\n * WebGL renderer implementation for virtual background replacement effects.\n * Handles rendering of video backgrounds with proper frame rate throttling.\n */\nexport class ReplacementRenderer extends WebGLRenderer implements LadonRenderer {\n /** The compiled WebGL shader program used for rendering. */\n private program: WebGLProgram;\n\n /** Timestamp of the last video frame update in milliseconds. */\n private lastVideoFrameTime = 0;\n\n /** Interval between video frame updates in milliseconds. */\n private videoFrameInterval: number;\n\n /** WebGL texture containing the background image or video frame. */\n protected backgroundTexture!: WebGLTexture;\n\n /** Fragment shader source code for the replacement effect. */\n protected fragmentShader!: string;\n\n /**\n * Create a new replacement renderer instance.\n *\n * @param outputCanvas - The canvas element where the rendered output will be displayed.\n * @param config - Pipeline configuration containing render settings and background source.\n */\n constructor(public outputCanvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n super(outputCanvas, config);\n\n // Initialize video frame interval based on background source\n const renderConfig = this.config.render as ReplacementConfig;\n const backgroundSource = renderConfig.background;\n\n if (backgroundSource instanceof HTMLVideoElement) {\n // Use video's frame rate, or default to 30fps\n const videoFPS = this.getVideoFrameRate(backgroundSource) || 30;\n this.videoFrameInterval = 1000 / videoFPS;\n } else {\n this.videoFrameInterval = 0; // No throttling for static backgrounds\n }\n\n // Build the fragment shader\n this.fragmentShader = this.buildFragmentShader();\n\n // Compile and link shaders\n const vertexShader = compileShader(this.gl, this.gl.VERTEX_SHADER, this.vertex_shader_src);\n const fragmentShader = compileShader(this.gl, this.gl.FRAGMENT_SHADER, this.fragmentShader);\n\n this.program = linkProgram(this.gl, vertexShader, fragmentShader);\n this.gl.useProgram(this.program);\n\n this.setSharedUniforms(this.program);\n\n this.setupBackgroundTexture();\n }\n\n /**\n * Build the fragment shader source code for the replacement effect.\n * Creates a shader that blends the background with the input video based on the mask.\n *\n * @returns The complete fragment shader source code.\n */\n protected buildFragmentShader(): string {\n return `${this.fragment_shader_header}\n \nuniform sampler2D u_background;\n\n${this.shared_shader_functions}\n\nvoid main() {\n ${this.getUVCalculation()}\n\n vec3 background = texture(u_background, st).rgb;\n vec3 image = texture(u_image, st).rgb;\n float mask = get_mask(st);\n\n vec3 color = mix(background, image, mask);\n outColor = vec4(color, 1.0);\n}`;\n }\n\n /**\n * Set up the WebGL texture for the background image or video.\n * Creates and configures the texture that will be used as the replacement background.\n */\n protected setupBackgroundTexture(): void {\n const renderConfig = this.config.render as ReplacementConfig;\n const backgroundSource = renderConfig.background;\n\n this.backgroundTexture = this.createTexture(\n this.gl.RGBA,\n backgroundSource.width,\n backgroundSource.height,\n () => {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n }\n );\n\n const uBackground = this.gl.getUniformLocation(this.program, 'u_background');\n if (!uBackground) {\n throw new Error('[ladon-ts] Error finding \"u_background\" uniform');\n }\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n this.gl.uniform1i(uBackground, 2);\n }\n\n /**\n * Render a frame with the replacement background effect.\n * Combines the input video stream with the background based on the provided mask.\n * Includes frame rate throttling for video backgrounds to prevent stuttering.\n * Canvas backgrounds are updated every frame to support dynamic content.\n *\n * @param input - The input frame data (video element or image data).\n * @param mask - Optional mask data for determining foreground/background blend.\n * @returns Promise that resolves when rendering is complete.\n */\n public async render(input: FrameData, mask?: LadonOutput): Promise<void> {\n this.uploadVideoStream(input);\n this.uploadMask(mask);\n\n const renderConfig = this.config.render as ReplacementConfig;\n const shouldDrawVideoBackground = !renderConfig.static;\n const backgroundSource = renderConfig.background;\n\n if (shouldDrawVideoBackground) {\n if (backgroundSource instanceof HTMLVideoElement) {\n // Throttle video backgrounds to prevent stuttering\n await this.updateVideoBackgroundTexture(backgroundSource);\n } else {\n // Always update canvas backgrounds (like dynamic Game of Life)\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n }\n }\n\n this.gl.useProgram(this.program);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n this.postDraw();\n }\n\n /**\n * Throttle video background updates and upload texture only when needed.\n * This prevents stuttering and unnecessary GPU uploads when inference is skipped.\n *\n * @param backgroundSource - The video element to potentially upload.\n * @returns Promise that resolves after appropriate throttling and upload.\n */\n private async updateVideoBackgroundTexture(backgroundSource: HTMLVideoElement): Promise<void> {\n if (this.videoFrameInterval <= 0) return;\n\n const now = performance.now();\n const timeSinceLastUpdate = now - this.lastVideoFrameTime;\n\n if (timeSinceLastUpdate >= this.videoFrameInterval) {\n // Only upload texture when enough time has passed\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n\n this.lastVideoFrameTime = now;\n }\n }\n\n /**\n * Attempt to determine the frame rate of a video element.\n * Tries to extract frame rate from video track settings if available.\n *\n * @param video - The video element to analyze.\n * @returns The video's frame rate in frames per second, or undefined if not determinable.\n */\n private getVideoFrameRate(video: HTMLVideoElement): number | undefined {\n // Try to get frame rate from video tracks\n if (video.srcObject instanceof MediaStream) {\n const videoTrack = video.srcObject.getVideoTracks()[0];\n if (videoTrack?.getSettings) {\n return videoTrack.getSettings().frameRate;\n }\n }\n return undefined; // Fall back to default\n }\n\n /**\n * Load and initialize the renderer.\n * Currently a no-op for this renderer implementation.\n *\n * @returns Promise that resolves when loading is complete.\n */\n public async load(): Promise<void> {\n // empty\n }\n\n /**\n * Clean up resources and destroy the renderer.\n * Currently a no-op for this renderer implementation.\n */\n public destroy(): void {\n // empty\n }\n}\n"],"names":["ReplacementRenderer","WebGLRenderer","outputCanvas","config","backgroundSource","videoFPS","vertexShader","compileShader","fragmentShader","linkProgram","uBackground","input","mask","renderConfig","shouldDrawVideoBackground","now","video","videoTrack"],"mappings":";;AAWO,MAAMA,UAA4BC,EAAuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsB5E,YAAmBC,GAAiDC,GAAwB;AACxF,UAAMD,GAAcC,CAAM,GADX,KAAA,eAAAD,GAAiD,KAAA,SAAAC,GAjBpE,KAAQ,qBAAqB;AAsBzB,UAAMC,IADe,KAAK,OAAO,OACK;AAEtC,QAAIA,aAA4B,kBAAkB;AAE9C,YAAMC,IAAW,KAAK,kBAAkBD,CAAgB,KAAK;AAC7D,WAAK,qBAAqB,MAAOC;AAAA,IAAA;AAEjC,WAAK,qBAAqB;AAIzB,SAAA,iBAAiB,KAAK,oBAAoB;AAGzC,UAAAC,IAAeC,EAAc,KAAK,IAAI,KAAK,GAAG,eAAe,KAAK,iBAAiB,GACnFC,IAAiBD,EAAc,KAAK,IAAI,KAAK,GAAG,iBAAiB,KAAK,cAAc;AAE1F,SAAK,UAAUE,EAAY,KAAK,IAAIH,GAAcE,CAAc,GAC3D,KAAA,GAAG,WAAW,KAAK,OAAO,GAE1B,KAAA,kBAAkB,KAAK,OAAO,GAEnC,KAAK,uBAAuB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAStB,sBAA8B;AAC7B,WAAA,GAAG,KAAK,sBAAsB;AAAA;AAAA;AAAA;AAAA,EAI3C,KAAK,uBAAuB;AAAA;AAAA;AAAA,MAGxB,KAAK,iBAAkB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAef,yBAA+B;AAErC,UAAMJ,IADe,KAAK,OAAO,OACK;AAEtC,SAAK,oBAAoB,KAAK;AAAA,MAC1B,KAAK,GAAG;AAAA,MACRA,EAAiB;AAAA,MACjBA,EAAiB;AAAA,MACjB,MAAM;AACF,aAAK,GAAG;AAAA,UACJ,KAAK,GAAG;AAAA,UACR;AAAA,UACA,KAAK,GAAG;AAAA,UACR,KAAK,GAAG;AAAA,UACR,KAAK,GAAG;AAAA,UACRA;AAAA,QACJ;AAAA,MAAA;AAAA,IAER;AAEA,UAAMM,IAAc,KAAK,GAAG,mBAAmB,KAAK,SAAS,cAAc;AAC3E,QAAI,CAACA;AACK,YAAA,IAAI,MAAM,iDAAiD;AAErE,SAAK,GAAG,cAAc,KAAK,GAAG,QAAQ,GACtC,KAAK,GAAG,YAAY,KAAK,GAAG,YAAY,KAAK,iBAAiB,GACzD,KAAA,GAAG,UAAUA,GAAa,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAapC,MAAa,OAAOC,GAAkBC,GAAmC;AACrE,SAAK,kBAAkBD,CAAK,GAC5B,KAAK,WAAWC,CAAI;AAEd,UAAAC,IAAe,KAAK,OAAO,QAC3BC,IAA4B,CAACD,EAAa,QAC1CT,IAAmBS,EAAa;AAEtC,IAAIC,MACIV,aAA4B,mBAEtB,MAAA,KAAK,6BAA6BA,CAAgB,KAGxD,KAAK,GAAG,cAAc,KAAK,GAAG,QAAQ,GACtC,KAAK,GAAG,YAAY,KAAK,GAAG,YAAY,KAAK,iBAAiB,GAE9D,KAAK,GAAG;AAAA,MACJ,KAAK,GAAG;AAAA,MACR;AAAA,MACA,KAAK,GAAG;AAAA,MACR,KAAK,GAAG;AAAA,MACR,KAAK,GAAG;AAAA,MACRA;AAAA,IACJ,KAIH,KAAA,GAAG,WAAW,KAAK,OAAO,GAC1B,KAAA,GAAG,WAAW,KAAK,GAAG,WAAW,GAAG,KAAK,MAAM,SAAS,CAAC,GAC9D,KAAK,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUlB,MAAc,6BAA6BA,GAAmD;AACtF,QAAA,KAAK,sBAAsB,EAAG;AAE5B,UAAAW,IAAM,YAAY,IAAI;AAGxB,IAFwBA,IAAM,KAAK,sBAEZ,KAAK,uBAE5B,KAAK,GAAG,cAAc,KAAK,GAAG,QAAQ,GACtC,KAAK,GAAG,YAAY,KAAK,GAAG,YAAY,KAAK,iBAAiB,GAE9D,KAAK,GAAG;AAAA,MACJ,KAAK,GAAG;AAAA,MACR;AAAA,MACA,KAAK,GAAG;AAAA,MACR,KAAK,GAAG;AAAA,MACR,KAAK,GAAG;AAAA,MACRX;AAAA,IACJ,GAEA,KAAK,qBAAqBW;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUI,kBAAkBC,GAA6C;AAE/D,QAAAA,EAAM,qBAAqB,aAAa;AACxC,YAAMC,IAAaD,EAAM,UAAU,eAAA,EAAiB,CAAC;AACrD,UAAIC,KAAA,QAAAA,EAAY;AACL,eAAAA,EAAW,cAAc;AAAA,IACpC;AAAA,EAEG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASX,MAAa,OAAsB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5B,UAAgB;AAAA,EAAA;AAG3B;"}
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "5.7.1";
1
+ export declare const LIB_VERSION = "5.9.0";
@@ -1,4 +1,4 @@
1
- const o = "5.7.1";
1
+ const o = "5.9.0";
2
2
  export {
3
3
  o as LIB_VERSION
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sources":["../../../src/lib/version.ts"],"sourcesContent":["export const LIB_VERSION = '5.7.1';\n"],"names":["LIB_VERSION"],"mappings":"AAAO,MAAMA,IAAc;"}
1
+ {"version":3,"file":"version.js","sources":["../../../src/lib/version.ts"],"sourcesContent":["export const LIB_VERSION = '5.9.0';\n"],"names":["LIB_VERSION"],"mappings":"AAAO,MAAMA,IAAc;"}