diff --git a/css/_variables.scss b/css/_variables.scss index e25d93796..bea3852a8 100644 --- a/css/_variables.scss +++ b/css/_variables.scss @@ -46,6 +46,8 @@ $participantNameColor: #fff; $thumbnailPictogramColor: #fff; $dominantSpeakerBg: #165ecc; $raiseHandBg: #D6D61E; +$audioLevelBg: #44A5FF; +$audioLevelShadow: rgba(9, 36, 77, 0.9); $rateStarDefault: #ccc; $rateStarActivity: #165ecc; diff --git a/css/_videolayout_default.scss b/css/_videolayout_default.scss index ab5a2ba2d..e8311bb0d 100644 --- a/css/_videolayout_default.scss +++ b/css/_videolayout_default.scss @@ -363,6 +363,46 @@ background: $raiseHandBg; } +/** + * Audio indicator on video thumbnails. + */ +.videocontainer>span.audioindicator { + position: absolute; + display: inline-block; + left: 6px; + top: 50%; + margin-top: -17px; + width: 6px; + height: 35px; + z-index: 2; + border: none; + + .audiodot-top, + .audiodot-bottom, + .audiodot-middle { + opacity: 0; + display: inline-block; + @include circle(5px); + background: $audioLevelShadow; + margin: 1px 0 1px 0; + transition: opacity .25s ease-in-out; + -moz-transition: opacity .25s ease-in-out; + } + + span.audiodot-top::after, + span.audiodot-bottom::after, + span.audiodot-middle::after { + content: ""; + display: inline-block; + width: 5px; + height: 5px; + border-radius: 50%; + -webkit-filter: blur(0.5px); + filter: blur(0.5px); + background: $audioLevelBg; + } +} + #indicatoricon { width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder; height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder; @@ -399,25 +439,20 @@ width: 300px; height: 300px; margin: auto; - overflow: hidden; position: relative; } -#dominantSpeakerAudioLevel { - position: absolute; - top: 0px; - left: 0px; - z-index: 2; - visibility: inherit; -} - #mixedstream { display:none !important; } -#dominantSpeakerAvatar { +#dominantSpeakerAvatar, +.dynamic-shadow { width: 200px; height: 200px; +} + +#dominantSpeakerAvatar { top: 50px; margin: auto; position: relative; @@ -427,6 +462,15 @@ background-color: #000000; } +.dynamic-shadow { + border-radius: 50%; + position: absolute; + top: 50%; + left: 50%; + margin: -100px 0 0 -100px; + transition: box-shadow 0.3s ease; +} + .userAvatar { @include circle(60px); @include absoluteAligning(60px, 60px); diff --git a/index.html b/index.html index 760cfd276..66e070f9d 100644 --- a/index.html +++ b/index.html @@ -225,8 +225,8 @@ jitsi.org
+
-
diff --git a/interface_config.js b/interface_config.js index 8a8b1385b..5218f0c56 100644 --- a/interface_config.js +++ b/interface_config.js @@ -41,5 +41,7 @@ var interfaceConfig = { REMOTE_THUMBNAIL_RATIO_HEIGHT: 1, // Enables feedback star animation. ENABLE_FEEDBACK_ANIMATION: false, - DISABLE_FOCUS_INDICATOR: false -}; + DISABLE_FOCUS_INDICATOR: false, + AUDIO_LEVEL_PRIMARY_COLOR: "rgba(255,255,255,0.7)", + AUDIO_LEVEL_SECONDARY_COLOR: "rgba(255,255,255,0.4)" +}; \ No newline at end of file diff --git a/modules/UI/audio_levels/AudioLevels.js b/modules/UI/audio_levels/AudioLevels.js index 258168909..ab0a8a32f 100644 --- a/modules/UI/audio_levels/AudioLevels.js +++ b/modules/UI/audio_levels/AudioLevels.js @@ -1,259 +1,165 @@ -/* global APP, interfaceConfig, $ */ -/* jshint -W101 */ +/* global interfaceConfig */ -import CanvasUtil from './CanvasUtils'; -import FilmStrip from '../videolayout/FilmStrip'; - -const LOCAL_LEVEL = 'local'; - -let ASDrawContext = null; -let audioLevelCanvasCache = {}; -let dominantSpeakerAudioElement = null; - -function _initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) { - let ASRadius = dominantSpeakerAvatarSize / 2; - let ASCenter = (dominantSpeakerAvatarSize + ASRadius) / 2; - - // Draw a circle. - ASDrawContext.beginPath(); - ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI); - ASDrawContext.closePath(); - - // Add a shadow around the circle - ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR; - ASDrawContext.shadowOffsetX = 0; - ASDrawContext.shadowOffsetY = 0; -} +import UIUtil from "../util/UIUtil"; /** - * Resizes the given audio level canvas to match the given thumbnail size. - */ -function _resizeAudioLevelCanvas( audioLevelCanvas, - thumbnailWidth, - thumbnailHeight) { - audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA; - audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA; -} - -/** - * Draws the audio level canvas into the cached canvas object. - * - * @param id of the user for whom we draw the audio level - * @param audioLevel the newAudio level to render - */ -function drawAudioLevelCanvas(id, audioLevel) { - if (!audioLevelCanvasCache[id]) { - - let videoSpanId = getVideoSpanId(id); - - let audioLevelCanvasOrig = $(`#${videoSpanId}>canvas`).get(0); - - /* - * FIXME Testing has shown that audioLevelCanvasOrig may not exist. - * In such a case, the method CanvasUtil.cloneCanvas may throw an - * error. Since audio levels are frequently updated, the errors have - * been observed to pile into the console, strain the CPU. - */ - if (audioLevelCanvasOrig) { - audioLevelCanvasCache[id] - = CanvasUtil.cloneCanvas(audioLevelCanvasOrig); - } - } - - let canvas = audioLevelCanvasCache[id]; - - if (!canvas) { - return; - } - - let drawContext = canvas.getContext('2d'); - - drawContext.clearRect(0, 0, canvas.width, canvas.height); - - let shadowLevel = getShadowLevel(audioLevel); - - if (shadowLevel > 0) { - // drawContext, x, y, w, h, r, shadowColor, shadowLevel - CanvasUtil.drawRoundRectGlow( - drawContext, - interfaceConfig.CANVAS_EXTRA / 2, interfaceConfig.CANVAS_EXTRA / 2, - canvas.width - interfaceConfig.CANVAS_EXTRA, - canvas.height - interfaceConfig.CANVAS_EXTRA, - interfaceConfig.CANVAS_RADIUS, - interfaceConfig.SHADOW_COLOR, - shadowLevel); - } -} - -/** - * Returns the shadow/glow level for the given audio level. - * - * @param audioLevel the audio level from which we determine the shadow - * level - */ -function getShadowLevel (audioLevel) { - let shadowLevel = 0; - - if (audioLevel <= 0.3) { - shadowLevel = Math.round( - interfaceConfig.CANVAS_EXTRA/2*(audioLevel/0.3)); - } else if (audioLevel <= 0.6) { - shadowLevel = Math.round( - interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3)); - } else { - shadowLevel = Math.round( - interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4)); - } - - return shadowLevel; -} - -/** - * Returns the video span id corresponding to the given user id - */ -function getVideoSpanId(id) { - let videoSpanId = null; - - if (id === LOCAL_LEVEL || APP.conference.isLocalId(id)) { - videoSpanId = 'localVideoContainer'; - } else { - videoSpanId = `participant_${id}`; - } - - return videoSpanId; -} - -/** - * The audio Levels plugin. + * Responsible for drawing audio levels. */ const AudioLevels = { - init () { - dominantSpeakerAudioElement = $('#dominantSpeakerAudioLevel')[0]; - ASDrawContext = dominantSpeakerAudioElement.getContext('2d'); - - let parentContainer = $("#dominantSpeaker"); - let dominantSpeakerWidth = parentContainer.width(); - let dominantSpeakerHeight = parentContainer.height(); - - dominantSpeakerAudioElement.width = dominantSpeakerWidth; - dominantSpeakerAudioElement.height = dominantSpeakerHeight; - - let dominantSpeakerAvatar = $("#dominantSpeakerAvatar"); - _initDominantSpeakerAudioLevels(dominantSpeakerAvatar.width()); - }, + /** + * The number of dots. + * + * IMPORTANT: functions below assume that this is an odd number. + */ + _AUDIO_LEVEL_DOTS: 5, /** - * Updates the audio level canvas for the given id. If the canvas - * didn't exist we create it. + * Creates the audio level indicator span element. + * + * IMPORTANT: This function assumes that the number of dots is an + * odd number. + * + * @return {Element} the document element representing audio levels */ - createAudioLevelCanvas (videoSpanId, thumbWidth, thumbHeight) { + createThumbnailAudioLevelIndicator() { - let videoSpan = document.getElementById(videoSpanId); + let audioSpan = document.createElement('span'); + audioSpan.className = 'audioindicator'; - if (!videoSpan) { - if (videoSpanId) { - console.error("No video element for id", videoSpanId); - } else { - console.error("No video element for local video."); - } - return; - } - - let audioLevelCanvas = $(`#${videoSpanId}>canvas`); - - if (!audioLevelCanvas || audioLevelCanvas.length === 0) { - - audioLevelCanvas = document.createElement('canvas'); - audioLevelCanvas.className = "audiolevel"; - audioLevelCanvas.style.bottom - = `-${interfaceConfig.CANVAS_EXTRA/2}px`; - audioLevelCanvas.style.left - = `-${interfaceConfig.CANVAS_EXTRA/2}px`; - _resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight); - - videoSpan.appendChild(audioLevelCanvas); - } else { - audioLevelCanvas = audioLevelCanvas.get(0); - - _resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight); + this.sideDotsCount = Math.floor(this._AUDIO_LEVEL_DOTS/2); + + for (let i = 0; i < this._AUDIO_LEVEL_DOTS; i++) { + let audioDot = document.createElement('span'); + + // The median index will be equal to the number of dots on each + // side. + if (i === this.sideDotsCount) + audioDot.className = "audiodot-middle"; + else + audioDot.className = (i < this.sideDotsCount) + ? "audiodot-top" + : "audiodot-bottom"; + + audioSpan.appendChild(audioDot); } + return audioSpan; }, /** * Updates the audio level UI for the given id. * - * @param id id of the user for whom we draw the audio level - * @param audioLevel the newAudio level to render + * @param {string} id id of the user for whom we draw the audio level + * @param {number} audioLevel the newAudio level to render */ - updateAudioLevel (id, audioLevel, largeVideoId) { - drawAudioLevelCanvas(id, audioLevel); + updateThumbnailAudioLevel (id, audioLevel) { - let videoSpanId = getVideoSpanId(id); + // First make sure we are sensitive enough. + audioLevel *= 1.2; + audioLevel = Math.min(audioLevel, 1); - let audioLevelCanvas = $(`#${videoSpanId}>canvas`).get(0); + // Let's now stretch the audio level over the number of dots we have. + let stretchedAudioLevel = (this.sideDotsCount + 1) * audioLevel; + let dotLevel = 0.0; - if (!audioLevelCanvas) { - return; - } + for (let i = 0; i < (this.sideDotsCount + 1); i++) { - let drawContext = audioLevelCanvas.getContext('2d'); - - let canvasCache = audioLevelCanvasCache[id]; - - drawContext.clearRect( - 0, 0, audioLevelCanvas.width, audioLevelCanvas.height - ); - drawContext.drawImage(canvasCache, 0, 0); - - if (id === LOCAL_LEVEL) { - id = APP.conference.getMyUserId(); - if (!id) { - return; - } - } - - if(id === largeVideoId) { - window.requestAnimationFrame(function () { - AudioLevels.updateDominantSpeakerAudioLevel(audioLevel); - }); + dotLevel = Math.min(1, Math.max(0, (stretchedAudioLevel - i))); + this._setDotLevel(id, i, dotLevel); } }, - updateDominantSpeakerAudioLevel (audioLevel) { - if($("#dominantSpeaker").css("visibility") == "hidden" - || ASDrawContext === null) { + /** + * Fills the dot(s) with the specified "index", with as much opacity as + * indicated by "opacity". + * + * @param {string} elementID the parent audio indicator span element + * @param {number} index the index of the dots to fill, where 0 indicates + * the middle dot and the following increments point toward the + * corresponding pair of dots. + * @param {number} opacity the opacity to set for the specified dot. + */ + _setDotLevel(elementID, index, opacity) { + + let audioSpan = document.getElementById(elementID) + .getElementsByClassName("audioindicator"); + + // Make sure the audio span is still around. + if (audioSpan && audioSpan.length > 0) + audioSpan = audioSpan[0]; + else + return; + + let audioTopDots + = audioSpan.getElementsByClassName("audiodot-top"); + let audioDotMiddle + = audioSpan.getElementsByClassName("audiodot-middle"); + let audioBottomDots + = audioSpan.getElementsByClassName("audiodot-bottom"); + + // First take care of the middle dot case. + if (index === 0){ + audioDotMiddle[0].style.opacity = opacity; return; } - ASDrawContext.clearRect(0, 0, - dominantSpeakerAudioElement.width, - dominantSpeakerAudioElement.height); - - if (!audioLevel) { - return; - } - - ASDrawContext.shadowBlur = getShadowLevel(audioLevel); - - // Fill the shape. - ASDrawContext.fill(); + // Index > 0 : we are setting non-middle dots. + index--; + audioBottomDots[index].style.opacity = opacity; + audioTopDots[this.sideDotsCount - index - 1].style.opacity = opacity; }, - updateCanvasSize (localVideo, remoteVideo) { - let { remoteThumbs, localThumb } = FilmStrip.getThumbs(); + /** + * Updates the audio level of the large video. + * + * @param audioLevel the new audio level to set. + */ + updateLargeVideoAudioLevel(elementId, audioLevel) { + let element = document.getElementById(elementId); - remoteThumbs.each(( index, element ) => { - this.createAudioLevelCanvas(element.id, - remoteVideo.thumbWidth, - remoteVideo.thumbHeight); - }); + if(!UIUtil.isVisible(element)) + return; - if (localThumb) { - this.createAudioLevelCanvas(localThumb.get(0).id, - localVideo.thumbWidth, - localVideo.thumbHeight); - } + let level = parseFloat(audioLevel); + + level = isNaN(level) ? 0 : level; + + let shadowElement = element.getElementsByClassName("dynamic-shadow"); + + if (shadowElement && shadowElement.length > 0) + shadowElement = shadowElement[0]; + + shadowElement.style.boxShadow = this._updateLargeVideoShadow(level); + }, + + /** + * Updates the large video shadow effect. + */ + _updateLargeVideoShadow (level) { + var scale = 2, + + // Internal circle audio level. + int = { + level: level > 0.15 ? 20 : 0, + color: interfaceConfig.AUDIO_LEVEL_PRIMARY_COLOR + }, + + // External circle audio level. + ext = { + level: (int.level * scale * level + int.level).toFixed(0), + color: interfaceConfig.AUDIO_LEVEL_SECONDARY_COLOR + }; + + // Internal blur. + int.blur = int.level ? 2 : 0; + + // External blur. + ext.blur = ext.level ? 6 : 0; + + return [ + `0 0 ${ int.blur }px ${ int.level }px ${ int.color }`, + `0 0 ${ ext.blur }px ${ ext.level }px ${ ext.color }` + ].join(', '); } }; diff --git a/modules/UI/audio_levels/CanvasUtils.js b/modules/UI/audio_levels/CanvasUtils.js deleted file mode 100644 index 8f738ca07..000000000 --- a/modules/UI/audio_levels/CanvasUtils.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Utility class for drawing canvas shapes. - */ -const CanvasUtil = { - - /** - * Draws a round rectangle with a glow. The glowWidth indicates the depth - * of the glow. - * - * @param drawContext the context of the canvas to draw to - * @param x the x coordinate of the round rectangle - * @param y the y coordinate of the round rectangle - * @param w the width of the round rectangle - * @param h the height of the round rectangle - * @param glowColor the color of the glow - * @param glowWidth the width of the glow - */ - drawRoundRectGlow (drawContext, x, y, w, h, r, glowColor, glowWidth) { - - // Save the previous state of the context. - drawContext.save(); - - if (w < 2 * r) r = w / 2; - if (h < 2 * r) r = h / 2; - - // Draw a round rectangle. - drawContext.beginPath(); - drawContext.moveTo(x+r, y); - drawContext.arcTo(x+w, y, x+w, y+h, r); - drawContext.arcTo(x+w, y+h, x, y+h, r); - drawContext.arcTo(x, y+h, x, y, r); - drawContext.arcTo(x, y, x+w, y, r); - drawContext.closePath(); - - // Add a shadow around the rectangle - drawContext.shadowColor = glowColor; - drawContext.shadowBlur = glowWidth; - drawContext.shadowOffsetX = 0; - drawContext.shadowOffsetY = 0; - - // Fill the shape. - drawContext.fill(); - - drawContext.save(); - - drawContext.restore(); - -// 1) Uncomment this line to use Composite Operation, which is doing the -// same as the clip function below and is also antialiasing the round -// border, but is said to be less fast performance wise. - -// drawContext.globalCompositeOperation='destination-out'; - - drawContext.beginPath(); - drawContext.moveTo(x+r, y); - drawContext.arcTo(x+w, y, x+w, y+h, r); - drawContext.arcTo(x+w, y+h, x, y+h, r); - drawContext.arcTo(x, y+h, x, y, r); - drawContext.arcTo(x, y, x+w, y, r); - drawContext.closePath(); - -// 2) Uncomment this line to use Composite Operation, which is doing the -// same as the clip function below and is also antialiasing the round -// border, but is said to be less fast performance wise. - -// drawContext.fill(); - - // Comment these two lines if choosing to do the same with composite - // operation above 1 and 2. - drawContext.clip(); - drawContext.clearRect(0, 0, 277, 200); - - // Restore the previous context state. - drawContext.restore(); - }, - - /** - * Clones the given canvas. - * - * @return the new cloned canvas. - */ - cloneCanvas (oldCanvas) { - /* - * FIXME Testing has shown that oldCanvas may not exist. In such a case, - * the method CanvasUtil.cloneCanvas may throw an error. Since audio - * levels are frequently updated, the errors have been observed to pile - * into the console, strain the CPU. - */ - if (!oldCanvas) - return oldCanvas; - - //create a new canvas - var newCanvas = document.createElement('canvas'); - var context = newCanvas.getContext('2d'); - - //set dimensions - newCanvas.width = oldCanvas.width; - newCanvas.height = oldCanvas.height; - - //apply the old canvas to the new one - context.drawImage(oldCanvas, 0, 0); - - //return the new canvas - return newCanvas; - } -}; - -export default CanvasUtil; diff --git a/modules/UI/videolayout/FilmStrip.js b/modules/UI/videolayout/FilmStrip.js index e0aa813c8..10a8366c0 100644 --- a/modules/UI/videolayout/FilmStrip.js +++ b/modules/UI/videolayout/FilmStrip.js @@ -266,7 +266,7 @@ const FilmStrip = { return { remoteThumbs, localThumb }; } - }, + } }; diff --git a/modules/UI/videolayout/LargeVideoManager.js b/modules/UI/videolayout/LargeVideoManager.js index e0a06be33..e699edc41 100644 --- a/modules/UI/videolayout/LargeVideoManager.js +++ b/modules/UI/videolayout/LargeVideoManager.js @@ -5,8 +5,11 @@ import Avatar from "../avatar/Avatar"; import {createDeferred} from '../../util/helpers'; import UIUtil from "../util/UIUtil"; import {VideoContainer, VIDEO_CONTAINER_TYPE} from "./VideoContainer"; + import LargeContainer from "./LargeContainer"; +import AudioLevels from "../audio_levels/AudioLevels"; + /** * Manager for all Large containers. */ @@ -307,6 +310,15 @@ export default class LargeVideoManager { $("#dominantSpeakerAvatar").attr('src', avatarUrl); } + /** + * Updates the audio level indicator of the large video. + * + * @param lvl the new audio level to set + */ + updateLargeVideoAudioLevel (lvl) { + AudioLevels.updateLargeVideoAudioLevel("dominantSpeaker", lvl); + } + /** * Show or hide watermark. * @param {boolean} show diff --git a/modules/UI/videolayout/LocalVideo.js b/modules/UI/videolayout/LocalVideo.js index 1a070652b..ed4d67105 100644 --- a/modules/UI/videolayout/LocalVideo.js +++ b/modules/UI/videolayout/LocalVideo.js @@ -28,6 +28,7 @@ function LocalVideo(VideoLayout, emitter) { this.setDisplayName(); this.createConnectionIndicator(); + this.addAudioLevelIndicator(); } LocalVideo.prototype = Object.create(SmallVideo.prototype); diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index 8da42cf9c..99e29495b 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -63,13 +63,12 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() { let { remoteVideo } = this.VideoLayout.resizeThumbnails(false, true); let { thumbHeight, thumbWidth } = remoteVideo; - AudioLevels.createAudioLevelCanvas( - this.videoSpanId, thumbWidth, thumbHeight); + + this.addAudioLevelIndicator(); return this.container; }; - /** * Initializes the remote participant popup menu, by specifying previously * constructed popupMenuElement, containing all the menu items. diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index 7ff935cc7..cde3ffad5 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -2,6 +2,7 @@ import Avatar from "../avatar/Avatar"; import UIUtil from "../util/UIUtil"; import UIEvents from "../../../service/UI/UIEvents"; +import AudioLevels from "../audio_levels/AudioLevels"; const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper; @@ -266,7 +267,7 @@ SmallVideo.prototype.getAudioMutedIndicator = function () { * @param {boolean} isMuted indicates if we should set the view to muted view * or not */ -SmallVideo.prototype.setMutedView = function(isMuted) { +SmallVideo.prototype.setVideoMutedView = function(isMuted) { this.isVideoMuted = isMuted; this.updateView(); @@ -308,10 +309,11 @@ SmallVideo.prototype.getVideoMutedIndicator = function () { }; /** - * Creates the element indicating the moderator(owner) of the conference. + * Adds the element indicating the moderator(owner) of the conference. */ -SmallVideo.prototype.createModeratorIndicatorElement = function () { - // don't create moderator indicator if DISABLE_FOCUS_INDICATOR is true +SmallVideo.prototype.addModeratorIndicator = function () { + + // Don't create moderator indicator if DISABLE_FOCUS_INDICATOR is true if (interfaceConfig.DISABLE_FOCUS_INDICATOR) return false; @@ -339,10 +341,33 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () { indicatorSpan.appendChild(moderatorIndicator); }; +/** + * Adds the element indicating the audio level of the participant. + */ +SmallVideo.prototype.addAudioLevelIndicator = function () { + var audioSpan = $('#' + this.videoSpanId + ' .audioindicator'); + + if (audioSpan.length) { + return; + } + + this.container.appendChild( + AudioLevels.createThumbnailAudioLevelIndicator()); +}; + +/** + * Updates the audio level for this small video. + * + * @param lvl the new audio level to set + */ +SmallVideo.prototype.updateAudioLevelIndicator = function (lvl) { + AudioLevels.updateThumbnailAudioLevel(this.videoSpanId, lvl); +}; + /** * Removes the element indicating the moderator(owner) of the conference. */ -SmallVideo.prototype.removeModeratorIndicatorElement = function () { +SmallVideo.prototype.removeModeratorIndicator = function () { $('#' + this.videoSpanId + ' .focusindicator').remove(); }; diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index 7b1adfcd6..e98ead8de 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -1,7 +1,6 @@ /* global config, APP, $, interfaceConfig, JitsiMeetJS */ /* jshint -W101 */ -import AudioLevels from "../audio_levels/AudioLevels"; import Avatar from "../avatar/Avatar"; import FilmStrip from "./FilmStrip"; import UIEvents from "../../../service/UI/UIEvents"; @@ -108,10 +107,6 @@ var VideoLayout = { // if we do not resize the thumbs here, if there is no video device // the local video thumb maybe one pixel let { localVideo } = this.resizeThumbnails(false, true); - AudioLevels.createAudioLevelCanvas( - "localVideoContainer", - localVideo.thumbWidth, - localVideo.thumbHeight); emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked); this.lastNCount = config.channelLastN; @@ -123,16 +118,21 @@ var VideoLayout = { largeVideo.onLocalFlipXChange(localFlipX); } largeVideo.updateContainerSize(); - AudioLevels.init(); }, + /** + * 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) { - return; - } - AudioLevels.updateAudioLevel( - id, lvl, largeVideo.id - ); + let smallVideo = this.getSmallVideo(id); + if (smallVideo) + smallVideo.updateAudioLevelIndicator(lvl); + + if (largeVideo && id === largeVideo.id) + largeVideo.updateLargeVideoAudioLevel(lvl); }, isInLastN (resource) { @@ -469,9 +469,9 @@ var VideoLayout = { showModeratorIndicator () { let isModerator = APP.conference.isModerator; if (isModerator) { - localVideoThumbnail.createModeratorIndicatorElement(); + localVideoThumbnail.addModeratorIndicator(); } else { - localVideoThumbnail.removeModeratorIndicatorElement(); + localVideoThumbnail.removeModeratorIndicator(); } APP.conference.listMembers().forEach(function (member) { @@ -481,7 +481,7 @@ var VideoLayout = { return; if (member.isModerator()) { - remoteVideo.createModeratorIndicatorElement(); + remoteVideo.addModeratorIndicator(); } if (isModerator) { @@ -528,7 +528,6 @@ var VideoLayout = { FilmStrip.resizeThumbnails(localVideo, remoteVideo, animate, forceUpdate) .then(function () { - AudioLevels.updateCanvasSize(localVideo, remoteVideo); if (onComplete && typeof onComplete === "function") onComplete(); }); @@ -558,11 +557,11 @@ var VideoLayout = { */ onVideoMute (id, value) { if (APP.conference.isLocalId(id)) { - localVideoThumbnail.setMutedView(value); + localVideoThumbnail.setVideoMutedView(value); } else { let remoteVideo = remoteVideos[id]; if (remoteVideo) - remoteVideo.setMutedView(value); + remoteVideo.setVideoMutedView(value); } if (this.isCurrentlyOnLarge(id)) {