@voltkit-rtc/client 0.7.3 → 0.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.
- package/CHANGELOG.md +86 -0
- package/README.md +346 -2
- package/dist/cjs/adaptive.d.ts +104 -0
- package/dist/cjs/adaptive.d.ts.map +1 -0
- package/dist/cjs/adaptive.js +263 -0
- package/dist/cjs/adaptive.js.map +1 -0
- package/dist/cjs/devices.d.ts +42 -0
- package/dist/cjs/devices.d.ts.map +1 -0
- package/dist/cjs/devices.js +56 -0
- package/dist/cjs/devices.js.map +1 -0
- package/dist/cjs/diagnostics.d.ts +65 -0
- package/dist/cjs/diagnostics.d.ts.map +1 -0
- package/dist/cjs/diagnostics.js +69 -0
- package/dist/cjs/diagnostics.js.map +1 -0
- package/dist/cjs/effects-core.d.ts +95 -0
- package/dist/cjs/effects-core.d.ts.map +1 -0
- package/dist/cjs/effects-core.js +154 -0
- package/dist/cjs/effects-core.js.map +1 -0
- package/dist/cjs/effects-gl.d.ts +79 -0
- package/dist/cjs/effects-gl.d.ts.map +1 -0
- package/dist/cjs/effects-gl.js +351 -0
- package/dist/cjs/effects-gl.js.map +1 -0
- package/dist/cjs/fallback.d.ts +81 -0
- package/dist/cjs/fallback.d.ts.map +1 -0
- package/dist/cjs/fallback.js +148 -0
- package/dist/cjs/fallback.js.map +1 -0
- package/dist/cjs/health.d.ts +70 -0
- package/dist/cjs/health.d.ts.map +1 -0
- package/dist/cjs/health.js +116 -0
- package/dist/cjs/health.js.map +1 -0
- package/dist/cjs/index.d.ts +396 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1367 -81
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/link.d.ts +99 -0
- package/dist/cjs/link.d.ts.map +1 -0
- package/dist/cjs/link.js +219 -0
- package/dist/cjs/link.js.map +1 -0
- package/dist/cjs/noise-suppression.d.ts +44 -0
- package/dist/cjs/noise-suppression.d.ts.map +1 -0
- package/dist/cjs/noise-suppression.js +103 -0
- package/dist/cjs/noise-suppression.js.map +1 -0
- package/dist/cjs/preflight.d.ts +79 -0
- package/dist/cjs/preflight.d.ts.map +1 -0
- package/dist/cjs/preflight.js +159 -0
- package/dist/cjs/preflight.js.map +1 -0
- package/dist/cjs/processors.d.ts +34 -0
- package/dist/cjs/processors.d.ts.map +1 -0
- package/dist/cjs/processors.js +10 -0
- package/dist/cjs/processors.js.map +1 -0
- package/dist/cjs/speakers.d.ts +34 -0
- package/dist/cjs/speakers.d.ts.map +1 -0
- package/dist/cjs/speakers.js +71 -0
- package/dist/cjs/speakers.js.map +1 -0
- package/dist/cjs/video-effects.d.ts +93 -0
- package/dist/cjs/video-effects.d.ts.map +1 -0
- package/dist/cjs/video-effects.js +493 -0
- package/dist/cjs/video-effects.js.map +1 -0
- package/dist/esm/adaptive.d.ts +104 -0
- package/dist/esm/adaptive.d.ts.map +1 -0
- package/dist/esm/adaptive.js +256 -0
- package/dist/esm/adaptive.js.map +1 -0
- package/dist/esm/devices.d.ts +42 -0
- package/dist/esm/devices.d.ts.map +1 -0
- package/dist/esm/devices.js +50 -0
- package/dist/esm/devices.js.map +1 -0
- package/dist/esm/diagnostics.d.ts +65 -0
- package/dist/esm/diagnostics.d.ts.map +1 -0
- package/dist/esm/diagnostics.js +65 -0
- package/dist/esm/diagnostics.js.map +1 -0
- package/dist/esm/effects-core.d.ts +95 -0
- package/dist/esm/effects-core.d.ts.map +1 -0
- package/dist/esm/effects-core.js +142 -0
- package/dist/esm/effects-core.js.map +1 -0
- package/dist/esm/effects-gl.d.ts +79 -0
- package/dist/esm/effects-gl.d.ts.map +1 -0
- package/dist/esm/effects-gl.js +347 -0
- package/dist/esm/effects-gl.js.map +1 -0
- package/dist/esm/fallback.d.ts +81 -0
- package/dist/esm/fallback.d.ts.map +1 -0
- package/dist/esm/fallback.js +142 -0
- package/dist/esm/fallback.js.map +1 -0
- package/dist/esm/health.d.ts +70 -0
- package/dist/esm/health.d.ts.map +1 -0
- package/dist/esm/health.js +111 -0
- package/dist/esm/health.js.map +1 -0
- package/dist/esm/index.d.ts +396 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1365 -81
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/link.d.ts +99 -0
- package/dist/esm/link.d.ts.map +1 -0
- package/dist/esm/link.js +214 -0
- package/dist/esm/link.js.map +1 -0
- package/dist/esm/noise-suppression.d.ts +44 -0
- package/dist/esm/noise-suppression.d.ts.map +1 -0
- package/dist/esm/noise-suppression.js +99 -0
- package/dist/esm/noise-suppression.js.map +1 -0
- package/dist/esm/preflight.d.ts +79 -0
- package/dist/esm/preflight.d.ts.map +1 -0
- package/dist/esm/preflight.js +153 -0
- package/dist/esm/preflight.js.map +1 -0
- package/dist/esm/processors.d.ts +34 -0
- package/dist/esm/processors.d.ts.map +1 -0
- package/dist/esm/processors.js +9 -0
- package/dist/esm/processors.js.map +1 -0
- package/dist/esm/speakers.d.ts +34 -0
- package/dist/esm/speakers.d.ts.map +1 -0
- package/dist/esm/speakers.js +67 -0
- package/dist/esm/speakers.js.map +1 -0
- package/dist/esm/video-effects.d.ts +93 -0
- package/dist/esm/video-effects.d.ts.map +1 -0
- package/dist/esm/video-effects.js +488 -0
- package/dist/esm/video-effects.js.map +1 -0
- package/package.json +31 -1
- package/src/adaptive.ts +295 -0
- package/src/devices.ts +70 -0
- package/src/diagnostics.ts +94 -0
- package/src/effects-core.ts +177 -0
- package/src/effects-gl.ts +384 -0
- package/src/fallback.ts +169 -0
- package/src/health.ts +127 -0
- package/src/index.ts +1364 -81
- package/src/link.ts +255 -0
- package/src/noise-suppression.ts +135 -0
- package/src/preflight.ts +191 -0
- package/src/processors.ts +35 -0
- package/src/speakers.ts +66 -0
- package/src/video-effects.ts +552 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_LANDSCAPE_MODEL = exports.DEFAULT_SEGMENTER_MODEL = exports.MEDIAPIPE_VERSION = void 0;
|
|
4
|
+
exports.isVideoEffectsSupported = isVideoEffectsSupported;
|
|
5
|
+
exports.createVideoEffects = createVideoEffects;
|
|
6
|
+
/**
|
|
7
|
+
* Virtual background and beauty for the camera (docs/implementation-progress/RTC_PERF_PROGRESS.md P3-5): a blurred or
|
|
8
|
+
* replaced background and skin smoothing, as LiveKit's track processors, Agora's and ZEGOCLOUD's virtual background and
|
|
9
|
+
* beauty extensions, and Tencent TRTC's video effect plugin do -- in the browser, on the GPU.
|
|
10
|
+
*
|
|
11
|
+
* Imported on its own (`@voltkit-rtc/client/video-effects`), so an app that does not use it never ships it:
|
|
12
|
+
*
|
|
13
|
+
* import { createVideoEffects } from '@voltkit-rtc/client/video-effects';
|
|
14
|
+
* const effects = createVideoEffects({ background: 'blur', beauty: true });
|
|
15
|
+
* await client.setVideoProcessor(effects);
|
|
16
|
+
* await effects.update({ background: { image: '/office.jpg' } }); // live, no pause for viewers
|
|
17
|
+
*
|
|
18
|
+
* Finding the person takes Google's MediaPipe selfie segmenter: install `@mediapipe/tasks-vision@0.10.35` next to the
|
|
19
|
+
* SDK (an optional peer dependency, 0.10.35 exactly -- later versions report usage to Google). Its wasm (3 MB
|
|
20
|
+
* compressed) and the model (250 kB) load the first time a background effect is used: the wasm from jsDelivr at that
|
|
21
|
+
* version and the model from Google, unless you serve them yourself (`wasmPath`, `modelUrl`). Beauty alone needs none
|
|
22
|
+
* of it.
|
|
23
|
+
*
|
|
24
|
+
* Chrome and Edge process each camera frame as it arrives (insertable streams: smooth, and it keeps going in a
|
|
25
|
+
* background tab). Firefox and Safari draw from a hidden video on a worker's timer into a captured canvas -- what
|
|
26
|
+
* LiveKit, Twilio and Amazon Chime fall back to -- which a background tab can slow down.
|
|
27
|
+
*/
|
|
28
|
+
const effects_core_js_1 = require("./effects-core.js");
|
|
29
|
+
const effects_gl_js_1 = require("./effects-gl.js");
|
|
30
|
+
/** The `@mediapipe/tasks-vision` version this SDK was tested with; the default wasm URL points at it. */
|
|
31
|
+
exports.MEDIAPIPE_VERSION = '0.10.35';
|
|
32
|
+
const MEDIAPIPE_WASM = `https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${exports.MEDIAPIPE_VERSION}/wasm`;
|
|
33
|
+
/** Google's selfie segmenter for portrait and square pictures (phones), at a fixed version. */
|
|
34
|
+
exports.DEFAULT_SEGMENTER_MODEL = 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/1/selfie_segmenter.tflite';
|
|
35
|
+
/**
|
|
36
|
+
* Its landscape version, for wide pictures (webcams): fed a 16:9 frame, the square model leaves patches of the room
|
|
37
|
+
* beside the head, the landscape one traces it cleanly (as LiveKit, Twilio, Chime and TRTC use for webcams).
|
|
38
|
+
*/
|
|
39
|
+
exports.DEFAULT_LANDSCAPE_MODEL = 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter_landscape/float16/1/selfie_segmenter_landscape.tflite';
|
|
40
|
+
/** Whether this browser can run the effects (WebGL2, and a way to take and give back frames). */
|
|
41
|
+
function isVideoEffectsSupported() {
|
|
42
|
+
if (typeof document === 'undefined' || typeof WebGL2RenderingContext === 'undefined')
|
|
43
|
+
return false;
|
|
44
|
+
if (pumpKind() === null)
|
|
45
|
+
return false;
|
|
46
|
+
try {
|
|
47
|
+
const gl = document.createElement('canvas').getContext('webgl2');
|
|
48
|
+
if (!gl)
|
|
49
|
+
return false;
|
|
50
|
+
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function pumpKind() {
|
|
58
|
+
const g = globalThis;
|
|
59
|
+
if (typeof g.MediaStreamTrackProcessor === 'function' && typeof g.MediaStreamTrackGenerator === 'function' && typeof VideoFrame === 'function')
|
|
60
|
+
return 'insertable';
|
|
61
|
+
if (typeof HTMLCanvasElement !== 'undefined' && typeof HTMLCanvasElement.prototype.captureStream === 'function')
|
|
62
|
+
return 'canvas';
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/** MediaPipe's selfie segmenter, on the GPU where it starts, else the CPU. */
|
|
66
|
+
async function loadMediaPipe(wasmPath, modelUrl) {
|
|
67
|
+
const vision = await Promise.resolve().then(() => require('@mediapipe/tasks-vision'));
|
|
68
|
+
const fileset = await vision.FilesetResolver.forVisionTasks(wasmPath);
|
|
69
|
+
const make = (delegate) => vision.ImageSegmenter.createFromOptions(fileset, {
|
|
70
|
+
baseOptions: { modelAssetPath: modelUrl, delegate },
|
|
71
|
+
runningMode: 'VIDEO',
|
|
72
|
+
outputConfidenceMasks: true,
|
|
73
|
+
outputCategoryMask: false,
|
|
74
|
+
});
|
|
75
|
+
let segmenter;
|
|
76
|
+
try {
|
|
77
|
+
segmenter = await make('GPU');
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
segmenter = await make('CPU');
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
segment(input, timestampMs, use) {
|
|
84
|
+
segmenter.segmentForVideo(input, timestampMs, (result) => {
|
|
85
|
+
// The selfie segmenter's one mask: how sure it is that each pixel is the person. Only valid in here.
|
|
86
|
+
const mask = result.confidenceMasks?.[0];
|
|
87
|
+
if (mask)
|
|
88
|
+
use(mask.getAsFloat32Array());
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
close() {
|
|
92
|
+
segmenter.close();
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const browserDeps = { loadSegmenter: loadMediaPipe };
|
|
97
|
+
/** The largest side a background picture is kept at, to spare the GPU's memory. */
|
|
98
|
+
const PICTURE_MAX = 1920;
|
|
99
|
+
async function loadPicture(source) {
|
|
100
|
+
let input;
|
|
101
|
+
if (typeof source === 'string') {
|
|
102
|
+
const img = new Image();
|
|
103
|
+
img.crossOrigin = 'anonymous';
|
|
104
|
+
img.decoding = 'async';
|
|
105
|
+
img.src = source;
|
|
106
|
+
await img.decode();
|
|
107
|
+
input = img;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
input = source;
|
|
111
|
+
}
|
|
112
|
+
let bitmap = await createImageBitmap(input);
|
|
113
|
+
const longest = Math.max(bitmap.width, bitmap.height);
|
|
114
|
+
if (longest > PICTURE_MAX) {
|
|
115
|
+
const scale = PICTURE_MAX / longest;
|
|
116
|
+
const smaller = await createImageBitmap(bitmap, { resizeWidth: Math.round(bitmap.width * scale), resizeHeight: Math.round(bitmap.height * scale), resizeQuality: 'high' });
|
|
117
|
+
bitmap.close();
|
|
118
|
+
bitmap = smaller;
|
|
119
|
+
}
|
|
120
|
+
const b = bitmap;
|
|
121
|
+
return { image: b, width: b.width, height: b.height, close: () => b.close() };
|
|
122
|
+
}
|
|
123
|
+
/** Chrome and Edge: every camera frame as it arrives, out through a generator track; no timer, no page rendering. */
|
|
124
|
+
function insertablePump(source, draw, canvas, frameIntervalMs, minGapUs) {
|
|
125
|
+
const g = globalThis;
|
|
126
|
+
const reader = new g.MediaStreamTrackProcessor({ track: source }).readable.getReader();
|
|
127
|
+
const generator = new g.MediaStreamTrackGenerator({ kind: 'video' });
|
|
128
|
+
const writer = generator.writable.getWriter();
|
|
129
|
+
let stopped = false;
|
|
130
|
+
let lastOut = Number.NEGATIVE_INFINITY;
|
|
131
|
+
let failures = 0;
|
|
132
|
+
const stop = () => {
|
|
133
|
+
if (stopped)
|
|
134
|
+
return;
|
|
135
|
+
stopped = true;
|
|
136
|
+
void reader.cancel().catch(() => undefined);
|
|
137
|
+
void writer.close().catch(() => undefined);
|
|
138
|
+
generator.stop();
|
|
139
|
+
};
|
|
140
|
+
void (async () => {
|
|
141
|
+
try {
|
|
142
|
+
for (;;) {
|
|
143
|
+
const { value: frame, done } = await reader.read();
|
|
144
|
+
if (done || !frame)
|
|
145
|
+
return;
|
|
146
|
+
if (stopped || generator.readyState === 'ended') {
|
|
147
|
+
frame.close();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Paused (the camera is off), or sooner than maxFrameRate allows: nothing to do for this one.
|
|
151
|
+
if (!generator.enabled || frame.timestamp - lastOut < minGapUs) {
|
|
152
|
+
frame.close();
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
let out = null;
|
|
156
|
+
try {
|
|
157
|
+
draw(frame, frame.displayWidth, frame.displayHeight, frameIntervalMs);
|
|
158
|
+
out = new VideoFrame(canvas, { timestamp: frame.timestamp });
|
|
159
|
+
lastOut = frame.timestamp;
|
|
160
|
+
failures = 0;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
// A frame that cannot be drawn is dropped, never sent as it is: the background may be what is hidden.
|
|
164
|
+
if ((failures += 1) === 1)
|
|
165
|
+
console.warn('video effects: a frame could not be processed', err);
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
frame.close();
|
|
169
|
+
}
|
|
170
|
+
if (out)
|
|
171
|
+
await writer.write(out);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// The output was stopped, or the camera went.
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
stop();
|
|
179
|
+
}
|
|
180
|
+
})();
|
|
181
|
+
return { output: generator, stop };
|
|
182
|
+
}
|
|
183
|
+
/** A timer that keeps time in a background tab: a worker's (LiveKit's FrameTicker), the page's if a CSP refuses it. */
|
|
184
|
+
function startTicker(ms, tick) {
|
|
185
|
+
let interval = null;
|
|
186
|
+
const onPage = () => {
|
|
187
|
+
if (interval === null)
|
|
188
|
+
interval = setInterval(tick, ms);
|
|
189
|
+
};
|
|
190
|
+
let worker = null;
|
|
191
|
+
let url = null;
|
|
192
|
+
try {
|
|
193
|
+
url = URL.createObjectURL(new Blob(['let t=null;onmessage=(e)=>{clearInterval(t);t=e.data>0?setInterval(()=>postMessage(0),e.data):null;};'], { type: 'text/javascript' }));
|
|
194
|
+
worker = new Worker(url);
|
|
195
|
+
worker.onmessage = () => tick();
|
|
196
|
+
worker.onerror = () => {
|
|
197
|
+
worker?.terminate();
|
|
198
|
+
worker = null;
|
|
199
|
+
onPage();
|
|
200
|
+
};
|
|
201
|
+
worker.postMessage(ms);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
worker = null;
|
|
205
|
+
onPage();
|
|
206
|
+
}
|
|
207
|
+
return () => {
|
|
208
|
+
worker?.terminate();
|
|
209
|
+
worker = null;
|
|
210
|
+
if (interval !== null)
|
|
211
|
+
clearInterval(interval);
|
|
212
|
+
interval = null;
|
|
213
|
+
if (url)
|
|
214
|
+
URL.revokeObjectURL(url);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** Firefox and Safari: a hidden video drawn on a worker's timer, the canvas captured as the output. */
|
|
218
|
+
function canvasPump(source, draw, canvas, frameIntervalMs) {
|
|
219
|
+
const video = document.createElement('video');
|
|
220
|
+
video.muted = true;
|
|
221
|
+
video.playsInline = true;
|
|
222
|
+
video.autoplay = true;
|
|
223
|
+
video.srcObject = new MediaStream([source]);
|
|
224
|
+
const play = () => { void video.play().catch(() => undefined); };
|
|
225
|
+
play();
|
|
226
|
+
const output = canvas.captureStream().getVideoTracks()[0];
|
|
227
|
+
if (!output)
|
|
228
|
+
throw new Error('video effects: this browser gives no track from a canvas');
|
|
229
|
+
let stopped = false;
|
|
230
|
+
let lastTime = -1;
|
|
231
|
+
let failures = 0;
|
|
232
|
+
const stop = () => {
|
|
233
|
+
if (stopped)
|
|
234
|
+
return;
|
|
235
|
+
stopped = true;
|
|
236
|
+
stopTicker();
|
|
237
|
+
video.pause();
|
|
238
|
+
video.srcObject = null;
|
|
239
|
+
output.stop();
|
|
240
|
+
};
|
|
241
|
+
const stopTicker = startTicker(frameIntervalMs, () => {
|
|
242
|
+
if (stopped)
|
|
243
|
+
return;
|
|
244
|
+
if (output.readyState === 'ended' || source.readyState === 'ended') {
|
|
245
|
+
stop();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// WebKit can refuse play() until later: keep asking.
|
|
249
|
+
if (video.paused)
|
|
250
|
+
play();
|
|
251
|
+
if (!output.enabled || video.readyState < 2 || video.videoWidth === 0)
|
|
252
|
+
return;
|
|
253
|
+
if (video.currentTime === lastTime)
|
|
254
|
+
return;
|
|
255
|
+
lastTime = video.currentTime;
|
|
256
|
+
try {
|
|
257
|
+
draw(video, video.videoWidth, video.videoHeight, frameIntervalMs);
|
|
258
|
+
failures = 0;
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
if ((failures += 1) === 1)
|
|
262
|
+
console.warn('video effects: a frame could not be processed', err);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
return { output, stop };
|
|
266
|
+
}
|
|
267
|
+
/** Virtual background and beauty, for `client.setVideoProcessor()`. */
|
|
268
|
+
function createVideoEffects(options = {}, stand = {}) {
|
|
269
|
+
const deps = { ...browserDeps, ...stand };
|
|
270
|
+
const wasmPath = options.wasmPath ?? MEDIAPIPE_WASM;
|
|
271
|
+
const modelFor = (width, height) => options.modelUrl ?? (width > height ? exports.DEFAULT_LANDSCAPE_MODEL : exports.DEFAULT_SEGMENTER_MODEL);
|
|
272
|
+
const maxFrameRate = typeof options.maxFrameRate === 'number' && options.maxFrameRate > 0 ? options.maxFrameRate : null;
|
|
273
|
+
const state = {
|
|
274
|
+
mode: 'none',
|
|
275
|
+
blur: (0, effects_core_js_1.blurLevel)(options.blur),
|
|
276
|
+
beauty: (0, effects_core_js_1.beautySettings)(options.beauty),
|
|
277
|
+
};
|
|
278
|
+
let picture = null;
|
|
279
|
+
// By model: one for wide pictures, one for tall ones, each loaded when first needed.
|
|
280
|
+
const segmenters = new Map();
|
|
281
|
+
const loading = new Map();
|
|
282
|
+
// The camera's shape, for the model to load before it is seen; a webcam's until a camera is given.
|
|
283
|
+
let shape = { width: 1280, height: 720 };
|
|
284
|
+
let renderer = null;
|
|
285
|
+
let pipeline = null;
|
|
286
|
+
let destroyed = false;
|
|
287
|
+
let overloaded = false;
|
|
288
|
+
const pacer = new effects_core_js_1.SegmentationPacer();
|
|
289
|
+
const clock = new effects_core_js_1.MonotonicClock();
|
|
290
|
+
// Per camera: the mask and how far along it is.
|
|
291
|
+
let maskReady = false;
|
|
292
|
+
let steady = null;
|
|
293
|
+
let maskBytes = new Uint8Array(0);
|
|
294
|
+
let frameIndex = 0;
|
|
295
|
+
let segCanvas = null;
|
|
296
|
+
let segContext = null;
|
|
297
|
+
let pictureUploaded = null;
|
|
298
|
+
const ensureSegmenter = (model) => {
|
|
299
|
+
const ready = segmenters.get(model);
|
|
300
|
+
if (ready)
|
|
301
|
+
return Promise.resolve(ready);
|
|
302
|
+
let pending = loading.get(model);
|
|
303
|
+
if (!pending) {
|
|
304
|
+
pending = deps.loadSegmenter(wasmPath, model).then((s) => {
|
|
305
|
+
loading.delete(model);
|
|
306
|
+
if (destroyed) {
|
|
307
|
+
s.close();
|
|
308
|
+
throw new Error('video effects: destroyed');
|
|
309
|
+
}
|
|
310
|
+
segmenters.set(model, s);
|
|
311
|
+
return s;
|
|
312
|
+
}, (err) => {
|
|
313
|
+
loading.delete(model); // tried again next time (a network blip)
|
|
314
|
+
throw err;
|
|
315
|
+
});
|
|
316
|
+
loading.set(model, pending);
|
|
317
|
+
}
|
|
318
|
+
return pending;
|
|
319
|
+
};
|
|
320
|
+
/** A fresh start for the mask: nothing behind the person shows until the next one is found. */
|
|
321
|
+
const restartMask = () => {
|
|
322
|
+
maskReady = false;
|
|
323
|
+
steady = null;
|
|
324
|
+
frameIndex = 0;
|
|
325
|
+
};
|
|
326
|
+
const segmentInto = (input, width, height, frameIntervalMs) => {
|
|
327
|
+
const model = modelFor(width, height);
|
|
328
|
+
let seg = segmenters.get(model);
|
|
329
|
+
if (!seg) {
|
|
330
|
+
// The camera turned (a phone rotated): the other model loads meanwhile, and the one there is goes on.
|
|
331
|
+
void ensureSegmenter(model).catch(() => undefined);
|
|
332
|
+
seg = segmenters.values().next().value;
|
|
333
|
+
}
|
|
334
|
+
if (!seg || !renderer)
|
|
335
|
+
return;
|
|
336
|
+
const size = (0, effects_core_js_1.segmentationSize)(width, height);
|
|
337
|
+
if (!segCanvas || segCanvas.width !== size.width || segCanvas.height !== size.height) {
|
|
338
|
+
segCanvas = typeof OffscreenCanvas === 'function' ? new OffscreenCanvas(size.width, size.height) : Object.assign(document.createElement('canvas'), size);
|
|
339
|
+
segContext = segCanvas.getContext('2d');
|
|
340
|
+
maskBytes = new Uint8Array(size.width * size.height);
|
|
341
|
+
steady = null;
|
|
342
|
+
}
|
|
343
|
+
if (!segContext)
|
|
344
|
+
return;
|
|
345
|
+
segContext.drawImage(input, 0, 0, size.width, size.height);
|
|
346
|
+
const started = performance.now();
|
|
347
|
+
seg.segment(segCanvas, clock.next(started), (confidence) => {
|
|
348
|
+
if (confidence.length !== size.width * size.height || !renderer)
|
|
349
|
+
return;
|
|
350
|
+
steady = (0, effects_core_js_1.smoothMask)(confidence, steady, maskBytes);
|
|
351
|
+
renderer.uploadMask(maskBytes, size.width, size.height);
|
|
352
|
+
maskReady = true;
|
|
353
|
+
});
|
|
354
|
+
const now = performance.now();
|
|
355
|
+
pacer.report(now - started, frameIntervalMs, now);
|
|
356
|
+
const over = pacer.overloaded(frameIntervalMs);
|
|
357
|
+
if (over !== overloaded) {
|
|
358
|
+
overloaded = over;
|
|
359
|
+
try {
|
|
360
|
+
options.onOverload?.(over);
|
|
361
|
+
}
|
|
362
|
+
catch { /* the app's */ }
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
const draw = (input, width, height, frameIntervalMs) => {
|
|
366
|
+
const r = renderer;
|
|
367
|
+
if (!r || r.lost || !(width > 0 && height > 0))
|
|
368
|
+
return;
|
|
369
|
+
r.setSize(width, height);
|
|
370
|
+
r.uploadFrame(input);
|
|
371
|
+
const mode = state.mode;
|
|
372
|
+
if (mode !== 'none') {
|
|
373
|
+
if (pacer.shouldSegment(frameIndex))
|
|
374
|
+
segmentInto(input, width, height, frameIntervalMs);
|
|
375
|
+
frameIndex += 1;
|
|
376
|
+
}
|
|
377
|
+
if (mode === 'image' && picture && pictureUploaded !== picture) {
|
|
378
|
+
r.setPicture(picture.image, picture.width, picture.height);
|
|
379
|
+
pictureUploaded = picture;
|
|
380
|
+
}
|
|
381
|
+
else if (mode !== 'image' && pictureUploaded) {
|
|
382
|
+
r.setPicture(null);
|
|
383
|
+
pictureUploaded = null;
|
|
384
|
+
}
|
|
385
|
+
r.render({
|
|
386
|
+
mode,
|
|
387
|
+
blurSigma: (0, effects_core_js_1.blurSigma)(state.blur, r.size.blurHeight),
|
|
388
|
+
beauty: state.beauty,
|
|
389
|
+
maskReady,
|
|
390
|
+
cover: picture ? (0, effects_core_js_1.coverFit)(picture.width, picture.height, width, height) : { sx: 1, sy: 1, ox: 0, oy: 0 },
|
|
391
|
+
});
|
|
392
|
+
};
|
|
393
|
+
const apply = async (changes) => {
|
|
394
|
+
if (destroyed)
|
|
395
|
+
throw new Error('video effects: destroyed');
|
|
396
|
+
let mode = state.mode;
|
|
397
|
+
let nextPicture;
|
|
398
|
+
if ('background' in changes) {
|
|
399
|
+
mode = (0, effects_core_js_1.backgroundMode)(changes.background);
|
|
400
|
+
// Loaded before anything changes: one that fails leaves the effects as they were.
|
|
401
|
+
if (mode === 'image')
|
|
402
|
+
nextPicture = await loadPicture(changes.background.image);
|
|
403
|
+
if (mode !== 'none') {
|
|
404
|
+
try {
|
|
405
|
+
await ensureSegmenter(modelFor(shape.width, shape.height));
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
nextPicture?.close();
|
|
409
|
+
throw err;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (destroyed) {
|
|
414
|
+
nextPicture?.close();
|
|
415
|
+
throw new Error('video effects: destroyed');
|
|
416
|
+
}
|
|
417
|
+
if ('blur' in changes)
|
|
418
|
+
state.blur = (0, effects_core_js_1.blurLevel)(changes.blur);
|
|
419
|
+
if ('beauty' in changes)
|
|
420
|
+
state.beauty = (0, effects_core_js_1.beautySettings)(changes.beauty);
|
|
421
|
+
if (mode !== state.mode)
|
|
422
|
+
restartMask();
|
|
423
|
+
if (nextPicture !== undefined || mode !== 'image') {
|
|
424
|
+
const old = picture;
|
|
425
|
+
picture = nextPicture ?? null;
|
|
426
|
+
if (old && old !== picture)
|
|
427
|
+
old.close();
|
|
428
|
+
}
|
|
429
|
+
state.mode = mode;
|
|
430
|
+
};
|
|
431
|
+
// The background asked for at creation starts loading now, so it is likely ready by the time the camera is.
|
|
432
|
+
let queue = apply({ background: options.background ?? 'none' });
|
|
433
|
+
queue.catch(() => undefined);
|
|
434
|
+
const pumpChoice = deps.pump ?? pumpKind();
|
|
435
|
+
return {
|
|
436
|
+
name: 'video-effects',
|
|
437
|
+
async process(track) {
|
|
438
|
+
if (destroyed)
|
|
439
|
+
throw new Error('video effects: destroyed');
|
|
440
|
+
if (!pumpChoice)
|
|
441
|
+
throw new Error('video effects are not supported in this browser');
|
|
442
|
+
await queue; // the background asked for at creation: a failure is the SDK's to report
|
|
443
|
+
const settings = track.getSettings();
|
|
444
|
+
if (typeof settings.width === 'number' && typeof settings.height === 'number' && settings.width > 0 && settings.height > 0) {
|
|
445
|
+
shape = { width: settings.width, height: settings.height };
|
|
446
|
+
}
|
|
447
|
+
// The model for this camera's shape, before its first frame: a background shows nothing behind the person meanwhile.
|
|
448
|
+
if (state.mode !== 'none')
|
|
449
|
+
await ensureSegmenter(modelFor(shape.width, shape.height));
|
|
450
|
+
pipeline?.stop();
|
|
451
|
+
pipeline = null;
|
|
452
|
+
if (!renderer) {
|
|
453
|
+
const canvas = pumpChoice === 'insertable' && typeof OffscreenCanvas === 'function' ? new OffscreenCanvas(2, 2) : document.createElement('canvas');
|
|
454
|
+
renderer = new effects_gl_js_1.EffectsRenderer(canvas);
|
|
455
|
+
}
|
|
456
|
+
restartMask();
|
|
457
|
+
const cameraRate = typeof settings.frameRate === 'number' && settings.frameRate > 0 ? settings.frameRate : 30;
|
|
458
|
+
const rate = maxFrameRate ? Math.min(maxFrameRate, cameraRate) : cameraRate;
|
|
459
|
+
const frameIntervalMs = 1000 / rate;
|
|
460
|
+
pipeline = pumpChoice === 'insertable'
|
|
461
|
+
? insertablePump(track, draw, renderer.canvas, frameIntervalMs, maxFrameRate ? (1e6 / maxFrameRate) * 0.8 : 0)
|
|
462
|
+
: canvasPump(track, draw, renderer.canvas, frameIntervalMs);
|
|
463
|
+
return pipeline.output;
|
|
464
|
+
},
|
|
465
|
+
update(changes) {
|
|
466
|
+
// One change at a time, in the order asked.
|
|
467
|
+
const next = queue.catch(() => undefined).then(() => apply(changes));
|
|
468
|
+
queue = next.catch(() => undefined);
|
|
469
|
+
return next;
|
|
470
|
+
},
|
|
471
|
+
get settings() {
|
|
472
|
+
return { background: state.mode, blur: state.blur, beauty: state.beauty ? { ...state.beauty } : null };
|
|
473
|
+
},
|
|
474
|
+
get stats() {
|
|
475
|
+
return { segmentEvery: pacer.interval, segmentMs: Math.round(pacer.segmentMs * 10) / 10, overloaded };
|
|
476
|
+
},
|
|
477
|
+
destroy() {
|
|
478
|
+
if (destroyed)
|
|
479
|
+
return;
|
|
480
|
+
destroyed = true;
|
|
481
|
+
pipeline?.stop();
|
|
482
|
+
pipeline = null;
|
|
483
|
+
for (const s of segmenters.values())
|
|
484
|
+
s.close();
|
|
485
|
+
segmenters.clear();
|
|
486
|
+
renderer?.dispose();
|
|
487
|
+
renderer = null;
|
|
488
|
+
picture?.close();
|
|
489
|
+
picture = null;
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
//# sourceMappingURL=video-effects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-effects.js","sourceRoot":"","sources":["../../src/video-effects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive stream: receive each remote video at the size it is shown, and not
|
|
3
|
+
* at all while nobody can see it.
|
|
4
|
+
*
|
|
5
|
+
* The app attaches the element that shows a video (`attachVideo`). From that
|
|
6
|
+
* element's size on screen (CSS pixels × devicePixelRatio, at most 2) the
|
|
7
|
+
* smallest simulcast layer that fills it sharply is chosen; while no attached
|
|
8
|
+
* element is visible (scrolled away, `display: none`, the tab in the
|
|
9
|
+
* background) the video is paused at the media server, so it costs no
|
|
10
|
+
* bandwidth and no decoding. This is what LiveKit calls adaptiveStream and
|
|
11
|
+
* Tencent TRTC "small stream + receiveWhenViewVisible": the single biggest
|
|
12
|
+
* saving on a viewer with several tiles, where every tile otherwise decodes
|
|
13
|
+
* the publisher's full-size layer.
|
|
14
|
+
*
|
|
15
|
+
* Pure decisions are exported for tests; the browser parts (observers,
|
|
16
|
+
* timers) are injected.
|
|
17
|
+
*/
|
|
18
|
+
import type { VideoLayer } from './quality.js';
|
|
19
|
+
/** Where a video is drawn, in CSS pixels, and how it is fitted into that box. */
|
|
20
|
+
export interface ViewBox {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
fit: 'cover' | 'contain';
|
|
24
|
+
}
|
|
25
|
+
/** Never ask for more than twice the CSS size: a 3× phone screen gains nothing visible from it. */
|
|
26
|
+
export declare const MAX_PIXEL_RATIO = 2;
|
|
27
|
+
/** A layer shown at most this much bigger than it is still looks sharp: avoids a whole layer for a few pixels. */
|
|
28
|
+
export declare const MAX_UPSCALE = 1.15;
|
|
29
|
+
/**
|
|
30
|
+
* The smallest layer that fills `box` sharply, or null when the layers are
|
|
31
|
+
* not known (an older publisher): then the server chooses, as before.
|
|
32
|
+
*
|
|
33
|
+
* With `cover` the picture is scaled until it fills the box (a part is
|
|
34
|
+
* cropped), with `contain` until it fits. Either way the scale is measured
|
|
35
|
+
* against each layer's own size, so a portrait layer list works too.
|
|
36
|
+
*/
|
|
37
|
+
export declare function layerForBox(layers: readonly VideoLayer[], box: ViewBox, pixelRatio?: number): number | null;
|
|
38
|
+
/** What one video should get now: paused, or a layer (null = let the server choose). */
|
|
39
|
+
export interface AdaptiveWant {
|
|
40
|
+
visible: boolean;
|
|
41
|
+
spatialLayer: number | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The largest view among the visible ones decides; any visible view keeps the video flowing. A view not measured
|
|
45
|
+
* yet (just attached, before layout) counts as shown at the server's choice, so attaching never pauses a video
|
|
46
|
+
* for the moment before the browser has sized the element.
|
|
47
|
+
*/
|
|
48
|
+
export declare function wantFor(views: readonly {
|
|
49
|
+
box: ViewBox;
|
|
50
|
+
visible: boolean;
|
|
51
|
+
measured?: boolean;
|
|
52
|
+
}[], layers: readonly VideoLayer[], pixelRatio: number, pageVisible: boolean): AdaptiveWant;
|
|
53
|
+
/** Pause a hidden video only after this long, so scrolling past or a quick tab switch does not stop it. */
|
|
54
|
+
export declare const PAUSE_AFTER_MS = 1500;
|
|
55
|
+
/** Go down a layer only after this long (going up is immediate): a tile shrinking for a moment keeps its picture. */
|
|
56
|
+
export declare const LOWER_AFTER_MS = 1200;
|
|
57
|
+
export interface AdaptiveHost {
|
|
58
|
+
/** The layers this producer's publisher declared (smallest first or any order); [] when unknown. */
|
|
59
|
+
layers(producerId: string): readonly VideoLayer[];
|
|
60
|
+
/** Apply a decision for one video. */
|
|
61
|
+
apply(producerId: string, want: AdaptiveWant): void;
|
|
62
|
+
/** For tests: the environment. */
|
|
63
|
+
pixelRatio?(): number;
|
|
64
|
+
pageVisible?(): boolean;
|
|
65
|
+
now?(): number;
|
|
66
|
+
setTimer?(fn: () => void, ms: number): unknown;
|
|
67
|
+
clearTimer?(handle: unknown): void;
|
|
68
|
+
/** For tests: stand-ins for ResizeObserver / IntersectionObserver / document visibility. */
|
|
69
|
+
observe?(element: Element, onChange: (box: ViewBox | null, visible: boolean | null) => void): () => void;
|
|
70
|
+
onPageVisibility?(listener: () => void): () => void;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The videos being shown, by producer id. Decisions are applied through the
|
|
74
|
+
* host; going up (or becoming visible) is applied at once, going down (or
|
|
75
|
+
* hiding) after a short delay.
|
|
76
|
+
*/
|
|
77
|
+
export declare class AdaptiveVideos {
|
|
78
|
+
private readonly host;
|
|
79
|
+
private readonly entries;
|
|
80
|
+
private stopPage;
|
|
81
|
+
constructor(host: AdaptiveHost);
|
|
82
|
+
/** Show `producerId` in `element`; returns the function that stops. */
|
|
83
|
+
attach(producerId: string, element: Element): () => void;
|
|
84
|
+
detach(producerId: string, element: Element): void;
|
|
85
|
+
/** Whether the app shows this video through attachVideo (its decisions then apply to new consumers too). */
|
|
86
|
+
has(producerId: string): boolean;
|
|
87
|
+
/** The decision in force, for a consumer made (or made again) after it: null when the app does not attach it. */
|
|
88
|
+
current(producerId: string): AdaptiveWant | null;
|
|
89
|
+
/**
|
|
90
|
+
* A new consumer was made with `current()` applied (its layers, and paused when hidden): record that as applied,
|
|
91
|
+
* so nothing is sent twice, and drop a pending change made for the old one.
|
|
92
|
+
*/
|
|
93
|
+
consumed(producerId: string): void;
|
|
94
|
+
/** Stop everything (the session closed). */
|
|
95
|
+
dispose(): void;
|
|
96
|
+
private decide;
|
|
97
|
+
private update;
|
|
98
|
+
private clear;
|
|
99
|
+
private observe;
|
|
100
|
+
private watchPage;
|
|
101
|
+
}
|
|
102
|
+
/** The page is visible (not a background tab). */
|
|
103
|
+
export declare function pageIsVisible(): boolean;
|
|
104
|
+
//# sourceMappingURL=adaptive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive.d.ts","sourceRoot":"","sources":["../../src/adaptive.ts"],"names":[],"mappings":"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"}
|