mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
prioritize participants with screen shares support local screen share track auto pin screen share support screen share for large video ensure fake screen share participants are sorted fix local screen share in vertical filmstrip fix local screen share in tile mode use FakeScreenShareParticipant component for screen share thumbnails ensure changes are behind feature flag and update jsdocs fix bug where local screen share was not rendering update receiver constraints to include SS source names remove fake ss participant creation on track update fix: handle screenshare muted change and track removal refactor: update key values for sortedFakeScreenShareParticipants address PR comments refactor getter for screenshare tracks rename state to sortedRemoteFakeScreenShareParticipants
346 lines
9.6 KiB
JavaScript
346 lines
9.6 KiB
JavaScript
/* global APP */
|
|
|
|
import Logger from '@jitsi/logger';
|
|
|
|
import { getSourceNameSignalingFeatureFlag } from '../../../react/features/base/config';
|
|
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media';
|
|
import {
|
|
getPinnedParticipant,
|
|
getParticipantById
|
|
} from '../../../react/features/base/participants';
|
|
import {
|
|
getTrackByMediaTypeAndParticipant,
|
|
getFakeScreenshareParticipantTrack
|
|
} from '../../../react/features/base/tracks';
|
|
|
|
import LargeVideoManager from './LargeVideoManager';
|
|
import { VIDEO_CONTAINER_TYPE } from './VideoContainer';
|
|
|
|
const logger = Logger.getLogger(__filename);
|
|
let largeVideo;
|
|
|
|
const VideoLayout = {
|
|
/**
|
|
* Handler for local flip X changed event.
|
|
*/
|
|
onLocalFlipXChanged() {
|
|
if (largeVideo) {
|
|
const { store } = APP;
|
|
const { localFlipX } = store.getState()['features/base/settings'];
|
|
|
|
largeVideo.onLocalFlipXChange(localFlipX);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Cleans up state of this singleton {@code VideoLayout}.
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
reset() {
|
|
this._resetLargeVideo();
|
|
},
|
|
|
|
initLargeVideo() {
|
|
this._resetLargeVideo();
|
|
|
|
largeVideo = new LargeVideoManager();
|
|
|
|
const { store } = APP;
|
|
const { localFlipX } = store.getState()['features/base/settings'];
|
|
|
|
if (typeof localFlipX === 'boolean') {
|
|
largeVideo.onLocalFlipXChange(localFlipX);
|
|
}
|
|
largeVideo.updateContainerSize();
|
|
},
|
|
|
|
/**
|
|
* Sets the audio level of the video elements associated to the given id.
|
|
*
|
|
* @param id the video identifier in the form it comes from the library
|
|
* @param lvl the new audio level to update to
|
|
*/
|
|
setAudioLevel(id, lvl) {
|
|
if (largeVideo && id === largeVideo.id) {
|
|
largeVideo.updateLargeVideoAudioLevel(lvl);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* FIXME get rid of this method once muted indicator are reactified (by
|
|
* making sure that user with no tracks is displayed as muted )
|
|
*
|
|
* If participant has no tracks will make the UI display muted status.
|
|
* @param {string} participantId
|
|
*/
|
|
updateVideoMutedForNoTracks(participantId) {
|
|
const participant = APP.conference.getParticipantById(participantId);
|
|
|
|
if (participant && !participant.getTracksByMediaType('video').length) {
|
|
VideoLayout._updateLargeVideoIfDisplayed(participantId, true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Return the type of the remote video.
|
|
* @param id the id for the remote video
|
|
* @returns {String} the video type video or screen.
|
|
*/
|
|
getRemoteVideoType(id) {
|
|
const state = APP.store.getState();
|
|
const participant = getParticipantById(state, id);
|
|
|
|
if (participant?.isFakeParticipant) {
|
|
return VIDEO_TYPE.CAMERA;
|
|
}
|
|
|
|
if (getSourceNameSignalingFeatureFlag(state) && participant?.isFakeScreenShareParticipant) {
|
|
return VIDEO_TYPE.DESKTOP;
|
|
}
|
|
|
|
const videoTrack = getTrackByMediaTypeAndParticipant(state['features/base/tracks'], MEDIA_TYPE.VIDEO, id);
|
|
|
|
return videoTrack?.videoType;
|
|
},
|
|
|
|
getPinnedId() {
|
|
const { id } = getPinnedParticipant(APP.store.getState()) || {};
|
|
|
|
return id || null;
|
|
},
|
|
|
|
/**
|
|
* Shows/hides warning about a user's connectivity issues.
|
|
*
|
|
* @param {string} id - The ID of the remote participant(MUC nickname).
|
|
* @returns {void}
|
|
*/
|
|
onParticipantConnectionStatusChanged(id) {
|
|
if (APP.conference.isLocalId(id)) {
|
|
|
|
return;
|
|
}
|
|
|
|
// We have to trigger full large video update to transition from
|
|
// avatar to video on connectivity restored.
|
|
this._updateLargeVideoIfDisplayed(id, true);
|
|
},
|
|
|
|
/**
|
|
* On last N change event.
|
|
*
|
|
* @param endpointsLeavingLastN the list currently leaving last N
|
|
* endpoints
|
|
* @param endpointsEnteringLastN the list currently entering last N
|
|
* endpoints
|
|
*/
|
|
onLastNEndpointsChanged(endpointsLeavingLastN, endpointsEnteringLastN) {
|
|
if (endpointsLeavingLastN) {
|
|
endpointsLeavingLastN.forEach(this._updateLargeVideoIfDisplayed, this);
|
|
}
|
|
|
|
if (endpointsEnteringLastN) {
|
|
endpointsEnteringLastN.forEach(this._updateLargeVideoIfDisplayed, this);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Resizes the video area.
|
|
*/
|
|
resizeVideoArea() {
|
|
if (largeVideo) {
|
|
largeVideo.updateContainerSize();
|
|
largeVideo.resize(false);
|
|
}
|
|
},
|
|
|
|
changeUserAvatar(id, avatarUrl) {
|
|
if (this.isCurrentlyOnLarge(id)) {
|
|
largeVideo.updateAvatar(avatarUrl);
|
|
}
|
|
},
|
|
|
|
isLargeVideoVisible() {
|
|
return this.isLargeContainerTypeVisible(VIDEO_CONTAINER_TYPE);
|
|
},
|
|
|
|
/**
|
|
* @return {LargeContainer} the currently displayed container on large
|
|
* video.
|
|
*/
|
|
getCurrentlyOnLargeContainer() {
|
|
return largeVideo.getCurrentContainer();
|
|
},
|
|
|
|
isCurrentlyOnLarge(id) {
|
|
return largeVideo && largeVideo.id === id;
|
|
},
|
|
|
|
updateLargeVideo(id, forceUpdate) {
|
|
if (!largeVideo) {
|
|
return;
|
|
}
|
|
const currentContainer = largeVideo.getCurrentContainer();
|
|
const currentContainerType = largeVideo.getCurrentContainerType();
|
|
const isOnLarge = this.isCurrentlyOnLarge(id);
|
|
const state = APP.store.getState();
|
|
const participant = getParticipantById(state, id);
|
|
const tracks = state['features/base/tracks'];
|
|
|
|
let videoTrack;
|
|
|
|
if (getSourceNameSignalingFeatureFlag(state) && participant?.isFakeScreenShareParticipant) {
|
|
videoTrack = getFakeScreenshareParticipantTrack(tracks, id);
|
|
} else {
|
|
videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
|
|
}
|
|
|
|
const videoStream = videoTrack?.jitsiTrack;
|
|
|
|
if (isOnLarge && !forceUpdate
|
|
&& LargeVideoManager.isVideoContainer(currentContainerType)
|
|
&& videoStream) {
|
|
const currentStreamId = currentContainer.getStreamID();
|
|
const newStreamId = videoStream?.getId() || null;
|
|
|
|
// FIXME it might be possible to get rid of 'forceUpdate' argument
|
|
if (currentStreamId !== newStreamId) {
|
|
logger.debug('Enforcing large video update for stream change');
|
|
forceUpdate = true; // eslint-disable-line no-param-reassign
|
|
}
|
|
}
|
|
|
|
if (!isOnLarge || forceUpdate) {
|
|
const videoType = this.getRemoteVideoType(id);
|
|
|
|
|
|
largeVideo.updateLargeVideo(
|
|
id,
|
|
videoStream,
|
|
videoType || VIDEO_TYPE.CAMERA
|
|
).catch(() => {
|
|
// do nothing
|
|
});
|
|
}
|
|
},
|
|
|
|
addLargeVideoContainer(type, container) {
|
|
largeVideo && largeVideo.addContainer(type, container);
|
|
},
|
|
|
|
removeLargeVideoContainer(type) {
|
|
largeVideo && largeVideo.removeContainer(type);
|
|
},
|
|
|
|
/**
|
|
* @returns Promise
|
|
*/
|
|
showLargeVideoContainer(type, show) {
|
|
if (!largeVideo) {
|
|
return Promise.reject();
|
|
}
|
|
|
|
const isVisible = this.isLargeContainerTypeVisible(type);
|
|
|
|
if (isVisible === show) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
let containerTypeToShow = type;
|
|
|
|
// if we are hiding a container and there is focusedVideo
|
|
// (pinned remote video) use its video type,
|
|
// if not then use default type - large video
|
|
|
|
if (!show) {
|
|
const pinnedId = this.getPinnedId();
|
|
|
|
if (pinnedId) {
|
|
containerTypeToShow = this.getRemoteVideoType(pinnedId);
|
|
} else {
|
|
containerTypeToShow = VIDEO_CONTAINER_TYPE;
|
|
}
|
|
}
|
|
|
|
return largeVideo.showContainer(containerTypeToShow);
|
|
},
|
|
|
|
isLargeContainerTypeVisible(type) {
|
|
return largeVideo && largeVideo.state === type;
|
|
},
|
|
|
|
/**
|
|
* Returns the id of the current video shown on large.
|
|
* Currently used by tests (torture).
|
|
*/
|
|
getLargeVideoID() {
|
|
return largeVideo && largeVideo.id;
|
|
},
|
|
|
|
/**
|
|
* Returns the the current video shown on large.
|
|
* Currently used by tests (torture).
|
|
*/
|
|
getLargeVideo() {
|
|
return largeVideo;
|
|
},
|
|
|
|
/**
|
|
* Returns the wrapper jquery selector for the largeVideo
|
|
* @returns {JQuerySelector} the wrapper jquery selector for the largeVideo
|
|
*/
|
|
getLargeVideoWrapper() {
|
|
return this.getCurrentlyOnLargeContainer().$wrapper;
|
|
},
|
|
|
|
/**
|
|
* Helper method to invoke when the video layout has changed and elements
|
|
* have to be re-arranged and resized.
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
refreshLayout() {
|
|
VideoLayout.resizeVideoArea();
|
|
},
|
|
|
|
/**
|
|
* Cleans up any existing largeVideo instance.
|
|
*
|
|
* @private
|
|
* @returns {void}
|
|
*/
|
|
_resetLargeVideo() {
|
|
if (largeVideo) {
|
|
largeVideo.destroy();
|
|
}
|
|
|
|
largeVideo = null;
|
|
},
|
|
|
|
/**
|
|
* Triggers an update of large video if the passed in participant is
|
|
* currently displayed on large video.
|
|
*
|
|
* @param {string} participantId - The participant ID that should trigger an
|
|
* update of large video if displayed.
|
|
* @param {boolean} force - Whether or not the large video update should
|
|
* happen no matter what.
|
|
* @returns {void}
|
|
*/
|
|
_updateLargeVideoIfDisplayed(participantId, force = false) {
|
|
if (this.isCurrentlyOnLarge(participantId)) {
|
|
this.updateLargeVideo(participantId, force);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handles window resizes.
|
|
*/
|
|
onResize() {
|
|
VideoLayout.resizeVideoArea();
|
|
}
|
|
};
|
|
|
|
export default VideoLayout;
|