Переглянути джерело

Audio level indication. Improvements in rollover and active speaker UI.

j8
yanas 11 роки тому
джерело
коміт
50f1521d5c
2 змінених файлів з 294 додано та 0 видалено
  1. 193
    0
      audio_levels.js
  2. 101
    0
      canvas_util.js

+ 193
- 0
audio_levels.js Переглянути файл

@@ -0,0 +1,193 @@
1
+/**
2
+ * The audio Levels plugin.
3
+ */
4
+var AudioLevels = (function(my) {
5
+    var CANVAS_EXTRA = 104;
6
+    var CANVAS_RADIUS = 7;
7
+    var SHADOW_COLOR = '#00ccff';
8
+    var audioLevelCanvasCache = {};
9
+
10
+    /**
11
+     * Updates the audio level canvas for the given peerJid. If the canvas
12
+     * didn't exist we create it.
13
+     */
14
+    my.updateAudioLevelCanvas = function (peerJid) {
15
+        var resourceJid = null;
16
+        var videoSpanId = null;
17
+        if (!peerJid)
18
+            videoSpanId = 'localVideoContainer';
19
+        else {
20
+            resourceJid = Strophe.getResourceFromJid(peerJid);
21
+
22
+            videoSpanId = 'participant_' + resourceJid;
23
+        }
24
+
25
+        videoSpan = document.getElementById(videoSpanId);
26
+
27
+        if (!videoSpan) {
28
+            if (resourceJid)
29
+                console.error("No video element for jid", resourceJid);
30
+            else
31
+                console.error("No video element for local video.");
32
+
33
+            return;
34
+        }
35
+
36
+        var audioLevelCanvas = $('#' + videoSpanId + '>canvas');
37
+
38
+        var videoSpaceWidth = $('#remoteVideos').width();
39
+        var thumbnailSize
40
+            = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
41
+        var thumbnailWidth = thumbnailSize[0];
42
+        var thumbnailHeight = thumbnailSize[1];
43
+
44
+        if (!audioLevelCanvas || audioLevelCanvas.length === 0) {
45
+
46
+            audioLevelCanvas = document.createElement('canvas');
47
+            audioLevelCanvas.className = "audiolevel";
48
+            audioLevelCanvas.style.bottom = "-" + CANVAS_EXTRA/2 + "px";
49
+            audioLevelCanvas.style.left = "-" + CANVAS_EXTRA/2 + "px";
50
+            resizeAudioLevelCanvas( audioLevelCanvas,
51
+                    thumbnailWidth,
52
+                    thumbnailHeight);
53
+
54
+            videoSpan.appendChild(audioLevelCanvas);
55
+        } else {
56
+            audioLevelCanvas = audioLevelCanvas.get(0);
57
+
58
+            resizeAudioLevelCanvas( audioLevelCanvas,
59
+                    thumbnailWidth,
60
+                    thumbnailHeight);
61
+        }
62
+    };
63
+
64
+    /**
65
+     * Updates the audio level UI for the given resourceJid.
66
+     *
67
+     * @param resourceJid the resource jid indicating the video element for
68
+     * which we draw the audio level
69
+     * @param audioLevel the newAudio level to render
70
+     */
71
+    my.updateAudioLevel = function (resourceJid, audioLevel) {
72
+        drawAudioLevelCanvas(resourceJid, audioLevel);
73
+
74
+        var videoSpanId = null;
75
+        if (resourceJid
76
+                === Strophe.getResourceFromJid(connection.emuc.myroomjid))
77
+            videoSpanId = 'localVideoContainer';
78
+        else
79
+            videoSpanId = 'participant_' + resourceJid;
80
+
81
+        var audioLevelCanvas = $('#' + videoSpanId + '>canvas').get(0);
82
+
83
+        if (!audioLevelCanvas)
84
+            return ;
85
+
86
+        var drawContext = audioLevelCanvas.getContext('2d');
87
+
88
+        var canvasCache = audioLevelCanvasCache[resourceJid];
89
+
90
+        drawContext.clearRect (0, 0,
91
+                audioLevelCanvas.width, audioLevelCanvas.height);
92
+        drawContext.drawImage(canvasCache, 0, 0);
93
+    };
94
+
95
+    function resizeAudioLevelCanvas(audioLevelCanvas,
96
+                                    thumbnailWidth,
97
+                                    thumbnailHeight) {
98
+        audioLevelCanvas.width = thumbnailWidth + CANVAS_EXTRA;
99
+        audioLevelCanvas.height = thumbnailHeight + CANVAS_EXTRA;
100
+    };
101
+
102
+    /**
103
+     * Draws the audio level canvas into the cached canvas object.
104
+     *
105
+     * @param resourceJid the resource jid indicating the video element for
106
+     * which we draw the audio level
107
+     * @param audioLevel the newAudio level to render
108
+     */
109
+    function drawAudioLevelCanvas(resourceJid, audioLevel) {
110
+        if (!audioLevelCanvasCache[resourceJid]) {
111
+            var videoSpanId = null;
112
+            if (resourceJid
113
+                    === Strophe.getResourceFromJid(connection.emuc.myroomjid))
114
+                videoSpanId = 'localVideoContainer';
115
+            else
116
+                videoSpanId = 'participant_' + resourceJid;
117
+
118
+            var audioLevelCanvasOrig = $('#' + videoSpanId + '>canvas').get(0);
119
+
120
+            audioLevelCanvasCache[resourceJid]
121
+                = CanvasUtil.cloneCanvas(audioLevelCanvasOrig);
122
+        }
123
+
124
+        var canvas = audioLevelCanvasCache[resourceJid];
125
+
126
+        var drawContext = canvas.getContext('2d');
127
+
128
+        drawContext.clearRect (0, 0, canvas.width, canvas.height);
129
+
130
+        var shadowLevel = getShadowLevel(audioLevel);
131
+
132
+        if (shadowLevel > 0)
133
+            // drawContext, x, y, w, h, r, shadowColor, shadowLevel
134
+            CanvasUtil.drawRoundRectGlow(   drawContext,
135
+                                            CANVAS_EXTRA/2, CANVAS_EXTRA/2,
136
+                                            canvas.width - CANVAS_EXTRA,
137
+                                            canvas.height - CANVAS_EXTRA,
138
+                                            CANVAS_RADIUS,
139
+                                            SHADOW_COLOR,
140
+                                            shadowLevel);
141
+    };
142
+
143
+    /**
144
+     * Returns the shadow/glow level for the given audio level.
145
+     *
146
+     * @param audioLevel the audio level from which we determine the shadow
147
+     * level
148
+     */
149
+    function getShadowLevel (audioLevel) {
150
+        var shadowLevel = 0;
151
+
152
+        if (audioLevel <= 0.3) {
153
+            shadowLevel = Math.round(CANVAS_EXTRA/2*(audioLevel/0.3));
154
+        }
155
+        else if (audioLevel <= 0.6) {
156
+            shadowLevel = Math.round(CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3));
157
+        }
158
+        else {
159
+            shadowLevel = Math.round(CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4));
160
+        }
161
+        return shadowLevel;
162
+    };
163
+
164
+    /**
165
+     * Indicates that the remote video has been resized.
166
+     */
167
+    $(document).bind('remotevideo.resized', function (event, width, height) {
168
+        var resized = false;
169
+        $('#remoteVideos>span>canvas').each(function() {
170
+            var canvas = $(this).get(0);
171
+            if (canvas.width !== width + CANVAS_EXTRA) {
172
+                canvas.width = width + CANVAS_EXTRA;
173
+                resized = true;
174
+            }
175
+
176
+            if (canvas.heigh !== height + CANVAS_EXTRA) {
177
+                canvas.height = height + CANVAS_EXTRA;
178
+                resized = true;
179
+            }
180
+        });
181
+
182
+        if (resized)
183
+            Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) {
184
+                audioLevelCanvasCache[resourceJid].width
185
+                    = width + CANVAS_EXTRA;
186
+                audioLevelCanvasCache[resourceJid].height
187
+                    = height + CANVAS_EXTRA;
188
+            });
189
+    });
190
+
191
+    return my;
192
+
193
+})(AudioLevels || {});

+ 101
- 0
canvas_util.js Переглянути файл

@@ -0,0 +1,101 @@
1
+/**
2
+ * Utility class for drawing canvas shapes.
3
+ */
4
+var CanvasUtil = (function(my) {
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
+    my.drawRoundRectGlow
19
+        = function(drawContext, x, y, w, h, r, glowColor, glowWidth) {
20
+
21
+        // Save the previous state of the context.
22
+        drawContext.save();
23
+
24
+        if (w < 2 * r) r = w / 2;
25
+        if (h < 2 * r) r = h / 2;
26
+
27
+        // Draw a round rectangle.
28
+        drawContext.beginPath();
29
+        drawContext.moveTo(x+r, y);
30
+        drawContext.arcTo(x+w, y,   x+w, y+h, r);
31
+        drawContext.arcTo(x+w, y+h, x,   y+h, r);
32
+        drawContext.arcTo(x,   y+h, x,   y,   r);
33
+        drawContext.arcTo(x,   y,   x+w, y,   r);
34
+        drawContext.closePath();
35
+
36
+        // Add a shadow around the rectangle
37
+        drawContext.shadowColor = glowColor;
38
+        drawContext.shadowBlur = glowWidth;
39
+        drawContext.shadowOffsetX = 0;
40
+        drawContext.shadowOffsetY = 0;
41
+
42
+        // Fill the shape.
43
+        drawContext.fill();
44
+
45
+        drawContext.save();
46
+
47
+        drawContext.restore();
48
+
49
+//      1) Uncomment this line to use Composite Operation, which is doing the
50
+//      same as the clip function below and is also antialiasing the round
51
+//      border, but is said to be less fast performance wise.
52
+
53
+//      drawContext.globalCompositeOperation='destination-out';
54
+
55
+        drawContext.beginPath();
56
+        drawContext.moveTo(x+r, y);
57
+        drawContext.arcTo(x+w, y,   x+w, y+h, r);
58
+        drawContext.arcTo(x+w, y+h, x,   y+h, r);
59
+        drawContext.arcTo(x,   y+h, x,   y,   r);
60
+        drawContext.arcTo(x,   y,   x+w, y,   r);
61
+        drawContext.closePath();
62
+
63
+//      2) Uncomment this line to use Composite Operation, which is doing the
64
+//      same as the clip function below and is also antialiasing the round
65
+//      border, but is said to be less fast performance wise.
66
+
67
+//      drawContext.fill();
68
+
69
+        // Comment these two lines if choosing to do the same with composite
70
+        // operation above 1 and 2.
71
+        drawContext.clip();
72
+        drawContext.clearRect(0, 0, 277, 200);
73
+
74
+        // Restore the previous context state.
75
+        drawContext.restore();
76
+    };
77
+
78
+    /**
79
+     * Clones the given canvas.
80
+     *
81
+     * @return the new cloned canvas.
82
+     */
83
+    my.cloneCanvas = function (oldCanvas) {
84
+
85
+        //create a new canvas
86
+        var newCanvas = document.createElement('canvas');
87
+        var context = newCanvas.getContext('2d');
88
+
89
+        //set dimensions
90
+        newCanvas.width = oldCanvas.width;
91
+        newCanvas.height = oldCanvas.height;
92
+
93
+        //apply the old canvas to the new one
94
+        context.drawImage(oldCanvas, 0, 0);
95
+
96
+        //return the new canvas
97
+        return newCanvas;
98
+    };
99
+
100
+    return my;
101
+})(CanvasUtil || {});

Завантаження…
Відмінити
Зберегти