diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 4425b7379..8dd4a88da 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -822,14 +822,6 @@ UI.notifyInitiallyMuted = function() { null); }; -/** - * Mark user as dominant speaker. - * @param {string} id user id - */ -UI.markDominantSpeaker = function(id) { - VideoLayout.onDominantSpeakerChanged(id); -}; - UI.handleLastNEndpoints = function(leavingIds, enteringIds) { VideoLayout.onLastNEndpointsChanged(leavingIds, enteringIds); }; diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index ba854afda..cff14a439 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -679,6 +679,10 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function(show) { return; } + if (this._showDominantSpeaker === show) { + return; + } + this._showDominantSpeaker = show; this.updateIndicators(); diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index ec20ed600..11f4e7521 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -5,6 +5,7 @@ import { JitsiParticipantConnectionStatus } from '../../../react/features/base/lib-jitsi-meet'; import { + getParticipants, getPinnedParticipant, pinParticipant } from '../../../react/features/base/participants'; @@ -21,8 +22,6 @@ import LocalVideo from './LocalVideo'; const remoteVideos = {}; let localVideoThumbnail = null; -let currentDominantSpeaker = null; - let eventEmitter = null; let largeVideo; @@ -43,6 +42,24 @@ function onLocalFlipXChanged(val) { } } +/** + * Returns the user ID of the remote participant that is current the dominant + * speaker. + * + * @private + * @returns {string|null} + */ +function getCurrentRemoteDominantSpeakerID() { + const dominantSpeaker = getParticipants(APP.store.getState) + .find(participant => participant.dominantSpeaker); + + if (dominantSpeaker) { + return dominantSpeaker.local ? null : dominantSpeaker.id; + } + + return null; +} + /** * Returns the corresponding resource id to the given peer container * DOM element. @@ -228,8 +245,8 @@ const VideoLayout = { if (pinnedId) { newId = pinnedId; - } else if (currentDominantSpeaker) { - newId = currentDominantSpeaker; + } else if (getCurrentRemoteDominantSpeakerID()) { + newId = getCurrentRemoteDominantSpeakerID(); } else { // Otherwise select last visible video newId = this.electLastVisibleVideo(); } @@ -392,8 +409,8 @@ const VideoLayout = { this.pinParticipant(null); // Enable the currently set dominant speaker. - if (currentDominantSpeaker) { - this.updateLargeVideo(currentDominantSpeaker); + if (getCurrentRemoteDominantSpeakerID()) { + this.updateLargeVideo(getCurrentRemoteDominantSpeakerID()); } else { // if there is no currentDominantSpeaker, it can also be // that local participant is the dominant speaker @@ -501,11 +518,11 @@ const VideoLayout = { const pinnedId = this.getPinnedId(); if ((!pinnedId - && !currentDominantSpeaker + && !getCurrentRemoteDominantSpeakerID() && this.isLargeContainerTypeVisible(VIDEO_CONTAINER_TYPE)) || pinnedId === resourceJid || (!pinnedId && resourceJid - && currentDominantSpeaker === resourceJid) + && getCurrentRemoteDominantSpeakerID() === resourceJid) /* Playback started while we're on the stage - may need to update video source with the new stream */ @@ -662,43 +679,22 @@ const VideoLayout = { /** * On dominant speaker changed event. + * + * @param {string} id - The participant ID of the new dominant speaker. + * @returns {void} */ onDominantSpeakerChanged(id) { - if (id === currentDominantSpeaker) { + Object.values(remoteVideos).forEach(remoteVideo => + remoteVideo.showDominantSpeakerIndicator( + id === remoteVideo.getId())); + + localVideoThumbnail.showDominantSpeakerIndicator( + APP.conference.isLocalId(id)); + + if (!remoteVideos[id]) { return; } - const oldSpeakerRemoteVideo = remoteVideos[currentDominantSpeaker]; - - // We ignore local user events, but just unmark remote user as dominant - // while we are talking - - if (APP.conference.isLocalId(id)) { - if (oldSpeakerRemoteVideo) { - oldSpeakerRemoteVideo.showDominantSpeakerIndicator(false); - currentDominantSpeaker = null; - } - localVideoThumbnail.showDominantSpeakerIndicator(true); - - return; - } - - const remoteVideo = remoteVideos[id]; - - if (!remoteVideo) { - return; - } - - // Update the current dominant speaker. - remoteVideo.showDominantSpeakerIndicator(true); - localVideoThumbnail.showDominantSpeakerIndicator(false); - - // let's remove the indications from the remote video if any - if (oldSpeakerRemoteVideo) { - oldSpeakerRemoteVideo.showDominantSpeakerIndicator(false); - } - currentDominantSpeaker = id; - // Local video will not have container found, but that's ok // since we don't want to switch to local video. if (!interfaceConfig.filmStripOnly && !this.getPinnedId() @@ -804,11 +800,6 @@ const VideoLayout = { this.pinParticipant(null); } - if (currentDominantSpeaker === id) { - logger.info('Dominant speaker has left the conference'); - currentDominantSpeaker = null; - } - const remoteVideo = remoteVideos[id]; if (remoteVideo) { diff --git a/react/features/base/participants/middleware.js b/react/features/base/participants/middleware.js index e49d9e0c4..a2549f132 100644 --- a/react/features/base/participants/middleware.js +++ b/react/features/base/participants/middleware.js @@ -77,8 +77,6 @@ MiddlewareRegistry.register(store => next => action => { raisedHand: false })); - typeof APP === 'object' && APP.UI.markDominantSpeaker(id); - break; } diff --git a/react/features/video-layout/middleware.web.js b/react/features/video-layout/middleware.web.js index c0930695d..753ecc0d3 100644 --- a/react/features/video-layout/middleware.web.js +++ b/react/features/video-layout/middleware.web.js @@ -1,3 +1,6 @@ +import VideoLayout from '../../../modules/UI/videolayout/VideoLayout.js'; + +import { DOMINANT_SPEAKER_CHANGED } from '../base/participants'; import { MiddlewareRegistry } from '../base/redux'; /** @@ -9,4 +12,14 @@ import { MiddlewareRegistry } from '../base/redux'; * @returns {Function} */ // eslint-disable-next-line no-unused-vars -MiddlewareRegistry.register(store => next => action => next(action)); +MiddlewareRegistry.register(store => next => action => { + const result = next(action); + + switch (action.type) { + case DOMINANT_SPEAKER_CHANGED: + VideoLayout.onDominantSpeakerChanged(action.participant.id); + break; + } + + return result; +});