@pexip/media 20.1.0 → 20.3.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,5 +1,5 @@
1
- import { createVideoProcessor, createCanvasTransform, createVideoTrackProcessor, createVideoTrackProcessorWithFallback, isRenderEffects, isSegmentationModel, } from '@pexip/media-processor';
2
- import { muteStreamTrack, extractConstraintsWithKeys, getValueFromConstrainNumber, } from '@pexip/media-control';
1
+ import { createCanvasTransform, createVideoProcessor, createVideoTrackProcessor, createVideoTrackProcessorWithFallback, isRenderEffects, isSegmentationModel, } from '@pexip/media-processor';
2
+ import { extractConstraintsWithKeys, getValueFromConstrainNumber, muteStreamTrack, } from '@pexip/media-control';
3
3
  import { isEmpty, assert } from '@pexip/utils';
4
4
  import { createMediaTrack, getBlurKernelSize } from './utils';
5
5
  import { logger, proxyWithLog } from './logger';
@@ -94,7 +94,7 @@ export const updateFeatureProps = (constraints, props) => {
94
94
  }
95
95
  }, {});
96
96
  };
97
- const applyFeatures = (transformer, features) => {
97
+ const applyFeatures = (transformer, features, lowestProcessingHeight) => {
98
98
  for (const key in features) {
99
99
  const k = key;
100
100
  switch (k) {
@@ -110,7 +110,7 @@ const applyFeatures = (transformer, features) => {
110
110
  case 'backgroundBlurAmount': {
111
111
  const value = features[k];
112
112
  if (value !== undefined) {
113
- transformer[k] = getBlurKernelSize(value, transformer.height);
113
+ transformer[k] = getBlurKernelSize(value, transformer.height, lowestProcessingHeight);
114
114
  }
115
115
  break;
116
116
  }
@@ -141,19 +141,15 @@ const getTrackProcessor = (shouldUseStreamTrackProcessor, ...params) => {
141
141
  }
142
142
  return createVideoTrackProcessorWithFallback(...params);
143
143
  };
144
- export const createVideoStreamProcess = ({ trackProcessorAPI = () => 'stream', processingWidth, processingHeight, shouldEnable, frameRate,
145
- //backgroundBlurAmount,
146
- videoSegmentation,
147
- //edgeBlurAmount,
148
- foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label = PROCESSOR_LABELS.VideoProcessor, dynamicProcessingDimensions = () => false, gpuAPI = () => 'webgl', // dynamically changing the config will not be picked up, not a priority for now
149
- ...options }) => {
144
+ 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
145
+ label = PROCESSOR_LABELS.VideoProcessor, lowestProcessingHeight, maskCombineRatio, processingHeight, processingWidth, shouldEnable, stopAsMute = () => false, trackProcessorAPI = () => 'stream', videoSegmentation, signals, ...options }) => {
150
146
  const videoSegmentationModel = options.videoSegmentationModel ?? 'selfie';
151
147
  const segmenter = options.segmenters[videoSegmentationModel];
152
148
  if (!segmenter) {
153
149
  throw new Error('Segmenter is undefined');
154
150
  }
155
151
  const backgroundBlurAmount = options.backgroundBlurAmount &&
156
- getBlurKernelSize(options.backgroundBlurAmount, processingHeight);
152
+ getBlurKernelSize(options.backgroundBlurAmount, processingHeight, lowestProcessingHeight);
157
153
  const transformer = options.transformer ??
158
154
  createCanvasTransform(segmenter, {
159
155
  width: processingWidth,
@@ -199,29 +195,39 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
199
195
  const features = updateFeatureProps(prevMediaTrack.getConstraints(), props);
200
196
  const shouldEnabled = shouldEnable();
201
197
  if (!shouldEnabled || !prevMediaTrack.track) {
202
- logger.debug({ label, features, shouldEnabled }, 'Video processing is skipped');
198
+ logger.debug({
199
+ label,
200
+ features,
201
+ shouldEnabled,
202
+ }, 'Video processing is skipped');
203
203
  return prevMediaTrack;
204
204
  }
205
205
  if (!props.hasInitialized) {
206
206
  if (dynamicProcessingDimensions()) {
207
207
  const { width, height } = prevMediaTrack.getSettings();
208
- /**
209
- * when the processor is closed, the segmenter is not killed, but the track is released.
210
- * this means we can't listen on the track settings changes when we change quality
211
- * we need to update instead of open if the segmenter is already running
212
- */
213
- if (props.transformer.segmenter.status === 'new') {
214
- await props.videoProcessor().open({
215
- processingWidth: width,
216
- processingHeight: height,
217
- });
218
- props.transformer.backend = gpuAPI();
219
- }
220
- else {
221
- props.transformer.update({
222
- processingWidth: width,
223
- processingHeight: height,
224
- });
208
+ if (width && height) {
209
+ /**
210
+ * when the processor is closed, the segmenter is not killed, but the track is released.
211
+ * this means we can't listen on the track settings changes when we change quality
212
+ * we need to update instead of open if the segmenter is already running
213
+ */
214
+ if (props.transformer.segmenter.status === 'new') {
215
+ await props.videoProcessor().open({
216
+ processingWidth: width,
217
+ processingHeight: height,
218
+ });
219
+ props.transformer.backend = gpuAPI();
220
+ }
221
+ else {
222
+ props.transformer.update({
223
+ processingWidth: width,
224
+ processingHeight: height,
225
+ });
226
+ if (props.backgroundBlurAmount !== undefined) {
227
+ props.transformer.backgroundBlurAmount =
228
+ getBlurKernelSize(props.backgroundBlurAmount, height, lowestProcessingHeight);
229
+ }
230
+ }
225
231
  }
226
232
  }
227
233
  else {
@@ -230,7 +236,7 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
230
236
  }
231
237
  props.hasInitialized = true;
232
238
  }
233
- applyFeatures(props.transformer, features);
239
+ applyFeatures(props.transformer, features, lowestProcessingHeight);
234
240
  const model = features.videoSegmentationModel &&
235
241
  props.segmenters[features.videoSegmentationModel];
236
242
  if (features.videoSegmentationModel &&
@@ -247,30 +253,61 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
247
253
  assert(track, 'Video track should be there');
248
254
  // Inherit contentHint from previous track
249
255
  track.contentHint = prevMediaTrack.track.contentHint;
250
- const release = async () => {
251
- props.videoProcessor().close();
252
- await prevMediaTrack.release();
253
- _videoProcessor = undefined;
254
- props.hasInitialized = false;
255
- };
256
- const mute = (mute) => {
257
- props.transformer.effects = mute
258
- ? 'none'
259
- : props.videoSegmentation ?? 'none';
260
- prevMediaTrack.mute(mute);
261
- muteStreamTrack(stream)(mute, 'video');
262
- };
256
+ let trackReleaseTimeoutID = 0;
263
257
  return createMediaTrack({
264
258
  label,
265
259
  kind: 'videoinput',
266
- constraints: prevMediaTrack.getConstraints(),
267
- previousMediaTrack: prevMediaTrack,
268
- input: prevMediaTrack.input,
269
- expectedInput: prevMediaTrack.expectedInput,
260
+ get constraints() {
261
+ return this.previousMediaTrack?.getConstraints();
262
+ },
263
+ get previousMediaTrack() {
264
+ return prevMediaTrack;
265
+ },
266
+ get input() {
267
+ return this.previousMediaTrack?.input;
268
+ },
269
+ get expectedInput() {
270
+ return this.previousMediaTrack?.expectedInput;
271
+ },
270
272
  track,
271
- mute,
272
- release,
273
- applyConstraints: async (constraints) => {
273
+ overrideMute: true,
274
+ get muted() {
275
+ return this.previousMediaTrack?.muted;
276
+ },
277
+ mute(mute, _self, soft) {
278
+ props.transformer.effects = mute
279
+ ? 'none'
280
+ : props.videoSegmentation ?? 'none';
281
+ muteStreamTrack(stream)(mute, 'video');
282
+ if (stopAsMute() && !soft) {
283
+ if (mute) {
284
+ // Track cannot be stopped immediately as we need to ensure
285
+ // the last frame is black to avoid frozen frame in a gateway call
286
+ trackReleaseTimeoutID = window.setTimeout(() => {
287
+ for (const track of stream.getVideoTracks()) {
288
+ track.stop();
289
+ }
290
+ void this?.release?.();
291
+ trackReleaseTimeoutID = 0;
292
+ }, 500);
293
+ }
294
+ else {
295
+ if (trackReleaseTimeoutID) {
296
+ window.clearTimeout(trackReleaseTimeoutID);
297
+ trackReleaseTimeoutID = 0;
298
+ }
299
+ }
300
+ }
301
+ this.previousMediaTrack?.mute(mute, soft);
302
+ },
303
+ signals,
304
+ async release() {
305
+ props.videoProcessor().close();
306
+ await this.previousMediaTrack?.release();
307
+ _videoProcessor = undefined;
308
+ props.hasInitialized = false;
309
+ },
310
+ async applyConstraints(constraints) {
274
311
  if (isEmpty(constraints)) {
275
312
  return;
276
313
  }
@@ -279,14 +316,18 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
279
316
  if (isEmpty(features)) {
280
317
  return;
281
318
  }
282
- applyFeatures(props.transformer, features);
319
+ applyFeatures(props.transformer, features, lowestProcessingHeight);
283
320
  if (dynamicProcessingDimensions()) {
284
- const acceptedSettings = prevMediaTrack.source.getSettings();
285
- if (acceptedSettings.width && acceptedSettings.height) {
321
+ const acceptedSettings = this.previousMediaTrack?.source.getSettings();
322
+ if (acceptedSettings?.width && acceptedSettings?.height) {
286
323
  props.transformer.update({
287
324
  processingHeight: acceptedSettings.height,
288
325
  processingWidth: acceptedSettings.width,
289
326
  });
327
+ if (props.backgroundBlurAmount !== undefined) {
328
+ props.transformer.backgroundBlurAmount =
329
+ getBlurKernelSize(props.backgroundBlurAmount, acceptedSettings.height, lowestProcessingHeight);
330
+ }
290
331
  }
291
332
  }
292
333
  const model = features.videoSegmentationModel &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pexip/media",
3
- "version": "20.1.0",
3
+ "version": "20.3.0",
4
4
  "description": "Home for media related stuff",
5
5
  "homepage": "https://gitlab.com/pexip/zoo",
6
6
  "bugs": "https://gitlab.com/pexip/zoo/issues",
@@ -44,14 +44,14 @@
44
44
  "typecheck": "yarn tsc --noEmit -p ."
45
45
  },
46
46
  "dependencies": {
47
- "@pexip/media-control": "20.1.0",
48
- "@pexip/media-processor": "20.1.0",
49
- "@pexip/signal": "16.8.0",
50
- "@pexip/utils": "17.0.0"
47
+ "@pexip/media-control": "20.3.0",
48
+ "@pexip/media-processor": "20.3.0",
49
+ "@pexip/signal": "16.9.0",
50
+ "@pexip/utils": "17.0.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@jest/globals": "^29.7.0",
54
- "@pexip/bundler": "18.1.0",
54
+ "@pexip/bundler": "18.1.2",
55
55
  "@swc/core": "^1.10.12",
56
56
  "@swc/jest": "^0.2.37",
57
57
  "jest": "^29.5.12",
@@ -1 +0,0 @@
1
- {"root":["../src/audioMixingProcessor.ts","../src/audioProcessor.ts","../src/baseLogger.ts","../src/constants.ts","../src/displayMedia.ts","../src/index.ts","../src/logger.ts","../src/media.ts","../src/previewController.ts","../src/signals.ts","../src/status.ts","../src/typeGuard.ts","../src/types.ts","../src/userMedia.ts","../src/utils.ts","../src/videoProcessor.ts","../../../@types/globals.d.ts"],"version":"5.7.3"}