@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.
@@ -1,132 +1,283 @@
1
- import { createCanvasTransform, createVideoProcessor, createVideoTrackProcessor, createVideoTrackProcessorWithFallback, isRenderEffects, isSegmentationModel, } from '@pexip/media-processor';
2
- import { extractConstraintsWithKeys, getValueFromConstrainNumber, muteStreamTrack, } from '@pexip/media-control';
3
- import { isEmpty, assert } from '@pexip/utils';
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
- 'backgroundBlurAmount',
10
- 'backgroundImageUrl',
11
- 'maskCombineRatio',
12
- 'edgeBlurAmount',
13
- 'foregroundThreshold',
14
- 'frameRate',
15
- 'videoSegmentation',
16
- 'videoSegmentationModel',
17
- 'width',
18
- 'height',
19
- 'pan',
20
- 'tilt',
21
- 'zoom',
22
- 'contentHint',
23
- ];
24
- const getVideoConstraints = extractConstraintsWithKeys(FEATURE_KEYS);
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 extracted = getVideoConstraints(constraints);
27
- return FEATURE_KEYS.reduce((accm, key) => {
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 = ''] = []] = extracted[key];
50
+ const [[feature = ''] = []] = extractConstraints(key)(constraints);
31
51
  if (isVideoContentHint(feature) && props[key] !== feature) {
32
52
  props[key] = feature;
33
- accm[key] = feature;
34
- return accm;
53
+ features[key] = feature;
35
54
  }
36
- return accm;
55
+ break;
37
56
  }
38
57
  case 'videoSegmentation': {
39
- const [[feature] = []] = extracted[key];
58
+ const [[feature] = []] = extractConstraints(key)(constraints);
40
59
  if (isRenderEffects(feature) && props[key] !== feature) {
41
60
  props[key] = feature;
42
- accm[key] = feature;
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
- return accm;
63
+ break;
55
64
  }
56
65
  case 'backgroundImageUrl': {
57
- const [[feature] = []] = extracted[key];
66
+ const [[feature] = []] = extractConstraints(key)(constraints);
58
67
  if (feature) {
59
68
  props[key] = feature;
60
- accm[key] = feature;
61
- return accm;
69
+ features[key] = feature;
62
70
  }
63
- return accm;
71
+ break;
64
72
  }
65
- case 'width':
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 'backgroundBlurAmount': {
72
- const [feature] = extracted[key];
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
- accm[key] = value;
78
- return accm;
109
+ features[key] = value;
79
110
  }
80
111
  }
81
- return accm;
112
+ break;
82
113
  }
83
114
  case 'pan':
84
115
  case 'tilt':
85
116
  case 'zoom': {
86
- const [feature] = extracted[key];
117
+ const [feature] = extractConstraints(key)(constraints);
87
118
  if (feature !== undefined && props[key] !== feature) {
88
119
  props[key] = feature;
89
- accm[key] = feature;
90
- return accm;
120
+ features[key] = feature;
91
121
  }
92
- return accm;
122
+ break;
93
123
  }
94
124
  default:
95
- return accm;
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 = (transformer, features, lowestProcessingHeight) => {
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
- transformer[k] = value;
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
- transformer[k] = getBlurKernelSize(value, transformer.height, lowestProcessingHeight);
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 !== transformer.effects) {
122
- transformer.effects = value;
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 && value !== transformer.backgroundImageUrl) {
129
- transformer.backgroundImageUrl = value;
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
- const getTrackProcessor = (shouldUseStreamTrackProcessor, ...params) => {
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, edgeBlurAmount, foregroundThreshold, frameRate, gpuAPI = () => 'webgl', // dynamically changing the config will not be picked up, not a priority for now
147
- label = PROCESSOR_LABELS.VideoProcessor, lowestProcessingHeight, maskCombineRatio, processingHeight, processingWidth, shouldEnable, stopAsMute = () => false, trackProcessorAPI = () => 'stream', videoSegmentation, signals, ...options }) => {
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, lowestProcessingHeight);
155
- const transformer = options.transformer ??
156
- createCanvasTransform(segmenter, {
157
- width: processingWidth,
158
- height: processingHeight,
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
- backgroundImageUrl,
310
+ lightWrapBlurAmount,
311
+ lightWrapEdgeBand,
312
+ lightWrapIntensity,
313
+ lightWrapTightness,
193
314
  maskCombineRatio,
194
- hasInitialized: options.hasInitializedDeps ?? false,
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
- if (!props.hasInitialized) {
208
- if (dynamicProcessingDimensions()) {
209
- const { width, height } = prevMediaTrack.getSettings();
210
- if (width && height) {
211
- /**
212
- * when the processor is closed, the segmenter is not killed, but the track is released.
213
- * this means we can't listen on the track settings changes when we change quality
214
- * we need to update instead of open if the segmenter is already running
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
- else {
236
- await props.videoProcessor().open();
237
- props.transformer.backend = gpuAPI();
345
+ if (props.edgeBlurAmount !== undefined) {
346
+ renderOptions.edgeBlurAmount = getTentBlurSize(props.edgeBlurAmount, referenceProcessingHeight ?? processingHeight, height ?? processingHeight);
238
347
  }
239
- props.hasInitialized = true;
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
- applyFeatures(props.transformer, features, lowestProcessingHeight);
242
- const model = features.videoSegmentationModel &&
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 stream = await props
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
- props.transformer.effects = mute
281
- ? 'none'
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
- for (const track of stream.getVideoTracks()) {
290
- track.stop();
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
- props.videoProcessor().close();
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(props.transformer, features, lowestProcessingHeight);
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
- props.transformer.update({
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
- edgeBlurAmount: transformer.edgeBlurAmount,
351
- maskCombineRatio: transformer.maskCombineRatio,
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
  });