@livekit/track-processors 0.6.1 → 0.7.2

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.
@@ -7,27 +7,30 @@ export interface VideoTransformerInitOptions extends TrackTransformerInitOptions
7
7
  }
8
8
  export interface AudioTransformerInitOptions extends TrackTransformerInitOptions {
9
9
  }
10
- export interface VideoTrackTransformer<Options extends Record<string, unknown>> extends BaseTrackTransformer<VideoTransformerInitOptions, VideoFrame> {
10
+ export interface VideoTrackTransformer<Options extends Record<string, unknown>> extends BaseTrackTransformer<VideoTransformerInitOptions, VideoFrame, TrackTransformerDestroyOptions> {
11
11
  init: (options: VideoTransformerInitOptions) => void;
12
- destroy: () => void;
12
+ destroy: (options?: TrackTransformerDestroyOptions) => void;
13
13
  restart: (options: VideoTransformerInitOptions) => void;
14
14
  transform: (frame: VideoFrame, controller: TransformStreamDefaultController) => void;
15
15
  transformer?: TransformStream;
16
16
  update: (options: Options) => void;
17
17
  }
18
- export interface AudioTrackTransformer<Options extends Record<string, unknown>> extends BaseTrackTransformer<AudioTransformerInitOptions, AudioData> {
18
+ export interface AudioTrackTransformer<Options extends Record<string, unknown>> extends BaseTrackTransformer<AudioTransformerInitOptions, AudioData, TrackTransformerDestroyOptions> {
19
19
  init: (options: AudioTransformerInitOptions) => void;
20
- destroy: () => void;
20
+ destroy: (options: TrackTransformerDestroyOptions) => void;
21
21
  restart: (options: AudioTransformerInitOptions) => void;
22
22
  transform: (frame: AudioData, controller: TransformStreamDefaultController) => void;
23
23
  transformer?: TransformStream;
24
24
  update: (options: Options) => void;
25
25
  }
26
+ export type TrackTransformerDestroyOptions = {
27
+ willProcessorRestart: boolean;
28
+ };
26
29
  export type TrackTransformer<Options extends Record<string, unknown>> = VideoTrackTransformer<Options> | AudioTrackTransformer<Options>;
27
- export interface BaseTrackTransformer<T extends TrackTransformerInitOptions, DataType extends VideoFrame | AudioData> {
28
- init: (options: T) => void;
29
- destroy: () => void;
30
- restart: (options: T) => void;
30
+ export interface BaseTrackTransformer<InitOpts extends TrackTransformerInitOptions, DataType extends VideoFrame | AudioData, DestroyOpts extends TrackTransformerDestroyOptions = TrackTransformerDestroyOptions> {
31
+ init: (options: InitOpts) => void;
32
+ destroy: (options: DestroyOpts) => void;
33
+ restart: (options: InitOpts) => void;
31
34
  transform: (frame: DataType, controller: TransformStreamDefaultController) => void;
32
35
  transformer?: TransformStream;
33
36
  }
@@ -3,5 +3,6 @@ export declare const setupWebGL: (canvas: OffscreenCanvas | HTMLCanvasElement) =
3
3
  updateMask: (mask: WebGLTexture) => void;
4
4
  setBackgroundImage: (image: ImageBitmap | null) => Promise<void>;
5
5
  setBlurRadius: (radius: number | null) => void;
6
+ setBackgroundDisabled: (disabled: boolean) => void;
6
7
  cleanup: () => void;
7
8
  } | undefined;
@@ -13,6 +13,7 @@ export declare function createCompositeProgram(gl: WebGL2RenderingContext): {
13
13
  mask: WebGLUniformLocation;
14
14
  frame: WebGLUniformLocation;
15
15
  background: WebGLUniformLocation;
16
+ disableBackground: WebGLUniformLocation;
16
17
  stepWidth: WebGLUniformLocation;
17
18
  };
18
19
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/track-processors",
3
- "version": "0.6.1",
3
+ "version": "0.7.2",
4
4
  "description": "LiveKit track processors",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -20,8 +20,8 @@
20
20
  "@mediapipe/tasks-vision": "0.10.14"
21
21
  },
22
22
  "peerDependencies": {
23
- "livekit-client": "^1.12.0 || ^2.1.0",
24
- "@types/dom-mediacapture-transform": "^0.1.9"
23
+ "@types/dom-mediacapture-transform": "^0.1.9",
24
+ "livekit-client": "^1.12.0 || ^2.1.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@changesets/cli": "^2.26.2",
@@ -38,7 +38,6 @@
38
38
  "typescript": "^5.8.3",
39
39
  "vite": "^7.0.2"
40
40
  },
41
- "packageManager": "pnpm@9.15.9+sha512.68046141893c66fad01c079231128e9afb89ef87e2691d69e4d40eee228988295fd4682181bae55b58418c3a253bde65a505ec7c5f9403ece5cc3cd37dcf2531",
42
41
  "scripts": {
43
42
  "build": "tsup --onSuccess \"tsc --declaration --emitDeclarationOnly\"",
44
43
  "build-sample": "cd example && vite build",
@@ -1,6 +1,9 @@
1
- import type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client';
2
- import { TrackTransformer } from './transformers';
1
+ import { type ProcessorOptions, type Track, type TrackProcessor } from 'livekit-client';
2
+ import { TrackTransformer, TrackTransformerDestroyOptions } from './transformers';
3
3
  import { createCanvas, waitForTrackResolution } from './utils';
4
+ import { LoggerNames, getLogger } from './logger';
5
+
6
+ type ProcessorWrapperLifecycleState = 'idle' | 'initializing' | 'running' | 'media-exhausted' | 'destroying' | 'destroyed';
4
7
 
5
8
  export interface ProcessorWrapperOptions {
6
9
  /**
@@ -10,7 +13,10 @@ export interface ProcessorWrapperOptions {
10
13
  maxFps?: number;
11
14
  }
12
15
 
13
- export default class ProcessorWrapper<TransformerOptions extends Record<string, unknown>>
16
+ export default class ProcessorWrapper<
17
+ TransformerOptions extends Record<string, unknown>,
18
+ Transformer extends TrackTransformer<TransformerOptions> = TrackTransformer<TransformerOptions>,
19
+ >
14
20
  implements TrackProcessor<Track.Kind>
15
21
  {
16
22
  /**
@@ -58,7 +64,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
58
64
 
59
65
  processedTrack?: MediaStreamTrack;
60
66
 
61
- transformer: TrackTransformer<TransformerOptions>;
67
+ transformer: Transformer;
62
68
 
63
69
  // For tracking whether we're using the stream API fallback
64
70
  private useStreamFallback = false;
@@ -77,10 +83,12 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
77
83
  // FPS control for fallback implementation
78
84
  private maxFps: number;
79
85
 
80
- private symbol?: Symbol;
86
+ private log = getLogger(LoggerNames.ProcessorWrapper);
87
+
88
+ private lifecycleState: ProcessorWrapperLifecycleState = 'idle';
81
89
 
82
90
  constructor(
83
- transformer: TrackTransformer<TransformerOptions>,
91
+ transformer: Transformer,
84
92
  name: string,
85
93
  options: ProcessorWrapperOptions = {},
86
94
  ) {
@@ -140,6 +148,8 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
140
148
  }
141
149
 
142
150
  async init(opts: ProcessorOptions<Track.Kind>): Promise<void> {
151
+ this.log.debug('Init called');
152
+ this.lifecycleState = 'initializing';
143
153
  await this.setup(opts);
144
154
 
145
155
  if (!this.canvas) {
@@ -156,6 +166,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
156
166
  } else {
157
167
  this.initStreamProcessorPath();
158
168
  }
169
+ this.lifecycleState = 'running';
159
170
  }
160
171
 
161
172
  private initStreamProcessorPath() {
@@ -168,20 +179,20 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
168
179
  const readableStream = this.processor.readable;
169
180
  const pipedStream = readableStream.pipeThrough(this.transformer!.transformer!);
170
181
 
171
- const symbol = Symbol('stream');
172
- this.symbol = symbol;
173
-
174
182
  pipedStream
175
183
  .pipeTo(this.trackGenerator.writable)
176
- // destroy processor if stream finishes
177
- .then(() => this.destroy(symbol))
184
+ // if stream finishes, the media to process is exhausted
185
+ .then(() => this.handleMediaExhausted())
178
186
  // destroy processor if stream errors - unless it's an abort error
179
187
  .catch((e) => {
180
188
  if (e instanceof DOMException && e.name === 'AbortError') {
181
- console.log('stream processor path aborted');
189
+ this.log.log('stream processor path aborted');
190
+ } else if (e instanceof DOMException && e.name === 'InvalidStateError' && e.message === 'Stream closed') {
191
+ this.log.log('stream processor underlying stream closed');
192
+ this.handleMediaExhausted();
182
193
  } else {
183
- console.error('error when trying to pipe', e);
184
- this.destroy(symbol);
194
+ this.log.error('error when trying to pipe', e);
195
+ this.destroy();
185
196
  }
186
197
  });
187
198
 
@@ -224,7 +235,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
224
235
  // @ts-ignore - The controller expects both VideoFrame & AudioData but we're only using VideoFrame
225
236
  this.transformer.transform(frame, controller);
226
237
  } catch (e) {
227
- console.error('Error in transform:', e);
238
+ this.log.error('Error in transform:', e);
228
239
  frame.close();
229
240
  }
230
241
  };
@@ -235,6 +246,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
235
246
 
236
247
  private startRenderLoop() {
237
248
  if (!this.sourceDummy || !(this.sourceDummy instanceof HTMLVideoElement)) {
249
+ this.handleMediaExhausted();
238
250
  return;
239
251
  }
240
252
 
@@ -257,12 +269,15 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
257
269
  !this.sourceDummy ||
258
270
  !(this.sourceDummy instanceof HTMLVideoElement)
259
271
  ) {
272
+ this.handleMediaExhausted();
260
273
  return;
261
274
  }
262
275
 
263
276
  if (this.sourceDummy.paused) {
264
- console.warn('Video is paused, trying to play');
265
- this.sourceDummy.play();
277
+ this.log.warn('Video is paused, trying to play');
278
+ this.sourceDummy.play().then(() => {
279
+ this.animationFrameId = requestAnimationFrame(renderLoop);
280
+ });
266
281
  return;
267
282
  }
268
283
 
@@ -298,7 +313,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
298
313
  window.location.hostname === '127.0.0.1';
299
314
 
300
315
  if (isDevelopment && now - lastFpsLog > 5000) {
301
- console.debug(
316
+ this.log.debug(
302
317
  `[${this.name}] Estimated video FPS: ${estimatedVideoFps.toFixed(
303
318
  1,
304
319
  )}, Processing at: ${(frameCount / 5).toFixed(1)} FPS`,
@@ -333,7 +348,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
333
348
  }
334
349
  }
335
350
  } catch (e) {
336
- console.error('Error in render loop:', e);
351
+ this.log.error('Error in render loop:', e);
337
352
  }
338
353
  }
339
354
  this.animationFrameId = requestAnimationFrame(renderLoop);
@@ -343,7 +358,8 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
343
358
  }
344
359
 
345
360
  async restart(opts: ProcessorOptions<Track.Kind>): Promise<void> {
346
- await this.destroy();
361
+ this.log.debug('Restart called');
362
+ await this.destroy({ willProcessorRestart: true });
347
363
  await this.init(opts);
348
364
  }
349
365
 
@@ -356,11 +372,18 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
356
372
  await this.transformer.update(options[0]);
357
373
  }
358
374
 
359
- async destroy(symbol?: Symbol) {
360
- if (symbol && this.symbol !== symbol) {
361
- // If the symbol is provided, we only destroy if it matches the current symbol
375
+ /** Called if the media pipeline no longer can read frames to process from the source media */
376
+ private async handleMediaExhausted() {
377
+ this.log.debug('Media was exhausted from source');
378
+ if (this.lifecycleState !== 'running') {
362
379
  return;
363
380
  }
381
+ this.lifecycleState = 'media-exhausted'
382
+ await this.cleanup();
383
+ }
384
+
385
+ /** Tears down the media stack logic initialized in initStreamProcessorPath / initFallbackPath */
386
+ private async cleanup() {
364
387
  if (this.useStreamFallback) {
365
388
  this.processingEnabled = false;
366
389
  if (this.animationFrameId) {
@@ -372,9 +395,28 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
372
395
  }
373
396
  this.capturedStream?.getTracks().forEach((track) => track.stop());
374
397
  } else {
398
+ // NOTE: closing writableControl below terminates the stream in initStreamProcessorPath /
399
+ // calls the .then(...) which calls this.handleMediaExhausted
375
400
  await this.processor?.writableControl?.close();
376
401
  this.trackGenerator?.stop();
377
402
  }
378
- await this.transformer.destroy();
403
+ }
404
+
405
+ async destroy(transformerDestroyOptions: TrackTransformerDestroyOptions = { willProcessorRestart: false }) {
406
+ this.log.debug(`Destroy called - lifecycleState=${this.lifecycleState}, transformerDestroyOptions=${JSON.stringify(transformerDestroyOptions)}`);
407
+ switch (this.lifecycleState) {
408
+ case 'running':
409
+ case 'media-exhausted':
410
+ this.lifecycleState = 'destroying';
411
+
412
+ await this.cleanup();
413
+
414
+ await this.transformer.destroy(transformerDestroyOptions);
415
+ this.lifecycleState = 'destroyed';
416
+ break;
417
+
418
+ default:
419
+ break;
420
+ }
379
421
  }
380
422
  }
package/src/index.ts CHANGED
@@ -14,6 +14,9 @@ export {
14
14
  BackgroundTransformer,
15
15
  type ProcessorWrapperOptions,
16
16
  };
17
+ export * from './logger';
18
+
19
+ const DEFAULT_BLUR_RADIUS = 10;
17
20
 
18
21
  /**
19
22
  * Determines if the current browser supports background processors
@@ -27,16 +30,217 @@ export const supportsBackgroundProcessors = () =>
27
30
  export const supportsModernBackgroundProcessors = () =>
28
31
  BackgroundTransformer.isSupported && ProcessorWrapper.hasModernApiSupport;
29
32
 
30
- export interface BackgroundProcessorOptions extends ProcessorWrapperOptions {
33
+ type SwitchBackgroundProcessorBackgroundBlurOptions = {
34
+ mode: 'background-blur';
35
+ /** If unspecified, defaults to {@link DEFAULT_BLUR_RADIUS} */
31
36
  blurRadius?: number;
32
- imagePath?: string;
37
+ };
38
+
39
+ type SwitchBackgroundProcessorVirtualBackgroundOptions = {
40
+ mode: 'virtual-background';
41
+ imagePath: string;
42
+ };
43
+
44
+ type SwitchBackgroundProcessorDisabledOptions = {
45
+ mode: 'disabled';
46
+ };
47
+
48
+ export type SwitchBackgroundProcessorOptions =
49
+ | SwitchBackgroundProcessorDisabledOptions
50
+ | SwitchBackgroundProcessorBackgroundBlurOptions
51
+ | SwitchBackgroundProcessorVirtualBackgroundOptions
52
+
53
+ type BackgroundProcessorCommonOptions = ProcessorWrapperOptions & {
33
54
  segmenterOptions?: SegmenterOptions;
34
55
  assetPaths?: { tasksVisionFileSet?: string; modelAssetPath?: string };
35
56
  onFrameProcessed?: (stats: FrameProcessingStats) => void;
57
+ };
58
+
59
+ type BackgroundProcessorModeOptions = BackgroundProcessorCommonOptions & SwitchBackgroundProcessorOptions;
60
+ type BackgroundProcessorLegacyOptions = BackgroundProcessorCommonOptions & {
61
+ mode?: never;
62
+ blurRadius?: number;
63
+ imagePath?: string;
64
+ };
65
+
66
+ export type BackgroundProcessorOptions =
67
+ | BackgroundProcessorModeOptions
68
+ | BackgroundProcessorLegacyOptions;
69
+
70
+ /**
71
+ * A {@link ProcessorWrapper} that supports applying effects to the background of a video track.
72
+ *
73
+ * For more info and for how to construct this object, see {@link BackgroundProcessor}.
74
+ */
75
+ export class BackgroundProcessorWrapper extends ProcessorWrapper<BackgroundOptions, BackgroundTransformer> {
76
+ get mode(): BackgroundProcessorModeOptions['mode'] | 'legacy' {
77
+ const options = this.transformer.options;
78
+
79
+ if (options.backgroundDisabled) {
80
+ return 'disabled';
81
+ }
82
+
83
+ if (typeof options.imagePath === 'string' && typeof options.blurRadius === 'undefined') {
84
+ return 'virtual-background';
85
+ }
86
+
87
+ if (typeof options.imagePath === 'undefined') {
88
+ return 'background-blur';
89
+ }
90
+
91
+ return 'legacy';
92
+ }
93
+
94
+ async switchTo(options: SwitchBackgroundProcessorOptions) {
95
+ switch (options.mode) {
96
+ case 'background-blur':
97
+ await this.updateTransformerOptions({
98
+ imagePath: undefined,
99
+ blurRadius: options.blurRadius ?? DEFAULT_BLUR_RADIUS,
100
+ backgroundDisabled: false,
101
+ });
102
+ break;
103
+ case 'virtual-background':
104
+ await this.updateTransformerOptions({
105
+ imagePath: options.imagePath,
106
+ blurRadius: undefined,
107
+ backgroundDisabled: false,
108
+ });
109
+ break;
110
+ case 'disabled':
111
+ await this.updateTransformerOptions({ imagePath: undefined, backgroundDisabled: true });
112
+ break;
113
+ }
114
+ }
36
115
  }
37
116
 
117
+ /**
118
+ * Instantiates a background processor that supports blurring the background of a user's local
119
+ * video or replacing the user's background with a virtual background image, and supports switching
120
+ * the active mode later on the fly to avoid visual artifacts.
121
+ *
122
+ * @example
123
+ * const camTrack = currentRoom.localParticipant.getTrackPublication(Track.Source.Camera)!.track as LocalVideoTrack;
124
+ * const processor = BackgroundProcessor({ mode: 'background-blur', blurRadius: 10 });
125
+ * camTrack.setProcessor(processor);
126
+ *
127
+ * // Change to background image:
128
+ * processor.switchToVirtualBackground('path/to/image.png');
129
+ * // Change back to background blur:
130
+ * processor.switchToBackgroundBlur(10);
131
+ */
132
+ export const BackgroundProcessor = (
133
+ options: BackgroundProcessorOptions,
134
+ name = 'background-processor',
135
+ ) => {
136
+ const isTransformerSupported = BackgroundTransformer.isSupported;
137
+ const isProcessorSupported = ProcessorWrapper.isSupported;
138
+
139
+ if (!isTransformerSupported) {
140
+ throw new Error('Background transformer is not supported in this browser');
141
+ }
142
+
143
+ if (!isProcessorSupported) {
144
+ throw new Error(
145
+ 'Neither MediaStreamTrackProcessor nor canvas.captureStream() fallback is supported in this browser',
146
+ );
147
+ }
148
+
149
+ // Extract transformer-specific options and processor options
150
+ let transformer, processorOpts;
151
+ switch (options.mode) {
152
+ case 'background-blur': {
153
+ const {
154
+ // eslint-disable-next-line no-unused-vars
155
+ mode,
156
+ blurRadius = DEFAULT_BLUR_RADIUS,
157
+ segmenterOptions,
158
+ assetPaths,
159
+ onFrameProcessed,
160
+ ...rest
161
+ } = options;
162
+
163
+ processorOpts = rest;
164
+ transformer = new BackgroundTransformer({
165
+ blurRadius,
166
+ segmenterOptions,
167
+ assetPaths,
168
+ onFrameProcessed,
169
+ });
170
+ break;
171
+ }
172
+
173
+ case 'virtual-background': {
174
+ const {
175
+ // eslint-disable-next-line no-unused-vars
176
+ mode,
177
+ imagePath,
178
+ segmenterOptions,
179
+ assetPaths,
180
+ onFrameProcessed,
181
+ ...rest
182
+ } = options;
183
+
184
+ processorOpts = rest;
185
+ transformer = new BackgroundTransformer({
186
+ imagePath,
187
+ segmenterOptions,
188
+ assetPaths,
189
+ onFrameProcessed,
190
+ });
191
+ break;
192
+ }
193
+
194
+ case 'disabled': {
195
+ const {
196
+ segmenterOptions,
197
+ assetPaths,
198
+ onFrameProcessed,
199
+ ...rest
200
+ } = options;
201
+
202
+ processorOpts = rest;
203
+ transformer = new BackgroundTransformer({
204
+ segmenterOptions,
205
+ assetPaths,
206
+ onFrameProcessed,
207
+ });
208
+ break;
209
+ }
210
+
211
+ default: {
212
+ const {
213
+ blurRadius,
214
+ imagePath,
215
+ segmenterOptions,
216
+ assetPaths,
217
+ onFrameProcessed,
218
+ ...rest
219
+ } = options;
220
+
221
+ processorOpts = rest;
222
+ transformer = new BackgroundTransformer({
223
+ blurRadius,
224
+ imagePath,
225
+ segmenterOptions,
226
+ assetPaths,
227
+ onFrameProcessed,
228
+ });
229
+ break;
230
+ }
231
+ }
232
+
233
+ const processor = new BackgroundProcessorWrapper(transformer, name, processorOpts);
234
+
235
+ return processor;
236
+ };
237
+
238
+ /**
239
+ * Instantiates a background processor that is configured in blur mode.
240
+ * @deprecated Use `BackgroundProcessor({ mode: 'background-blur', blurRadius: 10, ... })` instead.
241
+ */
38
242
  export const BackgroundBlur = (
39
- blurRadius: number = 10,
243
+ blurRadius: number = DEFAULT_BLUR_RADIUS,
40
244
  segmenterOptions?: SegmenterOptions,
41
245
  onFrameProcessed?: (stats: FrameProcessingStats) => void,
42
246
  processorOptions?: ProcessorWrapperOptions,
@@ -52,6 +256,10 @@ export const BackgroundBlur = (
52
256
  );
53
257
  };
54
258
 
259
+ /**
260
+ * Instantiates a background processor that is configured in virtual background mode.
261
+ * @deprecated Use `BackgroundProcessor({ mode: 'virtual-background', imagePath: '...', ... })` instead.
262
+ */
55
263
  export const VirtualBackground = (
56
264
  imagePath: string,
57
265
  segmenterOptions?: SegmenterOptions,
@@ -69,42 +277,3 @@ export const VirtualBackground = (
69
277
  );
70
278
  };
71
279
 
72
- export const BackgroundProcessor = (
73
- options: BackgroundProcessorOptions,
74
- name = 'background-processor',
75
- ) => {
76
- const isTransformerSupported = BackgroundTransformer.isSupported;
77
- const isProcessorSupported = ProcessorWrapper.isSupported;
78
-
79
- if (!isTransformerSupported) {
80
- throw new Error('Background transformer is not supported in this browser');
81
- }
82
-
83
- if (!isProcessorSupported) {
84
- throw new Error(
85
- 'Neither MediaStreamTrackProcessor nor canvas.captureStream() fallback is supported in this browser',
86
- );
87
- }
88
-
89
- // Extract transformer-specific options and processor options
90
- const {
91
- blurRadius,
92
- imagePath,
93
- segmenterOptions,
94
- assetPaths,
95
- onFrameProcessed,
96
- ...processorOpts
97
- } = options;
98
-
99
- const transformer = new BackgroundTransformer({
100
- blurRadius,
101
- imagePath,
102
- segmenterOptions,
103
- assetPaths,
104
- onFrameProcessed,
105
- });
106
-
107
- const processor = new ProcessorWrapper(transformer, name, processorOpts);
108
-
109
- return processor;
110
- };
package/src/logger.ts ADDED
@@ -0,0 +1,74 @@
1
+ import { getLogger as clientGetLogger } from 'livekit-client';
2
+
3
+ export enum LogLevel {
4
+ trace = 0,
5
+ debug = 1,
6
+ info = 2,
7
+ warn = 3,
8
+ error = 4,
9
+ silent = 5,
10
+ }
11
+
12
+ export enum LoggerNames {
13
+ ProcessorWrapper = 'livekit-processor-wrapper',
14
+ BackgroundProcessor = 'livekit-background-processor',
15
+ WebGl = 'livekit-track-processor-web-gl',
16
+ }
17
+
18
+ type LogLevelString = keyof typeof LogLevel;
19
+
20
+ export type StructuredLogger = ReturnType<typeof clientGetLogger>;
21
+
22
+ let livekitLogger = getLogger('livekit');
23
+ const livekitLoggers = Object.values(LoggerNames).map((name) => getLogger(name));
24
+
25
+ livekitLogger.setDefaultLevel(LogLevel.info);
26
+
27
+ export default livekitLogger as StructuredLogger;
28
+
29
+ /**
30
+ * @internal
31
+ */
32
+ export function getLogger(name: string): StructuredLogger {
33
+ return clientGetLogger(name);
34
+ }
35
+
36
+ export function setLogLevel(level: LogLevel | LogLevelString, loggerName?: LoggerNames) {
37
+ if (loggerName) {
38
+ getLogger(loggerName).setLevel(level);
39
+ } else {
40
+ for (const logger of livekitLoggers) {
41
+ logger.setLevel(level);
42
+ }
43
+ }
44
+ }
45
+
46
+ export type LogExtension = (level: LogLevel, msg: string, context?: object) => void;
47
+
48
+ /**
49
+ * use this to hook into the logging function to allow sending internal livekit logs to third party services
50
+ * if set, the browser logs will lose their stacktrace information (see https://github.com/pimterry/loglevel#writing-plugins)
51
+ */
52
+ export function setLogExtension(extension: LogExtension, logger?: StructuredLogger) {
53
+ const loggers = logger ? [logger] : livekitLoggers;
54
+
55
+ loggers.forEach((logR) => {
56
+ const originalFactory = logR.methodFactory;
57
+
58
+ logR.methodFactory = (methodName, configLevel, loggerName) => {
59
+ const rawMethod = originalFactory(methodName, configLevel, loggerName);
60
+
61
+ const logLevel = LogLevel[methodName as LogLevelString];
62
+ const needLog = logLevel >= configLevel && logLevel < LogLevel.silent;
63
+
64
+ return (msg, context?: [msg: string, context: object]) => {
65
+ if (context) rawMethod(msg, context);
66
+ else rawMethod(msg);
67
+ if (needLog) {
68
+ extension(logLevel, msg, context);
69
+ }
70
+ };
71
+ };
72
+ logR.setLevel(logR.getLevel());
73
+ });
74
+ }