123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- /* global APP */
-
- import Logger from 'jitsi-meet-logger';
-
- import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media';
- import {
- getPinnedParticipant,
- getParticipantById
- } from '../../../react/features/base/participants';
- import { getTrackByMediaTypeAndParticipant } 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;
- }
-
- 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 videoTrack = getTrackByMediaTypeAndParticipant(state['features/base/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;
|