jiti-meet/react/features/video-quality/middleware.js

223 lines
7.8 KiB
JavaScript

// @flow
import {
CONFERENCE_JOINED,
DATA_CHANNEL_OPENED
} from '../base/conference';
import { SET_CONFIG } from '../base/config';
import { getParticipantCount } from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { shouldDisplayTileView } from '../video-layout';
import { setPreferredVideoQuality, setMaxReceiverVideoQuality } from './actions';
import { VIDEO_QUALITY_LEVELS } from './constants';
import { getReceiverVideoQualityLevel } from './functions';
import logger from './logger';
import { getMinHeightForQualityLvlMap } from './selector';
declare var APP: Object;
/**
* Implements the middleware of the feature video-quality.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
if (action.type === DATA_CHANNEL_OPENED) {
return _syncReceiveVideoQuality(getState, next, action);
}
const result = next(action);
switch (action.type) {
case CONFERENCE_JOINED: {
if (navigator.product === 'ReactNative') {
const { resolution } = getState()['features/base/config'];
if (typeof resolution !== 'undefined') {
dispatch(setPreferredVideoQuality(Number.parseInt(resolution, 10)));
logger.info(`Configured preferred receiver video frame height to: ${resolution}`);
}
}
break;
}
case SET_CONFIG: {
const state = getState();
const { videoQuality = {} } = state['features/base/config'];
if (videoQuality.persist) {
dispatch(
setPreferredVideoQuality(
state['features/video-quality-persistent-storage'].persistedPrefferedVideoQuality));
}
break;
}
}
return result;
});
/**
* Implements a state listener in order to calculate max receiver video quality.
*/
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);
return {
displayTileView: _shouldDisplayTileView,
participantCount,
reducedUI,
thumbnailHeight: thumbnailSize?.height
};
},
/* listener */ ({ displayTileView, participantCount, reducedUI, thumbnailHeight }, { dispatch, getState }) => {
const state = getState();
const { maxReceiverVideoQuality } = state['features/video-quality'];
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 = getReceiverVideoQualityLevel(thumbnailHeight, getMinHeightForQualityLvlMap(state));
// 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(`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) {
dispatch(setMaxReceiverVideoQuality(newMaxRecvVideoQuality));
}
}, {
deepEquals: true
});
/**
* Helper function for updating the preferred receiver video constraint, based
* on the user preference and the internal maximum.
*
* @param {JitsiConference} conference - The JitsiConference instance for the
* current call.
* @param {number} preferred - The user preferred max frame height.
* @param {number} max - The maximum frame height the application should
* receive.
* @returns {void}
*/
function _setReceiverVideoConstraint(conference, preferred, max) {
if (conference) {
const value = Math.min(preferred, max);
conference.setReceiverVideoConstraint(value);
logger.info(`setReceiverVideoConstraint: ${value}`);
}
}
/**
* Helper function for updating the preferred sender video constraint, based
* on the user preference.
*
* @param {JitsiConference} conference - The JitsiConference instance for the
* current call.
* @param {number} preferred - The user preferred max frame height.
* @returns {void}
*/
function _setSenderVideoConstraint(conference, preferred) {
if (conference) {
conference.setSenderVideoConstraint(preferred)
.catch(err => {
logger.error(`Changing sender resolution to ${preferred} failed - ${err} `);
});
}
}
/**
* Sets the maximum receive video quality.
*
* @param {Function} getState - The redux function which returns the current redux state.
* @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 DATA_CHANNEL_STATUS_CHANGED}
* which is being dispatched in the specified {@code store}.
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _syncReceiveVideoQuality(getState, next, action) {
const state = getState();
const {
conference
} = state['features/base/conference'];
const {
maxReceiverVideoQuality,
preferredVideoQuality
} = state['features/video-quality'];
_setReceiverVideoConstraint(
conference,
preferredVideoQuality,
maxReceiverVideoQuality);
return next(action);
}
/**
* Registers a change handler for state['features/base/conference'] to update
* the preferred video quality levels based on user preferred and internal
* settings.
*/
StateListenerRegistry.register(
/* selector */ state => {
const { conference } = state['features/base/conference'];
const {
maxReceiverVideoQuality,
preferredVideoQuality
} = state['features/video-quality'];
return {
conference,
maxReceiverVideoQuality,
preferredVideoQuality
};
},
/* listener */ (currentState, store, previousState = {}) => {
const {
conference,
maxReceiverVideoQuality,
preferredVideoQuality
} = currentState;
const changedConference = conference !== previousState.conference;
const changedPreferredVideoQuality = preferredVideoQuality !== previousState.preferredVideoQuality;
const changedMaxVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality;
if (changedConference || changedPreferredVideoQuality || changedMaxVideoQuality) {
_setReceiverVideoConstraint(conference, preferredVideoQuality, maxReceiverVideoQuality);
}
if (changedConference || changedPreferredVideoQuality) {
_setSenderVideoConstraint(conference, preferredVideoQuality);
}
if (typeof APP !== 'undefined' && changedPreferredVideoQuality) {
APP.API.notifyVideoQualityChanged(preferredVideoQuality);
}
});