feat: request video only for visible thumbnails.
This commit is contained in:
parent
16cfda3c7a
commit
2d319d18c3
|
@ -70,3 +70,15 @@ export const SET_VERTICAL_VIEW_DIMENSIONS = 'SET_VERTICAL_VIEW_DIMENSIONS';
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const SET_VOLUME = 'SET_VOLUME';
|
export const SET_VOLUME = 'SET_VOLUME';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the action which sets the list of visible remote participants in the filmstrip by storing the start and
|
||||||
|
* end index in the remote participants array.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: SET_VISIBLE_REMOTE_PARTICIPANTS,
|
||||||
|
* startIndex: number,
|
||||||
|
* endIndex: number
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const SET_VISIBLE_REMOTE_PARTICIPANTS = 'SET_VISIBLE_REMOTE_PARTICIPANTS';
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
||||||
SET_TILE_VIEW_DIMENSIONS,
|
SET_TILE_VIEW_DIMENSIONS,
|
||||||
SET_VERTICAL_VIEW_DIMENSIONS,
|
SET_VERTICAL_VIEW_DIMENSIONS,
|
||||||
|
SET_VISIBLE_REMOTE_PARTICIPANTS,
|
||||||
SET_VOLUME
|
SET_VOLUME
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import {
|
import {
|
||||||
|
@ -153,4 +154,24 @@ export function setVolume(participantId: string, volume: number) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of the visible participants in the filmstrip by storing the start and end index from the remote
|
||||||
|
* participants array.
|
||||||
|
*
|
||||||
|
* @param {number} startIndex - The start index from the remote participants array.
|
||||||
|
* @param {number} endIndex - The end index from the remote participants array.
|
||||||
|
* @returns {{
|
||||||
|
* type: SET_VISIBLE_REMOTE_PARTICIPANTS,
|
||||||
|
* startIndex: number,
|
||||||
|
* endIndex: number
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function setVisibleRemoteParticipants(startIndex: number, endIndex: number) {
|
||||||
|
return {
|
||||||
|
type: SET_VISIBLE_REMOTE_PARTICIPANTS,
|
||||||
|
startIndex,
|
||||||
|
endIndex
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export * from './actions.native';
|
export * from './actions.native';
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { connect } from '../../../base/redux';
|
||||||
import { showToolbox } from '../../../toolbox/actions.web';
|
import { showToolbox } from '../../../toolbox/actions.web';
|
||||||
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
|
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
|
||||||
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
||||||
import { setFilmstripVisible } from '../../actions';
|
import { setFilmstripVisible, setVisibleRemoteParticipants } from '../../actions';
|
||||||
import { TILE_HORIZONTAL_MARGIN, TILE_VERTICAL_MARGIN, TOOLBAR_HEIGHT } from '../../constants';
|
import { TILE_HORIZONTAL_MARGIN, TILE_VERTICAL_MARGIN, TOOLBAR_HEIGHT } from '../../constants';
|
||||||
import { shouldRemoteVideosBeVisible } from '../../functions';
|
import { shouldRemoteVideosBeVisible } from '../../functions';
|
||||||
|
|
||||||
|
@ -67,7 +67,6 @@ type Props = {
|
||||||
*/
|
*/
|
||||||
_remoteParticipants: Array<Object>,
|
_remoteParticipants: Array<Object>,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the remote participants array.
|
* The length of the remote participants array.
|
||||||
*/
|
*/
|
||||||
|
@ -137,6 +136,8 @@ class Filmstrip extends PureComponent <Props> {
|
||||||
this._onTabIn = this._onTabIn.bind(this);
|
this._onTabIn = this._onTabIn.bind(this);
|
||||||
this._gridItemKey = this._gridItemKey.bind(this);
|
this._gridItemKey = this._gridItemKey.bind(this);
|
||||||
this._listItemKey = this._listItemKey.bind(this);
|
this._listItemKey = this._listItemKey.bind(this);
|
||||||
|
this._onGridItemsRendered = this._onGridItemsRendered.bind(this);
|
||||||
|
this._onListItemsRendered = this._onListItemsRendered.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,6 +269,41 @@ class Filmstrip extends PureComponent <Props> {
|
||||||
return _remoteParticipants[index];
|
return _remoteParticipants[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onListItemsRendered: Object => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles items rendered changes in stage view.
|
||||||
|
*
|
||||||
|
* @param {Object} data - Information about the rendered items.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onListItemsRendered({ overscanStartIndex, overscanStopIndex }) {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
|
dispatch(setVisibleRemoteParticipants(overscanStartIndex, overscanStopIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onGridItemsRendered: Object => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles items rendered changes in tile view.
|
||||||
|
*
|
||||||
|
* @param {Object} data - Information about the rendered items.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onGridItemsRendered({
|
||||||
|
overscanColumnStartIndex,
|
||||||
|
overscanColumnStopIndex,
|
||||||
|
overscanRowStartIndex,
|
||||||
|
overscanRowStopIndex
|
||||||
|
}) {
|
||||||
|
const { _columns, dispatch } = this.props;
|
||||||
|
const startIndex = (overscanRowStartIndex * _columns) + overscanColumnStartIndex;
|
||||||
|
const endIndex = (overscanRowStopIndex * _columns) + overscanColumnStopIndex;
|
||||||
|
|
||||||
|
dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the thumbnails for remote participants.
|
* Renders the thumbnails for remote participants.
|
||||||
*
|
*
|
||||||
|
@ -301,6 +337,7 @@ class Filmstrip extends PureComponent <Props> {
|
||||||
initialScrollLeft = { 0 }
|
initialScrollLeft = { 0 }
|
||||||
initialScrollTop = { 0 }
|
initialScrollTop = { 0 }
|
||||||
itemKey = { this._gridItemKey }
|
itemKey = { this._gridItemKey }
|
||||||
|
onItemsRendered = { this._onGridItemsRendered }
|
||||||
rowCount = { _rows }
|
rowCount = { _rows }
|
||||||
rowHeight = { _thumbnailHeight + TILE_VERTICAL_MARGIN }
|
rowHeight = { _thumbnailHeight + TILE_VERTICAL_MARGIN }
|
||||||
width = { _filmstripWidth }>
|
width = { _filmstripWidth }>
|
||||||
|
@ -318,6 +355,7 @@ class Filmstrip extends PureComponent <Props> {
|
||||||
height: _filmstripHeight,
|
height: _filmstripHeight,
|
||||||
itemKey: this._listItemKey,
|
itemKey: this._listItemKey,
|
||||||
itemSize: 0,
|
itemSize: 0,
|
||||||
|
onItemsRendered: this._onListItemsRendered,
|
||||||
width: _filmstripWidth,
|
width: _filmstripWidth,
|
||||||
style: {
|
style: {
|
||||||
willChange: 'auto'
|
willChange: 'auto'
|
||||||
|
|
|
@ -12,16 +12,16 @@ import Thumbnail from './Thumbnail';
|
||||||
*/
|
*/
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The horizontal offset in px for the thumbnail. Used to center the thumbnails in the last row in tile view.
|
||||||
|
*/
|
||||||
|
_horizontalOffset: number,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID of the participant associated with the Thumbnail.
|
* The ID of the participant associated with the Thumbnail.
|
||||||
*/
|
*/
|
||||||
_participantID: ?string,
|
_participantID: ?string,
|
||||||
|
|
||||||
/**
|
|
||||||
* The horizontal offset in px for the thumbnail. Used to center the thumbnails in the last row in tile view.
|
|
||||||
*/
|
|
||||||
_horizontalOffset: number,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the column in tile view.
|
* The index of the column in tile view.
|
||||||
*/
|
*/
|
||||||
|
@ -114,12 +114,11 @@ function _mapStateToProps(state, ownProps) {
|
||||||
|
|
||||||
if (rowIndex === rows - 1) { // center the last row
|
if (rowIndex === rows - 1) { // center the last row
|
||||||
const { width: thumbnailWidth } = thumbnailSize;
|
const { width: thumbnailWidth } = thumbnailSize;
|
||||||
const participantsInTheLastRow = (remoteParticipantsLength + 1) % columns;
|
const partialLastRowParticipantsNumber = (remoteParticipantsLength + 1) % columns;
|
||||||
|
|
||||||
if (participantsInTheLastRow > 0) {
|
if (partialLastRowParticipantsNumber > 0) {
|
||||||
horizontalOffset = Math.floor((columns - participantsInTheLastRow) * (thumbnailWidth + 4) / 2);
|
horizontalOffset = Math.floor((columns - partialLastRowParticipantsNumber) * (thumbnailWidth + 4) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > remoteParticipantsLength) {
|
if (index > remoteParticipantsLength) {
|
||||||
|
@ -133,12 +132,10 @@ function _mapStateToProps(state, ownProps) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_participantID: remoteParticipants[index],
|
_participantID: remoteParticipants[index],
|
||||||
_horizontalOffset: horizontalOffset
|
_horizontalOffset: horizontalOffset
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { index } = ownProps;
|
const { index } = ownProps;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
||||||
SET_TILE_VIEW_DIMENSIONS,
|
SET_TILE_VIEW_DIMENSIONS,
|
||||||
SET_VERTICAL_VIEW_DIMENSIONS,
|
SET_VERTICAL_VIEW_DIMENSIONS,
|
||||||
|
SET_VISIBLE_REMOTE_PARTICIPANTS,
|
||||||
SET_VOLUME
|
SET_VOLUME
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
|
@ -66,7 +67,32 @@ const DEFAULT_STATE = {
|
||||||
* @public
|
* @public
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
visible: true
|
visible: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end index in the remote participants array that is visible in the filmstrip.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
visibleParticipantsEndIndex: 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The visible participants in the filmstrip.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @type {Array<string>}
|
||||||
|
*/
|
||||||
|
visibleParticipants: [],
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start index in the remote participants array that is visible in the filmstrip.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
visibleParticipantsStartIndex: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
ReducerRegistry.register(
|
ReducerRegistry.register(
|
||||||
|
@ -112,11 +138,24 @@ ReducerRegistry.register(
|
||||||
[action.participantId]: action.volume
|
[action.participantId]: action.volume
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
case SET_VISIBLE_REMOTE_PARTICIPANTS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
visibleParticipantsStartIndex: action.startIndex,
|
||||||
|
visibleParticipantsEndIndex: action.endIndex,
|
||||||
|
visibleParticipants: state.remoteParticipants.slice(action.startIndex, action.endIndex + 1)
|
||||||
|
};
|
||||||
case PARTICIPANT_JOINED: {
|
case PARTICIPANT_JOINED: {
|
||||||
const { id, local } = action.participant;
|
const { id, local } = action.participant;
|
||||||
|
|
||||||
if (!local) {
|
if (!local) {
|
||||||
state.remoteParticipants = [ ...state.remoteParticipants, id ];
|
state.remoteParticipants = [ ...state.remoteParticipants, id ];
|
||||||
|
|
||||||
|
const { visibleParticipantsStartIndex: startIndex, visibleParticipantsEndIndex: endIndex } = state;
|
||||||
|
|
||||||
|
if (state.remoteParticipants.length - 1 <= endIndex) {
|
||||||
|
state.visibleParticipants = state.remoteParticipants.slice(startIndex, endIndex + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
@ -128,7 +167,24 @@ ReducerRegistry.register(
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.remoteParticipants = state.remoteParticipants.filter(participantId => participantId !== id);
|
let removedParticipantIndex = 0;
|
||||||
|
|
||||||
|
state.remoteParticipants = state.remoteParticipants.filter((participantId, index) => {
|
||||||
|
if (participantId === id) {
|
||||||
|
removedParticipantIndex = index;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const { visibleParticipantsStartIndex: startIndex, visibleParticipantsEndIndex: endIndex } = state;
|
||||||
|
|
||||||
|
if (removedParticipantIndex >= startIndex && removedParticipantIndex <= endIndex) {
|
||||||
|
state.visibleParticipants = state.remoteParticipants.slice(startIndex, endIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
delete state.participantsVolume[id];
|
delete state.participantsVolume[id];
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -3,30 +3,12 @@
|
||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { MEDIA_TYPE } from '../base/media';
|
import { MEDIA_TYPE } from '../base/media';
|
||||||
import { getParticipants } from '../base/participants';
|
|
||||||
import { selectEndpoints, shouldDisplayTileView } from '../video-layout';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SELECT_LARGE_VIDEO_PARTICIPANT,
|
SELECT_LARGE_VIDEO_PARTICIPANT,
|
||||||
UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION
|
UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
/**
|
|
||||||
* Signals conference to select a participant.
|
|
||||||
*
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
export function selectParticipant() {
|
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
|
||||||
const state = getState();
|
|
||||||
const ids = shouldDisplayTileView(state)
|
|
||||||
? getParticipants(state).map(participant => participant.id)
|
|
||||||
: [ state['features/large-video'].participantId ];
|
|
||||||
|
|
||||||
dispatch(selectEndpoints(ids));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action to select the participant to be displayed in LargeVideo based on the
|
* Action to select the participant to be displayed in LargeVideo based on the
|
||||||
* participant id provided. If a participant id is not provided, the LargeVideo
|
* participant id provided. If a participant id is not provided, the LargeVideo
|
||||||
|
@ -60,8 +42,6 @@ export function selectParticipantInLargeVideo(participant: ?string) {
|
||||||
type: SELECT_LARGE_VIDEO_PARTICIPANT,
|
type: SELECT_LARGE_VIDEO_PARTICIPANT,
|
||||||
participantId
|
participantId
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(selectParticipant());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { CONFERENCE_JOINED } from '../base/conference';
|
|
||||||
import {
|
import {
|
||||||
DOMINANT_SPEAKER_CHANGED,
|
DOMINANT_SPEAKER_CHANGED,
|
||||||
PARTICIPANT_JOINED,
|
PARTICIPANT_JOINED,
|
||||||
|
@ -11,13 +10,11 @@ import {
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
import { isTestModeEnabled } from '../base/testing';
|
import { isTestModeEnabled } from '../base/testing';
|
||||||
import {
|
import {
|
||||||
getTrackByJitsiTrack,
|
|
||||||
TRACK_ADDED,
|
TRACK_ADDED,
|
||||||
TRACK_REMOVED,
|
TRACK_REMOVED
|
||||||
TRACK_UPDATED
|
|
||||||
} from '../base/tracks';
|
} from '../base/tracks';
|
||||||
|
|
||||||
import { selectParticipant, selectParticipantInLargeVideo } from './actions.any';
|
import { selectParticipantInLargeVideo } from './actions';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
|
||||||
import './subscriber';
|
import './subscriber';
|
||||||
|
@ -54,30 +51,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
case TRACK_REMOVED:
|
case TRACK_REMOVED:
|
||||||
store.dispatch(selectParticipantInLargeVideo());
|
store.dispatch(selectParticipantInLargeVideo());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONFERENCE_JOINED:
|
|
||||||
// Ensure a participant is selected on conference join. This addresses
|
|
||||||
// the case where video tracks were received before CONFERENCE_JOINED
|
|
||||||
// fired; without the conference selection may not happen.
|
|
||||||
store.dispatch(selectParticipant());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TRACK_UPDATED:
|
|
||||||
// In order to minimize re-calculations, we need to select participant
|
|
||||||
// only if the videoType of the current participant rendered in
|
|
||||||
// LargeVideo has changed.
|
|
||||||
if ('videoType' in action.track) {
|
|
||||||
const state = store.getState();
|
|
||||||
const track
|
|
||||||
= getTrackByJitsiTrack(
|
|
||||||
state['features/base/tracks'],
|
|
||||||
action.track.jitsiTrack);
|
|
||||||
const participantId = state['features/large-video'].participantId;
|
|
||||||
|
|
||||||
(track.participantId === participantId)
|
|
||||||
&& store.dispatch(selectParticipant());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -1363,6 +1363,7 @@ class Toolbox extends Component<Props> {
|
||||||
{ showOverflowMenuButton && <OverflowMenuButton
|
{ showOverflowMenuButton && <OverflowMenuButton
|
||||||
ariaControls = 'overflow-menu'
|
ariaControls = 'overflow-menu'
|
||||||
isOpen = { _overflowMenuVisible }
|
isOpen = { _overflowMenuVisible }
|
||||||
|
key = 'overflow-menu'
|
||||||
onVisibilityChange = { this._onSetOverflowVisible }>
|
onVisibilityChange = { this._onSetOverflowVisible }>
|
||||||
<ul
|
<ul
|
||||||
aria-label = { t(toolbarAccLabel) }
|
aria-label = { t(toolbarAccLabel) }
|
||||||
|
@ -1375,6 +1376,7 @@ class Toolbox extends Component<Props> {
|
||||||
</OverflowMenuButton>}
|
</OverflowMenuButton>}
|
||||||
<HangupButton
|
<HangupButton
|
||||||
customClass = 'hangup-button'
|
customClass = 'hangup-button'
|
||||||
|
key = 'hangup-button'
|
||||||
visible = { this.props._shouldShowButton('hangup') } />
|
visible = { this.props._shouldShowButton('hangup') } />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,12 +10,6 @@
|
||||||
export const SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED
|
export const SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED
|
||||||
= 'SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED';
|
= 'SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED';
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the action which sets the list of the endpoints to be selected for video forwarding
|
|
||||||
* from the bridge.
|
|
||||||
*/
|
|
||||||
export const SELECT_ENDPOINTS = 'SELECT_ENDPOINTS';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the action which enables or disables the feature for showing
|
* The type of the action which enables or disables the feature for showing
|
||||||
* video thumbnails in a two-axis tile view.
|
* video thumbnails in a two-axis tile view.
|
||||||
|
|
|
@ -4,28 +4,10 @@ import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||||
SELECT_ENDPOINTS,
|
|
||||||
SET_TILE_VIEW
|
SET_TILE_VIEW
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import { shouldDisplayTileView } from './functions';
|
import { shouldDisplayTileView } from './functions';
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a (redux) action which signals that a new set of remote endpoints need to be selected.
|
|
||||||
*
|
|
||||||
* @param {Array<string>} participantIds - The remote participants that are currently selected
|
|
||||||
* for video forwarding from the bridge.
|
|
||||||
* @returns {{
|
|
||||||
* type: SELECT_ENDPOINTS,
|
|
||||||
* particpantsIds: Array<string>
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
export function selectEndpoints(participantIds: Array<string>) {
|
|
||||||
return {
|
|
||||||
type: SELECT_ENDPOINTS,
|
|
||||||
participantIds
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a (redux) action which signals that the list of known remote participants
|
* Creates a (redux) action which signals that the list of known remote participants
|
||||||
* with screen shares has changed.
|
* with screen shares has changed.
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||||
SELECT_ENDPOINTS,
|
|
||||||
SET_TILE_VIEW
|
SET_TILE_VIEW
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
|
@ -35,13 +34,6 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case SELECT_ENDPOINTS: {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
selectedEndpoints: action.participantIds
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
case SET_TILE_VIEW:
|
case SET_TILE_VIEW:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -4,24 +4,10 @@ import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
import { StateListenerRegistry, equals } from '../base/redux';
|
import { StateListenerRegistry, equals } from '../base/redux';
|
||||||
import { isFollowMeActive } from '../follow-me';
|
import { isFollowMeActive } from '../follow-me';
|
||||||
import { selectParticipant } from '../large-video/actions.any';
|
|
||||||
|
|
||||||
import { setRemoteParticipantsWithScreenShare } from './actions';
|
import { setRemoteParticipantsWithScreenShare } from './actions';
|
||||||
import { getAutoPinSetting, updateAutoPinnedParticipant } from './functions';
|
import { getAutoPinSetting, updateAutoPinnedParticipant } from './functions';
|
||||||
|
|
||||||
/**
|
|
||||||
* StateListenerRegistry provides a reliable way of detecting changes to
|
|
||||||
* preferred layout state and dispatching additional actions.
|
|
||||||
*/
|
|
||||||
StateListenerRegistry.register(
|
|
||||||
/* selector */ state => state['features/video-layout'].tileViewEnabled,
|
|
||||||
/* listener */ (tileViewEnabled, store) => {
|
|
||||||
const { dispatch } = store;
|
|
||||||
|
|
||||||
dispatch(selectParticipant());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For auto-pin mode, listen for changes to the known media tracks and look
|
* For auto-pin mode, listen for changes to the known media tracks and look
|
||||||
* for updates to screen shares. The listener is debounced to avoid state
|
* for updates to screen shares. The listener is debounced to avoid state
|
||||||
|
|
|
@ -14,7 +14,8 @@ export const VIDEO_QUALITY_LEVELS = {
|
||||||
ULTRA: 2160,
|
ULTRA: 2160,
|
||||||
HIGH: 720,
|
HIGH: 720,
|
||||||
STANDARD: 360,
|
STANDARD: 360,
|
||||||
LOW: 180
|
LOW: 180,
|
||||||
|
NONE: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,16 +17,47 @@ import { getMinHeightForQualityLvlMap } from './selector';
|
||||||
declare var APP: Object;
|
declare var APP: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StateListenerRegistry provides a reliable way of detecting changes to selected
|
* Handles changes in the visible participants in the filmstrip. The listener is debounced
|
||||||
* endpoints state and dispatching additional actions. The listener is debounced
|
|
||||||
* so that the client doesn't end up sending too many bridge messages when the user is
|
* so that the client doesn't end up sending too many bridge messages when the user is
|
||||||
* scrolling through the thumbnails prompting updates to the selected endpoints.
|
* scrolling through the thumbnails prompting updates to the selected endpoints.
|
||||||
*/
|
*/
|
||||||
StateListenerRegistry.register(
|
StateListenerRegistry.register(
|
||||||
/* selector */ state => state['features/video-layout'].selectedEndpoints,
|
/* selector */ state => state['features/filmstrip'].visibleParticipants,
|
||||||
/* listener */ debounce((selectedEndpoints, store) => {
|
/* listener */ debounce((visibleParticipants, store) => {
|
||||||
_updateReceiverVideoConstraints(store);
|
_updateReceiverVideoConstraints(store);
|
||||||
}, 1000));
|
}, 100));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the use case when the on-stage participant has changed.
|
||||||
|
*/
|
||||||
|
StateListenerRegistry.register(
|
||||||
|
state => state['features/large-video'].participantId,
|
||||||
|
(participantId, store) => {
|
||||||
|
_updateReceiverVideoConstraints(store);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the use case when we have set some of the constraints in redux but the conference object wasn't available
|
||||||
|
* and we haven't been able to pass the constraints to lib-jitsi-meet.
|
||||||
|
*/
|
||||||
|
StateListenerRegistry.register(
|
||||||
|
state => state['features/base/conference'].conference,
|
||||||
|
(conference, store) => {
|
||||||
|
_updateReceiverVideoConstraints(store);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* StateListenerRegistry provides a reliable way of detecting changes to
|
||||||
|
@ -64,6 +95,8 @@ StateListenerRegistry.register(
|
||||||
typeof APP !== 'undefined' && APP.API.notifyVideoQualityChanged(preferredVideoQuality);
|
typeof APP !== 'undefined' && APP.API.notifyVideoQualityChanged(preferredVideoQuality);
|
||||||
}
|
}
|
||||||
changedReceiverVideoQuality && _updateReceiverVideoConstraints(store);
|
changedReceiverVideoQuality && _updateReceiverVideoConstraints(store);
|
||||||
|
}, {
|
||||||
|
deepEquals: true
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,28 +189,43 @@ function _updateReceiverVideoConstraints({ getState }) {
|
||||||
}
|
}
|
||||||
const { lastN } = state['features/base/lastn'];
|
const { lastN } = state['features/base/lastn'];
|
||||||
const { maxReceiverVideoQuality, preferredVideoQuality } = state['features/video-quality'];
|
const { maxReceiverVideoQuality, preferredVideoQuality } = state['features/video-quality'];
|
||||||
const { selectedEndpoints } = state['features/video-layout'];
|
const { visibleParticipants } = state['features/filmstrip'];
|
||||||
|
const { participantId: largeVideoParticipantId } = state['features/large-video'];
|
||||||
const maxFrameHeight = Math.min(maxReceiverVideoQuality, preferredVideoQuality);
|
const maxFrameHeight = Math.min(maxReceiverVideoQuality, preferredVideoQuality);
|
||||||
const receiverConstraints = {
|
const receiverConstraints = {
|
||||||
constraints: {},
|
constraints: {},
|
||||||
defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.LOW },
|
defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.NONE },
|
||||||
lastN,
|
lastN,
|
||||||
onStageEndpoints: [],
|
onStageEndpoints: [],
|
||||||
selectedEndpoints: []
|
selectedEndpoints: []
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!selectedEndpoints?.length) {
|
// Tile view.
|
||||||
return;
|
if (shouldDisplayTileView(state)) {
|
||||||
}
|
if (!visibleParticipants?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
visibleParticipants.forEach(participantId => {
|
||||||
|
receiverConstraints.constraints[participantId] = { 'maxHeight': maxFrameHeight };
|
||||||
|
});
|
||||||
|
|
||||||
// Stage view.
|
// Stage view.
|
||||||
if (selectedEndpoints?.length === 1) {
|
|
||||||
receiverConstraints.constraints[selectedEndpoints[0]] = { 'maxHeight': maxFrameHeight };
|
|
||||||
receiverConstraints.onStageEndpoints = selectedEndpoints;
|
|
||||||
|
|
||||||
// Tile view.
|
|
||||||
} else {
|
} else {
|
||||||
receiverConstraints.defaultConstraints = { 'maxHeight': maxFrameHeight };
|
if (!visibleParticipants?.length && !largeVideoParticipantId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibleParticipants?.length > 0) {
|
||||||
|
visibleParticipants.forEach(participantId => {
|
||||||
|
receiverConstraints.constraints[participantId] = { 'maxHeight': VIDEO_QUALITY_LEVELS.LOW };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (largeVideoParticipantId) {
|
||||||
|
receiverConstraints.constraints[largeVideoParticipantId] = { 'maxHeight': maxFrameHeight };
|
||||||
|
receiverConstraints.onStageEndpoints = [ largeVideoParticipantId ];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Setting receiver video constraints to ${JSON.stringify(receiverConstraints)}`);
|
logger.info(`Setting receiver video constraints to ${JSON.stringify(receiverConstraints)}`);
|
||||||
|
|
Loading…
Reference in New Issue