diff --git a/src/components/CallView/shared/LocalVideo.vue b/src/components/CallView/shared/LocalVideo.vue index e91b043e703..1c802b81d0d 100644 --- a/src/components/CallView/shared/LocalVideo.vue +++ b/src/components/CallView/shared/LocalVideo.vue @@ -252,6 +252,10 @@ export default { screenshotModeUrl() { return this.screenshotMode ? placeholderImage(8) : '' }, + + localStreamAttachIdentifier() { + return [this.localMediaModel.attributes.localStream, this.localMediaModel.attributes.videoAvailable] + }, }, watch: { @@ -269,7 +273,14 @@ export default { }, }, - 'localMediaModel.attributes.localStream': function(localStream) { + localStreamAttachIdentifier([localStream, videoAvailable], [oldLocalStream, oldVideoAvailable]) { + // Reattach the stream if it was replaced, or if the video is + // available again after the local track was replaced, to + // prevent rendering the last frame drawn before the change. + if (localStream === oldLocalStream && !(videoAvailable && !oldVideoAvailable)) { + return + } + this._setLocalStream(localStream) }, diff --git a/src/components/CallView/shared/VideoVue.vue b/src/components/CallView/shared/VideoVue.vue index 0f15b82c381..8f72452b68d 100644 --- a/src/components/CallView/shared/VideoVue.vue +++ b/src/components/CallView/shared/VideoVue.vue @@ -545,10 +545,21 @@ export default { screenshotModeUrl() { return this.screenshotMode ? placeholderImage(6) : '' }, + + streamAttachTrigger() { + return [this.model.attributes.stream, this.hasVideo] + }, }, watch: { - 'model.attributes.stream': function(stream) { + streamAttachTrigger([stream, hasVideo], [oldStream, oldHasVideo]) { + // Reattach the stream if it was replaced, or if the video is + // available again after the remote track was replaced, to + // prevent rendering the last frame received before the change. + if (stream === oldStream && !(hasVideo && !oldHasVideo)) { + return + } + this._setStream(stream) }, diff --git a/src/utils/webrtc/webrtc.js b/src/utils/webrtc/webrtc.js index 91869c50964..b827efa15ff 100644 --- a/src/utils/webrtc/webrtc.js +++ b/src/utils/webrtc/webrtc.js @@ -525,6 +525,8 @@ export function initWebRtc(signaling, _callParticipantCollection, _localCallPart // is received before the "leave call" request ends. localUserInCall = false + clearTimeout(updateCallFlagsFromLocalMediaTimeout) + localStateBroadcaster.destroy() localStateBroadcaster = null }) @@ -1422,6 +1424,24 @@ export function initWebRtc(signaling, _callParticipantCollection, _localCallPart return callFlags } + let updateCallFlagsFromLocalMediaTimeout = null + + /** + * Update the call flags to match the current local tracks. + */ + function updateCallFlagsFromLocalMedia() { + // As update can be deferred, check whether the call may have been left in the meantime + if (!localUserInCall) { + return + } + + const callFlags = getCallFlagsFromLocalMedia() + + if (signaling.getCurrentCallFlags() !== callFlags) { + signaling.updateCurrentCallFlags(callFlags) + } + } + signaling.on('joinCall', function(token) { const expectedCallFlags = getCallFlagsFromLocalMedia() @@ -1463,6 +1483,8 @@ export function initWebRtc(signaling, _callParticipantCollection, _localCallPart } webrtc.on('localTrackReplaced', function(newTrack, oldTrack/* , stream */) { + clearTimeout(updateCallFlagsFromLocalMediaTimeout) + const callFlags = getCallFlagsFromLocalMedia() // A reconnection is not needed if a device is disabled or if there are @@ -1479,7 +1501,13 @@ export function initWebRtc(signaling, _callParticipantCollection, _localCallPart return } - if (signaling.getCurrentCallFlags() !== callFlags) { + const removedCallFlags = signaling.getCurrentCallFlags() & ~callFlags + if (removedCallFlags) { + // If a media is no longer sent because of "MediaDevicesManager._stopIncompatibleTracks()" + // (flags change 7->5->7 or 7->3->7 on input device replacement), defer the update + // and give some time for the new track to be set, then check local flags again. + updateCallFlagsFromLocalMediaTimeout = setTimeout(updateCallFlagsFromLocalMedia, 1_000) + } else if (signaling.getCurrentCallFlags() !== callFlags) { signaling.updateCurrentCallFlags(callFlags) } })