ref(receiver-constraints): Refactor and fixes.

This commit is contained in:
Hristo Terezov 2022-08-16 14:56:47 +03:00
parent cfb1fef162
commit adcd9a501b
15 changed files with 639 additions and 275 deletions

View File

@ -9,6 +9,7 @@ import { browser } from '../../../react/features/base/lib-jitsi-meet';
import { isTestModeEnabled } from '../../../react/features/base/testing';
import { FILMSTRIP_BREAKPOINT } from '../../../react/features/filmstrip';
import { ORIENTATION, LargeVideoBackground, updateLastLargeVideoMediaEvent } from '../../../react/features/large-video';
import { setLargeVideoDimensions } from '../../../react/features/large-video/actions.any';
import { LAYOUTS, getCurrentLayout } from '../../../react/features/video-layout';
/* eslint-enable no-unused-vars */
import UIUtil from '../util/UIUtil';
@ -446,6 +447,8 @@ export class VideoContainer extends LargeContainer {
const { horizontalIndent, verticalIndent }
= this.getVideoPosition(width, height, containerWidth, containerHeight, verticalFilmstripWidth);
APP.store.dispatch(setLargeVideoDimensions(height, width));
this.$wrapper.animate({
width,
height,

View File

@ -7,6 +7,7 @@ import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
import {
FILMSTRIP_TYPE
} from '../../constants';
import { getScreenshareFilmstripParticipantId } from '../../functions';
import Filmstrip from './Filmstrip';
@ -105,15 +106,9 @@ function _mapStateToProps(state) {
filmstripHeight,
filmstripWidth,
thumbnailSize
},
screenshareFilmstripParticipantId
}
} = state['features/filmstrip'];
const screenshares = state['features/video-layout'].remoteScreenShares;
let id = screenshares.find(sId => sId === screenshareFilmstripParticipantId);
if (!id && screenshares.length) {
id = screenshares[0];
}
const id = getScreenshareFilmstripParticipantId(state);
return {
_columns: 1,

View File

@ -71,6 +71,15 @@ export function shouldRemoteVideosBeVisible(state: Object) {
|| disable1On1Mode);
}
/**
* Not implemented on mobile.
*
* @returns {Array<string>}
*/
export function getActiveParticipantsIds() {
return [];
}
/**
* Returns the number of participants displayed in tile view.
*
@ -169,6 +178,16 @@ export function isStageFilmstripEnabled() {
return false;
}
/**
* Whether or not the top panel is enabled.
*
* @returns {boolean}
*/
export function isTopPanelEnabled() {
return false;
}
/**
* Calculates the width and height of the filmstrip based on the screen size and aspect ratio.
*
@ -232,4 +251,13 @@ export function shouldDisplayLocalThumbnailSeparately() {
return Platform.OS !== 'android';
}
/**
* Not implemented on mobile.
*
* @returns {undefined}
*/
export function getScreenshareFilmstripParticipantId() {
return undefined;
}

View File

@ -778,6 +778,24 @@ export function getThumbnailTypeFromLayout(currentLayout, filmstripType) {
}
}
/**
* Returns the id of the participant displayed on the screen share filmstrip.
*
* @param {Object} state - Redux state.
* @returns {string} - The participant id.
*/
export function getScreenshareFilmstripParticipantId(state) {
const { screenshareFilmstripParticipantId } = state['features/filmstrip'];
const screenshares = state['features/video-layout'].remoteScreenShares;
let id = screenshares.find(sId => sId === screenshareFilmstripParticipantId);
if (!id && screenshares.length) {
id = screenshares[0];
}
return id;
}
/**
* Whether or not the top panel is enabled.
*

View File

@ -9,6 +9,17 @@
export const SELECT_LARGE_VIDEO_PARTICIPANT
= 'SELECT_LARGE_VIDEO_PARTICIPANT';
/**
* Action to set the dimensions of the large video.
*
* {
* type: SET_LARGE_VIDEO_DIMENSIONS,
* height: number,
* width: number
* }
*/
export const SET_LARGE_VIDEO_DIMENSIONS = 'SET_LARGE_VIDEO_DIMENSIONS';
/**
* Action to update the redux store with the current resolution of large video.
*

View File

@ -13,6 +13,7 @@ import { isStageFilmstripAvailable } from '../filmstrip/functions';
import {
SELECT_LARGE_VIDEO_PARTICIPANT,
SET_LARGE_VIDEO_DIMENSIONS,
UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION
} from './actionTypes';
@ -79,6 +80,25 @@ export function updateKnownLargeVideoResolution(resolution: number) {
};
}
/**
* Sets the dimenstions of the large video in redux.
*
* @param {number} height - The height of the large video.
* @param {number} width - The width of the large video.
* @returns {{
* type: SET_LARGE_VIDEO_DIMENSIONS,
* height: number,
* width: number
* }}
*/
export function setLargeVideoDimensions(height, width) {
return {
type: SET_LARGE_VIDEO_DIMENSIONS,
height,
width
};
}
/**
* Returns the most recent existing remote video track.
*

View File

@ -3,6 +3,7 @@ import ReducerRegistry from '../base/redux/ReducerRegistry';
import {
SELECT_LARGE_VIDEO_PARTICIPANT,
SET_LARGE_VIDEO_DIMENSIONS,
UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION,
UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT,
SET_SEE_WHAT_IS_BEING_SHARED
@ -38,6 +39,13 @@ ReducerRegistry.register('features/large-video', (state: ILargeVideoState = {},
participantId: action.participantId
};
case SET_LARGE_VIDEO_DIMENSIONS:
return {
...state,
height: action.height,
width: action.width
};
case UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION:
return {
...state,

View File

@ -10,6 +10,8 @@ import {
import { isStageFilmstripAvailable } from '../filmstrip/functions';
import { isVideoPlaying } from '../shared-video/functions';
import { VIDEO_QUALITY_LEVELS } from '../video-quality/constants';
import { getReceiverVideoQualityLevel } from '../video-quality/functions';
import { getMinHeightForQualityLvlMap } from '../video-quality/selector';
import { LAYOUTS } from './constants';
@ -154,7 +156,7 @@ export function isLayoutTileView(state: Object) {
}
/**
* Gets the video quality for the given height.
* Returns the video quality for the given height.
*
* @param {number|undefined} height - Height of the video container.
* @returns {number}
@ -177,36 +179,56 @@ function getVideoQualityForHeight(height: number) {
}
/**
* Gets the video quality level for the resizable filmstrip thumbnail height.
* Returns the video quality level for the resizable filmstrip thumbnail height.
*
* @param {number} height - The height of the thumbnail.
* @param {Object} state - Redux state.
* @returns {number}
*/
export function getVideoQualityForResizableFilmstripThumbnails(state) {
const height = state['features/filmstrip'].verticalViewDimensions?.gridView?.thumbnailSize?.height;
export function getVideoQualityForResizableFilmstripThumbnails(height, state) {
if (!height) {
return VIDEO_QUALITY_LEVELS.LOW;
}
return getVideoQualityForHeight(height);
return getReceiverVideoQualityLevel(height, getMinHeightForQualityLvlMap(state));
}
/**
* Gets the video quality for the large video.
*
* @returns {number}
*/
export function getVideoQualityForLargeVideo() {
const wrapper = document.querySelector('#largeVideoWrapper');
return getVideoQualityForHeight(wrapper.clientHeight);
}
/**
* Gets the video quality level for the thumbnails in the stage filmstrip.
* Returns the video quality level for the screen sharing filmstrip thumbnail height.
*
* @param {number} height - The height of the thumbnail.
* @param {Object} state - Redux state.
* @returns {number}
*/
export function getVideoQualityForStageThumbnails(state) {
const height = state['features/filmstrip'].stageFilmstripDimensions?.thumbnailSize?.height;
export function getVideoQualityForScreenSharingFilmstrip(height, state) {
if (!height) {
return VIDEO_QUALITY_LEVELS.LOW;
}
return getVideoQualityForHeight(height);
return getReceiverVideoQualityLevel(height, getMinHeightForQualityLvlMap(state));
}
/**
* Returns the video quality for the large video.
*
* @param {number} largeVideoHeight - The height of the large video.
* @returns {number} - The video quality for the large video.
*/
export function getVideoQualityForLargeVideo(largeVideoHeight) {
return getVideoQualityForHeight(largeVideoHeight);
}
/**
* Returns the video quality level for the thumbnails in the stage filmstrip.
*
* @param {number} height - The height of the thumbnails.
* @param {Object} state - Redux state.
* @returns {number}
*/
export function getVideoQualityForStageThumbnails(height, state) {
if (!height) {
return VIDEO_QUALITY_LEVELS.LOW;
}
return getReceiverVideoQualityLevel(height, getMinHeightForQualityLvlMap(state));
}

View File

@ -1,5 +1,7 @@
import { MEDIA_TYPE, setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../base/media';
import { MiddlewareRegistry } from '../base/redux';
import { CLIENT_RESIZED } from '../base/responsive-ui';
import { setLargeVideoDimensions } from '../large-video/actions.any';
import { SET_CAR_MODE } from './actionTypes';
import './middleware.any';
@ -18,6 +20,13 @@ MiddlewareRegistry.register(store => next => action => {
case SET_CAR_MODE:
dispatch(setVideoMuted(action.enabled, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.CAR_MODE));
break;
case CLIENT_RESIZED: {
const { clientHeight, clientWidth } = store.getState()['features/base/responsive-ui'];
// On mobile the large video should always fill the screen.
dispatch(setLargeVideoDimensions(clientHeight, clientWidth));
break;
}
}
return result;

View File

@ -1,3 +1,65 @@
/**
* The type of (redux) action which sets the maximum video height that should be
* received from remote participants for the large video, even if the user prefers a larger video
* height.
*
* {
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO,
* maxReceiverVideoQuality: number
* }
*/
export const SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO = 'SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO';
/**
* The type of (redux) action which sets the maximum video height that should be
* received from remote participants for the screen sharing filmstrip, even if the user prefers a larger video
* height.
*
* {
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP,
* maxReceiverVideoQuality: number
* }
*/
export const SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP = 'SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP';
/**
* The type of (redux) action which sets the maximum video height that should be
* received from remote participants for stage filmstrip, even if the user prefers a larger video
* height.
*
* {
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP,
* maxReceiverVideoQuality: number
* }
*/
export const SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP = 'SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP';
/**
* The type of (redux) action which sets the maximum video height that should be
* received from remote participants for tile view, even if the user prefers a larger video
* height.
*
* {
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW,
* maxReceiverVideoQuality: number
* }
*/
export const SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW = 'SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW';
/**
* The type of (redux) action which sets the maximum video height that should be
* received from remote participants for vertical filmstrip, even if the user prefers a larger video
* height.
*
* {
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP,
* maxReceiverVideoQuality: number
* }
*/
export const SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP = 'SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP';
/**
* The type of (redux) action which sets the preferred maximum video height that
* should be sent to and received from remote participants.
@ -7,17 +69,4 @@
* preferredVideoQuality: number
* }
*/
export const SET_PREFERRED_VIDEO_QUALITY = 'SET_PREFERRED_VIDEO_QUALITY';
/**
* 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 = 'SET_MAX_RECEIVER_VIDEO_QUALITY';
export const SET_PREFERRED_VIDEO_QUALITY = 'SET_PREFERRED_VIDEO_QUALITY';

View File

@ -2,10 +2,102 @@
import type { Dispatch } from 'redux';
import { SET_MAX_RECEIVER_VIDEO_QUALITY, SET_PREFERRED_VIDEO_QUALITY } from './actionTypes';
import { VIDEO_QUALITY_LEVELS } from './constants';
import {
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP,
SET_PREFERRED_VIDEO_QUALITY
} from './actionTypes';
import { MAX_VIDEO_QUALITY, VIDEO_QUALITY_LEVELS } from './constants';
import logger from './logger';
/**
* Sets the max frame height that should be received for the large video.
*
* @param {number} maxReceiverVideoQuality - The max video frame height to
* receive.
* @returns {{
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO,
* maxReceiverVideoQuality: number
* }}
*/
export function setMaxReceiverVideoQualityForLargeVideo(maxReceiverVideoQuality: number) {
return {
type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO,
maxReceiverVideoQuality
};
}
/**
* Sets the max frame height that should be received for the screen sharing filmstrip particpant.
*
* @param {number} maxReceiverVideoQuality - The max video frame height to
* receive.
* @returns {{
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP,
* maxReceiverVideoQuality: number
* }}
*/
export function setMaxReceiverVideoQualityForScreenSharingFilmstrip(maxReceiverVideoQuality: number) {
return {
type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP,
maxReceiverVideoQuality
};
}
/**
* Sets the max frame height that should be received from remote videos for the stage filmstrip.
*
* @param {number} maxReceiverVideoQuality - The max video frame height to
* receive.
* @returns {{
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP,
* maxReceiverVideoQuality: number
* }}
*/
export function setMaxReceiverVideoQualityForStageFilmstrip(maxReceiverVideoQuality: number) {
return {
type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP,
maxReceiverVideoQuality
};
}
/**
* Sets the max frame height that should be received from remote videos in tile view.
*
* @param {number} maxReceiverVideoQuality - The max video frame height to
* receive.
* @returns {{
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW,
* maxReceiverVideoQuality: number
* }}
*/
export function setMaxReceiverVideoQualityForTileView(maxReceiverVideoQuality: number) {
return {
type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW,
maxReceiverVideoQuality
};
}
/**
* Sets the max frame height that should be received from remote videos for the vertical filmstrip.
*
* @param {number} maxReceiverVideoQuality - The max video frame height to
* receive.
* @returns {{
* type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP,
* maxReceiverVideoQuality: number
* }}
*/
export function setMaxReceiverVideoQualityForVerticalFilmstrip(maxReceiverVideoQuality: number) {
return {
type: SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP,
maxReceiverVideoQuality
};
}
/**
* Sets the max frame height the user prefers to send and receive from the
* remote participants.
@ -24,24 +116,6 @@ export function setPreferredVideoQuality(preferredVideoQuality: 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 maximum video size the local participant should send and receive from
* remote participants.
@ -58,6 +132,6 @@ export function setVideoQuality(frameHeight: number) {
return;
}
dispatch(setPreferredVideoQuality(Math.min(frameHeight, VIDEO_QUALITY_LEVELS.HIGH)));
dispatch(setPreferredVideoQuality(Math.min(frameHeight, MAX_VIDEO_QUALITY)));
};
}

View File

@ -19,6 +19,16 @@ export const VIDEO_QUALITY_LEVELS = {
NONE: 0
};
/**
* Indicates unlimited video quality.
*/
export const VIDEO_QUALITY_UNLIMITED = -1;
/**
* The maximum video quality from the VIDEO_QUALITY_LEVELS map.
*/
export const MAX_VIDEO_QUALITY = Math.max(...Object.values(VIDEO_QUALITY_LEVELS));
/**
* Maps quality level names used in the config.videoQuality.minHeightForQualityLvl to the quality level constants used
* by the application.
@ -28,5 +38,6 @@ export const VIDEO_QUALITY_LEVELS = {
export const CFG_LVL_TO_APP_QUALITY_LVL = {
'low': VIDEO_QUALITY_LEVELS.LOW,
'standard': VIDEO_QUALITY_LEVELS.STANDARD,
'high': VIDEO_QUALITY_LEVELS.HIGH
'high': VIDEO_QUALITY_LEVELS.HIGH,
'ultra': VIDEO_QUALITY_LEVELS.ULTRA
};

View File

@ -1,28 +1,5 @@
// @flow
import { CFG_LVL_TO_APP_QUALITY_LVL, VIDEO_QUALITY_LEVELS } from './constants';
const { LOW, STANDARD, HIGH, ULTRA } = VIDEO_QUALITY_LEVELS;
const videoQualityLevels = [ LOW, STANDARD, HIGH, ULTRA ];
/**
* Finds the nearest video quality level to the passed video quality.
*
* @param {number} videoQuality - The video quality.
* @returns {number|undefined} - The found quality level.
*/
export function findNearestQualityLevel(videoQuality: number) {
for (let i = 0; i < videoQualityLevels.length; i++) {
const level = videoQualityLevels[i];
if (level >= videoQuality) {
return level;
}
}
return undefined;
}
/**
* Selects {@code VIDEO_QUALITY_LEVELS} for the given {@link availableHeight} and threshold to quality mapping.
*
@ -52,7 +29,7 @@ export function getReceiverVideoQualityLevel(availableHeight: number, heightToLe
* @returns {Map<number, number>|undefined} - A mapping of minimal thumbnail height required for given quality level or
* {@code undefined} if the map contains invalid values.
*/
export function validateMinHeightForQualityLvl(minHeightForQualityLvl: Object): ?Map<number, number> {
export function validateMinHeightForQualityLvl(minHeightForQualityLvl) {
if (typeof minHeightForQualityLvl !== 'object'
|| Object.keys(minHeightForQualityLvl).map(lvl => Number(lvl))
.find(lvl => lvl === null || isNaN(lvl) || lvl < 0)) {
@ -65,6 +42,13 @@ export function validateMinHeightForQualityLvl(minHeightForQualityLvl: Object):
.sort((a, b) => a - b);
const map = new Map();
Object.values(VIDEO_QUALITY_LEVELS).sort()
.forEach(value => {
if (value > VIDEO_QUALITY_LEVELS.NONE) {
map.set(value, value);
}
});
for (const level of levelsSorted) {
const configQuality = minHeightForQualityLvl[level];
const appQuality = CFG_LVL_TO_APP_QUALITY_LVL[configQuality];
@ -73,6 +57,7 @@ export function validateMinHeightForQualityLvl(minHeightForQualityLvl: Object):
return undefined;
}
map.delete(appQuality);
map.set(level, appQuality);
}

View File

@ -4,7 +4,14 @@ import PersistenceRegistry from '../base/redux/PersistenceRegistry';
import ReducerRegistry from '../base/redux/ReducerRegistry';
import { set } from '../base/redux/functions';
import { SET_MAX_RECEIVER_VIDEO_QUALITY, SET_PREFERRED_VIDEO_QUALITY } from './actionTypes';
import {
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW,
SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP,
SET_PREFERRED_VIDEO_QUALITY
} from './actionTypes';
import { VIDEO_QUALITY_LEVELS } from './constants';
/* eslint-disable-next-line lines-around-comment */
// @ts-ignore
@ -12,16 +19,29 @@ import { validateMinHeightForQualityLvl } from './functions';
import logger from './logger';
const DEFAULT_STATE = {
maxReceiverVideoQuality: VIDEO_QUALITY_LEVELS.ULTRA,
maxReceiverVideoQualityForLargeVideo: VIDEO_QUALITY_LEVELS.ULTRA,
maxReceiverVideoQualityForScreenSharingFilmstrip: VIDEO_QUALITY_LEVELS.HIGH,
maxReceiverVideoQualityForStageFilmstrip: VIDEO_QUALITY_LEVELS.HIGH,
maxReceiverVideoQualityForTileView: VIDEO_QUALITY_LEVELS.STANDARD,
maxReceiverVideoQualityForVerticalFilmstrip: VIDEO_QUALITY_LEVELS.LOW,
minHeightForQualityLvl: new Map(),
preferredVideoQuality: VIDEO_QUALITY_LEVELS.ULTRA
};
DEFAULT_STATE.minHeightForQualityLvl.set(360, VIDEO_QUALITY_LEVELS.STANDARD);
DEFAULT_STATE.minHeightForQualityLvl.set(720, VIDEO_QUALITY_LEVELS.HIGH);
Object.values(VIDEO_QUALITY_LEVELS).sort()
.forEach(value => {
if (value > VIDEO_QUALITY_LEVELS.NONE) {
DEFAULT_STATE.minHeightForQualityLvl.set(value, value);
}
});
export interface IVideoQualityState {
maxReceiverVideoQuality: number;
maxReceiverVideoQualityForLargeVideo: number,
maxReceiverVideoQualityForScreenSharingFilmstrip: number,
maxReceiverVideoQualityForStageFilmstrip: number,
maxReceiverVideoQualityForTileView: number,
maxReceiverVideoQualityForVerticalFilmstrip: number,
minHeightForQualityLvl: Map<number, number>;
preferredVideoQuality: number;
}
@ -56,10 +76,28 @@ ReducerRegistry.register('features/video-quality',
switch (action.type) {
case SET_CONFIG:
return _setConfig(state, action);
case SET_MAX_RECEIVER_VIDEO_QUALITY:
case SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_LARGE_VIDEO:
return set(state,
'maxReceiverVideoQualityForLargeVideo',
action.maxReceiverVideoQuality);
case SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_SCREEN_SHARING_FILMSTRIP:
return set(state,
'maxReceiverVideoQualityForScreenSharingFilmstrip',
action.maxReceiverVideoQuality);
case SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_STAGE_FILMSTRIP:
return set(
state,
'maxReceiverVideoQuality',
'maxReceiverVideoQualityForStageFilmstrip',
action.maxReceiverVideoQuality);
case SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_TILE_VIEW:
return set(
state,
'maxReceiverVideoQualityForTileView',
action.maxReceiverVideoQuality);
case SET_MAX_RECEIVER_VIDEO_QUALITY_FOR_VERTICAL_FILMSTRIP:
return set(
state,
'maxReceiverVideoQualityForVerticalFilmstrip',
action.maxReceiverVideoQuality);
case SET_PREFERRED_VIDEO_QUALITY: {
const { preferredVideoQuality } = action;

View File

@ -1,30 +1,40 @@
// @flow
/* global APP */
import debounce from 'lodash/debounce';
import { _handleParticipantError } from '../base/conference';
import { getSourceNameSignalingFeatureFlag } from '../base/config';
import { MEDIA_TYPE } from '../base/media';
import { getLocalParticipant, getParticipantCount } from '../base/participants';
import { getLocalParticipant } from '../base/participants';
import { StateListenerRegistry } from '../base/redux';
import { getRemoteScreenSharesSourceNames, getTrackSourceNameByMediaTypeAndParticipant } from '../base/tracks';
import { reportError } from '../base/util';
import { getActiveParticipantsIds } from '../filmstrip/functions.web';
import {
getActiveParticipantsIds,
getScreenshareFilmstripParticipantId,
isTopPanelEnabled
} from '../filmstrip/functions';
import {
getVideoQualityForLargeVideo,
getVideoQualityForResizableFilmstripThumbnails,
getVideoQualityForStageThumbnails,
LAYOUTS,
shouldDisplayTileView
} from '../video-layout';
import { getCurrentLayout, getVideoQualityForScreenSharingFilmstrip } from '../video-layout/functions.any';
import { setMaxReceiverVideoQuality } from './actions';
import { VIDEO_QUALITY_LEVELS } from './constants';
import {
setMaxReceiverVideoQualityForLargeVideo,
setMaxReceiverVideoQualityForScreenSharingFilmstrip,
setMaxReceiverVideoQualityForStageFilmstrip,
setMaxReceiverVideoQualityForTileView,
setMaxReceiverVideoQualityForVerticalFilmstrip
} from './actions';
import { MAX_VIDEO_QUALITY, VIDEO_QUALITY_LEVELS, VIDEO_QUALITY_UNLIMITED } from './constants';
import { getReceiverVideoQualityLevel } from './functions';
import logger from './logger';
import { getMinHeightForQualityLvlMap } from './selector';
declare var APP: Object;
/**
* Handles changes in the visible participants in the filmstrip. The listener is debounced
* so that the client doesn't end up sending too many bridge messages when the user is
@ -63,17 +73,6 @@ StateListenerRegistry.register(
}
);
/**
* Updates the receiver constraints when the layout changes. When we are in stage view we need to handle the
* on-stage participant differently.
*/
StateListenerRegistry.register(
/* selector */ state => state['features/video-layout'].tileViewEnabled,
/* listener */ (tileViewEnabled, store) => {
_updateReceiverVideoConstraints(store);
}
);
/**
* StateListenerRegistry provides a reliable way of detecting changes to
* lastn state and dispatching additional actions.
@ -84,26 +83,6 @@ StateListenerRegistry.register(
_updateReceiverVideoConstraints(store);
});
/**
* Updates the receiver constraints when the tiles in the resizable filmstrip change dimensions.
*/
StateListenerRegistry.register(
state => getVideoQualityForResizableFilmstripThumbnails(state),
(_, store) => {
_updateReceiverVideoConstraints(store);
}
);
/**
* Updates the receiver constraints when the tiles in the resizable top panel change dimensions.
*/
StateListenerRegistry.register(
state => getVideoQualityForStageThumbnails(state),
(_, store) => {
_updateReceiverVideoConstraints(store);
}
);
/**
* Updates the receiver constraints when the stage participants change.
*/
@ -118,30 +97,37 @@ StateListenerRegistry.register(
/**
* StateListenerRegistry provides a reliable way of detecting changes to
* maxReceiverVideoQuality and preferredVideoQuality state and dispatching additional actions.
* maxReceiverVideoQuality* and preferredVideoQuality state and dispatching additional actions.
*/
StateListenerRegistry.register(
/* selector */ state => {
const {
maxReceiverVideoQuality,
maxReceiverVideoQualityForLargeVideo,
maxReceiverVideoQualityForScreenSharingFilmstrip,
maxReceiverVideoQualityForStageFilmstrip,
maxReceiverVideoQualityForTileView,
maxReceiverVideoQualityForVerticalFilmstrip,
preferredVideoQuality
} = state['features/video-quality'];
return {
maxReceiverVideoQuality,
maxReceiverVideoQualityForLargeVideo,
maxReceiverVideoQualityForScreenSharingFilmstrip,
maxReceiverVideoQualityForStageFilmstrip,
maxReceiverVideoQualityForTileView,
maxReceiverVideoQualityForVerticalFilmstrip,
preferredVideoQuality
};
},
/* listener */ (currentState, store, previousState = {}) => {
const { maxReceiverVideoQuality, preferredVideoQuality } = currentState;
const { preferredVideoQuality } = currentState;
const changedPreferredVideoQuality = preferredVideoQuality !== previousState.preferredVideoQuality;
const changedReceiverVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality;
if (changedPreferredVideoQuality) {
_setSenderVideoConstraint(preferredVideoQuality, store);
typeof APP !== 'undefined' && APP.API.notifyVideoQualityChanged(preferredVideoQuality);
}
changedReceiverVideoQuality && _updateReceiverVideoConstraints(store);
_updateReceiverVideoConstraints(store);
}, {
deepEquals: true
});
@ -153,48 +139,158 @@ StateListenerRegistry.register(
/* selector */ state => {
const { reducedUI } = state['features/base/responsive-ui'];
const _shouldDisplayTileView = shouldDisplayTileView(state);
const thumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize;
const participantCount = getParticipantCount(state);
const tileViewThumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize;
const { visibleRemoteParticipants } = state['features/filmstrip'];
const { height: largeVideoHeight } = state['features/large-video'];
const activeParticipantsIds = getActiveParticipantsIds(state);
const {
screenshareFilmstripDimensions: {
thumbnailSize
}
} = state['features/filmstrip'];
const screenshareFilmstripParticipantId = getScreenshareFilmstripParticipantId(state);
return {
activeParticipantsCount: activeParticipantsIds?.length,
displayTileView: _shouldDisplayTileView,
participantCount,
largeVideoHeight,
participantCount: visibleRemoteParticipants?.size || 0,
reducedUI,
thumbnailHeight: thumbnailSize?.height
screenSharingFilmstripHeight:
screenshareFilmstripParticipantId && getCurrentLayout(state) === LAYOUTS.STAGE_FILMSTRIP_VIEW
? thumbnailSize?.height : undefined,
stageFilmstripThumbnailHeight: state['features/filmstrip'].stageFilmstripDimensions?.thumbnailSize?.height,
tileViewThumbnailHeight: tileViewThumbnailSize?.height,
verticalFilmstripThumbnailHeight:
state['features/filmstrip'].verticalViewDimensions?.gridView?.thumbnailSize?.height
};
},
/* listener */ ({ displayTileView, participantCount, reducedUI, thumbnailHeight }, { dispatch, getState }) => {
/* listener */ ({
activeParticipantsCount,
displayTileView,
largeVideoHeight,
participantCount,
reducedUI,
screenSharingFilmstripHeight,
stageFilmstripThumbnailHeight,
tileViewThumbnailHeight,
verticalFilmstripThumbnailHeight
}, store, previousState = {}) => {
const { dispatch, getState } = store;
const state = getState();
const { maxReceiverVideoQuality } = state['features/video-quality'];
const {
maxReceiverVideoQualityForLargeVideo,
maxReceiverVideoQualityForScreenSharingFilmstrip,
maxReceiverVideoQualityForStageFilmstrip,
maxReceiverVideoQualityForTileView,
maxReceiverVideoQualityForVerticalFilmstrip
} = state['features/video-quality'];
const { maxFullResolutionParticipants = 2 } = state['features/base/config'];
let maxVideoQualityChanged = false;
let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.ULTRA;
if (reducedUI) {
newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW;
} else if (displayTileView && !Number.isNaN(thumbnailHeight)) {
newMaxRecvVideoQuality = getReceiverVideoQualityLevel(thumbnailHeight, getMinHeightForQualityLvlMap(state));
if (displayTileView) {
let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD;
// Override HD level calculated for the thumbnail height when # of participants threshold is exceeded
if (maxReceiverVideoQuality !== newMaxRecvVideoQuality && maxFullResolutionParticipants !== -1) {
const override
= participantCount > maxFullResolutionParticipants
&& newMaxRecvVideoQuality > VIDEO_QUALITY_LEVELS.STANDARD;
if (reducedUI) {
newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW;
} else if (typeof tileViewThumbnailHeight === 'number' && !Number.isNaN(tileViewThumbnailHeight)) {
newMaxRecvVideoQuality
= getReceiverVideoQualityLevel(tileViewThumbnailHeight, getMinHeightForQualityLvlMap(state));
logger.info(`Video quality level for thumbnail height: ${thumbnailHeight}, `
+ `is: ${newMaxRecvVideoQuality}, `
+ `override: ${String(override)}, `
+ `max full res N: ${maxFullResolutionParticipants}`);
// Override HD level calculated for the thumbnail height when # of participants threshold is exceeded
if (maxFullResolutionParticipants !== -1) {
const override
= participantCount > maxFullResolutionParticipants
&& newMaxRecvVideoQuality > VIDEO_QUALITY_LEVELS.STANDARD;
if (override) {
newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD;
logger.info(`Video quality level for thumbnail height: ${tileViewThumbnailHeight}, `
+ `is: ${newMaxRecvVideoQuality}, `
+ `override: ${String(override)}, `
+ `max full res N: ${maxFullResolutionParticipants}`);
if (override) {
newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD;
}
}
}
if (maxReceiverVideoQualityForTileView !== newMaxRecvVideoQuality) {
maxVideoQualityChanged = true;
dispatch(setMaxReceiverVideoQualityForTileView(newMaxRecvVideoQuality));
}
} else {
let newMaxRecvVideoQualityForStageFilmstrip;
let newMaxRecvVideoQualityForVerticalFilmstrip;
let newMaxRecvVideoQualityForLargeVideo;
let newMaxRecvVideoQualityForScreenSharingFilmstrip;
if (reducedUI) {
newMaxRecvVideoQualityForVerticalFilmstrip
= newMaxRecvVideoQualityForStageFilmstrip
= newMaxRecvVideoQualityForLargeVideo
= newMaxRecvVideoQualityForScreenSharingFilmstrip
= VIDEO_QUALITY_LEVELS.LOW;
} else {
newMaxRecvVideoQualityForStageFilmstrip
= getVideoQualityForStageThumbnails(stageFilmstripThumbnailHeight, state);
newMaxRecvVideoQualityForVerticalFilmstrip
= getVideoQualityForResizableFilmstripThumbnails(verticalFilmstripThumbnailHeight, state);
newMaxRecvVideoQualityForLargeVideo = getVideoQualityForLargeVideo(largeVideoHeight);
newMaxRecvVideoQualityForScreenSharingFilmstrip
= getVideoQualityForScreenSharingFilmstrip(screenSharingFilmstripHeight, state);
// Override HD level calculated for the thumbnail height when # of participants threshold is exceeded
if (maxFullResolutionParticipants !== -1) {
if (activeParticipantsCount > 0
&& newMaxRecvVideoQualityForStageFilmstrip > VIDEO_QUALITY_LEVELS.STANDARD) {
const isScreenSharingFilmstripParticipantFullResolution
= newMaxRecvVideoQualityForScreenSharingFilmstrip > VIDEO_QUALITY_LEVELS.STANDARD;
if (activeParticipantsCount > maxFullResolutionParticipants
- (isScreenSharingFilmstripParticipantFullResolution ? 1 : 0)) {
newMaxRecvVideoQualityForStageFilmstrip = VIDEO_QUALITY_LEVELS.STANDARD;
newMaxRecvVideoQualityForVerticalFilmstrip
= Math.min(VIDEO_QUALITY_LEVELS.STANDARD, newMaxRecvVideoQualityForVerticalFilmstrip);
} else if (newMaxRecvVideoQualityForVerticalFilmstrip > VIDEO_QUALITY_LEVELS.STANDARD
&& participantCount > maxFullResolutionParticipants - activeParticipantsCount) {
newMaxRecvVideoQualityForVerticalFilmstrip = VIDEO_QUALITY_LEVELS.STANDARD;
}
} else if (newMaxRecvVideoQualityForVerticalFilmstrip > VIDEO_QUALITY_LEVELS.STANDARD
&& participantCount > maxFullResolutionParticipants
- (newMaxRecvVideoQualityForLargeVideo > VIDEO_QUALITY_LEVELS.STANDARD ? 1 : 0)) {
newMaxRecvVideoQualityForVerticalFilmstrip = VIDEO_QUALITY_LEVELS.STANDARD;
}
}
}
if (maxReceiverVideoQualityForStageFilmstrip !== newMaxRecvVideoQualityForStageFilmstrip) {
maxVideoQualityChanged = true;
dispatch(setMaxReceiverVideoQualityForStageFilmstrip(newMaxRecvVideoQualityForStageFilmstrip));
}
if (maxReceiverVideoQualityForVerticalFilmstrip !== newMaxRecvVideoQualityForVerticalFilmstrip) {
maxVideoQualityChanged = true;
dispatch(setMaxReceiverVideoQualityForVerticalFilmstrip(newMaxRecvVideoQualityForVerticalFilmstrip));
}
if (maxReceiverVideoQualityForLargeVideo !== newMaxRecvVideoQualityForLargeVideo) {
maxVideoQualityChanged = true;
dispatch(setMaxReceiverVideoQualityForLargeVideo(newMaxRecvVideoQualityForLargeVideo));
}
if (maxReceiverVideoQualityForScreenSharingFilmstrip !== newMaxRecvVideoQualityForScreenSharingFilmstrip) {
maxVideoQualityChanged = true;
dispatch(
setMaxReceiverVideoQualityForScreenSharingFilmstrip(
newMaxRecvVideoQualityForScreenSharingFilmstrip));
}
}
if (maxReceiverVideoQuality !== newMaxRecvVideoQuality) {
dispatch(setMaxReceiverVideoQuality(newMaxRecvVideoQuality));
if (!maxVideoQualityChanged && Boolean(displayTileView) !== Boolean(previousState.displayTileView)) {
_updateReceiverVideoConstraints(store);
}
}, {
deepEquals: true
});
@ -235,31 +331,47 @@ function _updateReceiverVideoConstraints({ getState }) {
return;
}
const { lastN } = state['features/base/lastn'];
const { maxReceiverVideoQuality, preferredVideoQuality } = state['features/video-quality'];
const {
maxReceiverVideoQualityForTileView,
maxReceiverVideoQualityForStageFilmstrip,
maxReceiverVideoQualityForVerticalFilmstrip,
maxReceiverVideoQualityForLargeVideo,
maxReceiverVideoQualityForScreenSharingFilmstrip,
preferredVideoQuality
} = state['features/video-quality'];
const { participantId: largeVideoParticipantId } = state['features/large-video'];
const maxFrameHeight = Math.min(maxReceiverVideoQuality, preferredVideoQuality);
const maxFrameHeightForTileView = Math.min(maxReceiverVideoQualityForTileView, preferredVideoQuality);
const maxFrameHeightForStageFilmstrip = Math.min(maxReceiverVideoQualityForStageFilmstrip, preferredVideoQuality);
const maxFrameHeightForVerticalFilmstrip
= Math.min(maxReceiverVideoQualityForVerticalFilmstrip, preferredVideoQuality);
const maxFrameHeightForLargeVideo
= Math.min(maxReceiverVideoQualityForLargeVideo, preferredVideoQuality);
const maxFrameHeightForScreenSharingFilmstrip
= Math.min(maxReceiverVideoQualityForScreenSharingFilmstrip, preferredVideoQuality);
const { remoteScreenShares } = state['features/video-layout'];
const { visibleRemoteParticipants } = state['features/filmstrip'];
const tracks = state['features/base/tracks'];
const sourceNameSignaling = getSourceNameSignalingFeatureFlag(state);
const localParticipantId = getLocalParticipant(state).id;
const activeParticipantsIds = getActiveParticipantsIds(state);
const screenshareFilmstripParticipantId = isTopPanelEnabled(state) && getScreenshareFilmstripParticipantId(state);
let receiverConstraints;
const receiverConstraints = {
constraints: {},
defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.NONE },
lastN
};
let remoteScreenSharesSourceNames;
let visibleRemoteTrackSourceNames = [];
let largeVideoSourceName;
let activeParticipantsSources = [];
if (sourceNameSignaling) {
const remoteScreenSharesSourceNames = getRemoteScreenSharesSourceNames(state, remoteScreenShares);
receiverConstraints.onStageSources = [];
receiverConstraints.selectedSources = [];
receiverConstraints = {
constraints: {},
defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.NONE },
lastN,
onStageSources: [],
selectedSources: []
};
const visibleRemoteTrackSourceNames = [];
let largeVideoSourceName;
const activeParticipantsSources = [];
remoteScreenSharesSourceNames = getRemoteScreenSharesSourceNames(state, remoteScreenShares);
if (visibleRemoteParticipants?.size) {
visibleRemoteParticipants.forEach(participantId => {
@ -273,13 +385,27 @@ function _updateReceiverVideoConstraints({ getState }) {
if (sourceName) {
visibleRemoteTrackSourceNames.push(sourceName);
if (activeParticipantsIds.find(id => id === participantId)) {
activeParticipantsSources.push(sourceName);
}
}
});
}
if (activeParticipantsIds?.length > 0) {
activeParticipantsIds.forEach(participantId => {
let sourceName;
if (remoteScreenSharesSourceNames.includes(participantId)) {
sourceName = participantId;
} else {
sourceName = getTrackSourceNameByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantId);
}
if (sourceName) {
activeParticipantsSources.push(sourceName);
}
});
}
if (localParticipantId !== largeVideoParticipantId) {
if (remoteScreenSharesSourceNames.includes(largeVideoParticipantId)) {
largeVideoSourceName = largeVideoParticipantId;
@ -289,110 +415,77 @@ function _updateReceiverVideoConstraints({ getState }) {
);
}
}
// Tile view.
if (shouldDisplayTileView(state)) {
if (!visibleRemoteTrackSourceNames?.length) {
return;
}
visibleRemoteTrackSourceNames.forEach(sourceName => {
receiverConstraints.constraints[sourceName] = { 'maxHeight': maxFrameHeight };
});
// Prioritize screenshare in tile view.
if (remoteScreenSharesSourceNames?.length) {
receiverConstraints.selectedSources = remoteScreenSharesSourceNames;
}
// Stage view.
} else {
if (!visibleRemoteTrackSourceNames?.length && !largeVideoSourceName) {
return;
}
if (visibleRemoteTrackSourceNames?.length) {
const qualityLevel = getVideoQualityForResizableFilmstripThumbnails(state);
const stageParticipantsLevel = getVideoQualityForStageThumbnails(state);
visibleRemoteTrackSourceNames.forEach(sourceName => {
const isStageParticipant = activeParticipantsSources.find(name => name === sourceName);
const quality = Math.min(maxFrameHeight, isStageParticipant
? stageParticipantsLevel : qualityLevel);
receiverConstraints.constraints[sourceName] = { 'maxHeight': quality };
});
}
if (largeVideoSourceName) {
let quality = maxFrameHeight;
if (navigator.product !== 'ReactNative'
&& !remoteScreenShares.find(id => id === largeVideoParticipantId)) {
quality = getVideoQualityForLargeVideo();
}
receiverConstraints.constraints[largeVideoSourceName] = { 'maxHeight': quality };
receiverConstraints.onStageSources = [ largeVideoSourceName ];
}
}
if (remoteScreenSharesSourceNames?.length) {
remoteScreenSharesSourceNames.forEach(sourceName => {
receiverConstraints.constraints[sourceName] = { 'maxHeight': VIDEO_QUALITY_LEVELS.ULTRA };
});
}
} else {
receiverConstraints = {
constraints: {},
defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.NONE },
lastN,
onStageEndpoints: [],
selectedEndpoints: []
};
receiverConstraints.onStageEndpoints = [];
receiverConstraints.selectedEndpoints = [];
// Tile view.
if (shouldDisplayTileView(state)) {
if (!visibleRemoteParticipants?.size) {
return;
}
remoteScreenSharesSourceNames = remoteScreenShares;
visibleRemoteTrackSourceNames = [ ...visibleRemoteParticipants ];
largeVideoSourceName = largeVideoParticipantId;
activeParticipantsSources = activeParticipantsIds;
}
visibleRemoteParticipants.forEach(participantId => {
receiverConstraints.constraints[participantId] = { 'maxHeight': maxFrameHeight };
// Tile view.
if (shouldDisplayTileView(state)) {
if (!visibleRemoteTrackSourceNames?.length) {
return;
}
visibleRemoteTrackSourceNames.forEach(sourceName => {
receiverConstraints.constraints[sourceName] = { 'maxHeight': maxFrameHeightForTileView };
});
// Prioritize screenshare in tile view.
if (remoteScreenSharesSourceNames?.length) {
receiverConstraints[sourceNameSignaling ? 'selectedSources' : 'selectedEndpoints']
= remoteScreenSharesSourceNames;
}
// Stage view.
} else {
if (!visibleRemoteTrackSourceNames?.length && !largeVideoSourceName && !activeParticipantsSources?.length) {
return;
}
if (visibleRemoteTrackSourceNames?.length) {
visibleRemoteTrackSourceNames.forEach(sourceName => {
receiverConstraints.constraints[sourceName] = { 'maxHeight': maxFrameHeightForVerticalFilmstrip };
});
}
if (getCurrentLayout(state) === LAYOUTS.STAGE_FILMSTRIP_VIEW && activeParticipantsSources.length > 0) {
const onStageSources = [ ...activeParticipantsSources ];
activeParticipantsSources.forEach(sourceName => {
const isScreenSharing = remoteScreenShares.includes(sourceName);
const quality
= isScreenSharing && preferredVideoQuality >= MAX_VIDEO_QUALITY
? VIDEO_QUALITY_UNLIMITED : maxFrameHeightForStageFilmstrip;
receiverConstraints.constraints[sourceName] = { 'maxHeight': quality };
});
// Prioritize screenshare in tile view.
remoteScreenShares?.length && (receiverConstraints.selectedEndpoints = remoteScreenShares);
// Stage view.
} else {
if (!visibleRemoteParticipants?.size && !largeVideoParticipantId) {
return;
if (screenshareFilmstripParticipantId) {
onStageSources.push(screenshareFilmstripParticipantId);
receiverConstraints.constraints[screenshareFilmstripParticipantId]
= {
'maxHeight':
preferredVideoQuality >= MAX_VIDEO_QUALITY
? VIDEO_QUALITY_UNLIMITED : maxFrameHeightForScreenSharingFilmstrip
};
}
if (visibleRemoteParticipants?.size > 0) {
const qualityLevel = getVideoQualityForResizableFilmstripThumbnails(state);
const stageParticipantsLevel = getVideoQualityForStageThumbnails(state);
receiverConstraints[sourceNameSignaling ? 'onStageSources' : 'onStageEndpoints'] = onStageSources;
} else if (largeVideoSourceName) {
let quality = VIDEO_QUALITY_UNLIMITED;
visibleRemoteParticipants.forEach(participantId => {
const isStageParticipant = activeParticipantsIds.find(id => id === participantId);
const quality = Math.min(maxFrameHeight, isStageParticipant
? stageParticipantsLevel : qualityLevel);
receiverConstraints.constraints[participantId] = { 'maxHeight': quality };
});
}
if (largeVideoParticipantId) {
let quality = maxFrameHeight;
if (navigator.product !== 'ReactNative'
&& !remoteScreenShares.find(id => id === largeVideoParticipantId)) {
quality = getVideoQualityForLargeVideo();
}
receiverConstraints.constraints[largeVideoParticipantId] = { 'maxHeight': quality };
receiverConstraints.onStageEndpoints = [ largeVideoParticipantId ];
if (preferredVideoQuality < MAX_VIDEO_QUALITY
|| !remoteScreenShares.find(id => id === largeVideoParticipantId)) {
quality = maxFrameHeightForLargeVideo;
}
receiverConstraints.constraints[largeVideoSourceName] = { 'maxHeight': quality };
receiverConstraints[sourceNameSignaling ? 'onStageSources' : 'onStageEndpoints']
= [ largeVideoSourceName ];
}
}