diff --git a/react/features/base/conference/actionTypes.js b/react/features/base/conference/actionTypes.js index fbc03d59b..39aa715bc 100644 --- a/react/features/base/conference/actionTypes.js +++ b/react/features/base/conference/actionTypes.js @@ -152,6 +152,19 @@ export const SET_FOLLOW_ME = Symbol('SET_FOLLOW_ME'); */ export const SET_LASTN = Symbol('SET_LASTN'); +/** + * The type of (redux) action which sets the maximum video height that should be + * received from remote participants, even if the user prefers a larger video + * height. + * + * { + * type: SET_MAX_RECEIVER_VIDEO_QUALITY, + * maxReceiverVideoQuality: number + * } + */ +export const SET_MAX_RECEIVER_VIDEO_QUALITY + = Symbol('SET_MAX_RECEIVER_VIDEO_QUALITY'); + /** * The type of (redux) action which sets the password to join or lock a specific * {@code JitsiConference}. @@ -177,8 +190,8 @@ export const SET_PASSWORD = Symbol('SET_PASSWORD'); export const SET_PASSWORD_FAILED = Symbol('SET_PASSWORD_FAILED'); /** - * The type of (redux) action which sets the maximum video size should be - * received from remote participants. + * The type of (redux) action which sets the preferred maximum video height that + * should be received from remote participants. * * { * type: SET_PREFERRED_RECEIVER_VIDEO_QUALITY, diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index ddbc75c6c..5f976f99a 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -37,6 +37,7 @@ import { SET_DESKTOP_SHARING_ENABLED, SET_FOLLOW_ME, SET_LASTN, + SET_MAX_RECEIVER_VIDEO_QUALITY, SET_PASSWORD, SET_PASSWORD_FAILED, SET_PREFERRED_RECEIVER_VIDEO_QUALITY, @@ -569,6 +570,23 @@ export function setLastN(lastN: ?number) { }; } +/** + * Sets the max frame height that should be received from remote videos. + * + * @param {number} maxReceiverVideoQuality - The max video frame height to + * receive. + * @returns {{ + * type: SET_MAX_RECEIVER_VIDEO_QUALITY, + * maxReceiverVideoQuality: number + * }} + */ +export function setMaxReceiverVideoQuality(maxReceiverVideoQuality: number) { + return { + type: SET_MAX_RECEIVER_VIDEO_QUALITY, + maxReceiverVideoQuality + }; +} + /** * Sets the password to join or lock a specific JitsiConference. * @@ -641,7 +659,8 @@ export function setPassword( } /** - * Sets the max frame height to receive from remote participant videos. + * Sets the max frame height the user prefers to receive from remote participant + * videos. * * @param {number} preferredReceiverVideoQuality - The max video resolution to * receive. diff --git a/react/features/base/conference/middleware.js b/react/features/base/conference/middleware.js index 742458e62..974aa6c7e 100644 --- a/react/features/base/conference/middleware.js +++ b/react/features/base/conference/middleware.js @@ -34,6 +34,7 @@ import { DATA_CHANNEL_OPENED, SET_AUDIO_ONLY, SET_LASTN, + SET_MAX_RECEIVER_VIDEO_QUALITY, SET_PREFERRED_RECEIVER_VIDEO_QUALITY, SET_ROOM } from './actionTypes'; @@ -80,6 +81,9 @@ MiddlewareRegistry.register(store => next => action => { case SET_LASTN: return _setLastN(store, next, action); + case SET_MAX_RECEIVER_VIDEO_QUALITY: + return _setMaximumReceiverVideoQuality(store, next, action); + case SET_PREFERRED_RECEIVER_VIDEO_QUALITY: return _setPreferredReceiverVideoQuality(store, next, action); @@ -434,7 +438,38 @@ function _setLastN({ getState }, next, action) { } /** - * Sets the maximum receive video quality and will turn off audio only mode if + * Sets an internal maximum for the video frame height to receive from remote + * videos. This maximum acts as a cap so user preferences cannot exceed a + * specified frame height. + * + * @private + * @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_MAXIMUM_RECEIVER_VIDEO_QUALITY} which is being dispatched in the + * specified {@code store}. + * @private + * @returns {Object} The value returned by {@code next(action)}. + */ +function _setMaximumReceiverVideoQuality({ getState }, next, action) { + const { conference, preferredReceiverVideoQuality } + = getState()['features/base/conference']; + + if (conference) { + if (typeof preferredReceiverVideoQuality === 'undefined' + || preferredReceiverVideoQuality > action.maxReceiverVideoQuality) { + conference.setReceiverVideoConstraint( + action.maxReceiverVideoQuality); + } + } + + return next(action); +} + +/** + * Sets the preferred receive video quality and will turn off audio only mode if * enabled. * * @param {Store} store - The redux store in which the specified {@code action} @@ -451,12 +486,19 @@ function _setPreferredReceiverVideoQuality( { dispatch, getState }, next, action) { - const { audioOnly, conference } - = getState()['features/base/conference']; + const { + audioOnly, + conference, + maxReceiverVideoQuality + } = getState()['features/base/conference']; if (conference) { - conference.setReceiverVideoConstraint( - action.preferredReceiverVideoQuality); + const { preferredReceiverVideoQuality } = action; + const targetQuality = typeof maxReceiverVideoQuality === 'undefined' + ? preferredReceiverVideoQuality + : Math.min(maxReceiverVideoQuality, preferredReceiverVideoQuality); + + conference.setReceiverVideoConstraint(targetQuality); audioOnly && dispatch(toggleAudioOnly()); } diff --git a/react/features/base/conference/reducer.js b/react/features/base/conference/reducer.js index 3578d8df2..8441fe4df 100644 --- a/react/features/base/conference/reducer.js +++ b/react/features/base/conference/reducer.js @@ -17,6 +17,7 @@ import { SET_AUDIO_ONLY, SET_DESKTOP_SHARING_ENABLED, SET_FOLLOW_ME, + SET_MAX_RECEIVER_VIDEO_QUALITY, SET_PASSWORD, SET_PREFERRED_RECEIVER_VIDEO_QUALITY, SET_ROOM, @@ -69,6 +70,12 @@ ReducerRegistry.register('features/base/conference', (state = {}, action) => { case SET_LOCATION_URL: return set(state, 'room', undefined); + case SET_MAX_RECEIVER_VIDEO_QUALITY: + return set( + state, + 'maxReceiverVideoQuality', + action.maxReceiverVideoQuality); + case SET_PASSWORD: return _setPassword(state, action);