@pexip/media 19.0.0 → 20.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/status.d.ts CHANGED
@@ -26,6 +26,7 @@ export declare const isUnknownError: UserMediaValidator;
26
26
  export declare const isInitial: UserMediaValidator;
27
27
  export declare const isInitialPermissions: UserMediaValidator;
28
28
  export declare const isInitialPermissionsNotGranted: UserMediaValidator;
29
+ export declare const isRequestPermissionNeeded: UserMediaValidator;
29
30
  export declare const isInitialPermissionsGranted: UserMediaValidator;
30
31
  export declare const isAudioDeviceInUse: UserMediaValidator;
31
32
  export declare const isDeviceInUse: UserMediaValidator;
@@ -36,4 +37,4 @@ export declare const getPermissionStatus: ({ audio, video, }: {
36
37
  audio: PermissionState;
37
38
  video: PermissionState;
38
39
  }) => UserMediaStatus.Initial | UserMediaStatus.InitialPermissionsGranted | UserMediaStatus.InitialPermissionsNotGranted | UserMediaStatus.InitialPermissionsVideoInputDenied | UserMediaStatus.InitialPermissionsAudioInputDenied | UserMediaStatus.InitialPermissionsVideoInputGranted | UserMediaStatus.InitialPermissionsAudioInputGranted | UserMediaStatus.InitialPermissionsGrantedVideoInputDenied | UserMediaStatus.InitialPermissionsGrantedAudioInputDenied | UserMediaStatus.PermissionsRejected;
39
- export declare const deriveInitialPermissionStatus: (prevStatus: UserMediaStatus, getPermissionState?: (anyActiveAudioTrack?: boolean | undefined, anyActiveVideoTrack?: boolean | undefined, getDevices?: (() => Promise<MediaDeviceInfo[]>) | undefined) => Promise<import("@pexip/media-control").InputDevicePermission>) => Promise<UserMediaStatus>;
40
+ export declare const deriveInitialPermissionStatus: (prevStatus: UserMediaStatus, getPermissionState?: (anyActiveAudioTrack?: boolean, anyActiveVideoTrack?: boolean, getDevices?: () => Promise<MediaDeviceInfo[]>) => Promise<import("@pexip/media-control").InputDevicePermission>) => Promise<UserMediaStatus>;
package/dist/status.js CHANGED
@@ -110,10 +110,18 @@ export const isInitialPermissionsNotGranted = status => [
110
110
  UserMediaStatus.InitialPermissionsNotGranted,
111
111
  UserMediaStatus.InitialPermissionsVideoInputDenied,
112
112
  ].includes(status);
113
+ const PermissionPromptStatusSet = new Set([
114
+ UserMediaStatus.InitialPermissionsNotGranted,
115
+ UserMediaStatus.InitialPermissionsAudioInputGranted,
116
+ UserMediaStatus.InitialPermissionsVideoInputGranted,
117
+ ]);
118
+ export const isRequestPermissionNeeded = status => PermissionPromptStatusSet.has(status);
113
119
  export const isInitialPermissionsGranted = status => [
114
120
  UserMediaStatus.InitialPermissionsGranted,
115
121
  UserMediaStatus.InitialPermissionsGrantedAudioInputDenied,
122
+ UserMediaStatus.InitialPermissionsAudioInputGranted,
116
123
  UserMediaStatus.InitialPermissionsGrantedVideoInputDenied,
124
+ UserMediaStatus.InitialPermissionsVideoInputGranted,
117
125
  ].includes(status);
118
126
  export const isAudioDeviceInUse = status => status === UserMediaStatus.AudioDeviceInUse;
119
127
  export const isDeviceInUse = status => [
@@ -0,0 +1 @@
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"}
@@ -1,5 +1,5 @@
1
1
  import type { AnalyzerNodeInit } from '@pexip/media-processor';
2
- import type { Media, AudioContentHint, VideoContentHint } from './types';
2
+ import type { AudioContentHint, Media, VideoContentHint, VideoStreamTrackProcessorAPIs } 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,3 +7,4 @@ 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,3 +38,6 @@ 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
@@ -128,7 +128,7 @@ export interface Media extends MediaAttributes {
128
128
  getVideoTracks(): MediaTrack[];
129
129
  addTrack(track: MediaTrack): void;
130
130
  removeTrack(track: MediaTrack): void;
131
- clone(): Media;
131
+ clone(signals?: MediaSignals): Media;
132
132
  }
133
133
  export interface MediaInit {
134
134
  constraints: MediaDeviceRequest;
@@ -181,7 +181,7 @@ export interface MediaTrack {
181
181
  getConstraints(): InputDeviceConstraint;
182
182
  applyConstraints(constraints: InputDeviceConstraint): Promise<void>;
183
183
  release(): Promise<void>;
184
- clone(): MediaTrack;
184
+ clone(signals?: MediaSignals): MediaTrack;
185
185
  toJSON(): unknown;
186
186
  }
187
187
  export interface MediaTrackInit extends Partial<MediaTrack> {
@@ -205,7 +205,7 @@ export type GetUserMediaProcess = (request: {
205
205
  }) => Promise<Media>;
206
206
  /**
207
207
  * Use which processor API to process the stream track
208
- * `stream` - Use MediaStreamTrackProcessor, when available
208
+ * `stream` - Use `MediaStreamTrackProcessor`, when available
209
209
  * `canvas` - Use Canvas
210
210
  */
211
211
  export type VideoStreamTrackProcessorAPIs = 'stream' | 'canvas';
@@ -11,7 +11,13 @@ 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.PermissionsOnlyAudioinput | UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices | UserMediaStatus.PermissionsOnlyAudioinputFallback | UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices | UserMediaStatus.PermissionsOnlyVideoinput | UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices | UserMediaStatus.PermissionsOnlyVideoinputFallback | UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
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;
15
+ export declare const hasLiveTrack: (kind: "audio" | "video", tracks: MediaStreamTrack[]) => boolean;
16
+ /**
17
+ * Merge the previous status with the next status and assuming requesting both
18
+ * video and audio inputs.
19
+ */
20
+ export declare const mergeStatus: (prevStatus: UserMediaStatus, nextStatus: UserMediaStatus, prevTracks?: MediaStreamTrack[], nextTracks?: MediaStreamTrack[]) => UserMediaStatus;
15
21
  /**
16
22
  * Try to come up with an error level according to provided UserMediaStatus and
17
23
  * MediaDeviceRequest
@@ -26,6 +32,7 @@ export declare const mergeNoDeviceStatus: (constraints: MediaDeviceRequest, anyD
26
32
  audio: boolean;
27
33
  video: boolean;
28
34
  }, status: UserMediaStatus) => UserMediaStatus;
35
+ export declare const deriveUserMediaStatus: (devices: IndexedDevices, constraints: MediaDeviceRequest, prevStatus: UserMediaStatus) => UserMediaStatus;
29
36
  export declare const requestUserMediaWithRetry: (getCurrentDevices: GetCurrentDevices, createRequestUserMedia?: (getCurrentDevices: GetCurrentDevices, getMedia?: ({ audio, video, }: MediaDeviceRequest) => Promise<MediaStream>) => GetUserMedia, gUM?: ({ audio, video, }: MediaDeviceRequest) => Promise<MediaStream>) => GetUserMedia;
30
37
  interface Options {
31
38
  getUserMedia: GetUserMedia;
package/dist/userMedia.js CHANGED
@@ -72,26 +72,317 @@ export const toSameDeviceStatus = ({ audio, video, }) => {
72
72
  return UserMediaStatus.PermissionsGrantedFallback;
73
73
  };
74
74
  export const toOnlyDeviceStatus = (kind, matched, devices) => {
75
- const hasRelatedDevices = devices.size(kind) > 0;
75
+ const hasAnotherTypeOfAuthorizedDevice = devices.anyAuthorizedDevice(kind === 'audioinput' ? 'videoinput' : 'audioinput');
76
+ const hasAnotherTypeOfDevice = devices.size(kind === 'audioinput' ? 'videoinput' : 'audioinput') > 0;
76
77
  if (matched) {
77
- if (hasRelatedDevices) {
78
+ if (hasAnotherTypeOfDevice) {
79
+ if (hasAnotherTypeOfAuthorizedDevice) {
80
+ return kind === 'audioinput'
81
+ ? UserMediaStatus.PermissionsOnlyAudioinput
82
+ : UserMediaStatus.PermissionsOnlyVideoinput;
83
+ }
78
84
  return kind === 'audioinput'
79
- ? UserMediaStatus.PermissionsOnlyAudioinput
80
- : UserMediaStatus.PermissionsOnlyVideoinput;
85
+ ? UserMediaStatus.PermissionsRejectedVideoInput
86
+ : UserMediaStatus.PermissionsRejectedAudioInput;
81
87
  }
82
88
  return kind === 'audioinput'
83
89
  ? UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices
84
90
  : UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices;
85
91
  }
86
- if (hasRelatedDevices) {
92
+ if (hasAnotherTypeOfDevice) {
93
+ if (hasAnotherTypeOfAuthorizedDevice) {
94
+ return kind === 'audioinput'
95
+ ? UserMediaStatus.PermissionsOnlyAudioinputFallback
96
+ : UserMediaStatus.PermissionsOnlyVideoinputFallback;
97
+ }
87
98
  return kind === 'audioinput'
88
- ? UserMediaStatus.PermissionsOnlyAudioinputFallback
89
- : UserMediaStatus.PermissionsOnlyVideoinputFallback;
99
+ ? UserMediaStatus.PermissionsRejectedVideoInput
100
+ : UserMediaStatus.PermissionsRejectedAudioInput;
90
101
  }
91
102
  return kind === 'audioinput'
92
103
  ? UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices
93
104
  : UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
94
105
  };
106
+ export const hasLiveTrack = (kind, tracks) => tracks.some(track => track.kind === kind && track.readyState === 'live');
107
+ /**
108
+ * Merge the previous status with the next status and assuming requesting both
109
+ * video and audio inputs.
110
+ */
111
+ export const mergeStatus = (prevStatus, nextStatus, prevTracks = [], nextTracks = []) => {
112
+ const hasPrevLiveAudio = hasLiveTrack('audio', prevTracks);
113
+ const hasPrevLiveVideo = hasLiveTrack('video', prevTracks);
114
+ const hasNextLiveAudio = hasLiveTrack('audio', nextTracks);
115
+ const hasNextLiveVideo = hasLiveTrack('video', nextTracks);
116
+ // Replace Audio Track
117
+ if (hasNextLiveAudio) {
118
+ assert(hasPrevLiveAudio === false, 'Only 1 live audio');
119
+ // Replace Audio and Video tracks
120
+ if (hasNextLiveVideo) {
121
+ assert(hasPrevLiveVideo === false, 'Only 1 live video');
122
+ // No need to consider the previous status
123
+ switch (nextStatus) {
124
+ case UserMediaStatus.PermissionsOnlyVideoinput:
125
+ case UserMediaStatus.PermissionsOnlyAudioinput:
126
+ return UserMediaStatus.PermissionsGranted;
127
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback:
128
+ return UserMediaStatus.PermissionsGrantedFallbackAudioinput;
129
+ case UserMediaStatus.PermissionsOnlyVideoinputFallback:
130
+ return UserMediaStatus.PermissionsGrantedFallbackVideoinput;
131
+ default:
132
+ return nextStatus;
133
+ }
134
+ }
135
+ if (hasPrevLiveVideo) {
136
+ // Merge previous video status with next audio status
137
+ switch (prevStatus) {
138
+ case UserMediaStatus.PermissionsGranted:
139
+ case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
140
+ case UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices:
141
+ case UserMediaStatus.PermissionsOnlyVideoinput: {
142
+ switch (nextStatus) {
143
+ case UserMediaStatus.PermissionsOnlyAudioinput:
144
+ return UserMediaStatus.PermissionsGranted;
145
+ case UserMediaStatus.PermissionsGrantedFallback:
146
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback:
147
+ return UserMediaStatus.PermissionsGrantedFallbackAudioinput;
148
+ default:
149
+ return nextStatus;
150
+ }
151
+ }
152
+ case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
153
+ case UserMediaStatus.PermissionsGrantedFallback:
154
+ case UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices:
155
+ case UserMediaStatus.PermissionsOnlyVideoinputFallback: {
156
+ switch (nextStatus) {
157
+ case UserMediaStatus.PermissionsGranted:
158
+ case UserMediaStatus.PermissionsOnlyAudioinput:
159
+ return UserMediaStatus.PermissionsGrantedFallbackVideoinput;
160
+ case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
161
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback:
162
+ return UserMediaStatus.PermissionsGrantedFallback;
163
+ default:
164
+ return nextStatus;
165
+ }
166
+ }
167
+ default:
168
+ return nextStatus;
169
+ }
170
+ }
171
+ // No previous Live Video
172
+ switch (nextStatus) {
173
+ case UserMediaStatus.PermissionsRejected:
174
+ case UserMediaStatus.PermissionsRejectedVideoInput: {
175
+ switch (prevStatus) {
176
+ case UserMediaStatus.PermissionsGrantedFallback:
177
+ case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
178
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback:
179
+ case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
180
+ return UserMediaStatus.PermissionsOnlyAudioinputFallback;
181
+ default:
182
+ return UserMediaStatus.PermissionsOnlyAudioinput;
183
+ }
184
+ }
185
+ case UserMediaStatus.NoDevicesFound:
186
+ case UserMediaStatus.AudioVideoDevicesNotFound:
187
+ case UserMediaStatus.VideoDeviceNotFound:
188
+ case UserMediaStatus.NoVideoDevicesFound:
189
+ switch (prevStatus) {
190
+ case UserMediaStatus.PermissionsGrantedFallback:
191
+ case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
192
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback:
193
+ case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
194
+ return UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices;
195
+ default:
196
+ return UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices;
197
+ }
198
+ case UserMediaStatus.DevicesInUse:
199
+ case UserMediaStatus.VideoDeviceInUse:
200
+ return UserMediaStatus.VideoDeviceInUse;
201
+ case UserMediaStatus.Overconstrained:
202
+ case UserMediaStatus.VideoOverconstrained:
203
+ return UserMediaStatus.VideoOverconstrained;
204
+ case UserMediaStatus.NotSupportedError:
205
+ case UserMediaStatus.NotSupportedErrorOnlyVideoInput:
206
+ return UserMediaStatus.NotSupportedErrorOnlyVideoInput;
207
+ case UserMediaStatus.InvalidConstraints:
208
+ case UserMediaStatus.InvalidVideoConstraints:
209
+ return UserMediaStatus.InvalidVideoConstraints;
210
+ case UserMediaStatus.UnknownError:
211
+ case UserMediaStatus.UnknownErrorOnlyVideoinput:
212
+ return UserMediaStatus.UnknownErrorOnlyVideoinput;
213
+ default:
214
+ return nextStatus;
215
+ }
216
+ }
217
+ // Replace Video Track Only
218
+ if (hasNextLiveVideo) {
219
+ assert(hasPrevLiveVideo === false, 'Only 1 live video');
220
+ if (hasPrevLiveAudio) {
221
+ switch (prevStatus) {
222
+ case UserMediaStatus.PermissionsGranted:
223
+ case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
224
+ case UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices:
225
+ case UserMediaStatus.PermissionsOnlyAudioinput: {
226
+ switch (nextStatus) {
227
+ case UserMediaStatus.PermissionsOnlyVideoinput:
228
+ return UserMediaStatus.PermissionsGranted;
229
+ case UserMediaStatus.PermissionsGrantedFallback:
230
+ case UserMediaStatus.PermissionsOnlyVideoinputFallback:
231
+ return UserMediaStatus.PermissionsGrantedFallbackVideoinput;
232
+ default:
233
+ return nextStatus;
234
+ }
235
+ }
236
+ case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
237
+ case UserMediaStatus.PermissionsGrantedFallback:
238
+ case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
239
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback: {
240
+ switch (nextStatus) {
241
+ case UserMediaStatus.PermissionsGranted:
242
+ case UserMediaStatus.PermissionsOnlyVideoinput:
243
+ return UserMediaStatus.PermissionsGrantedFallbackAudioinput;
244
+ case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
245
+ case UserMediaStatus.PermissionsOnlyVideoinputFallback:
246
+ return UserMediaStatus.PermissionsGrantedFallback;
247
+ default:
248
+ return nextStatus;
249
+ }
250
+ }
251
+ default:
252
+ return nextStatus;
253
+ }
254
+ }
255
+ }
256
+ // Failed to get a new track
257
+ if (hasPrevLiveAudio) {
258
+ switch (nextStatus) {
259
+ case UserMediaStatus.PermissionsRejected:
260
+ case UserMediaStatus.PermissionsRejectedVideoInput: {
261
+ switch (prevStatus) {
262
+ case UserMediaStatus.PermissionsGrantedFallback:
263
+ case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
264
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback:
265
+ case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
266
+ return UserMediaStatus.PermissionsOnlyAudioinputFallback;
267
+ default:
268
+ return UserMediaStatus.PermissionsOnlyAudioinput;
269
+ }
270
+ }
271
+ case UserMediaStatus.NoDevicesFound:
272
+ case UserMediaStatus.AudioVideoDevicesNotFound:
273
+ case UserMediaStatus.VideoDeviceNotFound:
274
+ case UserMediaStatus.NoVideoDevicesFound:
275
+ switch (prevStatus) {
276
+ case UserMediaStatus.PermissionsGrantedFallback:
277
+ case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
278
+ case UserMediaStatus.PermissionsOnlyAudioinputFallback:
279
+ case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
280
+ return UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices;
281
+ default:
282
+ return UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices;
283
+ }
284
+ case UserMediaStatus.DevicesInUse:
285
+ case UserMediaStatus.VideoDeviceInUse:
286
+ return UserMediaStatus.VideoDeviceInUse;
287
+ case UserMediaStatus.Overconstrained:
288
+ case UserMediaStatus.VideoOverconstrained:
289
+ return UserMediaStatus.VideoOverconstrained;
290
+ case UserMediaStatus.NotSupportedError:
291
+ case UserMediaStatus.NotSupportedErrorOnlyVideoInput:
292
+ return UserMediaStatus.NotSupportedErrorOnlyVideoInput;
293
+ case UserMediaStatus.InvalidConstraints:
294
+ case UserMediaStatus.InvalidVideoConstraints:
295
+ return UserMediaStatus.InvalidVideoConstraints;
296
+ case UserMediaStatus.UnknownError:
297
+ case UserMediaStatus.UnknownErrorOnlyVideoinput:
298
+ return UserMediaStatus.UnknownErrorOnlyVideoinput;
299
+ default:
300
+ return prevStatus;
301
+ }
302
+ }
303
+ if (hasPrevLiveVideo) {
304
+ switch (nextStatus) {
305
+ case UserMediaStatus.PermissionsRejected:
306
+ case UserMediaStatus.PermissionsRejectedAudioInput:
307
+ switch (prevStatus) {
308
+ case UserMediaStatus.PermissionsGrantedFallback:
309
+ case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
310
+ case UserMediaStatus.PermissionsOnlyVideoinputFallback:
311
+ case UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices:
312
+ return UserMediaStatus.PermissionsOnlyVideoinputFallback;
313
+ default:
314
+ return UserMediaStatus.PermissionsOnlyVideoinput;
315
+ }
316
+ case UserMediaStatus.NoDevicesFound:
317
+ case UserMediaStatus.AudioVideoDevicesNotFound:
318
+ case UserMediaStatus.AudioDeviceNotFound:
319
+ case UserMediaStatus.NoAudioDevicesFound: {
320
+ switch (prevStatus) {
321
+ case UserMediaStatus.PermissionsGrantedFallback:
322
+ case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
323
+ case UserMediaStatus.PermissionsOnlyVideoinputFallback:
324
+ case UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices:
325
+ return UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
326
+ default:
327
+ return UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices;
328
+ }
329
+ }
330
+ case UserMediaStatus.DevicesInUse:
331
+ case UserMediaStatus.AudioDeviceInUse:
332
+ return UserMediaStatus.AudioDeviceInUse;
333
+ case UserMediaStatus.Overconstrained:
334
+ case UserMediaStatus.AudioOverconstrained:
335
+ return UserMediaStatus.AudioOverconstrained;
336
+ case UserMediaStatus.NotSupportedError:
337
+ case UserMediaStatus.NotSupportedErrorOnlyAudioInput:
338
+ return UserMediaStatus.NotSupportedErrorOnlyAudioInput;
339
+ case UserMediaStatus.InvalidConstraints:
340
+ case UserMediaStatus.InvalidAudioConstraints:
341
+ return UserMediaStatus.InvalidAudioConstraints;
342
+ case UserMediaStatus.UnknownError:
343
+ case UserMediaStatus.UnknownErrorOnlyAudioinput:
344
+ return UserMediaStatus.UnknownErrorOnlyAudioinput;
345
+ default:
346
+ return prevStatus;
347
+ }
348
+ }
349
+ switch (nextStatus) {
350
+ case UserMediaStatus.PermissionsRejected:
351
+ case UserMediaStatus.PermissionsRejectedAudioInput:
352
+ case UserMediaStatus.PermissionsRejectedVideoInput:
353
+ return UserMediaStatus.PermissionsRejected;
354
+ case UserMediaStatus.NoDevicesFound:
355
+ case UserMediaStatus.AudioVideoDevicesNotFound:
356
+ case UserMediaStatus.AudioDeviceNotFound:
357
+ case UserMediaStatus.VideoDeviceNotFound:
358
+ case UserMediaStatus.NoVideoDevicesFound:
359
+ case UserMediaStatus.NoAudioDevicesFound: {
360
+ return UserMediaStatus.NoDevicesFound;
361
+ }
362
+ case UserMediaStatus.DevicesInUse:
363
+ case UserMediaStatus.AudioDeviceInUse:
364
+ case UserMediaStatus.VideoDeviceInUse:
365
+ return UserMediaStatus.DevicesInUse;
366
+ case UserMediaStatus.Overconstrained:
367
+ case UserMediaStatus.AudioOverconstrained:
368
+ case UserMediaStatus.VideoOverconstrained:
369
+ return UserMediaStatus.Overconstrained;
370
+ case UserMediaStatus.InvalidConstraints:
371
+ case UserMediaStatus.InvalidVideoConstraints:
372
+ case UserMediaStatus.InvalidAudioConstraints:
373
+ return UserMediaStatus.InvalidConstraints;
374
+ case UserMediaStatus.NotSupportedError:
375
+ case UserMediaStatus.NotSupportedErrorOnlyAudioInput:
376
+ case UserMediaStatus.NotSupportedErrorOnlyVideoInput:
377
+ return UserMediaStatus.NotSupportedError;
378
+ case UserMediaStatus.UnknownError:
379
+ case UserMediaStatus.UnknownErrorOnlyAudioinput:
380
+ case UserMediaStatus.UnknownErrorOnlyVideoinput:
381
+ return UserMediaStatus.UnknownError;
382
+ default:
383
+ return nextStatus;
384
+ }
385
+ };
95
386
  /**
96
387
  * Try to come up with an error level according to provided UserMediaStatus and
97
388
  * MediaDeviceRequest
@@ -116,18 +407,26 @@ export const deriveErrorLevel = (error, status, constraints) => {
116
407
  : 'warn';
117
408
  };
118
409
  export const requestUserMedia = (getCurrentDevices, getMedia = getUserMedia) => async (constraints) => {
119
- const deriveDeviceStatus = makeDeriveDeviceStatus(constraints);
120
410
  try {
121
411
  const stream = await getMedia(constraints);
122
- const grantedDevices = await getCurrentDevices();
123
- const { audio, video } = isStreamingRequestedDevices(constraints, stream, grantedDevices);
124
- const onlyAudioStatus = toOnlyDeviceStatus('audioinput', audio, grantedDevices);
125
- const onlyVideoStatus = toOnlyDeviceStatus('videoinput', video, grantedDevices);
412
+ const devices = await getCurrentDevices();
413
+ const deriveDeviceStatus = makeDeriveDeviceStatus({
414
+ audio: devices.anyAuthorizedDevice('audioinput')
415
+ ? constraints.audio
416
+ : false,
417
+ video: devices.anyAuthorizedDevice('videoinput')
418
+ ? constraints.video
419
+ : false,
420
+ });
421
+ const { audio, video } = isStreamingRequestedDevices(constraints, stream, devices);
422
+ const onlyAudioStatus = toOnlyDeviceStatus('audioinput', audio, devices);
423
+ const onlyVideoStatus = toOnlyDeviceStatus('videoinput', video, devices);
126
424
  const status = deriveDeviceStatus(onlyAudioStatus, onlyVideoStatus, toSameDeviceStatus({ audio, video }));
127
425
  return [stream, status];
128
426
  }
129
427
  catch (error) {
130
428
  if (error instanceof Error) {
429
+ const deriveDeviceStatus = makeDeriveDeviceStatus(constraints);
131
430
  const status = toErrorDeviceStatus(error, deriveDeviceStatus);
132
431
  return [undefined, status];
133
432
  }
@@ -150,17 +449,18 @@ export const mergeNoDeviceStatus = (constraints, anyDevices, status) => {
150
449
  }
151
450
  return status;
152
451
  };
452
+ export const deriveUserMediaStatus = (devices, constraints, prevStatus) => {
453
+ const anyAudioDevices = devices.size('audioinput') > 0;
454
+ const anyVideoDevices = devices.size('videoinput') > 0;
455
+ return mergeNoDeviceStatus(constraints, { audio: anyAudioDevices, video: anyVideoDevices }, prevStatus);
456
+ };
153
457
  export const requestUserMediaWithRetry = (getCurrentDevices, createRequestUserMedia = requestUserMedia, gUM = getUserMedia) => {
154
458
  const request = createRequestUserMedia(getCurrentDevices, gUM);
155
459
  return async (constraints) => {
156
460
  const [stream, status] = await request(constraints);
157
461
  const devices = await getCurrentDevices();
158
- const anyAudioDevices = devices.size('audioinput') > 0;
159
- const anyVideoDevices = devices.size('videoinput') > 0;
160
- return [
161
- stream,
162
- mergeNoDeviceStatus(constraints, { audio: anyAudioDevices, video: anyVideoDevices }, status),
163
- ];
462
+ const finalStatus = deriveUserMediaStatus(devices, constraints, status);
463
+ return [stream, finalStatus];
164
464
  };
165
465
  };
166
466
  /**
@@ -216,8 +516,32 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
216
516
  }));
217
517
  }
218
518
  const [stream, status] = await getUserMedia(constraints);
219
- logger.debug({ scope, stream, status }, 'post getUserMedia');
519
+ let finalStatus = status;
520
+ if (currentMedia &&
521
+ constraints.audio !== false &&
522
+ constraints.video !== false) {
523
+ finalStatus = mergeStatus(currentMedia.status, status,
524
+ // When the constraint is undefined, it means the track will be kept as what it is.
525
+ // i.e. It belongs to the next track for this mergeStatus logic.
526
+ [
527
+ ...(constraints.audio === undefined
528
+ ? []
529
+ : currentMedia.stream.getAudioTracks()),
530
+ ...(constraints.video === undefined
531
+ ? []
532
+ : currentMedia.stream.getVideoTracks()),
533
+ ], [
534
+ ...(stream?.getTracks() ?? []),
535
+ ...(constraints.audio === undefined
536
+ ? currentMedia.stream.getAudioTracks()
537
+ : []),
538
+ ...(constraints.video === undefined
539
+ ? currentMedia.stream.getVideoTracks()
540
+ : []),
541
+ ]);
542
+ }
220
543
  const devices = await getCurrentDevices();
544
+ logger.debug({ scope, stream, devices, status: finalStatus }, 'post getUserMedia');
221
545
  const findInput = findMediaInputFromMediaStreamTrack(devices);
222
546
  let audioMediaTrack;
223
547
  let videoMediaTrack;
@@ -225,6 +549,7 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
225
549
  if (constraints.audio !== undefined) {
226
550
  const audioTrack = stream?.getAudioTracks().at(0);
227
551
  const audioInput = findInput(audioTrack);
552
+ assert(Boolean(audioTrack) === Boolean(audioInput), 'audioTrack <=> audioInput');
228
553
  const { contentHint: [[contentHint] = []], } = extractContentHint(constraints.audio);
229
554
  if (audioTrack) {
230
555
  audioTrack.contentHint = contentHint ?? '';
@@ -243,6 +568,7 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
243
568
  if (constraints.video !== undefined) {
244
569
  const videoTrack = stream?.getVideoTracks().at(0);
245
570
  const videoInput = findInput(videoTrack);
571
+ assert(Boolean(videoTrack) === Boolean(videoInput), 'videoTrack <=> videoInput');
246
572
  const { contentHint: [[contentHint] = []], } = extractContentHint(constraints.video);
247
573
  if (videoTrack) {
248
574
  videoTrack.contentHint = contentHint ?? '';
@@ -258,8 +584,19 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
258
584
  });
259
585
  currentMedia?.addTrack(videoMediaTrack);
260
586
  }
587
+ logger.info({
588
+ permission,
589
+ devices,
590
+ stream,
591
+ status: finalStatus,
592
+ originalConstraints,
593
+ currentMedia,
594
+ audioMediaTrack,
595
+ videoMediaTrack,
596
+ constraints,
597
+ }, 'GetUserMedia');
261
598
  if (currentMedia) {
262
- currentMedia.status = status;
599
+ currentMedia.status = finalStatus;
263
600
  currentMedia.setOriginalConstraints(originalConstraints);
264
601
  return currentMedia;
265
602
  }
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import type { IndexedDevices, InputDeviceConstraint, MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
2
- import type { ExtendedMediaTrackSettings, ExtendedMediaTrackSettingsKey, Media, MediaInit, MediaTrack, MediaTrackInit, TrackProcessor, UserMediaStatus } from './types';
1
+ import type { IndexedDevices, InputDeviceConstraint, MediaDeviceInfoLike, MediaDeviceRequest, InputConstraintSet } from '@pexip/media-control';
2
+ import { type RenderEffects } from '@pexip/media-processor';
3
+ import type { AudioContentHint, ExtendedMediaTrackSettings, ExtendedMediaTrackSettingsKey, Media, MediaInit, MediaTrack, MediaTrackInit, TrackProcessor, UserMediaStatus, VideoContentHint } from './types';
3
4
  export declare const makeDeriveDeviceStatus: (constraints: MediaDeviceRequest) => (audio: UserMediaStatus, video: UserMediaStatus, both: UserMediaStatus) => UserMediaStatus;
4
5
  export declare const createMediaTrack: (trackInit: MediaTrackInit) => MediaTrack;
5
6
  export declare const createTrackProcessingPipeline: (processors: TrackProcessor[]) => (track: MediaTrack) => Promise<MediaTrack>;
@@ -8,12 +9,12 @@ export declare const createTrackProcessingPipeline: (processors: TrackProcessor[
8
9
  * otherwise `undefined`
9
10
  */
10
11
  export declare const interpretInput: (input: boolean | MediaDeviceInfoLike | undefined, getCurrentInput: () => MediaDeviceInfoLike | undefined) => MediaDeviceInfoLike | undefined;
11
- export declare const findExpectedInput: (devices: IndexedDevices, constraints: InputDeviceConstraint | undefined, input: MediaDeviceInfoLike | undefined, kind: 'audioinput' | 'videoinput') => MediaDeviceInfoLike | undefined;
12
+ export declare const findExpectedInput: (devices: IndexedDevices, constraints: InputDeviceConstraint | undefined, input: MediaDeviceInfoLike | undefined, kind: "audioinput" | "videoinput") => MediaDeviceInfoLike | undefined;
12
13
  /**
13
14
  * A utility function to check if the provided track is muted. There are
14
15
  * 2 factors to be considered: `MediaStreamTrack['muted']` and `MediaStreamTrack['enabled']`.
15
16
  *
16
- * ```
17
+ * ```md
17
18
  * | muted \ enabled | true | false |
18
19
  * |-----------------| ----- | ----- |
19
20
  * | true | true | true |
@@ -27,7 +28,7 @@ export declare const findExpectedInput: (devices: IndexedDevices, constraints: I
27
28
  * there is no track to check
28
29
  */
29
30
  export declare const isTrackMuted: (track: MediaStreamTrack | undefined) => boolean | undefined;
30
- export declare const buildMedia: (mediaInit: MediaInit, onDevicesChanged?: import("@pexip/signal").Signal<IndexedDevices>) => Media;
31
+ export declare const buildMedia: (mediaInit: MediaInit, onDevicesChanged?: import("@pexip/signal").Signal<IndexedDevices, IndexedDevices>) => Media;
31
32
  /**
32
33
  * Shallow copy the provided object and override with provided overriding
33
34
  *
@@ -38,8 +39,6 @@ export declare const buildMedia: (mediaInit: MediaInit, onDevicesChanged?: impor
38
39
  */
39
40
  export declare const shallowCopy: <T>(original: T, overriding: Partial<T>) => T;
40
41
  export declare const getDevicesChanges: (prev: MediaDeviceInfoLike[], next: MediaDeviceInfoLike[]) => {
41
- unauthorized: MediaDeviceInfoLike[];
42
- authorized: MediaDeviceInfoLike[];
43
42
  found: MediaDeviceInfoLike[];
44
43
  lost: MediaDeviceInfoLike[];
45
44
  devices: MediaDeviceInfoLike[];
@@ -47,7 +46,10 @@ export declare const getDevicesChanges: (prev: MediaDeviceInfoLike[], next: Medi
47
46
  export declare const AUDIO_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
48
47
  export declare const VIDEO_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
49
48
  export declare const MIXING_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
49
+ export declare const PAN_TILT_ZOOM_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
50
50
  export declare const hasSettingsChanged: (keysToLookFor: ExtendedMediaTrackSettingsKey[]) => (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => boolean;
51
+ export declare const isApplyingRenderEffect: (effect: RenderEffects) => boolean;
52
+ export declare const getSettingsFromKeys: (keys: ExtendedMediaTrackSettingsKey[], settings: InputConstraintSet | undefined | false) => false | ExtendedMediaTrackSettings | undefined;
51
53
  export declare const diffSettings: (keysToLookFor: ExtendedMediaTrackSettingsKey[]) => (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => ExtendedMediaTrackSettings | undefined;
52
54
  export declare const mergeSettings: (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => ExtendedMediaTrackSettings | undefined;
53
55
  /**
@@ -67,7 +69,7 @@ export declare const getBlurKernelSize: (percentage: number, height: number, max
67
69
  * @param hint - Content hint
68
70
  * @param track - The track to be applied
69
71
  */
70
- export declare const applyContentHint: <T extends "" | "speech" | "speech-recognition" | "music" | "motion" | "detail" | "text">(hint?: T | undefined) => (track: MediaStreamTrack) => void;
72
+ export declare const applyContentHint: <T extends AudioContentHint | VideoContentHint>(hint?: T) => (track: MediaStreamTrack) => void;
71
73
  /**
72
74
  * Check if the browser supports PTZ feature
73
75
  * @returns true if the browser supports PTZ feature, otherwise false