UNPKG

@twilio/voice-sdk

Version:
160 lines 13.8 kB
/** * @packageDocumentation * @module Voice * @internalapi */ // @ts-nocheck import EventTarget from './eventtarget'; const POLL_INTERVAL_MS = 500; let nativeMediaDevices = null; /** * Make a custom MediaDevices object, and proxy through existing functionality. If * devicechange is present, we simply reemit the event. If not, we will do the * detection ourselves and fire the event when necessary. The same logic exists * for deviceinfochange for consistency, however deviceinfochange is our own event * so it is unlikely that it will ever be native. The w3c spec for devicechange * is unclear as to whether MediaDeviceInfo changes (such as label) will * trigger the devicechange event. We have an open question on this here: * https://bugs.chromium.org/p/chromium/issues/detail?id=585096 */ class MediaDevicesShim extends EventTarget { constructor() { super(); this._defineEventHandler('devicechange'); this._defineEventHandler('deviceinfochange'); const knownDevices = []; Object.defineProperties(this, { _deviceChangeIsNative: { value: reemitNativeEvent(this, 'devicechange') }, _deviceInfoChangeIsNative: { value: reemitNativeEvent(this, 'deviceinfochange') }, _knownDevices: { value: knownDevices }, _pollInterval: { value: null, writable: true, }, }); if (typeof nativeMediaDevices.enumerateDevices === 'function') { nativeMediaDevices.enumerateDevices().then(devices => { devices.sort(sortDevicesById).forEach(d => knownDevices.push(d)); }); } this._eventEmitter.on('newListener', function maybeStartPolling(eventName) { if (eventName !== 'devicechange' && eventName !== 'deviceinfochange') { return; } // TODO: Remove polling in the next major release. this._pollInterval = this._pollInterval || setInterval(sampleDevices.bind(null, this), POLL_INTERVAL_MS); }.bind(this)); this._eventEmitter.on('removeListener', function maybeStopPolling() { if (this._pollInterval && !hasChangeListeners(this)) { clearInterval(this._pollInterval); this._pollInterval = null; } }.bind(this)); } } MediaDevicesShim.prototype.enumerateDevices = function enumerateDevices() { if (nativeMediaDevices && typeof nativeMediaDevices.enumerateDevices === 'function') { return nativeMediaDevices.enumerateDevices(...arguments); } return null; }; MediaDevicesShim.prototype.getUserMedia = function getUserMedia() { return nativeMediaDevices.getUserMedia(...arguments); }; function deviceInfosHaveChanged(newDevices, oldDevices) { newDevices = newDevices.filter(d => d.kind === 'audioinput' || d.kind === 'audiooutput'); oldDevices = oldDevices.filter(d => d.kind === 'audioinput' || d.kind === 'audiooutput'); // On certain browsers, we cannot use deviceId as a key for comparison. // It's missing along with the device label if the customer has not granted permission. // The following checks whether some old devices have empty labels and if they are now available. // This means, the user has granted permissions and the device info have changed. if (oldDevices.some(d => !d.deviceId) && newDevices.some(d => !!d.deviceId)) { return true; } // Use both deviceId and "kind" to create a unique key // since deviceId is not unique across different kinds of devices. const oldLabels = oldDevices.reduce((map, device) => map.set(`${device.deviceId}-${device.kind}`, device.label), new Map()); return newDevices.some(device => { const oldLabel = oldLabels.get(`${device.deviceId}-${device.kind}`); return typeof oldLabel !== 'undefined' && oldLabel !== device.label; }); } function devicesHaveChanged(newDevices, oldDevices) { return newDevices.length !== oldDevices.length || propertyHasChanged('deviceId', newDevices, oldDevices); } function hasChangeListeners(mediaDevices) { return ['devicechange', 'deviceinfochange'].reduce((count, event) => count + mediaDevices._eventEmitter.listenerCount(event), 0) > 0; } /** * Sample the current set of devices and emit devicechange event if a device has been * added or removed, and deviceinfochange if a device's label has changed. * @param {MediaDevicesShim} mediaDevices * @private */ function sampleDevices(mediaDevices) { nativeMediaDevices.enumerateDevices().then(newDevices => { const knownDevices = mediaDevices._knownDevices; const oldDevices = knownDevices.slice(); // Replace known devices in-place [].splice.apply(knownDevices, [0, knownDevices.length] .concat(newDevices.sort(sortDevicesById))); if (!mediaDevices._deviceChangeIsNative && devicesHaveChanged(knownDevices, oldDevices)) { mediaDevices.dispatchEvent(new Event('devicechange')); } if (!mediaDevices._deviceInfoChangeIsNative && deviceInfosHaveChanged(knownDevices, oldDevices)) { mediaDevices.dispatchEvent(new Event('deviceinfochange')); } }); } /** * Accepts two sorted arrays and the name of a property to compare on objects from each. * Arrays should also be of the same length. * @param {string} propertyName - Name of the property to compare on each object * @param {Array<Object>} as - The left-side array of objects to compare. * @param {Array<Object>} bs - The right-side array of objects to compare. * @private * @returns {boolean} True if the property of any object in array A is different than * the same property of its corresponding object in array B. */ function propertyHasChanged(propertyName, as, bs) { return as.some((a, i) => a[propertyName] !== bs[i][propertyName]); } /** * Re-emit the native event, if the native mediaDevices has the corresponding property. * @param {MediaDevicesShim} mediaDevices * @param {string} eventName - Name of the event * @private * @returns {boolean} Whether the native mediaDevice had the corresponding property */ function reemitNativeEvent(mediaDevices, eventName) { const methodName = `on${eventName}`; function dispatchEvent(event) { mediaDevices.dispatchEvent(event); } if (methodName in nativeMediaDevices) { // Use addEventListener if it's available so we don't stomp on any other listeners // for this event. Currently, navigator.mediaDevices.addEventListener does not exist in Safari. if ('addEventListener' in nativeMediaDevices) { nativeMediaDevices.addEventListener(eventName, dispatchEvent); } else { nativeMediaDevices[methodName] = dispatchEvent; } return true; } return false; } function sortDevicesById(a, b) { return a.deviceId < b.deviceId; } const getMediaDevicesInstance = () => { nativeMediaDevices = typeof navigator !== 'undefined' ? navigator.mediaDevices : null; return nativeMediaDevices ? new MediaDevicesShim() : null; }; export default getMediaDevicesInstance; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVkaWFkZXZpY2VzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vbGliL3R3aWxpby9zaGltcy9tZWRpYWRldmljZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUNILGNBQWM7QUFFZCxPQUFPLFdBQVcsTUFBTSxlQUFlLENBQUM7QUFFeEMsTUFBTSxnQkFBZ0IsR0FBRyxHQUFHLENBQUM7QUFFN0IsSUFBSSxrQkFBa0IsR0FBRyxJQUFJLENBQUM7QUFFOUI7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxnQkFBaUIsU0FBUSxXQUFXO0lBQ3hDO1FBQ0UsS0FBSyxFQUFFLENBQUM7UUFFUixJQUFJLENBQUMsbUJBQW1CLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFFN0MsTUFBTSxZQUFZLEdBQUcsRUFBRSxDQUFDO1FBQ3hCLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUU7WUFDNUIscUJBQXFCLEVBQUUsRUFBRSxLQUFLLEVBQUUsaUJBQWlCLENBQUMsSUFBSSxFQUFFLGNBQWMsQ0FBQyxFQUFFO1lBQ3pFLHlCQUF5QixFQUFFLEVBQUUsS0FBSyxFQUFFLGlCQUFpQixDQUFDLElBQUksRUFBRSxrQkFBa0IsQ0FBQyxFQUFFO1lBQ2pGLGFBQWEsRUFBRSxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUU7WUFDdEMsYUFBYSxFQUFFO2dCQUNiLEtBQUssRUFBRSxJQUFJO2dCQUNYLFFBQVEsRUFBRSxJQUFJO2FBQ2Y7U0FDRixDQUFDLENBQUM7UUFFSCxJQUFJLE9BQU8sa0JBQWtCLENBQUMsZ0JBQWdCLEtBQUssVUFBVSxFQUFFO1lBQzdELGtCQUFrQixDQUFDLGdCQUFnQixFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUNuRCxPQUFPLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNuRSxDQUFDLENBQUMsQ0FBQztTQUNKO1FBRUQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsYUFBYSxFQUFFLFNBQVMsaUJBQWlCLENBQUMsU0FBUztZQUN2RSxJQUFJLFNBQVMsS0FBSyxjQUFjLElBQUksU0FBUyxLQUFLLGtCQUFrQixFQUFFO2dCQUNwRSxPQUFPO2FBQ1I7WUFFRCxrREFBa0Q7WUFDbEQsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsYUFBYTttQkFDbEMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxFQUFFLGdCQUFnQixDQUFDLENBQUM7UUFDckUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBRWQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsZ0JBQWdCLEVBQUUsU0FBUyxnQkFBZ0I7WUFDL0QsSUFBSSxJQUFJLENBQUMsYUFBYSxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ25ELGFBQWEsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7Z0JBQ2xDLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDO2FBQzNCO1FBQ0gsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ2hCLENBQUM7Q0FDRjtBQUVELGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsR0FBRyxTQUFTLGdCQUFnQjtJQUNyRSxJQUFJLGtCQUFrQixJQUFJLE9BQU8sa0JBQWtCLENBQUMsZ0JBQWdCLEtBQUssVUFBVSxFQUFFO1FBQ25GLE9BQU8sa0JBQWtCLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQztLQUMxRDtJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQyxDQUFDO0FBRUYsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLFlBQVksR0FBRyxTQUFTLFlBQVk7SUFDN0QsT0FBTyxrQkFBa0IsQ0FBQyxZQUFZLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQztBQUN2RCxDQUFDLENBQUM7QUFFRixTQUFTLHNCQUFzQixDQUFDLFVBQVUsRUFBRSxVQUFVO0lBQ3BELFVBQVUsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxZQUFZLElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxhQUFhLENBQUMsQ0FBQztJQUN6RixVQUFVLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssWUFBWSxJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssYUFBYSxDQUFDLENBQUM7SUFFekYsdUVBQXVFO0lBQ3ZFLHVGQUF1RjtJQUN2RixpR0FBaUc7SUFDakcsaUZBQWlGO0lBQ2pGLElBQUksVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztRQUNuQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsRUFBRTtRQUNwQyxPQUFPLElBQUksQ0FBQztLQUNiO0lBRUQsc0RBQXNEO0lBQ3RELGtFQUFrRTtJQUNsRSxNQUFNLFNBQVMsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQ2xELEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFBRSxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsSUFBSSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBRXpFLE9BQU8sVUFBVSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRTtRQUM5QixNQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDLFFBQVEsSUFBSSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNwRSxPQUFPLE9BQU8sUUFBUSxLQUFLLFdBQVcsSUFBSSxRQUFRLEtBQUssTUFBTSxDQUFDLEtBQUssQ0FBQztJQUN0RSxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFTLGtCQUFrQixDQUFDLFVBQVUsRUFBRSxVQUFVO0lBQ2hELE9BQU8sVUFBVSxDQUFDLE1BQU0sS0FBSyxVQUFVLENBQUMsTUFBTTtXQUN6QyxrQkFBa0IsQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0FBQzlELENBQUM7QUFFRCxTQUFTLGtCQUFrQixDQUFDLFlBQVk7SUFDdEMsT0FBTyxDQUFDLGNBQWMsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssR0FBRyxZQUFZLENBQUMsYUFBYSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDdkksQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxhQUFhLENBQUMsWUFBWTtJQUNqQyxrQkFBa0IsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRTtRQUN0RCxNQUFNLFlBQVksR0FBRyxZQUFZLENBQUMsYUFBYSxDQUFDO1FBQ2hELE1BQU0sVUFBVSxHQUFHLFlBQVksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUV4QyxpQ0FBaUM7UUFDakMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLENBQUMsQ0FBQyxFQUFFLFlBQVksQ0FBQyxNQUFNLENBQUM7YUFDbkQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTdDLElBQUksQ0FBQyxZQUFZLENBQUMscUJBQXFCO2VBQ2xDLGtCQUFrQixDQUFDLFlBQVksRUFBRSxVQUFVLENBQUMsRUFBRTtZQUNqRCxZQUFZLENBQUMsYUFBYSxDQUFDLElBQUksS0FBSyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7U0FDdkQ7UUFFRCxJQUFJLENBQUMsWUFBWSxDQUFDLHlCQUF5QjtlQUN0QyxzQkFBc0IsQ0FBQyxZQUFZLEVBQUUsVUFBVSxDQUFDLEVBQUU7WUFDckQsWUFBWSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUM7U0FDM0Q7SUFDSCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7Ozs7Ozs7O0dBU0c7QUFDSCxTQUFTLGtCQUFrQixDQUFDLFlBQVksRUFBRSxFQUFFLEVBQUUsRUFBRTtJQUM5QyxPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7QUFDcEUsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILFNBQVMsaUJBQWlCLENBQUMsWUFBWSxFQUFFLFNBQVM7SUFDaEQsTUFBTSxVQUFVLEdBQUcsS0FBSyxTQUFTLEVBQUUsQ0FBQztJQUVwQyxTQUFTLGFBQWEsQ0FBQyxLQUFLO1FBQzFCLFlBQVksQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUVELElBQUksVUFBVSxJQUFJLGtCQUFrQixFQUFFO1FBQ3BDLGtGQUFrRjtRQUNsRiwrRkFBK0Y7UUFDL0YsSUFBSSxrQkFBa0IsSUFBSSxrQkFBa0IsRUFBRTtZQUM1QyxrQkFBa0IsQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLEVBQUUsYUFBYSxDQUFDLENBQUM7U0FDL0Q7YUFBTTtZQUNMLGtCQUFrQixDQUFDLFVBQVUsQ0FBQyxHQUFHLGFBQWEsQ0FBQztTQUNoRDtRQUVELE9BQU8sSUFBSSxDQUFDO0tBQ2I7SUFFRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRCxTQUFTLGVBQWUsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUMzQixPQUFPLENBQUMsQ0FBQyxRQUFRLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQztBQUNqQyxDQUFDO0FBRUQsTUFBTSx1QkFBdUIsR0FBRyxHQUFHLEVBQUU7SUFDbkMsa0JBQWtCLEdBQUcsT0FBTyxTQUFTLEtBQUssV0FBVyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDdEYsT0FBTyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsSUFBSSxnQkFBZ0IsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7QUFDNUQsQ0FBQyxDQUFDO0FBRUYsZUFBZSx1QkFBdUIsQ0FBQyJ9