diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2020-09-24 18:18:00 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-24 18:18:00 +0300 |
commit | af402ccbc59a19a4ea79c28981e4d2b3c51e5991 (patch) | |
tree | 271d7c4d030e93e3b31185cea8bf223158c83949 | |
parent | a0df00217b316b24b7cfb14e8894933d64e39d03 (diff) | |
parent | f3356644d016269e98105eabfae46840b63d5566 (diff) |
Merge pull request #4224 from nextcloud/backport/4218/stable20
[stable20] Remember selected input devices
-rw-r--r-- | src/utils/webrtc/MediaDevicesManager.js | 68 | ||||
-rw-r--r-- | src/utils/webrtc/simplewebrtc/localmedia.js | 84 |
2 files changed, 121 insertions, 31 deletions
diff --git a/src/utils/webrtc/MediaDevicesManager.js b/src/utils/webrtc/MediaDevicesManager.js index 6d6dc0a68..fd4239887 100644 --- a/src/utils/webrtc/MediaDevicesManager.js +++ b/src/utils/webrtc/MediaDevicesManager.js @@ -19,6 +19,14 @@ * */ +import BrowserStorage from '../../services/BrowserStorage' + +/** + * Special string to set null device ids in local storage (as only strings are + * allowed). + */ +const LOCAL_STORAGE_NULL_DEVICE_ID = 'local-storage-null-device-id' + /** * Wrapper for MediaDevices to simplify its use. * @@ -57,7 +65,9 @@ * modified. * * The selected devices will be automatically cleared if they are no longer - * available. When no device of certain kind is selected and there are other + * available, and they will be restored once they are again available + * (immediately if events are enabled, or otherwise the next time that devices + * are got). When no device of certain kind is selected and there are other * devices of that kind the selected device will fall back to the first one * found, or to the one with the "default" id (if any). It is possible to * explicitly disable devices of certain kind by setting xxxInputId to "null" @@ -83,6 +93,15 @@ export default function MediaDevicesManager() { this._tracks = [] this._updateDevicesBound = this._updateDevices.bind(this) + + this._pendingEnumerateDevicesPromise = null + + if (BrowserStorage.getItem('audioInputId') === LOCAL_STORAGE_NULL_DEVICE_ID) { + this.attributes.audioInputId = null + } + if (BrowserStorage.getItem('videoInputId') === LOCAL_STORAGE_NULL_DEVICE_ID) { + this.attributes.videoInputId = null + } } MediaDevicesManager.prototype = { @@ -94,6 +113,24 @@ MediaDevicesManager.prototype = { this.attributes[key] = value this._trigger('change:' + key, [value]) + + this._storeDeviceId(key, value) + }, + + _storeDeviceId: function(key, value) { + if (key !== 'audioInputId' && key !== 'videoInputId') { + return + } + + if (value === null) { + value = LOCAL_STORAGE_NULL_DEVICE_ID + } + + if (value) { + BrowserStorage.setItem(key, value) + } else { + BrowserStorage.removeItem(key) + } }, on: function(event, handler) { @@ -171,7 +208,7 @@ MediaDevicesManager.prototype = { }, _updateDevices: function() { - navigator.mediaDevices.enumerateDevices().then(devices => { + this._pendingEnumerateDevicesPromise = navigator.mediaDevices.enumerateDevices().then(devices => { const previousAudioInputId = this.attributes.audioInputId const previousVideoInputId = this.attributes.videoInputId @@ -197,8 +234,12 @@ MediaDevicesManager.prototype = { if (previousVideoInputId !== this.attributes.videoInputId) { this._trigger('change:videoInputId', [this.attributes.videoInputId]) } + + this._pendingEnumerateDevicesPromise = null }).catch(function(error) { console.error('Could not update known media devices: ' + error.name + ': ' + error.message) + + this._pendingEnumerateDevicesPromise = null }) }, @@ -274,9 +315,13 @@ MediaDevicesManager.prototype = { // Always refresh the known device with the latest values. this._knownDevices[addedDevice.kind + '-' + addedDevice.deviceId] = addedDevice - // Set first available device as fallback, and override any - // fallback previously set if the default device is added. + // Restore previously selected device if it becomes available again. + // Additionally, set first available device as fallback, and override + // any fallback previously set if the default device is added. if (addedDevice.kind === 'audioinput') { + if (BrowserStorage.getItem('audioInputId') === addedDevice.deviceId) { + this.attributes.audioInputId = addedDevice.deviceId + } if (!this._fallbackAudioInputId || addedDevice.deviceId === 'default') { this._fallbackAudioInputId = addedDevice.deviceId } @@ -284,6 +329,9 @@ MediaDevicesManager.prototype = { this.attributes.audioInputId = this._fallbackAudioInputId } } else if (addedDevice.kind === 'videoinput') { + if (BrowserStorage.getItem('videoInputId') === addedDevice.deviceId) { + this.attributes.videoInputId = addedDevice.deviceId + } if (!this._fallbackVideoInputId || addedDevice.deviceId === 'default') { this._fallbackVideoInputId = addedDevice.deviceId } @@ -318,6 +366,18 @@ MediaDevicesManager.prototype = { }) } + if (!this._pendingEnumerateDevicesPromise) { + return this._getUserMediaInternal(constraints) + } + + return this._pendingEnumerateDevicesPromise.then(() => { + return this._getUserMediaInternal(constraints) + }).catch(() => { + return this._getUserMediaInternal(constraints) + }) + }, + + _getUserMediaInternal: function(constraints) { if (constraints.audio && !constraints.audio.deviceId) { if (this.attributes.audioInputId) { if (!(constraints.audio instanceof Object)) { diff --git a/src/utils/webrtc/simplewebrtc/localmedia.js b/src/utils/webrtc/simplewebrtc/localmedia.js index 2d3aab187..35c1563a9 100644 --- a/src/utils/webrtc/simplewebrtc/localmedia.js +++ b/src/utils/webrtc/simplewebrtc/localmedia.js @@ -138,6 +138,12 @@ LocalMedia.prototype.start = function(mediaConstraints, cb, context) { this.emit('localStreamRequested', constraints, context) + if (!context) { + // Try to get the devices list before getting user media. + webrtcIndex.mediaDevicesManager.enableDeviceEvents() + webrtcIndex.mediaDevicesManager.disableDeviceEvents() + } + webrtcIndex.mediaDevicesManager.getUserMedia(constraints).then(function(stream) { // Although the promise should be resolved only if all the constraints // are met Edge resolves it if both audio and video are requested but @@ -165,7 +171,7 @@ LocalMedia.prototype.start = function(mediaConstraints, cb, context) { } track.addEventListener('ended', function() { - if (isAllTracksEnded(stream)) { + if (isAllTracksEnded(stream) && !self._pendingAudioInputIdChangedCount && !self._pendingVideoInputIdChangedCount) { self._removeStream(stream) } }) @@ -204,6 +210,26 @@ LocalMedia.prototype._handleAudioInputIdChanged = function(mediaDevicesManager, return } + this._pendingAudioInputIdChangedCount = 1 + + const resetPendingAudioInputIdChangedCount = () => { + const audioInputIdChangedAgain = this._pendingAudioInputIdChangedCount > 1 + + this._pendingAudioInputIdChangedCount = 0 + + if (audioInputIdChangedAgain) { + this._handleAudioInputIdChanged(webrtcIndex.mediaDevicesManager.get('audioInputId')) + } + + if (!this._pendingAudioInputIdChangedCount && !this._pendingVideoInputIdChangedCount) { + this.localStreams.forEach(stream => { + if (isAllTracksEnded(stream)) { + this._removeStream(stream) + } + }) + } + } + const localStreamsChanged = [] const localTracksReplaced = [] @@ -244,23 +270,15 @@ LocalMedia.prototype._handleAudioInputIdChanged = function(mediaDevicesManager, this.emit('localTrackReplaced', null, trackStreamPair.track, trackStreamPair.stream) }) - return - } + resetPendingAudioInputIdChangedCount() - if (localTracksReplaced.length === 0) { return } - this._pendingAudioInputIdChangedCount = 1 - - const resetPendingAudioInputIdChangedCount = () => { - const audioInputIdChangedAgain = this._pendingAudioInputIdChangedCount > 1 - - this._pendingAudioInputIdChangedCount = 0 + if (localTracksReplaced.length === 0) { + resetPendingAudioInputIdChangedCount() - if (audioInputIdChangedAgain) { - this._handleAudioInputIdChanged(webrtcIndex.mediaDevicesManager.get('audioInputId')) - } + return } webrtcIndex.mediaDevicesManager.getUserMedia({ audio: true }).then(stream => { @@ -303,7 +321,7 @@ LocalMedia.prototype._handleAudioInputIdChanged = function(mediaDevicesManager, } clonedTrack.addEventListener('ended', () => { - if (isAllTracksEnded(stream)) { + if (isAllTracksEnded(stream) && !this._pendingAudioInputIdChangedCount && !this._pendingVideoInputIdChangedCount) { this._removeStream(stream) } }) @@ -337,6 +355,26 @@ LocalMedia.prototype._handleVideoInputIdChanged = function(mediaDevicesManager, return } + this._pendingVideoInputIdChangedCount = 1 + + const resetPendingVideoInputIdChangedCount = () => { + const videoInputIdChangedAgain = this._pendingVideoInputIdChangedCount > 1 + + this._pendingVideoInputIdChangedCount = 0 + + if (videoInputIdChangedAgain) { + this._handleVideoInputIdChanged(webrtcIndex.mediaDevicesManager.get('videoInputId')) + } + + if (!this._pendingAudioInputIdChangedCount && !this._pendingVideoInputIdChangedCount) { + this.localStreams.forEach(stream => { + if (isAllTracksEnded(stream)) { + this._removeStream(stream) + } + }) + } + } + const localStreamsChanged = [] const localTracksReplaced = [] @@ -377,23 +415,15 @@ LocalMedia.prototype._handleVideoInputIdChanged = function(mediaDevicesManager, this.emit('localTrackReplaced', null, trackStreamPair.track, trackStreamPair.stream) }) - return - } + resetPendingVideoInputIdChangedCount() - if (localTracksReplaced.length === 0) { return } - this._pendingVideoInputIdChangedCount = 1 - - const resetPendingVideoInputIdChangedCount = () => { - const videoInputIdChangedAgain = this._pendingVideoInputIdChangedCount > 1 - - this._pendingVideoInputIdChangedCount = 0 + if (localTracksReplaced.length === 0) { + resetPendingVideoInputIdChangedCount() - if (videoInputIdChangedAgain) { - this._handleVideoInputIdChanged(webrtcIndex.mediaDevicesManager.get('videoInputId')) - } + return } webrtcIndex.mediaDevicesManager.getUserMedia({ video: true }).then(stream => { @@ -423,7 +453,7 @@ LocalMedia.prototype._handleVideoInputIdChanged = function(mediaDevicesManager, } clonedTrack.addEventListener('ended', () => { - if (isAllTracksEnded(stream)) { + if (isAllTracksEnded(stream) && !this._pendingAudioInputIdChangedCount && !this._pendingVideoInputIdChangedCount) { this._removeStream(stream) } }) |