@pexip/media 21.0.0 → 22.1.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +20 -32
  3. package/api-docs/README.mdx +20 -35
  4. package/api-docs/functions/applyContentHint.mdx +1 -1
  5. package/api-docs/functions/createAudioMixingProcess.mdx +1 -1
  6. package/api-docs/functions/createGetDisplayMedia.mdx +3 -3
  7. package/api-docs/functions/createMediaSignal.mdx +2 -2
  8. package/api-docs/functions/createMediaSignals.mdx +5 -3
  9. package/api-docs/functions/deriveInitialPermissionStatus.mdx +4 -2
  10. package/api-docs/functions/getPermissionStatus.mdx +31 -11
  11. package/api-docs/functions/toDeniedDevices.mdx +3 -1
  12. package/api-docs/interfaces/AudioDetectionSignals.mdx +2 -2
  13. package/api-docs/interfaces/AudioSignalDetectionOptions.mdx +1 -1
  14. package/api-docs/interfaces/ExtendedMediaTrackSettings.mdx +11 -7
  15. package/api-docs/interfaces/Media.mdx +4 -4
  16. package/api-docs/interfaces/MediaAttributes.mdx +2 -2
  17. package/api-docs/interfaces/MediaChangesSignals.mdx +5 -5
  18. package/api-docs/interfaces/MediaController.mdx +3 -3
  19. package/api-docs/interfaces/MediaInit.mdx +1 -1
  20. package/api-docs/interfaces/MediaOptions.mdx +3 -3
  21. package/api-docs/interfaces/MediaTrack.mdx +3 -3
  22. package/api-docs/interfaces/MediaTrackInit.mdx +12 -10
  23. package/api-docs/interfaces/PreviewControllerProps.mdx +1 -0
  24. package/api-docs/interfaces/PreviewEventHandler.mdx +7 -7
  25. package/api-docs/interfaces/PreviewStreamController.mdx +10 -10
  26. package/api-docs/interfaces/PreviewStreamParams.mdx +7 -6
  27. package/api-docs/interfaces/VoiceActivityDetectionOptions.mdx +1 -1
  28. package/api-docs/type-aliases/GetUserMediaProcess.mdx +4 -2
  29. package/api-docs/type-aliases/Process.mdx +3 -1
  30. package/api-docs/variables/internalSignals.mdx +1 -1
  31. package/dist/audioMixingProcessor.d.ts +4 -2
  32. package/dist/audioMixingProcessor.js +22 -12
  33. package/dist/audioProcessor.d.ts +7 -2
  34. package/dist/audioProcessor.js +25 -20
  35. package/dist/logger.d.ts +0 -2
  36. package/dist/logger.js +0 -16
  37. package/dist/media.js +5 -5
  38. package/dist/previewController.d.ts +3 -1
  39. package/dist/previewController.js +42 -9
  40. package/dist/typeGuard.d.ts +1 -2
  41. package/dist/typeGuard.js +0 -3
  42. package/dist/types.d.ts +7 -14
  43. package/dist/utils.js +15 -5
  44. package/dist/videoProcessor.d.ts +40 -22
  45. package/dist/videoProcessor.js +227 -247
  46. package/package.json +11 -11
  47. package/api-docs/functions/isVideoStreamTrackProcessorAPIs.mdx +0 -13
  48. package/api-docs/type-aliases/Segmenters.mdx +0 -3
  49. package/api-docs/type-aliases/VideoStreamTrackProcessorAPIs.mdx +0 -7
@@ -1,6 +1,8 @@
1
1
  ## Extends
2
2
 
3
- - `Partial`\<`Omit`\<[`MediaTrack`](MediaTrack.mdx), `"mute"`\>\>
3
+ ```ts
4
+ - Partial<Omit<MediaTrack, "mute">>
5
+ ```
4
6
 
5
7
  ## Properties
6
8
 
@@ -14,16 +16,16 @@
14
16
  | <a id="track"></a> `track?` | `readonly` | `MediaStreamTrack` | The MediaStreamTrack object representing the track **See** https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack | - | [`MediaTrack`](MediaTrack.mdx).[`track`](MediaTrack.mdx#track) |
15
17
  | <a id="muted"></a> `muted?` | `public` | `boolean` | The current mute state of the track. `undefined` means the track is not available | - | [`MediaTrack`](MediaTrack.mdx).[`muted`](MediaTrack.mdx#muted) |
16
18
  | <a id="source"></a> `source?` | `readonly` | [`MediaTrack`](MediaTrack.mdx) | The source track that it is originated from | - | [`MediaTrack`](MediaTrack.mdx).[`source`](MediaTrack.mdx#source) |
17
- | <a id="getsettings"></a> `getSettings?` | `public` | () => [`ExtendedMediaTrackSettings`](ExtendedMediaTrackSettings.mdx) | - | - | [`MediaTrack`](MediaTrack.mdx).[`getSettings`](MediaTrack.mdx#getsettings) |
18
- | <a id="getconstraints"></a> `getConstraints?` | `public` | () => `InputDeviceConstraint` | - | - | [`MediaTrack`](MediaTrack.mdx).[`getConstraints`](MediaTrack.mdx#getconstraints) |
19
- | <a id="applyconstraints"></a> `applyConstraints?` | `public` | (`constraints`) => `Promise`\<`void`\> | - | - | [`MediaTrack`](MediaTrack.mdx).[`applyConstraints`](MediaTrack.mdx#applyconstraints) |
20
- | <a id="release"></a> `release?` | `public` | () => `Promise`\<`void`\> | - | - | [`MediaTrack`](MediaTrack.mdx).[`release`](MediaTrack.mdx#release) |
21
- | <a id="clone"></a> `clone?` | `public` | (`signals?`, `label?`) => [`MediaTrack`](MediaTrack.mdx) | - | - | [`MediaTrack`](MediaTrack.mdx).[`clone`](MediaTrack.mdx#clone) |
22
- | <a id="tojson"></a> `toJSON?` | `public` | () => `unknown` | - | - | [`MediaTrack`](MediaTrack.mdx).[`toJSON`](MediaTrack.mdx#tojson) |
23
- | <a id="kind"></a> `kind` | `public` | `"audioinput"` \| `"videoinput"` | - | [`MediaTrack`](MediaTrack.mdx).[`kind`](MediaTrack.mdx#kind) | - |
24
- | <a id="input"></a> `input` | `public` | `MediaDeviceInfoLike` \| `undefined` | The `MediaDeviceInfo` object representing the device that the track is connected to. **See** https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo | [`MediaTrack`](MediaTrack.mdx).[`input`](MediaTrack.mdx#input) | - |
19
+ | <a id="getsettings"></a> `getSettings?` | `public` | () =\> [`ExtendedMediaTrackSettings`](ExtendedMediaTrackSettings.mdx) | - | - | [`MediaTrack`](MediaTrack.mdx).[`getSettings`](MediaTrack.mdx#getsettings) |
20
+ | <a id="getconstraints"></a> `getConstraints?` | `public` | `() => InputDeviceConstraint` | - | - | [`MediaTrack`](MediaTrack.mdx).[`getConstraints`](MediaTrack.mdx#getconstraints) |
21
+ | <a id="applyconstraints"></a> `applyConstraints?` | `public` | `(constraints) => Promise<void>` | - | - | [`MediaTrack`](MediaTrack.mdx).[`applyConstraints`](MediaTrack.mdx#applyconstraints) |
22
+ | <a id="release"></a> `release?` | `public` | `() => Promise<void>` | - | - | [`MediaTrack`](MediaTrack.mdx).[`release`](MediaTrack.mdx#release) |
23
+ | <a id="clone"></a> `clone?` | `public` | (`signals?`, `label?`) =\> [`MediaTrack`](MediaTrack.mdx) | - | - | [`MediaTrack`](MediaTrack.mdx).[`clone`](MediaTrack.mdx#clone) |
24
+ | <a id="tojson"></a> `toJSON?` | `public` | `() => unknown` | - | - | [`MediaTrack`](MediaTrack.mdx).[`toJSON`](MediaTrack.mdx#tojson) |
25
+ | <a id="kind"></a> `kind` | `public` | `"audioinput" \| "videoinput"` | - | [`MediaTrack`](MediaTrack.mdx).[`kind`](MediaTrack.mdx#kind) | - |
26
+ | <a id="input"></a> `input` | `public` | `MediaDeviceInfoLike \| undefined` | The `MediaDeviceInfo` object representing the device that the track is connected to. **See** https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo | [`MediaTrack`](MediaTrack.mdx).[`input`](MediaTrack.mdx#input) | - |
25
27
  | <a id="expectedinput"></a> `expectedInput?` | `public` | `MediaDeviceInfoLike` | The `MediaDeviceInfo` object representing the device that the track is expected to be connected to. **See** https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo | [`MediaTrack`](MediaTrack.mdx).[`expectedInput`](MediaTrack.mdx#expectedinput) | - |
26
28
  | <a id="constraints"></a> `constraints` | `public` | `InputDeviceConstraint` | - | - | - |
27
29
  | <a id="overridemute"></a> `overrideMute?` | `public` | `boolean` | - | - | - |
28
30
  | <a id="signals"></a> `signals?` | `public` | [`MediaSignals`](../type-aliases/MediaSignals.mdx) | - | - | - |
29
- | <a id="mute"></a> `mute?` | `public` | (`toMute`, `self`, `soft?`) => `void` | Overriding Mute the track | - | - |
31
+ | <a id="mute"></a> `mute?` | `public` | `(toMute, self, soft?) => void` | Overriding Mute the track | - | - |
@@ -12,3 +12,4 @@
12
12
  | <a id="discardmedia"></a> `discardMedia` | `boolean` |
13
13
  | <a id="initialized"></a> `initialized` | `boolean` |
14
14
  | <a id="signals"></a> `signals` | [`MediaSignals`](../type-aliases/MediaSignals.mdx) |
15
+ | <a id="keepvideomuted"></a> `keepVideoMuted` | `boolean` |
@@ -9,11 +9,11 @@
9
9
  | <a id="audioinputerror"></a> `audioInputError?` | `EventErrorCallback` |
10
10
  | <a id="applychangeserror"></a> `applyChangesError?` | `EventErrorCallback` |
11
11
  | <a id="revertchangeserror"></a> `revertChangesError?` | `EventErrorCallback` |
12
- | <a id="audiomuted"></a> `audioMuted?` | `EventCallback`\<`boolean` \| `undefined`\> |
13
- | <a id="audiosuspended"></a> `audioSuspended?` | `EventCallback`\<`boolean` \| `undefined`\> |
14
- | <a id="updatingmain"></a> `updatingMain?` | `EventCallback`\<`boolean`\> |
15
- | <a id="updatingpreviewaudio"></a> `updatingPreviewAudio?` | `EventCallback`\<`boolean`\> |
16
- | <a id="updatingpreviewvideo"></a> `updatingPreviewVideo?` | `EventCallback`\<`boolean`\> |
17
- | <a id="videomuted"></a> `videoMuted?` | `EventCallback`\<`boolean` \| `undefined`\> |
18
- | <a id="videosuspended"></a> `videoSuspended?` | `EventCallback`\<`boolean` \| `undefined`\> |
12
+ | <a id="audiomuted"></a> `audioMuted?` | `EventCallback<boolean \| undefined>` |
13
+ | <a id="audiosuspended"></a> `audioSuspended?` | `EventCallback<boolean \| undefined>` |
14
+ | <a id="updatingmain"></a> `updatingMain?` | `EventCallback<boolean>` |
15
+ | <a id="updatingpreviewaudio"></a> `updatingPreviewAudio?` | `EventCallback<boolean>` |
16
+ | <a id="updatingpreviewvideo"></a> `updatingPreviewVideo?` | `EventCallback<boolean>` |
17
+ | <a id="videomuted"></a> `videoMuted?` | `EventCallback<boolean \| undefined>` |
18
+ | <a id="videosuspended"></a> `videoSuspended?` | `EventCallback<boolean \| undefined>` |
19
19
  | <a id="unsubscribemain"></a> `unsubscribeMain?` | [`Unsubscribe`](../type-aliases/Unsubscribe.mdx) |
@@ -11,7 +11,7 @@
11
11
  | <a id="updatingmain"></a> `updatingMain` | `boolean` |
12
12
  | <a id="updatingpreviewaudio"></a> `updatingPreviewAudio` | `boolean` |
13
13
  | <a id="updatingpreviewvideo"></a> `updatingPreviewVideo` | `boolean` |
14
- | <a id="cleanup"></a> `cleanup` | () => `Promise`\<`void`\> |
14
+ | <a id="cleanup"></a> `cleanup` | `() => Promise<void>` |
15
15
 
16
16
  ## Methods
17
17
 
@@ -83,7 +83,7 @@ applyChanges(force?): Promise<void>;
83
83
 
84
84
  #### Returns
85
85
 
86
- `Promise`\<`void`\>
86
+ `Promise<void>`
87
87
 
88
88
  ***
89
89
 
@@ -95,7 +95,7 @@ revertChanges(): Promise<void>;
95
95
 
96
96
  #### Returns
97
97
 
98
- `Promise`\<`void`\>
98
+ `Promise<void>`
99
99
 
100
100
  ***
101
101
 
@@ -163,7 +163,7 @@ onUpdatingPreviewAudio(callback): Unsubscribe;
163
163
 
164
164
  | Parameter | Type |
165
165
  | ------ | ------ |
166
- | `callback` | `EventCallback`\<`boolean`\> |
166
+ | `callback` | `EventCallback<boolean>` |
167
167
 
168
168
  #### Returns
169
169
 
@@ -181,7 +181,7 @@ onUpdatingPreviewVideo(callback): Unsubscribe;
181
181
 
182
182
  | Parameter | Type |
183
183
  | ------ | ------ |
184
- | `callback` | `EventCallback`\<`boolean`\> |
184
+ | `callback` | `EventCallback<boolean>` |
185
185
 
186
186
  #### Returns
187
187
 
@@ -199,7 +199,7 @@ onUpdatingMain(callback): Unsubscribe;
199
199
 
200
200
  | Parameter | Type |
201
201
  | ------ | ------ |
202
- | `callback` | `EventCallback`\<`boolean`\> |
202
+ | `callback` | `EventCallback<boolean>` |
203
203
 
204
204
  #### Returns
205
205
 
@@ -289,7 +289,7 @@ onAudioMuted(callback): Unsubscribe;
289
289
 
290
290
  | Parameter | Type |
291
291
  | ------ | ------ |
292
- | `callback` | `EventCallback`\<`boolean` \| `undefined`\> |
292
+ | `callback` | `EventCallback<boolean \| undefined>` |
293
293
 
294
294
  #### Returns
295
295
 
@@ -307,7 +307,7 @@ onAudioSuspended(callback): Unsubscribe;
307
307
 
308
308
  | Parameter | Type |
309
309
  | ------ | ------ |
310
- | `callback` | `EventCallback`\<`boolean` \| `undefined`\> |
310
+ | `callback` | `EventCallback<boolean \| undefined>` |
311
311
 
312
312
  #### Returns
313
313
 
@@ -325,7 +325,7 @@ onVideoMuted(callback): Unsubscribe;
325
325
 
326
326
  | Parameter | Type |
327
327
  | ------ | ------ |
328
- | `callback` | `EventCallback`\<`boolean` \| `undefined`\> |
328
+ | `callback` | `EventCallback<boolean \| undefined>` |
329
329
 
330
330
  #### Returns
331
331
 
@@ -343,7 +343,7 @@ onVideoSuspended(callback): Unsubscribe;
343
343
 
344
344
  | Parameter | Type |
345
345
  | ------ | ------ |
346
- | `callback` | `EventCallback`\<`boolean` \| `undefined`\> |
346
+ | `callback` | `EventCallback<boolean \| undefined>` |
347
347
 
348
348
  #### Returns
349
349
 
@@ -2,14 +2,15 @@
2
2
 
3
3
  | Property | Type | Description |
4
4
  | ------ | ------ | ------ |
5
- | <a id="getcurrentdevices"></a> `getCurrentDevices` | () => `IndexedDevices` | - |
6
- | <a id="getcurrentmedia"></a> `getCurrentMedia` | () => [`Media`](Media.mdx) \| `undefined` | - |
7
- | <a id="updatemainstream"></a> `updateMainStream` | (`request`) => `Promise`\<`void`\> | - |
5
+ | <a id="getcurrentdevices"></a> `getCurrentDevices` | `() => IndexedDevices` | - |
6
+ | <a id="getcurrentmedia"></a> `getCurrentMedia` | () =\> [`Media`](Media.mdx) \| `undefined` | - |
7
+ | <a id="updatemainstream"></a> `updateMainStream` | `(request) => Promise<void>` | - |
8
8
  | <a id="mainmediasignal"></a> `mainMediaSignal` | `Signal`\<[`Media`](Media.mdx) \| `undefined`, [`Media`](Media.mdx) \| `undefined`\> | - |
9
9
  | <a id="signals"></a> `signals` | [`MediaSignals`](../type-aliases/MediaSignals.mdx) | - |
10
- | <a id="onended"></a> `onEnded?` | () => `void` | - |
10
+ | <a id="onended"></a> `onEnded?` | `() => void` | - |
11
11
  | <a id="fftsize"></a> `fftSize?` | `number` | - |
12
- | <a id="queueoptions"></a> `queueOptions?` | `Partial`\<`AsyncQueueOptions`\> | - |
12
+ | <a id="queueoptions"></a> `queueOptions?` | `Partial<AsyncQueueOptions>` | - |
13
13
  | <a id="audioprocessors"></a> `audioProcessors` | [`TrackProcessor`](../type-aliases/TrackProcessor.mdx)[] | - |
14
14
  | <a id="videoprocessors"></a> `videoProcessors` | [`TrackProcessor`](../type-aliases/TrackProcessor.mdx)[] | - |
15
- | <a id="getdefaultconstraints"></a> `getDefaultConstraints?` | () => `object` | Pass default constraints to use with get media wrappers |
15
+ | <a id="getdefaultconstraints"></a> `getDefaultConstraints?` | `() => object` | Pass default constraints to use with get media wrappers |
16
+ | <a id="stopvideotrackasmute"></a> `stopVideoTrackAsMute?` | `() => boolean` | - |
@@ -3,4 +3,4 @@
3
3
  | Property | Type | Description |
4
4
  | ------ | ------ | ------ |
5
5
  | <a id="vadthrottlems"></a> `vadThrottleMS?` | `number` | Voice Activity Detection in millisecond |
6
- | <a id="shoulddetectvoiceactivity"></a> `shouldDetectVoiceActivity` | () => `boolean` | Whether or not to detect voice activity |
6
+ | <a id="shoulddetectvoiceactivity"></a> `shouldDetectVoiceActivity` | `() => boolean` | Whether or not to detect voice activity |
@@ -9,11 +9,13 @@ type GetUserMediaProcess = (request) => Promise<Media>;
9
9
  | `request` | \{ `constraints`: `MediaDeviceRequest`; `originalConstraints`: `MediaDeviceRequest`; `permission`: \{ `audio`: `PermissionState`; `video`: `PermissionState`; \}; `currentMedia`: [`Media`](../interfaces/Media.mdx) \| `undefined`; \} |
10
10
  | `request.constraints` | `MediaDeviceRequest` |
11
11
  | `request.originalConstraints` | `MediaDeviceRequest` |
12
- | `request.permission` | \{ `audio`: `PermissionState`; `video`: `PermissionState`; \} |
12
+ | `request.permission` | `{ audio: PermissionState; video: PermissionState; }` |
13
13
  | `request.permission.audio` | `PermissionState` |
14
14
  | `request.permission.video` | `PermissionState` |
15
15
  | `request.currentMedia` | [`Media`](../interfaces/Media.mdx) \| `undefined` |
16
16
 
17
17
  ## Returns
18
18
 
19
- `Promise`\<[`Media`](../interfaces/Media.mdx)\>
19
+ ```ts
20
+ Promise<Media>
21
+ ```
@@ -16,4 +16,6 @@ type Process<T> = (a) => Promise<MediaTrack>;
16
16
 
17
17
  ## Returns
18
18
 
19
- `Promise`\<[`MediaTrack`](../interfaces/MediaTrack.mdx)\>
19
+ ```ts
20
+ Promise<MediaTrack>
21
+ ```
@@ -10,4 +10,4 @@ Internal signals
10
10
 
11
11
  | Name | Type |
12
12
  | ------ | ------ |
13
- | <a id="ondeviceschanged"></a> `onDevicesChanged` | `Signal`\<`IndexedDevices`, `IndexedDevices`\> |
13
+ | <a id="ondeviceschanged"></a> `onDevicesChanged` | `Signal<IndexedDevices, IndexedDevices>` |
@@ -7,8 +7,10 @@ interface AudioStreamProcessorProps {
7
7
  displaySource?: AudioNodeInit<MediaStreamAudioSourceNode, MediaStreamAudioSourceNode>;
8
8
  audioGraph?: AudioGraph;
9
9
  }
10
- declare const FEATURE_KEYS: ['mixWithAdditionalMedia'];
11
- type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
10
+ declare const FEATURE_KEYS: {
11
+ readonly mixWithAdditionalMedia: "boolean";
12
+ };
13
+ type FeaturePropKeys = keyof typeof FEATURE_KEYS;
12
14
  type FeatureProps = Pick<AudioStreamProcessorProps, FeaturePropKeys>;
13
15
  export declare const updateFeatureProps: (constraints: MediaDeviceRequest["audio"], props: FeatureProps) => FeatureProps;
14
16
  /**
@@ -1,22 +1,32 @@
1
- import { extractConstraintsWithKeys } from '@pexip/media-control';
1
+ import { extractConstraints } from '@pexip/media-control';
2
2
  import { isEmpty, assert } from '@pexip/utils';
3
3
  import { createAudioGraph, createAudioGraphProxy, createChannelMergerGraphNode, createStreamDestinationGraphNode, createStreamSourceGraphNode, resumeAudioOnUnmute, } from '@pexip/media-processor';
4
4
  import { logger } from './logger';
5
5
  import { createMediaTrack } from './utils';
6
6
  import { PROCESSOR_LABELS } from './constants';
7
- const FEATURE_KEYS = ['mixWithAdditionalMedia'];
8
- const getAudioConstraints = extractConstraintsWithKeys(FEATURE_KEYS);
7
+ const FEATURE_KEYS = { mixWithAdditionalMedia: 'boolean' };
9
8
  export const updateFeatureProps = (constraints, props) => {
10
- const extracted = getAudioConstraints(constraints);
11
- return FEATURE_KEYS.reduce((accm, key) => {
12
- const [feature] = extracted[key];
13
- if (feature !== undefined && props[key] !== feature) {
14
- props[key] = feature;
15
- accm[key] = feature;
16
- return accm;
9
+ const features = {};
10
+ if (constraints === undefined || typeof constraints === 'boolean') {
11
+ return features;
12
+ }
13
+ for (const k of Object.keys(constraints)) {
14
+ const key = k;
15
+ if (!FEATURE_KEYS[key]) {
16
+ continue;
17
17
  }
18
- return accm;
19
- }, {});
18
+ switch (key) {
19
+ case 'mixWithAdditionalMedia': {
20
+ const [feature] = extractConstraints(key)(constraints);
21
+ if (feature !== undefined && props[key] !== feature) {
22
+ props[key] = feature;
23
+ features[key] = feature;
24
+ }
25
+ break;
26
+ }
27
+ }
28
+ }
29
+ return features;
20
30
  };
21
31
  /**
22
32
  * Create an Audio Mixing Processor and will own the stream passed-in
@@ -76,8 +76,13 @@ interface AudioStreamProcessorProps {
76
76
  contentHint?: AudioContentHint;
77
77
  audioGraph?: AudioGraph;
78
78
  }
79
- declare const FEATURE_KEYS: ['denoise', 'vad', 'asd', 'contentHint'];
80
- type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
79
+ declare const FEATURE_KEYS: {
80
+ readonly denoise: "boolean";
81
+ readonly vad: "boolean";
82
+ readonly asd: "boolean";
83
+ readonly contentHint: "string";
84
+ };
85
+ type FeaturePropKeys = keyof typeof FEATURE_KEYS;
81
86
  type FeatureProps = Pick<AudioStreamProcessorProps, FeaturePropKeys>;
82
87
  export declare const updateFeatureProps: (constraints: MediaDeviceRequest["audio"], props: FeatureProps) => FeatureProps;
83
88
  /**
@@ -1,4 +1,4 @@
1
- import { extractConstraintsWithKeys } from '@pexip/media-control';
1
+ import { extractConstraints } from '@pexip/media-control';
2
2
  import { createQueue, isEmpty, assert } from '@pexip/utils';
3
3
  import { avg, createAnalyzerSubscribableGraphNode, createAudioGraph, createAudioGraphProxy, createAudioSignalDetector, createDenoiseWorkletGraphNode, createStreamDestinationGraphNode, createStreamSourceGraphNode, createVADetector, createVoiceDetectorFromProbability, createVoiceDetectorFromTimeData, } from '@pexip/media-processor';
4
4
  import { PROCESSOR_LABELS } from './constants';
@@ -17,41 +17,46 @@ const fetchDenoiseWasm = async (denoiseWasm, wasmURL) => {
17
17
  }
18
18
  return await (await fetch(wasmURL)).arrayBuffer();
19
19
  };
20
- const FEATURE_KEYS = [
21
- 'denoise',
22
- 'vad',
23
- 'asd',
24
- 'contentHint',
25
- ];
26
- const getAudioConstraints = extractConstraintsWithKeys(FEATURE_KEYS);
20
+ const FEATURE_KEYS = {
21
+ denoise: 'boolean',
22
+ vad: 'boolean',
23
+ asd: 'boolean',
24
+ contentHint: 'string',
25
+ };
27
26
  export const updateFeatureProps = (constraints, props) => {
28
- const extracted = getAudioConstraints(constraints);
29
- return FEATURE_KEYS.reduce((accm, key) => {
27
+ const features = {};
28
+ if (constraints === undefined || typeof constraints === 'boolean') {
29
+ return features;
30
+ }
31
+ for (const k of Object.keys(constraints)) {
32
+ const key = k;
33
+ if (!FEATURE_KEYS[key]) {
34
+ continue;
35
+ }
30
36
  switch (key) {
31
37
  case 'contentHint': {
32
- const [[feature] = []] = extracted[key];
38
+ const [[feature] = []] = extractConstraints(key)(constraints);
33
39
  if (isAudioContentHint(feature) && props[key] !== feature) {
34
40
  props[key] = feature;
35
- accm[key] = feature;
36
- return accm;
41
+ features[key] = feature;
37
42
  }
38
- return accm;
43
+ break;
39
44
  }
40
45
  case 'denoise':
41
46
  case 'asd':
42
47
  case 'vad': {
43
- const [feature] = extracted[key];
48
+ const [feature] = extractConstraints(key)(constraints);
44
49
  if (feature !== undefined && props[key] !== feature) {
45
50
  props[key] = feature;
46
- accm[key] = feature;
47
- return accm;
51
+ features[key] = feature;
48
52
  }
49
- return accm;
53
+ break;
50
54
  }
51
55
  default:
52
- return accm;
56
+ break;
53
57
  }
54
- }, {});
58
+ }
59
+ return features;
55
60
  };
56
61
  /**
57
62
  * Create an Audio Stream Processor and will own the stream passed-in
package/dist/logger.d.ts CHANGED
@@ -7,5 +7,3 @@ export declare function setLogger(newLogger: Logger): void;
7
7
  * @param metaBase - The meta data which will always be included into the log
8
8
  */
9
9
  export declare const createModuleLogger: (metaBase: unknown) => Logger;
10
- export declare const createLogProxyHandler: <T extends object>(logger: Logger, name: string, scope: string) => ProxyHandler<T>;
11
- export declare const proxyWithLog: (logger: Logger, scope: string) => <T extends object>(obj: T, name: string) => T;
package/dist/logger.js CHANGED
@@ -23,19 +23,3 @@ export const createModuleLogger = (metaBase) => {
23
23
  return log;
24
24
  }, {});
25
25
  };
26
- export const createLogProxyHandler = (logger, name, scope) => {
27
- return {
28
- get: (target, p, receiver) => {
29
- const value = Reflect.get(target, p, receiver);
30
- logger.debug({ scope, name, prop: p, value }, `called get ${name}[${String(p)}]`);
31
- return value;
32
- },
33
- set: (target, p, value, receiver) => {
34
- logger.info({ scope, name, prop: p, value }, `called set ${name}[${String(p)}]`);
35
- return Reflect.set(target, p, value, receiver);
36
- },
37
- };
38
- };
39
- export const proxyWithLog = (logger, scope) => (obj, name) => {
40
- return new Proxy(obj, createLogProxyHandler(logger, name, scope));
41
- };
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) {
@@ -350,7 +365,25 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
350
365
  };
351
366
  const cleanup = async () => {
352
367
  logger.debug('Cleanup preview controller');
353
- await props.media?.release();
368
+ // Drop pending jobs, then enqueue release so it runs after any
369
+ // in-flight `processAndUpdateMedia`. Releasing concurrently with an
370
+ // in-flight `videoProcessor.process()` would tear down the worker
371
+ // pipeline mid-flight.
372
+ queue.empty();
373
+ const media = props.media;
374
+ if (media) {
375
+ await new Promise((resolve, reject) => {
376
+ queue.enqueue(async () => {
377
+ try {
378
+ await media.release();
379
+ resolve();
380
+ }
381
+ catch (error) {
382
+ reject(error);
383
+ }
384
+ });
385
+ });
386
+ }
354
387
  cleanUpMainSubscription();
355
388
  for (const unsub of subscriptions) {
356
389
  unsub?.();
@@ -475,7 +508,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
475
508
  return updateAudioInput(input);
476
509
  }
477
510
  case 'videoinput': {
478
- return updateVideoInput(input);
511
+ return updateVideoInput(input, false);
479
512
  }
480
513
  }
481
514
  },
@@ -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
- };