@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/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
+ };