ソースを参照

Audio levels redesign.

j8
yanas 8年前
コミット
3bb877cc3a

+ 2
- 0
css/_variables.scss ファイルの表示

@@ -46,6 +46,8 @@ $participantNameColor: #fff;
46 46
 $thumbnailPictogramColor: #fff;
47 47
 $dominantSpeakerBg: #165ecc;
48 48
 $raiseHandBg: #D6D61E;
49
+$audioLevelBg: #44A5FF;
50
+$audioLevelBorder: rgba(14, 56, 121, .5);
49 51
 
50 52
 $rateStarDefault: #ccc;
51 53
 $rateStarActivity: #165ecc;

+ 44
- 10
css/_videolayout_default.scss ファイルの表示

@@ -363,6 +363,36 @@
363 363
     background: $raiseHandBg;
364 364
 }
365 365
 
366
+/**
367
+ * Audio indicator on video thumbnails.
368
+ */
369
+.videocontainer>span.audioindicator {
370
+    position: absolute;
371
+    display: inline-block;
372
+    left: 6px;
373
+    top: 50%;
374
+    margin-top: -21px;
375
+    width: 6px;
376
+    height: 42px;
377
+    z-index: 2;
378
+    border: none;
379
+
380
+    .audiodot-top,
381
+    .audiodot-bottom {
382
+        opacity: 0;
383
+        display: inline-block;
384
+        @include circle(4px);
385
+        background: $audioLevelBg;
386
+        margin: 1px 0 1px 0;
387
+        -webkit-filter: blur(0.5px);
388
+        filter: blur(0.5px);
389
+        border: 1px solid rgba(0, 0, 0, .5);
390
+        transition: opacity .25s ease-in-out;
391
+        -moz-transition: opacity .25s ease-in-out;
392
+        z-index: 2;
393
+    }
394
+}
395
+
366 396
 #indicatoricon {
367 397
     width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
368 398
     height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
@@ -399,25 +429,20 @@
399 429
     width: 300px;
400 430
     height: 300px;
401 431
     margin: auto;
402
-    overflow: hidden;
403 432
     position: relative;
404 433
 }
405 434
 
406
-#dominantSpeakerAudioLevel {
407
-    position: absolute;
408
-    top: 0px;
409
-    left: 0px;
410
-    z-index: 2;
411
-    visibility: inherit;
412
-}
413
-
414 435
 #mixedstream {
415 436
     display:none !important;
416 437
 }
417 438
 
418
-#dominantSpeakerAvatar {
439
+#dominantSpeakerAvatar,
440
+.dynamic-shadow {
419 441
     width: 200px;
420 442
     height: 200px;
443
+}
444
+
445
+#dominantSpeakerAvatar {
421 446
     top: 50px;
422 447
     margin: auto;
423 448
     position: relative;
@@ -427,6 +452,15 @@
427 452
     background-color: #000000;
428 453
 }
429 454
 
455
+.dynamic-shadow {
456
+    border-radius: 50%;
457
+    position: absolute;
458
+    top: 50%;
459
+    left: 50%;
460
+    margin: -100px 0 0 -100px;
461
+    transition: box-shadow 0.3s ease;
462
+}
463
+
430 464
 .userAvatar {
431 465
     @include circle(60px);
432 466
     @include absoluteAligning(60px, 60px);

+ 1
- 1
index.html ファイルの表示

@@ -225,8 +225,8 @@
225 225
                     <span data-i18n="poweredby"></span> jitsi.org
226 226
                 </a>
227 227
                 <div id="dominantSpeaker">
228
+                    <div class="dynamic-shadow"></div>
228 229
                     <img id="dominantSpeakerAvatar" src=""/>
229
-                    <canvas id="dominantSpeakerAudioLevel"></canvas>
230 230
                 </div>
231 231
                 <span id="remoteConnectionMessage"></span>
232 232
                 <div id="largeVideoWrapper">

+ 4
- 2
interface_config.js ファイルの表示

@@ -41,5 +41,7 @@ var interfaceConfig = {
41 41
     REMOTE_THUMBNAIL_RATIO_HEIGHT: 1,
42 42
     // Enables feedback star animation.
43 43
     ENABLE_FEEDBACK_ANIMATION: false,
44
-    DISABLE_FOCUS_INDICATOR: false
45
-};
44
+    DISABLE_FOCUS_INDICATOR: false,
45
+    AUDIO_LEVEL_PRIMARY_COLOR: "rgba(255,255,255,0.7)",
46
+    AUDIO_LEVEL_SECONDARY_COLOR: "rgba(255,255,255,0.4)"
47
+};

+ 93
- 221
modules/UI/audio_levels/AudioLevels.js ファイルの表示

@@ -1,183 +1,36 @@
1
-/* global APP, interfaceConfig, $ */
2
-/* jshint -W101 */
3
-
4
-import CanvasUtil from './CanvasUtils';
5
-import FilmStrip from '../videolayout/FilmStrip';
6
-
7
-const LOCAL_LEVEL = 'local';
8
-
9
-let ASDrawContext = null;
10
-let audioLevelCanvasCache = {};
11
-let dominantSpeakerAudioElement = null;
12
-
13
-function _initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) {
14
-    let ASRadius = dominantSpeakerAvatarSize / 2;
15
-    let ASCenter = (dominantSpeakerAvatarSize + ASRadius) / 2;
16
-
17
-    // Draw a circle.
18
-    ASDrawContext.beginPath();
19
-    ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI);
20
-    ASDrawContext.closePath();
21
-
22
-    // Add a shadow around the circle
23
-    ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR;
24
-    ASDrawContext.shadowOffsetX = 0;
25
-    ASDrawContext.shadowOffsetY = 0;
26
-}
27
-
28
-/**
29
- * Resizes the given audio level canvas to match the given thumbnail size.
30
- */
31
-function _resizeAudioLevelCanvas(   audioLevelCanvas,
32
-                                    thumbnailWidth,
33
-                                    thumbnailHeight) {
34
-    audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA;
35
-    audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA;
36
-}
1
+/* global interfaceConfig */
37 2
 
3
+import UIUtil from "../util/UIUtil";
38 4
 /**
39
- * Draws the audio level canvas into the cached canvas object.
40
- *
41
- * @param id of the user for whom we draw the audio level
42
- * @param audioLevel the newAudio level to render
43
- */
44
-function drawAudioLevelCanvas(id, audioLevel) {
45
-    if (!audioLevelCanvasCache[id]) {
46
-
47
-        let videoSpanId = getVideoSpanId(id);
48
-
49
-        let audioLevelCanvasOrig = $(`#${videoSpanId}>canvas`).get(0);
50
-
51
-        /*
52
-         * FIXME Testing has shown that audioLevelCanvasOrig may not exist.
53
-         * In such a case, the method CanvasUtil.cloneCanvas may throw an
54
-         * error. Since audio levels are frequently updated, the errors have
55
-         * been observed to pile into the console, strain the CPU.
56
-         */
57
-        if (audioLevelCanvasOrig) {
58
-            audioLevelCanvasCache[id]
59
-                = CanvasUtil.cloneCanvas(audioLevelCanvasOrig);
60
-        }
61
-    }
62
-
63
-    let canvas = audioLevelCanvasCache[id];
64
-
65
-    if (!canvas) {
66
-        return;
67
-    }
68
-
69
-    let drawContext = canvas.getContext('2d');
70
-
71
-    drawContext.clearRect(0, 0, canvas.width, canvas.height);
72
-
73
-    let shadowLevel = getShadowLevel(audioLevel);
74
-
75
-    if (shadowLevel > 0) {
76
-        // drawContext, x, y, w, h, r, shadowColor, shadowLevel
77
-        CanvasUtil.drawRoundRectGlow(
78
-            drawContext,
79
-            interfaceConfig.CANVAS_EXTRA / 2, interfaceConfig.CANVAS_EXTRA / 2,
80
-            canvas.width - interfaceConfig.CANVAS_EXTRA,
81
-            canvas.height - interfaceConfig.CANVAS_EXTRA,
82
-            interfaceConfig.CANVAS_RADIUS,
83
-            interfaceConfig.SHADOW_COLOR,
84
-            shadowLevel);
85
-    }
86
-}
87
-
88
-/**
89
- * Returns the shadow/glow level for the given audio level.
90
- *
91
- * @param audioLevel the audio level from which we determine the shadow
92
- * level
93
- */
94
-function getShadowLevel (audioLevel) {
95
-    let shadowLevel = 0;
96
-
97
-    if (audioLevel <= 0.3) {
98
-        shadowLevel = Math.round(
99
-            interfaceConfig.CANVAS_EXTRA/2*(audioLevel/0.3));
100
-    } else if (audioLevel <= 0.6) {
101
-        shadowLevel = Math.round(
102
-            interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3));
103
-    } else {
104
-        shadowLevel = Math.round(
105
-            interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4));
106
-    }
107
-
108
-    return shadowLevel;
109
-}
110
-
111
-/**
112
- * Returns the video span id corresponding to the given user id
113
- */
114
-function getVideoSpanId(id) {
115
-    let videoSpanId = null;
116
-
117
-    if (id === LOCAL_LEVEL || APP.conference.isLocalId(id)) {
118
-        videoSpanId = 'localVideoContainer';
119
-    } else {
120
-        videoSpanId = `participant_${id}`;
121
-    }
122
-
123
-    return videoSpanId;
124
-}
125
-
126
-/**
127
- * The audio Levels plugin.
5
+ * Responsible for drawing audio levels.
128 6
  */
129 7
 const AudioLevels = {
130 8
 
131
-    init () {
132
-        dominantSpeakerAudioElement =  $('#dominantSpeakerAudioLevel')[0];
133
-        ASDrawContext = dominantSpeakerAudioElement.getContext('2d');
134
-
135
-        let parentContainer = $("#dominantSpeaker");
136
-        let dominantSpeakerWidth = parentContainer.width();
137
-        let dominantSpeakerHeight = parentContainer.height();
138
-
139
-        dominantSpeakerAudioElement.width = dominantSpeakerWidth;
140
-        dominantSpeakerAudioElement.height = dominantSpeakerHeight;
141
-
142
-        let dominantSpeakerAvatar = $("#dominantSpeakerAvatar");
143
-        _initDominantSpeakerAudioLevels(dominantSpeakerAvatar.width());
144
-    },
145
-
146 9
     /**
147
-     * Updates the audio level canvas for the given id. If the canvas
148
-     * didn't exist we create it.
10
+     * The number of dots per class. We have 2 classes of dots "top" and
11
+     * "bottom". The total number of dots will be twice the below value.
149 12
      */
150
-    createAudioLevelCanvas (videoSpanId, thumbWidth, thumbHeight) {
13
+    _AUDIO_LEVEL_DOTS: 3,
151 14
 
152
-        let videoSpan = document.getElementById(videoSpanId);
153
-
154
-        if (!videoSpan) {
155
-            if (videoSpanId) {
156
-                console.error("No video element for id", videoSpanId);
157
-            } else {
158
-                console.error("No video element for local video.");
159
-            }
160
-            return;
161
-        }
162
-
163
-        let audioLevelCanvas = $(`#${videoSpanId}>canvas`);
164
-
165
-        if (!audioLevelCanvas || audioLevelCanvas.length === 0) {
15
+    /**
16
+     * Creates the audio level indicator span element.
17
+     *
18
+     * @return {Element} the document element representing audio levels
19
+     */
20
+    createThumbnailAudioLevelIndicator() {
166 21
 
167
-            audioLevelCanvas = document.createElement('canvas');
168
-            audioLevelCanvas.className = "audiolevel";
169
-            audioLevelCanvas.style.bottom
170
-                = `-${interfaceConfig.CANVAS_EXTRA/2}px`;
171
-            audioLevelCanvas.style.left
172
-                = `-${interfaceConfig.CANVAS_EXTRA/2}px`;
173
-            _resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight);
22
+        let audioSpan = document.createElement('span');
23
+        audioSpan.className = 'audioindicator';
174 24
 
175
-            videoSpan.appendChild(audioLevelCanvas);
176
-        } else {
177
-            audioLevelCanvas = audioLevelCanvas.get(0);
25
+        for (let i = 0; i < this._AUDIO_LEVEL_DOTS*2; i++) {
26
+            var audioDot = document.createElement('span');
27
+            audioDot.className = (i < this._AUDIO_LEVEL_DOTS)
28
+                                    ? "audiodot-top"
29
+                                    : "audiodot-bottom";
178 30
 
179
-            _resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight);
31
+            audioSpan.appendChild(audioDot);
180 32
         }
33
+        return audioSpan;
181 34
     },
182 35
 
183 36
     /**
@@ -186,74 +39,93 @@ const AudioLevels = {
186 39
      * @param id id of the user for whom we draw the audio level
187 40
      * @param audioLevel the newAudio level to render
188 41
      */
189
-    updateAudioLevel (id, audioLevel, largeVideoId) {
190
-        drawAudioLevelCanvas(id, audioLevel);
191
-
192
-        let videoSpanId = getVideoSpanId(id);
193
-
194
-        let audioLevelCanvas = $(`#${videoSpanId}>canvas`).get(0);
42
+    updateThumbnailAudioLevel (id, audioLevel) {
43
+        let audioSpan = document.getElementById(id)
44
+                            .getElementsByClassName("audioindicator");
195 45
 
196
-        if (!audioLevelCanvas) {
46
+        if (audioSpan && audioSpan.length > 0)
47
+            audioSpan = audioSpan[0];
48
+        else
197 49
             return;
198
-        }
199
-
200
-        let drawContext = audioLevelCanvas.getContext('2d');
201
-
202
-        let canvasCache = audioLevelCanvasCache[id];
203 50
 
204
-        drawContext.clearRect(
205
-            0, 0, audioLevelCanvas.width, audioLevelCanvas.height
206
-        );
207
-        drawContext.drawImage(canvasCache, 0, 0);
208
-
209
-        if (id === LOCAL_LEVEL) {
210
-            id = APP.conference.getMyUserId();
211
-            if (!id) {
212
-                return;
213
-            }
51
+        let audioTopDots
52
+            = audioSpan.getElementsByClassName("audiodot-top");
53
+        let audioBottomDots
54
+            = audioSpan.getElementsByClassName("audiodot-bottom");
55
+
56
+        let coloredDots = Math.round(this._AUDIO_LEVEL_DOTS*audioLevel);
57
+        let topColoredDots = this._AUDIO_LEVEL_DOTS - coloredDots;
58
+
59
+        for (let i = 0; i < audioTopDots.length; i++) {
60
+            if (i < topColoredDots)
61
+                audioTopDots[i].style.opacity = 0;
62
+            else if (i === topColoredDots && topColoredDots > 0)
63
+                audioTopDots[i].style.opacity = 0.5;
64
+            else
65
+                audioTopDots[i].style.opacity = 1;
214 66
         }
215 67
 
216
-        if(id === largeVideoId) {
217
-            window.requestAnimationFrame(function () {
218
-                AudioLevels.updateDominantSpeakerAudioLevel(audioLevel);
219
-            });
68
+        for (let i = 0; i < audioBottomDots.length; i++) {
69
+            if (i < coloredDots)
70
+                audioBottomDots[i].style.opacity = 1;
71
+            else if (i === coloredDots && coloredDots > 0)
72
+                audioBottomDots[i].style.opacity = 0.5;
73
+            else
74
+                audioBottomDots[i].style.opacity = 0;
220 75
         }
221 76
     },
222 77
 
223
-    updateDominantSpeakerAudioLevel (audioLevel) {
224
-        if($("#dominantSpeaker").css("visibility") == "hidden"
225
-            || ASDrawContext === null) {
226
-            return;
227
-        }
228
-
229
-        ASDrawContext.clearRect(0, 0,
230
-            dominantSpeakerAudioElement.width,
231
-            dominantSpeakerAudioElement.height);
78
+    /**
79
+     * Updates the audio level of the large video.
80
+     *
81
+     * @param audioLevel the new audio level to set.
82
+     */
83
+    updateLargeVideoAudioLevel(elementId, audioLevel) {
84
+        let element = document.getElementById(elementId);
232 85
 
233
-        if (!audioLevel) {
86
+        if(!UIUtil.isVisible(element))
234 87
             return;
235
-        }
236 88
 
237
-        ASDrawContext.shadowBlur = getShadowLevel(audioLevel);
89
+        let level = parseFloat(audioLevel);
238 90
 
239
-        // Fill the shape.
240
-        ASDrawContext.fill();
241
-    },
91
+        level = isNaN(level) ? 0 : level;
242 92
 
243
-    updateCanvasSize (localVideo, remoteVideo) {
244
-        let { remoteThumbs, localThumb } = FilmStrip.getThumbs();
93
+        let shadowElement = element.getElementsByClassName("dynamic-shadow");
245 94
 
246
-        remoteThumbs.each(( index, element ) => {
247
-            this.createAudioLevelCanvas(element.id,
248
-                                        remoteVideo.thumbWidth,
249
-                                        remoteVideo.thumbHeight);
250
-        });
95
+        if (shadowElement && shadowElement.length > 0)
96
+            shadowElement = shadowElement[0];
251 97
 
252
-        if (localThumb) {
253
-            this.createAudioLevelCanvas(localThumb.get(0).id,
254
-                                        localVideo.thumbWidth,
255
-                                        localVideo.thumbHeight);
256
-        }
98
+        shadowElement.style.boxShadow = this._updateLargeVideoShadow(level);
99
+    },
100
+
101
+    /**
102
+     * Updates the large video shadow effect.
103
+     */
104
+    _updateLargeVideoShadow (level) {
105
+        var scale = 2,
106
+
107
+        // Internal circle audio level.
108
+        int = {
109
+            level: level > 0.15 ? 20 : 0,
110
+            color: interfaceConfig.AUDIO_LEVEL_PRIMARY_COLOR
111
+        },
112
+
113
+        // External circle audio level.
114
+        ext = {
115
+            level: (int.level * scale * level + int.level).toFixed(0),
116
+            color: interfaceConfig.AUDIO_LEVEL_SECONDARY_COLOR
117
+        };
118
+
119
+        // Internal blur.
120
+        int.blur = int.level ? 2 : 0;
121
+
122
+        // External blur.
123
+        ext.blur = ext.level ? 6 : 0;
124
+
125
+        return [
126
+            `0 0 ${ int.blur }px ${ int.level }px ${ int.color }`,
127
+            `0 0 ${ ext.blur }px ${ ext.level }px ${ ext.color }`
128
+        ].join(', ');
257 129
     }
258 130
 };
259 131
 

+ 0
- 108
modules/UI/audio_levels/CanvasUtils.js ファイルの表示

@@ -1,108 +0,0 @@
1
-/**
2
- * Utility class for drawing canvas shapes.
3
- */
4
-const CanvasUtil = {
5
-
6
-    /**
7
-     * Draws a round rectangle with a glow. The glowWidth indicates the depth
8
-     * of the glow.
9
-     *
10
-     * @param drawContext the context of the canvas to draw to
11
-     * @param x the x coordinate of the round rectangle
12
-     * @param y the y coordinate of the round rectangle
13
-     * @param w the width of the round rectangle
14
-     * @param h the height of the round rectangle
15
-     * @param glowColor the color of the glow
16
-     * @param glowWidth the width of the glow
17
-     */
18
-    drawRoundRectGlow (drawContext, x, y, w, h, r, glowColor, glowWidth) {
19
-
20
-        // Save the previous state of the context.
21
-        drawContext.save();
22
-
23
-        if (w < 2 * r) r = w / 2;
24
-        if (h < 2 * r) r = h / 2;
25
-
26
-        // Draw a round rectangle.
27
-        drawContext.beginPath();
28
-        drawContext.moveTo(x+r, y);
29
-        drawContext.arcTo(x+w, y,   x+w, y+h, r);
30
-        drawContext.arcTo(x+w, y+h, x,   y+h, r);
31
-        drawContext.arcTo(x,   y+h, x,   y,   r);
32
-        drawContext.arcTo(x,   y,   x+w, y,   r);
33
-        drawContext.closePath();
34
-
35
-        // Add a shadow around the rectangle
36
-        drawContext.shadowColor = glowColor;
37
-        drawContext.shadowBlur = glowWidth;
38
-        drawContext.shadowOffsetX = 0;
39
-        drawContext.shadowOffsetY = 0;
40
-
41
-        // Fill the shape.
42
-        drawContext.fill();
43
-
44
-        drawContext.save();
45
-
46
-        drawContext.restore();
47
-
48
-//      1) Uncomment this line to use Composite Operation, which is doing the
49
-//      same as the clip function below and is also antialiasing the round
50
-//      border, but is said to be less fast performance wise.
51
-
52
-//      drawContext.globalCompositeOperation='destination-out';
53
-
54
-        drawContext.beginPath();
55
-        drawContext.moveTo(x+r, y);
56
-        drawContext.arcTo(x+w, y,   x+w, y+h, r);
57
-        drawContext.arcTo(x+w, y+h, x,   y+h, r);
58
-        drawContext.arcTo(x,   y+h, x,   y,   r);
59
-        drawContext.arcTo(x,   y,   x+w, y,   r);
60
-        drawContext.closePath();
61
-
62
-//      2) Uncomment this line to use Composite Operation, which is doing the
63
-//      same as the clip function below and is also antialiasing the round
64
-//      border, but is said to be less fast performance wise.
65
-
66
-//      drawContext.fill();
67
-
68
-        // Comment these two lines if choosing to do the same with composite
69
-        // operation above 1 and 2.
70
-        drawContext.clip();
71
-        drawContext.clearRect(0, 0, 277, 200);
72
-
73
-        // Restore the previous context state.
74
-        drawContext.restore();
75
-    },
76
-
77
-    /**
78
-     * Clones the given canvas.
79
-     *
80
-     * @return the new cloned canvas.
81
-     */
82
-    cloneCanvas (oldCanvas) {
83
-        /*
84
-         * FIXME Testing has shown that oldCanvas may not exist. In such a case,
85
-         * the method CanvasUtil.cloneCanvas may throw an error. Since audio
86
-         * levels are frequently updated, the errors have been observed to pile
87
-         * into the console, strain the CPU.
88
-         */
89
-        if (!oldCanvas)
90
-            return oldCanvas;
91
-
92
-        //create a new canvas
93
-        var newCanvas = document.createElement('canvas');
94
-        var context = newCanvas.getContext('2d');
95
-
96
-        //set dimensions
97
-        newCanvas.width = oldCanvas.width;
98
-        newCanvas.height = oldCanvas.height;
99
-
100
-        //apply the old canvas to the new one
101
-        context.drawImage(oldCanvas, 0, 0);
102
-
103
-        //return the new canvas
104
-        return newCanvas;
105
-    }
106
-};
107
-
108
-export default CanvasUtil;

+ 1
- 1
modules/UI/videolayout/FilmStrip.js ファイルの表示

@@ -266,7 +266,7 @@ const FilmStrip = {
266 266
             return { remoteThumbs, localThumb };
267 267
         }
268 268
 
269
-    },
269
+    }
270 270
 
271 271
 };
272 272
 

+ 12
- 0
modules/UI/videolayout/LargeVideoManager.js ファイルの表示

@@ -5,8 +5,11 @@ import Avatar from "../avatar/Avatar";
5 5
 import {createDeferred} from '../../util/helpers';
6 6
 import UIUtil from "../util/UIUtil";
7 7
 import {VideoContainer, VIDEO_CONTAINER_TYPE} from "./VideoContainer";
8
+
8 9
 import LargeContainer from "./LargeContainer";
9 10
 
11
+import AudioLevels from "../audio_levels/AudioLevels";
12
+
10 13
 /**
11 14
  * Manager for all Large containers.
12 15
  */
@@ -307,6 +310,15 @@ export default class LargeVideoManager {
307 310
         $("#dominantSpeakerAvatar").attr('src', avatarUrl);
308 311
     }
309 312
 
313
+    /**
314
+     * Updates the audio level indicator of the large video.
315
+     *
316
+     * @param lvl the new audio level to set
317
+     */
318
+    updateLargeVideoAudioLevel (lvl) {
319
+        AudioLevels.updateLargeVideoAudioLevel("dominantSpeaker", lvl);
320
+    }
321
+
310 322
     /**
311 323
      * Show or hide watermark.
312 324
      * @param {boolean} show

+ 1
- 0
modules/UI/videolayout/LocalVideo.js ファイルの表示

@@ -28,6 +28,7 @@ function LocalVideo(VideoLayout, emitter) {
28 28
     this.setDisplayName();
29 29
 
30 30
     this.createConnectionIndicator();
31
+    this.addAudioLevelIndicator();
31 32
 }
32 33
 
33 34
 LocalVideo.prototype = Object.create(SmallVideo.prototype);

+ 2
- 3
modules/UI/videolayout/RemoteVideo.js ファイルの表示

@@ -63,13 +63,12 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {
63 63
 
64 64
     let { remoteVideo } = this.VideoLayout.resizeThumbnails(false, true);
65 65
     let { thumbHeight, thumbWidth } = remoteVideo;
66
-    AudioLevels.createAudioLevelCanvas(
67
-        this.videoSpanId, thumbWidth, thumbHeight);
66
+
67
+    this.addAudioLevelIndicator();
68 68
 
69 69
     return this.container;
70 70
 };
71 71
 
72
-
73 72
 /**
74 73
  * Initializes the remote participant popup menu, by specifying previously
75 74
  * constructed popupMenuElement, containing all the menu items.

+ 30
- 5
modules/UI/videolayout/SmallVideo.js ファイルの表示

@@ -2,6 +2,7 @@
2 2
 import Avatar from "../avatar/Avatar";
3 3
 import UIUtil from "../util/UIUtil";
4 4
 import UIEvents from "../../../service/UI/UIEvents";
5
+import AudioLevels from "../audio_levels/AudioLevels";
5 6
 
6 7
 const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper;
7 8
 
@@ -266,7 +267,7 @@ SmallVideo.prototype.getAudioMutedIndicator = function () {
266 267
  * @param {boolean} isMuted indicates if we should set the view to muted view
267 268
  * or not
268 269
  */
269
-SmallVideo.prototype.setMutedView = function(isMuted) {
270
+SmallVideo.prototype.setVideoMutedView = function(isMuted) {
270 271
     this.isVideoMuted = isMuted;
271 272
     this.updateView();
272 273
 
@@ -308,10 +309,11 @@ SmallVideo.prototype.getVideoMutedIndicator = function () {
308 309
 };
309 310
 
310 311
 /**
311
- * Creates the element indicating the moderator(owner) of the conference.
312
+ * Adds the element indicating the moderator(owner) of the conference.
312 313
  */
313
-SmallVideo.prototype.createModeratorIndicatorElement = function () {
314
-    // don't create moderator indicator if DISABLE_FOCUS_INDICATOR is true
314
+SmallVideo.prototype.addModeratorIndicator = function () {
315
+
316
+    // Don't create moderator indicator if DISABLE_FOCUS_INDICATOR is true
315 317
     if (interfaceConfig.DISABLE_FOCUS_INDICATOR)
316 318
         return false;
317 319
 
@@ -339,10 +341,33 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () {
339 341
     indicatorSpan.appendChild(moderatorIndicator);
340 342
 };
341 343
 
344
+/**
345
+ * Adds the element indicating the audio level of the participant.
346
+ */
347
+SmallVideo.prototype.addAudioLevelIndicator = function () {
348
+    var audioSpan = $('#' + this.videoSpanId + ' .audioindicator');
349
+
350
+    if (audioSpan.length) {
351
+        return;
352
+    }
353
+
354
+    this.container.appendChild(
355
+        AudioLevels.createThumbnailAudioLevelIndicator());
356
+};
357
+
358
+/**
359
+ * Updates the audio level for this small video.
360
+ *
361
+ * @param lvl the new audio level to set
362
+ */
363
+SmallVideo.prototype.updateAudioLevelIndicator = function (lvl) {
364
+    AudioLevels.updateThumbnailAudioLevel(this.videoSpanId, lvl);
365
+};
366
+
342 367
 /**
343 368
  * Removes the element indicating the moderator(owner) of the conference.
344 369
  */
345
-SmallVideo.prototype.removeModeratorIndicatorElement = function () {
370
+SmallVideo.prototype.removeModeratorIndicator = function () {
346 371
     $('#' + this.videoSpanId + ' .focusindicator').remove();
347 372
 };
348 373
 

+ 17
- 18
modules/UI/videolayout/VideoLayout.js ファイルの表示

@@ -1,7 +1,6 @@
1 1
 /* global config, APP, $, interfaceConfig, JitsiMeetJS */
2 2
 /* jshint -W101 */
3 3
 
4
-import AudioLevels from "../audio_levels/AudioLevels";
5 4
 import Avatar from "../avatar/Avatar";
6 5
 import FilmStrip from "./FilmStrip";
7 6
 import UIEvents from "../../../service/UI/UIEvents";
@@ -108,10 +107,6 @@ var VideoLayout = {
108 107
         // if we do not resize the thumbs here, if there is no video device
109 108
         // the local video thumb maybe one pixel
110 109
         let { localVideo } = this.resizeThumbnails(false, true);
111
-        AudioLevels.createAudioLevelCanvas(
112
-            "localVideoContainer",
113
-            localVideo.thumbWidth,
114
-            localVideo.thumbHeight);
115 110
 
116 111
         emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
117 112
         this.lastNCount = config.channelLastN;
@@ -123,16 +118,21 @@ var VideoLayout = {
123 118
             largeVideo.onLocalFlipXChange(localFlipX);
124 119
         }
125 120
         largeVideo.updateContainerSize();
126
-        AudioLevels.init();
127 121
     },
128 122
 
123
+    /**
124
+     * Sets the audio level of the video elements associated to the given id.
125
+     *
126
+     * @param id the video identifier in the form it comes from the library
127
+     * @param lvl the new audio level to update to
128
+     */
129 129
     setAudioLevel(id, lvl) {
130
-        if (!largeVideo) {
131
-            return;
132
-        }
133
-        AudioLevels.updateAudioLevel(
134
-            id, lvl, largeVideo.id
135
-        );
130
+        let smallVideo = this.getSmallVideo(id);
131
+        if (smallVideo)
132
+            smallVideo.updateAudioLevelIndicator(lvl);
133
+
134
+        if (largeVideo && id === largeVideo.id)
135
+            largeVideo.updateLargeVideoAudioLevel(lvl);
136 136
     },
137 137
 
138 138
     isInLastN (resource) {
@@ -469,9 +469,9 @@ var VideoLayout = {
469 469
     showModeratorIndicator () {
470 470
         let isModerator = APP.conference.isModerator;
471 471
         if (isModerator) {
472
-            localVideoThumbnail.createModeratorIndicatorElement();
472
+            localVideoThumbnail.addModeratorIndicator();
473 473
         } else {
474
-            localVideoThumbnail.removeModeratorIndicatorElement();
474
+            localVideoThumbnail.removeModeratorIndicator();
475 475
         }
476 476
 
477 477
         APP.conference.listMembers().forEach(function (member) {
@@ -481,7 +481,7 @@ var VideoLayout = {
481 481
                 return;
482 482
 
483 483
             if (member.isModerator()) {
484
-                remoteVideo.createModeratorIndicatorElement();
484
+                remoteVideo.addModeratorIndicator();
485 485
             }
486 486
 
487 487
             if (isModerator) {
@@ -528,7 +528,6 @@ var VideoLayout = {
528 528
         FilmStrip.resizeThumbnails(localVideo, remoteVideo,
529 529
             animate, forceUpdate)
530 530
             .then(function () {
531
-                AudioLevels.updateCanvasSize(localVideo, remoteVideo);
532 531
                 if (onComplete && typeof onComplete === "function")
533 532
                     onComplete();
534 533
             });
@@ -558,11 +557,11 @@ var VideoLayout = {
558 557
      */
559 558
     onVideoMute (id, value) {
560 559
         if (APP.conference.isLocalId(id)) {
561
-            localVideoThumbnail.setMutedView(value);
560
+            localVideoThumbnail.setVideoMutedView(value);
562 561
         } else {
563 562
             let remoteVideo = remoteVideos[id];
564 563
             if (remoteVideo)
565
-                remoteVideo.setMutedView(value);
564
+                remoteVideo.setVideoMutedView(value);
566 565
         }
567 566
 
568 567
         if (this.isCurrentlyOnLarge(id)) {

読み込み中…
キャンセル
保存