@livekit/track-processors 0.6.0 → 0.7.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/README.md +31 -9
- package/dist/index.js +312 -99
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +311 -98
- package/dist/index.mjs.map +1 -1
- package/dist/src/ProcessorWrapper.d.ts +12 -7
- package/dist/src/index.d.ts +51 -5
- package/dist/src/logger.d.ts +29 -0
- package/dist/src/transformers/BackgroundTransformer.d.ts +9 -4
- package/dist/src/transformers/VideoTransformer.d.ts +1 -1
- package/dist/src/transformers/types.d.ts +11 -8
- package/dist/src/webgl/index.d.ts +1 -0
- package/dist/src/webgl/shader-programs/compositeShader.d.ts +1 -0
- package/package.json +3 -3
- package/src/ProcessorWrapper.ts +63 -23
- package/src/index.ts +206 -42
- package/src/logger.ts +74 -0
- package/src/transformers/BackgroundTransformer.ts +40 -23
- package/src/transformers/VideoTransformer.ts +1 -1
- package/src/transformers/types.ts +11 -8
- package/src/webgl/index.ts +24 -11
- package/src/webgl/shader-programs/compositeShader.ts +18 -13
- package/src/webgl/utils.ts +6 -2
|
@@ -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<
|
|
28
|
-
init: (options:
|
|
29
|
-
destroy: () => void;
|
|
30
|
-
restart: (options:
|
|
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.
|
|
3
|
+
"version": "0.7.0",
|
|
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
|
-
"
|
|
24
|
-
"
|
|
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",
|
package/src/ProcessorWrapper.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type
|
|
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<
|
|
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:
|
|
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
|
|
86
|
+
private log = getLogger(LoggerNames.ProcessorWrapper);
|
|
87
|
+
|
|
88
|
+
private lifecycleState: ProcessorWrapperLifecycleState = 'idle';
|
|
81
89
|
|
|
82
90
|
constructor(
|
|
83
|
-
transformer:
|
|
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
|
-
//
|
|
177
|
-
.then(() => this.
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
this.destroy(
|
|
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
|
-
|
|
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,11 +269,12 @@ 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
|
-
|
|
277
|
+
this.log.warn('Video is paused, trying to play');
|
|
265
278
|
this.sourceDummy.play();
|
|
266
279
|
return;
|
|
267
280
|
}
|
|
@@ -298,7 +311,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
|
|
|
298
311
|
window.location.hostname === '127.0.0.1';
|
|
299
312
|
|
|
300
313
|
if (isDevelopment && now - lastFpsLog > 5000) {
|
|
301
|
-
|
|
314
|
+
this.log.debug(
|
|
302
315
|
`[${this.name}] Estimated video FPS: ${estimatedVideoFps.toFixed(
|
|
303
316
|
1,
|
|
304
317
|
)}, Processing at: ${(frameCount / 5).toFixed(1)} FPS`,
|
|
@@ -333,7 +346,7 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
|
|
|
333
346
|
}
|
|
334
347
|
}
|
|
335
348
|
} catch (e) {
|
|
336
|
-
|
|
349
|
+
this.log.error('Error in render loop:', e);
|
|
337
350
|
}
|
|
338
351
|
}
|
|
339
352
|
this.animationFrameId = requestAnimationFrame(renderLoop);
|
|
@@ -343,7 +356,8 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
|
|
|
343
356
|
}
|
|
344
357
|
|
|
345
358
|
async restart(opts: ProcessorOptions<Track.Kind>): Promise<void> {
|
|
346
|
-
|
|
359
|
+
this.log.debug('Restart called');
|
|
360
|
+
await this.destroy({ willProcessorRestart: true });
|
|
347
361
|
await this.init(opts);
|
|
348
362
|
}
|
|
349
363
|
|
|
@@ -356,11 +370,18 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
|
|
|
356
370
|
await this.transformer.update(options[0]);
|
|
357
371
|
}
|
|
358
372
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
373
|
+
/** Called if the media pipeline no longer can read frames to process from the source media */
|
|
374
|
+
private async handleMediaExhausted() {
|
|
375
|
+
this.log.debug('Media was exhausted from source');
|
|
376
|
+
if (this.lifecycleState !== 'running') {
|
|
362
377
|
return;
|
|
363
378
|
}
|
|
379
|
+
this.lifecycleState = 'media-exhausted'
|
|
380
|
+
await this.cleanup();
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Tears down the media stack logic initialized in initStreamProcessorPath / initFallbackPath */
|
|
384
|
+
private async cleanup() {
|
|
364
385
|
if (this.useStreamFallback) {
|
|
365
386
|
this.processingEnabled = false;
|
|
366
387
|
if (this.animationFrameId) {
|
|
@@ -372,9 +393,28 @@ export default class ProcessorWrapper<TransformerOptions extends Record<string,
|
|
|
372
393
|
}
|
|
373
394
|
this.capturedStream?.getTracks().forEach((track) => track.stop());
|
|
374
395
|
} else {
|
|
396
|
+
// NOTE: closing writableControl below terminates the stream in initStreamProcessorPath /
|
|
397
|
+
// calls the .then(...) which calls this.handleMediaExhausted
|
|
375
398
|
await this.processor?.writableControl?.close();
|
|
376
399
|
this.trackGenerator?.stop();
|
|
377
400
|
}
|
|
378
|
-
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async destroy(transformerDestroyOptions: TrackTransformerDestroyOptions = { willProcessorRestart: false }) {
|
|
404
|
+
this.log.debug(`Destroy called - lifecycleState=${this.lifecycleState}, transformerDestroyOptions=${JSON.stringify(transformerDestroyOptions)}`);
|
|
405
|
+
switch (this.lifecycleState) {
|
|
406
|
+
case 'running':
|
|
407
|
+
case 'media-exhausted':
|
|
408
|
+
this.lifecycleState = 'destroying';
|
|
409
|
+
|
|
410
|
+
await this.cleanup();
|
|
411
|
+
|
|
412
|
+
await this.transformer.destroy(transformerDestroyOptions);
|
|
413
|
+
this.lifecycleState = 'destroyed';
|
|
414
|
+
break;
|
|
415
|
+
|
|
416
|
+
default:
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
379
419
|
}
|
|
380
420
|
}
|
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,212 @@ export const supportsBackgroundProcessors = () =>
|
|
|
27
30
|
export const supportsModernBackgroundProcessors = () =>
|
|
28
31
|
BackgroundTransformer.isSupported && ProcessorWrapper.hasModernApiSupport;
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
type SwitchBackgroundProcessorBackgroundBlurOptions = {
|
|
34
|
+
mode: 'background-blur';
|
|
35
|
+
/** If unspecified, defaults to {@link DEFAULT_BLUR_RADIUS} */
|
|
31
36
|
blurRadius?: number;
|
|
32
|
-
|
|
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
|
+
class BackgroundProcessorWrapper extends ProcessorWrapper<BackgroundOptions, BackgroundTransformer> {
|
|
71
|
+
get mode(): BackgroundProcessorModeOptions['mode'] | 'legacy' {
|
|
72
|
+
const options = this.transformer.options;
|
|
73
|
+
|
|
74
|
+
if (options.backgroundDisabled) {
|
|
75
|
+
return 'disabled';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeof options.imagePath === 'string' && typeof options.blurRadius === 'undefined') {
|
|
79
|
+
return 'virtual-background';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (typeof options.imagePath === 'undefined') {
|
|
83
|
+
return 'background-blur';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return 'legacy';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async switchTo(options: SwitchBackgroundProcessorOptions) {
|
|
90
|
+
switch (options.mode) {
|
|
91
|
+
case 'background-blur':
|
|
92
|
+
await this.updateTransformerOptions({
|
|
93
|
+
imagePath: undefined,
|
|
94
|
+
blurRadius: options.blurRadius ?? DEFAULT_BLUR_RADIUS,
|
|
95
|
+
backgroundDisabled: false,
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
case 'virtual-background':
|
|
99
|
+
await this.updateTransformerOptions({
|
|
100
|
+
imagePath: options.imagePath,
|
|
101
|
+
blurRadius: undefined,
|
|
102
|
+
backgroundDisabled: false,
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
case 'disabled':
|
|
106
|
+
await this.updateTransformerOptions({ imagePath: undefined, backgroundDisabled: true });
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
36
110
|
}
|
|
37
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Instantiates a background processor that supports blurring the background of a user's local
|
|
114
|
+
* video or replacing the user's background with a virtual background image, and supports switching
|
|
115
|
+
* the active mode later on the fly to avoid visual artifacts.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* const camTrack = currentRoom.localParticipant.getTrackPublication(Track.Source.Camera)!.track as LocalVideoTrack;
|
|
119
|
+
* const processor = BackgroundProcessor({ mode: 'background-blur', blurRadius: 10 });
|
|
120
|
+
* camTrack.setProcessor(processor);
|
|
121
|
+
*
|
|
122
|
+
* // Change to background image:
|
|
123
|
+
* processor.switchToVirtualBackground('path/to/image.png');
|
|
124
|
+
* // Change back to background blur:
|
|
125
|
+
* processor.switchToBackgroundBlur(10);
|
|
126
|
+
*/
|
|
127
|
+
export const BackgroundProcessor = (
|
|
128
|
+
options: BackgroundProcessorOptions,
|
|
129
|
+
name = 'background-processor',
|
|
130
|
+
) => {
|
|
131
|
+
const isTransformerSupported = BackgroundTransformer.isSupported;
|
|
132
|
+
const isProcessorSupported = ProcessorWrapper.isSupported;
|
|
133
|
+
|
|
134
|
+
if (!isTransformerSupported) {
|
|
135
|
+
throw new Error('Background transformer is not supported in this browser');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!isProcessorSupported) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
'Neither MediaStreamTrackProcessor nor canvas.captureStream() fallback is supported in this browser',
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Extract transformer-specific options and processor options
|
|
145
|
+
let transformer, processorOpts;
|
|
146
|
+
switch (options.mode) {
|
|
147
|
+
case 'background-blur': {
|
|
148
|
+
const {
|
|
149
|
+
// eslint-disable-next-line no-unused-vars
|
|
150
|
+
mode,
|
|
151
|
+
blurRadius = DEFAULT_BLUR_RADIUS,
|
|
152
|
+
segmenterOptions,
|
|
153
|
+
assetPaths,
|
|
154
|
+
onFrameProcessed,
|
|
155
|
+
...rest
|
|
156
|
+
} = options;
|
|
157
|
+
|
|
158
|
+
processorOpts = rest;
|
|
159
|
+
transformer = new BackgroundTransformer({
|
|
160
|
+
blurRadius,
|
|
161
|
+
segmenterOptions,
|
|
162
|
+
assetPaths,
|
|
163
|
+
onFrameProcessed,
|
|
164
|
+
});
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case 'virtual-background': {
|
|
169
|
+
const {
|
|
170
|
+
// eslint-disable-next-line no-unused-vars
|
|
171
|
+
mode,
|
|
172
|
+
imagePath,
|
|
173
|
+
segmenterOptions,
|
|
174
|
+
assetPaths,
|
|
175
|
+
onFrameProcessed,
|
|
176
|
+
...rest
|
|
177
|
+
} = options;
|
|
178
|
+
|
|
179
|
+
processorOpts = rest;
|
|
180
|
+
transformer = new BackgroundTransformer({
|
|
181
|
+
imagePath,
|
|
182
|
+
segmenterOptions,
|
|
183
|
+
assetPaths,
|
|
184
|
+
onFrameProcessed,
|
|
185
|
+
});
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case 'disabled': {
|
|
190
|
+
const {
|
|
191
|
+
segmenterOptions,
|
|
192
|
+
assetPaths,
|
|
193
|
+
onFrameProcessed,
|
|
194
|
+
...rest
|
|
195
|
+
} = options;
|
|
196
|
+
|
|
197
|
+
processorOpts = rest;
|
|
198
|
+
transformer = new BackgroundTransformer({
|
|
199
|
+
segmenterOptions,
|
|
200
|
+
assetPaths,
|
|
201
|
+
onFrameProcessed,
|
|
202
|
+
});
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
default: {
|
|
207
|
+
const {
|
|
208
|
+
blurRadius,
|
|
209
|
+
imagePath,
|
|
210
|
+
segmenterOptions,
|
|
211
|
+
assetPaths,
|
|
212
|
+
onFrameProcessed,
|
|
213
|
+
...rest
|
|
214
|
+
} = options;
|
|
215
|
+
|
|
216
|
+
processorOpts = rest;
|
|
217
|
+
transformer = new BackgroundTransformer({
|
|
218
|
+
blurRadius,
|
|
219
|
+
imagePath,
|
|
220
|
+
segmenterOptions,
|
|
221
|
+
assetPaths,
|
|
222
|
+
onFrameProcessed,
|
|
223
|
+
});
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const processor = new BackgroundProcessorWrapper(transformer, name, processorOpts);
|
|
229
|
+
|
|
230
|
+
return processor;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Instantiates a background processor that is configured in blur mode.
|
|
235
|
+
* @deprecated Use `BackgroundProcessor({ mode: 'background-blur', blurRadius: 10, ... })` instead.
|
|
236
|
+
*/
|
|
38
237
|
export const BackgroundBlur = (
|
|
39
|
-
blurRadius: number =
|
|
238
|
+
blurRadius: number = DEFAULT_BLUR_RADIUS,
|
|
40
239
|
segmenterOptions?: SegmenterOptions,
|
|
41
240
|
onFrameProcessed?: (stats: FrameProcessingStats) => void,
|
|
42
241
|
processorOptions?: ProcessorWrapperOptions,
|
|
@@ -52,6 +251,10 @@ export const BackgroundBlur = (
|
|
|
52
251
|
);
|
|
53
252
|
};
|
|
54
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Instantiates a background processor that is configured in virtual background mode.
|
|
256
|
+
* @deprecated Use `BackgroundProcessor({ mode: 'virtual-background', imagePath: '...', ... })` instead.
|
|
257
|
+
*/
|
|
55
258
|
export const VirtualBackground = (
|
|
56
259
|
imagePath: string,
|
|
57
260
|
segmenterOptions?: SegmenterOptions,
|
|
@@ -69,42 +272,3 @@ export const VirtualBackground = (
|
|
|
69
272
|
);
|
|
70
273
|
};
|
|
71
274
|
|
|
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) {
|
|
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
|
+
}
|