265 lines
8.6 KiB
JavaScript
265 lines
8.6 KiB
JavaScript
/* global APP, interfaceConfig, $, Strophe */
|
|
var CanvasUtil = require("./CanvasUtils");
|
|
|
|
var ASDrawContext = null;
|
|
|
|
function initActiveSpeakerAudioLevels() {
|
|
var ASRadius = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2;
|
|
var ASCenter = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + ASRadius) / 2;
|
|
|
|
// Draw a circle.
|
|
ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI);
|
|
|
|
// Add a shadow around the circle
|
|
ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR;
|
|
ASDrawContext.shadowOffsetX = 0;
|
|
ASDrawContext.shadowOffsetY = 0;
|
|
}
|
|
|
|
/**
|
|
* The audio Levels plugin.
|
|
*/
|
|
var AudioLevels = (function(my) {
|
|
var audioLevelCanvasCache = {};
|
|
|
|
my.LOCAL_LEVEL = 'local';
|
|
|
|
my.init = function () {
|
|
ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d');
|
|
initActiveSpeakerAudioLevels();
|
|
};
|
|
|
|
/**
|
|
* Updates the audio level canvas for the given peerJid. If the canvas
|
|
* didn't exist we create it.
|
|
*/
|
|
my.updateAudioLevelCanvas = function (peerJid, VideoLayout) {
|
|
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, largeVideoResourceJid) {
|
|
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(!APP.xmpp.myJid()) {
|
|
return;
|
|
}
|
|
resourceJid = APP.xmpp.myResource();
|
|
}
|
|
|
|
if(resourceJid === largeVideoResourceJid) {
|
|
window.requestAnimationFrame(function () {
|
|
AudioLevels.updateActiveSpeakerAudioLevel(audioLevel);
|
|
});
|
|
}
|
|
};
|
|
|
|
my.updateActiveSpeakerAudioLevel = function(audioLevel) {
|
|
if($("#activeSpeaker").css("visibility") == "hidden" || ASDrawContext === null)
|
|
return;
|
|
|
|
ASDrawContext.clearRect(0, 0, 300, 300);
|
|
if(audioLevel == 0)
|
|
return;
|
|
|
|
ASDrawContext.shadowBlur = getShadowLevel(audioLevel);
|
|
|
|
|
|
// Fill the shape.
|
|
ASDrawContext.fill();
|
|
};
|
|
|
|
/**
|
|
* 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 ||
|
|
(APP.xmpp.myResource() && resourceJid === APP.xmpp.myResource()))
|
|
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 || {});
|
|
|
|
module.exports = AudioLevels; |