diff --git a/react/features/base/media/middleware.js b/react/features/base/media/middleware.js index 47a5b7cdf..b12ce0a36 100644 --- a/react/features/base/media/middleware.js +++ b/react/features/base/media/middleware.js @@ -1,61 +1,95 @@ /* @flow */ -import { CONFERENCE_LEFT } from '../conference'; +import { SET_ROOM } from '../conference'; +import { parseURLParams } from '../config'; import { MiddlewareRegistry } from '../redux'; import { setTrackMuted, TRACK_ADDED } from '../tracks'; -import { - setAudioMuted, - setCameraFacingMode, - setVideoMuted -} from './actions'; +import { setAudioMuted, setCameraFacingMode, setVideoMuted } from './actions'; import { CAMERA_FACING_MODE, MEDIA_TYPE } from './constants'; /** - * Middleware that captures CONFERENCE_LEFT action and restores initial state - * for media devices. Also captures TRACK_ADDED to sync 'muted' state. + * Implements the entry point of the middleware of the feature base/media. * - * @param {Store} store - Redux store. + * @param {Store} store - The redux store. * @returns {Function} */ MiddlewareRegistry.register(store => next => action => { - const result = next(action); - switch (action.type) { - case CONFERENCE_LEFT: - _resetInitialMediaState(store); - break; + case SET_ROOM: + return _setRoom(store, next, action); + + case TRACK_ADDED: { + const result = next(action); - case TRACK_ADDED: action.track.local && _syncTrackMutedState(store, action.track); - break; + + return result; + } } - return result; + return next(action); }); /** - * Resets initial media state. + * Notifies the feature base/media that the action {@link SET_ROOM} is being + * dispatched within a specific redux {@code store}. * - * @param {Store} store - Redux store. + * @param {Store} store - The redux store in which the specified {@code action} + * is being dispatched. + * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the + * specified {@code action} to the specified {@code store}. + * @param {Action} action - The redux action, {@code SET_ROOM}, which is being + * dispatched in the specified {@code store}. * @private - * @returns {void} + * @returns {Object} The new state that is the result of the reduction of the + * specified {@code action}. */ -function _resetInitialMediaState(store) { - const { dispatch, getState } = store; - const state = getState()['features/base/media']; +function _setRoom({ dispatch, getState }, next, action) { + const state = getState(); + let audioMuted; + let videoMuted; - state.audio.muted && dispatch(setAudioMuted(false)); - (state.video.facingMode !== CAMERA_FACING_MODE.USER) + if (action.room) { + // The Jitsi Meet client may override the Jitsi Meet deployment on the + // subject of startWithAudioMuted and/or startWithVideoMuted in the + // (location) URL. + const urlParams + = parseURLParams(state['features/base/connection'].locationURL); + + audioMuted = urlParams['config.startWithAudioMuted']; + videoMuted = urlParams['config.startWithVideoMuted']; + } + + // Of course, the Jitsi Meet deployment may define startWithAudioMuted + // and/or startWithVideoMuted through config.js which should be respected if + // the client did not override it. + const config = state['features/base/config']; + + typeof audioMuted === 'undefined' + && (audioMuted = config.startWithAudioMuted); + typeof videoMuted === 'undefined' + && (videoMuted = config.startWithVideoMuted); + + // Apply startWithAudioMuted and startWithVideoMuted. + const { audio, video } = state['features/base/media']; + + audioMuted = Boolean(audioMuted); + videoMuted = Boolean(videoMuted); + + (audio.muted !== audioMuted) && dispatch(setAudioMuted(audioMuted)); + (video.facingMode !== CAMERA_FACING_MODE.USER) && dispatch(setCameraFacingMode(CAMERA_FACING_MODE.USER)); - state.video.muted && dispatch(setVideoMuted(false)); + (video.muted !== videoMuted) && dispatch(setVideoMuted(videoMuted)); + + return next(action); } /** * Syncs muted state of local media track with muted state from media state. * - * @param {Store} store - Redux store. - * @param {Track} track - Local media track. + * @param {Store} store - The redux store. + * @param {Track} track - The local media track. * @private * @returns {void} */ @@ -66,16 +100,18 @@ function _syncTrackMutedState(store, track) { // XXX If muted state of track when it was added is different from our media // muted state, we need to mute track and explicitly modify 'muted' property // on track. This is because though TRACK_ADDED action was dispatched it's - // not yet in Redux state and JitsiTrackEvents.TRACK_MUTE_CHANGED may be + // not yet in redux state and JitsiTrackEvents.TRACK_MUTE_CHANGED may be // fired before track gets to state. if (track.muted !== muted) { track.muted = muted; setTrackMuted(track.jitsiTrack, muted) .catch(error => { console.error(`setTrackMuted(${muted}) failed`, error); + const setMuted = track.mediaType === MEDIA_TYPE.AUDIO - ? setAudioMuted : setVideoMuted; + ? setAudioMuted + : setVideoMuted; // Failed to sync muted state - dispatch rollback action store.dispatch(setMuted(!muted));