diff --git a/modules/UI/videolayout/LargeContainer.js b/modules/UI/videolayout/LargeContainer.js index 319ccef62..cc80f3f42 100644 --- a/modules/UI/videolayout/LargeContainer.js +++ b/modules/UI/videolayout/LargeContainer.js @@ -38,4 +38,20 @@ export default class LargeContainer { */ onHoverOut (e) { } + + /** + * Update video stream. + * @param {JitsiTrack?} stream new stream + * @param {string} videoType video type + */ + setStream (stream, videoType) { + } + + /** + * Show or hide user avatar. + * @param {boolean} show + */ + showAvatar (show) { + } + } diff --git a/modules/UI/videolayout/LargeVideo.js b/modules/UI/videolayout/LargeVideo.js index 9f27bb8b2..88d74cd65 100644 --- a/modules/UI/videolayout/LargeVideo.js +++ b/modules/UI/videolayout/LargeVideo.js @@ -150,7 +150,7 @@ function getDesktopVideoPosition(videoWidth, return { horizontalIndent, verticalIndent }; } -export const VideoContainerType = "video"; +export const VideoContainerType = "camera"; /** * Container for user video. @@ -332,6 +332,10 @@ class VideoContainer extends LargeContainer { } hide () { + // as the container is hidden/replaced by another container + // hide its avatar + this.showAvatar(false); + // its already hidden if (!this.isVisible) { return Promise.resolve(); @@ -357,6 +361,8 @@ export default class LargeVideoManager { this.state = VideoContainerType; this.videoContainer = new VideoContainer(() => this.resizeContainer(VideoContainerType)); this.addContainer(VideoContainerType, this.videoContainer); + // use the same video container to handle and desktop tracks + this.addContainer("desktop", this.videoContainer); this.width = 0; this.height = 0; @@ -413,7 +419,8 @@ export default class LargeVideoManager { } get id () { - return this.videoContainer.id; + let container = this.getContainer(this.state); + return container.id; } scheduleLargeVideoUpdate () { @@ -430,8 +437,9 @@ export default class LargeVideoManager { this.newStreamData = null; console.info("hover in %s", id); - this.state = VideoContainerType; - this.videoContainer.setStream(stream, videoType); + this.state = videoType; + let container = this.getContainer(this.state); + container.setStream(stream, videoType); // change the avatar url on large this.updateAvatar(Avatar.getAvatarUrl(id)); @@ -439,7 +447,7 @@ export default class LargeVideoManager { let isVideoMuted = stream ? stream.isMuted() : true; // show the avatar on large if needed - this.videoContainer.showAvatar(isVideoMuted); + container.showAvatar(isVideoMuted); let promise; @@ -449,7 +457,7 @@ export default class LargeVideoManager { this.showWatermark(true); promise = Promise.resolve(); } else { - promise = this.videoContainer.show(); + promise = container.show(); } // resolve updateLargeVideo promise after everything is done @@ -529,7 +537,8 @@ export default class LargeVideoManager { * @param enable true to enable, false to disable */ enableVideoProblemFilter (enable) { - this.videoContainer.$video.toggleClass("videoProblemFilter", enable); + let container = this.getContainer(this.state); + container.$video.toggleClass("videoProblemFilter", enable); } /** diff --git a/modules/UI/videolayout/LocalVideo.js b/modules/UI/videolayout/LocalVideo.js index c108e3252..380e9d7cc 100644 --- a/modules/UI/videolayout/LocalVideo.js +++ b/modules/UI/videolayout/LocalVideo.js @@ -13,7 +13,6 @@ function LocalVideo(VideoLayout, emitter) { this.videoSpanId = "localVideoContainer"; this.container = $("#localVideoContainer").get(0); this.bindHoverHandler(); - this.VideoLayout = VideoLayout; this.flipX = true; this.isLocal = true; this.emitter = emitter; @@ -22,7 +21,7 @@ function LocalVideo(VideoLayout, emitter) { return APP.conference.localId; } }); - SmallVideo.call(this); + SmallVideo.call(this, VideoLayout); } LocalVideo.prototype = Object.create(SmallVideo.prototype); diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index d9f4dae10..60c9fbcf9 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -11,14 +11,13 @@ function RemoteVideo(id, VideoLayout, emitter) { this.id = id; this.emitter = emitter; this.videoSpanId = `participant_${id}`; - this.VideoLayout = VideoLayout; + SmallVideo.call(this, VideoLayout); this.addRemoteVideoContainer(); this.connectionIndicator = new ConnectionIndicator(this, id); this.setDisplayName(); this.bindHoverHandler(); this.flipX = false; this.isLocal = false; - SmallVideo.call(this); } RemoteVideo.prototype = Object.create(SmallVideo.prototype); diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index e55eafec6..fa5bac0bf 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -5,12 +5,13 @@ import UIUtil from "../util/UIUtil"; const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper; -function SmallVideo() { +function SmallVideo(VideoLayout) { this.isMuted = false; this.hasAvatar = false; this.isVideoMuted = false; this.videoStream = null; this.audioStream = null; + this.VideoLayout = VideoLayout; } function setVisibility(selector, show) { diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index 1dd96d20e..31adc8ae1 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -16,7 +16,6 @@ import PanelToggler from "../side_pannels/SidePanelToggler"; const RTCUIUtil = JitsiMeetJS.util.RTCUIHelper; var remoteVideos = {}; -var remoteVideoTypes = {}; var localVideoThumbnail = null; var currentDominantSpeaker = null; @@ -277,10 +276,11 @@ var VideoLayout = { /** * Return the type of the remote video. * @param id the id for the remote video - * @returns the video type video or screen. + * @returns {String} the video type video or screen. */ getRemoteVideoType (id) { - return remoteVideoTypes[id]; + let smallVideo = VideoLayout.getSmallVideo(id); + return smallVideo ? smallVideo.getVideoType() : null; }, handleVideoThumbClicked (noPinnedEndpointChangedEvent, @@ -326,22 +326,26 @@ var VideoLayout = { this.updateLargeVideo(resourceJid); }, - /** - * Checks if container for participant identified by given id exists - * in the document and creates it eventually. - * - * @return Returns true if the peer container exists, - * false - otherwise + * Creates a remote video for participant for the given id. + * @param id the id of the participant to add + * @param {SmallVideo} smallVideo optional small video instance to add as a + * remote video, if undefined RemoteVideo will be created */ - addParticipantContainer (id) { - let remoteVideo = new RemoteVideo(id, VideoLayout, eventEmitter); + addParticipantContainer (id, smallVideo) { + let remoteVideo; + if(smallVideo) + remoteVideo = smallVideo; + else + remoteVideo = new RemoteVideo(id, VideoLayout, eventEmitter); remoteVideos[id] = remoteVideo; - let videoType = remoteVideoTypes[id]; - if (videoType) { - remoteVideo.setVideoType(videoType); + let videoType = VideoLayout.getRemoteVideoType(id); + if (!videoType) { + // make video type the default one (camera) + videoType = VideoContainerType; } + remoteVideo.setVideoType(videoType); // In case this is not currently in the last n we don't show it. if (localLastNCount && localLastNCount > 0 && @@ -752,12 +756,11 @@ var VideoLayout = { }, onVideoTypeChanged (id, newVideoType) { - if (remoteVideoTypes[id] === newVideoType) { + if (VideoLayout.getRemoteVideoType(id) === newVideoType) { return; } console.info("Peer video type changed: ", id, newVideoType); - remoteVideoTypes[id] = newVideoType; var smallVideo; if (APP.conference.isLocalId(id)) { @@ -771,8 +774,8 @@ var VideoLayout = { } else { return; } - smallVideo.setVideoType(newVideoType); + if (this.isCurrentlyOnLarge(id)) { this.updateLargeVideo(id, true); }