| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 | /* 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;
 |