@pexip/media-control 16.7.1
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 +967 -0
- package/LICENSE +188 -0
- package/README.md +244 -0
- package/dist/index.d.ts +923 -0
- package/dist/index.mjs +1635 -0
- package/package.json +47 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1635 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var MediaDeviceKinds = /* @__PURE__ */ ((MediaDeviceKinds2) => {
|
|
3
|
+
MediaDeviceKinds2["AUDIOINPUT"] = "audioinput";
|
|
4
|
+
MediaDeviceKinds2["AUDIOOUTPUT"] = "audiooutput";
|
|
5
|
+
MediaDeviceKinds2["VIDEOINPUT"] = "videoinput";
|
|
6
|
+
return MediaDeviceKinds2;
|
|
7
|
+
})(MediaDeviceKinds || {});
|
|
8
|
+
var FACING_MODE = ["user", "environment", "left", "right"];
|
|
9
|
+
var MediaDeviceFailure = /* @__PURE__ */ ((MediaDeviceFailure2) => {
|
|
10
|
+
MediaDeviceFailure2["AbortError"] = "AbortError";
|
|
11
|
+
MediaDeviceFailure2["AudioAndVideoDeviceNotFoundError"] = "AudioAndVideoDeviceNotFoundError";
|
|
12
|
+
MediaDeviceFailure2["AudioInputDeviceNotFoundError"] = "AudioInputDeviceNotFoundError";
|
|
13
|
+
MediaDeviceFailure2["MissingConstraintsError"] = "MissingConstraintsError";
|
|
14
|
+
MediaDeviceFailure2["NotAllowedError"] = "NotAllowedError";
|
|
15
|
+
MediaDeviceFailure2["NotFoundError"] = "NotFoundError";
|
|
16
|
+
MediaDeviceFailure2["NotReadableError"] = "NotReadableError";
|
|
17
|
+
MediaDeviceFailure2["OverconstrainedError"] = "OverconstrainedError";
|
|
18
|
+
MediaDeviceFailure2["PermissionDeniedError"] = "PermissionDeniedError";
|
|
19
|
+
MediaDeviceFailure2["SecurityError"] = "SecurityError";
|
|
20
|
+
MediaDeviceFailure2["TrackStartError"] = "TrackStartError";
|
|
21
|
+
MediaDeviceFailure2["TypeError"] = "TypeError";
|
|
22
|
+
MediaDeviceFailure2["VideoInputDeviceNotFoundError"] = "VideoInputDeviceNotFoundError";
|
|
23
|
+
MediaDeviceFailure2["NotSupportedError"] = "NotSupportedError";
|
|
24
|
+
MediaDeviceFailure2["StreamTrackNotFound"] = "StreamTrackNotFound";
|
|
25
|
+
return MediaDeviceFailure2;
|
|
26
|
+
})(MediaDeviceFailure || {});
|
|
27
|
+
|
|
28
|
+
// src/typeGuards.ts
|
|
29
|
+
import { hasOwnProperty } from "@pexip/utils";
|
|
30
|
+
var isBoolean = (t) => typeof t === "boolean";
|
|
31
|
+
var isUndefined = (t) => typeof t === "undefined";
|
|
32
|
+
var isNumber = (t) => typeof t === "number" && !Number.isNaN(t);
|
|
33
|
+
var isInteger = (t) => Number.isInteger(t);
|
|
34
|
+
var isFloat = (t) => isNumber(t) && !Number.isInteger(t) && Number.isFinite(t);
|
|
35
|
+
var CONSTRAIN_STRING_KEYS = [
|
|
36
|
+
"facingMode",
|
|
37
|
+
"resizeMode",
|
|
38
|
+
"deviceId",
|
|
39
|
+
"groupId"
|
|
40
|
+
];
|
|
41
|
+
var EXTENDED_CONSTRAIN_STRING_KEYS = [
|
|
42
|
+
"videoSegmentation",
|
|
43
|
+
"videoSegmentationModel",
|
|
44
|
+
"bgImageUrl"
|
|
45
|
+
];
|
|
46
|
+
var CONSTRAIN_U_LONG_KEYS = [
|
|
47
|
+
"width",
|
|
48
|
+
"height",
|
|
49
|
+
"sampleRate",
|
|
50
|
+
"sampleSize",
|
|
51
|
+
"channelCount"
|
|
52
|
+
];
|
|
53
|
+
var EXTENDED_CONSTRAIN_U_LONG_KEYS = [
|
|
54
|
+
"backgroundBlurAmount",
|
|
55
|
+
"edgeBlurAmount"
|
|
56
|
+
];
|
|
57
|
+
var CONSTRAIN_DOUBLE_KEYS = [
|
|
58
|
+
"aspectRatio",
|
|
59
|
+
"frameRate",
|
|
60
|
+
"latency"
|
|
61
|
+
];
|
|
62
|
+
var EXTENDED_CONSTRAIN_DOUBLE_KEYS = ["foregroundThreshold"];
|
|
63
|
+
var CONSTRAIN_BOOLEAN_KEYS = [
|
|
64
|
+
"echoCancellation",
|
|
65
|
+
"autoGainControl",
|
|
66
|
+
"noiseSuppression"
|
|
67
|
+
];
|
|
68
|
+
var EXTENDED_CONSTRAIN_BOOLEAN_KEYS = [
|
|
69
|
+
// Voice Activity Detection
|
|
70
|
+
"vad",
|
|
71
|
+
// Audio Signal Detection
|
|
72
|
+
"asd",
|
|
73
|
+
// Mixing with another track
|
|
74
|
+
"mixWithAdditionalMedia",
|
|
75
|
+
// Noise Suppression using our own impl
|
|
76
|
+
"denoise",
|
|
77
|
+
// Flip the video horizontally
|
|
78
|
+
"flipHorizontal"
|
|
79
|
+
];
|
|
80
|
+
var CONSTRAINT_SET_KEYS = [
|
|
81
|
+
...CONSTRAIN_DOUBLE_KEYS,
|
|
82
|
+
...CONSTRAIN_U_LONG_KEYS,
|
|
83
|
+
...CONSTRAIN_STRING_KEYS,
|
|
84
|
+
...CONSTRAIN_BOOLEAN_KEYS
|
|
85
|
+
];
|
|
86
|
+
var isConstrainStringKeys = (t) => CONSTRAIN_STRING_KEYS.includes(t);
|
|
87
|
+
var isExtendedConstrainStringKeys = (t) => EXTENDED_CONSTRAIN_STRING_KEYS.includes(t);
|
|
88
|
+
var isConstrainULongKeys = (t) => CONSTRAIN_U_LONG_KEYS.includes(t);
|
|
89
|
+
var isExtendedConstrainULongKeys = (t) => EXTENDED_CONSTRAIN_U_LONG_KEYS.includes(t);
|
|
90
|
+
var isConstrainDoubleKeys = (t) => CONSTRAIN_DOUBLE_KEYS.includes(t);
|
|
91
|
+
var isExtendedConstrainDoubleKeys = (t) => EXTENDED_CONSTRAIN_DOUBLE_KEYS.includes(t);
|
|
92
|
+
var isConstrainBooleanKeys = (t) => CONSTRAIN_BOOLEAN_KEYS.includes(t);
|
|
93
|
+
var isExtendedConstrainBooleanKeys = (t) => EXTENDED_CONSTRAIN_BOOLEAN_KEYS.includes(t);
|
|
94
|
+
var isMediaTrackConstraintSetKey = (t) => CONSTRAINT_SET_KEYS.includes(t);
|
|
95
|
+
var isMediaTrackConstraintsKey = (t) => isMediaTrackConstraintSetKey(t) || t === "advanced";
|
|
96
|
+
var isMediaTrackConstraints = (t) => {
|
|
97
|
+
if (!t || typeof t !== "object" || Array.isArray(t) || t === null) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const keys = Object.keys(t);
|
|
101
|
+
return !!keys.length && Object.keys(t).every((key) => isMediaTrackConstraintsKey(key));
|
|
102
|
+
};
|
|
103
|
+
var isMediaDeviceInfo = (t) => {
|
|
104
|
+
if (!t || typeof t !== "object") {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return !!t && "deviceId" in t && "kind" in t;
|
|
108
|
+
};
|
|
109
|
+
var isMediaDeviceInfoArray = (t) => {
|
|
110
|
+
if (Array.isArray(t) && t.length && t.some(isMediaDeviceInfo)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
var isDeviceConstraint = (t) => {
|
|
116
|
+
return isMediaDeviceInfo(t) || isMediaDeviceInfoArray(t);
|
|
117
|
+
};
|
|
118
|
+
var isConstraintDOMString = (t) => typeof t === "string" && !!t || Array.isArray(t) && t.some(Boolean);
|
|
119
|
+
var CONSTRAIN_PARAM_KEYS = ["exact", "ideal"];
|
|
120
|
+
var CONSTRAIN_RANGE_KEYS = ["min", "max"];
|
|
121
|
+
var CONSTRAIN_KEYS = [
|
|
122
|
+
...CONSTRAIN_PARAM_KEYS,
|
|
123
|
+
...CONSTRAIN_RANGE_KEYS
|
|
124
|
+
];
|
|
125
|
+
var isConstrainDOMParameters = (isType, keys = CONSTRAIN_PARAM_KEYS) => (t) => {
|
|
126
|
+
if (!t || typeof t !== "object" || t === null || Object.keys(t).length <= 0) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return keys.some((key) => hasOwnProperty(t, key) && isType(t[key]));
|
|
130
|
+
};
|
|
131
|
+
var isConstrainDOMStringParameters = isConstrainDOMParameters(
|
|
132
|
+
isConstraintDOMString
|
|
133
|
+
);
|
|
134
|
+
var isConstrainBooleanParameters = isConstrainDOMParameters(isBoolean);
|
|
135
|
+
var isConstrainRange = isConstrainDOMParameters(
|
|
136
|
+
(t) => isFloat(t) || isInteger(t),
|
|
137
|
+
CONSTRAIN_RANGE_KEYS
|
|
138
|
+
);
|
|
139
|
+
var isConstrainDoubleRange = isConstrainDOMParameters(isFloat, CONSTRAIN_KEYS);
|
|
140
|
+
var isConstrainULongRange = isConstrainDOMParameters(isInteger, CONSTRAIN_KEYS);
|
|
141
|
+
var isConstraintDeviceParameters = isConstrainDOMParameters(
|
|
142
|
+
(t) => isMediaDeviceInfo(t) || isMediaDeviceInfoArray(t)
|
|
143
|
+
);
|
|
144
|
+
var isConstraintSetDevice = (t) => isMediaDeviceInfo(t) || isMediaDeviceInfoArray(t) || isConstraintDeviceParameters(t);
|
|
145
|
+
var isExtendedConstraint = (t) => {
|
|
146
|
+
if (typeof t !== "object" || t === null) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
if (hasOwnProperty(t, "device")) {
|
|
150
|
+
const { device } = t;
|
|
151
|
+
return isConstraintSetDevice(device);
|
|
152
|
+
}
|
|
153
|
+
return [
|
|
154
|
+
...EXTENDED_CONSTRAIN_DOUBLE_KEYS,
|
|
155
|
+
...EXTENDED_CONSTRAIN_STRING_KEYS,
|
|
156
|
+
...EXTENDED_CONSTRAIN_U_LONG_KEYS,
|
|
157
|
+
...EXTENDED_CONSTRAIN_BOOLEAN_KEYS
|
|
158
|
+
].some((key) => hasOwnProperty(t, key));
|
|
159
|
+
};
|
|
160
|
+
var isInputConstraintSet = (t) => {
|
|
161
|
+
if (typeof t !== "object" || t === null) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return isExtendedConstraint(t) || isMediaTrackConstraints(t);
|
|
165
|
+
};
|
|
166
|
+
var isMediaStreamTrack = (m) => {
|
|
167
|
+
if (m && typeof m === "object") {
|
|
168
|
+
return !!m && "getSettings" in m;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
};
|
|
172
|
+
var isFacingMode = (s) => {
|
|
173
|
+
if (typeof s === "string" && FACING_MODE.includes(s)) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// ../../shared/baseLogger.ts
|
|
180
|
+
var LogLevels = ((LogLevels2) => {
|
|
181
|
+
LogLevels2[LogLevels2["trace"] = 10] = "trace";
|
|
182
|
+
LogLevels2[LogLevels2["debug"] = 20] = "debug";
|
|
183
|
+
LogLevels2[LogLevels2["info"] = 30] = "info";
|
|
184
|
+
LogLevels2[LogLevels2["warn"] = 40] = "warn";
|
|
185
|
+
LogLevels2[LogLevels2["error"] = 50] = "error";
|
|
186
|
+
LogLevels2[LogLevels2["fatal"] = 60] = "fatal";
|
|
187
|
+
LogLevels2[LogLevels2["silent"] = Number.MAX_SAFE_INTEGER] = "silent";
|
|
188
|
+
return LogLevels2;
|
|
189
|
+
})(LogLevels || {});
|
|
190
|
+
function createConsoleLogger() {
|
|
191
|
+
return Object.freeze({
|
|
192
|
+
/* eslint-disable no-console -- set logger to console */
|
|
193
|
+
fatal: (meta, message) => console.error(message, meta),
|
|
194
|
+
error: (meta, message) => console.error(message, meta),
|
|
195
|
+
warn: (meta, message) => console.warn(message, meta),
|
|
196
|
+
info: (meta, message) => console.info(message, meta),
|
|
197
|
+
debug: (meta, message) => console.debug(message, meta),
|
|
198
|
+
trace() {
|
|
199
|
+
},
|
|
200
|
+
// Noop
|
|
201
|
+
silent() {
|
|
202
|
+
},
|
|
203
|
+
// Noop
|
|
204
|
+
redact() {
|
|
205
|
+
}
|
|
206
|
+
// Noop
|
|
207
|
+
/* eslint-enable no-console -- set logger to console */
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/logger.ts
|
|
212
|
+
var logger = createConsoleLogger();
|
|
213
|
+
function setLogger(newLogger) {
|
|
214
|
+
logger = newLogger;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/utils.ts
|
|
218
|
+
var isSameDeviceKind = (kind) => (device) => device.kind === kind;
|
|
219
|
+
var isAudioInput = isSameDeviceKind("audioinput" /* AUDIOINPUT */);
|
|
220
|
+
var isVideoInput = isSameDeviceKind("videoinput" /* VIDEOINPUT */);
|
|
221
|
+
var isAudioOutput = isSameDeviceKind("audiooutput" /* AUDIOOUTPUT */);
|
|
222
|
+
var isDeviceGranted = (device) => Boolean(device.label);
|
|
223
|
+
var not = (fn) => (arg) => !fn(arg);
|
|
224
|
+
var compareDevices = (deviceInfo, key = "deviceId") => (anotherDeviceInfo) => deviceInfo.kind === anotherDeviceInfo.kind && deviceInfo[key] === anotherDeviceInfo[key];
|
|
225
|
+
var findDevice = (deviceToFind, useFallback = true) => (devices) => {
|
|
226
|
+
const compareIDTo = compareDevices(deviceToFind);
|
|
227
|
+
const found = devices.find(
|
|
228
|
+
(device) => compareIDTo(device)
|
|
229
|
+
);
|
|
230
|
+
if (found || !useFallback) {
|
|
231
|
+
return found;
|
|
232
|
+
}
|
|
233
|
+
const compareLabelTo = compareDevices(deviceToFind, "label");
|
|
234
|
+
return devices.find(
|
|
235
|
+
(device) => compareLabelTo(device)
|
|
236
|
+
);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/constraints.ts
|
|
240
|
+
var isExactDeviceConstraint = (constraint) => {
|
|
241
|
+
if (isInputConstraintSet(constraint)) {
|
|
242
|
+
return !!constraint.device && typeof constraint.device === "object" && "exact" in constraint.device || !!constraint.deviceId && typeof constraint.deviceId === "object" && "exact" in constraint.deviceId;
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
};
|
|
246
|
+
var getConstraintsSetFilter = (supportedConstraints) => (trackConstraints) => {
|
|
247
|
+
if (!isMediaTrackConstraints(trackConstraints)) {
|
|
248
|
+
return trackConstraints;
|
|
249
|
+
}
|
|
250
|
+
const constraints = Object.entries(trackConstraints).reduce(
|
|
251
|
+
(acc, [key, val]) => key === "advanced" || supportedConstraints.has(
|
|
252
|
+
key
|
|
253
|
+
) ? { ...acc, [key]: val } : acc,
|
|
254
|
+
{}
|
|
255
|
+
);
|
|
256
|
+
if (Object.keys(constraints).length === 0) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
return constraints;
|
|
260
|
+
};
|
|
261
|
+
var getDefinedOnly = (obj) => {
|
|
262
|
+
return Object.entries(obj).reduce(
|
|
263
|
+
(acc, [key, val]) => typeof val === "undefined" ? acc : { ...acc, [key]: val },
|
|
264
|
+
{}
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
var removeUnsupportedConstraints = (constraints) => {
|
|
268
|
+
if (!(navigator && "getSupportedConstraints" in navigator.mediaDevices)) {
|
|
269
|
+
return constraints;
|
|
270
|
+
}
|
|
271
|
+
const supportedConstraints = Object.entries(
|
|
272
|
+
navigator.mediaDevices.getSupportedConstraints()
|
|
273
|
+
);
|
|
274
|
+
logger.debug({ supportedConstraints }, "Supported constraints");
|
|
275
|
+
const constraintsSetFilter = getConstraintsSetFilter(
|
|
276
|
+
supportedConstraints.reduce(
|
|
277
|
+
(set, [key, value]) => (
|
|
278
|
+
// FIXME: Cast inserted to unblock typescript upgrade, will have to be verified and properly fixed.
|
|
279
|
+
value && isMediaTrackConstraintSetKey(key) ? set.add(
|
|
280
|
+
key
|
|
281
|
+
) : set
|
|
282
|
+
),
|
|
283
|
+
/* @__PURE__ */ new Set()
|
|
284
|
+
)
|
|
285
|
+
);
|
|
286
|
+
return getDefinedOnly({
|
|
287
|
+
audio: constraintsSetFilter(constraints.audio),
|
|
288
|
+
peerIdentity: constraints.peerIdentity,
|
|
289
|
+
video: constraintsSetFilter(constraints.video)
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
var mergeConstraints = (baseConstraints) => (constraints) => {
|
|
293
|
+
if (!isInputConstraintSet(baseConstraints)) {
|
|
294
|
+
if (isDeviceConstraint(constraints)) {
|
|
295
|
+
return { device: constraints };
|
|
296
|
+
}
|
|
297
|
+
return constraints ?? false;
|
|
298
|
+
}
|
|
299
|
+
if (!constraints) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
if (isDeviceConstraint(constraints)) {
|
|
303
|
+
const result = {
|
|
304
|
+
...baseConstraints,
|
|
305
|
+
device: constraints
|
|
306
|
+
};
|
|
307
|
+
delete result.facingMode;
|
|
308
|
+
delete result.deviceId;
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
if (isInputConstraintSet(constraints)) {
|
|
312
|
+
const combined = { ...baseConstraints, ...constraints };
|
|
313
|
+
const hasDeviceId = isConstraintDOMString(combined.deviceId) || isConstrainDOMStringParameters(combined.deviceId);
|
|
314
|
+
const hasDevice = isDeviceConstraint(combined.device) || isConstraintDeviceParameters(combined.device);
|
|
315
|
+
if (hasDeviceId && hasDevice) {
|
|
316
|
+
const result = {
|
|
317
|
+
...combined
|
|
318
|
+
};
|
|
319
|
+
delete result.facingMode;
|
|
320
|
+
delete result.deviceId;
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
if (hasDevice || hasDeviceId) {
|
|
324
|
+
delete combined.facingMode;
|
|
325
|
+
return combined;
|
|
326
|
+
}
|
|
327
|
+
return combined;
|
|
328
|
+
}
|
|
329
|
+
return baseConstraints;
|
|
330
|
+
};
|
|
331
|
+
var normalizeDevice = (device) => {
|
|
332
|
+
if (!device) {
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
if (isMediaDeviceInfo(device)) {
|
|
336
|
+
return [device];
|
|
337
|
+
}
|
|
338
|
+
if (Array.isArray(device)) {
|
|
339
|
+
const devices = device.filter(isMediaDeviceInfo);
|
|
340
|
+
if (devices.length) {
|
|
341
|
+
return devices;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return void 0;
|
|
345
|
+
};
|
|
346
|
+
var normalizeDeviceConstraint = (constraints) => {
|
|
347
|
+
if (!constraints) {
|
|
348
|
+
return void 0;
|
|
349
|
+
}
|
|
350
|
+
if (isConstraintDeviceParameters(constraints)) {
|
|
351
|
+
return Object.keys(constraints).filter((k) => ["ideal", "exact"].includes(k)).reduce((cs, k) => {
|
|
352
|
+
const key = k;
|
|
353
|
+
const value = constraints[key];
|
|
354
|
+
const devices = normalizeDevice(value);
|
|
355
|
+
if (devices) {
|
|
356
|
+
return { ...cs ?? {}, [key]: devices };
|
|
357
|
+
}
|
|
358
|
+
return cs;
|
|
359
|
+
}, void 0);
|
|
360
|
+
}
|
|
361
|
+
const normalized = normalizeDevice(constraints);
|
|
362
|
+
return normalized && { ideal: normalized };
|
|
363
|
+
};
|
|
364
|
+
var toDeviceIdConstraintSet = (constraint) => {
|
|
365
|
+
const [devices, param] = extractConstrainDevice({ device: constraint });
|
|
366
|
+
return devices && { [param]: devices.map((device) => device.deviceId) };
|
|
367
|
+
};
|
|
368
|
+
var toArray = (t) => {
|
|
369
|
+
const r = Array.isArray(t) ? t : [t];
|
|
370
|
+
return r.filter(Boolean);
|
|
371
|
+
};
|
|
372
|
+
var NONE = [void 0, "ideal"];
|
|
373
|
+
var extractConstrainString = (key) => (constraints) => {
|
|
374
|
+
const constraint = constraints[key];
|
|
375
|
+
if (!constraint) {
|
|
376
|
+
return NONE;
|
|
377
|
+
}
|
|
378
|
+
if (isConstraintDOMString(constraint)) {
|
|
379
|
+
return [toArray(constraint), "ideal"];
|
|
380
|
+
}
|
|
381
|
+
if (isConstrainDOMStringParameters(constraint)) {
|
|
382
|
+
if (constraint.exact) {
|
|
383
|
+
const normalized = toArray(constraint.exact);
|
|
384
|
+
if (normalized.length) {
|
|
385
|
+
return [normalized, "exact"];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (constraint.ideal) {
|
|
389
|
+
return [toArray(constraint.ideal), "ideal"];
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return NONE;
|
|
393
|
+
};
|
|
394
|
+
var extractConstrainBoolean = (key) => (constraints) => {
|
|
395
|
+
const constraint = constraints[key];
|
|
396
|
+
if (isUndefined(constraint)) {
|
|
397
|
+
return NONE;
|
|
398
|
+
}
|
|
399
|
+
if (isBoolean(constraint)) {
|
|
400
|
+
return [constraint, "ideal"];
|
|
401
|
+
}
|
|
402
|
+
if (isConstrainBooleanParameters(constraint)) {
|
|
403
|
+
const { ideal, exact } = constraint;
|
|
404
|
+
if (isBoolean(exact)) {
|
|
405
|
+
return [exact, "exact"];
|
|
406
|
+
}
|
|
407
|
+
if (isBoolean(ideal)) {
|
|
408
|
+
return [ideal, "ideal"];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return NONE;
|
|
412
|
+
};
|
|
413
|
+
var extractConstrainNumber = (key) => (constraints) => {
|
|
414
|
+
const constraint = constraints[key];
|
|
415
|
+
if (constraint === void 0 || constraint === null) {
|
|
416
|
+
return NONE;
|
|
417
|
+
}
|
|
418
|
+
if (isFloat(constraint) || isInteger(constraint)) {
|
|
419
|
+
return [constraint, "ideal"];
|
|
420
|
+
}
|
|
421
|
+
if (isConstrainULongRange(constraint) || isConstrainDoubleRange(constraint)) {
|
|
422
|
+
const { exact, ideal, min, max } = constraint;
|
|
423
|
+
if (isFloat(min) || isFloat(max) || isInteger(min) || isInteger(max)) {
|
|
424
|
+
return [{ min, max, ideal, exact }, "min-max"];
|
|
425
|
+
}
|
|
426
|
+
if (isFloat(exact) || isInteger(exact)) {
|
|
427
|
+
return [exact, "exact"];
|
|
428
|
+
}
|
|
429
|
+
if (isFloat(ideal) || isInteger(ideal)) {
|
|
430
|
+
return [ideal, "ideal"];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return NONE;
|
|
434
|
+
};
|
|
435
|
+
var extractConstrainDevice = (constraints) => {
|
|
436
|
+
if (!constraints || isBoolean(constraints) || Array.isArray(constraints) && !constraints.length) {
|
|
437
|
+
return NONE;
|
|
438
|
+
}
|
|
439
|
+
const { device } = isInputConstraintSet(constraints) ? constraints : { device: constraints };
|
|
440
|
+
if (isMediaDeviceInfo(device) || isMediaDeviceInfoArray(device)) {
|
|
441
|
+
const normalized = normalizeDevice(device);
|
|
442
|
+
if (normalized) {
|
|
443
|
+
return [normalized, "ideal"];
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (isConstraintDeviceParameters(device)) {
|
|
447
|
+
const { exact, ideal } = normalizeDeviceConstraint(device) ?? {};
|
|
448
|
+
if (exact) {
|
|
449
|
+
return [exact, "exact"];
|
|
450
|
+
}
|
|
451
|
+
if (ideal) {
|
|
452
|
+
return [ideal, "ideal"];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return NONE;
|
|
456
|
+
};
|
|
457
|
+
var closedTo = (a, b, numDigits = 5) => {
|
|
458
|
+
const multiplier = Math.pow(10, numDigits);
|
|
459
|
+
return Math.round(a * multiplier) === Math.round(b * multiplier);
|
|
460
|
+
};
|
|
461
|
+
var between = (min, num, max) => {
|
|
462
|
+
if (!isUndefined(min)) {
|
|
463
|
+
if (!isUndefined(max)) {
|
|
464
|
+
return num >= min && num <= max;
|
|
465
|
+
}
|
|
466
|
+
return num >= min;
|
|
467
|
+
}
|
|
468
|
+
if (!isUndefined(max)) {
|
|
469
|
+
return num <= max;
|
|
470
|
+
}
|
|
471
|
+
return true;
|
|
472
|
+
};
|
|
473
|
+
var getValueFromConstrainNumber = (constraint) => {
|
|
474
|
+
if (isNumber(constraint)) {
|
|
475
|
+
return constraint;
|
|
476
|
+
}
|
|
477
|
+
const { min, max, ideal, exact } = constraint;
|
|
478
|
+
if (exact !== void 0) {
|
|
479
|
+
const withinRange = between(min, exact, max);
|
|
480
|
+
if (withinRange) {
|
|
481
|
+
return exact;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (ideal !== void 0) {
|
|
485
|
+
const withinRange = between(min, ideal, max);
|
|
486
|
+
if (withinRange) {
|
|
487
|
+
return ideal;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const value = max ?? min;
|
|
491
|
+
if (value !== void 0) {
|
|
492
|
+
return value;
|
|
493
|
+
}
|
|
494
|
+
throw Error("Constrain Number is undefined");
|
|
495
|
+
};
|
|
496
|
+
var getFacingModeFromConstraintString = (constraint) => {
|
|
497
|
+
if (isFacingMode(constraint)) {
|
|
498
|
+
return constraint;
|
|
499
|
+
}
|
|
500
|
+
return void 0;
|
|
501
|
+
};
|
|
502
|
+
var satisfyConstrainNumber = (constraint, num) => {
|
|
503
|
+
if (isUndefined(constraint) || isUndefined(num)) {
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
if (isInteger(constraint)) {
|
|
507
|
+
return num === constraint;
|
|
508
|
+
}
|
|
509
|
+
if (isFloat(constraint)) {
|
|
510
|
+
return closedTo(constraint, num);
|
|
511
|
+
}
|
|
512
|
+
if (isConstrainRange(constraint)) {
|
|
513
|
+
const { min, max, ideal, exact } = constraint;
|
|
514
|
+
const withinRange = between(min, num, max);
|
|
515
|
+
if (isFloat(exact)) {
|
|
516
|
+
return withinRange && closedTo(exact, num);
|
|
517
|
+
}
|
|
518
|
+
if (isInteger(exact)) {
|
|
519
|
+
return withinRange && exact === num;
|
|
520
|
+
}
|
|
521
|
+
if (isFloat(ideal)) {
|
|
522
|
+
return withinRange && closedTo(ideal, num);
|
|
523
|
+
}
|
|
524
|
+
if (isInteger(ideal)) {
|
|
525
|
+
return withinRange && ideal === num;
|
|
526
|
+
}
|
|
527
|
+
return withinRange;
|
|
528
|
+
}
|
|
529
|
+
return false;
|
|
530
|
+
};
|
|
531
|
+
var resolveMediaDeviceConstraints = (constraints, base) => {
|
|
532
|
+
const merge = mergeConstraints(base);
|
|
533
|
+
if (isInputConstraintSet(constraints)) {
|
|
534
|
+
if (isConstraintSetDevice(constraints.device)) {
|
|
535
|
+
return merge(
|
|
536
|
+
Object.keys(constraints).reduce((cs, k) => {
|
|
537
|
+
const key = k;
|
|
538
|
+
if (key === "device") {
|
|
539
|
+
return {
|
|
540
|
+
...cs,
|
|
541
|
+
deviceId: toDeviceIdConstraintSet(
|
|
542
|
+
constraints.device
|
|
543
|
+
)
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
return { ...cs, [key]: constraints[key] };
|
|
547
|
+
}, {})
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
return merge(constraints);
|
|
551
|
+
}
|
|
552
|
+
if (isConstraintSetDevice(constraints)) {
|
|
553
|
+
const deviceId = toDeviceIdConstraintSet(constraints);
|
|
554
|
+
return merge({ deviceId });
|
|
555
|
+
}
|
|
556
|
+
if (constraints && isMediaTrackConstraints(base)) {
|
|
557
|
+
return base;
|
|
558
|
+
}
|
|
559
|
+
return !!constraints;
|
|
560
|
+
};
|
|
561
|
+
var getMediaConstraints = ({
|
|
562
|
+
audio,
|
|
563
|
+
video,
|
|
564
|
+
defaultConstraints
|
|
565
|
+
}) => {
|
|
566
|
+
return removeUnsupportedConstraints({
|
|
567
|
+
audio: resolveMediaDeviceConstraints(audio, defaultConstraints.audio),
|
|
568
|
+
video: resolveMediaDeviceConstraints(video, defaultConstraints.video)
|
|
569
|
+
});
|
|
570
|
+
};
|
|
571
|
+
var applyConstraints = async (tracks, constraints) => {
|
|
572
|
+
if (!tracks || tracks.length === 0 || Object.keys(constraints).length === 0) {
|
|
573
|
+
return Promise.resolve();
|
|
574
|
+
}
|
|
575
|
+
const { audio, video } = removeUnsupportedConstraints({
|
|
576
|
+
audio: resolveMediaDeviceConstraints(constraints.audio),
|
|
577
|
+
video: resolveMediaDeviceConstraints(constraints.video)
|
|
578
|
+
});
|
|
579
|
+
await Promise.all(
|
|
580
|
+
tracks.flatMap((track) => {
|
|
581
|
+
if (track.kind === "audio" && isMediaTrackConstraints(audio)) {
|
|
582
|
+
return [track.applyConstraints(audio)];
|
|
583
|
+
}
|
|
584
|
+
if (track.kind === "video" && isMediaTrackConstraints(video)) {
|
|
585
|
+
return [track.applyConstraints(video)];
|
|
586
|
+
}
|
|
587
|
+
return [];
|
|
588
|
+
})
|
|
589
|
+
);
|
|
590
|
+
};
|
|
591
|
+
var findDeviceFrom = (devicesToFind, deviceList) => {
|
|
592
|
+
const devicesFound = devicesToFind.flatMap((device) => {
|
|
593
|
+
const found = findDevice(device)(deviceList);
|
|
594
|
+
if (found) {
|
|
595
|
+
return [found];
|
|
596
|
+
}
|
|
597
|
+
return [];
|
|
598
|
+
});
|
|
599
|
+
if (devicesFound.length) {
|
|
600
|
+
return devicesFound;
|
|
601
|
+
}
|
|
602
|
+
return void 0;
|
|
603
|
+
};
|
|
604
|
+
var findDeviceFromDeviceConstraints = (device, devices) => {
|
|
605
|
+
const normalized = normalizeDeviceConstraint(device);
|
|
606
|
+
if (normalized) {
|
|
607
|
+
const { ideal, exact } = normalized;
|
|
608
|
+
if (exact) {
|
|
609
|
+
return findDeviceFrom(exact, devices);
|
|
610
|
+
}
|
|
611
|
+
if (ideal) {
|
|
612
|
+
return findDeviceFrom(ideal, devices);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
var relaxDevice = (device, devices) => {
|
|
617
|
+
const found = findDeviceFromDeviceConstraints(device, devices);
|
|
618
|
+
if (found) {
|
|
619
|
+
return found;
|
|
620
|
+
}
|
|
621
|
+
return normalizeDevice(device);
|
|
622
|
+
};
|
|
623
|
+
var resolveOnlyDevice = (devices) => {
|
|
624
|
+
switch (devices.length) {
|
|
625
|
+
case 1:
|
|
626
|
+
return devices[0];
|
|
627
|
+
case 0:
|
|
628
|
+
return void 0;
|
|
629
|
+
default:
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
function extractConstraints(key) {
|
|
634
|
+
return (constraints) => {
|
|
635
|
+
if (!constraints || typeof constraints === "boolean" || Array.isArray(constraints) && !constraints.length) {
|
|
636
|
+
return NONE;
|
|
637
|
+
}
|
|
638
|
+
if (key === "device" && (isMediaDeviceInfo(constraints) || isMediaDeviceInfoArray(constraints))) {
|
|
639
|
+
return extractConstrainDevice(constraints);
|
|
640
|
+
}
|
|
641
|
+
if (isInputConstraintSet(constraints)) {
|
|
642
|
+
if (key === "device") {
|
|
643
|
+
return extractConstrainDevice(constraints);
|
|
644
|
+
}
|
|
645
|
+
if (isConstrainStringKeys(key) || isExtendedConstrainStringKeys(key)) {
|
|
646
|
+
return extractConstrainString(key)(constraints);
|
|
647
|
+
}
|
|
648
|
+
if (isConstrainDoubleKeys(key) || isConstrainULongKeys(key) || isExtendedConstrainULongKeys(key) || isExtendedConstrainDoubleKeys(key)) {
|
|
649
|
+
return extractConstrainNumber(key)(constraints);
|
|
650
|
+
}
|
|
651
|
+
if (isConstrainBooleanKeys(key) || isExtendedConstrainBooleanKeys(key)) {
|
|
652
|
+
return extractConstrainBoolean(key)(constraints);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return [void 0, "ideal"];
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
var extractConstraintsWithKeys = (keys) => (constraints) => keys.reduce((result, key) => {
|
|
659
|
+
if (key === "device") {
|
|
660
|
+
return { ...result, [key]: extractConstraints(key)(constraints) };
|
|
661
|
+
}
|
|
662
|
+
if (isConstrainULongKeys(key) || isConstrainDoubleKeys(key) || isExtendedConstrainULongKeys(key) || isExtendedConstrainDoubleKeys(key)) {
|
|
663
|
+
return { ...result, [key]: extractConstraints(key)(constraints) };
|
|
664
|
+
}
|
|
665
|
+
if (isConstrainStringKeys(key) || isExtendedConstrainStringKeys(key)) {
|
|
666
|
+
return { ...result, [key]: extractConstraints(key)(constraints) };
|
|
667
|
+
}
|
|
668
|
+
if (isConstrainBooleanKeys(key) || isExtendedConstrainBooleanKeys(key)) {
|
|
669
|
+
return { ...result, [key]: extractConstraints(key)(constraints) };
|
|
670
|
+
}
|
|
671
|
+
return result;
|
|
672
|
+
}, {});
|
|
673
|
+
var extractDeviceId = extractConstrainString("deviceId");
|
|
674
|
+
var findDeviceFromConstraints = (constraints, devices) => {
|
|
675
|
+
if (typeof constraints === "boolean") {
|
|
676
|
+
return constraints ? resolveOnlyDevice(devices) : constraints;
|
|
677
|
+
}
|
|
678
|
+
if (constraints === void 0 || !devices.length || devices.some((device) => !device.label) || Array.isArray(constraints) && !constraints.length) {
|
|
679
|
+
return void 0;
|
|
680
|
+
}
|
|
681
|
+
const [constrainDevices, deviceParam] = extractConstraints("device")(constraints);
|
|
682
|
+
if (constrainDevices) {
|
|
683
|
+
const [device] = findDeviceFrom(constrainDevices, devices) ?? [];
|
|
684
|
+
if (device) {
|
|
685
|
+
return device;
|
|
686
|
+
}
|
|
687
|
+
if (deviceParam === "exact") {
|
|
688
|
+
return void 0;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const [ConstrainDeviceIds, deviceIdParam] = extractConstraints("deviceId")(constraints);
|
|
692
|
+
if (ConstrainDeviceIds) {
|
|
693
|
+
const deviceFound = devices.find((device) => {
|
|
694
|
+
const id = ConstrainDeviceIds.find(
|
|
695
|
+
(id2) => id2 && device.label && device.deviceId === id2
|
|
696
|
+
);
|
|
697
|
+
return !!id;
|
|
698
|
+
});
|
|
699
|
+
if (deviceFound) {
|
|
700
|
+
return deviceFound;
|
|
701
|
+
}
|
|
702
|
+
if (deviceIdParam === "exact") {
|
|
703
|
+
return void 0;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return resolveOnlyDevice(devices);
|
|
707
|
+
};
|
|
708
|
+
var relaxInputConstraint = (input, devices) => {
|
|
709
|
+
if (devices.length === 0 || !input || typeof input === "boolean") {
|
|
710
|
+
return input;
|
|
711
|
+
}
|
|
712
|
+
const [device, param] = extractConstrainDevice(input);
|
|
713
|
+
const relaxedDevice = relaxDevice(device, devices);
|
|
714
|
+
const otherConstraints = isInputConstraintSet(input) ? input : {};
|
|
715
|
+
return relaxedDevice ? { ...otherConstraints, device: { [param]: relaxedDevice } } : input;
|
|
716
|
+
};
|
|
717
|
+
var getConstraintsHandlers = () => {
|
|
718
|
+
let defaultConstraints = {
|
|
719
|
+
audio: {
|
|
720
|
+
echoCancellation: { ideal: true },
|
|
721
|
+
noiseSuppression: { ideal: true }
|
|
722
|
+
},
|
|
723
|
+
video: {
|
|
724
|
+
frameRate: { ideal: 30 }
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
const setDefaultConstraints2 = (newConstraints) => {
|
|
728
|
+
defaultConstraints = { ...defaultConstraints, ...newConstraints };
|
|
729
|
+
};
|
|
730
|
+
const getDefaultConstraints = () => {
|
|
731
|
+
return defaultConstraints;
|
|
732
|
+
};
|
|
733
|
+
return {
|
|
734
|
+
getDefaultConstraints,
|
|
735
|
+
setDefaultConstraints: setDefaultConstraints2
|
|
736
|
+
};
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
// src/devices.ts
|
|
740
|
+
var SEPARATOR = ":";
|
|
741
|
+
var toKey = ({ id, kind, label }) => [kind, id, label].join(SEPARATOR);
|
|
742
|
+
var getDevices = async () => {
|
|
743
|
+
if ("mediaDevices" in navigator && "enumerateDevices" in navigator.mediaDevices) {
|
|
744
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
745
|
+
return devices;
|
|
746
|
+
}
|
|
747
|
+
return [];
|
|
748
|
+
};
|
|
749
|
+
var toDeviceKey = (device) => toKey({ id: device.deviceId, kind: device.kind, label: device.label });
|
|
750
|
+
var toDeviceTuple = (device) => [toDeviceKey(device), device];
|
|
751
|
+
var toDevicesMap = (devices) => {
|
|
752
|
+
return new Map(devices.map(toDeviceTuple));
|
|
753
|
+
};
|
|
754
|
+
var toUniqueDevices = (devices) => {
|
|
755
|
+
const map = toDevicesMap(devices);
|
|
756
|
+
return Array.from(map.values());
|
|
757
|
+
};
|
|
758
|
+
var createTrackDevicesChanges = (prevDevices = []) => {
|
|
759
|
+
let seen = toDevicesMap(prevDevices);
|
|
760
|
+
return (devices) => {
|
|
761
|
+
const found = [];
|
|
762
|
+
const lost = [];
|
|
763
|
+
const uniqueDevices = toUniqueDevices(devices);
|
|
764
|
+
const { authorized, unauthorized } = uniqueDevices.reduce(
|
|
765
|
+
(ds, d) => {
|
|
766
|
+
if (d.label) {
|
|
767
|
+
ds.authorized.push(d);
|
|
768
|
+
} else {
|
|
769
|
+
ds.unauthorized.push(d);
|
|
770
|
+
}
|
|
771
|
+
return ds;
|
|
772
|
+
},
|
|
773
|
+
{ authorized: [], unauthorized: [] }
|
|
774
|
+
);
|
|
775
|
+
for (const device of authorized) {
|
|
776
|
+
if (!seen.delete(toDeviceKey(device))) {
|
|
777
|
+
found.push(device);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
for (const device of seen.values()) {
|
|
781
|
+
lost.push(device);
|
|
782
|
+
}
|
|
783
|
+
seen = new Map(authorized.map(toDeviceTuple));
|
|
784
|
+
return { unauthorized, authorized, found, lost, devices: uniqueDevices };
|
|
785
|
+
};
|
|
786
|
+
};
|
|
787
|
+
var deviceChanged = (fn) => {
|
|
788
|
+
const trackChanges = createTrackDevicesChanges();
|
|
789
|
+
const odc = async () => {
|
|
790
|
+
const devices = await getDevices();
|
|
791
|
+
const changes = trackChanges(devices);
|
|
792
|
+
fn(changes);
|
|
793
|
+
};
|
|
794
|
+
if (navigator.mediaDevices && "ondevicechange" in navigator.mediaDevices) {
|
|
795
|
+
navigator.mediaDevices.ondevicechange = odc;
|
|
796
|
+
return () => {
|
|
797
|
+
navigator.mediaDevices.ondevicechange = null;
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
const it = window.setInterval(() => void odc(), 5e3);
|
|
801
|
+
return () => {
|
|
802
|
+
window.clearInterval(it);
|
|
803
|
+
};
|
|
804
|
+
};
|
|
805
|
+
var extractDeviceInfo = (track) => {
|
|
806
|
+
const { deviceId, groupId, ...settings } = track.getSettings();
|
|
807
|
+
const kind = track.kind === "audio" ? "audioinput" /* AUDIOINPUT */ : "videoinput" /* VIDEOINPUT */;
|
|
808
|
+
return {
|
|
809
|
+
kind,
|
|
810
|
+
deviceId: deviceId ?? "",
|
|
811
|
+
groupId: groupId ?? "",
|
|
812
|
+
label: track.label,
|
|
813
|
+
settings
|
|
814
|
+
};
|
|
815
|
+
};
|
|
816
|
+
var toMediaDeviceInfoLike = (track) => {
|
|
817
|
+
const { deviceId, label, kind, groupId } = extractDeviceInfo(track);
|
|
818
|
+
if (!deviceId) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
return { deviceId, label, kind, groupId };
|
|
822
|
+
};
|
|
823
|
+
var compareMediaDeviceToMediaTrack = (track) => (device) => {
|
|
824
|
+
const { deviceId } = track.getSettings();
|
|
825
|
+
const sameLabel = device.label === track.label;
|
|
826
|
+
const sameKind = device.kind.startsWith(track.kind);
|
|
827
|
+
if (deviceId) {
|
|
828
|
+
return sameKind && deviceId === device.deviceId;
|
|
829
|
+
}
|
|
830
|
+
return sameKind && sameLabel;
|
|
831
|
+
};
|
|
832
|
+
var toMediaDeviceInfoLikeJSON = (info) => ({
|
|
833
|
+
deviceId: info.deviceId,
|
|
834
|
+
groupId: info.groupId,
|
|
835
|
+
kind: info.kind,
|
|
836
|
+
label: info.label,
|
|
837
|
+
settings: info.settings
|
|
838
|
+
});
|
|
839
|
+
var findMediaInputFromMediaStreamTrack = (devices) => (track) => {
|
|
840
|
+
if (!devices.length || !track) {
|
|
841
|
+
return void 0;
|
|
842
|
+
}
|
|
843
|
+
const settings = track.getSettings();
|
|
844
|
+
const devicesWithSameKind = devices.filter(
|
|
845
|
+
(d) => d.kind === `${track.kind}input`
|
|
846
|
+
);
|
|
847
|
+
const onlyDevice = devicesWithSameKind[0];
|
|
848
|
+
if (devicesWithSameKind.length === 1 && onlyDevice) {
|
|
849
|
+
onlyDevice.settings = settings;
|
|
850
|
+
onlyDevice.toJSON = () => toMediaDeviceInfoLikeJSON(onlyDevice);
|
|
851
|
+
return onlyDevice;
|
|
852
|
+
}
|
|
853
|
+
const device = devicesWithSameKind.find(
|
|
854
|
+
compareMediaDeviceToMediaTrack(track)
|
|
855
|
+
);
|
|
856
|
+
if (device) {
|
|
857
|
+
device.settings = settings;
|
|
858
|
+
device.toJSON = () => toMediaDeviceInfoLikeJSON(device);
|
|
859
|
+
return device;
|
|
860
|
+
}
|
|
861
|
+
return device;
|
|
862
|
+
};
|
|
863
|
+
var findMediaInputFromStream = (devices) => (stream) => {
|
|
864
|
+
if (!stream || !devices.length) {
|
|
865
|
+
return {};
|
|
866
|
+
}
|
|
867
|
+
const findMediaInput = findMediaInputFromMediaStreamTrack(devices);
|
|
868
|
+
return {
|
|
869
|
+
audioInput: findMediaInput(...stream.getAudioTracks()),
|
|
870
|
+
videoInput: findMediaInput(...stream.getVideoTracks())
|
|
871
|
+
};
|
|
872
|
+
};
|
|
873
|
+
var compareDeviceConstraintAndTrackDevice = (device) => (constraint) => {
|
|
874
|
+
if (!device || !constraint) {
|
|
875
|
+
return Boolean(device) === Boolean(constraint);
|
|
876
|
+
}
|
|
877
|
+
const normalized = normalizeDeviceConstraint(constraint);
|
|
878
|
+
if (normalized) {
|
|
879
|
+
const requestingDevices = Object.values(
|
|
880
|
+
normalized
|
|
881
|
+
).flatMap((t) => Array.isArray(t) ? t : [t]);
|
|
882
|
+
const compare = compareDevices(
|
|
883
|
+
device,
|
|
884
|
+
device.label ? "label" : "deviceId"
|
|
885
|
+
);
|
|
886
|
+
return requestingDevices.some(compare);
|
|
887
|
+
}
|
|
888
|
+
return false;
|
|
889
|
+
};
|
|
890
|
+
var isRequestedResolution = (request, response) => {
|
|
891
|
+
if (!response || !request) {
|
|
892
|
+
return Boolean(response) === Boolean(request);
|
|
893
|
+
}
|
|
894
|
+
if (isInputConstraintSet(request)) {
|
|
895
|
+
const [width] = extractConstrainNumber("width")(request);
|
|
896
|
+
const [height] = extractConstrainNumber("height")(request);
|
|
897
|
+
const { width: responseWidth = 0, height: responseHeight = 0 } = response.settings ?? {};
|
|
898
|
+
const portrait = responseHeight >= responseWidth;
|
|
899
|
+
const resultWidth = portrait ? responseHeight : responseWidth;
|
|
900
|
+
const resultHeight = portrait ? responseWidth : responseHeight;
|
|
901
|
+
const sameWidth = satisfyConstrainNumber(width, resultWidth);
|
|
902
|
+
const sameHeight = satisfyConstrainNumber(height, resultHeight);
|
|
903
|
+
return sameWidth && sameHeight;
|
|
904
|
+
}
|
|
905
|
+
return true;
|
|
906
|
+
};
|
|
907
|
+
var isRequestedInputDevice = (request, response) => {
|
|
908
|
+
if (!response || !request) {
|
|
909
|
+
return Boolean(response) === Boolean(request);
|
|
910
|
+
}
|
|
911
|
+
const compareDevice = compareDeviceConstraintAndTrackDevice(response);
|
|
912
|
+
if (isInputConstraintSet(request)) {
|
|
913
|
+
if (isConstraintSetDevice(request.device)) {
|
|
914
|
+
const isSameDevice = compareDevice(request.device);
|
|
915
|
+
return isSameDevice;
|
|
916
|
+
}
|
|
917
|
+
if (request.deviceId) {
|
|
918
|
+
const { deviceId } = response;
|
|
919
|
+
if (!deviceId) {
|
|
920
|
+
return true;
|
|
921
|
+
}
|
|
922
|
+
if (isConstraintDOMString(request.deviceId)) {
|
|
923
|
+
const ids = toArray(request.deviceId);
|
|
924
|
+
return ids.some((id) => deviceId === id);
|
|
925
|
+
}
|
|
926
|
+
if (isConstrainDOMStringParameters(request.deviceId)) {
|
|
927
|
+
return Object.values(request.deviceId).flatMap(toArray).some((id) => id === deviceId);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const facingMode = response.settings?.facingMode;
|
|
931
|
+
if (request.facingMode && facingMode) {
|
|
932
|
+
const [facingModes] = extractConstrainString("facingMode")(request);
|
|
933
|
+
return facingModes?.some((fm) => fm === facingMode) ?? true;
|
|
934
|
+
}
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
937
|
+
if (isConstraintSetDevice(request)) {
|
|
938
|
+
return compareDevice(request);
|
|
939
|
+
}
|
|
940
|
+
return !!response;
|
|
941
|
+
};
|
|
942
|
+
var isRequestedInputTrack = (request, current) => {
|
|
943
|
+
if (!request || !current) {
|
|
944
|
+
return Boolean(request) === Boolean(current);
|
|
945
|
+
}
|
|
946
|
+
if (isMediaDeviceInfo(current)) {
|
|
947
|
+
return isRequestedInputDevice(request, current);
|
|
948
|
+
}
|
|
949
|
+
if (isMediaStreamTrack(current)) {
|
|
950
|
+
const device = extractDeviceInfo(current);
|
|
951
|
+
return isRequestedInputDevice(request, device);
|
|
952
|
+
}
|
|
953
|
+
return true;
|
|
954
|
+
};
|
|
955
|
+
var extractDevice = extractConstraintsWithKeys(["device", "deviceId"]);
|
|
956
|
+
var hasRequestingDevice = (request, kind, currentDevices) => {
|
|
957
|
+
if (!request) {
|
|
958
|
+
return false;
|
|
959
|
+
}
|
|
960
|
+
const {
|
|
961
|
+
device: [devices],
|
|
962
|
+
deviceId: [deviceIds]
|
|
963
|
+
} = extractDevice(request);
|
|
964
|
+
if ((!devices || devices.length === 0) && (!deviceIds || deviceIds.length === 0)) {
|
|
965
|
+
return currentDevices.some((device) => device.kind === kind);
|
|
966
|
+
}
|
|
967
|
+
const find = (device) => findDevice(device)(currentDevices);
|
|
968
|
+
return devices?.some((device) => find(device)) ?? deviceIds?.some(
|
|
969
|
+
(deviceId) => find({ kind, deviceId, label: "", groupId: "" })
|
|
970
|
+
) ?? false;
|
|
971
|
+
};
|
|
972
|
+
var shouldRequestDevice = (request, tracks, currentDevices) => {
|
|
973
|
+
if (!request || !currentDevices.some((device) => device.label)) {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
const { kind } = currentDevices[0] ?? {};
|
|
977
|
+
if (!kind || currentDevices.some((device) => device.kind !== kind)) {
|
|
978
|
+
throw new Error("Expect a single kind of device");
|
|
979
|
+
}
|
|
980
|
+
const [track] = tracks;
|
|
981
|
+
if (tracks.length === 0 || tracks.some((track2) => track2.readyState === "ended")) {
|
|
982
|
+
return true;
|
|
983
|
+
}
|
|
984
|
+
if (isRequestedInputTrack(request, track)) {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
if (hasRequestingDevice(request, kind, currentDevices)) {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
return false;
|
|
991
|
+
};
|
|
992
|
+
var resolveInputDevice = (tracksOrDevice, findInput) => {
|
|
993
|
+
if (isMediaDeviceInfo(tracksOrDevice)) {
|
|
994
|
+
return tracksOrDevice;
|
|
995
|
+
}
|
|
996
|
+
if (Array.isArray(tracksOrDevice)) {
|
|
997
|
+
const [track] = tracksOrDevice;
|
|
998
|
+
if (track?.readyState === "live") {
|
|
999
|
+
return findInput(track) ?? track;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return void 0;
|
|
1003
|
+
};
|
|
1004
|
+
var isStreamingRequestedDevicesBase = (request, tracksOrDevices, devices) => {
|
|
1005
|
+
const audioRequest = relaxInputConstraint(request.audio, devices);
|
|
1006
|
+
const videoRequest = relaxInputConstraint(request.video, devices);
|
|
1007
|
+
const findInput = findMediaInputFromMediaStreamTrack(devices);
|
|
1008
|
+
const audioInput = resolveInputDevice(tracksOrDevices.audio, findInput);
|
|
1009
|
+
const videoInput = resolveInputDevice(tracksOrDevices.video, findInput);
|
|
1010
|
+
const audio = isRequestedInputTrack(audioRequest, audioInput);
|
|
1011
|
+
const video = isRequestedInputTrack(videoRequest, videoInput);
|
|
1012
|
+
return { audio, video };
|
|
1013
|
+
};
|
|
1014
|
+
var isStreamingRequestedDevices = (request, stream, devices) => isStreamingRequestedDevicesBase(
|
|
1015
|
+
request,
|
|
1016
|
+
{
|
|
1017
|
+
audio: stream?.getAudioTracks(),
|
|
1018
|
+
video: stream?.getVideoTracks()
|
|
1019
|
+
},
|
|
1020
|
+
devices
|
|
1021
|
+
);
|
|
1022
|
+
var findPermissionGrantedDevices = (devices) => devices.filter(isDeviceGranted);
|
|
1023
|
+
var toPermissionState = (devices, anyActiveStream = false) => {
|
|
1024
|
+
const granted = devices.some((device) => device.label);
|
|
1025
|
+
if (anyActiveStream) {
|
|
1026
|
+
return granted ? "granted" : "denied";
|
|
1027
|
+
}
|
|
1028
|
+
return granted ? "granted" : "prompt";
|
|
1029
|
+
};
|
|
1030
|
+
var getInputDevicePermissionState = async (anyActiveStream = false) => {
|
|
1031
|
+
try {
|
|
1032
|
+
const video = await navigator.permissions.query({ name: "camera" });
|
|
1033
|
+
const audio = await navigator.permissions.query({ name: "microphone" });
|
|
1034
|
+
return { video: video.state, audio: audio.state };
|
|
1035
|
+
} catch {
|
|
1036
|
+
const devices = await getDevices();
|
|
1037
|
+
return ["videoinput", "audioinput"].reduce(
|
|
1038
|
+
(permission, kind) => {
|
|
1039
|
+
const state2 = toPermissionState(
|
|
1040
|
+
devices.filter((device) => device.kind === kind),
|
|
1041
|
+
anyActiveStream
|
|
1042
|
+
);
|
|
1043
|
+
permission[kind === "audioinput" ? "audio" : "video"] = state2;
|
|
1044
|
+
return permission;
|
|
1045
|
+
},
|
|
1046
|
+
{ video: "prompt", audio: "prompt" }
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
var findCurrentAudioOutputId = (audioOutput, devices) => {
|
|
1051
|
+
let selectedAudioOutputDeviceId = "";
|
|
1052
|
+
if (audioOutput?.deviceId && devices) {
|
|
1053
|
+
const latestSelectedAudioInput = findDevice(audioOutput)(devices);
|
|
1054
|
+
if (latestSelectedAudioInput) {
|
|
1055
|
+
selectedAudioOutputDeviceId = latestSelectedAudioInput.deviceId;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return selectedAudioOutputDeviceId;
|
|
1059
|
+
};
|
|
1060
|
+
var findCurrentVideoInputDeviceIdFromStream = (stream) => {
|
|
1061
|
+
const [videoTrack] = stream.getVideoTracks();
|
|
1062
|
+
return videoTrack && toMediaDeviceInfoLike(videoTrack)?.deviceId;
|
|
1063
|
+
};
|
|
1064
|
+
var findDeviceWithDeviceId = (devices, deviceId) => devices.find((d) => d.deviceId === deviceId);
|
|
1065
|
+
var findDevicesByKind = (kind) => (devices) => devices.filter(isSameDeviceKind(kind));
|
|
1066
|
+
var findAudioInputDevices = findDevicesByKind(
|
|
1067
|
+
"audioinput" /* AUDIOINPUT */
|
|
1068
|
+
);
|
|
1069
|
+
var findVideoInputDevices = findDevicesByKind(
|
|
1070
|
+
"videoinput" /* VIDEOINPUT */
|
|
1071
|
+
);
|
|
1072
|
+
var findAudioOutputDevices = findDevicesByKind(
|
|
1073
|
+
"audiooutput" /* AUDIOOUTPUT */
|
|
1074
|
+
);
|
|
1075
|
+
var muteStreamTrack = (stream) => (mute, mediaType = "all") => {
|
|
1076
|
+
switch (mediaType) {
|
|
1077
|
+
case "audio":
|
|
1078
|
+
stream?.getAudioTracks().forEach((track) => {
|
|
1079
|
+
track.enabled = !mute;
|
|
1080
|
+
});
|
|
1081
|
+
break;
|
|
1082
|
+
case "video":
|
|
1083
|
+
stream?.getVideoTracks().forEach((track) => {
|
|
1084
|
+
track.enabled = !mute;
|
|
1085
|
+
});
|
|
1086
|
+
break;
|
|
1087
|
+
default:
|
|
1088
|
+
stream?.getTracks().forEach((track) => {
|
|
1089
|
+
track.enabled = !mute;
|
|
1090
|
+
});
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
var stopMediaStream = (stream, onStopped) => {
|
|
1095
|
+
if (!stream) {
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
stream.getTracks().forEach((track) => {
|
|
1099
|
+
track.stop();
|
|
1100
|
+
onStopped?.(track);
|
|
1101
|
+
});
|
|
1102
|
+
};
|
|
1103
|
+
var areTracksEnabled = (stream, type) => {
|
|
1104
|
+
if (!stream) {
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
const tracks = type === "audio" ? stream.getAudioTracks() : stream.getVideoTracks();
|
|
1108
|
+
return tracks.length > 0 && tracks.every((track) => track.enabled);
|
|
1109
|
+
};
|
|
1110
|
+
var hasAudioOrVideoInputs = (devices) => devices.some(not(isAudioOutput));
|
|
1111
|
+
var hasAudioInputs = (devices) => devices.some(isAudioInput);
|
|
1112
|
+
var hasVideoInputs = (devices) => devices.some(isVideoInput);
|
|
1113
|
+
var hasAnyGrantedInput = (devices) => devices.some(
|
|
1114
|
+
(device) => isDeviceGranted(device) && (isVideoInput(device) || isAudioInput(device))
|
|
1115
|
+
);
|
|
1116
|
+
var hasAnyInputs = (devices, grantedOnly = false) => {
|
|
1117
|
+
let anyAudioDevices = false;
|
|
1118
|
+
let anyVideoDevices = false;
|
|
1119
|
+
for (const device of devices) {
|
|
1120
|
+
if (isAudioOutput(device) || grantedOnly && !isDeviceGranted(device)) {
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (isAudioInput(device)) {
|
|
1124
|
+
anyAudioDevices = true;
|
|
1125
|
+
}
|
|
1126
|
+
if (isVideoInput(device)) {
|
|
1127
|
+
anyVideoDevices = true;
|
|
1128
|
+
}
|
|
1129
|
+
if (anyVideoDevices && anyAudioDevices) {
|
|
1130
|
+
return [true, true];
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return [anyAudioDevices, anyVideoDevices];
|
|
1134
|
+
};
|
|
1135
|
+
var hasChangedInput = (oldInput, newInput) => {
|
|
1136
|
+
if (isMediaDeviceInfo(newInput)) {
|
|
1137
|
+
if (isMediaDeviceInfo(oldInput) && !compareDevices(oldInput)(newInput) || !oldInput) {
|
|
1138
|
+
return true;
|
|
1139
|
+
}
|
|
1140
|
+
} else if (oldInput) {
|
|
1141
|
+
return true;
|
|
1142
|
+
}
|
|
1143
|
+
return false;
|
|
1144
|
+
};
|
|
1145
|
+
var areMultipleFacingModeSupportted = (currentDevices, getSupportedConstraints = () => navigator.mediaDevices.getSupportedConstraints()) => {
|
|
1146
|
+
if (!getSupportedConstraints().facingMode) {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
const set = /* @__PURE__ */ new Set();
|
|
1150
|
+
const hasFrontAndBackCameras = currentDevices.some((device) => {
|
|
1151
|
+
if (device.kind !== "videoinput") {
|
|
1152
|
+
return false;
|
|
1153
|
+
}
|
|
1154
|
+
if (device.label.match(/front/i)) {
|
|
1155
|
+
set.add("front");
|
|
1156
|
+
}
|
|
1157
|
+
if (device.label.match(/(back|rear)/i)) {
|
|
1158
|
+
set.add("back");
|
|
1159
|
+
}
|
|
1160
|
+
return set.size > 1;
|
|
1161
|
+
});
|
|
1162
|
+
if (!hasFrontAndBackCameras) {
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
return true;
|
|
1166
|
+
};
|
|
1167
|
+
var interpretCurrentFacingMode = (currentTrack) => {
|
|
1168
|
+
if (!currentTrack || currentTrack.kind !== "video") {
|
|
1169
|
+
return void 0;
|
|
1170
|
+
}
|
|
1171
|
+
const settings = currentTrack.getSettings();
|
|
1172
|
+
if ("getCapabilities" in currentTrack) {
|
|
1173
|
+
const capabilities = currentTrack.getCapabilities();
|
|
1174
|
+
if (capabilities.facingMode && capabilities.facingMode.length > 0) {
|
|
1175
|
+
return settings.facingMode;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
if (settings.facingMode) {
|
|
1179
|
+
return settings.facingMode;
|
|
1180
|
+
}
|
|
1181
|
+
if (currentTrack.label.match(/front/i)) {
|
|
1182
|
+
return "user";
|
|
1183
|
+
}
|
|
1184
|
+
if (currentTrack.label.match(/(back|rear)/i)) {
|
|
1185
|
+
return "environment";
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
// src/eventEmitter.ts
|
|
1190
|
+
var MediaEventType = /* @__PURE__ */ ((MediaEventType2) => {
|
|
1191
|
+
MediaEventType2["Mute"] = "mute";
|
|
1192
|
+
MediaEventType2["Unmute"] = "unmute";
|
|
1193
|
+
MediaEventType2["Ended"] = "ended";
|
|
1194
|
+
MediaEventType2["DevicesChanged"] = "devices:changed";
|
|
1195
|
+
MediaEventType2["DevicesFound"] = "devices:found";
|
|
1196
|
+
MediaEventType2["DevicesLost"] = "devices:lost";
|
|
1197
|
+
MediaEventType2["DeviceLost"] = "device:lost";
|
|
1198
|
+
MediaEventType2["DevicesUnauthorized"] = "devices:unauthorized";
|
|
1199
|
+
MediaEventType2["NoInputDevices"] = "devices:noinput";
|
|
1200
|
+
MediaEventType2["Error"] = "error";
|
|
1201
|
+
MediaEventType2["Stream"] = "stream";
|
|
1202
|
+
return MediaEventType2;
|
|
1203
|
+
})(MediaEventType || {});
|
|
1204
|
+
var eventEmitter = () => {
|
|
1205
|
+
const element = document.createElement("a");
|
|
1206
|
+
const createEvent = (data) => {
|
|
1207
|
+
return new CustomEvent("data", { detail: data });
|
|
1208
|
+
};
|
|
1209
|
+
return {
|
|
1210
|
+
dispatch(event) {
|
|
1211
|
+
element.dispatchEvent(createEvent(event));
|
|
1212
|
+
},
|
|
1213
|
+
subscriber(listener, onUnsubscribe) {
|
|
1214
|
+
element.addEventListener(
|
|
1215
|
+
"data",
|
|
1216
|
+
listener,
|
|
1217
|
+
false
|
|
1218
|
+
);
|
|
1219
|
+
return () => {
|
|
1220
|
+
element.removeEventListener(
|
|
1221
|
+
"data",
|
|
1222
|
+
listener,
|
|
1223
|
+
false
|
|
1224
|
+
);
|
|
1225
|
+
onUnsubscribe();
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1231
|
+
// src/errors.ts
|
|
1232
|
+
var isDeviceInUseError = (error) => {
|
|
1233
|
+
return error === "NotReadableError" /* NotReadableError */ || error === "TrackStartError" /* TrackStartError */;
|
|
1234
|
+
};
|
|
1235
|
+
var isPermissionDeniedError = (error) => {
|
|
1236
|
+
return error === "NotAllowedError" /* NotAllowedError */ || error === "PermissionDeniedError" /* PermissionDeniedError */;
|
|
1237
|
+
};
|
|
1238
|
+
var isInputNotFoundError = ({ input, kind, devices }) => (error) => {
|
|
1239
|
+
if (error === "NotFoundError" /* NotFoundError */) {
|
|
1240
|
+
const hasDevices = devices.some((d) => d.kind === kind);
|
|
1241
|
+
if (typeof input === "boolean") {
|
|
1242
|
+
if (!hasDevices && input) {
|
|
1243
|
+
return true;
|
|
1244
|
+
}
|
|
1245
|
+
return input && !hasDevices;
|
|
1246
|
+
}
|
|
1247
|
+
if (isMediaTrackConstraints(input)) {
|
|
1248
|
+
const [deviceIds, requirement] = extractDeviceId(input);
|
|
1249
|
+
if (requirement === "ideal") {
|
|
1250
|
+
return !hasDevices;
|
|
1251
|
+
}
|
|
1252
|
+
if (requirement === "exact") {
|
|
1253
|
+
return !devices.some(
|
|
1254
|
+
(device) => deviceIds?.some(
|
|
1255
|
+
(id) => device.kind === kind && id === device.deviceId
|
|
1256
|
+
)
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
return false;
|
|
1262
|
+
};
|
|
1263
|
+
var normalizeError = (errors, parameters = []) => {
|
|
1264
|
+
const error = errors.find((e) => e.fn(...parameters));
|
|
1265
|
+
if (error) {
|
|
1266
|
+
return error.type;
|
|
1267
|
+
}
|
|
1268
|
+
return false;
|
|
1269
|
+
};
|
|
1270
|
+
var normalizeGetUserMediaError = (browserError, constraints, devices) => {
|
|
1271
|
+
const isAudioInputNotFoundError = isInputNotFoundError({
|
|
1272
|
+
input: constraints.audio,
|
|
1273
|
+
kind: "audioinput",
|
|
1274
|
+
devices
|
|
1275
|
+
});
|
|
1276
|
+
const isVideoInputNotFoundError = isInputNotFoundError({
|
|
1277
|
+
input: constraints.video,
|
|
1278
|
+
kind: "videoinput",
|
|
1279
|
+
devices
|
|
1280
|
+
});
|
|
1281
|
+
const areBothInputsNotFoundError = (error2) => isAudioInputNotFoundError(error2) && isVideoInputNotFoundError(error2);
|
|
1282
|
+
const errorsFn = [
|
|
1283
|
+
{
|
|
1284
|
+
fn: isDeviceInUseError,
|
|
1285
|
+
type: "NotReadableError" /* NotReadableError */
|
|
1286
|
+
// For now just normalize to current spec
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
fn: isPermissionDeniedError,
|
|
1290
|
+
type: "NotAllowedError" /* NotAllowedError */
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
fn: areBothInputsNotFoundError,
|
|
1294
|
+
type: "AudioAndVideoDeviceNotFoundError" /* AudioAndVideoDeviceNotFoundError */
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
fn: isAudioInputNotFoundError,
|
|
1298
|
+
type: "AudioInputDeviceNotFoundError" /* AudioInputDeviceNotFoundError */
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
fn: isVideoInputNotFoundError,
|
|
1302
|
+
type: "VideoInputDeviceNotFoundError" /* VideoInputDeviceNotFoundError */
|
|
1303
|
+
}
|
|
1304
|
+
];
|
|
1305
|
+
const errorMsg = browserError.name === "Error" ? browserError.message : browserError.name;
|
|
1306
|
+
const error = normalizeError(errorsFn, [errorMsg]);
|
|
1307
|
+
return error ? error : errorMsg;
|
|
1308
|
+
};
|
|
1309
|
+
|
|
1310
|
+
// src/getMediaStream.ts
|
|
1311
|
+
var getMediaStream = async (request) => {
|
|
1312
|
+
if (!request.audio && !request.video) {
|
|
1313
|
+
throw new Error("MissingConstraintsError" /* MissingConstraintsError */);
|
|
1314
|
+
}
|
|
1315
|
+
const devices = await getDevices();
|
|
1316
|
+
const audio = relaxInputConstraint(request.audio, devices);
|
|
1317
|
+
const video = relaxInputConstraint(request.video, devices);
|
|
1318
|
+
const constraints = getMediaConstraints({
|
|
1319
|
+
audio,
|
|
1320
|
+
video,
|
|
1321
|
+
defaultConstraints: request.getDefaultConstraints()
|
|
1322
|
+
});
|
|
1323
|
+
logger.debug(
|
|
1324
|
+
{ request, constraints, devices },
|
|
1325
|
+
"Constraints used for getting media"
|
|
1326
|
+
);
|
|
1327
|
+
try {
|
|
1328
|
+
navigator.mediaDevices.dispatchEvent(new Event("devicechange"));
|
|
1329
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
1330
|
+
navigator.mediaDevices.dispatchEvent(new Event("devicechange"));
|
|
1331
|
+
return stream;
|
|
1332
|
+
} catch (error) {
|
|
1333
|
+
if (error instanceof Error) {
|
|
1334
|
+
const devices2 = await getDevices();
|
|
1335
|
+
throw new Error(
|
|
1336
|
+
normalizeGetUserMediaError(error, constraints, devices2)
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
throw error;
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
// src/streams.ts
|
|
1344
|
+
var handleMediaStream = ({
|
|
1345
|
+
dispatch,
|
|
1346
|
+
getDefaultConstraints,
|
|
1347
|
+
streamTrackMap
|
|
1348
|
+
}) => {
|
|
1349
|
+
const setupMediaStreamTracks = ({ stream }) => {
|
|
1350
|
+
let userFacingMode = false;
|
|
1351
|
+
for (const track of stream.getTracks()) {
|
|
1352
|
+
const { facingMode } = track.getSettings();
|
|
1353
|
+
if (track) {
|
|
1354
|
+
streamTrackMap.add(track);
|
|
1355
|
+
}
|
|
1356
|
+
if (track.kind === "video" && isBoolean(facingMode)) {
|
|
1357
|
+
userFacingMode = facingMode;
|
|
1358
|
+
}
|
|
1359
|
+
const mutedListener = () => {
|
|
1360
|
+
dispatch({ id: track.id, type: "mute" /* Mute */ });
|
|
1361
|
+
};
|
|
1362
|
+
track.addEventListener("mute" /* Mute */, mutedListener, false);
|
|
1363
|
+
const unmutedListener = () => {
|
|
1364
|
+
dispatch({ id: track.id, type: "unmute" /* Unmute */ });
|
|
1365
|
+
};
|
|
1366
|
+
track.addEventListener(
|
|
1367
|
+
"unmute" /* Unmute */,
|
|
1368
|
+
unmutedListener,
|
|
1369
|
+
false
|
|
1370
|
+
);
|
|
1371
|
+
const endedListener = () => {
|
|
1372
|
+
dispatch({ id: track.id, type: "ended" /* Ended */ });
|
|
1373
|
+
track.removeEventListener(
|
|
1374
|
+
"ended" /* Ended */,
|
|
1375
|
+
endedListener,
|
|
1376
|
+
false
|
|
1377
|
+
);
|
|
1378
|
+
track.removeEventListener(
|
|
1379
|
+
"mute" /* Mute */,
|
|
1380
|
+
mutedListener,
|
|
1381
|
+
false
|
|
1382
|
+
);
|
|
1383
|
+
track.removeEventListener(
|
|
1384
|
+
"unmute" /* Unmute */,
|
|
1385
|
+
unmutedListener,
|
|
1386
|
+
false
|
|
1387
|
+
);
|
|
1388
|
+
if (streamTrackMap.has(track)) {
|
|
1389
|
+
streamTrackMap.remove(track);
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
track.addEventListener("ended", endedListener, true);
|
|
1393
|
+
}
|
|
1394
|
+
dispatch({
|
|
1395
|
+
facingMode: userFacingMode,
|
|
1396
|
+
stream,
|
|
1397
|
+
type: "stream" /* Stream */,
|
|
1398
|
+
video: stream.getVideoTracks().length > 0
|
|
1399
|
+
});
|
|
1400
|
+
};
|
|
1401
|
+
const getUserMedia2 = async ({
|
|
1402
|
+
audio,
|
|
1403
|
+
video
|
|
1404
|
+
}) => {
|
|
1405
|
+
try {
|
|
1406
|
+
const stream = await getMediaStream({
|
|
1407
|
+
audio,
|
|
1408
|
+
video,
|
|
1409
|
+
getDefaultConstraints
|
|
1410
|
+
});
|
|
1411
|
+
setupMediaStreamTracks({ stream });
|
|
1412
|
+
return stream;
|
|
1413
|
+
} catch (error) {
|
|
1414
|
+
if (error instanceof Error) {
|
|
1415
|
+
dispatch({ type: "error" /* Error */, error });
|
|
1416
|
+
}
|
|
1417
|
+
throw error;
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
return { getUserMedia: getUserMedia2 };
|
|
1421
|
+
};
|
|
1422
|
+
var createStreamTrackEventSubscriptions = (track, handlers) => {
|
|
1423
|
+
const trackSubscriptions = Object.keys(handlers).flatMap((eventKey) => {
|
|
1424
|
+
const key = eventKey;
|
|
1425
|
+
const trackEventHandler = handlers[key];
|
|
1426
|
+
if (trackEventHandler) {
|
|
1427
|
+
const handleEvent = () => trackEventHandler(track);
|
|
1428
|
+
track.addEventListener(key, handleEvent);
|
|
1429
|
+
const removeTrackEventHandler = () => {
|
|
1430
|
+
track.removeEventListener(key, handleEvent);
|
|
1431
|
+
};
|
|
1432
|
+
return [removeTrackEventHandler];
|
|
1433
|
+
}
|
|
1434
|
+
return [];
|
|
1435
|
+
});
|
|
1436
|
+
return () => {
|
|
1437
|
+
trackSubscriptions.forEach((unsubscribeEvent) => unsubscribeEvent());
|
|
1438
|
+
};
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// src/streamTrackMap.ts
|
|
1442
|
+
var createStreamTrackMap = (tracks) => {
|
|
1443
|
+
const streamTrackMap = new Map(
|
|
1444
|
+
tracks?.map((track) => [toKey(track), track])
|
|
1445
|
+
);
|
|
1446
|
+
const findTrack = (device) => {
|
|
1447
|
+
return [...streamTrackMap].map(([_, track]) => track).find((track) => {
|
|
1448
|
+
const { kind } = track;
|
|
1449
|
+
const { deviceId } = track.getSettings();
|
|
1450
|
+
if (!deviceId) {
|
|
1451
|
+
return false;
|
|
1452
|
+
}
|
|
1453
|
+
return device.kind.includes(kind) && device.deviceId === deviceId;
|
|
1454
|
+
});
|
|
1455
|
+
};
|
|
1456
|
+
const toStreamTrackKey = (deviceOrTrack) => {
|
|
1457
|
+
const track = isMediaStreamTrack(deviceOrTrack) ? deviceOrTrack : findTrack(deviceOrTrack);
|
|
1458
|
+
if (!track?.kind || !track.id) {
|
|
1459
|
+
return "";
|
|
1460
|
+
}
|
|
1461
|
+
return toKey(track);
|
|
1462
|
+
};
|
|
1463
|
+
const add = (track) => {
|
|
1464
|
+
const key = toStreamTrackKey(track);
|
|
1465
|
+
if (key) {
|
|
1466
|
+
return streamTrackMap.set(key, track);
|
|
1467
|
+
}
|
|
1468
|
+
throw new Error("StreamTrackNotFound" /* StreamTrackNotFound */);
|
|
1469
|
+
};
|
|
1470
|
+
const has = (deviceOrTrack) => {
|
|
1471
|
+
const key = toStreamTrackKey(deviceOrTrack);
|
|
1472
|
+
return key && streamTrackMap.has(key);
|
|
1473
|
+
};
|
|
1474
|
+
const clear = () => {
|
|
1475
|
+
streamTrackMap.forEach((track) => {
|
|
1476
|
+
track.stop();
|
|
1477
|
+
});
|
|
1478
|
+
streamTrackMap.clear();
|
|
1479
|
+
};
|
|
1480
|
+
const remove = (track) => {
|
|
1481
|
+
const key = toStreamTrackKey(track);
|
|
1482
|
+
track.stop();
|
|
1483
|
+
return streamTrackMap.delete(key);
|
|
1484
|
+
};
|
|
1485
|
+
const size = () => streamTrackMap.size;
|
|
1486
|
+
return {
|
|
1487
|
+
add,
|
|
1488
|
+
has,
|
|
1489
|
+
clear,
|
|
1490
|
+
remove,
|
|
1491
|
+
size
|
|
1492
|
+
};
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
// src/index.ts
|
|
1496
|
+
var state = () => {
|
|
1497
|
+
const { dispatch, subscriber } = eventEmitter();
|
|
1498
|
+
const streamTrackMap = createStreamTrackMap();
|
|
1499
|
+
const { getDefaultConstraints, setDefaultConstraints: setDefaultConstraints2 } = getConstraintsHandlers();
|
|
1500
|
+
const { getUserMedia: getUserMedia2 } = handleMediaStream({
|
|
1501
|
+
dispatch,
|
|
1502
|
+
getDefaultConstraints,
|
|
1503
|
+
streamTrackMap
|
|
1504
|
+
});
|
|
1505
|
+
const subscribe2 = (listener) => {
|
|
1506
|
+
return subscriber(
|
|
1507
|
+
listener,
|
|
1508
|
+
deviceChanged((event) => {
|
|
1509
|
+
dispatch({
|
|
1510
|
+
type: "devices:changed" /* DevicesChanged */,
|
|
1511
|
+
devices: event.devices
|
|
1512
|
+
});
|
|
1513
|
+
if (!hasAudioOrVideoInputs(event.devices)) {
|
|
1514
|
+
return dispatch({
|
|
1515
|
+
type: "devices:noinput" /* NoInputDevices */,
|
|
1516
|
+
devices: event.devices
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
if (event.unauthorized.length) {
|
|
1520
|
+
dispatch({
|
|
1521
|
+
type: "devices:unauthorized" /* DevicesUnauthorized */,
|
|
1522
|
+
devices: event.unauthorized,
|
|
1523
|
+
authorizedDevices: event.authorized
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
if (event.found.length) {
|
|
1527
|
+
dispatch({
|
|
1528
|
+
type: "devices:found" /* DevicesFound */,
|
|
1529
|
+
authorizedDevices: event.authorized,
|
|
1530
|
+
unauthorizedDevices: event.unauthorized,
|
|
1531
|
+
devices: event.found
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
if (event.lost.length) {
|
|
1535
|
+
dispatch({
|
|
1536
|
+
type: "devices:lost" /* DevicesLost */,
|
|
1537
|
+
authorizedDevices: event.authorized,
|
|
1538
|
+
unauthorizedDevices: event.unauthorized,
|
|
1539
|
+
devices: event.lost
|
|
1540
|
+
});
|
|
1541
|
+
for (const device of event.lost) {
|
|
1542
|
+
if (streamTrackMap.has(device)) {
|
|
1543
|
+
dispatch({ type: "device:lost" /* DeviceLost */, device });
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
})
|
|
1548
|
+
);
|
|
1549
|
+
};
|
|
1550
|
+
return {
|
|
1551
|
+
getUserMedia: getUserMedia2,
|
|
1552
|
+
setDefaultConstraints: setDefaultConstraints2,
|
|
1553
|
+
subscribe: subscribe2
|
|
1554
|
+
};
|
|
1555
|
+
};
|
|
1556
|
+
var {
|
|
1557
|
+
/**
|
|
1558
|
+
* Get MediaStream with provided {@link MediaDeviceRequest | input constraints}
|
|
1559
|
+
*/
|
|
1560
|
+
getUserMedia,
|
|
1561
|
+
/**
|
|
1562
|
+
* Set default media stream constraints
|
|
1563
|
+
*
|
|
1564
|
+
* A {@link
|
|
1565
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints |
|
|
1566
|
+
* MediaStreamConstraints} object specifying the types of media to request, along with any requirements for each type.
|
|
1567
|
+
*/
|
|
1568
|
+
setDefaultConstraints,
|
|
1569
|
+
/**
|
|
1570
|
+
* Subscribe media events
|
|
1571
|
+
*/
|
|
1572
|
+
subscribe
|
|
1573
|
+
} = state();
|
|
1574
|
+
export {
|
|
1575
|
+
MediaDeviceFailure,
|
|
1576
|
+
MediaDeviceKinds,
|
|
1577
|
+
MediaEventType,
|
|
1578
|
+
applyConstraints,
|
|
1579
|
+
areMultipleFacingModeSupportted,
|
|
1580
|
+
areTracksEnabled,
|
|
1581
|
+
compareDevices,
|
|
1582
|
+
createStreamTrackEventSubscriptions,
|
|
1583
|
+
createTrackDevicesChanges,
|
|
1584
|
+
deviceChanged,
|
|
1585
|
+
extractConstraintsWithKeys,
|
|
1586
|
+
findAudioInputDevices,
|
|
1587
|
+
findAudioOutputDevices,
|
|
1588
|
+
findCurrentAudioOutputId,
|
|
1589
|
+
findCurrentVideoInputDeviceIdFromStream,
|
|
1590
|
+
findDevice,
|
|
1591
|
+
findDeviceFromConstraints,
|
|
1592
|
+
findDeviceWithDeviceId,
|
|
1593
|
+
findDevicesByKind,
|
|
1594
|
+
findMediaInputFromMediaStreamTrack,
|
|
1595
|
+
findMediaInputFromStream,
|
|
1596
|
+
findPermissionGrantedDevices,
|
|
1597
|
+
findVideoInputDevices,
|
|
1598
|
+
getDevices,
|
|
1599
|
+
getFacingModeFromConstraintString,
|
|
1600
|
+
getInputDevicePermissionState,
|
|
1601
|
+
getUserMedia,
|
|
1602
|
+
getValueFromConstrainNumber,
|
|
1603
|
+
hasAnyGrantedInput,
|
|
1604
|
+
hasAnyInputs,
|
|
1605
|
+
hasAudioInputs,
|
|
1606
|
+
hasAudioOrVideoInputs,
|
|
1607
|
+
hasChangedInput,
|
|
1608
|
+
hasRequestingDevice,
|
|
1609
|
+
hasVideoInputs,
|
|
1610
|
+
interpretCurrentFacingMode,
|
|
1611
|
+
isAudioInput,
|
|
1612
|
+
isAudioOutput,
|
|
1613
|
+
isDeviceGranted,
|
|
1614
|
+
isExactDeviceConstraint,
|
|
1615
|
+
isFacingMode,
|
|
1616
|
+
isMediaDeviceInfo,
|
|
1617
|
+
isMediaDeviceInfoArray,
|
|
1618
|
+
isMediaStreamTrack,
|
|
1619
|
+
isRequestedInputDevice,
|
|
1620
|
+
isRequestedInputTrack,
|
|
1621
|
+
isRequestedResolution,
|
|
1622
|
+
isStreamingRequestedDevices,
|
|
1623
|
+
isStreamingRequestedDevicesBase,
|
|
1624
|
+
isVideoInput,
|
|
1625
|
+
mergeConstraints,
|
|
1626
|
+
muteStreamTrack,
|
|
1627
|
+
relaxInputConstraint,
|
|
1628
|
+
setDefaultConstraints,
|
|
1629
|
+
setLogger,
|
|
1630
|
+
shouldRequestDevice,
|
|
1631
|
+
stopMediaStream,
|
|
1632
|
+
subscribe,
|
|
1633
|
+
toKey,
|
|
1634
|
+
toMediaDeviceInfoLike
|
|
1635
|
+
};
|