diff --git a/config.js b/config.js index 5c0783a6a..2f2ee2c55 100644 --- a/config.js +++ b/config.js @@ -123,6 +123,10 @@ var config = { // Sets the preferred resolution (height) for local video. Defaults to 720. // resolution: 720, + // How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD. + // Use -1 to disable. + // maxFullResolutionParticipants: 2 + // w3c spec-compliant video constraints to use for video capture. Currently // used by browsers that return true from lib-jitsi-meet's // util#browser#usesNewGumFlow. The constraints are independent from diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 037711f9e..1c038286f 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -122,6 +122,7 @@ export default [ 'ignoreStartMuted', 'liveStreamingEnabled', 'localRecording', + 'maxFullResolutionParticipants', 'minParticipants', 'nick', 'openBridgeChannel', diff --git a/react/features/video-quality/middleware.js b/react/features/video-quality/middleware.js index 6ba7806f9..81f64594f 100644 --- a/react/features/video-quality/middleware.js +++ b/react/features/video-quality/middleware.js @@ -7,6 +7,7 @@ import { setMaxReceiverVideoQuality, setPreferredVideoQuality } from '../base/conference'; +import { getParticipantCount } from '../base/participants'; import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; import { shouldDisplayTileView } from '../video-layout'; @@ -46,21 +47,42 @@ StateListenerRegistry.register( const { reducedUI } = state['features/base/responsive-ui']; const _shouldDisplayTileView = shouldDisplayTileView(state); const thumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize; + const participantCount = getParticipantCount(state); return { displayTileView: _shouldDisplayTileView, + participantCount, reducedUI, thumbnailHeight: thumbnailSize?.height }; }, - /* listener */ ({ displayTileView, reducedUI, thumbnailHeight }, { dispatch, getState }) => { - const { maxReceiverVideoQuality } = getState()['features/base/conference']; + /* listener */ ({ displayTileView, participantCount, reducedUI, thumbnailHeight }, { dispatch, getState }) => { + const state = getState(); + const { maxReceiverVideoQuality } = state['features/base/conference']; + const { maxFullResolutionParticipants = 2 } = state['features/base/config']; + let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.HIGH; if (reducedUI) { newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW; } else if (displayTileView && !Number.isNaN(thumbnailHeight)) { newMaxRecvVideoQuality = getNearestReceiverVideoQualityLevel(thumbnailHeight); + + // 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; + + logger.info(`The nearest receiver video quality level for thumbnail height: ${thumbnailHeight}, ` + + `is: ${newMaxRecvVideoQuality}, ` + + `override: ${String(override)}, ` + + `max full res N: ${maxFullResolutionParticipants}`); + + if (override) { + newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD; + } + } } if (maxReceiverVideoQuality !== newMaxRecvVideoQuality) {