@pexip/media-control 18.5.0 → 19.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/CHANGELOG.md +32 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/constraints.d.ts +13 -12
- package/dist/constraints.js +47 -32
- package/dist/devices.d.ts +43 -41
- package/dist/devices.js +128 -93
- package/dist/getMediaStream.js +5 -15
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -5
- package/dist/typeGuards.d.ts +3 -1
- package/dist/typeGuards.js +29 -6
- package/dist/types.d.ts +25 -1
- package/dist/utils.d.ts +27 -2
- package/dist/utils.js +123 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 19.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 878fcca: Index devices
|
|
8
|
+
|
|
9
|
+
Change to device index instead of Array to store device list, and added the
|
|
10
|
+
following functions:
|
|
11
|
+
|
|
12
|
+
- `function createIndexedDevices(MediaDeviceInfoLike[]): IndexedDevices`
|
|
13
|
+
- `function isIndexedDevices(t: unknown): t is IndexedDevices`
|
|
14
|
+
- `function getDeviceId(device: Pick<MediaDeviceInfoLike, 'groupId' | 'deviceId' | 'kind'>): string`
|
|
15
|
+
- `function reverseDeviceId(id: string): MediaDeviceInfoLike`
|
|
16
|
+
|
|
17
|
+
All the device lookup functions are updated to use `IndexedDevices`.
|
|
18
|
+
|
|
19
|
+
Also, the artificial `devicechange` before and after `getUserMedia` call are
|
|
20
|
+
removed.
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- 6c42e61: Use `exact` constraint when the media device is connected and `ideal`
|
|
25
|
+
if it is not found.
|
|
26
|
+
- e362a65: Add `isTrackMutedOrEnded` function
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [164a3e2]
|
|
31
|
+
- Updated dependencies [b13010c]
|
|
32
|
+
- Updated dependencies [66a8f72]
|
|
33
|
+
- @pexip/utils@16.13.0
|
|
34
|
+
|
|
3
35
|
## 18.5.0
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
package/dist/constraints.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FacingMode, InputConstraintSet, InputDeviceConstraint, MediaConstraintRequirement, MediaDeviceInfoLike, MediaDeviceRequest, NormalizedConstraintDeviceParameters } from './types';
|
|
1
|
+
import type { FacingMode, InputConstraintSet, InputDeviceConstraint, MediaConstraintRequirement, MediaDeviceInfoLike, MediaDeviceRequest, NormalizedConstraintDeviceParameters, IndexedDevices } from './types';
|
|
2
2
|
import type { ConstrainBooleanKeys, ConstrainDoubleKeys, ConstrainParamKeys, ConstrainRangeParamKeys, ConstrainStringKeys, ConstrainULongKeys, ExtendedConstrainBooleanKeys, ExtendedConstrainDoubleKeys, ExtendedConstrainStringKeys, ExtendedConstrainULongKeys } from './typeGuards';
|
|
3
3
|
/**
|
|
4
4
|
* Check if provided constraint is an `exact` device constraint
|
|
@@ -12,7 +12,7 @@ export declare const removeUnsupportedConstraints: (constraints: MediaStreamCons
|
|
|
12
12
|
/**
|
|
13
13
|
* Merge base constraints with provided base constraints and another constraints
|
|
14
14
|
*/
|
|
15
|
-
export declare const mergeConstraints: (baseConstraints: InputConstraintSet | boolean | undefined) => (constraints: InputDeviceConstraint | undefined) => InputConstraintSet | boolean;
|
|
15
|
+
export declare const mergeConstraints: (baseConstraints: InputConstraintSet | boolean | undefined) => (constraints: InputDeviceConstraint | undefined) => InputConstraintSet | boolean | undefined;
|
|
16
16
|
/**
|
|
17
17
|
* Normalize Device
|
|
18
18
|
* Convert any valid `MediaDeviceInfoLike` into `MediaDeviceInfoLike[]`
|
|
@@ -52,7 +52,7 @@ export type ConstrainNumberTuple = [
|
|
|
52
52
|
];
|
|
53
53
|
export declare const extractConstrainNumber: (key: ConstrainULongKeys | ConstrainDoubleKeys | ExtendedConstrainULongKeys | ExtendedConstrainDoubleKeys) => (constraints: InputConstraintSet) => ConstrainNumberTuple | ConstrainNoneTuple;
|
|
54
54
|
export type ConstrainDeviceTuple = [MediaDeviceInfoLike[], ConstrainParamKeys];
|
|
55
|
-
export declare const extractConstrainDevice: (constraints: InputDeviceConstraint | undefined) => ConstrainDeviceTuple | ConstrainNoneTuple;
|
|
55
|
+
export declare const extractConstrainDevice: (constraints: InputDeviceConstraint | undefined, initialParam?: ConstrainParamKeys) => ConstrainDeviceTuple | ConstrainNoneTuple;
|
|
56
56
|
/**
|
|
57
57
|
* Compare float point number a and b to see if they are closed to be considered
|
|
58
58
|
* as having the same value
|
|
@@ -88,7 +88,7 @@ export declare const satisfyConstrainNumber: (constraint: ConstrainNumber | unde
|
|
|
88
88
|
*
|
|
89
89
|
* @internal
|
|
90
90
|
*/
|
|
91
|
-
export declare const resolveMediaDeviceConstraints: (constraints?: InputDeviceConstraint, base?: MediaTrackConstraints | boolean) => MediaTrackConstraints | boolean;
|
|
91
|
+
export declare const resolveMediaDeviceConstraints: (constraints?: InputDeviceConstraint, base?: MediaTrackConstraints | boolean) => MediaTrackConstraints | boolean | undefined;
|
|
92
92
|
/**
|
|
93
93
|
* @remarks
|
|
94
94
|
* Constraint multiple (exact) devices is not supported
|
|
@@ -103,10 +103,10 @@ export declare const getMediaConstraints: ({ audio, video, defaultConstraints, }
|
|
|
103
103
|
* @param constraints - constraints to be applied
|
|
104
104
|
*/
|
|
105
105
|
export declare const applyConstraints: (tracks: MediaStreamTrack[] | undefined, constraints: MediaDeviceRequest) => Promise<void>;
|
|
106
|
-
export declare const findDeviceFrom: (devicesToFind: MediaDeviceInfoLike[],
|
|
107
|
-
export declare const findDeviceFromDeviceConstraints: (device: InputConstraintSet['device'], devices:
|
|
108
|
-
export declare const relaxDevice: (device: InputConstraintSet['device'], devices:
|
|
109
|
-
export declare const resolveOnlyDevice: (devices:
|
|
106
|
+
export declare const findDeviceFrom: (devicesToFind: MediaDeviceInfoLike[], devices: IndexedDevices) => MediaDeviceInfoLike[];
|
|
107
|
+
export declare const findDeviceFromDeviceConstraints: (device: InputConstraintSet['device'], devices: IndexedDevices) => MediaDeviceInfoLike[] | undefined;
|
|
108
|
+
export declare const relaxDevice: (device: InputConstraintSet['device'], devices: IndexedDevices) => MediaDeviceInfoLike[] | undefined;
|
|
109
|
+
export declare const resolveOnlyDevice: (devices: IndexedDevices) => true | MediaDeviceInfoLike | undefined;
|
|
110
110
|
type ConstrainNumberKeys = ConstrainULongKeys | ConstrainDoubleKeys | ExtendedConstrainDoubleKeys | ExtendedConstrainULongKeys;
|
|
111
111
|
type ConstrainTuples = ConstrainDeviceTuple | ConstrainNumberTuple | ConstrainStringTuple | ConstrainBooleanTuple | ConstrainNoneTuple;
|
|
112
112
|
type ExtractConstraintFn<R extends ConstrainTuples> = (constraints: InputDeviceConstraint | undefined) => R;
|
|
@@ -118,7 +118,7 @@ type ConstrainStrings = Record<ConstrainStringKeys | ExtendedConstrainStringKeys
|
|
|
118
118
|
type ConstrainNumbers = Record<ConstrainNumberKeys, ConstrainNumberTuple | ConstrainNoneTuple>;
|
|
119
119
|
type ConstrainBooleans = Record<ConstrainBooleanKeys | ExtendedConstrainBooleanKeys, ConstrainBooleanTuple | ConstrainNoneTuple>;
|
|
120
120
|
type ConstrainDevices = Record<'device', ConstrainDeviceTuple | ConstrainNoneTuple>;
|
|
121
|
-
type Constraints = ConstrainStrings & ConstrainNumbers & ConstrainBooleans & ConstrainDevices;
|
|
121
|
+
export type Constraints = ConstrainStrings & ConstrainNumbers & ConstrainBooleans & ConstrainDevices;
|
|
122
122
|
/**
|
|
123
123
|
* Extract the constraints with provided keys
|
|
124
124
|
*
|
|
@@ -157,16 +157,17 @@ export declare const extractDeviceId: (constraints: InputConstraintSet) => Const
|
|
|
157
157
|
* @returns `true` means it can be any devices, `undefined` means not found,
|
|
158
158
|
* otherwise, the matched device will be returned
|
|
159
159
|
*/
|
|
160
|
-
export declare const findDeviceFromConstraints: (constraints: InputDeviceConstraint | undefined, devices:
|
|
160
|
+
export declare const findDeviceFromConstraints: (constraints: InputDeviceConstraint | undefined, devices: IndexedDevices) => boolean | MediaDeviceInfoLike | undefined;
|
|
161
161
|
/**
|
|
162
162
|
* Relax input constraints to enable looking up the device by `deviceId` as well
|
|
163
163
|
* as `label` as a fallback as a best effort to get a similar device when
|
|
164
|
-
* possible.
|
|
164
|
+
* possible. If the the device is found we will use the `exact` constraint otherwise
|
|
165
|
+
* we will use the `ideal`.
|
|
165
166
|
*
|
|
166
167
|
* @param input - The input constraints to be relaxed
|
|
167
168
|
* @param devices - The current device list
|
|
168
169
|
*/
|
|
169
|
-
export declare const relaxInputConstraint: (input: InputDeviceConstraint | undefined, devices:
|
|
170
|
+
export declare const relaxInputConstraint: (kind: 'audioinput' | 'videoinput', input: InputDeviceConstraint | undefined, devices: IndexedDevices) => InputDeviceConstraint | undefined;
|
|
170
171
|
export declare const getConstraintsHandlers: () => {
|
|
171
172
|
getDefaultConstraints: () => MediaStreamConstraints;
|
|
172
173
|
setDefaultConstraints: (newConstraints: MediaStreamConstraints) => void;
|
package/dist/constraints.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isBoolean, isConstrainBooleanKeys, isConstrainBooleanParameters, isConstrainDOMStringParameters, isConstrainDoubleKeys, isConstrainDoubleRange, isConstrainRange, isConstrainStringKeys, isConstrainULongKeys, isConstrainULongRange, isConstraintDOMString, isConstraintDeviceParameters, isConstraintSetDevice, isDeviceConstraint, isExtendedConstrainBooleanKeys, isExtendedConstrainDoubleKeys, isExtendedConstrainStringKeys, isExtendedConstrainULongKeys, isFacingMode, isFloat, isInputConstraintSet, isInteger, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaTrackConstraintSetKey, isMediaTrackConstraints, isNumber, isUndefined, } from './typeGuards';
|
|
2
2
|
import { logger } from './logger';
|
|
3
|
-
import { findDevice } from './utils';
|
|
4
3
|
/**
|
|
5
4
|
* Check if provided constraint is an `exact` device constraint
|
|
6
5
|
*/
|
|
@@ -61,9 +60,12 @@ export const mergeConstraints = (baseConstraints) => (constraints) => {
|
|
|
61
60
|
if (isDeviceConstraint(constraints)) {
|
|
62
61
|
return { device: constraints };
|
|
63
62
|
}
|
|
64
|
-
return constraints
|
|
63
|
+
return constraints;
|
|
65
64
|
}
|
|
66
|
-
if (
|
|
65
|
+
if (constraints === undefined) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
if (constraints === false) {
|
|
67
69
|
return false;
|
|
68
70
|
}
|
|
69
71
|
if (isDeviceConstraint(constraints)) {
|
|
@@ -165,7 +167,7 @@ export const toArray = (t) => {
|
|
|
165
167
|
export const NONE = [undefined, 'ideal'];
|
|
166
168
|
export const extractConstrainString = (key) => (constraints) => {
|
|
167
169
|
const constraint = constraints[key];
|
|
168
|
-
if (
|
|
170
|
+
if (constraint === undefined) {
|
|
169
171
|
return NONE;
|
|
170
172
|
}
|
|
171
173
|
if (isConstraintDOMString(constraint)) {
|
|
@@ -234,7 +236,7 @@ export const extractConstrainNumber = (key) => (constraints) => {
|
|
|
234
236
|
}
|
|
235
237
|
return NONE;
|
|
236
238
|
};
|
|
237
|
-
export const extractConstrainDevice = (constraints) => {
|
|
239
|
+
export const extractConstrainDevice = (constraints, initialParam = 'ideal') => {
|
|
238
240
|
if (!constraints ||
|
|
239
241
|
isBoolean(constraints) ||
|
|
240
242
|
(Array.isArray(constraints) && !constraints.length)) {
|
|
@@ -246,7 +248,7 @@ export const extractConstrainDevice = (constraints) => {
|
|
|
246
248
|
if (isMediaDeviceInfo(device) || isMediaDeviceInfoArray(device)) {
|
|
247
249
|
const normalized = normalizeDevice(device);
|
|
248
250
|
if (normalized) {
|
|
249
|
-
return [normalized,
|
|
251
|
+
return [normalized, initialParam];
|
|
250
252
|
}
|
|
251
253
|
}
|
|
252
254
|
if (isConstraintDeviceParameters(device)) {
|
|
@@ -389,7 +391,7 @@ export const resolveMediaDeviceConstraints = (constraints, base) => {
|
|
|
389
391
|
if (constraints && isMediaTrackConstraints(base)) {
|
|
390
392
|
return base;
|
|
391
393
|
}
|
|
392
|
-
return !!constraints;
|
|
394
|
+
return constraints === undefined ? constraints : !!constraints;
|
|
393
395
|
};
|
|
394
396
|
/**
|
|
395
397
|
* @remarks
|
|
@@ -429,43 +431,43 @@ export const applyConstraints = async (tracks, constraints) => {
|
|
|
429
431
|
return [];
|
|
430
432
|
}));
|
|
431
433
|
};
|
|
432
|
-
export const findDeviceFrom = (devicesToFind,
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
434
|
+
export const findDeviceFrom = (devicesToFind, devices) => devicesToFind.flatMap(device => devices.get(device));
|
|
435
|
+
const findDeviceFromDeviceId = (constrainDeviceIds, devices) => {
|
|
436
|
+
for (const deviceId of constrainDeviceIds) {
|
|
437
|
+
const device = devices.get({ deviceId, kind: 'audioinput' }).at(0) ??
|
|
438
|
+
devices.get({ deviceId, kind: 'videoinput' }).at(0) ??
|
|
439
|
+
devices.get({ deviceId, kind: 'audiooutput' }).at(0);
|
|
440
|
+
if (device) {
|
|
441
|
+
return device;
|
|
437
442
|
}
|
|
438
|
-
return [];
|
|
439
|
-
});
|
|
440
|
-
if (devicesFound.length) {
|
|
441
|
-
return devicesFound;
|
|
442
443
|
}
|
|
443
|
-
return undefined;
|
|
444
444
|
};
|
|
445
445
|
export const findDeviceFromDeviceConstraints = (device, devices) => {
|
|
446
446
|
const normalized = normalizeDeviceConstraint(device);
|
|
447
447
|
if (normalized) {
|
|
448
448
|
const { ideal, exact } = normalized;
|
|
449
449
|
if (exact) {
|
|
450
|
-
|
|
450
|
+
const found = findDeviceFrom(exact, devices);
|
|
451
|
+
return found.length ? found : undefined;
|
|
451
452
|
}
|
|
452
453
|
if (ideal) {
|
|
453
|
-
|
|
454
|
+
const found = findDeviceFrom(ideal, devices);
|
|
455
|
+
return found.length ? found : undefined;
|
|
454
456
|
}
|
|
455
457
|
}
|
|
456
458
|
};
|
|
457
459
|
export const relaxDevice = (device, devices) => {
|
|
458
460
|
const found = findDeviceFromDeviceConstraints(device, devices);
|
|
459
|
-
if (found) {
|
|
461
|
+
if (found?.length) {
|
|
460
462
|
return found;
|
|
461
463
|
}
|
|
462
464
|
// Return the original with normalized form
|
|
463
465
|
return normalizeDevice(device);
|
|
464
466
|
};
|
|
465
467
|
export const resolveOnlyDevice = (devices) => {
|
|
466
|
-
switch (devices.
|
|
468
|
+
switch (devices.size('audioinput') + devices.size('videoinput')) {
|
|
467
469
|
case 1:
|
|
468
|
-
return devices
|
|
470
|
+
return devices.first('audioinput') ?? devices.first('videoinput');
|
|
469
471
|
case 0:
|
|
470
472
|
return undefined;
|
|
471
473
|
default:
|
|
@@ -580,8 +582,7 @@ export const findDeviceFromConstraints = (constraints, devices) => {
|
|
|
580
582
|
return constraints ? resolveOnlyDevice(devices) : constraints;
|
|
581
583
|
}
|
|
582
584
|
if (constraints === undefined ||
|
|
583
|
-
!devices.
|
|
584
|
-
devices.some(device => !device.label) ||
|
|
585
|
+
!(devices.size('audioinput') + devices.size('videoinput')) ||
|
|
585
586
|
(Array.isArray(constraints) && !constraints.length)) {
|
|
586
587
|
return undefined;
|
|
587
588
|
}
|
|
@@ -598,10 +599,7 @@ export const findDeviceFromConstraints = (constraints, devices) => {
|
|
|
598
599
|
}
|
|
599
600
|
const [ConstrainDeviceIds, deviceIdParam] = extractConstraints('deviceId')(constraints);
|
|
600
601
|
if (ConstrainDeviceIds) {
|
|
601
|
-
const deviceFound =
|
|
602
|
-
const id = ConstrainDeviceIds.find(id => id && device.label && device.deviceId === id);
|
|
603
|
-
return !!id;
|
|
604
|
-
});
|
|
602
|
+
const deviceFound = findDeviceFromDeviceId(ConstrainDeviceIds, devices);
|
|
605
603
|
if (deviceFound) {
|
|
606
604
|
return deviceFound;
|
|
607
605
|
}
|
|
@@ -614,16 +612,33 @@ export const findDeviceFromConstraints = (constraints, devices) => {
|
|
|
614
612
|
/**
|
|
615
613
|
* Relax input constraints to enable looking up the device by `deviceId` as well
|
|
616
614
|
* as `label` as a fallback as a best effort to get a similar device when
|
|
617
|
-
* possible.
|
|
615
|
+
* possible. If the the device is found we will use the `exact` constraint otherwise
|
|
616
|
+
* we will use the `ideal`.
|
|
618
617
|
*
|
|
619
618
|
* @param input - The input constraints to be relaxed
|
|
620
619
|
* @param devices - The current device list
|
|
621
620
|
*/
|
|
622
|
-
export const relaxInputConstraint = (input, devices) => {
|
|
623
|
-
if (devices.
|
|
621
|
+
export const relaxInputConstraint = (kind, input, devices) => {
|
|
622
|
+
if (devices.size(kind) === 0 || !input || typeof input === 'boolean') {
|
|
624
623
|
return input;
|
|
625
624
|
}
|
|
626
|
-
|
|
625
|
+
let found = false;
|
|
626
|
+
if (isMediaDeviceInfo(input)) {
|
|
627
|
+
found = Boolean(devices.get(input).length);
|
|
628
|
+
}
|
|
629
|
+
else if (isMediaDeviceInfoArray(input)) {
|
|
630
|
+
found = Boolean(input.some(device => devices.get(device).length > 0));
|
|
631
|
+
}
|
|
632
|
+
else if (isInputConstraintSet(input)) {
|
|
633
|
+
found = Boolean(findDeviceFromDeviceConstraints(input.device, devices));
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
const [ConstrainDeviceIds] = extractConstraints('deviceId')(input);
|
|
637
|
+
if (ConstrainDeviceIds) {
|
|
638
|
+
found = Boolean(findDeviceFromDeviceId(ConstrainDeviceIds, devices));
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
const [device, param] = extractConstrainDevice(input, found ? 'exact' : 'ideal');
|
|
627
642
|
const relaxedDevice = relaxDevice(device, devices);
|
|
628
643
|
const otherConstraints = isInputConstraintSet(input) ? input : {};
|
|
629
644
|
return relaxedDevice
|
package/dist/devices.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { MediaDeviceKinds } from './types';
|
|
1
|
+
import type { FacingMode, IndexedDevices, InputConstraintSet, InputDeviceConstraint, InputDevicePermission, MediaDeviceInfoLike, MediaDeviceRequest, MediaDeviceTrackInfo, MediaInput } from './types';
|
|
3
2
|
/**
|
|
4
3
|
* DeviceChange Event
|
|
5
4
|
*
|
|
@@ -31,6 +30,7 @@ interface ToKeyOptions {
|
|
|
31
30
|
* @beta
|
|
32
31
|
*/
|
|
33
32
|
export declare const toKey: ({ id, kind, label }: ToKeyOptions) => string;
|
|
33
|
+
export declare const toMediaDeviceInputKind: (track: Pick<MediaStreamTrack, 'kind'>) => 'audioinput' | 'videoinput';
|
|
34
34
|
/**
|
|
35
35
|
* Future proofing: in case we want to alter the values or type returned
|
|
36
36
|
* by {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices | enumerateDevices}
|
|
@@ -66,27 +66,7 @@ export declare const createTrackDevicesChanges: (prevDevices?: MediaDeviceInfoLi
|
|
|
66
66
|
* @beta
|
|
67
67
|
*/
|
|
68
68
|
export declare const deviceChanged: (fn: (event: DeviceChangedChanges) => void) => () => void;
|
|
69
|
-
export declare const extractDeviceInfo: (track: MediaStreamTrack) =>
|
|
70
|
-
kind: MediaDeviceKinds;
|
|
71
|
-
deviceId: string;
|
|
72
|
-
groupId: string;
|
|
73
|
-
label: string;
|
|
74
|
-
settings: {
|
|
75
|
-
aspectRatio?: number | undefined;
|
|
76
|
-
autoGainControl?: boolean | undefined;
|
|
77
|
-
channelCount?: number | undefined;
|
|
78
|
-
displaySurface?: string | undefined;
|
|
79
|
-
echoCancellation?: boolean | undefined;
|
|
80
|
-
facingMode?: string | undefined;
|
|
81
|
-
frameRate?: number | undefined;
|
|
82
|
-
height?: number | undefined;
|
|
83
|
-
noiseSuppression?: boolean | undefined;
|
|
84
|
-
sampleRate?: number | undefined;
|
|
85
|
-
sampleSize?: number | undefined;
|
|
86
|
-
width?: number | undefined;
|
|
87
|
-
latency?: number | undefined;
|
|
88
|
-
};
|
|
89
|
-
};
|
|
69
|
+
export declare const extractDeviceInfo: (track: MediaStreamTrack) => MediaDeviceTrackInfo;
|
|
90
70
|
/**
|
|
91
71
|
* Convert `MediaStreamTrack` to `MediaDeviceInfoLike`
|
|
92
72
|
*
|
|
@@ -128,7 +108,7 @@ export declare const toMediaDeviceInfo: (info: MediaDeviceInfoLike) => {
|
|
|
128
108
|
*
|
|
129
109
|
* @beta
|
|
130
110
|
*/
|
|
131
|
-
export declare const findMediaInputFromMediaStreamTrack: (devices:
|
|
111
|
+
export declare const findMediaInputFromMediaStreamTrack: (devices: IndexedDevices) => (track?: MediaStreamTrack | undefined) => MediaDeviceInfoLike | undefined;
|
|
132
112
|
/**
|
|
133
113
|
* Find media input from media stream
|
|
134
114
|
*
|
|
@@ -140,31 +120,48 @@ export declare const findMediaInputFromMediaStreamTrack: (devices: MediaDeviceIn
|
|
|
140
120
|
*
|
|
141
121
|
* @beta
|
|
142
122
|
*/
|
|
143
|
-
export declare const findMediaInputFromStream: (devices:
|
|
123
|
+
export declare const findMediaInputFromStream: (devices: IndexedDevices) => (stream: MediaStream | undefined) => MediaInput;
|
|
144
124
|
export declare const compareDeviceConstraintAndTrackDevice: (device: MediaDeviceInfoLike | undefined) => (constraint: InputConstraintSet['device']) => boolean;
|
|
145
|
-
export declare const isRequestedResolution: (request: InputDeviceConstraint | undefined, response:
|
|
146
|
-
export declare const isRequestedInputDevice: (request: InputDeviceConstraint | undefined, response: MediaDeviceInfoLike | undefined) => boolean;
|
|
125
|
+
export declare const isRequestedResolution: (request: InputDeviceConstraint | undefined, response: MediaTrackSettings | undefined) => boolean;
|
|
126
|
+
export declare const isRequestedInputDevice: (request: InputDeviceConstraint | undefined, response: MediaDeviceInfoLike | MediaDeviceTrackInfo | undefined) => boolean;
|
|
147
127
|
/**
|
|
148
128
|
* Compare request and the input to see if the request has been fulfilled
|
|
149
129
|
*/
|
|
150
130
|
export declare const isRequestedInputTrack: (request: InputDeviceConstraint | undefined, current: MediaStreamTrack | MediaDeviceInfoLike | undefined) => boolean;
|
|
151
|
-
export declare const hasRequestingDevice: (request: InputDeviceConstraint | undefined, kind: MediaDeviceKind, currentDevices: MediaDeviceInfoLike[]) => boolean;
|
|
152
131
|
/**
|
|
153
|
-
*
|
|
132
|
+
* Check if there is a requesting device with respecting to the current devices
|
|
154
133
|
*
|
|
155
134
|
* @param request - Requesting input device constraint
|
|
156
|
-
* @param
|
|
157
|
-
* @param currentDevices - The current
|
|
158
|
-
|
|
135
|
+
* @param kind - The kind of the device
|
|
136
|
+
* @param currentDevices - The current indexed devices
|
|
137
|
+
*/
|
|
138
|
+
export declare const hasRequestingDevice: (request: InputDeviceConstraint | undefined, kind: MediaDeviceKind, currentDevices: IndexedDevices) => boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Check if the track is muted or 'ended'
|
|
141
|
+
* @param track - The media stream tracks
|
|
142
|
+
*/
|
|
143
|
+
export declare const isTrackMutedOrEnded: (track: MediaStreamTrack) => boolean;
|
|
144
|
+
interface DeviceRequestParams {
|
|
145
|
+
kind: 'audioinput' | 'videoinput';
|
|
146
|
+
permission: PermissionState;
|
|
147
|
+
request: InputDeviceConstraint | undefined;
|
|
148
|
+
tracks: MediaStreamTrack[];
|
|
149
|
+
currentDevices: IndexedDevices;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Decide if we should send a new gUM request
|
|
153
|
+
*
|
|
154
|
+
* @param deviceRequestParams - @see DeviceRequestParams
|
|
159
155
|
*
|
|
160
156
|
* @returns `true` means the request should be conducted, otherwise `false`.
|
|
161
157
|
*/
|
|
162
|
-
export declare const shouldRequestDevice: (
|
|
163
|
-
export declare const resolveInputDevice: (tracksOrDevice: MediaDeviceInfoLike | MediaStreamTrack[] | undefined, findInput: ReturnType<typeof findMediaInputFromMediaStreamTrack>) =>
|
|
158
|
+
export declare const shouldRequestDevice: ({ kind, permission, request, tracks, currentDevices, }: DeviceRequestParams) => boolean;
|
|
159
|
+
export declare const resolveInputDevice: (tracksOrDevice: MediaDeviceInfoLike | MediaStreamTrack[] | undefined, findInput: ReturnType<typeof findMediaInputFromMediaStreamTrack>) => MediaDeviceInfoLike | MediaStreamTrack | undefined;
|
|
160
|
+
export declare const isStreamingRequestingDevice: (request: InputDeviceConstraint | undefined, trackOrDevice: MediaDeviceInfoLike | MediaStreamTrack[] | undefined, devices: IndexedDevices) => boolean;
|
|
164
161
|
export declare const isStreamingRequestedDevicesBase: (request: MediaDeviceRequest, tracksOrDevices: {
|
|
165
162
|
audio?: MediaDeviceInfoLike | MediaStreamTrack[];
|
|
166
163
|
video?: MediaDeviceInfoLike | MediaStreamTrack[];
|
|
167
|
-
}, devices:
|
|
164
|
+
}, devices: IndexedDevices) => {
|
|
168
165
|
audio: boolean;
|
|
169
166
|
video: boolean;
|
|
170
167
|
};
|
|
@@ -178,7 +175,7 @@ export declare const isStreamingRequestedDevicesBase: (request: MediaDeviceReque
|
|
|
178
175
|
* audio - The stream is using the same device as requested if `true`
|
|
179
176
|
* video - The stream is using the same device as requested if `true`
|
|
180
177
|
*/
|
|
181
|
-
export declare const isStreamingRequestedDevices: (request: MediaDeviceRequest, stream: MediaStream | undefined, devices:
|
|
178
|
+
export declare const isStreamingRequestedDevices: (request: MediaDeviceRequest, stream: MediaStream | undefined, devices: IndexedDevices) => {
|
|
182
179
|
audio: boolean;
|
|
183
180
|
video: boolean;
|
|
184
181
|
};
|
|
@@ -202,19 +199,24 @@ export declare const findPermissionGrantedDevices: (devices: MediaDeviceInfoLike
|
|
|
202
199
|
* stream and there is no device with label, 'prompt' will be returned
|
|
203
200
|
* otherwise, 'denied'.
|
|
204
201
|
*
|
|
205
|
-
* @param
|
|
206
|
-
* @param
|
|
202
|
+
* @param anyGrantedDevice - Any permission granted device
|
|
203
|
+
* @param anyLiveTrack - Has ever got a live track
|
|
207
204
|
* guess the state more accurately
|
|
208
205
|
*/
|
|
209
|
-
export declare const toPermissionState: (
|
|
206
|
+
export declare const toPermissionState: (anyGrantedDevice: boolean, anyLiveTrack?: boolean) => PermissionState;
|
|
207
|
+
export declare const queryPermissionFallback: (name: 'camera' | 'microphone', everActiveTrack?: boolean, getDevices?: () => Promise<MediaDeviceInfo[]>) => Promise<PermissionState>;
|
|
208
|
+
export declare const queryPermission: (name: 'camera' | 'microphone', everActiveTrack?: boolean, getDevices?: () => Promise<MediaDeviceInfo[]>) => Promise<PermissionState>;
|
|
210
209
|
/**
|
|
211
210
|
* A wrapper for `navigator.permissions.query` with fallback to use
|
|
212
211
|
* `navigator.mediaDevices.enumerateDevices` to guess the `PermissionState`
|
|
213
212
|
*
|
|
214
|
-
* @param
|
|
213
|
+
* @param anyActiveAudioTrack - Has ever got an active audio to help the fallback to
|
|
214
|
+
* guess the state more accurately
|
|
215
|
+
* @param anyActiveVideoTrack - Has ever got an active video to help the fallback to
|
|
215
216
|
* guess the state more accurately
|
|
217
|
+
* @param getDevices - A function to get the list of devices, e.g. navigator.mediaDevices.enumerateDevices
|
|
216
218
|
*/
|
|
217
|
-
export declare const getInputDevicePermissionState: (
|
|
219
|
+
export declare const getInputDevicePermissionState: (anyActiveAudioTrack?: boolean, anyActiveVideoTrack?: boolean, getDevices?: () => Promise<MediaDeviceInfo[]>) => Promise<InputDevicePermission>;
|
|
218
220
|
/**
|
|
219
221
|
* Find current audio output id to be used to set as sinkId
|
|
220
222
|
*
|
package/dist/devices.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
|
+
import { assert } from '@pexip/utils';
|
|
1
2
|
import { MediaDeviceKinds } from './types';
|
|
2
|
-
import { extractConstrainNumber, extractConstrainString,
|
|
3
|
-
import {
|
|
4
|
-
import { findDevice,
|
|
5
|
-
|
|
3
|
+
import { extractConstrainNumber, extractConstrainString, extractConstraintsWithKeys, normalizeDeviceConstraint, satisfyConstrainNumber, toArray, } from './constraints';
|
|
4
|
+
import { isConstrainDOMStringParameters, isConstraintDOMString, isConstraintSetDevice, isInputConstraintSet, isMediaDeviceInfo, isMediaDeviceTrackInfo, isMediaStreamTrack, } from './typeGuards';
|
|
5
|
+
import { compareDevices, findDevice, isAudioInput, isAudioOutput, isDeviceGranted, isSameDeviceKind, isVideoInput, not, } from './utils';
|
|
6
|
+
import { DEVICE_ID_SEPARATOR } from './constants';
|
|
6
7
|
/**
|
|
7
8
|
* Convert provided info to key for Map
|
|
8
9
|
*
|
|
9
10
|
* @beta
|
|
10
11
|
*/
|
|
11
|
-
export const toKey = ({ id, kind, label }) => [kind, id, label].join(
|
|
12
|
+
export const toKey = ({ id, kind, label }) => [kind, id, label].join(DEVICE_ID_SEPARATOR);
|
|
13
|
+
export const toMediaDeviceInputKind = (track) => {
|
|
14
|
+
switch (track.kind) {
|
|
15
|
+
case 'audio':
|
|
16
|
+
return 'audioinput';
|
|
17
|
+
case 'video':
|
|
18
|
+
return 'videoinput';
|
|
19
|
+
default:
|
|
20
|
+
throw new Error('Invalid track kind');
|
|
21
|
+
}
|
|
22
|
+
};
|
|
12
23
|
/**
|
|
13
24
|
* Future proofing: in case we want to alter the values or type returned
|
|
14
25
|
* by {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices | enumerateDevices}
|
|
@@ -108,6 +119,7 @@ export const extractDeviceInfo = (track) => {
|
|
|
108
119
|
? MediaDeviceKinds.AUDIOINPUT
|
|
109
120
|
: MediaDeviceKinds.VIDEOINPUT;
|
|
110
121
|
return {
|
|
122
|
+
id: track.id,
|
|
111
123
|
kind,
|
|
112
124
|
deviceId: deviceId ?? '',
|
|
113
125
|
groupId: groupId ?? '',
|
|
@@ -155,13 +167,6 @@ export const compareMediaDeviceToMediaTrack = (track) => (device) => {
|
|
|
155
167
|
}
|
|
156
168
|
return sameKind && sameLabel;
|
|
157
169
|
};
|
|
158
|
-
const toMediaDeviceInfoLikeJSON = (info) => ({
|
|
159
|
-
deviceId: info.deviceId,
|
|
160
|
-
groupId: info.groupId,
|
|
161
|
-
kind: info.kind,
|
|
162
|
-
label: info.label,
|
|
163
|
-
settings: info.settings,
|
|
164
|
-
});
|
|
165
170
|
export const toMediaDeviceInfo = (info) => ({
|
|
166
171
|
deviceId: info.deviceId,
|
|
167
172
|
groupId: info.groupId,
|
|
@@ -180,25 +185,20 @@ export const toMediaDeviceInfo = (info) => ({
|
|
|
180
185
|
* @beta
|
|
181
186
|
*/
|
|
182
187
|
export const findMediaInputFromMediaStreamTrack = (devices) => (track) => {
|
|
183
|
-
if (!
|
|
188
|
+
if (!track) {
|
|
184
189
|
return undefined;
|
|
185
190
|
}
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const onlyDevice = devicesWithSameKind[0];
|
|
190
|
-
if (devicesWithSameKind.length === 1 && onlyDevice) {
|
|
191
|
-
onlyDevice.settings = settings;
|
|
192
|
-
onlyDevice.toJSON = () => toMediaDeviceInfoLikeJSON(onlyDevice);
|
|
193
|
-
return onlyDevice;
|
|
194
|
-
}
|
|
195
|
-
const device = devicesWithSameKind.find(compareMediaDeviceToMediaTrack(track));
|
|
196
|
-
if (device) {
|
|
197
|
-
device.settings = settings;
|
|
198
|
-
device.toJSON = () => toMediaDeviceInfoLikeJSON(device);
|
|
199
|
-
return device;
|
|
191
|
+
const kind = toMediaDeviceInputKind(track);
|
|
192
|
+
if (!devices.size(kind)) {
|
|
193
|
+
return undefined;
|
|
200
194
|
}
|
|
201
|
-
|
|
195
|
+
const settings = track.getSettings();
|
|
196
|
+
const devicesFound = devices.get({
|
|
197
|
+
kind,
|
|
198
|
+
label: track.label,
|
|
199
|
+
deviceId: settings.deviceId,
|
|
200
|
+
});
|
|
201
|
+
return devicesFound[0];
|
|
202
202
|
};
|
|
203
203
|
/**
|
|
204
204
|
* Find media input from media stream
|
|
@@ -212,7 +212,7 @@ export const findMediaInputFromMediaStreamTrack = (devices) => (track) => {
|
|
|
212
212
|
* @beta
|
|
213
213
|
*/
|
|
214
214
|
export const findMediaInputFromStream = (devices) => (stream) => {
|
|
215
|
-
if (!stream
|
|
215
|
+
if (!stream) {
|
|
216
216
|
return {};
|
|
217
217
|
}
|
|
218
218
|
const findMediaInput = findMediaInputFromMediaStreamTrack(devices);
|
|
@@ -228,7 +228,9 @@ export const compareDeviceConstraintAndTrackDevice = (device) => (constraint) =>
|
|
|
228
228
|
const normalized = normalizeDeviceConstraint(constraint);
|
|
229
229
|
if (normalized) {
|
|
230
230
|
const requestingDevices = Object.values(normalized).flatMap(t => (Array.isArray(t) ? t : [t]));
|
|
231
|
-
const compare = compareDevices(device,
|
|
231
|
+
const compare = compareDevices(device,
|
|
232
|
+
// Always use `deviceId` for exact constraints
|
|
233
|
+
!!normalized.exact || !device.label ? 'deviceId' : 'label');
|
|
232
234
|
return requestingDevices.some(compare);
|
|
233
235
|
}
|
|
234
236
|
return false;
|
|
@@ -240,7 +242,7 @@ export const isRequestedResolution = (request, response) => {
|
|
|
240
242
|
if (isInputConstraintSet(request)) {
|
|
241
243
|
const [width] = extractConstrainNumber('width')(request);
|
|
242
244
|
const [height] = extractConstrainNumber('height')(request);
|
|
243
|
-
const { width: responseWidth = 0, height: responseHeight = 0 } = response
|
|
245
|
+
const { width: responseWidth = 0, height: responseHeight = 0 } = response ?? {};
|
|
244
246
|
const portrait = responseHeight >= responseWidth;
|
|
245
247
|
const resultWidth = portrait ? responseHeight : responseWidth;
|
|
246
248
|
const resultHeight = portrait ? responseWidth : responseHeight;
|
|
@@ -275,10 +277,12 @@ export const isRequestedInputDevice = (request, response) => {
|
|
|
275
277
|
.some(id => id === deviceId);
|
|
276
278
|
}
|
|
277
279
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
if (isMediaDeviceTrackInfo(response)) {
|
|
281
|
+
const facingMode = response.settings.facingMode;
|
|
282
|
+
if (request.facingMode && facingMode) {
|
|
283
|
+
const [facingModes] = extractConstrainString('facingMode')(request);
|
|
284
|
+
return facingModes?.some(fm => fm === facingMode) ?? true;
|
|
285
|
+
}
|
|
282
286
|
}
|
|
283
287
|
return true;
|
|
284
288
|
}
|
|
@@ -304,6 +308,13 @@ export const isRequestedInputTrack = (request, current) => {
|
|
|
304
308
|
return true;
|
|
305
309
|
};
|
|
306
310
|
const extractDevice = extractConstraintsWithKeys(['device', 'deviceId']);
|
|
311
|
+
/**
|
|
312
|
+
* Check if there is a requesting device with respecting to the current devices
|
|
313
|
+
*
|
|
314
|
+
* @param request - Requesting input device constraint
|
|
315
|
+
* @param kind - The kind of the device
|
|
316
|
+
* @param currentDevices - The current indexed devices
|
|
317
|
+
*/
|
|
307
318
|
export const hasRequestingDevice = (request, kind, currentDevices) => {
|
|
308
319
|
if (!request) {
|
|
309
320
|
return false;
|
|
@@ -312,44 +323,52 @@ export const hasRequestingDevice = (request, kind, currentDevices) => {
|
|
|
312
323
|
// No device specified
|
|
313
324
|
if ((!devices || devices.length === 0) &&
|
|
314
325
|
(!deviceIds || deviceIds.length === 0)) {
|
|
315
|
-
return currentDevices.
|
|
326
|
+
return Boolean(currentDevices.size(kind));
|
|
316
327
|
}
|
|
317
328
|
// Has device specified
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
deviceIds?.some(deviceId => find({ kind, deviceId, label: '', groupId: '' })) ??
|
|
329
|
+
return (devices?.some(device => currentDevices.get(device).length) ??
|
|
330
|
+
deviceIds?.some(deviceId => currentDevices.get({ kind, deviceId }).length) ??
|
|
321
331
|
false);
|
|
322
332
|
};
|
|
323
333
|
/**
|
|
324
|
-
*
|
|
334
|
+
* Check if the track is muted or 'ended'
|
|
335
|
+
* @param track - The media stream tracks
|
|
336
|
+
*/
|
|
337
|
+
export const isTrackMutedOrEnded = (track) => track.readyState === 'ended' || track.muted;
|
|
338
|
+
/**
|
|
339
|
+
* Decide if we should send a new gUM request
|
|
325
340
|
*
|
|
326
|
-
* @param
|
|
327
|
-
* @param tracks - The current media stream tracks
|
|
328
|
-
* @param currentDevices - The current list of device of the same kind of the
|
|
329
|
-
* request
|
|
341
|
+
* @param deviceRequestParams - @see DeviceRequestParams
|
|
330
342
|
*
|
|
331
343
|
* @returns `true` means the request should be conducted, otherwise `false`.
|
|
332
344
|
*/
|
|
333
|
-
export const shouldRequestDevice = (request, tracks, currentDevices) => {
|
|
334
|
-
if (!request
|
|
335
|
-
return false;
|
|
336
|
-
}
|
|
337
|
-
const { kind } = currentDevices[0] ?? {};
|
|
338
|
-
if (!kind || currentDevices.some(device => device.kind !== kind)) {
|
|
339
|
-
throw new Error('Expect a single kind of device');
|
|
340
|
-
}
|
|
341
|
-
const [track] = tracks;
|
|
342
|
-
if (tracks.length === 0 ||
|
|
343
|
-
tracks.some(track => track.readyState === 'ended' || track.muted)) {
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
if (isRequestedInputTrack(request, track)) {
|
|
345
|
+
export const shouldRequestDevice = ({ kind, permission, request, tracks, currentDevices, }) => {
|
|
346
|
+
if (!request) {
|
|
347
347
|
return false;
|
|
348
348
|
}
|
|
349
|
-
|
|
350
|
-
|
|
349
|
+
switch (permission) {
|
|
350
|
+
case 'prompt':
|
|
351
|
+
return true;
|
|
352
|
+
case 'granted': {
|
|
353
|
+
if (currentDevices.size(kind) === 0) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
if (tracks.length === 0 || tracks.some(isTrackMutedOrEnded)) {
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
const [track] = tracks;
|
|
360
|
+
assert(track);
|
|
361
|
+
if (isRequestedInputTrack(request, track)) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
if (hasRequestingDevice(request, kind, currentDevices)) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
default:
|
|
370
|
+
return false;
|
|
351
371
|
}
|
|
352
|
-
return false;
|
|
353
372
|
};
|
|
354
373
|
export const resolveInputDevice = (tracksOrDevice, findInput) => {
|
|
355
374
|
if (isMediaDeviceInfo(tracksOrDevice)) {
|
|
@@ -363,14 +382,20 @@ export const resolveInputDevice = (tracksOrDevice, findInput) => {
|
|
|
363
382
|
}
|
|
364
383
|
return undefined;
|
|
365
384
|
};
|
|
366
|
-
export const
|
|
367
|
-
|
|
368
|
-
|
|
385
|
+
export const isStreamingRequestingDevice = (request, trackOrDevice, devices) => {
|
|
386
|
+
if (request === undefined) {
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
369
389
|
const findInput = findMediaInputFromMediaStreamTrack(devices);
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
390
|
+
const input = resolveInputDevice(trackOrDevice, findInput);
|
|
391
|
+
if (!input) {
|
|
392
|
+
return Boolean(request) === Boolean(input);
|
|
393
|
+
}
|
|
394
|
+
return isRequestedInputTrack(request, input);
|
|
395
|
+
};
|
|
396
|
+
export const isStreamingRequestedDevicesBase = (request, tracksOrDevices, devices) => {
|
|
397
|
+
const audio = isStreamingRequestingDevice(request.audio, tracksOrDevices.audio, devices);
|
|
398
|
+
const video = isStreamingRequestingDevice(request.video, tracksOrDevices.video, devices);
|
|
374
399
|
return { audio, video };
|
|
375
400
|
};
|
|
376
401
|
/**
|
|
@@ -407,41 +432,51 @@ export const findPermissionGrantedDevices = (devices) => devices.filter(isDevice
|
|
|
407
432
|
* stream and there is no device with label, 'prompt' will be returned
|
|
408
433
|
* otherwise, 'denied'.
|
|
409
434
|
*
|
|
410
|
-
* @param
|
|
411
|
-
* @param
|
|
435
|
+
* @param anyGrantedDevice - Any permission granted device
|
|
436
|
+
* @param anyLiveTrack - Has ever got a live track
|
|
412
437
|
* guess the state more accurately
|
|
413
438
|
*/
|
|
414
|
-
export const toPermissionState = (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
return granted ? 'granted' : 'denied';
|
|
439
|
+
export const toPermissionState = (anyGrantedDevice, anyLiveTrack = false) => {
|
|
440
|
+
if (anyLiveTrack) {
|
|
441
|
+
return anyGrantedDevice ? 'granted' : 'denied';
|
|
418
442
|
}
|
|
419
|
-
return
|
|
443
|
+
return anyGrantedDevice ? 'granted' : 'prompt';
|
|
420
444
|
};
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
*/
|
|
428
|
-
export const getInputDevicePermissionState = async (anyActiveStream = false) => {
|
|
445
|
+
export const queryPermissionFallback = async (name, everActiveTrack = false, getDevices = () => navigator.mediaDevices.enumerateDevices()) => {
|
|
446
|
+
const devices = await getDevices();
|
|
447
|
+
const kind = name === 'camera' ? 'videoinput' : 'audioinput';
|
|
448
|
+
return toPermissionState(devices.some(device => device.kind === kind && isDeviceGranted(device)), everActiveTrack);
|
|
449
|
+
};
|
|
450
|
+
export const queryPermission = async (name, everActiveTrack = false, getDevices = () => navigator.mediaDevices.enumerateDevices()) => {
|
|
429
451
|
try {
|
|
430
452
|
// @ts-expect-error -- Typings/dom.d.ts change blocks upgrade to typescript 4.4
|
|
431
|
-
const
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
453
|
+
const { state } = await navigator.permissions.query({ name });
|
|
454
|
+
// Firefox v133 added permission API but always returns granted but
|
|
455
|
+
// based on the device label, it is actually not granted, so we need to
|
|
456
|
+
// fallback to use `enumerateDevices` to guess the state
|
|
457
|
+
if (state === 'granted') {
|
|
458
|
+
return await queryPermissionFallback(name, everActiveTrack, getDevices);
|
|
459
|
+
}
|
|
460
|
+
return state;
|
|
435
461
|
}
|
|
436
462
|
catch {
|
|
437
|
-
|
|
438
|
-
return ['videoinput', 'audioinput'].reduce((permission, kind) => {
|
|
439
|
-
const state = toPermissionState(devices.filter(device => device.kind === kind), anyActiveStream);
|
|
440
|
-
permission[kind === 'audioinput' ? 'audio' : 'video'] = state;
|
|
441
|
-
return permission;
|
|
442
|
-
}, { video: 'prompt', audio: 'prompt' });
|
|
463
|
+
return await queryPermissionFallback(name, everActiveTrack, getDevices);
|
|
443
464
|
}
|
|
444
465
|
};
|
|
466
|
+
/**
|
|
467
|
+
* A wrapper for `navigator.permissions.query` with fallback to use
|
|
468
|
+
* `navigator.mediaDevices.enumerateDevices` to guess the `PermissionState`
|
|
469
|
+
*
|
|
470
|
+
* @param anyActiveAudioTrack - Has ever got an active audio to help the fallback to
|
|
471
|
+
* guess the state more accurately
|
|
472
|
+
* @param anyActiveVideoTrack - Has ever got an active video to help the fallback to
|
|
473
|
+
* guess the state more accurately
|
|
474
|
+
* @param getDevices - A function to get the list of devices, e.g. navigator.mediaDevices.enumerateDevices
|
|
475
|
+
*/
|
|
476
|
+
export const getInputDevicePermissionState = async (anyActiveAudioTrack = false, anyActiveVideoTrack = false, getDevices = () => navigator.mediaDevices.enumerateDevices()) => ({
|
|
477
|
+
audio: await queryPermission('microphone', anyActiveAudioTrack, getDevices),
|
|
478
|
+
video: await queryPermission('camera', anyActiveVideoTrack, getDevices),
|
|
479
|
+
});
|
|
445
480
|
/**
|
|
446
481
|
* Find current audio output id to be used to set as sinkId
|
|
447
482
|
*
|
package/dist/getMediaStream.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { MediaDeviceFailure } from './types';
|
|
2
1
|
import { getDevices } from './devices';
|
|
3
|
-
import { getMediaConstraints
|
|
2
|
+
import { getMediaConstraints } from './constraints';
|
|
4
3
|
import { normalizeGetUserMediaError } from './errors';
|
|
5
4
|
import { logger } from './logger';
|
|
6
5
|
/**
|
|
@@ -11,25 +10,16 @@ import { logger } from './logger';
|
|
|
11
10
|
* @beta
|
|
12
11
|
*/
|
|
13
12
|
export const getMediaStream = async (request) => {
|
|
14
|
-
if (!request.audio && !request.video) {
|
|
15
|
-
throw new Error(MediaDeviceFailure.MissingConstraintsError);
|
|
16
|
-
}
|
|
17
|
-
const devices = await getDevices();
|
|
18
|
-
const audio = relaxInputConstraint(request.audio, devices);
|
|
19
|
-
const video = relaxInputConstraint(request.video, devices);
|
|
20
13
|
const constraints = getMediaConstraints({
|
|
21
|
-
audio,
|
|
22
|
-
video,
|
|
14
|
+
audio: request.audio,
|
|
15
|
+
video: request.video,
|
|
23
16
|
defaultConstraints: request.getDefaultConstraints(),
|
|
24
17
|
});
|
|
25
|
-
logger.debug({ request, constraints
|
|
18
|
+
logger.debug({ request, constraints }, 'Constraints used for getting media');
|
|
26
19
|
try {
|
|
27
20
|
// Track device changes before and after the getUserMedia call to keep
|
|
28
21
|
// track of authorized/unauthorized devices
|
|
29
|
-
navigator.mediaDevices.
|
|
30
|
-
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
31
|
-
navigator.mediaDevices.dispatchEvent(new Event('devicechange'));
|
|
32
|
-
return stream;
|
|
22
|
+
return await navigator.mediaDevices.getUserMedia(constraints);
|
|
33
23
|
}
|
|
34
24
|
catch (error) {
|
|
35
25
|
if (error instanceof Error) {
|
package/dist/index.d.ts
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
|
-
import { DeviceChangedChanges, areMultipleFacingModeSupported, areTracksEnabled, createTrackDevicesChanges, deviceChanged, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDevices, getInputDevicePermissionState, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, muteStreamTrack, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike } from './devices';
|
|
11
|
-
import { compareDevices,
|
|
12
|
-
import {
|
|
10
|
+
import { DeviceChangedChanges, areMultipleFacingModeSupported, areTracksEnabled, createTrackDevicesChanges, deviceChanged, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDevices, getInputDevicePermissionState, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, isStreamingRequestingDevice, isTrackMutedOrEnded, muteStreamTrack, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike, toMediaDeviceInputKind } from './devices';
|
|
11
|
+
import { compareDevices, createIndexedDevices, findDevice, getDeviceId, isAudioInput, isAudioOutput, isDeviceGranted, isVideoInput, reverseDeviceId } from './utils';
|
|
12
|
+
import { applyConstraints, extractConstrainDevice, extractConstraintsWithKeys, findDeviceFromConstraints, getFacingModeFromConstraintString, getValueFromConstrainNumber, isExactDeviceConstraint, mergeConstraints, relaxInputConstraint, resolveMediaDeviceConstraints } from './constraints';
|
|
13
13
|
import { MediaEvent, Events, MediaEventType } from './eventEmitter';
|
|
14
14
|
import { createStreamTrackEventSubscriptions } from './streams';
|
|
15
|
-
import {
|
|
16
|
-
import { isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack
|
|
15
|
+
import { DisplayMediaOptions, FacingMode, IndexedDevices, InputConstraintSet, InputDeviceConstraint, MediaDeviceFailure, MediaDeviceInfoLike, MediaDeviceKinds, MediaDeviceRequest, MediaInput, MediaStreamTrackLike, StreamTrackEventHandlers, Unsubscribe } from './types';
|
|
16
|
+
import { isFacingMode, isIndexedDevices, isInputConstraintSet, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack } from './typeGuards';
|
|
17
17
|
export { setLogger } from './logger';
|
|
18
18
|
declare const
|
|
19
19
|
/**
|
|
@@ -33,7 +33,7 @@ setDefaultConstraints: (newConstraints: MediaStreamConstraints) => void,
|
|
|
33
33
|
*/
|
|
34
34
|
subscribe: (listener: (event: MediaEvent) => void) => () => void;
|
|
35
35
|
export { getUserMedia, setDefaultConstraints, subscribe };
|
|
36
|
-
export { DeviceChangedChanges, DisplayMediaOptions, Events, FacingMode, InputConstraintSet, MediaDeviceFailure, MediaDeviceInfoLike, MediaDeviceKinds, MediaDeviceRequest, MediaEvent, MediaEventType, MediaInput, MediaStreamTrackLike, StreamTrackEventHandlers, Unsubscribe, applyConstraints, areMultipleFacingModeSupported, areTracksEnabled, compareDevices, createStreamTrackEventSubscriptions, createTrackDevicesChanges, deviceChanged, extractConstraintsWithKeys, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDevice, findDeviceFromConstraints, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDevices, getFacingModeFromConstraintString, getInputDevicePermissionState, getValueFromConstrainNumber, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isAudioInput, isAudioOutput, isDeviceGranted, isExactDeviceConstraint, isFacingMode, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, isVideoInput, mergeConstraints, muteStreamTrack, relaxInputConstraint, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike,
|
|
36
|
+
export { DeviceChangedChanges, DisplayMediaOptions, Events, FacingMode, IndexedDevices, InputConstraintSet, InputDeviceConstraint, MediaDeviceFailure, MediaDeviceInfoLike, MediaDeviceKinds, MediaDeviceRequest, MediaEvent, MediaEventType, MediaInput, MediaStreamTrackLike, StreamTrackEventHandlers, Unsubscribe, applyConstraints, areMultipleFacingModeSupported, areTracksEnabled, compareDevices, createIndexedDevices, createStreamTrackEventSubscriptions, createTrackDevicesChanges, deviceChanged, extractConstrainDevice, extractConstraintsWithKeys, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDevice, findDeviceFromConstraints, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDeviceId, getDevices, getFacingModeFromConstraintString, getInputDevicePermissionState, getValueFromConstrainNumber, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isAudioInput, isAudioOutput, isDeviceGranted, isExactDeviceConstraint, isFacingMode, isIndexedDevices, isInputConstraintSet, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, isStreamingRequestingDevice, isTrackMutedOrEnded, isVideoInput, mergeConstraints, muteStreamTrack, relaxInputConstraint, resolveMediaDeviceConstraints, reverseDeviceId, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike, toMediaDeviceInputKind, };
|
|
37
37
|
export * from './constants';
|
|
38
38
|
export * from './displayMedia';
|
|
39
39
|
/**
|
package/dist/index.js
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
|
-
import { areMultipleFacingModeSupported, areTracksEnabled, createTrackDevicesChanges, deviceChanged, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDevices, getInputDevicePermissionState, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, muteStreamTrack, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike, } from './devices';
|
|
11
|
-
import { compareDevices,
|
|
12
|
-
import {
|
|
10
|
+
import { areMultipleFacingModeSupported, areTracksEnabled, createTrackDevicesChanges, deviceChanged, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDevices, getInputDevicePermissionState, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, isStreamingRequestingDevice, isTrackMutedOrEnded, muteStreamTrack, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike, toMediaDeviceInputKind, } from './devices';
|
|
11
|
+
import { compareDevices, createIndexedDevices, findDevice, getDeviceId, isAudioInput, isAudioOutput, isDeviceGranted, isVideoInput, reverseDeviceId, } from './utils';
|
|
12
|
+
import { applyConstraints, extractConstrainDevice, extractConstraintsWithKeys, findDeviceFromConstraints, getConstraintsHandlers, getFacingModeFromConstraintString, getValueFromConstrainNumber, isExactDeviceConstraint, mergeConstraints, relaxInputConstraint, resolveMediaDeviceConstraints, } from './constraints';
|
|
13
13
|
import { eventEmitter, MediaEventType } from './eventEmitter';
|
|
14
14
|
import { handleMediaStream, createStreamTrackEventSubscriptions, } from './streams';
|
|
15
15
|
import { MediaDeviceFailure, MediaDeviceKinds, } from './types';
|
|
16
|
-
import { isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack,
|
|
16
|
+
import { isFacingMode, isIndexedDevices, isInputConstraintSet, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack, } from './typeGuards';
|
|
17
17
|
import { createStreamTrackMap } from './streamTrackMap';
|
|
18
18
|
export { setLogger } from './logger';
|
|
19
19
|
const state = () => {
|
|
@@ -100,6 +100,6 @@ setDefaultConstraints,
|
|
|
100
100
|
*/
|
|
101
101
|
subscribe, } = state();
|
|
102
102
|
export { getUserMedia, setDefaultConstraints, subscribe };
|
|
103
|
-
export { MediaDeviceFailure, MediaDeviceKinds, MediaEventType, applyConstraints, areMultipleFacingModeSupported, areTracksEnabled, compareDevices, createStreamTrackEventSubscriptions, createTrackDevicesChanges, deviceChanged, extractConstraintsWithKeys, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDevice, findDeviceFromConstraints, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDevices, getFacingModeFromConstraintString, getInputDevicePermissionState, getValueFromConstrainNumber, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isAudioInput, isAudioOutput, isDeviceGranted, isExactDeviceConstraint, isFacingMode, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, isVideoInput, mergeConstraints, muteStreamTrack, relaxInputConstraint, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike,
|
|
103
|
+
export { MediaDeviceFailure, MediaDeviceKinds, MediaEventType, applyConstraints, areMultipleFacingModeSupported, areTracksEnabled, compareDevices, createIndexedDevices, createStreamTrackEventSubscriptions, createTrackDevicesChanges, deviceChanged, extractConstrainDevice, extractConstraintsWithKeys, findAudioInputDevices, findAudioOutputDevices, findCurrentAudioOutputId, findCurrentVideoInputDeviceIdFromStream, findDevice, findDeviceFromConstraints, findDeviceWithDeviceId, findDevicesByKind, findMediaInputFromMediaStreamTrack, findMediaInputFromStream, findPermissionGrantedDevices, findVideoInputDevices, getDeviceId, getDevices, getFacingModeFromConstraintString, getInputDevicePermissionState, getValueFromConstrainNumber, hasAnyGrantedInput, hasAnyInputs, hasAudioInputs, hasAudioOrVideoInputs, hasChangedInput, hasRequestingDevice, hasVideoInputs, interpretCurrentFacingMode, isAudioInput, isAudioOutput, isDeviceGranted, isExactDeviceConstraint, isFacingMode, isIndexedDevices, isInputConstraintSet, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaStreamTrack, isRequestedInputDevice, isRequestedInputTrack, isRequestedResolution, isStreamingRequestedDevices, isStreamingRequestedDevicesBase, isStreamingRequestingDevice, isTrackMutedOrEnded, isVideoInput, mergeConstraints, muteStreamTrack, relaxInputConstraint, resolveMediaDeviceConstraints, reverseDeviceId, shouldRequestDevice, stopMediaStream, toKey, toMediaDeviceInfo, toMediaDeviceInfoLike, toMediaDeviceInputKind, };
|
|
104
104
|
export * from './constants';
|
|
105
105
|
export * from './displayMedia';
|
package/dist/typeGuards.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConstraintDeviceParameters, DeviceConstraint, InputConstraintSet } from './types';
|
|
1
|
+
import type { ConstraintDeviceParameters, DeviceConstraint, IndexedDevices, InputConstraintSet, MediaDeviceTrackInfo } from './types';
|
|
2
2
|
export declare const isBoolean: (t: unknown) => t is boolean;
|
|
3
3
|
export declare const isUndefined: (t: unknown) => t is undefined;
|
|
4
4
|
/**
|
|
@@ -86,4 +86,6 @@ export declare const isInputConstraintSet: (t: unknown) => t is InputConstraintS
|
|
|
86
86
|
*/
|
|
87
87
|
export declare const isMediaStreamTrack: (m: unknown) => m is MediaStreamTrack;
|
|
88
88
|
export declare const isFacingMode: (s: unknown) => s is "user" | "environment" | "left" | "right";
|
|
89
|
+
export declare const isIndexedDevices: (t: unknown) => t is IndexedDevices;
|
|
90
|
+
export declare const isMediaDeviceTrackInfo: (t: unknown) => t is MediaDeviceTrackInfo;
|
|
89
91
|
export {};
|
package/dist/typeGuards.js
CHANGED
|
@@ -146,6 +146,12 @@ export const isConstraintDeviceParameters = isConstrainDOMParameters(t => isMedi
|
|
|
146
146
|
export const isConstraintSetDevice = (t) => isMediaDeviceInfo(t) ||
|
|
147
147
|
isMediaDeviceInfoArray(t) ||
|
|
148
148
|
isConstraintDeviceParameters(t);
|
|
149
|
+
const EXTENDED_CONSTRAIN_KEYS = [
|
|
150
|
+
...EXTENDED_CONSTRAIN_DOUBLE_KEYS,
|
|
151
|
+
...EXTENDED_CONSTRAIN_STRING_KEYS,
|
|
152
|
+
...EXTENDED_CONSTRAIN_U_LONG_KEYS,
|
|
153
|
+
...EXTENDED_CONSTRAIN_BOOLEAN_KEYS,
|
|
154
|
+
];
|
|
149
155
|
export const isExtendedConstraint = (t) => {
|
|
150
156
|
if (typeof t !== 'object' || t === null) {
|
|
151
157
|
return false;
|
|
@@ -154,12 +160,7 @@ export const isExtendedConstraint = (t) => {
|
|
|
154
160
|
const { device } = t;
|
|
155
161
|
return isConstraintSetDevice(device);
|
|
156
162
|
}
|
|
157
|
-
return
|
|
158
|
-
...EXTENDED_CONSTRAIN_DOUBLE_KEYS,
|
|
159
|
-
...EXTENDED_CONSTRAIN_STRING_KEYS,
|
|
160
|
-
...EXTENDED_CONSTRAIN_U_LONG_KEYS,
|
|
161
|
-
...EXTENDED_CONSTRAIN_BOOLEAN_KEYS,
|
|
162
|
-
].some(key => hasOwn(t, key));
|
|
163
|
+
return EXTENDED_CONSTRAIN_KEYS.some(key => hasOwn(t, key));
|
|
163
164
|
};
|
|
164
165
|
export const isInputConstraintSet = (t) => {
|
|
165
166
|
if (typeof t !== 'object' || t === null) {
|
|
@@ -184,3 +185,25 @@ export const isFacingMode = (s) => {
|
|
|
184
185
|
}
|
|
185
186
|
return false;
|
|
186
187
|
};
|
|
188
|
+
export const isIndexedDevices = (t) => {
|
|
189
|
+
if (typeof t === 'object' && t !== null) {
|
|
190
|
+
if ('get' in t &&
|
|
191
|
+
'size' in t &&
|
|
192
|
+
typeof t.get === 'function' &&
|
|
193
|
+
typeof t.size === 'function') {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
};
|
|
199
|
+
export const isMediaDeviceTrackInfo = (t) => {
|
|
200
|
+
if (typeof t === 'object' && t !== null) {
|
|
201
|
+
return ('deviceId' in t &&
|
|
202
|
+
'groupId' in t &&
|
|
203
|
+
'kind' in t &&
|
|
204
|
+
'label' in t &&
|
|
205
|
+
'id' in t &&
|
|
206
|
+
'settings' in t);
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -4,9 +4,33 @@
|
|
|
4
4
|
* @beta
|
|
5
5
|
*/
|
|
6
6
|
export type MediaDeviceInfoLike = Omit<MediaDeviceInfo, 'toJSON'> & {
|
|
7
|
-
settings?: MediaTrackSettings;
|
|
8
7
|
toJSON?: MediaDeviceInfo['toJSON'];
|
|
9
8
|
};
|
|
9
|
+
export type MediaDeviceTrackInfo = MediaDeviceInfoLike & {
|
|
10
|
+
id: string;
|
|
11
|
+
settings: MediaTrackSettings;
|
|
12
|
+
};
|
|
13
|
+
export type IndexedDeviceMap = {
|
|
14
|
+
[Key in MediaDeviceInfo['kind']]: {
|
|
15
|
+
deviceId: Map<MediaDeviceInfoLike['deviceId'], MediaDeviceInfoLike>;
|
|
16
|
+
label: Map<MediaDeviceInfoLike['label'], MediaDeviceInfoLike[]>;
|
|
17
|
+
size: number;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export interface IndexedDevices {
|
|
21
|
+
/**
|
|
22
|
+
* Get devices by kind and device id or label
|
|
23
|
+
* When both `deviceId` and `label` are provided, it will use `deviceId` to
|
|
24
|
+
* to find the device, and when it's not found, it will use `label` to find
|
|
25
|
+
*
|
|
26
|
+
* @param device - the device to find
|
|
27
|
+
* @returns found devices otherwise empty array
|
|
28
|
+
*/
|
|
29
|
+
get(device?: Pick<Partial<MediaDeviceInfoLike>, 'deviceId' | 'groupId' | 'label' | 'kind'>): MediaDeviceInfoLike[];
|
|
30
|
+
size(kind?: MediaDeviceKind): number;
|
|
31
|
+
first(kind: MediaDeviceInfo['kind']): MediaDeviceInfoLike | undefined;
|
|
32
|
+
toJSON(): Record<MediaDeviceKind, MediaDeviceInfoLike[]>;
|
|
33
|
+
}
|
|
10
34
|
export type DeviceConstraint = MediaDeviceInfoLike | MediaDeviceInfoLike[];
|
|
11
35
|
export interface NormalizedConstraintDeviceParameters {
|
|
12
36
|
exact?: MediaDeviceInfoLike[];
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MediaDeviceInfoLike } from './types';
|
|
1
|
+
import type { MediaDeviceInfoLike, IndexedDevices } from './types';
|
|
2
2
|
import { MediaDeviceKinds } from './types';
|
|
3
3
|
export declare const isSameDeviceKind: (kind: MediaDeviceKinds) => (device: MediaDeviceInfoLike) => boolean;
|
|
4
4
|
export declare const isAudioInput: (device: MediaDeviceInfoLike) => boolean;
|
|
@@ -14,6 +14,31 @@ export declare const isAudioOutput: (device: MediaDeviceInfoLike) => boolean;
|
|
|
14
14
|
**/
|
|
15
15
|
export declare const isDeviceGranted: (device: MediaDeviceInfoLike) => boolean;
|
|
16
16
|
export declare const not: <T = void>(fn: (arg: T) => boolean) => (arg: T) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Create a device ID from provided `device`.
|
|
19
|
+
* The device ID here is in the format of `groupId:deviceId:kind`, be default.
|
|
20
|
+
*
|
|
21
|
+
* @param device - The device to create the ID from
|
|
22
|
+
* @param separator - The separator to use to join the device ID
|
|
23
|
+
*/
|
|
24
|
+
export declare const getDeviceId: (device: Pick<MediaDeviceInfoLike, 'groupId' | 'deviceId' | 'kind'>, separator?: string) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Reverse the device ID to its original form i.e. `MediaDeviceInfo`
|
|
27
|
+
* The device ID here is in the format of `groupId:deviceId:kind`, be default.
|
|
28
|
+
*
|
|
29
|
+
* @param id - The device ID to reverse
|
|
30
|
+
* @param separator - The separator used to split the device ID
|
|
31
|
+
*/
|
|
32
|
+
export declare const reverseDeviceId: (id: string, separator?: string) => {
|
|
33
|
+
deviceId: string;
|
|
34
|
+
groupId: string;
|
|
35
|
+
kind: MediaDeviceKind;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Create an `IndexedDevices` construct from provided `devices` which index the
|
|
39
|
+
* device by device ID or kind.
|
|
40
|
+
*/
|
|
41
|
+
export declare const createIndexedDevices: (devices: MediaDeviceInfoLike[]) => IndexedDevices;
|
|
17
42
|
/**
|
|
18
43
|
* Compare 2 devices to see if they are the same by `deviceId` or `label` as
|
|
19
44
|
* a fallback
|
|
@@ -22,7 +47,7 @@ export declare const not: <T = void>(fn: (arg: T) => boolean) => (arg: T) => boo
|
|
|
22
47
|
*/
|
|
23
48
|
export declare const compareDevices: (deviceInfo: MediaDeviceInfoLike, key?: Exclude<keyof MediaDeviceInfoLike, 'kind' | 'settings'>) => (anotherDeviceInfo: MediaDeviceInfoLike) => boolean;
|
|
24
49
|
/**
|
|
25
|
-
* Lookup the
|
|
50
|
+
* Lookup the `device` from `devices` list
|
|
26
51
|
*
|
|
27
52
|
* @remarks
|
|
28
53
|
* When we cannot find the device by `deviceId`, we compare the label as last
|
package/dist/utils.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { assert } from '@pexip/utils';
|
|
1
2
|
import { MediaDeviceKinds } from './types';
|
|
3
|
+
import { DEVICE_ID_SEPARATOR } from './constants';
|
|
2
4
|
export const isSameDeviceKind = (kind) => (device) => device.kind === kind;
|
|
3
5
|
export const isAudioInput = isSameDeviceKind(MediaDeviceKinds.AUDIOINPUT);
|
|
4
6
|
export const isVideoInput = isSameDeviceKind(MediaDeviceKinds.VIDEOINPUT);
|
|
@@ -13,6 +15,126 @@ export const isAudioOutput = isSameDeviceKind(MediaDeviceKinds.AUDIOOUTPUT);
|
|
|
13
15
|
**/
|
|
14
16
|
export const isDeviceGranted = (device) => Boolean(device.label);
|
|
15
17
|
export const not = (fn) => (arg) => !fn(arg);
|
|
18
|
+
/**
|
|
19
|
+
* Create a device ID from provided `device`.
|
|
20
|
+
* The device ID here is in the format of `groupId:deviceId:kind`, be default.
|
|
21
|
+
*
|
|
22
|
+
* @param device - The device to create the ID from
|
|
23
|
+
* @param separator - The separator to use to join the device ID
|
|
24
|
+
*/
|
|
25
|
+
export const getDeviceId = (device, separator = DEVICE_ID_SEPARATOR) => [device.groupId, device.deviceId, device.kind].join(separator);
|
|
26
|
+
/**
|
|
27
|
+
* Reverse the device ID to its original form i.e. `MediaDeviceInfo`
|
|
28
|
+
* The device ID here is in the format of `groupId:deviceId:kind`, be default.
|
|
29
|
+
*
|
|
30
|
+
* @param id - The device ID to reverse
|
|
31
|
+
* @param separator - The separator used to split the device ID
|
|
32
|
+
*/
|
|
33
|
+
export const reverseDeviceId = (id, separator = DEVICE_ID_SEPARATOR) => {
|
|
34
|
+
const [groupId, deviceId, kind] = id.split(separator);
|
|
35
|
+
assert(groupId !== undefined, `Invalid id[${id}] format: groupId`);
|
|
36
|
+
assert(deviceId !== undefined, `Invalid id[${id}] format: deviceId`);
|
|
37
|
+
assert(kind === 'audioinput' ||
|
|
38
|
+
kind === 'videoinput' ||
|
|
39
|
+
kind === 'audiooutput', `Invalid id[${id}] format: kind`);
|
|
40
|
+
return { deviceId, groupId, kind: kind };
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Create an `IndexedDevices` construct from provided `devices` which index the
|
|
44
|
+
* device by device ID or kind.
|
|
45
|
+
*/
|
|
46
|
+
export const createIndexedDevices = (devices) => {
|
|
47
|
+
const emptyIndex = {
|
|
48
|
+
audioinput: {
|
|
49
|
+
deviceId: new Map(),
|
|
50
|
+
label: new Map(),
|
|
51
|
+
get size() {
|
|
52
|
+
return this.deviceId.size;
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
audiooutput: {
|
|
56
|
+
deviceId: new Map(),
|
|
57
|
+
label: new Map(),
|
|
58
|
+
get size() {
|
|
59
|
+
return this.deviceId.size;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
videoinput: {
|
|
63
|
+
deviceId: new Map(),
|
|
64
|
+
label: new Map(),
|
|
65
|
+
get size() {
|
|
66
|
+
return this.deviceId.size;
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
size(kind) {
|
|
70
|
+
switch (kind) {
|
|
71
|
+
case 'audioinput':
|
|
72
|
+
return this.audioinput.size;
|
|
73
|
+
case 'audiooutput':
|
|
74
|
+
return this.audiooutput.size;
|
|
75
|
+
case 'videoinput':
|
|
76
|
+
return this.videoinput.size;
|
|
77
|
+
default:
|
|
78
|
+
return (this.audioinput.size +
|
|
79
|
+
this.audiooutput.size +
|
|
80
|
+
this.videoinput.size);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
get(deviceToFind) {
|
|
84
|
+
const { deviceId, kind, label } = deviceToFind ?? {};
|
|
85
|
+
if (!kind) {
|
|
86
|
+
return [
|
|
87
|
+
...this.get({ ...deviceToFind, kind: 'audioinput' }),
|
|
88
|
+
...this.get({ ...deviceToFind, kind: 'audiooutput' }),
|
|
89
|
+
...this.get({ ...deviceToFind, kind: 'videoinput' }),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
const deviceIdMap = this[kind].deviceId;
|
|
93
|
+
const findSimilarDevices = (label) => {
|
|
94
|
+
const foundSimilar = this[kind].label.get(label);
|
|
95
|
+
return foundSimilar ? foundSimilar : [];
|
|
96
|
+
};
|
|
97
|
+
if (deviceId !== undefined) {
|
|
98
|
+
const foundExact = deviceIdMap.get(deviceId);
|
|
99
|
+
if (!foundExact && label) {
|
|
100
|
+
return findSimilarDevices(label);
|
|
101
|
+
}
|
|
102
|
+
return foundExact ? [foundExact] : [];
|
|
103
|
+
}
|
|
104
|
+
if (label) {
|
|
105
|
+
return findSimilarDevices(label);
|
|
106
|
+
}
|
|
107
|
+
return [...deviceIdMap.values()];
|
|
108
|
+
},
|
|
109
|
+
first(kind) {
|
|
110
|
+
if (!kind) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
for (const device of this[kind].deviceId.values()) {
|
|
114
|
+
return device;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
toJSON() {
|
|
118
|
+
return {
|
|
119
|
+
audioinput: this.get({ kind: 'audioinput' }),
|
|
120
|
+
videoinput: this.get({ kind: 'videoinput' }),
|
|
121
|
+
audiooutput: this.get({ kind: 'audiooutput' }),
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
return devices.reduce((acc, device) => {
|
|
126
|
+
// We don't want to index devices that are not granted
|
|
127
|
+
if (device.label && device.deviceId !== undefined) {
|
|
128
|
+
acc[device.kind].deviceId.set(device.deviceId, device);
|
|
129
|
+
}
|
|
130
|
+
if (device.label) {
|
|
131
|
+
const labelDevices = acc[device.kind].label.get(device.label) ?? [];
|
|
132
|
+
labelDevices.push(device);
|
|
133
|
+
acc[device.kind].label.set(device.label, labelDevices);
|
|
134
|
+
}
|
|
135
|
+
return acc;
|
|
136
|
+
}, emptyIndex);
|
|
137
|
+
};
|
|
16
138
|
/**
|
|
17
139
|
* Compare 2 devices to see if they are the same by `deviceId` or `label` as
|
|
18
140
|
* a fallback
|
|
@@ -22,7 +144,7 @@ export const not = (fn) => (arg) => !fn(arg);
|
|
|
22
144
|
export const compareDevices = (deviceInfo, key = 'deviceId') => (anotherDeviceInfo) => deviceInfo.kind === anotherDeviceInfo.kind &&
|
|
23
145
|
deviceInfo[key] === anotherDeviceInfo[key];
|
|
24
146
|
/**
|
|
25
|
-
* Lookup the
|
|
147
|
+
* Lookup the `device` from `devices` list
|
|
26
148
|
*
|
|
27
149
|
* @remarks
|
|
28
150
|
* When we cannot find the device by `deviceId`, we compare the label as last
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pexip/media-control",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "19.0.0",
|
|
4
4
|
"homepage": "https://gitlab.com/pexip/zoo",
|
|
5
5
|
"bugs": "https://gitlab.com/pexip/zoo/issues",
|
|
6
6
|
"repository": {
|
|
@@ -28,10 +28,11 @@
|
|
|
28
28
|
"typecheck": "yarn tsc --noEmit -p tsconfig.build.json"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@pexip/utils": "16.
|
|
31
|
+
"@pexip/utils": "16.13.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@
|
|
34
|
+
"@jest/globals": "^29.7.0",
|
|
35
|
+
"@pexip/bundler": "17.1.0",
|
|
35
36
|
"@swc/core": "^1.4.6",
|
|
36
37
|
"@swc/jest": "^0.2.36",
|
|
37
38
|
"@types/uuid": "^8.3.0",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"jest-junit": "^16.0.0",
|
|
41
42
|
"prettier": "^3.2.5",
|
|
42
43
|
"typescript": "~5.3.0",
|
|
43
|
-
"uuid": "^
|
|
44
|
+
"uuid": "^11.0.0"
|
|
44
45
|
},
|
|
45
46
|
"publishConfig": {
|
|
46
47
|
"access": "public",
|