@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/dist/media.js CHANGED
@@ -7,7 +7,7 @@ import { createGetUserMediaProcess, requestUserMediaWithRetry, } from './userMed
7
7
  import { AUDIO_SETTINGS_KEYS, MIXING_SETTINGS_KEYS, PAN_TILT_ZOOM_SETTINGS_KEYS, VIDEO_SETTINGS_KEYS, buildMedia, createMediaProcessor, diffSettings, getDevicesChanges, getSettingsFromKeys, hasPtzFeature, isApplyingRenderEffect, mergeSettings, refineMediaConstraints, } from './utils';
8
8
  import { getPermissionStatus, isRequestPermissionNeeded } from './status';
9
9
  import { isMedia } from './typeGuard';
10
- import { updateFeatureProps as getVideoFeatures } from './videoProcessor';
10
+ import { updateFeatureProps as getVideoFeatures, mapFeatureToSettings, mapSettingsToFeatures, } from './videoProcessor';
11
11
  import { updateFeatureProps as getAudioFeatures } from './audioProcessor';
12
12
  import { updateFeatureProps as getMixingFeatures } from './audioMixingProcessor';
13
13
  import { GET_USER_MEDIA_TIMEOUT_MS, DEVICE_CHANGE_DEBOUNCE_TIMEOUT_MS, } from './constants';
@@ -59,7 +59,7 @@ export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDisc
59
59
  const defaultVideoSettings = getSettingsFromKeys(VIDEO_SETTINGS_KEYS, getDefaultConstraints?.().video);
60
60
  const videoFeaturesChanged = defaultVideoSettings === false
61
61
  ? undefined
62
- : diffSettings(VIDEO_SETTINGS_KEYS)(prevVideoSettings ?? defaultVideoSettings, videoFeatures);
62
+ : diffSettings(VIDEO_SETTINGS_KEYS)(prevVideoSettings ?? defaultVideoSettings, mapFeatureToSettings(videoFeatures));
63
63
  const defaultMixingSettings = getSettingsFromKeys(MIXING_SETTINGS_KEYS, getDefaultConstraints?.().audio);
64
64
  const mixingFeaturesChanged = defaultMixingSettings === false
65
65
  ? undefined
@@ -68,7 +68,7 @@ export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDisc
68
68
  const ptzFeaturesChanges = defaultPTZSettings === false
69
69
  ? undefined
70
70
  : hasPtzFeature() &&
71
- diffSettings(PAN_TILT_ZOOM_SETTINGS_KEYS)(prevVideoSettings ?? defaultPTZSettings, videoFeatures);
71
+ diffSettings(PAN_TILT_ZOOM_SETTINGS_KEYS)(prevVideoSettings ?? defaultPTZSettings, mapFeatureToSettings(videoFeatures));
72
72
  const audioRequest = refineMediaConstraints({
73
73
  kind: 'audioinput',
74
74
  request: constraints.audio,
@@ -154,8 +154,8 @@ export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDisc
154
154
  : undefined;
155
155
  if (audioDiff || videoDiff) {
156
156
  await currentMedia?.applyConstraints({
157
- audio: audioDiff,
158
- video: videoDiff,
157
+ audio: audioDiff && mapSettingsToFeatures(audioDiff),
158
+ video: videoDiff && mapSettingsToFeatures(videoDiff),
159
159
  });
160
160
  }
161
161
  };
@@ -39,6 +39,7 @@ export interface PreviewStreamParams {
39
39
  audio?: InputConstraintSet | false;
40
40
  video?: InputConstraintSet | false;
41
41
  };
42
+ stopVideoTrackAsMute?: () => boolean;
42
43
  }
43
44
  export interface PreviewControllerProps {
44
45
  media: Media | undefined;
@@ -51,6 +52,7 @@ export interface PreviewControllerProps {
51
52
  discardMedia: boolean;
52
53
  initialized: boolean;
53
54
  signals: MediaSignals;
55
+ keepVideoMuted: boolean;
54
56
  }
55
57
  export interface PreviewStreamController {
56
58
  media: Media | undefined;
@@ -83,6 +85,6 @@ export interface PreviewStreamController {
83
85
  onVideoMuted(callback: EventCallback<boolean | undefined>): Unsubscribe;
84
86
  onVideoSuspended(callback: EventCallback<boolean | undefined>): Unsubscribe;
85
87
  }
86
- export declare const createPreviewStreamController: ({ getCurrentDevices, getCurrentMedia, updateMainStream, onEnded, mainMediaSignal, queueOptions, audioProcessors, videoProcessors, getDefaultConstraints, signals, }: PreviewStreamParams) => PreviewStreamController;
88
+ export declare const createPreviewStreamController: ({ getCurrentDevices, getCurrentMedia, updateMainStream, onEnded, mainMediaSignal, queueOptions, audioProcessors, videoProcessors, getDefaultConstraints, signals, stopVideoTrackAsMute, }: PreviewStreamParams) => PreviewStreamController;
87
89
  export type CreatePreviewStreamController = typeof createPreviewStreamController;
88
90
  export {};
@@ -51,7 +51,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
51
51
  throttleInMS: DEFAULT_QUEUE_THROTTLE_MS,
52
52
  delayInMS: DEFAULT_QUEUE_DELAY_MS,
53
53
  dropLast: DEFAULT_QUEUE_DROP_LAST,
54
- }, audioProcessors, videoProcessors, getDefaultConstraints = () => ({}), signals, }) => {
54
+ }, audioProcessors, videoProcessors, getDefaultConstraints = () => ({}), signals, stopVideoTrackAsMute = () => false, }) => {
55
55
  const queue = createAsyncQueue(queueOptions);
56
56
  const eventHandlers = {};
57
57
  const internalProps = {
@@ -62,6 +62,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
62
62
  discardMedia: false,
63
63
  initialized: false,
64
64
  signals,
65
+ keepVideoMuted: false,
65
66
  };
66
67
  const logger = createModuleLogger({
67
68
  module: 'PreviewStreamController',
@@ -125,7 +126,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
125
126
  eventHandlers.audioMuted?.(track.muted);
126
127
  }
127
128
  else {
128
- eventHandlers.videoMuted?.(track.muted);
129
+ eventHandlers.videoMuted?.(props.keepVideoMuted || track.muted);
129
130
  }
130
131
  }),
131
132
  props.signals.onMediaTrackSuspended?.add(track => {
@@ -151,7 +152,9 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
151
152
  break;
152
153
  }
153
154
  case 'video': {
154
- eventHandlers.videoMuted?.(isTrackMuted(track) || isTrackEnded(track));
155
+ eventHandlers.videoMuted?.(props.keepVideoMuted ||
156
+ isTrackMuted(track) ||
157
+ isTrackEnded(track));
155
158
  break;
156
159
  }
157
160
  }
@@ -163,7 +166,9 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
163
166
  break;
164
167
  }
165
168
  case 'video': {
166
- eventHandlers.videoMuted?.(isTrackMuted(track) || isTrackEnded(track));
169
+ eventHandlers.videoMuted?.(props.keepVideoMuted ||
170
+ isTrackMuted(track) ||
171
+ isTrackEnded(track));
167
172
  break;
168
173
  }
169
174
  }
@@ -173,7 +178,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
173
178
  getUserMedia: requestUserMediaWithRetry(() => Promise.resolve(getCurrentDevices())),
174
179
  signals: props.signals,
175
180
  getCurrentDevices: () => Promise.resolve(getCurrentDevices()),
176
- stopVideoTrackAsMute: () => false,
181
+ stopVideoTrackAsMute,
177
182
  updateMedia: constraints => updateMedia(constraints),
178
183
  });
179
184
  const mergeMediaConstraints = (constraints) => {
@@ -223,6 +228,14 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
223
228
  getCurrentDevices: () => Promise.resolve(getCurrentDevices()),
224
229
  shouldDiscardMedia: () => props.discardMedia,
225
230
  onMediaTracksChanged: (media, tracks) => {
231
+ if (props.keepVideoMuted) {
232
+ for (const track of tracks) {
233
+ if (track.kind === 'videoinput') {
234
+ track.mute(true);
235
+ }
236
+ }
237
+ props.keepVideoMuted = false;
238
+ }
226
239
  props.media = media;
227
240
  queue.enqueue(async () => {
228
241
  await processAndUpdateMedia(media, tracks, false);
@@ -328,14 +341,16 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
328
341
  props.updatingPreviewAudio = false;
329
342
  }
330
343
  };
331
- const updateVideoInput = async (input) => {
344
+ const updateVideoInput = async (input, syncMuteState = true) => {
332
345
  try {
333
346
  props.videoInput = input;
334
347
  props.updatingPreviewVideo = true;
335
348
  if (input === undefined) {
336
349
  return await releaseVideo();
337
350
  }
338
- return await updatePreviewMedia({ video: { device: { exact: input } } });
351
+ props.keepVideoMuted = syncMuteState && !!props.media?.videoMuted;
352
+ await updatePreviewMedia({ video: { device: { exact: input } } });
353
+ return;
339
354
  }
340
355
  catch (error) {
341
356
  if (error instanceof Error) {
@@ -373,7 +388,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
373
388
  const audioSettings = extractFeaturesToConstraints(AUDIO_SETTINGS_KEYS, previewAudio);
374
389
  // Omit the width & height settings from the preview so that the one
375
390
  // from the main can be applied
376
- const { width: _width, height: _height, ...videoSettings } = extractFeaturesToConstraints(VIDEO_SETTINGS_KEYS, previewVideo);
391
+ const { width: _width, height: _height, edgeBlurAmount: _edgeBlurAmount, lightWrapBlurAmount: _lightWrapBlurAmount, backgroundBlurAmount: _backgroundBlurAmount, ...videoSettings } = extractFeaturesToConstraints(VIDEO_SETTINGS_KEYS, previewVideo);
377
392
  logger.info({ audioInput, videoInput, audioSettings, videoSettings }, 'Apply changes to main');
378
393
  try {
379
394
  await replaceMainStream({
@@ -475,7 +490,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
475
490
  return updateAudioInput(input);
476
491
  }
477
492
  case 'videoinput': {
478
- return updateVideoInput(input);
493
+ return updateVideoInput(input, false);
479
494
  }
480
495
  }
481
496
  },
@@ -1,5 +1,5 @@
1
1
  import type { AnalyzerNodeInit } from '@pexip/media-processor';
2
- import type { AudioContentHint, Media, VideoContentHint, VideoStreamTrackProcessorAPIs } from './types';
2
+ import type { AudioContentHint, Media, VideoContentHint } from './types';
3
3
  import { UserMediaStatus } from './types';
4
4
  export declare const isNonNullObject: (value: unknown) => value is Record<string, unknown>;
5
5
  export declare const isUserMediaStatus: (value: unknown) => value is UserMediaStatus;
@@ -7,4 +7,3 @@ export declare const isMedia: (value: unknown) => value is Media;
7
7
  export declare const isAnalyzerNodeInitProp: (value: unknown) => value is AnalyzerNodeInit | undefined;
8
8
  export declare const isAudioContentHint: (value: unknown) => value is AudioContentHint;
9
9
  export declare const isVideoContentHint: (value: unknown) => value is VideoContentHint;
10
- export declare const isVideoStreamTrackProcessorAPIs: (value: unknown) => value is VideoStreamTrackProcessorAPIs;
package/dist/typeGuard.js CHANGED
@@ -38,6 +38,3 @@ export const isVideoContentHint = (value) => {
38
38
  }
39
39
  return false;
40
40
  };
41
- export const isVideoStreamTrackProcessorAPIs = (value) => {
42
- return (typeof value === 'string' && (value === 'stream' || value === 'canvas'));
43
- };
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { IndexedDevices, InputConstraintSet, InputDeviceConstraint, MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
2
- import type { RendererOptions, RenderEffects, Segmenter, SegmentationModel } from '@pexip/media-processor';
2
+ import type { RendererOptions, RenderEffects } from '@pexip/media-processor';
3
3
  import type { Signal } from '@pexip/signal';
4
4
  export type Unsubscribe = () => void;
5
5
  export interface ExtendedMediaTrackSettings extends MediaTrackSettings {
@@ -14,10 +14,12 @@ export interface ExtendedMediaTrackSettings extends MediaTrackSettings {
14
14
  maskCombineRatio?: number;
15
15
  foregroundThreshold?: number;
16
16
  videoSegmentation?: RenderEffects;
17
- videoSegmentationModel?: SegmentationModel;
18
- pan?: boolean;
19
- tilt?: boolean;
20
- zoom?: boolean;
17
+ panEnabled?: boolean;
18
+ tiltEnabled?: boolean;
19
+ zoomEnabled?: boolean;
20
+ pan?: number;
21
+ tilt?: number;
22
+ zoom?: number;
21
23
  contentHint?: AudioContentHint | VideoContentHint;
22
24
  }
23
25
  export type ExtendedMediaTrackSettingsKey = keyof ExtendedMediaTrackSettings;
@@ -230,12 +232,6 @@ export type GetUserMediaProcess = (request: {
230
232
  };
231
233
  currentMedia: Media | undefined;
232
234
  }) => Promise<Media>;
233
- /**
234
- * Use which processor API to process the stream track
235
- * `stream` - Use `MediaStreamTrackProcessor`, when available
236
- * `canvas` - Use Canvas
237
- */
238
- export type VideoStreamTrackProcessorAPIs = 'stream' | 'canvas';
239
235
  export declare enum UserMediaStatus {
240
236
  /**
241
237
  * The initial status
@@ -531,7 +527,7 @@ export interface MediaController {
531
527
  */
532
528
  tryAndGetUserMedia: (constraints: MediaDeviceRequest) => void;
533
529
  }
534
- export type VideoRenderParams = Omit<RendererOptions, 'effects'> & {
530
+ export type VideoRenderParams = Omit<RendererOptions, 'effects' | 'personCenter'> & {
535
531
  /**
536
532
  * Target frame rate for the video segmentation
537
533
  */
@@ -545,6 +541,8 @@ export type VideoRenderParams = Omit<RendererOptions, 'effects'> & {
545
541
  pan?: boolean;
546
542
  tilt?: boolean;
547
543
  zoom?: boolean;
544
+ personCenterX?: number;
545
+ personCenterY?: number;
548
546
  };
549
547
  export interface MediaChangesSignals {
550
548
  onAddTrack: Signal<MediaStreamTrack>;
@@ -571,9 +569,6 @@ export declare enum DeniedDevices {
571
569
  Camera = "camera",
572
570
  Both = "microphone-and-camera"
573
571
  }
574
- export type Segmenters = {
575
- [Property in SegmentationModel]: Segmenter;
576
- };
577
572
  /**
578
573
  * Audio content hints are only applicable when the MediaStreamTrack contains an
579
574
  * audio track
@@ -11,7 +11,10 @@ export declare const toSameDeviceStatus: ({ audio, video, }: {
11
11
  audio: boolean;
12
12
  video: boolean;
13
13
  }) => UserMediaStatus.PermissionsGranted | UserMediaStatus.PermissionsGrantedFallback | UserMediaStatus.PermissionsGrantedFallbackAudioinput | UserMediaStatus.PermissionsGrantedFallbackVideoinput;
14
- export declare const toOnlyDeviceStatus: (kind: "audioinput" | "videoinput", matched: boolean, devices: IndexedDevices) => UserMediaStatus.PermissionsRejectedAudioInput | UserMediaStatus.PermissionsRejectedVideoInput | UserMediaStatus.PermissionsOnlyAudioinput | UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices | UserMediaStatus.PermissionsOnlyAudioinputFallback | UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices | UserMediaStatus.PermissionsOnlyVideoinput | UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices | UserMediaStatus.PermissionsOnlyVideoinputFallback | UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
14
+ export declare const toOnlyDeviceStatus: (kind: "audioinput" | "videoinput", streamedRequestedDevices: {
15
+ audio: boolean;
16
+ video: boolean;
17
+ }, devices: IndexedDevices) => UserMediaStatus.PermissionsRejectedAudioInput | UserMediaStatus.PermissionsRejectedVideoInput | UserMediaStatus.PermissionsOnlyAudioinput | UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices | UserMediaStatus.PermissionsOnlyAudioinputFallback | UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices | UserMediaStatus.PermissionsOnlyVideoinput | UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices | UserMediaStatus.PermissionsOnlyVideoinputFallback | UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
15
18
  /**
16
19
  * Check if there is any track match with the `kind`
17
20
  */
package/dist/userMedia.js CHANGED
@@ -71,12 +71,16 @@ export const toSameDeviceStatus = ({ audio, video, }) => {
71
71
  }
72
72
  return UserMediaStatus.PermissionsGrantedFallback;
73
73
  };
74
- export const toOnlyDeviceStatus = (kind, matched, devices) => {
75
- const hasAnotherTypeOfAuthorizedDevice = devices.anyAuthorizedDevice(kind === 'audioinput' ? 'videoinput' : 'audioinput');
76
- const hasAnotherTypeOfDevice = devices.size(kind === 'audioinput' ? 'videoinput' : 'audioinput') > 0;
74
+ export const toOnlyDeviceStatus = (kind, streamedRequestedDevices, devices) => {
75
+ const [self, other] = kind === 'audioinput' ? ['audio', 'video'] : ['video', 'audio'];
76
+ const otherKind = kind === 'audioinput' ? 'videoinput' : 'audioinput';
77
+ const matched = streamedRequestedDevices[self];
78
+ const anotherDeviceRequested = streamedRequestedDevices[other];
79
+ const hasAnotherTypeOfAuthorizedDevice = devices.anyAuthorizedDevice(otherKind);
80
+ const hasAnotherTypeOfDevice = devices.size(otherKind) > 0;
77
81
  if (matched) {
78
82
  if (hasAnotherTypeOfDevice) {
79
- if (hasAnotherTypeOfAuthorizedDevice) {
83
+ if (hasAnotherTypeOfAuthorizedDevice || !anotherDeviceRequested) {
80
84
  return kind === 'audioinput'
81
85
  ? UserMediaStatus.PermissionsOnlyAudioinput
82
86
  : UserMediaStatus.PermissionsOnlyVideoinput;
@@ -90,7 +94,7 @@ export const toOnlyDeviceStatus = (kind, matched, devices) => {
90
94
  : UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices;
91
95
  }
92
96
  if (hasAnotherTypeOfDevice) {
93
- if (hasAnotherTypeOfAuthorizedDevice) {
97
+ if (hasAnotherTypeOfAuthorizedDevice || !anotherDeviceRequested) {
94
98
  return kind === 'audioinput'
95
99
  ? UserMediaStatus.PermissionsOnlyAudioinputFallback
96
100
  : UserMediaStatus.PermissionsOnlyVideoinputFallback;
@@ -421,10 +425,10 @@ export const requestUserMedia = (getCurrentDevices, getMedia = getUserMedia) =>
421
425
  ? constraints.video
422
426
  : false,
423
427
  });
424
- const { audio, video } = isStreamingRequestedDevices(constraints, stream, devices);
425
- const onlyAudioStatus = toOnlyDeviceStatus('audioinput', audio, devices);
426
- const onlyVideoStatus = toOnlyDeviceStatus('videoinput', video, devices);
427
- const status = deriveDeviceStatus(onlyAudioStatus, onlyVideoStatus, toSameDeviceStatus({ audio, video }));
428
+ const streamedRequestedDevices = isStreamingRequestedDevices(constraints, stream, devices);
429
+ const onlyAudioStatus = toOnlyDeviceStatus('audioinput', streamedRequestedDevices, devices);
430
+ const onlyVideoStatus = toOnlyDeviceStatus('videoinput', streamedRequestedDevices, devices);
431
+ const status = deriveDeviceStatus(onlyAudioStatus, onlyVideoStatus, toSameDeviceStatus(streamedRequestedDevices));
428
432
  return [stream, status];
429
433
  }
430
434
  catch (error) {
@@ -521,10 +525,10 @@ export const createGetUserMediaProcess = ({ getCurrentDevices, getUserMedia, sco
521
525
  }));
522
526
  }
523
527
  const relaxedConstraints = {};
524
- if (constraints.audio) {
528
+ if (constraints.audio !== undefined) {
525
529
  relaxedConstraints.audio = relaxInputConstraint('audioinput', constraints.audio, currentDevices);
526
530
  }
527
- if (constraints.video) {
531
+ if (constraints.video !== undefined) {
528
532
  relaxedConstraints.video = relaxInputConstraint('videoinput', constraints.video, currentDevices);
529
533
  }
530
534
  const [stream, status] = await getUserMedia(relaxedConstraints);
package/dist/utils.d.ts CHANGED
@@ -54,17 +54,28 @@ export declare const getSettingsFromKeys: (keys: ExtendedMediaTrackSettingsKey[]
54
54
  export declare const diffSettings: (keysToLookFor: ExtendedMediaTrackSettingsKey[]) => (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => ExtendedMediaTrackSettings | undefined;
55
55
  export declare const mergeSettings: (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => ExtendedMediaTrackSettings | undefined;
56
56
  /**
57
- * A function to get the blur kernel size of image height
57
+ * A function to get the blur kernel size of image height based on
58
+ * Kawase blur algorithm
58
59
  *
59
- * @param percentage - The percentage of image height to calculate the blur
60
- * kernel size
61
- * @param height - The image height
62
- * @param lowestProcessingHeight - The lowest image height that is processed by the consumer, used to adjust the blur kernel size across different resolutions
60
+ * @param norm - [0-100] The normalized blur amount
61
+ * @param heightRef - The reference image height
62
+ * @param height - The current image height
63
63
  * @param max - The upper bound
64
64
  *
65
65
  * @returns blur kernel size
66
66
  */
67
- export declare const getBlurKernelSize: (percentage: number, height: number, lowestProcessingHeight?: number, max?: number) => number;
67
+ export declare const getBlurKernelSize: (norm: number, heightRef: number, height: number, max?: number) => number;
68
+ /**
69
+ * A function to get the Tent blur size of image height
70
+ *
71
+ * @param norm - [0-100] The normalized blur amount
72
+ * @param heightRef - The reference image height
73
+ * @param height - The current image height
74
+ * @param max - The upper bound
75
+ *
76
+ * @returns tent blur size
77
+ */
78
+ export declare const getTentBlurSize: (norm: number, heightRef: number, height: number, max?: number) => number;
68
79
  /**
69
80
  * Apply the content hint to the track
70
81
  *
package/dist/utils.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { createStreamTrackEventSubscriptions, createTrackDevicesChanges, extractConstraintsWithKeys, findDeviceFromConstraints, relaxInputConstraint, resolveMediaDeviceConstraints, shouldRequestDevice, toMediaDeviceInputKind, } from '@pexip/media-control';
2
- import { calculateMaxBlurPass } from '@pexip/media-processor';
2
+ import { calculateMaxBlurPass, clamping, } from '@pexip/media-processor';
3
3
  import { hasOwn, assert, isEmpty } from '@pexip/utils';
4
4
  import { internalSignals } from './signals';
5
+ import { MAX_TENT_BLUR_SIZE } from './constants';
6
+ import { mapFeatureToSettings } from './videoProcessor';
5
7
  export const makeDeriveDeviceStatus = (constraints) => (audio, video, both) => {
6
8
  if (constraints.audio) {
7
9
  if (constraints.video) {
@@ -570,7 +572,6 @@ export const AUDIO_SETTINGS_KEYS = [
570
572
  export const VIDEO_SETTINGS_KEYS = [
571
573
  'frameRate',
572
574
  'videoSegmentation',
573
- 'videoSegmentationModel',
574
575
  'foregroundThreshold',
575
576
  'backgroundBlurAmount',
576
577
  'edgeBlurAmount',
@@ -628,10 +629,11 @@ export const getSettingsFromKeys = (keys, settings) => {
628
629
  return settings;
629
630
  }
630
631
  const result = {};
632
+ const mapped = mapFeatureToSettings(settings);
631
633
  for (const key of keys) {
632
- if (settings[key] !== undefined) {
634
+ if (mapped[key] !== undefined) {
633
635
  // @ts-expect-error --- Type issue and need to be fixed when we have time
634
- result[key] = settings[key];
636
+ result[key] = mapped[key];
635
637
  }
636
638
  }
637
639
  return result;
@@ -684,26 +686,39 @@ export const mergeSettings = (settingsA, settingsB) => {
684
686
  }
685
687
  return { ...settingsA, ...settingsB };
686
688
  };
689
+ const clampNorm = clamping(0, 100);
687
690
  /**
688
- * A function to get the blur kernel size of image height
691
+ * A function to get the blur kernel size of image height based on
692
+ * Kawase blur algorithm
689
693
  *
690
- * @param percentage - The percentage of image height to calculate the blur
691
- * kernel size
692
- * @param height - The image height
693
- * @param lowestProcessingHeight - The lowest image height that is processed by the consumer, used to adjust the blur kernel size across different resolutions
694
+ * @param norm - [0-100] The normalized blur amount
695
+ * @param heightRef - The reference image height
696
+ * @param height - The current image height
694
697
  * @param max - The upper bound
695
698
  *
696
699
  * @returns blur kernel size
697
700
  */
698
- export const getBlurKernelSize = (percentage, height, lowestProcessingHeight, max = calculateMaxBlurPass(height)) => {
699
- if (height <= 0 || percentage <= 0 || max <= 0) {
701
+ export const getBlurKernelSize = (norm, heightRef, height, max = calculateMaxBlurPass(height)) => {
702
+ if (height <= 0 || norm <= 0 || max <= 0 || heightRef <= 0) {
700
703
  return 0;
701
704
  }
702
- const minHeightMaxBlurPass = lowestProcessingHeight
703
- ? calculateMaxBlurPass(lowestProcessingHeight)
704
- : max;
705
- return Math.min(Math.ceil(percentage * 0.01 * max) +
706
- Math.max(0, max - minHeightMaxBlurPass), max);
705
+ return clamping(0, max)(Math.round(clampNorm(norm) * 0.01 * max - Math.log2(heightRef / height)));
706
+ };
707
+ /**
708
+ * A function to get the Tent blur size of image height
709
+ *
710
+ * @param norm - [0-100] The normalized blur amount
711
+ * @param heightRef - The reference image height
712
+ * @param height - The current image height
713
+ * @param max - The upper bound
714
+ *
715
+ * @returns tent blur size
716
+ */
717
+ export const getTentBlurSize = (norm, heightRef, height, max = MAX_TENT_BLUR_SIZE) => {
718
+ if (height <= 0 || norm <= 0 || heightRef <= 0 || max <= 0) {
719
+ return 0;
720
+ }
721
+ return clamping(0, max)(Math.round((norm * 0.01 * max * height) / heightRef));
707
722
  };
708
723
  /**
709
724
  * Apply the content hint to the track
@@ -1,27 +1,15 @@
1
- import type { RenderBackend, SegmentationModel, SegmentationTransform, VideoProcessor } from '@pexip/media-processor';
2
- import type { MediaDeviceRequest } from '@pexip/media-control';
3
- import type { MediaSignals, Segmenters, TrackProcessor, VideoContentHint, VideoRenderParams, VideoStreamTrackProcessorAPIs } from './types';
4
- interface ProcessorDeps {
5
- segmenters: Partial<Segmenters>;
6
- transformer?: SegmentationTransform;
7
- videoProcessor?: () => VideoProcessor;
8
- videoSegmentationModel?: SegmentationModel;
9
- }
10
- interface VideoStreamProcessOptions extends Partial<VideoRenderParams>, Omit<ProcessorDeps, 'videoProcessor'> {
11
- /**
12
- * What API to use for processing the MediaStreamTrack
13
- * `stream` - Use MediaStreamTrackProcessor, when available
14
- * `canvas` - Use Canvas
15
- */
16
- trackProcessorAPI?: () => VideoStreamTrackProcessorAPIs;
1
+ import type { RenderBackend, VideoProcessor } from '@pexip/media-processor';
2
+ import type { InputConstraintSet, MediaDeviceRequest } from '@pexip/media-control';
3
+ import type { ExtendedMediaTrackSettings, MediaSignals, TrackProcessor, VideoContentHint, VideoRenderParams } from './types';
4
+ interface VideoStreamProcessOptions extends Partial<VideoRenderParams> {
17
5
  /**
18
6
  * Whether or to enable this processor
19
7
  */
20
8
  shouldEnable: () => boolean;
21
- lowestProcessingHeight?: number;
9
+ getVideoProcessor: () => VideoProcessor;
10
+ referenceProcessingHeight?: number;
22
11
  processingWidth: number;
23
12
  processingHeight: number;
24
- hasInitializedDeps?: boolean;
25
13
  width?: number;
26
14
  height?: number;
27
15
  label?: string;
@@ -30,13 +18,43 @@ interface VideoStreamProcessOptions extends Partial<VideoRenderParams>, Omit<Pro
30
18
  gpuAPI?: () => RenderBackend;
31
19
  signals?: MediaSignals;
32
20
  }
33
- interface VideoStreamProcessProps extends Partial<VideoRenderParams>, Required<ProcessorDeps> {
34
- hasInitialized: boolean;
21
+ interface VideoStreamProcessProps extends Partial<VideoRenderParams> {
35
22
  contentHint?: VideoContentHint;
36
23
  }
37
- declare const FEATURE_KEYS: readonly ["backgroundBlurAmount", "backgroundImageUrl", "maskCombineRatio", "edgeBlurAmount", "foregroundThreshold", "frameRate", "videoSegmentation", "videoSegmentationModel", "width", "height", "pan", "tilt", "zoom", "contentHint"];
38
- type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
24
+ declare const FEATURE_KEYS: {
25
+ readonly backgroundBlurAmount: "number";
26
+ readonly backgroundImageUrl: "string";
27
+ readonly backgroundThreshold: "number";
28
+ readonly maskCombineRatio: "number";
29
+ readonly edgeBlurAmount: "number";
30
+ readonly foregroundThreshold: "number";
31
+ readonly frameRate: "number";
32
+ readonly videoSegmentation: "string";
33
+ readonly width: "number";
34
+ readonly height: "number";
35
+ readonly pan: "boolean";
36
+ readonly tilt: "boolean";
37
+ readonly zoom: "boolean";
38
+ readonly contentHint: "string";
39
+ readonly sigmaSpace: "number";
40
+ readonly sigmaRangeLo: "number";
41
+ readonly sigmaRangeHi: "number";
42
+ readonly excludeBystanders: "boolean";
43
+ readonly personCenterX: "number";
44
+ readonly personCenterY: "number";
45
+ readonly morphErodeRadiusPx: "number";
46
+ readonly morphPass: "number";
47
+ readonly morphDilateRadiusPx: "number";
48
+ readonly lightWrapIntensity: "number";
49
+ readonly lightWrapTightness: "number";
50
+ readonly lightWrapEdgeBand: "number";
51
+ readonly lightWrapBlurAmount: "number";
52
+ readonly downSampleFactor: "number";
53
+ };
54
+ type FeaturePropKeys = keyof typeof FEATURE_KEYS;
39
55
  type FeatureProps = Pick<Partial<VideoStreamProcessProps>, FeaturePropKeys>;
40
56
  export declare const updateFeatureProps: (constraints: MediaDeviceRequest["video"], props: FeatureProps) => FeatureProps;
41
- export declare const createVideoStreamProcess: ({ backgroundImageUrl, dynamicProcessingDimensions, edgeBlurAmount, foregroundThreshold, frameRate, gpuAPI, label, lowestProcessingHeight, maskCombineRatio, processingHeight, processingWidth, shouldEnable, stopAsMute, trackProcessorAPI, videoSegmentation, signals, ...options }: VideoStreamProcessOptions) => TrackProcessor;
57
+ export declare const mapFeatureToSettings: (props: InputConstraintSet) => ExtendedMediaTrackSettings;
58
+ export declare const mapSettingsToFeatures: (settings: ExtendedMediaTrackSettings) => InputConstraintSet;
59
+ export declare const createVideoStreamProcess: ({ backgroundImageUrl, dynamicProcessingDimensions, backgroundThreshold, downSampleFactor, foregroundThreshold, lightWrapEdgeBand, lightWrapIntensity, lightWrapTightness, morphDilateRadiusPx, morphErodeRadiusPx, morphPass, personCenterX, personCenterY, sigmaSpace, sigmaRangeHi, sigmaRangeLo, frameRate, gpuAPI, label, referenceProcessingHeight, maskCombineRatio, processingHeight, processingWidth, shouldEnable, stopAsMute, videoSegmentation, signals, getVideoProcessor, ...options }: VideoStreamProcessOptions) => TrackProcessor;
42
60
  export {};