From f2a7a43ba7441bde833f2e857a51ffd36bbb5859 Mon Sep 17 00:00:00 2001 From: hristoterezov Date: Wed, 11 Feb 2015 18:29:20 +0200 Subject: [PATCH] Fixes audio level performance issue on avatar. --- index.html | 2 +- libs/app.bundle.js | 98 +++++++++++++++---------- modules/UI/UI.js | 2 +- modules/UI/audio_levels/AudioLevels.js | 57 +++++++------- modules/statistics/RTPStatsCollector.js | 37 ++++++++-- modules/statistics/statistics.js | 2 +- 6 files changed, 121 insertions(+), 77 deletions(-) diff --git a/index.html b/index.html index 15d23727d..995d17b22 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,7 @@ - + diff --git a/libs/app.bundle.js b/libs/app.bundle.js index 8bb67a651..f2f27d49e 100644 --- a/libs/app.bundle.js +++ b/libs/app.bundle.js @@ -1315,7 +1315,7 @@ function registerListeners() { VideoLayout.onSimulcastLayersChanging(endpointSimulcastLayers); }); VideoLayout.init(eventEmitter); - + AudioLevels.init(); APP.statistics.addAudioLevelListener(function(jid, audioLevel) { var resourceJid; @@ -1933,6 +1933,21 @@ module.exports = UI; },{"../../service/RTC/RTCEvents":80,"../../service/RTC/StreamEventTypes":81,"../../service/connectionquality/CQEvents":83,"../../service/desktopsharing/DesktopSharingEventTypes":84,"../../service/xmpp/XMPPEvents":86,"./audio_levels/AudioLevels.js":9,"./authentication/Authentication":11,"./avatar/Avatar":12,"./etherpad/Etherpad.js":13,"./prezi/Prezi.js":14,"./side_pannels/SidePanelToggler":16,"./side_pannels/chat/Chat.js":17,"./side_pannels/contactlist/ContactList":21,"./side_pannels/settings/Settings":22,"./side_pannels/settings/SettingsMenu":23,"./toolbars/BottomToolbar":24,"./toolbars/Toolbar":25,"./toolbars/ToolbarToggler":26,"./util/MessageHandler":28,"./util/NicknameHandler":29,"./util/UIUtil":30,"./videolayout/VideoLayout.js":32,"./welcome_page/RoomnameGenerator":33,"./welcome_page/WelcomePage":34,"events":87}],9:[function(require,module,exports){ var CanvasUtil = require("./CanvasUtils"); +var ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d'); + +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. */ @@ -1941,6 +1956,10 @@ var AudioLevels = (function(my) { my.LOCAL_LEVEL = 'local'; + my.init = function () { + initActiveSpeakerAudioLevels(); + } + /** * Updates the audio level canvas for the given peerJid. If the canvas * didn't exist we create it. @@ -2027,44 +2046,26 @@ var AudioLevels = (function(my) { } if(resourceJid === largeVideoResourceJid) { - AudioLevels.updateActiveSpeakerAudioLevel(audioLevel); + window.requestAnimationFrame(function () { + 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; + if($("#activeSpeaker").css("visibility") == "hidden") + return; - // Save the previous state of the context. - drawContext.save(); - drawContext.clearRect(0, 0, 300, 300); + ASDrawContext.clearRect(0, 0, 300, 300); + if(audioLevel == 0) + return; - // Draw a circle. - drawContext.arc(center, center, r, 0, 2 * Math.PI); + ASDrawContext.shadowBlur = getShadowLevel(audioLevel); - // 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(); + ASDrawContext.fill(); }; /** @@ -10604,9 +10605,13 @@ PeerStats.prototype.setSsrcBitrate = function (ssrc, bitrate) PeerStats.prototype.setSsrcAudioLevel = function (ssrc, audioLevel) { // Range limit 0 - 1 - this.ssrc2AudioLevel[ssrc] = Math.min(Math.max(audioLevel, 0), 1); + this.ssrc2AudioLevel[ssrc] = formatAudioLevel(audioLevel); }; +function formatAudioLevel(audioLevel) { + return Math.min(Math.max(audioLevel, 0), 1); +} + /** * Array with the transport information. * @type {Array} @@ -10681,16 +10686,26 @@ module.exports = StatsCollector; /** * Stops stats updates. */ -StatsCollector.prototype.stop = function () -{ - if (this.audioLevelsIntervalId) - { +StatsCollector.prototype.stop = function () { + if (this.audioLevelsIntervalId) { clearInterval(this.audioLevelsIntervalId); this.audioLevelsIntervalId = null; + } + + if (this.statsIntervalId) + { clearInterval(this.statsIntervalId); this.statsIntervalId = null; + } + + if(this.logStatsIntervalId) + { clearInterval(this.logStatsIntervalId); this.logStatsIntervalId = null; + } + + if(this.gatherStatsIntervalId) + { clearInterval(this.gatherStatsIntervalId); this.gatherStatsIntervalId = null; } @@ -10713,6 +10728,7 @@ StatsCollector.prototype.start = function () { var self = this; if(!config.disableAudioLevels) { + console.debug("set audio levels interval"); this.audioLevelsIntervalId = setInterval( function () { // Interval updates @@ -10740,6 +10756,7 @@ StatsCollector.prototype.start = function () } if(!config.disableStats) { + console.debug("set stats interval"); this.statsIntervalId = setInterval( function () { // Interval updates @@ -11170,10 +11187,15 @@ StatsCollector.prototype.processAudioLevelReport = function () { // TODO: can't find specs about what this value really is, // but it seems to vary between 0 and around 32k. - audioLevel = audioLevel / 32767; - jidStats.setSsrcAudioLevel(ssrc, audioLevel); - if(jid != APP.xmpp.myJid()) + audioLevel = formatAudioLevel(audioLevel / 32767); + var oldLevel = jidStats.ssrc2AudioLevel[ssrc]; + if(jid != APP.xmpp.myJid() && (!oldLevel || oldLevel != audioLevel)) + { + + jidStats.ssrc2AudioLevel[ssrc] = audioLevel; + this.eventEmitter.emit("statistics.audioLevel", jid, audioLevel); + } } } @@ -11231,7 +11253,7 @@ function onStreamCreated(stream) if(stream.getOriginalStream().getAudioTracks().length === 0) return; - localStats = new LocalStats(stream.getOriginalStream(), 100, statistics, + localStats = new LocalStats(stream.getOriginalStream(), 200, statistics, eventEmitter); localStats.start(); } diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 807ca8042..2fc47dd54 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -103,7 +103,7 @@ function registerListeners() { VideoLayout.onSimulcastLayersChanging(endpointSimulcastLayers); }); VideoLayout.init(eventEmitter); - + AudioLevels.init(); APP.statistics.addAudioLevelListener(function(jid, audioLevel) { var resourceJid; diff --git a/modules/UI/audio_levels/AudioLevels.js b/modules/UI/audio_levels/AudioLevels.js index bbd60cf44..9bc9b264d 100644 --- a/modules/UI/audio_levels/AudioLevels.js +++ b/modules/UI/audio_levels/AudioLevels.js @@ -1,5 +1,20 @@ var CanvasUtil = require("./CanvasUtils"); +var ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d'); + +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. */ @@ -8,6 +23,10 @@ var AudioLevels = (function(my) { my.LOCAL_LEVEL = 'local'; + my.init = function () { + initActiveSpeakerAudioLevels(); + } + /** * Updates the audio level canvas for the given peerJid. If the canvas * didn't exist we create it. @@ -94,44 +113,26 @@ var AudioLevels = (function(my) { } if(resourceJid === largeVideoResourceJid) { - AudioLevels.updateActiveSpeakerAudioLevel(audioLevel); + window.requestAnimationFrame(function () { + 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; + if($("#activeSpeaker").css("visibility") == "hidden") + return; - // Save the previous state of the context. - drawContext.save(); - drawContext.clearRect(0, 0, 300, 300); + ASDrawContext.clearRect(0, 0, 300, 300); + if(audioLevel == 0) + return; - // Draw a circle. - drawContext.arc(center, center, r, 0, 2 * Math.PI); + ASDrawContext.shadowBlur = getShadowLevel(audioLevel); - // 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(); + ASDrawContext.fill(); }; /** diff --git a/modules/statistics/RTPStatsCollector.js b/modules/statistics/RTPStatsCollector.js index 3475535fc..430f178b9 100644 --- a/modules/statistics/RTPStatsCollector.js +++ b/modules/statistics/RTPStatsCollector.js @@ -111,9 +111,13 @@ PeerStats.prototype.setSsrcBitrate = function (ssrc, bitrate) PeerStats.prototype.setSsrcAudioLevel = function (ssrc, audioLevel) { // Range limit 0 - 1 - this.ssrc2AudioLevel[ssrc] = Math.min(Math.max(audioLevel, 0), 1); + this.ssrc2AudioLevel[ssrc] = formatAudioLevel(audioLevel); }; +function formatAudioLevel(audioLevel) { + return Math.min(Math.max(audioLevel, 0), 1); +} + /** * Array with the transport information. * @type {Array} @@ -188,16 +192,26 @@ module.exports = StatsCollector; /** * Stops stats updates. */ -StatsCollector.prototype.stop = function () -{ - if (this.audioLevelsIntervalId) - { +StatsCollector.prototype.stop = function () { + if (this.audioLevelsIntervalId) { clearInterval(this.audioLevelsIntervalId); this.audioLevelsIntervalId = null; + } + + if (this.statsIntervalId) + { clearInterval(this.statsIntervalId); this.statsIntervalId = null; + } + + if(this.logStatsIntervalId) + { clearInterval(this.logStatsIntervalId); this.logStatsIntervalId = null; + } + + if(this.gatherStatsIntervalId) + { clearInterval(this.gatherStatsIntervalId); this.gatherStatsIntervalId = null; } @@ -220,6 +234,7 @@ StatsCollector.prototype.start = function () { var self = this; if(!config.disableAudioLevels) { + console.debug("set audio levels interval"); this.audioLevelsIntervalId = setInterval( function () { // Interval updates @@ -247,6 +262,7 @@ StatsCollector.prototype.start = function () } if(!config.disableStats) { + console.debug("set stats interval"); this.statsIntervalId = setInterval( function () { // Interval updates @@ -677,10 +693,15 @@ StatsCollector.prototype.processAudioLevelReport = function () { // TODO: can't find specs about what this value really is, // but it seems to vary between 0 and around 32k. - audioLevel = audioLevel / 32767; - jidStats.setSsrcAudioLevel(ssrc, audioLevel); - if(jid != APP.xmpp.myJid()) + audioLevel = formatAudioLevel(audioLevel / 32767); + var oldLevel = jidStats.ssrc2AudioLevel[ssrc]; + if(jid != APP.xmpp.myJid() && (!oldLevel || oldLevel != audioLevel)) + { + + jidStats.ssrc2AudioLevel[ssrc] = audioLevel; + this.eventEmitter.emit("statistics.audioLevel", jid, audioLevel); + } } } diff --git a/modules/statistics/statistics.js b/modules/statistics/statistics.js index d240e5dec..1031ec5ed 100644 --- a/modules/statistics/statistics.js +++ b/modules/statistics/statistics.js @@ -48,7 +48,7 @@ function onStreamCreated(stream) if(stream.getOriginalStream().getAudioTracks().length === 0) return; - localStats = new LocalStats(stream.getOriginalStream(), 100, statistics, + localStats = new LocalStats(stream.getOriginalStream(), 200, statistics, eventEmitter); localStats.start(); }