diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index 351326d57..ae5952443 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -238,6 +238,18 @@ RemoteVideo.prototype.isConnectionActive = function() { return this.user.isConnectionActive(); }; +/** + * The remote video is considered "playable" once the stream has started + * according to the {@link #hasVideoStarted} result. + * + * @inheritdoc + * @override + */ +RemoteVideo.prototype.isVideoPlayable = function () { + return SmallVideo.prototype.isVideoPlayable.call(this) + && this.hasVideoStarted(); +}; + /** * @inheritDoc */ diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index 5cc9769f1..65a433469 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -5,6 +5,27 @@ import UIEvents from "../../../service/UI/UIEvents"; const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper; +/** + * Display mode constant used when video is being displayed on the small video. + * @type {number} + * @constant + */ +const DISPLAY_VIDEO = 0; +/** + * Display mode constant used when the user's avatar is being displayed on + * the small video. + * @type {number} + * @constant + */ +const DISPLAY_AVATAR = 1; +/** + * Display mode constant used when neither video nor avatar is being displayed + * on the small video. + * @type {number} + * @constant + */ +const DISPLAY_BLACKNESS = 2; + function SmallVideo(VideoLayout) { this.isAudioMuted = false; this.hasAvatar = false; @@ -380,6 +401,36 @@ SmallVideo.prototype.isCurrentlyOnLargeVideo = function () { return this.VideoLayout.isCurrentlyOnLarge(this.id); }; +/** + * Checks whether there is a playable video stream available for the user + * associated with this SmallVideo. + * + * @return {boolean} true if there is a playable video stream available + * or false otherwise. + */ +SmallVideo.prototype.isVideoPlayable = function() { + return this.videoStream // Is there anything to display ? + && !this.isVideoMuted && !this.videoStream.isMuted() // Muted ? + && (this.isLocal || this.VideoLayout.isInLastN(this.id)); +}; + +/** + * Determines what should be display on the thumbnail. + * + * @return {number} one of DISPLAY_VIDEO,DISPLAY_AVATAR + * or DISPLAY_BLACKNESS. + */ +SmallVideo.prototype.selectDisplayMode = function() { + // Display name is always and only displayed when user is on the stage + if (this.isCurrentlyOnLargeVideo()) { + return DISPLAY_BLACKNESS; + } else if (this.isVideoPlayable() && this.selectVideoElement().length) { + return DISPLAY_VIDEO; + } else { + return DISPLAY_AVATAR; + } +}; + /** * Hides or shows the user's avatar. * This update assumes that large video had been updated and we will @@ -399,30 +450,12 @@ SmallVideo.prototype.updateView = function () { } } - let video = this.selectVideoElement(); - - let avatar = this.$avatar; - - var isCurrentlyOnLarge = this.isCurrentlyOnLargeVideo(); - - var showVideo = !this.isVideoMuted && !isCurrentlyOnLarge; - var showAvatar; - if ((!this.isLocal - && !this.VideoLayout.isInLastN(this.id)) - || this.isVideoMuted) { - showAvatar = true; - } else { - // We want to show the avatar when the video is muted or not exists - // that is when 'true' or 'null' is returned - showAvatar = !this.videoStream || this.videoStream.isMuted(); - } - - showAvatar = showAvatar && !isCurrentlyOnLarge; - - if (video && video.length > 0) { - setVisibility(video, showVideo); - } - setVisibility(avatar, showAvatar); + // Determine whether video, avatar or blackness should be displayed + let displayMode = this.selectDisplayMode(); + // Show/hide video + setVisibility(this.selectVideoElement(), displayMode === DISPLAY_VIDEO); + // Show/hide the avatar + setVisibility(this.$avatar(), displayMode === DISPLAY_AVATAR); }; SmallVideo.prototype.avatarChanged = function (avatarUrl) {