ref(large-video): switch LargeVideo logic to react

This commit is contained in:
Hristo Terezov 2019-07-02 14:06:52 +01:00
parent 625d268373
commit 21dcc41d31
7 changed files with 26 additions and 236 deletions

View File

@ -1253,7 +1253,7 @@ export default {
this.localVideo = newStream;
this._setSharingScreen(newStream);
if (newStream) {
APP.UI.addLocalStream(newStream);
APP.UI.addLocalVideoStream(newStream);
}
this.setVideoMuteStatus(this.isLocalVideoMuted());
})
@ -1304,9 +1304,6 @@ export default {
replaceLocalTrack(this.localAudio, newStream, room))
.then(() => {
this.localAudio = newStream;
if (newStream) {
APP.UI.addLocalStream(newStream);
}
this.setAudioMuteStatus(this.isLocalAudioMuted());
})
.then(resolve)

View File

@ -229,22 +229,11 @@ UI.unbindEvents = () => {
};
/**
* Show local stream on UI.
* Show local video stream on UI.
* @param {JitsiTrack} track stream to show
*/
UI.addLocalStream = track => {
switch (track.getType()) {
case 'audio':
// Local audio is not rendered so no further action is needed at this
// point.
break;
case 'video':
VideoLayout.changeLocalVideo(track);
break;
default:
logger.error(`Unknown stream type: ${track.getType()}`);
break;
}
UI.addLocalVideoStream = track => {
VideoLayout.changeLocalVideo(track);
};
/**

View File

@ -53,16 +53,6 @@ function onLocalFlipXChanged(val) {
}
}
/**
* Returns the redux representation of all known users.
*
* @private
* @returns {Array}
*/
function getAllParticipants() {
return APP.store.getState()['features/base/participants'];
}
/**
* Returns an array of all thumbnails in the filmstrip.
*
@ -86,43 +76,6 @@ function getLocalParticipant() {
return getLocalParticipantFromStore(APP.store.getState());
}
/**
* Returns the user ID of the remote participant that is current the dominant
* speaker.
*
* @private
* @returns {string|null}
*/
function getCurrentRemoteDominantSpeakerID() {
const dominantSpeaker = getAllParticipants()
.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.
*
* @return the corresponding resource id to the given peer container
* DOM element
*/
function getPeerContainerResourceId(containerElement) {
if (localVideoThumbnail.container === containerElement) {
return localVideoThumbnail.id;
}
const i = containerElement.id.indexOf('participant_');
if (i >= 0) {
return containerElement.id.substring(i + 12);
}
}
const VideoLayout = {
init(emitter) {
eventEmitter = emitter;
@ -208,10 +161,6 @@ const VideoLayout = {
* and setting them assume the id is already set.
*/
mucJoined() {
if (largeVideo && !largeVideo.id) {
this.updateLargeVideo(getLocalParticipant().id, true);
}
// FIXME: replace this call with a generic update call once SmallVideo
// only contains a ReactElement. Then remove this call once the
// Filmstrip is fully in React.
@ -247,79 +196,6 @@ const VideoLayout = {
localVideoThumbnail.setVisible(visible);
},
/**
* Checks if removed video is currently displayed and tries to display
* another one instead.
* Uses focusedID if any or dominantSpeakerID if any,
* otherwise elects new video, in this order.
*/
_updateAfterThumbRemoved(id) {
// Always trigger an update if large video is empty.
if (!largeVideo
|| (this.getLargeVideoID() && !this.isCurrentlyOnLarge(id))) {
return;
}
const pinnedId = this.getPinnedId();
let newId;
if (pinnedId) {
newId = pinnedId;
} else if (getCurrentRemoteDominantSpeakerID()) {
newId = getCurrentRemoteDominantSpeakerID();
} else { // Otherwise select last visible video
newId = this.electLastVisibleVideo();
}
this.updateLargeVideo(newId);
},
electLastVisibleVideo() {
// pick the last visible video in the row
// if nobody else is left, this picks the local video
const remoteThumbs = Filmstrip.getThumbs(true).remoteThumbs;
let thumbs = remoteThumbs.filter('[id!="mixedstream"]');
const lastVisible = thumbs.filter(':visible:last');
if (lastVisible.length) {
const id = getPeerContainerResourceId(lastVisible[0]);
if (remoteVideos[id]) {
logger.info(`electLastVisibleVideo: ${id}`);
return id;
}
// The RemoteVideo was removed (but the DOM elements may still
// exist).
}
logger.info('Last visible video no longer exists');
thumbs = Filmstrip.getThumbs().remoteThumbs;
if (thumbs.length) {
const id = getPeerContainerResourceId(thumbs[0]);
if (remoteVideos[id]) {
logger.info(`electLastVisibleVideo: ${id}`);
return id;
}
// The RemoteVideo was removed (but the DOM elements may
// still exist).
}
// Go with local video
logger.info('Fallback to local video...');
const { id } = getLocalParticipant();
logger.info(`electLastVisibleVideo: ${id}`);
return id;
},
onRemoteStreamAdded(stream) {
const id = stream.getParticipantId();
const remoteVideo = remoteVideos[id];
@ -423,23 +299,6 @@ const VideoLayout = {
getAllThumbnails().forEach(thumbnail =>
thumbnail.focus(pinnedParticipantID === thumbnail.getId()));
if (pinnedParticipantID) {
this.updateLargeVideo(pinnedParticipantID);
} else {
const currentDominantSpeakerID
= getCurrentRemoteDominantSpeakerID();
if (currentDominantSpeakerID) {
this.updateLargeVideo(currentDominantSpeakerID);
} else {
// if there is no currentDominantSpeakerID, it can also be
// that local participant is the dominant speaker
// we should act as a participant has left and was on large
// and we should choose somebody (electLastVisibleVideo)
this.updateLargeVideo(this.electLastVisibleVideo());
}
}
},
/**
@ -473,19 +332,6 @@ const VideoLayout = {
this.updateMutedForNoTracks(id, 'audio');
this.updateMutedForNoTracks(id, 'video');
const remoteVideosCount = Object.keys(remoteVideos).length;
if (remoteVideosCount === 1) {
window.setTimeout(() => {
const updatedRemoteVideosCount
= Object.keys(remoteVideos).length;
if (updatedRemoteVideosCount === 1 && remoteVideos[id]) {
this._maybePlaceParticipantOnLargeVideo(id);
}
}, 3000);
}
},
/**
@ -512,43 +358,14 @@ const VideoLayout = {
// FIXME: what does this do???
remoteVideoActive(videoElement, resourceJid) {
logger.info(`${resourceJid} video is now active`, videoElement);
VideoLayout.resizeThumbnails(
false, () => {
if (videoElement) {
$(videoElement).show();
}
});
this._maybePlaceParticipantOnLargeVideo(resourceJid);
},
/**
* Update the large video to the last added video only if there's no current
* dominant, focused speaker or update it to the current dominant speaker.
*
* @params {string} resourceJid - The id of the user to maybe display on
* large video.
* @returns {void}
*/
_maybePlaceParticipantOnLargeVideo(resourceJid) {
const pinnedId = this.getPinnedId();
if ((!pinnedId
&& !getCurrentRemoteDominantSpeakerID()
&& this.isLargeContainerTypeVisible(VIDEO_CONTAINER_TYPE))
|| pinnedId === resourceJid
|| (!pinnedId && resourceJid
&& getCurrentRemoteDominantSpeakerID() === resourceJid)
/* Playback started while we're on the stage - may need to update
video source with the new stream */
|| this.isCurrentlyOnLarge(resourceJid)) {
this.updateLargeVideo(resourceJid, true);
}
this._updateLargeVideoIfDisplayed(resourceJid, true);
},
/**
@ -646,10 +463,8 @@ const VideoLayout = {
}
}
if (this.isCurrentlyOnLarge(id)) {
// large video will show avatar instead of muted stream
this.updateLargeVideo(id, true);
}
// large video will show avatar instead of muted stream
this._updateLargeVideoIfDisplayed(id, true);
},
/**
@ -677,18 +492,6 @@ const VideoLayout = {
onDominantSpeakerChanged(id) {
getAllThumbnails().forEach(thumbnail =>
thumbnail.showDominantSpeakerIndicator(id === thumbnail.getId()));
if (!remoteVideos[id]) {
return;
}
// 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()
&& !this.getCurrentlyOnLargeContainer().stayOnStage()) {
this.updateLargeVideo(id);
}
},
/**
@ -758,9 +561,7 @@ const VideoLayout = {
if (remoteVideo) {
remoteVideo.updateView();
if (remoteVideo.isCurrentlyOnLargeVideo()) {
this.updateLargeVideo(id);
}
this._updateLargeVideoIfDisplayed(id);
}
},
@ -809,7 +610,6 @@ const VideoLayout = {
}
VideoLayout.resizeThumbnails();
VideoLayout._updateAfterThumbRemoved(id);
},
onVideoTypeChanged(id, newVideoType) {
@ -835,9 +635,7 @@ const VideoLayout = {
}
smallVideo.setVideoType(newVideoType);
if (this.isCurrentlyOnLarge(id)) {
this.updateLargeVideo(id, true);
}
this._updateLargeVideoIfDisplayed(id, true);
},
/**

View File

@ -124,9 +124,7 @@ MiddlewareRegistry.register(store => next => action => {
} else {
APP.UI.setVideoMuted(participantID, muted);
}
APP.UI.onPeerVideoTypeChanged(
participantID,
jitsiTrack.videoType);
APP.UI.onPeerVideoTypeChanged(participantID, jitsiTrack.videoType);
} else if (jitsiTrack.isLocal()) {
APP.conference.setAudioMuteStatus(muted);
} else {

View File

@ -3,3 +3,4 @@ export * from './components';
import './middleware';
import './reducer';
import './subscriber';

View File

@ -38,7 +38,7 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case CONFERENCE_JOINED:
case PARTICIPANT_JOINED:
case PARTICIPANT_LEFT:
case PIN_PARTICIPANT:
@ -47,13 +47,6 @@ MiddlewareRegistry.register(store => next => action => {
store.dispatch(selectParticipantInLargeVideo());
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

View File

@ -0,0 +1,14 @@
// @flow
import { StateListenerRegistry } from '../base/redux';
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout';
/**
* Updates the on stage participant video.
*/
StateListenerRegistry.register(
/* selector */ state => state['features/large-video'].participantId,
/* listener */ participantId => {
VideoLayout.updateLargeVideo(participantId, true);
}
);