@pexip/media-control 17.2.0 → 17.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,653 @@
1
+ import { isBoolean, isConstrainBooleanKeys, isConstrainBooleanParameters, isConstrainDOMStringParameters, isConstrainDoubleKeys, isConstrainDoubleRange, isConstrainRange, isConstrainStringKeys, isConstrainULongKeys, isConstrainULongRange, isConstraintDOMString, isConstraintDeviceParameters, isConstraintSetDevice, isDeviceConstraint, isExtendedConstrainBooleanKeys, isExtendedConstrainDoubleKeys, isExtendedConstrainStringKeys, isExtendedConstrainULongKeys, isFacingMode, isFloat, isInputConstraintSet, isInteger, isMediaDeviceInfo, isMediaDeviceInfoArray, isMediaTrackConstraintSetKey, isMediaTrackConstraints, isNumber, isUndefined, } from './typeGuards';
2
+ import { logger } from './logger';
3
+ import { findDevice } from './utils';
4
+ /**
5
+ * Check if provided constraint is an `exact` device constraint
6
+ */
7
+ export const isExactDeviceConstraint = (constraint) => {
8
+ if (isInputConstraintSet(constraint)) {
9
+ return ((!!constraint.device &&
10
+ typeof constraint.device === 'object' &&
11
+ 'exact' in constraint.device) ||
12
+ (!!constraint.deviceId &&
13
+ typeof constraint.deviceId === 'object' &&
14
+ 'exact' in constraint.deviceId));
15
+ }
16
+ return false;
17
+ };
18
+ const getConstraintsSetFilter = (supportedConstraints) => (trackConstraints) => {
19
+ if (!isMediaTrackConstraints(trackConstraints)) {
20
+ return trackConstraints;
21
+ }
22
+ const constraints = Object.entries(trackConstraints).reduce((acc, [key, val]) => key === 'advanced' ||
23
+ supportedConstraints.has(key)
24
+ ? { ...acc, [key]: val }
25
+ : acc, {});
26
+ // If we don't have any valid constraints it seems sensible to fallback to a boolean constraint -ea
27
+ if (Object.keys(constraints).length === 0) {
28
+ return true;
29
+ }
30
+ return constraints;
31
+ };
32
+ const getDefinedOnly = (obj) => {
33
+ return Object.entries(obj).reduce((acc, [key, val]) => typeof val === 'undefined' ? acc : { ...acc, [key]: val }, {});
34
+ };
35
+ /**
36
+ * Use navigator.mediaDevices.getSupportedConstraints to remove constraints not supported by agent.
37
+ * Creates a copy of MediaStreamConstraints with only supported properties.
38
+ */
39
+ export const removeUnsupportedConstraints = (constraints) => {
40
+ if (!(navigator && 'getSupportedConstraints' in navigator.mediaDevices)) {
41
+ return constraints;
42
+ }
43
+ const supportedConstraints = Object.entries(navigator.mediaDevices.getSupportedConstraints());
44
+ logger.debug({ supportedConstraints }, 'Supported constraints');
45
+ const constraintsSetFilter = getConstraintsSetFilter(supportedConstraints.reduce((set, [key, value]) =>
46
+ // FIXME: Cast inserted to unblock typescript upgrade, will have to be verified and properly fixed.
47
+ value && isMediaTrackConstraintSetKey(key)
48
+ ? set.add(key)
49
+ : set, new Set()));
50
+ return getDefinedOnly({
51
+ audio: constraintsSetFilter(constraints.audio),
52
+ peerIdentity: constraints.peerIdentity,
53
+ video: constraintsSetFilter(constraints.video),
54
+ });
55
+ };
56
+ /**
57
+ * Merge base constraints with provided base constraints and another constraints
58
+ */
59
+ export const mergeConstraints = (baseConstraints) => (constraints) => {
60
+ if (!isInputConstraintSet(baseConstraints)) {
61
+ if (isDeviceConstraint(constraints)) {
62
+ return { device: constraints };
63
+ }
64
+ return constraints ?? false;
65
+ }
66
+ if (!constraints) {
67
+ return false;
68
+ }
69
+ if (isDeviceConstraint(constraints)) {
70
+ const result = {
71
+ ...baseConstraints,
72
+ device: constraints,
73
+ };
74
+ delete result.facingMode;
75
+ delete result.deviceId;
76
+ return result;
77
+ }
78
+ if (isInputConstraintSet(constraints)) {
79
+ const combined = { ...baseConstraints, ...constraints };
80
+ const hasDeviceId = isConstraintDOMString(combined.deviceId) ||
81
+ isConstrainDOMStringParameters(combined.deviceId);
82
+ const hasDevice = isDeviceConstraint(combined.device) ||
83
+ isConstraintDeviceParameters(combined.device);
84
+ if (hasDeviceId && hasDevice) {
85
+ const result = {
86
+ ...combined,
87
+ };
88
+ delete result.facingMode;
89
+ delete result.deviceId;
90
+ return result;
91
+ }
92
+ if (hasDevice || hasDeviceId) {
93
+ delete combined.facingMode;
94
+ return combined;
95
+ }
96
+ return combined;
97
+ }
98
+ return baseConstraints;
99
+ };
100
+ /**
101
+ * Normalize Device
102
+ * Convert any valid `MediaDeviceInfoLike` into `MediaDeviceInfoLike[]`
103
+ *
104
+ * Check test cases for the details
105
+ */
106
+ export const normalizeDevice = (device) => {
107
+ if (!device) {
108
+ return undefined;
109
+ }
110
+ if (isMediaDeviceInfo(device)) {
111
+ return [device];
112
+ }
113
+ if (Array.isArray(device)) {
114
+ const devices = device.filter(isMediaDeviceInfo);
115
+ if (devices.length) {
116
+ return devices;
117
+ }
118
+ }
119
+ return undefined;
120
+ };
121
+ /**
122
+ * Normalize Device Constraint
123
+ * Convert Device Constraints into a form of `ConstraintDeviceParameters`
124
+ * e.g.
125
+ * `{ideal?: MediaDeviceInfoLike[], exact?: MediaDeviceInfoLike[]}`
126
+ *
127
+ * Check test cases for the details
128
+ *
129
+ * @returns
130
+ * `undefined` if nothing is meaningful for the constraint, otherwise,
131
+ * a normalized form of `ConstraintDeviceParameters`
132
+ */
133
+ export const normalizeDeviceConstraint = (constraints) => {
134
+ if (!constraints) {
135
+ return undefined;
136
+ }
137
+ if (isConstraintDeviceParameters(constraints)) {
138
+ return Object.keys(constraints)
139
+ .filter(k => ['ideal', 'exact'].includes(k))
140
+ .reduce((cs, k) => {
141
+ const key = k;
142
+ const value = constraints[key];
143
+ const devices = normalizeDevice(value);
144
+ if (devices) {
145
+ return { ...(cs ?? {}), [key]: devices };
146
+ }
147
+ return cs;
148
+ }, undefined);
149
+ }
150
+ const normalized = normalizeDevice(constraints);
151
+ return normalized && { ideal: normalized };
152
+ };
153
+ /**
154
+ * Convert `InputConstraintSet['device']` into
155
+ * `MediaTrackConstraintSet['deviceId']` in a normalized form
156
+ */
157
+ export const toDeviceIdConstraintSet = (constraint) => {
158
+ const [devices, param] = extractConstrainDevice({ device: constraint });
159
+ return devices && { [param]: devices.map(device => device.deviceId) };
160
+ };
161
+ export const toArray = (t) => {
162
+ const r = Array.isArray(t) ? t : [t];
163
+ return r.filter(Boolean);
164
+ };
165
+ export const NONE = [undefined, 'ideal'];
166
+ export const extractConstrainString = (key) => (constraints) => {
167
+ const constraint = constraints[key];
168
+ if (!constraint) {
169
+ return NONE;
170
+ }
171
+ if (isConstraintDOMString(constraint)) {
172
+ return [toArray(constraint), 'ideal'];
173
+ }
174
+ if (isConstrainDOMStringParameters(constraint)) {
175
+ // `exact` takes priority
176
+ if (constraint.exact) {
177
+ const normalized = toArray(constraint.exact);
178
+ if (normalized.length) {
179
+ return [normalized, 'exact'];
180
+ }
181
+ }
182
+ if (constraint.ideal) {
183
+ return [toArray(constraint.ideal), 'ideal'];
184
+ }
185
+ }
186
+ return NONE;
187
+ };
188
+ export const extractConstrainBoolean = (key) => (constraints) => {
189
+ const constraint = constraints[key];
190
+ if (isUndefined(constraint)) {
191
+ return NONE;
192
+ }
193
+ if (isBoolean(constraint)) {
194
+ return [constraint, 'ideal'];
195
+ }
196
+ if (isConstrainBooleanParameters(constraint)) {
197
+ const { ideal, exact } = constraint;
198
+ // `exact` takes priority
199
+ if (isBoolean(exact)) {
200
+ return [exact, 'exact'];
201
+ }
202
+ if (isBoolean(ideal)) {
203
+ return [ideal, 'ideal'];
204
+ }
205
+ }
206
+ return NONE;
207
+ };
208
+ export const extractConstrainNumber = (key) => (constraints) => {
209
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- @typescript-eslint and Typescript 5 issue.
210
+ const constraint = constraints[key];
211
+ if (constraint === undefined || constraint === null) {
212
+ return NONE;
213
+ }
214
+ if (isFloat(constraint) || isInteger(constraint)) {
215
+ return [constraint, 'ideal'];
216
+ }
217
+ if (isConstrainULongRange(constraint) ||
218
+ isConstrainDoubleRange(constraint)) {
219
+ const { exact, ideal, min, max } = constraint;
220
+ // all 'min', 'max', and 'exact' constraints in the basic Constraint
221
+ // structure are together treated as the required constraints
222
+ if (isFloat(min) ||
223
+ isFloat(max) ||
224
+ isInteger(min) ||
225
+ isInteger(max)) {
226
+ return [{ min, max, ideal, exact }, 'min-max'];
227
+ }
228
+ if (isFloat(exact) || isInteger(exact)) {
229
+ return [exact, 'exact'];
230
+ }
231
+ if (isFloat(ideal) || isInteger(ideal)) {
232
+ return [ideal, 'ideal'];
233
+ }
234
+ }
235
+ return NONE;
236
+ };
237
+ export const extractConstrainDevice = (constraints) => {
238
+ if (!constraints ||
239
+ isBoolean(constraints) ||
240
+ (Array.isArray(constraints) && !constraints.length)) {
241
+ return NONE;
242
+ }
243
+ const { device } = isInputConstraintSet(constraints)
244
+ ? constraints
245
+ : { device: constraints };
246
+ if (isMediaDeviceInfo(device) || isMediaDeviceInfoArray(device)) {
247
+ const normalized = normalizeDevice(device);
248
+ if (normalized) {
249
+ return [normalized, 'ideal'];
250
+ }
251
+ }
252
+ if (isConstraintDeviceParameters(device)) {
253
+ const { exact, ideal } = normalizeDeviceConstraint(device) ?? {};
254
+ if (exact) {
255
+ return [exact, 'exact'];
256
+ }
257
+ if (ideal) {
258
+ return [ideal, 'ideal'];
259
+ }
260
+ }
261
+ return NONE;
262
+ };
263
+ /**
264
+ * Compare float point number a and b to see if they are closed to be considered
265
+ * as having the same value
266
+ *
267
+ * @param a - Floating point number a
268
+ * @param b - Floating point number b
269
+ * @param numDigits - The number of digits to check after the decimal point @defaultValue 5 digits
270
+ */
271
+ export const closedTo = (a, b, numDigits = 5) => {
272
+ const multiplier = Math.pow(10, numDigits);
273
+ return Math.round(a * multiplier) === Math.round(b * multiplier);
274
+ };
275
+ /**
276
+ * Check if provided num is between min (inclusive) and max (inclusive)
277
+ *
278
+ * @param min - Lower boundary of the checking
279
+ * @param num - The number used for the checking
280
+ * @param max - Upper boundary of the checking
281
+ */
282
+ export const between = (min, num, max) => {
283
+ if (!isUndefined(min)) {
284
+ if (!isUndefined(max)) {
285
+ return num >= min && num <= max;
286
+ }
287
+ return num >= min;
288
+ }
289
+ if (!isUndefined(max)) {
290
+ return num <= max;
291
+ }
292
+ return true;
293
+ };
294
+ export const getValueFromConstrainNumber = (constraint) => {
295
+ if (isNumber(constraint)) {
296
+ return constraint;
297
+ }
298
+ const { min, max, ideal, exact } = constraint;
299
+ if (exact !== undefined) {
300
+ const withinRange = between(min, exact, max);
301
+ if (withinRange) {
302
+ return exact;
303
+ }
304
+ }
305
+ if (ideal !== undefined) {
306
+ const withinRange = between(min, ideal, max);
307
+ if (withinRange) {
308
+ return ideal;
309
+ }
310
+ }
311
+ const value = max ?? min;
312
+ if (value !== undefined) {
313
+ return value;
314
+ }
315
+ throw Error('Constrain Number is undefined');
316
+ };
317
+ export const getFacingModeFromConstraintString = (constraint) => {
318
+ if (isFacingMode(constraint)) {
319
+ return constraint;
320
+ }
321
+ return undefined;
322
+ };
323
+ /**
324
+ * Compare the provided constraint and num and see if the num satisfy the
325
+ * constraint
326
+ *
327
+ * @param constraint - The constraint to be used
328
+ * @param num - The num to be used to check
329
+ *
330
+ * @returns `true` means satisfy otherwise `false`
331
+ */
332
+ export const satisfyConstrainNumber = (constraint, num) => {
333
+ if (isUndefined(constraint) || isUndefined(num)) {
334
+ return true;
335
+ }
336
+ if (isInteger(constraint)) {
337
+ return num === constraint;
338
+ }
339
+ if (isFloat(constraint)) {
340
+ return closedTo(constraint, num);
341
+ }
342
+ if (isConstrainRange(constraint)) {
343
+ const { min, max, ideal, exact } = constraint;
344
+ const withinRange = between(min, num, max);
345
+ if (isFloat(exact)) {
346
+ return withinRange && closedTo(exact, num);
347
+ }
348
+ if (isInteger(exact)) {
349
+ return withinRange && exact === num;
350
+ }
351
+ if (isFloat(ideal)) {
352
+ return withinRange && closedTo(ideal, num);
353
+ }
354
+ if (isInteger(ideal)) {
355
+ return withinRange && ideal === num;
356
+ }
357
+ return withinRange;
358
+ }
359
+ return false;
360
+ };
361
+ /**
362
+ * Resolve *constraints* and mediaTrackConstraints by checking the type and
363
+ * try to return the best possible from mediaTrackConstraints.
364
+ *
365
+ * @internal
366
+ */
367
+ export const resolveMediaDeviceConstraints = (constraints, base) => {
368
+ const merge = mergeConstraints(base);
369
+ // Handle InputConstraintSet
370
+ if (isInputConstraintSet(constraints)) {
371
+ if (isConstraintSetDevice(constraints.device)) {
372
+ return merge(Object.keys(constraints).reduce((cs, k) => {
373
+ const key = k;
374
+ if (key === 'device') {
375
+ return {
376
+ ...cs,
377
+ deviceId: toDeviceIdConstraintSet(constraints.device),
378
+ };
379
+ }
380
+ return { ...cs, [key]: constraints[key] };
381
+ }, {}));
382
+ }
383
+ return merge(constraints);
384
+ }
385
+ if (isConstraintSetDevice(constraints)) {
386
+ const deviceId = toDeviceIdConstraintSet(constraints);
387
+ return merge({ deviceId });
388
+ }
389
+ if (constraints && isMediaTrackConstraints(base)) {
390
+ return base;
391
+ }
392
+ return !!constraints;
393
+ };
394
+ /**
395
+ * @remarks
396
+ * Constraint multiple (exact) devices is not supported
397
+ *
398
+ * @internal
399
+ */
400
+ export const getMediaConstraints = ({ audio, video, defaultConstraints, }) => {
401
+ return removeUnsupportedConstraints({
402
+ audio: resolveMediaDeviceConstraints(audio, defaultConstraints.audio),
403
+ video: resolveMediaDeviceConstraints(video, defaultConstraints.video),
404
+ });
405
+ };
406
+ /**
407
+ * Call applyConstraints foreach track accordingly
408
+ *
409
+ * @param tracks - Tracks to be applied
410
+ * @param constraints - constraints to be applied
411
+ */
412
+ export const applyConstraints = async (tracks, constraints) => {
413
+ if (!tracks ||
414
+ tracks.length === 0 ||
415
+ Object.keys(constraints).length === 0) {
416
+ return Promise.resolve();
417
+ }
418
+ const { audio, video } = removeUnsupportedConstraints({
419
+ audio: resolveMediaDeviceConstraints(constraints.audio),
420
+ video: resolveMediaDeviceConstraints(constraints.video),
421
+ });
422
+ await Promise.all(tracks.flatMap(track => {
423
+ if (track.kind === 'audio' && isMediaTrackConstraints(audio)) {
424
+ return [track.applyConstraints(audio)];
425
+ }
426
+ if (track.kind === 'video' && isMediaTrackConstraints(video)) {
427
+ return [track.applyConstraints(video)];
428
+ }
429
+ return [];
430
+ }));
431
+ };
432
+ export const findDeviceFrom = (devicesToFind, deviceList) => {
433
+ const devicesFound = devicesToFind.flatMap(device => {
434
+ const found = findDevice(device)(deviceList);
435
+ if (found) {
436
+ return [found];
437
+ }
438
+ return [];
439
+ });
440
+ if (devicesFound.length) {
441
+ return devicesFound;
442
+ }
443
+ return undefined;
444
+ };
445
+ export const findDeviceFromDeviceConstraints = (device, devices) => {
446
+ const normalized = normalizeDeviceConstraint(device);
447
+ if (normalized) {
448
+ const { ideal, exact } = normalized;
449
+ if (exact) {
450
+ return findDeviceFrom(exact, devices);
451
+ }
452
+ if (ideal) {
453
+ return findDeviceFrom(ideal, devices);
454
+ }
455
+ }
456
+ };
457
+ export const relaxDevice = (device, devices) => {
458
+ const found = findDeviceFromDeviceConstraints(device, devices);
459
+ if (found) {
460
+ return found;
461
+ }
462
+ // Return the original with normalized form
463
+ return normalizeDevice(device);
464
+ };
465
+ export const resolveOnlyDevice = (devices) => {
466
+ switch (devices.length) {
467
+ case 1:
468
+ return devices[0];
469
+ case 0:
470
+ return undefined;
471
+ default:
472
+ return true;
473
+ }
474
+ };
475
+ export function extractConstraints(key) {
476
+ return constraints => {
477
+ if (!constraints ||
478
+ typeof constraints === 'boolean' ||
479
+ (Array.isArray(constraints) && !constraints.length)) {
480
+ return NONE;
481
+ }
482
+ if (key === 'device' &&
483
+ (isMediaDeviceInfo(constraints) ||
484
+ isMediaDeviceInfoArray(constraints))) {
485
+ return extractConstrainDevice(constraints);
486
+ }
487
+ if (isInputConstraintSet(constraints)) {
488
+ if (key === 'device') {
489
+ return extractConstrainDevice(constraints);
490
+ }
491
+ if (isConstrainStringKeys(key) ||
492
+ isExtendedConstrainStringKeys(key)) {
493
+ return extractConstrainString(key)(constraints);
494
+ }
495
+ if (isConstrainDoubleKeys(key) ||
496
+ isConstrainULongKeys(key) ||
497
+ isExtendedConstrainULongKeys(key) ||
498
+ isExtendedConstrainDoubleKeys(key)) {
499
+ return extractConstrainNumber(key)(constraints);
500
+ }
501
+ if (isConstrainBooleanKeys(key) ||
502
+ isExtendedConstrainBooleanKeys(key)) {
503
+ return extractConstrainBoolean(key)(constraints);
504
+ }
505
+ }
506
+ return [undefined, 'ideal'];
507
+ };
508
+ }
509
+ /**
510
+ * Extract the constraints with provided keys
511
+ *
512
+ * @param keys - The keys to be used for the extraction
513
+ * @param constraints - The constraints to be used for the extraction
514
+ *
515
+ * @returns an object with the provided key and the value-param tuple
516
+ *
517
+ * @example
518
+ *
519
+ * ```typescript
520
+ * const device = {deviceId: 'xxxx', label: 'abc', kind: 'audioinput'};
521
+ * const constraints = { device, noiseSuppression: true };
522
+ * const extract = extractConstraintsWithKeys(['device', 'noiseSuppression']);
523
+ * const {
524
+ * device: [devices, deviceParam],
525
+ * noiseSuppression: [noiseSuppression, noiseSuppressionParam],
526
+ * } = extract(constraints);
527
+ * expect(devices).toEqual([device]);
528
+ * expect(deviceParam).toEqual('ideal');
529
+ * expect(noiseSuppression).toEqual(true);
530
+ * expect(noiseSuppressionParam).toEqual('ideal');
531
+ * ```
532
+ */
533
+ export const extractConstraintsWithKeys = (keys) => (constraints) => keys.reduce((result, key) => {
534
+ if (key === 'device') {
535
+ return {
536
+ ...result,
537
+ [key]: extractConstraints(key)(constraints),
538
+ };
539
+ }
540
+ if (isConstrainULongKeys(key) ||
541
+ isConstrainDoubleKeys(key) ||
542
+ isExtendedConstrainULongKeys(key) ||
543
+ isExtendedConstrainDoubleKeys(key)) {
544
+ return {
545
+ ...result,
546
+ [key]: extractConstraints(key)(constraints),
547
+ };
548
+ }
549
+ if (isConstrainStringKeys(key) ||
550
+ isExtendedConstrainStringKeys(key)) {
551
+ return {
552
+ ...result,
553
+ [key]: extractConstraints(key)(constraints),
554
+ };
555
+ }
556
+ if (isConstrainBooleanKeys(key) ||
557
+ isExtendedConstrainBooleanKeys(key)) {
558
+ return {
559
+ ...result,
560
+ [key]: extractConstraints(key)(constraints),
561
+ };
562
+ }
563
+ return result;
564
+ }, {});
565
+ /**
566
+ * Extract deviceId from constraints
567
+ */
568
+ export const extractDeviceId = extractConstrainString('deviceId');
569
+ /**
570
+ * Find device from the device list with provided constraints
571
+ *
572
+ * @param constraints - The constraints to be used for the lookup
573
+ * @param devices - The devices to be used for the lookup
574
+ *
575
+ * @returns `true` means it can be any devices, `undefined` means not found,
576
+ * otherwise, the matched device will be returned
577
+ */
578
+ export const findDeviceFromConstraints = (constraints, devices) => {
579
+ if (typeof constraints === 'boolean') {
580
+ return constraints ? resolveOnlyDevice(devices) : constraints;
581
+ }
582
+ if (constraints === undefined ||
583
+ !devices.length ||
584
+ devices.some(device => !device.label) ||
585
+ (Array.isArray(constraints) && !constraints.length)) {
586
+ return undefined;
587
+ }
588
+ const [constrainDevices, deviceParam] = extractConstraints('device')(constraints);
589
+ // Device takes priority
590
+ if (constrainDevices) {
591
+ const [device] = findDeviceFrom(constrainDevices, devices) ?? [];
592
+ if (device) {
593
+ return device;
594
+ }
595
+ if (deviceParam === 'exact') {
596
+ return undefined;
597
+ }
598
+ }
599
+ const [ConstrainDeviceIds, deviceIdParam] = extractConstraints('deviceId')(constraints);
600
+ if (ConstrainDeviceIds) {
601
+ const deviceFound = devices.find(device => {
602
+ const id = ConstrainDeviceIds.find(id => id && device.label && device.deviceId === id);
603
+ return !!id;
604
+ });
605
+ if (deviceFound) {
606
+ return deviceFound;
607
+ }
608
+ if (deviceIdParam === 'exact') {
609
+ return undefined;
610
+ }
611
+ }
612
+ return resolveOnlyDevice(devices);
613
+ };
614
+ /**
615
+ * Relax input constraints to enable looking up the device by `deviceId` as well
616
+ * as `label` as a fallback as a best effort to get a similar device when
617
+ * possible.
618
+ *
619
+ * @param input - The input constraints to be relaxed
620
+ * @param devices - The current device list
621
+ */
622
+ export const relaxInputConstraint = (input, devices) => {
623
+ if (devices.length === 0 || !input || typeof input === 'boolean') {
624
+ return input;
625
+ }
626
+ const [device, param] = extractConstrainDevice(input);
627
+ const relaxedDevice = relaxDevice(device, devices);
628
+ const otherConstraints = isInputConstraintSet(input) ? input : {};
629
+ return relaxedDevice
630
+ ? { ...otherConstraints, device: { [param]: relaxedDevice } }
631
+ : input;
632
+ };
633
+ export const getConstraintsHandlers = () => {
634
+ let defaultConstraints = {
635
+ audio: {
636
+ echoCancellation: { ideal: true },
637
+ noiseSuppression: { ideal: true },
638
+ },
639
+ video: {
640
+ frameRate: { ideal: 30 },
641
+ },
642
+ };
643
+ const setDefaultConstraints = (newConstraints) => {
644
+ defaultConstraints = { ...defaultConstraints, ...newConstraints };
645
+ };
646
+ const getDefaultConstraints = () => {
647
+ return defaultConstraints;
648
+ };
649
+ return {
650
+ getDefaultConstraints,
651
+ setDefaultConstraints,
652
+ };
653
+ };