@pexip/media 20.3.5 → 22.0.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 +106 -0
- package/README.md +20 -32
- package/api-docs/README.mdx +23 -35
- package/api-docs/functions/getBlurKernelSize.mdx +25 -0
- package/api-docs/functions/getTentBlurSize.mdx +24 -0
- package/api-docs/functions/proxyWithLog.mdx +33 -0
- package/api-docs/interfaces/ExtendedMediaTrackSettings.mdx +9 -5
- package/api-docs/interfaces/PreviewControllerProps.mdx +1 -0
- package/api-docs/interfaces/PreviewStreamParams.mdx +1 -0
- package/api-docs/type-aliases/VideoRenderParams.mdx +3 -1
- package/dist/audioMixingProcessor.d.ts +4 -2
- package/dist/audioMixingProcessor.js +22 -12
- package/dist/audioProcessor.d.ts +7 -2
- package/dist/audioProcessor.js +25 -20
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/logger.js +8 -0
- package/dist/media.js +5 -5
- package/dist/previewController.d.ts +3 -1
- package/dist/previewController.js +24 -9
- package/dist/typeGuard.d.ts +1 -2
- package/dist/typeGuard.js +0 -3
- package/dist/types.d.ts +10 -15
- package/dist/userMedia.d.ts +4 -1
- package/dist/userMedia.js +15 -11
- package/dist/utils.d.ts +17 -6
- package/dist/utils.js +31 -16
- package/dist/videoProcessor.d.ts +41 -23
- package/dist/videoProcessor.js +315 -193
- package/package.json +14 -14
- package/api-docs/functions/isVideoStreamTrackProcessorAPIs.mdx +0 -13
- package/api-docs/type-aliases/Segmenters.mdx +0 -3
- package/api-docs/type-aliases/VideoStreamTrackProcessorAPIs.mdx +0 -7
package/dist/videoProcessor.js
CHANGED
|
@@ -1,132 +1,283 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { createMediaTrack, getBlurKernelSize } from './utils';
|
|
1
|
+
import { isRenderEffects } from '@pexip/media-processor';
|
|
2
|
+
import { extractConstraints, getValueFromConstrainNumber, } from '@pexip/media-control';
|
|
3
|
+
import { hasOwn, isEmpty } from '@pexip/utils';
|
|
4
|
+
import { createMediaTrack, getBlurKernelSize, getTentBlurSize } from './utils';
|
|
5
5
|
import { logger, proxyWithLog } from './logger';
|
|
6
6
|
import { isVideoContentHint } from './typeGuard';
|
|
7
7
|
import { PROCESSOR_LABELS } from './constants';
|
|
8
|
-
const FEATURE_KEYS =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
8
|
+
const FEATURE_KEYS = {
|
|
9
|
+
backgroundBlurAmount: 'number',
|
|
10
|
+
backgroundImageUrl: 'string',
|
|
11
|
+
backgroundThreshold: 'number',
|
|
12
|
+
maskCombineRatio: 'number',
|
|
13
|
+
edgeBlurAmount: 'number',
|
|
14
|
+
foregroundThreshold: 'number',
|
|
15
|
+
frameRate: 'number',
|
|
16
|
+
videoSegmentation: 'string',
|
|
17
|
+
width: 'number',
|
|
18
|
+
height: 'number',
|
|
19
|
+
pan: 'boolean',
|
|
20
|
+
tilt: 'boolean',
|
|
21
|
+
zoom: 'boolean',
|
|
22
|
+
contentHint: 'string',
|
|
23
|
+
sigmaSpace: 'number',
|
|
24
|
+
sigmaRangeLo: 'number',
|
|
25
|
+
sigmaRangeHi: 'number',
|
|
26
|
+
excludeBystanders: 'boolean',
|
|
27
|
+
personCenterX: 'number',
|
|
28
|
+
personCenterY: 'number',
|
|
29
|
+
morphErodeRadiusPx: 'number',
|
|
30
|
+
morphPass: 'number',
|
|
31
|
+
morphDilateRadiusPx: 'number',
|
|
32
|
+
lightWrapIntensity: 'number',
|
|
33
|
+
lightWrapTightness: 'number',
|
|
34
|
+
lightWrapEdgeBand: 'number',
|
|
35
|
+
lightWrapBlurAmount: 'number',
|
|
36
|
+
downSampleFactor: 'number',
|
|
37
|
+
};
|
|
25
38
|
export const updateFeatureProps = (constraints, props) => {
|
|
26
|
-
const
|
|
27
|
-
|
|
39
|
+
const features = {};
|
|
40
|
+
if (constraints === undefined || typeof constraints === 'boolean') {
|
|
41
|
+
return features;
|
|
42
|
+
}
|
|
43
|
+
for (const k of Object.keys(constraints)) {
|
|
44
|
+
const key = k;
|
|
45
|
+
if (!FEATURE_KEYS[key]) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
28
48
|
switch (key) {
|
|
29
49
|
case 'contentHint': {
|
|
30
|
-
const [[feature = ''] = []] =
|
|
50
|
+
const [[feature = ''] = []] = extractConstraints(key)(constraints);
|
|
31
51
|
if (isVideoContentHint(feature) && props[key] !== feature) {
|
|
32
52
|
props[key] = feature;
|
|
33
|
-
|
|
34
|
-
return accm;
|
|
53
|
+
features[key] = feature;
|
|
35
54
|
}
|
|
36
|
-
|
|
55
|
+
break;
|
|
37
56
|
}
|
|
38
57
|
case 'videoSegmentation': {
|
|
39
|
-
const [[feature] = []] =
|
|
58
|
+
const [[feature] = []] = extractConstraints(key)(constraints);
|
|
40
59
|
if (isRenderEffects(feature) && props[key] !== feature) {
|
|
41
60
|
props[key] = feature;
|
|
42
|
-
|
|
43
|
-
return accm;
|
|
44
|
-
}
|
|
45
|
-
return accm;
|
|
46
|
-
}
|
|
47
|
-
case 'videoSegmentationModel': {
|
|
48
|
-
const [[feature] = []] = extracted[key];
|
|
49
|
-
if (isSegmentationModel(feature) && props[key] !== feature) {
|
|
50
|
-
props[key] = feature;
|
|
51
|
-
accm[key] = feature;
|
|
52
|
-
return accm;
|
|
61
|
+
features[key] = feature;
|
|
53
62
|
}
|
|
54
|
-
|
|
63
|
+
break;
|
|
55
64
|
}
|
|
56
65
|
case 'backgroundImageUrl': {
|
|
57
|
-
const [[feature] = []] =
|
|
66
|
+
const [[feature] = []] = extractConstraints(key)(constraints);
|
|
58
67
|
if (feature) {
|
|
59
68
|
props[key] = feature;
|
|
60
|
-
|
|
61
|
-
return accm;
|
|
69
|
+
features[key] = feature;
|
|
62
70
|
}
|
|
63
|
-
|
|
71
|
+
break;
|
|
64
72
|
}
|
|
65
|
-
case '
|
|
66
|
-
case 'height':
|
|
67
|
-
case 'frameRate':
|
|
68
|
-
case 'foregroundThreshold':
|
|
73
|
+
case 'backgroundBlurAmount':
|
|
69
74
|
case 'edgeBlurAmount':
|
|
75
|
+
case 'lightWrapBlurAmount': {
|
|
76
|
+
const [feature] = extractConstraints(key)(constraints);
|
|
77
|
+
if (feature !== undefined) {
|
|
78
|
+
const value = getValueFromConstrainNumber(feature);
|
|
79
|
+
if (props[key] !== value) {
|
|
80
|
+
props[key] = value;
|
|
81
|
+
features[key] = value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'backgroundThreshold':
|
|
87
|
+
case 'downSampleFactor':
|
|
88
|
+
case 'foregroundThreshold':
|
|
89
|
+
case 'frameRate':
|
|
90
|
+
case 'height':
|
|
91
|
+
case 'lightWrapEdgeBand':
|
|
92
|
+
case 'lightWrapIntensity':
|
|
93
|
+
case 'lightWrapTightness':
|
|
70
94
|
case 'maskCombineRatio':
|
|
71
|
-
case '
|
|
72
|
-
|
|
95
|
+
case 'morphDilateRadiusPx':
|
|
96
|
+
case 'morphErodeRadiusPx':
|
|
97
|
+
case 'morphPass':
|
|
98
|
+
case 'personCenterX':
|
|
99
|
+
case 'personCenterY':
|
|
100
|
+
case 'sigmaRangeHi':
|
|
101
|
+
case 'sigmaRangeLo':
|
|
102
|
+
case 'sigmaSpace':
|
|
103
|
+
case 'width': {
|
|
104
|
+
const [feature] = extractConstraints(key)(constraints);
|
|
73
105
|
if (feature !== undefined) {
|
|
74
106
|
const value = getValueFromConstrainNumber(feature);
|
|
75
107
|
if (props[key] !== value) {
|
|
76
108
|
props[key] = value;
|
|
77
|
-
|
|
78
|
-
return accm;
|
|
109
|
+
features[key] = value;
|
|
79
110
|
}
|
|
80
111
|
}
|
|
81
|
-
|
|
112
|
+
break;
|
|
82
113
|
}
|
|
83
114
|
case 'pan':
|
|
84
115
|
case 'tilt':
|
|
85
116
|
case 'zoom': {
|
|
86
|
-
const [feature] =
|
|
117
|
+
const [feature] = extractConstraints(key)(constraints);
|
|
87
118
|
if (feature !== undefined && props[key] !== feature) {
|
|
88
119
|
props[key] = feature;
|
|
89
|
-
|
|
90
|
-
return accm;
|
|
120
|
+
features[key] = feature;
|
|
91
121
|
}
|
|
92
|
-
|
|
122
|
+
break;
|
|
93
123
|
}
|
|
94
124
|
default:
|
|
95
|
-
|
|
125
|
+
break;
|
|
96
126
|
}
|
|
97
|
-
}
|
|
127
|
+
}
|
|
128
|
+
return features;
|
|
129
|
+
};
|
|
130
|
+
export const mapFeatureToSettings = (props) => {
|
|
131
|
+
return new Proxy({}, {
|
|
132
|
+
get(_target, p, receiver) {
|
|
133
|
+
switch (p) {
|
|
134
|
+
case 'tilt':
|
|
135
|
+
case 'zoom':
|
|
136
|
+
case 'pan': {
|
|
137
|
+
if (props[p] === undefined || props[p] === false) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
default: {
|
|
143
|
+
return Reflect.get(props, p, receiver);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
has(_target, p) {
|
|
148
|
+
return p in props;
|
|
149
|
+
},
|
|
150
|
+
ownKeys() {
|
|
151
|
+
return Reflect.ownKeys(props);
|
|
152
|
+
},
|
|
153
|
+
getOwnPropertyDescriptor(_target, p) {
|
|
154
|
+
if (hasOwn(props, p)) {
|
|
155
|
+
return {
|
|
156
|
+
configurable: true,
|
|
157
|
+
enumerable: true,
|
|
158
|
+
writable: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
export const mapSettingsToFeatures = (settings) => {
|
|
166
|
+
return new Proxy({}, {
|
|
167
|
+
get(_target, p, receiver) {
|
|
168
|
+
switch (p) {
|
|
169
|
+
case 'tilt':
|
|
170
|
+
case 'zoom':
|
|
171
|
+
case 'pan': {
|
|
172
|
+
if (settings[p] === undefined) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
default: {
|
|
178
|
+
return Reflect.get(settings, p, receiver);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
has(_target, p) {
|
|
183
|
+
return p in settings;
|
|
184
|
+
},
|
|
185
|
+
ownKeys() {
|
|
186
|
+
return Reflect.ownKeys(settings);
|
|
187
|
+
},
|
|
188
|
+
getOwnPropertyDescriptor(target, p) {
|
|
189
|
+
if (hasOwn(settings, p)) {
|
|
190
|
+
return {
|
|
191
|
+
configurable: true,
|
|
192
|
+
enumerable: true,
|
|
193
|
+
writable: true,
|
|
194
|
+
value: this.get?.(target, p, undefined),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return undefined;
|
|
198
|
+
},
|
|
199
|
+
});
|
|
98
200
|
};
|
|
99
|
-
const applyFeatures = (
|
|
201
|
+
const applyFeatures = async (processor, features, referenceProcessingHeight) => {
|
|
202
|
+
const renderOptions = {};
|
|
100
203
|
for (const key in features) {
|
|
101
204
|
const k = key;
|
|
102
205
|
switch (k) {
|
|
103
|
-
case 'edgeBlurAmount':
|
|
104
206
|
case 'maskCombineRatio':
|
|
105
|
-
case 'foregroundThreshold':
|
|
207
|
+
case 'foregroundThreshold':
|
|
208
|
+
case 'sigmaSpace':
|
|
209
|
+
case 'sigmaRangeLo':
|
|
210
|
+
case 'sigmaRangeHi':
|
|
211
|
+
case 'morphErodeRadiusPx':
|
|
212
|
+
case 'morphPass':
|
|
213
|
+
case 'morphDilateRadiusPx':
|
|
214
|
+
case 'lightWrapIntensity':
|
|
215
|
+
case 'lightWrapTightness':
|
|
216
|
+
case 'lightWrapEdgeBand':
|
|
217
|
+
case 'downSampleFactor':
|
|
218
|
+
case 'backgroundThreshold': {
|
|
106
219
|
const value = features[k];
|
|
107
220
|
if (value !== undefined) {
|
|
108
|
-
|
|
221
|
+
renderOptions[k] = value;
|
|
109
222
|
}
|
|
110
223
|
break;
|
|
111
224
|
}
|
|
225
|
+
case 'excludeBystanders': {
|
|
226
|
+
const value = features[k];
|
|
227
|
+
if (value !== undefined) {
|
|
228
|
+
renderOptions[k] = value;
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'personCenterX': {
|
|
233
|
+
const value = features[k];
|
|
234
|
+
if (value !== undefined) {
|
|
235
|
+
renderOptions.personCenter = [
|
|
236
|
+
value,
|
|
237
|
+
renderOptions.personCenter?.[1] ??
|
|
238
|
+
processor.renderOptions.personCenter[1],
|
|
239
|
+
];
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case 'personCenterY': {
|
|
244
|
+
const value = features[k];
|
|
245
|
+
if (value !== undefined) {
|
|
246
|
+
renderOptions.personCenter = [
|
|
247
|
+
renderOptions.personCenter?.[0] ??
|
|
248
|
+
processor.renderOptions.personCenter[0],
|
|
249
|
+
value,
|
|
250
|
+
];
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
case 'lightWrapBlurAmount':
|
|
112
255
|
case 'backgroundBlurAmount': {
|
|
113
256
|
const value = features[k];
|
|
114
257
|
if (value !== undefined) {
|
|
115
|
-
|
|
258
|
+
renderOptions[k] = getBlurKernelSize(value, referenceProcessingHeight ?? processor.processingHeight, processor.processingHeight);
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case 'edgeBlurAmount': {
|
|
263
|
+
const value = features[k];
|
|
264
|
+
if (value !== undefined) {
|
|
265
|
+
renderOptions[k] = getTentBlurSize(value, referenceProcessingHeight ?? processor.processingHeight, processor.processingHeight);
|
|
116
266
|
}
|
|
117
267
|
break;
|
|
118
268
|
}
|
|
119
269
|
case 'videoSegmentation': {
|
|
120
270
|
const value = features[k];
|
|
121
|
-
if (value && value !==
|
|
122
|
-
|
|
271
|
+
if (value && value !== processor.renderOptions.effects) {
|
|
272
|
+
renderOptions.effects = value;
|
|
123
273
|
}
|
|
124
274
|
break;
|
|
125
275
|
}
|
|
126
276
|
case 'backgroundImageUrl': {
|
|
127
277
|
const value = features[k];
|
|
128
|
-
if (value &&
|
|
129
|
-
|
|
278
|
+
if (value &&
|
|
279
|
+
value !== processor.renderOptions.backgroundImageUrl) {
|
|
280
|
+
renderOptions.backgroundImageUrl = value;
|
|
130
281
|
}
|
|
131
282
|
break;
|
|
132
283
|
}
|
|
@@ -135,63 +286,40 @@ const applyFeatures = (transformer, features, lowestProcessingHeight) => {
|
|
|
135
286
|
}
|
|
136
287
|
}
|
|
137
288
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (shouldUseStreamTrackProcessor &&
|
|
141
|
-
'MediaStreamTrackProcessor' in window) {
|
|
142
|
-
return createVideoTrackProcessor();
|
|
289
|
+
if (!isEmpty(renderOptions)) {
|
|
290
|
+
await processor.update({ renderOptions });
|
|
143
291
|
}
|
|
144
|
-
return createVideoTrackProcessorWithFallback(...params);
|
|
145
292
|
};
|
|
146
|
-
export const createVideoStreamProcess = ({ backgroundImageUrl, dynamicProcessingDimensions = () => false,
|
|
147
|
-
label = PROCESSOR_LABELS.VideoProcessor,
|
|
148
|
-
const videoSegmentationModel = options.videoSegmentationModel ?? 'selfie';
|
|
149
|
-
const segmenter = options.segmenters[videoSegmentationModel];
|
|
150
|
-
if (!segmenter) {
|
|
151
|
-
throw new Error('Segmenter is undefined');
|
|
152
|
-
}
|
|
293
|
+
export const createVideoStreamProcess = ({ backgroundImageUrl, dynamicProcessingDimensions = () => false, backgroundThreshold, downSampleFactor, foregroundThreshold, lightWrapEdgeBand, lightWrapIntensity, lightWrapTightness, morphDilateRadiusPx, morphErodeRadiusPx, morphPass, personCenterX, personCenterY, sigmaSpace, sigmaRangeHi, sigmaRangeLo, frameRate, gpuAPI = () => 'webgl', // dynamically changing the config will not be picked up, not a priority for now
|
|
294
|
+
label = PROCESSOR_LABELS.VideoProcessor, referenceProcessingHeight, maskCombineRatio, processingHeight, processingWidth, shouldEnable, stopAsMute = () => false, videoSegmentation, signals, getVideoProcessor, ...options }) => {
|
|
153
295
|
const backgroundBlurAmount = options.backgroundBlurAmount &&
|
|
154
|
-
getBlurKernelSize(options.backgroundBlurAmount, processingHeight,
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
effects: videoSegmentation,
|
|
160
|
-
foregroundThreshold,
|
|
161
|
-
backgroundBlurAmount,
|
|
162
|
-
edgeBlurAmount,
|
|
163
|
-
backgroundImageUrl,
|
|
164
|
-
maskCombineRatio,
|
|
165
|
-
});
|
|
166
|
-
const proxy = proxyWithLog(logger, label);
|
|
167
|
-
let _videoProcessor = undefined;
|
|
296
|
+
getBlurKernelSize(options.backgroundBlurAmount, referenceProcessingHeight ?? processingHeight, processingHeight);
|
|
297
|
+
const lightWrapBlurAmount = options.lightWrapBlurAmount &&
|
|
298
|
+
getBlurKernelSize(options.lightWrapBlurAmount, referenceProcessingHeight ?? processingHeight, processingHeight);
|
|
299
|
+
const edgeBlurAmount = options.edgeBlurAmount &&
|
|
300
|
+
getTentBlurSize(options.edgeBlurAmount, referenceProcessingHeight ?? processingHeight, processingHeight);
|
|
168
301
|
const props = {
|
|
169
|
-
videoSegmentationModel,
|
|
170
|
-
segmenters: {
|
|
171
|
-
selfie: options.segmenters.selfie &&
|
|
172
|
-
proxy(options.segmenters.selfie, 'Segmenter'),
|
|
173
|
-
deeplabV3: options.segmenters.deeplabV3 &&
|
|
174
|
-
proxy(options.segmenters.deeplabV3, 'Segmenter'),
|
|
175
|
-
},
|
|
176
|
-
transformer: proxy(transformer, 'Transformer'),
|
|
177
|
-
videoProcessor: () => {
|
|
178
|
-
if (!_videoProcessor) {
|
|
179
|
-
_videoProcessor = proxy(createVideoProcessor([transformer], getTrackProcessor(trackProcessorAPI() === 'stream', {
|
|
180
|
-
width: processingWidth,
|
|
181
|
-
height: processingHeight,
|
|
182
|
-
frameRate,
|
|
183
|
-
})), 'VideoProcessor');
|
|
184
|
-
}
|
|
185
|
-
return _videoProcessor;
|
|
186
|
-
},
|
|
187
302
|
videoSegmentation,
|
|
188
303
|
backgroundBlurAmount,
|
|
304
|
+
backgroundImageUrl,
|
|
305
|
+
backgroundThreshold,
|
|
306
|
+
downSampleFactor,
|
|
189
307
|
edgeBlurAmount,
|
|
190
308
|
foregroundThreshold,
|
|
191
309
|
frameRate,
|
|
192
|
-
|
|
310
|
+
lightWrapBlurAmount,
|
|
311
|
+
lightWrapEdgeBand,
|
|
312
|
+
lightWrapIntensity,
|
|
313
|
+
lightWrapTightness,
|
|
193
314
|
maskCombineRatio,
|
|
194
|
-
|
|
315
|
+
morphDilateRadiusPx,
|
|
316
|
+
morphErodeRadiusPx,
|
|
317
|
+
morphPass,
|
|
318
|
+
personCenterX,
|
|
319
|
+
personCenterY,
|
|
320
|
+
sigmaRangeHi,
|
|
321
|
+
sigmaRangeLo,
|
|
322
|
+
sigmaSpace,
|
|
195
323
|
};
|
|
196
324
|
return async (prevMediaTrack) => {
|
|
197
325
|
const features = updateFeatureProps(prevMediaTrack.getConstraints(), props);
|
|
@@ -204,55 +332,33 @@ label = PROCESSOR_LABELS.VideoProcessor, lowestProcessingHeight, maskCombineRati
|
|
|
204
332
|
}, 'Video processing is skipped');
|
|
205
333
|
return prevMediaTrack;
|
|
206
334
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (props.transformer.segmenter.status === 'new') {
|
|
217
|
-
await props.videoProcessor().open({
|
|
218
|
-
processingWidth: width,
|
|
219
|
-
processingHeight: height,
|
|
220
|
-
});
|
|
221
|
-
props.transformer.backend = gpuAPI();
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
props.transformer.update({
|
|
225
|
-
processingWidth: width,
|
|
226
|
-
processingHeight: height,
|
|
227
|
-
});
|
|
228
|
-
if (props.backgroundBlurAmount !== undefined) {
|
|
229
|
-
props.transformer.backgroundBlurAmount =
|
|
230
|
-
getBlurKernelSize(props.backgroundBlurAmount, height, lowestProcessingHeight);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
335
|
+
const { videoSegmentation, ...renderOptions } = props;
|
|
336
|
+
const videoProcessor = proxyWithLog(logger, label)(getVideoProcessor(), 'VideoProcessor');
|
|
337
|
+
if (dynamicProcessingDimensions()) {
|
|
338
|
+
const { width, height } = prevMediaTrack.getSettings();
|
|
339
|
+
if (props.backgroundBlurAmount !== undefined) {
|
|
340
|
+
renderOptions.backgroundBlurAmount = getBlurKernelSize(props.backgroundBlurAmount, referenceProcessingHeight ?? processingHeight, height ?? processingHeight);
|
|
341
|
+
}
|
|
342
|
+
if (props.lightWrapBlurAmount !== undefined) {
|
|
343
|
+
renderOptions.lightWrapBlurAmount = getBlurKernelSize(props.lightWrapBlurAmount, referenceProcessingHeight ?? processingHeight, height ?? processingHeight);
|
|
234
344
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
props.transformer.backend = gpuAPI();
|
|
345
|
+
if (props.edgeBlurAmount !== undefined) {
|
|
346
|
+
renderOptions.edgeBlurAmount = getTentBlurSize(props.edgeBlurAmount, referenceProcessingHeight ?? processingHeight, height ?? processingHeight);
|
|
238
347
|
}
|
|
239
|
-
|
|
348
|
+
await videoProcessor.update({
|
|
349
|
+
processingWidth: width,
|
|
350
|
+
processingHeight: height,
|
|
351
|
+
renderOptions: {
|
|
352
|
+
backend: gpuAPI(),
|
|
353
|
+
...renderOptions,
|
|
354
|
+
effects: videoSegmentation,
|
|
355
|
+
},
|
|
356
|
+
});
|
|
240
357
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
props.segmenters[features.videoSegmentationModel];
|
|
244
|
-
if (features.videoSegmentationModel &&
|
|
245
|
-
features.videoSegmentationModel !==
|
|
246
|
-
props.transformer.segmenter.modelAsset.modelName &&
|
|
247
|
-
model) {
|
|
248
|
-
props.transformer.segmenter = model;
|
|
358
|
+
else {
|
|
359
|
+
await applyFeatures(videoProcessor, { ...features, videoSegmentation }, referenceProcessingHeight);
|
|
249
360
|
}
|
|
250
|
-
const
|
|
251
|
-
.videoProcessor()
|
|
252
|
-
// FIXME: Change process to accept MediaStreamTrack instead of MediaStream
|
|
253
|
-
.process(new MediaStream([prevMediaTrack.track]));
|
|
254
|
-
const track = stream.getVideoTracks().at(0);
|
|
255
|
-
assert(track, 'Video track should be there');
|
|
361
|
+
const track = await videoProcessor.process(prevMediaTrack.track);
|
|
256
362
|
// Inherit contentHint from previous track
|
|
257
363
|
track.contentHint = prevMediaTrack.track.contentHint;
|
|
258
364
|
let trackReleaseTimeoutID = 0;
|
|
@@ -277,18 +383,15 @@ label = PROCESSOR_LABELS.VideoProcessor, lowestProcessingHeight, maskCombineRati
|
|
|
277
383
|
return this.previousMediaTrack?.muted;
|
|
278
384
|
},
|
|
279
385
|
mute(mute, _self, soft) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
: props.videoSegmentation ?? 'none';
|
|
283
|
-
muteStreamTrack(stream)(mute, 'video');
|
|
386
|
+
videoProcessor.mute(mute);
|
|
387
|
+
track.enabled = !mute;
|
|
284
388
|
if (stopAsMute() && !soft) {
|
|
285
389
|
if (mute) {
|
|
286
390
|
// Track cannot be stopped immediately as we need to ensure
|
|
287
391
|
// the last frame is black to avoid frozen frame in a gateway call
|
|
288
392
|
trackReleaseTimeoutID = window.setTimeout(() => {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
393
|
+
track.stop();
|
|
394
|
+
videoProcessor.close();
|
|
292
395
|
void this?.release?.();
|
|
293
396
|
trackReleaseTimeoutID = 0;
|
|
294
397
|
}, 500);
|
|
@@ -304,10 +407,8 @@ label = PROCESSOR_LABELS.VideoProcessor, lowestProcessingHeight, maskCombineRati
|
|
|
304
407
|
},
|
|
305
408
|
signals,
|
|
306
409
|
async release() {
|
|
307
|
-
|
|
410
|
+
videoProcessor.close();
|
|
308
411
|
await this.previousMediaTrack?.release();
|
|
309
|
-
_videoProcessor = undefined;
|
|
310
|
-
props.hasInitialized = false;
|
|
311
412
|
},
|
|
312
413
|
async applyConstraints(constraints) {
|
|
313
414
|
if (isEmpty(constraints)) {
|
|
@@ -318,43 +419,64 @@ label = PROCESSOR_LABELS.VideoProcessor, lowestProcessingHeight, maskCombineRati
|
|
|
318
419
|
if (isEmpty(features)) {
|
|
319
420
|
return;
|
|
320
421
|
}
|
|
321
|
-
applyFeatures(
|
|
422
|
+
await applyFeatures(videoProcessor, features, referenceProcessingHeight);
|
|
322
423
|
if (dynamicProcessingDimensions()) {
|
|
323
424
|
const acceptedSettings = this.previousMediaTrack?.source.getSettings();
|
|
324
425
|
if (acceptedSettings?.width && acceptedSettings?.height) {
|
|
325
|
-
|
|
426
|
+
const renderOptions = {};
|
|
427
|
+
if (props.backgroundBlurAmount !== undefined) {
|
|
428
|
+
renderOptions.backgroundBlurAmount =
|
|
429
|
+
getBlurKernelSize(props.backgroundBlurAmount, referenceProcessingHeight ??
|
|
430
|
+
processingHeight, acceptedSettings.height);
|
|
431
|
+
}
|
|
432
|
+
if (props.lightWrapBlurAmount !== undefined) {
|
|
433
|
+
renderOptions.lightWrapBlurAmount =
|
|
434
|
+
getBlurKernelSize(props.lightWrapBlurAmount, referenceProcessingHeight ??
|
|
435
|
+
processingHeight, acceptedSettings.height);
|
|
436
|
+
}
|
|
437
|
+
if (props.edgeBlurAmount !== undefined) {
|
|
438
|
+
renderOptions.edgeBlurAmount = getTentBlurSize(props.edgeBlurAmount, referenceProcessingHeight ?? processingHeight, acceptedSettings.height);
|
|
439
|
+
}
|
|
440
|
+
await videoProcessor.update({
|
|
326
441
|
processingHeight: acceptedSettings.height,
|
|
327
442
|
processingWidth: acceptedSettings.width,
|
|
443
|
+
renderOptions,
|
|
328
444
|
});
|
|
329
|
-
if (props.backgroundBlurAmount !== undefined) {
|
|
330
|
-
props.transformer.backgroundBlurAmount =
|
|
331
|
-
getBlurKernelSize(props.backgroundBlurAmount, acceptedSettings.height, lowestProcessingHeight);
|
|
332
|
-
}
|
|
333
445
|
}
|
|
334
446
|
}
|
|
335
|
-
const model = features.videoSegmentationModel &&
|
|
336
|
-
props.segmenters[features.videoSegmentationModel];
|
|
337
|
-
if (model &&
|
|
338
|
-
features.videoSegmentationModel !==
|
|
339
|
-
props.transformer.segmenter.modelAsset.modelName) {
|
|
340
|
-
props.transformer.segmenter = model;
|
|
341
|
-
}
|
|
342
447
|
},
|
|
343
448
|
getSettings: () => {
|
|
344
449
|
const contentHint = track.contentHint ?? '';
|
|
450
|
+
const ro = videoProcessor.renderOptions;
|
|
345
451
|
return {
|
|
346
|
-
videoSegmentation: transformer.effects,
|
|
347
|
-
foregroundThreshold: transformer.foregroundThreshold,
|
|
348
|
-
// Background blur amount is a function of height
|
|
349
452
|
backgroundBlurAmount: props.backgroundBlurAmount,
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
backgroundImageUrl: transformer.backgroundImageUrl,
|
|
353
|
-
videoSegmentationModel: transformer.segmenter.modelAsset.modelName,
|
|
354
|
-
pan: props.pan,
|
|
355
|
-
tilt: props.tilt,
|
|
356
|
-
zoom: props.zoom,
|
|
453
|
+
backgroundImageUrl: ro.backgroundImageUrl,
|
|
454
|
+
backgroundThreshold: ro.backgroundThreshold,
|
|
357
455
|
contentHint,
|
|
456
|
+
downSampleFactor: ro.downSampleFactor,
|
|
457
|
+
edgeBlurAmount: ro.edgeBlurAmount,
|
|
458
|
+
excludeBystanders: ro.excludeBystanders,
|
|
459
|
+
foregroundThreshold: ro.foregroundThreshold,
|
|
460
|
+
frameRate: props.frameRate,
|
|
461
|
+
height: props.height,
|
|
462
|
+
lightWrapBlurAmount: ro.lightWrapBlurAmount,
|
|
463
|
+
lightWrapEdgeBand: ro.lightWrapEdgeBand,
|
|
464
|
+
lightWrapIntensity: ro.lightWrapIntensity,
|
|
465
|
+
lightWrapTightness: ro.lightWrapTightness,
|
|
466
|
+
maskCombineRatio: ro.maskCombineRatio,
|
|
467
|
+
morphDilateRadiusPx: ro.morphDilateRadiusPx,
|
|
468
|
+
morphErodeRadiusPx: ro.morphErodeRadiusPx,
|
|
469
|
+
morphPass: ro.morphPass,
|
|
470
|
+
panEnabled: props.pan,
|
|
471
|
+
personCenterX: ro.personCenter[0],
|
|
472
|
+
personCenterY: ro.personCenter[1],
|
|
473
|
+
sigmaRangeHi: ro.sigmaRangeHi,
|
|
474
|
+
sigmaRangeLo: ro.sigmaRangeLo,
|
|
475
|
+
sigmaSpace: ro.sigmaSpace,
|
|
476
|
+
tiltEnabled: props.tilt,
|
|
477
|
+
videoSegmentation: ro.effects,
|
|
478
|
+
width: props.width,
|
|
479
|
+
zoomEnabled: props.zoom,
|
|
358
480
|
};
|
|
359
481
|
},
|
|
360
482
|
});
|