/** * The audio Levels plugin. */ var AudioLevels = (function(my) { var audioLevelCanvasCache = {}; my.LOCAL_LEVEL = 'local'; /** * Updates the audio level canvas for the given peerJid. If the canvas * didn't exist we create it. */ my.updateAudioLevelCanvas = function (peerJid) { var resourceJid = null; var videoSpanId = null; if (!peerJid) videoSpanId = 'localVideoContainer'; else { resourceJid = Strophe.getResourceFromJid(peerJid); videoSpanId = 'participant_' + resourceJid; } var videoSpan = document.getElementById(videoSpanId); if (!videoSpan) { if (resourceJid) console.error("No video element for jid", resourceJid); else console.error("No video element for local video."); return; } var audioLevelCanvas = $('#' + videoSpanId + '>canvas'); var videoSpaceWidth = $('#remoteVideos').width(); var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth); var thumbnailWidth = thumbnailSize[0]; var thumbnailHeight = thumbnailSize[1]; 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, thumbnailWidth, thumbnailHeight); videoSpan.appendChild(audioLevelCanvas); } else { audioLevelCanvas = audioLevelCanvas.get(0); resizeAudioLevelCanvas( audioLevelCanvas, thumbnailWidth, thumbnailHeight); } }; /** * Updates the audio level UI for the given resourceJid. * * @param resourceJid the resource jid indicating the video element for * which we draw the audio level * @param audioLevel the newAudio level to render */ my.updateAudioLevel = function (resourceJid, audioLevel) { drawAudioLevelCanvas(resourceJid, audioLevel); var videoSpanId = getVideoSpanId(resourceJid); var audioLevelCanvas = $('#' + videoSpanId + '>canvas').get(0); if (!audioLevelCanvas) return; var drawContext = audioLevelCanvas.getContext('2d'); var canvasCache = audioLevelCanvasCache[resourceJid]; drawContext.clearRect (0, 0, audioLevelCanvas.width, audioLevelCanvas.height); drawContext.drawImage(canvasCache, 0, 0); if(resourceJid === AudioLevels.LOCAL_LEVEL) { if(!connection.emuc.myroomjid) { return; } resourceJid = Strophe.getResourceFromJid(connection.emuc.myroomjid); } if(resourceJid === VideoLayout.getLargeVideoState().userResourceJid) { AudioLevels.updateActiveSpeakerAudioLevel(audioLevel); } }; my.updateActiveSpeakerAudioLevel = function(audioLevel) { var drawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d'); var r = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2; var center = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + r) / 2; // Save the previous state of the context. drawContext.save(); drawContext.clearRect(0, 0, 300, 300); // Draw a circle. drawContext.arc(center, center, r, 0, 2 * Math.PI); // Add a shadow around the circle drawContext.shadowColor = interfaceConfig.SHADOW_COLOR; drawContext.shadowBlur = getShadowLevel(audioLevel); drawContext.shadowOffsetX = 0; drawContext.shadowOffsetY = 0; // Fill the shape. drawContext.fill(); drawContext.save(); drawContext.restore(); drawContext.arc(center, center, r, 0, 2 * Math.PI); drawContext.clip(); drawContext.clearRect(0, 0, 277, 200); // Restore the previous context state. drawContext.restore(); }; /** * 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 resourceJid the resource jid indicating the video element for * which we draw the audio level * @param audioLevel the newAudio level to render */ function drawAudioLevelCanvas(resourceJid, audioLevel) { if (!audioLevelCanvasCache[resourceJid]) { var videoSpanId = getVideoSpanId(resourceJid); var 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[resourceJid] = CanvasUtil.cloneCanvas(audioLevelCanvasOrig); } } var canvas = audioLevelCanvasCache[resourceJid]; if (!canvas) return; var drawContext = canvas.getContext('2d'); drawContext.clearRect(0, 0, canvas.width, canvas.height); var 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) { var 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 resourceJid or local * user. */ function getVideoSpanId(resourceJid) { var videoSpanId = null; if (resourceJid === AudioLevels.LOCAL_LEVEL || (connection.emuc.myroomjid && resourceJid === Strophe.getResourceFromJid(connection.emuc.myroomjid))) videoSpanId = 'localVideoContainer'; else videoSpanId = 'participant_' + resourceJid; return videoSpanId; } /** * Indicates that the remote video has been resized. */ $(document).bind('remotevideo.resized', function (event, width, height) { var resized = false; $('#remoteVideos>span>canvas').each(function() { var canvas = $(this).get(0); if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) { canvas.width = width + interfaceConfig.CANVAS_EXTRA; resized = true; } if (canvas.heigh !== height + interfaceConfig.CANVAS_EXTRA) { canvas.height = height + interfaceConfig.CANVAS_EXTRA; resized = true; } }); if (resized) Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) { audioLevelCanvasCache[resourceJid].width = width + interfaceConfig.CANVAS_EXTRA; audioLevelCanvasCache[resourceJid].height = height + interfaceConfig.CANVAS_EXTRA; }); }); return my; })(AudioLevels || {});