You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AudioLevels.js 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /* global APP, interfaceConfig, $, Strophe */
  2. /* jshint -W101 */
  3. var CanvasUtil = require("./CanvasUtils");
  4. var ASDrawContext = null;
  5. function initActiveSpeakerAudioLevels() {
  6. var ASRadius = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2;
  7. var ASCenter = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + ASRadius) / 2;
  8. // Draw a circle.
  9. ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI);
  10. // Add a shadow around the circle
  11. ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR;
  12. ASDrawContext.shadowOffsetX = 0;
  13. ASDrawContext.shadowOffsetY = 0;
  14. }
  15. /**
  16. * The audio Levels plugin.
  17. */
  18. var AudioLevels = (function(my) {
  19. var audioLevelCanvasCache = {};
  20. my.LOCAL_LEVEL = 'local';
  21. my.init = function () {
  22. ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d');
  23. initActiveSpeakerAudioLevels();
  24. };
  25. /**
  26. * Updates the audio level canvas for the given peerJid. If the canvas
  27. * didn't exist we create it.
  28. */
  29. my.updateAudioLevelCanvas = function (peerJid, VideoLayout) {
  30. var resourceJid = null;
  31. var videoSpanId = null;
  32. if (!peerJid)
  33. videoSpanId = 'localVideoContainer';
  34. else {
  35. resourceJid = Strophe.getResourceFromJid(peerJid);
  36. videoSpanId = 'participant_' + resourceJid;
  37. }
  38. var videoSpan = document.getElementById(videoSpanId);
  39. if (!videoSpan) {
  40. if (resourceJid)
  41. console.error("No video element for jid", resourceJid);
  42. else
  43. console.error("No video element for local video.");
  44. return;
  45. }
  46. var audioLevelCanvas = $('#' + videoSpanId + '>canvas');
  47. var videoSpaceWidth = $('#remoteVideos').width();
  48. var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
  49. var thumbnailWidth = thumbnailSize[0];
  50. var thumbnailHeight = thumbnailSize[1];
  51. if (!audioLevelCanvas || audioLevelCanvas.length === 0) {
  52. audioLevelCanvas = document.createElement('canvas');
  53. audioLevelCanvas.className = "audiolevel";
  54. audioLevelCanvas.style.bottom = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px";
  55. audioLevelCanvas.style.left = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px";
  56. resizeAudioLevelCanvas( audioLevelCanvas,
  57. thumbnailWidth,
  58. thumbnailHeight);
  59. videoSpan.appendChild(audioLevelCanvas);
  60. } else {
  61. audioLevelCanvas = audioLevelCanvas.get(0);
  62. resizeAudioLevelCanvas( audioLevelCanvas,
  63. thumbnailWidth,
  64. thumbnailHeight);
  65. }
  66. };
  67. /**
  68. * Updates the audio level UI for the given resourceJid.
  69. *
  70. * @param resourceJid the resource jid indicating the video element for
  71. * which we draw the audio level
  72. * @param audioLevel the newAudio level to render
  73. */
  74. my.updateAudioLevel = function (resourceJid, audioLevel, largeVideoResourceJid) {
  75. drawAudioLevelCanvas(resourceJid, audioLevel);
  76. var videoSpanId = getVideoSpanId(resourceJid);
  77. var audioLevelCanvas = $('#' + videoSpanId + '>canvas').get(0);
  78. if (!audioLevelCanvas)
  79. return;
  80. var drawContext = audioLevelCanvas.getContext('2d');
  81. var canvasCache = audioLevelCanvasCache[resourceJid];
  82. drawContext.clearRect (0, 0,
  83. audioLevelCanvas.width, audioLevelCanvas.height);
  84. drawContext.drawImage(canvasCache, 0, 0);
  85. if(resourceJid === AudioLevels.LOCAL_LEVEL) {
  86. if(!APP.xmpp.myJid()) {
  87. return;
  88. }
  89. resourceJid = APP.xmpp.myResource();
  90. }
  91. if(resourceJid === largeVideoResourceJid) {
  92. window.requestAnimationFrame(function () {
  93. AudioLevels.updateActiveSpeakerAudioLevel(audioLevel);
  94. });
  95. }
  96. };
  97. my.updateActiveSpeakerAudioLevel = function(audioLevel) {
  98. if($("#activeSpeaker").css("visibility") == "hidden" || ASDrawContext === null)
  99. return;
  100. ASDrawContext.clearRect(0, 0, 300, 300);
  101. if (!audioLevel)
  102. return;
  103. ASDrawContext.shadowBlur = getShadowLevel(audioLevel);
  104. // Fill the shape.
  105. ASDrawContext.fill();
  106. };
  107. /**
  108. * Resizes the given audio level canvas to match the given thumbnail size.
  109. */
  110. function resizeAudioLevelCanvas(audioLevelCanvas,
  111. thumbnailWidth,
  112. thumbnailHeight) {
  113. audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA;
  114. audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA;
  115. }
  116. /**
  117. * Draws the audio level canvas into the cached canvas object.
  118. *
  119. * @param resourceJid the resource jid indicating the video element for
  120. * which we draw the audio level
  121. * @param audioLevel the newAudio level to render
  122. */
  123. function drawAudioLevelCanvas(resourceJid, audioLevel) {
  124. if (!audioLevelCanvasCache[resourceJid]) {
  125. var videoSpanId = getVideoSpanId(resourceJid);
  126. var audioLevelCanvasOrig = $('#' + videoSpanId + '>canvas').get(0);
  127. /*
  128. * FIXME Testing has shown that audioLevelCanvasOrig may not exist.
  129. * In such a case, the method CanvasUtil.cloneCanvas may throw an
  130. * error. Since audio levels are frequently updated, the errors have
  131. * been observed to pile into the console, strain the CPU.
  132. */
  133. if (audioLevelCanvasOrig) {
  134. audioLevelCanvasCache[resourceJid] =
  135. CanvasUtil.cloneCanvas(audioLevelCanvasOrig);
  136. }
  137. }
  138. var canvas = audioLevelCanvasCache[resourceJid];
  139. if (!canvas)
  140. return;
  141. var drawContext = canvas.getContext('2d');
  142. drawContext.clearRect(0, 0, canvas.width, canvas.height);
  143. var shadowLevel = getShadowLevel(audioLevel);
  144. if (shadowLevel > 0) {
  145. // drawContext, x, y, w, h, r, shadowColor, shadowLevel
  146. CanvasUtil.drawRoundRectGlow(drawContext,
  147. interfaceConfig.CANVAS_EXTRA / 2, interfaceConfig.CANVAS_EXTRA / 2,
  148. canvas.width - interfaceConfig.CANVAS_EXTRA,
  149. canvas.height - interfaceConfig.CANVAS_EXTRA,
  150. interfaceConfig.CANVAS_RADIUS,
  151. interfaceConfig.SHADOW_COLOR,
  152. shadowLevel);
  153. }
  154. }
  155. /**
  156. * Returns the shadow/glow level for the given audio level.
  157. *
  158. * @param audioLevel the audio level from which we determine the shadow
  159. * level
  160. */
  161. function getShadowLevel (audioLevel) {
  162. var shadowLevel = 0;
  163. if (audioLevel <= 0.3) {
  164. shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*(audioLevel/0.3));
  165. }
  166. else if (audioLevel <= 0.6) {
  167. shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3));
  168. }
  169. else {
  170. shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4));
  171. }
  172. return shadowLevel;
  173. }
  174. /**
  175. * Returns the video span id corresponding to the given resourceJid or local
  176. * user.
  177. */
  178. function getVideoSpanId(resourceJid) {
  179. var videoSpanId = null;
  180. if (resourceJid === AudioLevels.LOCAL_LEVEL ||
  181. (APP.xmpp.myResource() && resourceJid === APP.xmpp.myResource()))
  182. videoSpanId = 'localVideoContainer';
  183. else
  184. videoSpanId = 'participant_' + resourceJid;
  185. return videoSpanId;
  186. }
  187. /**
  188. * Indicates that the remote video has been resized.
  189. */
  190. $(document).bind('remotevideo.resized', function (event, width, height) {
  191. var resized = false;
  192. $('#remoteVideos>span>canvas').each(function() {
  193. var canvas = $(this).get(0);
  194. if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) {
  195. canvas.width = width + interfaceConfig.CANVAS_EXTRA;
  196. resized = true;
  197. }
  198. if (canvas.heigh !== height + interfaceConfig.CANVAS_EXTRA) {
  199. canvas.height = height + interfaceConfig.CANVAS_EXTRA;
  200. resized = true;
  201. }
  202. });
  203. if (resized)
  204. Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) {
  205. audioLevelCanvasCache[resourceJid].width =
  206. width + interfaceConfig.CANVAS_EXTRA;
  207. audioLevelCanvasCache[resourceJid].height =
  208. height + interfaceConfig.CANVAS_EXTRA;
  209. });
  210. });
  211. return my;
  212. })(AudioLevels || {});
  213. module.exports = AudioLevels;