@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,84 @@
1
1
  # @pexip/media
2
2
 
3
+ ## 20.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 69d6be4: Refactor `MediaTrack` to decouple system/hardware mute from mute
8
+
9
+ - Add `stopped` attributes to `MediaTrack` to indicate the track `live` or
10
+ `ended`
11
+ - Add `suspended` attributes to
12
+ `MediaTrack to indicate the track is being system/hardware muted`
13
+ - Add signal supports to `MediaTrack` to be able to subscribe track related
14
+ signals
15
+ - Implement Two-hand control to secure mute consistency
16
+
17
+ When there is `muted` event from `MediaStreamTrack`, the track will follow
18
+ the event to set the `enabled` attribute to `false` to avoid any
19
+ possibilities of leaking the local media to remote parties. When the
20
+ `unmuted` event is triggered, it depends on whether the `enabled` attribute
21
+ is set before the `muted` event or not to unmute the track.
22
+
23
+ Breaking changes:
24
+
25
+ - Replace `MediaStreamTrack` related signal with the new `MediaTrack` signals
26
+
27
+ - `onStreamTrackEnded` -> `onMediaTrackStopped`
28
+ - `onStreamTrackMuted` -> `onMediaTrackSuspended`
29
+ - `onStreamTrackUnmuted` -> `onMediaTrackResumed`
30
+ - `onStreamTrackEnabled` -> `onMediaTrackMuted`
31
+ - `onAudioMuteStateChanged` -> `onMediaTrackMuted`
32
+ - `onVideoMuteStateChanged` -> `onMediaTrackMuted`
33
+
34
+ ### Patch Changes
35
+
36
+ - f8a8159: Fix Subsequant mute/unmute events handling
37
+ - e347770: Handle suspended state fro preview controller
38
+ - 0a375c7: Delay stopping video track to ensure last frame being black
39
+ - Updated dependencies [69d6be4]
40
+ - Updated dependencies [adc1a50]
41
+ - @pexip/media-control@20.3.0
42
+ - @pexip/utils@17.0.1
43
+ - @pexip/media-processor@20.3.0
44
+ - @pexip/signal@16.9.0
45
+
46
+ ## 20.2.0
47
+
48
+ ### Minor Changes
49
+
50
+ - 574cf03: Adjusts blur passes based on processing height:
51
+
52
+ - `getBlurKernelSize` can now take `lowestProcessingHeight` so that the blur
53
+ passes are adjusted compared to the lowest processing height used by the
54
+ consumer. This fixes an issue with different blur amount being applied to
55
+ frames of different resolutions.
56
+ - background blur amount is updated every time processing dimensions are
57
+ updated.
58
+
59
+ - be12a15: Add `getDefaultConstraints` and rename `mediaSignals` to
60
+ `mainMediaSignal`
61
+
62
+ - Add `getDefaultConstraints` to be consistent to the main one
63
+ - Rename `mediaSignals` to `mainMediaSignal`
64
+ - Fix `Media['clone']` to avoid interfering the original pipeline
65
+
66
+ - b4c3bad: Stop camera as Mute
67
+
68
+ - Decouple updatingMedia to updatingAudio and updatingVideo
69
+ - Stop camera when mute, and re-request the same camera when unmute
70
+
71
+ - 752005c: Release the track when providing false constraints independently
72
+
73
+ ### Patch Changes
74
+
75
+ - Updated dependencies [f5e5b51]
76
+ - Updated dependencies [6fddc11]
77
+ - @pexip/media-processor@20.2.0
78
+ - @pexip/signal@16.9.0
79
+ - @pexip/media-control@20.2.0
80
+ - @pexip/utils@17.0.0
81
+
3
82
  ## 20.1.0
4
83
 
5
84
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  import type { MediaDeviceRequest } from '@pexip/media-control';
2
2
  import type { AudioGraph, AudioNodeInit } from '@pexip/media-processor';
3
- import type { TrackProcessor } from './types';
3
+ import type { MediaSignals, TrackProcessor } from './types';
4
4
  interface AudioStreamProcessorProps {
5
5
  mixWithAdditionalMedia?: boolean;
6
6
  merger?: AudioNodeInit<ChannelMergerNode, ChannelMergerNode>;
@@ -12,7 +12,7 @@ type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
12
12
  type FeatureProps = Pick<AudioStreamProcessorProps, FeaturePropKeys>;
13
13
  export declare const updateFeatureProps: (constraints: MediaDeviceRequest["audio"], props: FeatureProps) => FeatureProps;
14
14
  /**
15
- * Create a Audio Mixing Processor and will own the stream passed-in
15
+ * Create an Audio Mixing Processor and will own the stream passed-in
16
16
  */
17
- export declare const createAudioMixingProcess: (getCurrentMedia: () => MediaStream | undefined, label?: "AudioMixingProcessor") => TrackProcessor;
17
+ export declare const createAudioMixingProcess: (getCurrentMedia: () => MediaStream | undefined, signals?: MediaSignals, label?: "AudioMixingProcessor") => TrackProcessor;
18
18
  export {};
@@ -2,7 +2,7 @@ import { extractConstraintsWithKeys } 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
- import { createMediaTrack, isTrackMuted } from './utils';
5
+ import { createMediaTrack } from './utils';
6
6
  import { PROCESSOR_LABELS } from './constants';
7
7
  const FEATURE_KEYS = ['mixWithAdditionalMedia'];
8
8
  const getAudioConstraints = extractConstraintsWithKeys(FEATURE_KEYS);
@@ -19,9 +19,9 @@ export const updateFeatureProps = (constraints, props) => {
19
19
  }, {});
20
20
  };
21
21
  /**
22
- * Create a Audio Mixing Processor and will own the stream passed-in
22
+ * Create an Audio Mixing Processor and will own the stream passed-in
23
23
  */
24
- export const createAudioMixingProcess = (getCurrentMedia, label = PROCESSOR_LABELS.AudioMixingProcessor) => {
24
+ export const createAudioMixingProcess = (getCurrentMedia, signals, label = PROCESSOR_LABELS.AudioMixingProcessor) => {
25
25
  const props = {};
26
26
  return async (prevMediaTrack) => {
27
27
  updateFeatureProps(prevMediaTrack.getConstraints(), props);
@@ -154,12 +154,12 @@ export const createAudioMixingProcess = (getCurrentMedia, label = PROCESSOR_LABE
154
154
  expectedInput: prevMediaTrack.expectedInput,
155
155
  track,
156
156
  overrideMute: true,
157
- mute: toMute => {
158
- prevMediaTrack.mute(toMute);
157
+ mute: (toMute, _self, soft) => {
159
158
  for (const track of mainSource.node?.mediaStream.getAudioTracks() ??
160
159
  []) {
161
160
  track.enabled = !toMute;
162
161
  }
162
+ prevMediaTrack.mute(toMute, soft);
163
163
  },
164
164
  get muted() {
165
165
  return (
@@ -168,11 +168,12 @@ export const createAudioMixingProcess = (getCurrentMedia, label = PROCESSOR_LABE
168
168
  !!prevMediaTrack.muted ||
169
169
  mainSource.node?.mediaStream
170
170
  .getAudioTracks()
171
- .some(isTrackMuted));
171
+ .some(track => track.enabled === false));
172
172
  },
173
173
  constraints: prevMediaTrack.getConstraints(),
174
174
  applyConstraints,
175
175
  release,
176
+ signals,
176
177
  getSettings: () => {
177
178
  const mixWithAdditionalMedia = !!props.mixWithAdditionalMedia;
178
179
  const contentHint = track.contentHint ?? '';
@@ -1,6 +1,6 @@
1
- import type { AudioGraphOptions, AudioNodeInit, ThrottleOptions, DenoiseWorkletNodeInit, AnalyzerNodeInit, AudioGraph } from '@pexip/media-processor';
1
+ import type { AnalyzerNodeInit, AudioGraph, AudioGraphOptions, AudioNodeInit, DenoiseWorkletNodeInit, ThrottleOptions } from '@pexip/media-processor';
2
2
  import type { MediaDeviceRequest } from '@pexip/media-control';
3
- import type { TrackProcessor, DenoiseParams, AudioContentHint } from './types';
3
+ import type { AudioContentHint, DenoiseParams, MediaSignals, TrackProcessor } from './types';
4
4
  type AudioNodeInits = AudioNodeInit[];
5
5
  /**
6
6
  * A function to be called to create the AudioNodes needed for the graph
@@ -60,6 +60,7 @@ interface AudioProcessOptions {
60
60
  */
61
61
  silentThreshold?: number;
62
62
  label?: string;
63
+ signals?: MediaSignals;
63
64
  }
64
65
  interface AudioStreamProcessorProps {
65
66
  audioGraphOptions?: AudioGraphOptions;
@@ -80,7 +81,7 @@ type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
80
81
  type FeatureProps = Pick<AudioStreamProcessorProps, FeaturePropKeys>;
81
82
  export declare const updateFeatureProps: (constraints: MediaDeviceRequest["audio"], props: FeatureProps) => FeatureProps;
82
83
  /**
83
- * Create a Audio Stream Processor and will own the stream passed-in
84
+ * Create an Audio Stream Processor and will own the stream passed-in
84
85
  */
85
- export declare const createAudioStreamProcess: ({ analyzerUpdateFrequency, audioGraphOptions, audioSignalDetectionDuration, clock, createNodes, denoiseParams, fftSize, onAudioSignalDetected, onVoiceActivityDetected, shouldEnable, silentThreshold, throttleMs, label, }: AudioProcessOptions & ThrottleOptions) => TrackProcessor;
86
+ export declare const createAudioStreamProcess: ({ analyzerUpdateFrequency, audioGraphOptions, audioSignalDetectionDuration, clock, createNodes, denoiseParams, fftSize, onAudioSignalDetected, onVoiceActivityDetected, shouldEnable, signals, silentThreshold, throttleMs, label, }: AudioProcessOptions & ThrottleOptions) => TrackProcessor;
86
87
  export {};
@@ -1,6 +1,6 @@
1
1
  import { extractConstraintsWithKeys } from '@pexip/media-control';
2
2
  import { createQueue, isEmpty, assert } from '@pexip/utils';
3
- import { createAudioGraph, createAudioGraphProxy, createStreamSourceGraphNode, createStreamDestinationGraphNode, createAnalyzerSubscribableGraphNode, createDenoiseWorkletGraphNode, createAudioSignalDetector, createVADetector, createVoiceDetectorFromTimeData, createVoiceDetectorFromProbability, avg, } from '@pexip/media-processor';
3
+ import { avg, createAnalyzerSubscribableGraphNode, createAudioGraph, createAudioGraphProxy, createAudioSignalDetector, createDenoiseWorkletGraphNode, createStreamDestinationGraphNode, createStreamSourceGraphNode, createVADetector, createVoiceDetectorFromProbability, createVoiceDetectorFromTimeData, } from '@pexip/media-processor';
4
4
  import { PROCESSOR_LABELS } from './constants';
5
5
  import { logger } from './logger';
6
6
  import { isAudioContentHint } from './typeGuard';
@@ -54,12 +54,12 @@ export const updateFeatureProps = (constraints, props) => {
54
54
  }, {});
55
55
  };
56
56
  /**
57
- * Create a Audio Stream Processor and will own the stream passed-in
57
+ * Create an Audio Stream Processor and will own the stream passed-in
58
58
  */
59
59
  export const createAudioStreamProcess = ({ analyzerUpdateFrequency = 0.5, // 0.5 Hz
60
60
  audioGraphOptions, audioSignalDetectionDuration = 4.0, // 4 seconds
61
61
  clock, createNodes, denoiseParams, fftSize = 2048, // FFT size
62
- onAudioSignalDetected, onVoiceActivityDetected, shouldEnable, silentThreshold = 10.0 / 32767, // At least one LSB 16-bit data (compare is on absolute value).
62
+ onAudioSignalDetected, onVoiceActivityDetected, shouldEnable, signals, silentThreshold = 10.0 / 32767, // At least one LSB 16-bit data (compare is on absolute value).
63
63
  throttleMs = 3000, // 3 seconds
64
64
  label = PROCESSOR_LABELS.AudioProcessor, }) => {
65
65
  const props = {
@@ -198,11 +198,6 @@ label = PROCESSOR_LABELS.AudioProcessor, }) => {
198
198
  props.analyzer = undefined;
199
199
  props.audioGraph = undefined;
200
200
  };
201
- const mute = (mute) => {
202
- if (track) {
203
- track.enabled = !mute;
204
- }
205
- };
206
201
  return createMediaTrack({
207
202
  label,
208
203
  kind: 'audioinput',
@@ -210,9 +205,18 @@ label = PROCESSOR_LABELS.AudioProcessor, }) => {
210
205
  previousMediaTrack: prevMediaTrack,
211
206
  input: prevMediaTrack.input,
212
207
  expectedInput: prevMediaTrack.expectedInput,
213
- mute,
208
+ overrideMute: true,
209
+ mute(toMute) {
210
+ if (track) {
211
+ track.enabled = !toMute;
212
+ }
213
+ },
214
+ get muted() {
215
+ return !track.enabled;
216
+ },
214
217
  track,
215
218
  release,
219
+ signals,
216
220
  applyConstraints: async (constraints) => {
217
221
  if (isEmpty(constraints)) {
218
222
  return;
package/dist/media.d.ts CHANGED
@@ -19,4 +19,4 @@ export declare const createMediaUpdater: ({ getUserMedia, getCurrentDevices, sho
19
19
  *
20
20
  * @param options - @see MediaOptions
21
21
  */
22
- export declare const createMedia: ({ getMuteState, signals, audioProcessors, videoProcessors, getDefaultConstraints, }: MediaOptions) => MediaController;
22
+ export declare const createMedia: ({ getMuteState, signals, audioProcessors, videoProcessors, stopVideoTrackAsMute, getDefaultConstraints, }: MediaOptions) => MediaController;
package/dist/media.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createIndexedDevices, createStreamTrackEventSubscriptions, getDevices, getInputDevicePermissionState, isIndexedDevices, isRequestedResolution, mergeConstraints, } from '@pexip/media-control';
1
+ import { createIndexedDevices, getDevices, getInputDevicePermissionState, isIndexedDevices, isRequestedResolution, mergeConstraints, } from '@pexip/media-control';
2
2
  import { createAsyncQueue, isEmpty, assert } from '@pexip/utils';
3
3
  import { internalSignals } from './signals';
4
4
  import { UserMediaStatus } from './types';
@@ -15,27 +15,35 @@ export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDisc
15
15
  return async (constraints, currentMedia) => {
16
16
  const currentDevices = await getCurrentDevices();
17
17
  const permission = await getInputDevicePermission();
18
- const requestNothing = !constraints.audio && !constraints.video;
19
- const permissionRejected = permission.audio === 'denied' && permission.video === 'denied';
20
- if (requestNothing || permissionRejected) {
18
+ const releaseAudio = constraints.audio === false || permission.audio === 'denied';
19
+ const releaseVideo = constraints.video === false || permission.video === 'denied';
20
+ if (releaseAudio || releaseVideo) {
21
+ // Release related
21
22
  for (const track of currentMedia?.getTracks() ?? []) {
22
- await track.release();
23
- currentMedia?.removeTrack(track);
23
+ if ((track.kind === 'audioinput' && releaseAudio) ||
24
+ (track.kind === 'videoinput' && releaseVideo)) {
25
+ await track.release();
26
+ currentMedia?.removeTrack(track);
27
+ }
28
+ }
29
+ // When both are true, there is nothing to request
30
+ if (releaseAudio && releaseVideo) {
31
+ currentMedia?.setOriginalConstraints(constraints);
32
+ const media = currentMedia ??
33
+ buildMedia({
34
+ constraints,
35
+ permission,
36
+ devices: currentDevices,
37
+ status: permission.audio === 'denied' &&
38
+ permission.video === 'denied'
39
+ ? UserMediaStatus.PermissionsRejected
40
+ : UserMediaStatus.PermissionsGranted,
41
+ stream: undefined,
42
+ signals,
43
+ tracks: [],
44
+ });
45
+ return onMediaTracksChanged(media, []);
24
46
  }
25
- currentMedia?.setOriginalConstraints(constraints);
26
- const media = currentMedia ??
27
- buildMedia({
28
- constraints,
29
- permission,
30
- devices: currentDevices,
31
- status: permissionRejected
32
- ? UserMediaStatus.PermissionsRejected
33
- : UserMediaStatus.PermissionsGranted,
34
- stream: undefined,
35
- signals,
36
- tracks: [],
37
- });
38
- return onMediaTracksChanged(media, []);
39
47
  }
40
48
  const { audio: prevAudioSettings, video: prevVideoSettings } = currentMedia?.getSettings() ?? {};
41
49
  const videoFeatures = getVideoFeatures(constraints.video, {});
@@ -206,9 +214,20 @@ const createMediaPropsHandler = (signals) => ({
206
214
  }
207
215
  return true;
208
216
  }
209
- case 'updatingMedia': {
217
+ case 'updatingAudio': {
218
+ if (target[p] === value) {
219
+ return true;
220
+ }
221
+ const result = Reflect.set(target, p, value);
222
+ signals?.onUpdatingAudio?.emit(value);
223
+ return result;
224
+ }
225
+ case 'updatingVideo': {
226
+ if (target[p] === value) {
227
+ return true;
228
+ }
210
229
  const result = Reflect.set(target, p, value);
211
- signals?.onUpdatingMedia?.emit(value);
230
+ signals?.onUpdatingVideo?.emit(value);
212
231
  return result;
213
232
  }
214
233
  default: {
@@ -223,11 +242,12 @@ const createMediaPropsHandler = (signals) => ({
223
242
  *
224
243
  * @param options - @see MediaOptions
225
244
  */
226
- export const createMedia = ({ getMuteState, signals, audioProcessors, videoProcessors, getDefaultConstraints = () => ({}), }) => {
245
+ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProcessors, stopVideoTrackAsMute = () => true, getDefaultConstraints = () => ({}), }) => {
227
246
  const _props = {
228
247
  devices: createIndexedDevices([]),
229
248
  discardMedia: false,
230
- updatingMedia: false,
249
+ updatingAudio: false,
250
+ updatingVideo: false,
231
251
  };
232
252
  const props = new Proxy(_props, createMediaPropsHandler(signals));
233
253
  const queue = createAsyncQueue({
@@ -263,28 +283,6 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
263
283
  props.devices = createIndexedDevices(await getDevices());
264
284
  return props.devices;
265
285
  };
266
- const syncMuteState = (tracks) => {
267
- const inputMuted = getMuteState();
268
- for (const track of tracks) {
269
- switch (track.kind) {
270
- case 'audioinput': {
271
- track.mute(inputMuted.audio);
272
- // FIXME: this can fire too often, but we want to make sure we signal initial state as well.
273
- // We should only fire this when needed.
274
- signals.onAudioMuteStateChanged?.emit(track.muted);
275
- break;
276
- }
277
- case 'videoinput':
278
- track.mute(inputMuted.video);
279
- // FIXME: this can fire too often, but we want to make sure we signal initial state as well.
280
- // We should only fire this when needed.
281
- signals.onVideoMuteStateChanged?.emit(track.muted);
282
- break;
283
- default:
284
- break;
285
- }
286
- }
287
- };
288
286
  const processMedia = createMediaProcessor({
289
287
  audioProcessors,
290
288
  videoProcessors,
@@ -304,19 +302,22 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
304
302
  },
305
303
  });
306
304
  const getUserMediaProcess = createGetUserMediaProcess({
307
- getUserMedia: requestUserMediaWithRetry(getCurrentDevices),
308
305
  getCurrentDevices,
306
+ getUserMedia: requestUserMediaWithRetry(getCurrentDevices),
309
307
  signals,
308
+ stopVideoTrackAsMute,
309
+ updateMedia: constraints => updateMedia(constraints),
310
310
  });
311
311
  const processAndUpdateMedia = async (media, tracks) => {
312
312
  const processedTracks = await processMedia(tracks);
313
- // Sync mute state to processed tracks
314
- syncMuteState(processedTracks);
315
313
  for (const [idx, track] of processedTracks.entries()) {
316
314
  const originTrack = tracks.at(idx);
317
315
  assert(originTrack, 'Processed track should always has the original track in the same order');
318
316
  // Only replace track when they are not the same
319
317
  if (originTrack.id !== track.id) {
318
+ if (originTrack.muted !== undefined) {
319
+ track.mute(originTrack.muted);
320
+ }
320
321
  media.removeTrack(originTrack);
321
322
  media.addTrack(track);
322
323
  }
@@ -329,19 +330,12 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
329
330
  shouldDiscardMedia: () => props.discardMedia,
330
331
  getDefaultConstraints,
331
332
  onMediaTracksChanged: (media, tracks) => {
332
- // Sync mute state to input tracks
333
- syncMuteState(tracks);
333
+ const inputMuted = getMuteState();
334
334
  for (const track of tracks) {
335
- if (track.track) {
336
- const unsubscribe = createStreamTrackEventSubscriptions(track.track, {
337
- ended: track => {
338
- signals.onStreamTrackEnded?.emit(track);
339
- unsubscribe();
340
- },
341
- mute: signals.onStreamTrackMuted?.emit,
342
- unmute: signals.onStreamTrackUnmuted?.emit,
343
- });
344
- }
335
+ // Sync mute state to input tracks
336
+ track.mute(track.kind === 'audioinput'
337
+ ? inputMuted.audio
338
+ : inputMuted.video);
345
339
  }
346
340
  logger.debug({ media, tracks }, 'Update media tracks');
347
341
  props.media = media;
@@ -359,12 +353,24 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
359
353
  * @param constraints - @see MediaDeviceRequest
360
354
  */
361
355
  const updateMedia = async (constraints) => {
356
+ const updateAudio = constraints.audio !== undefined;
357
+ const updateVideo = constraints.video !== undefined;
362
358
  try {
363
- props.updatingMedia = true;
359
+ if (updateAudio) {
360
+ props.updatingAudio = true;
361
+ }
362
+ if (updateVideo) {
363
+ props.updatingVideo = true;
364
+ }
364
365
  await updateMediaProcess(constraints, props.media);
365
366
  }
366
367
  finally {
367
- props.updatingMedia = false;
368
+ if (updateAudio) {
369
+ props.updatingAudio = false;
370
+ }
371
+ if (updateVideo) {
372
+ props.updatingVideo = false;
373
+ }
368
374
  }
369
375
  };
370
376
  const mergeMediaConstraints = (constraints) => {
@@ -424,8 +430,11 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
424
430
  navigator.mediaDevices.addEventListener('devicechange', handleDeviceChange);
425
431
  }
426
432
  return {
427
- get updatingMedia() {
428
- return props.updatingMedia;
433
+ get updatingAudio() {
434
+ return props.updatingAudio;
435
+ },
436
+ get updatingVideo() {
437
+ return props.updatingVideo;
429
438
  },
430
439
  get media() {
431
440
  return props.media;
@@ -1,4 +1,4 @@
1
- import type { IndexedDevices, MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
1
+ import type { IndexedDevices, InputConstraintSet, MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
2
2
  import type { AsyncQueueOptions } from '@pexip/utils';
3
3
  import type { Media, MediaSignals, TrackProcessor, Unsubscribe } from './types';
4
4
  type EventCallback<T> = (event: T) => void;
@@ -12,30 +12,45 @@ export interface PreviewEventHandler {
12
12
  audioInputError?: EventErrorCallback;
13
13
  applyChangesError?: EventErrorCallback;
14
14
  revertChangesError?: EventErrorCallback;
15
- updatingPreview?: EventCallback<boolean>;
15
+ audioMuted?: EventCallback<boolean | undefined>;
16
+ audioSuspended?: EventCallback<boolean | undefined>;
16
17
  updatingMain?: EventCallback<boolean>;
18
+ updatingPreviewAudio?: EventCallback<boolean>;
19
+ updatingPreviewVideo?: EventCallback<boolean>;
20
+ videoMuted?: EventCallback<boolean | undefined>;
21
+ videoSuspended?: EventCallback<boolean | undefined>;
17
22
  unsubscribeMain?: Unsubscribe;
18
23
  }
19
24
  export interface PreviewStreamParams {
20
25
  getCurrentDevices: () => IndexedDevices;
21
26
  getCurrentMedia: () => Media | undefined;
22
27
  updateMainStream: (request: MediaDeviceRequest) => Promise<void>;
23
- mediaSignal: MediaSignals['onMediaChanged'];
28
+ mainMediaSignal: MediaSignals['onMediaChanged'];
29
+ signals: MediaSignals;
24
30
  onEnded?: () => void;
25
31
  fftSize?: number;
26
32
  queueOptions?: Partial<AsyncQueueOptions>;
27
33
  audioProcessors: TrackProcessor[];
28
34
  videoProcessors: TrackProcessor[];
35
+ /**
36
+ * Pass default constraints to use with get media wrappers
37
+ */
38
+ getDefaultConstraints?: () => {
39
+ audio?: InputConstraintSet | false;
40
+ video?: InputConstraintSet | false;
41
+ };
29
42
  }
30
43
  export interface PreviewControllerProps {
31
44
  media: Media | undefined;
32
45
  audioInput?: MediaDeviceInfoLike;
33
46
  videoInput?: MediaDeviceInfoLike;
34
- updatingPreview: boolean;
47
+ updatingPreviewAudio: boolean;
48
+ updatingPreviewVideo: boolean;
35
49
  updatingMain: boolean;
36
50
  originalMainAudioInput?: MediaDeviceInfoLike;
37
51
  discardMedia: boolean;
38
52
  initialized: boolean;
53
+ signals: MediaSignals;
39
54
  }
40
55
  export interface PreviewStreamController {
41
56
  media: Media | undefined;
@@ -44,8 +59,10 @@ export interface PreviewStreamController {
44
59
  inputChanged: boolean;
45
60
  audioInput: PreviewInput;
46
61
  videoInput: PreviewInput;
47
- updatingPreview: boolean;
48
62
  updatingMain: boolean;
63
+ updatingPreviewAudio: boolean;
64
+ updatingPreviewVideo: boolean;
65
+ updatePreviewInput(input: PreviewInput): void;
49
66
  updateAudioInput(id: string): void;
50
67
  updateVideoInput(id: string): void;
51
68
  applyChanges(force?: boolean): Promise<void>;
@@ -54,13 +71,18 @@ export interface PreviewStreamController {
54
71
  onMediaChanged(callback: EventCallback<Media>): Unsubscribe;
55
72
  onAudioInputChanged(callback: EventCallback<PreviewInput>): Unsubscribe;
56
73
  onVideoInputChanged(callback: EventCallback<PreviewInput>): Unsubscribe;
57
- onUpdatingPreview(callback: EventCallback<boolean>): Unsubscribe;
74
+ onUpdatingPreviewAudio(callback: EventCallback<boolean>): Unsubscribe;
75
+ onUpdatingPreviewVideo(callback: EventCallback<boolean>): Unsubscribe;
58
76
  onUpdatingMain(callback: EventCallback<boolean>): Unsubscribe;
59
77
  onAudioInputError(callback: EventErrorCallback): Unsubscribe;
60
78
  onVideoInputError(callback: EventErrorCallback): Unsubscribe;
61
79
  onApplyChangesError(callback: EventErrorCallback): Unsubscribe;
62
80
  onRevertChangesError(callback: EventErrorCallback): Unsubscribe;
81
+ onAudioMuted(callback: EventCallback<boolean | undefined>): Unsubscribe;
82
+ onAudioSuspended(callback: EventCallback<boolean | undefined>): Unsubscribe;
83
+ onVideoMuted(callback: EventCallback<boolean | undefined>): Unsubscribe;
84
+ onVideoSuspended(callback: EventCallback<boolean | undefined>): Unsubscribe;
63
85
  }
64
- export declare const createPreviewStreamController: ({ getCurrentDevices, getCurrentMedia, updateMainStream, onEnded, mediaSignal, queueOptions, audioProcessors, videoProcessors, }: PreviewStreamParams) => PreviewStreamController;
86
+ export declare const createPreviewStreamController: ({ getCurrentDevices, getCurrentMedia, updateMainStream, onEnded, mainMediaSignal, queueOptions, audioProcessors, videoProcessors, getDefaultConstraints, signals, }: PreviewStreamParams) => PreviewStreamController;
65
87
  export type CreatePreviewStreamController = typeof createPreviewStreamController;
66
88
  export {};