mirror of https://github.com/jitsi/jitsi-meet
parent
304cdf5b40
commit
50f1521d5c
@ -0,0 +1,193 @@ |
|||||||
|
/** |
||||||
|
* The audio Levels plugin. |
||||||
|
*/ |
||||||
|
var AudioLevels = (function(my) { |
||||||
|
var CANVAS_EXTRA = 104; |
||||||
|
var CANVAS_RADIUS = 7; |
||||||
|
var SHADOW_COLOR = '#00ccff'; |
||||||
|
var audioLevelCanvasCache = {}; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
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 = "-" + CANVAS_EXTRA/2 + "px"; |
||||||
|
audioLevelCanvas.style.left = "-" + 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 = null; |
||||||
|
if (resourceJid |
||||||
|
=== Strophe.getResourceFromJid(connection.emuc.myroomjid)) |
||||||
|
videoSpanId = 'localVideoContainer'; |
||||||
|
else |
||||||
|
videoSpanId = 'participant_' + 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); |
||||||
|
}; |
||||||
|
|
||||||
|
function resizeAudioLevelCanvas(audioLevelCanvas, |
||||||
|
thumbnailWidth, |
||||||
|
thumbnailHeight) { |
||||||
|
audioLevelCanvas.width = thumbnailWidth + CANVAS_EXTRA; |
||||||
|
audioLevelCanvas.height = thumbnailHeight + 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 = null; |
||||||
|
if (resourceJid |
||||||
|
=== Strophe.getResourceFromJid(connection.emuc.myroomjid)) |
||||||
|
videoSpanId = 'localVideoContainer'; |
||||||
|
else |
||||||
|
videoSpanId = 'participant_' + resourceJid; |
||||||
|
|
||||||
|
var audioLevelCanvasOrig = $('#' + videoSpanId + '>canvas').get(0); |
||||||
|
|
||||||
|
audioLevelCanvasCache[resourceJid] |
||||||
|
= CanvasUtil.cloneCanvas(audioLevelCanvasOrig); |
||||||
|
} |
||||||
|
|
||||||
|
var canvas = audioLevelCanvasCache[resourceJid]; |
||||||
|
|
||||||
|
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, |
||||||
|
CANVAS_EXTRA/2, CANVAS_EXTRA/2, |
||||||
|
canvas.width - CANVAS_EXTRA, |
||||||
|
canvas.height - CANVAS_EXTRA, |
||||||
|
CANVAS_RADIUS, |
||||||
|
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(CANVAS_EXTRA/2*(audioLevel/0.3)); |
||||||
|
} |
||||||
|
else if (audioLevel <= 0.6) { |
||||||
|
shadowLevel = Math.round(CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3)); |
||||||
|
} |
||||||
|
else { |
||||||
|
shadowLevel = Math.round(CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4)); |
||||||
|
} |
||||||
|
return shadowLevel; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 + CANVAS_EXTRA) { |
||||||
|
canvas.width = width + CANVAS_EXTRA; |
||||||
|
resized = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (canvas.heigh !== height + CANVAS_EXTRA) { |
||||||
|
canvas.height = height + CANVAS_EXTRA; |
||||||
|
resized = true; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
if (resized) |
||||||
|
Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) { |
||||||
|
audioLevelCanvasCache[resourceJid].width |
||||||
|
= width + CANVAS_EXTRA; |
||||||
|
audioLevelCanvasCache[resourceJid].height |
||||||
|
= height + CANVAS_EXTRA; |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
return my; |
||||||
|
|
||||||
|
})(AudioLevels || {}); |
@ -0,0 +1,101 @@ |
|||||||
|
/** |
||||||
|
* Utility class for drawing canvas shapes. |
||||||
|
*/ |
||||||
|
var CanvasUtil = (function(my) { |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
my.drawRoundRectGlow |
||||||
|
= function(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. |
||||||
|
*/ |
||||||
|
my.cloneCanvas = function (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; |
||||||
|
}; |
||||||
|
|
||||||
|
return my; |
||||||
|
})(CanvasUtil || {}); |
Loading…
Reference in new issue