From 122a7f63467535d907a22ee7d6a2db29e0cb5236 Mon Sep 17 00:00:00 2001 From: paweldomas Date: Mon, 24 Jul 2017 17:36:19 +0200 Subject: [PATCH] fix(AudioOnly+web): crash when untoggle audio only Because on web video track is stored both in redux and in 'localVideo' field, video is attempted to be unmuted twice when turning off the audio only mode. This will crash the app with 'unmute operation is already in progress'. This commit will prevent from taking action from the web world if the video track already exists and will make the redux side rollback unmuted status in case unmute fails. --- conference.js | 21 ++++++++++++++++++++- react/features/base/media/middleware.js | 13 +++++++++++-- react/features/base/tracks/functions.js | 6 +----- react/features/base/tracks/middleware.js | 14 +++++++++++++- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/conference.js b/conference.js index bb99581d8..52aaea994 100644 --- a/conference.js +++ b/conference.js @@ -752,6 +752,9 @@ export default { if (!this._localTracksInitialized) { this.videoMuted = mute; + return; + } else if (localVideo && localVideo.isMuted() === mute) { + // NO-OP return; } @@ -1994,7 +1997,23 @@ export default { ); APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly => { - this.muteVideo(audioOnly); + + // FIXME On web video track is stored both in redux and in + // 'localVideo' field, video is attempted to be unmuted twice when + // turning off the audio only mode. This will crash the app with + // 'unmute operation is already in progress'. + // Because there's no logic in redux about creating new track in + // case unmute when not track exists the things have to go through + // muteVideo logic in such case. + const tracks = APP.store.getState()['features/base/tracks']; + const isTrackInRedux + = Boolean( + tracks.find( + track => track.jitsiTrack + && track.jitsiTrack.getType() === 'video')); + if (!isTrackInRedux) { + this.muteVideo(audioOnly); + } // Immediately update the UI by having remote videos and the large // video update themselves instead of waiting for some other event diff --git a/react/features/base/media/middleware.js b/react/features/base/media/middleware.js index 605eb0a61..47a5b7cdf 100644 --- a/react/features/base/media/middleware.js +++ b/react/features/base/media/middleware.js @@ -9,7 +9,7 @@ import { setCameraFacingMode, setVideoMuted } from './actions'; -import { CAMERA_FACING_MODE } from './constants'; +import { CAMERA_FACING_MODE, MEDIA_TYPE } from './constants'; /** * Middleware that captures CONFERENCE_LEFT action and restores initial state @@ -70,6 +70,15 @@ function _syncTrackMutedState(store, track) { // fired before track gets to state. if (track.muted !== muted) { track.muted = muted; - setTrackMuted(track.jitsiTrack, muted); + setTrackMuted(track.jitsiTrack, muted) + .catch(error => { + console.error(`setTrackMuted(${muted}) failed`, error); + const setMuted + = track.mediaType === MEDIA_TYPE.AUDIO + ? setAudioMuted : setVideoMuted; + + // Failed to sync muted state - dispatch rollback action + store.dispatch(setMuted(!muted)); + }); } } diff --git a/react/features/base/tracks/functions.js b/react/features/base/tracks/functions.js index f452b516d..b0b3ba28b 100644 --- a/react/features/base/tracks/functions.js +++ b/react/features/base/tracks/functions.js @@ -174,9 +174,5 @@ export function setTrackMuted(track, muted) { const f = muted ? 'mute' : 'unmute'; - return track[f]() - .catch(err => { - console.warn(`Track ${f} was rejected:`, err); - throw err; - }); + return track[f](); } diff --git a/react/features/base/tracks/middleware.js b/react/features/base/tracks/middleware.js index 388d0d44c..d44c63987 100644 --- a/react/features/base/tracks/middleware.js +++ b/react/features/base/tracks/middleware.js @@ -160,7 +160,19 @@ function _getLocalTrack(store, mediaType: MEDIA_TYPE) { function _setMuted(store, action, mediaType: MEDIA_TYPE) { const localTrack = _getLocalTrack(store, mediaType); - localTrack && setTrackMuted(localTrack.jitsiTrack, action.muted); + if (localTrack) { + setTrackMuted(localTrack.jitsiTrack, action.muted) + .catch(error => { + console.error(`setTrackMuted(${action.muted}) failed`, error); + + const setMuted + = mediaType === MEDIA_TYPE.AUDIO + ? setAudioMuted : setVideoMuted; + + // Failed to modify muted state - dispatch rollback action + store.dispatch(setMuted(!action.muted)); + }); + } } /**