diff --git a/src/utils/webrtc/simplewebrtc/localmedia.js b/src/utils/webrtc/simplewebrtc/localmedia.js index 35c1563a931..bcb69904db8 100644 --- a/src/utils/webrtc/simplewebrtc/localmedia.js +++ b/src/utils/webrtc/simplewebrtc/localmedia.js @@ -144,6 +144,10 @@ LocalMedia.prototype.start = function(mediaConstraints, cb, context) { webrtcIndex.mediaDevicesManager.disableDeviceEvents() } + // The handlers for "change:audioInputId" and "change:videoInputId" events + // expect the initial "getUserMedia" call to have been completed before + // being used, so they must be set when the promise is resolved or rejected. + 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 @@ -187,8 +191,8 @@ LocalMedia.prototype.start = function(mediaConstraints, cb, context) { } }).catch(function(err) { // Fallback for users without a camera or with a camera that can not be - // accessed. - if (self.config.audioFallback && constraints.video !== false) { + // accessed, but only if audio is meant to be used. + if (constraints.audio !== false && self.config.audioFallback && constraints.video !== false) { self.emit('localStreamRequestFailedRetryNoVideo', constraints, err) constraints.video = false self.start(constraints, cb, 'retry-no-video') @@ -197,6 +201,9 @@ LocalMedia.prototype.start = function(mediaConstraints, cb, context) { self.emit('localStreamRequestFailed', constraints) + webrtcIndex.mediaDevicesManager.on('change:audioInputId', self._handleAudioInputIdChangedBound) + webrtcIndex.mediaDevicesManager.on('change:videoInputId', self._handleVideoInputIdChangedBound) + if (cb) { return cb(err, null) } @@ -484,10 +491,8 @@ LocalMedia.prototype.stop = function(stream) { this.stopStream(stream) this.stopScreenShare(stream) - if (!this.localStreams.length) { - webrtcIndex.mediaDevicesManager.off('change:audioInputId', this._handleAudioInputIdChangedBound) - webrtcIndex.mediaDevicesManager.off('change:videoInputId', this._handleVideoInputIdChangedBound) - } + webrtcIndex.mediaDevicesManager.off('change:audioInputId', this._handleAudioInputIdChangedBound) + webrtcIndex.mediaDevicesManager.off('change:videoInputId', this._handleVideoInputIdChangedBound) } LocalMedia.prototype.stopStream = function(stream) { diff --git a/src/utils/webrtc/webrtc.js b/src/utils/webrtc/webrtc.js index 5be62030f84..085e32ffe32 100644 --- a/src/utils/webrtc/webrtc.js +++ b/src/utils/webrtc/webrtc.js @@ -48,6 +48,7 @@ let callParticipantCollection = null let localCallParticipantModel = null let showedTURNWarning = false let sendCurrentStateWithRepetitionTimeout = null +let startedWithMedia function arrayDiff(a, b) { return a.filter(function(i) { @@ -524,6 +525,8 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local }) webrtc.startMedia = function(token) { + startedWithMedia = undefined + webrtc.joinCall(token) } @@ -844,6 +847,8 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local // reconnection is forced to start sending it. signaling.setSendVideoIfAvailable(true) + startedWithMedia = true + let flags = signaling.getCurrentCallFlags() flags |= PARTICIPANT.CALL_FLAG.WITH_VIDEO @@ -909,9 +914,38 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local } }) + webrtc.on('localTrackReplaced', function(newTrack /*, oldTrack, stream */) { + // Device disabled, nothing to do here. + if (!newTrack) { + return + } + + // If the call was started with media the connections will be already + // established. If it has not started yet the connections will be + // established once started. + if (startedWithMedia || startedWithMedia === undefined) { + return + } + + // If the call was originally started without media the participant + // needs to reconnect to establish the sender connections. + startedWithMedia = true + + let flags = signaling.getCurrentCallFlags() + if (newTrack.kind === 'audio') { + flags |= PARTICIPANT.CALL_FLAG.WITH_AUDIO + } else if (newTrack.kind === 'video') { + flags |= PARTICIPANT.CALL_FLAG.WITH_VIDEO + } + + forceReconnect(signaling, flags) + }) + webrtc.on('localMediaStarted', function(/* configuration */) { console.info('localMediaStarted') + startedWithMedia = true + clearLocalStreamRequestedTimeoutAndHideNotification() if (signaling.hasFeature('mcu')) { @@ -922,8 +956,16 @@ export default function initWebRTC(signaling, _callParticipantCollection, _local webrtc.on('localMediaError', function(error) { console.warn('Access to microphone & camera failed', error) + startedWithMedia = false + clearLocalStreamRequestedTimeoutAndHideNotification() + if (error.name === 'TypeError') { + // Both audio and video were explicitly disabled, no need to show an + // error. + return + } + let message let timeout = TOAST_PERMANENT_TIMEOUT if ((error.name === 'NotSupportedError'