Ver código fonte

Fixes flickering issue with simulcast.

master
George Politis 10 anos atrás
pai
commit
71c08450bb
3 arquivos alterados com 115 adições e 108 exclusões
  1. 0
    1
      config.js
  2. 24
    22
      simulcast.js
  3. 91
    85
      videolayout.js

+ 0
- 1
config.js Ver arquivo

26
     enableRecording: false,
26
     enableRecording: false,
27
     enableWelcomePage: false,
27
     enableWelcomePage: false,
28
     enableSimulcast: false,
28
     enableSimulcast: false,
29
-    useNativeSimulcast: false,
30
     isBrand: false
29
     isBrand: false
31
 };
30
 };

+ 24
- 22
simulcast.js Ver arquivo

8
     "use strict";
8
     "use strict";
9
 
9
 
10
     // TODO(gp) split the Simulcast class in two classes : NativeSimulcast and ClassicSimulcast.
10
     // TODO(gp) split the Simulcast class in two classes : NativeSimulcast and ClassicSimulcast.
11
-    this.debugLvl = 1;
11
+
12
+    // Once we properly support native simulcast, enable it automatically in the
13
+    // supported browsers (Chrome).
14
+    this.useNativeSimulcast = false;
15
+
16
+    // TODO(gp) we need a logging framework for javascript à la log4j or the
17
+    // java logging framework that allows for selective log display
18
+    this.debugLvl = 0;
12
 }
19
 }
13
 
20
 
14
 (function () {
21
 (function () {
446
      * @returns {*}
453
      * @returns {*}
447
      */
454
      */
448
     Simulcast.prototype.transformAnswer = function (desc) {
455
     Simulcast.prototype.transformAnswer = function (desc) {
449
-        if (config.enableSimulcast && config.useNativeSimulcast) {
456
+        if (config.enableSimulcast && this.useNativeSimulcast) {
450
 
457
 
451
             var sb = desc.sdp.split('\r\n');
458
             var sb = desc.sdp.split('\r\n');
452
 
459
 
523
 
530
 
524
         if (config.enableSimulcast) {
531
         if (config.enableSimulcast) {
525
 
532
 
526
-            if (config.useNativeSimulcast) {
533
+            if (this.useNativeSimulcast) {
527
                 sb = desc.sdp.split('\r\n');
534
                 sb = desc.sdp.split('\r\n');
528
 
535
 
529
                 this._explodeLocalSimulcastSources(sb);
536
                 this._explodeLocalSimulcastSources(sb);
595
      * @returns {*}
602
      * @returns {*}
596
      */
603
      */
597
     Simulcast.prototype.transformLocalDescription = function (desc) {
604
     Simulcast.prototype.transformLocalDescription = function (desc) {
598
-        if (config.enableSimulcast && !config.useNativeSimulcast) {
605
+        if (config.enableSimulcast && !this.useNativeSimulcast) {
599
 
606
 
600
             var sb = desc.sdp.split('\r\n');
607
             var sb = desc.sdp.split('\r\n');
601
 
608
 
632
             this._cacheRemoteVideoSources(sb);
639
             this._cacheRemoteVideoSources(sb);
633
             this._removeSimulcastGroup(sb); // NOTE(gp) this needs to be called after updateRemoteMaps because we need the simulcast group in the _updateRemoteMaps() method.
640
             this._removeSimulcastGroup(sb); // NOTE(gp) this needs to be called after updateRemoteMaps because we need the simulcast group in the _updateRemoteMaps() method.
634
 
641
 
635
-            if (config.useNativeSimulcast) {
642
+            if (this.useNativeSimulcast) {
636
                 // We don't need the goog conference flag if we're not doing
643
                 // We don't need the goog conference flag if we're not doing
637
                 // native simulcast.
644
                 // native simulcast.
638
                 this._ensureGoogConference(sb);
645
                 this._ensureGoogConference(sb);
700
             : stream;
707
             : stream;
701
     };
708
     };
702
 
709
 
703
-    var stream;
710
+    var localStream, displayedLocalVideoStream;
704
 
711
 
705
     /**
712
     /**
706
      * GUM for simulcast.
713
      * GUM for simulcast.
727
         console.log('HQ constraints: ', constraints);
734
         console.log('HQ constraints: ', constraints);
728
         console.log('LQ constraints: ', lqConstraints);
735
         console.log('LQ constraints: ', lqConstraints);
729
 
736
 
730
-        if (config.enableSimulcast && !config.useNativeSimulcast) {
737
+        if (config.enableSimulcast && !this.useNativeSimulcast) {
731
 
738
 
732
             // NOTE(gp) if we request the lq stream first webkitGetUserMedia
739
             // NOTE(gp) if we request the lq stream first webkitGetUserMedia
733
             // fails randomly. Tested with Chrome 37. As fippo suggested, the
740
             // fails randomly. Tested with Chrome 37. As fippo suggested, the
736
 
743
 
737
             navigator.webkitGetUserMedia(constraints, function (hqStream) {
744
             navigator.webkitGetUserMedia(constraints, function (hqStream) {
738
 
745
 
746
+                localStream = hqStream;
747
+
739
                 // reset local maps.
748
                 // reset local maps.
740
                 localMaps.msids = [];
749
                 localMaps.msids = [];
741
                 localMaps.msid2ssrc = {};
750
                 localMaps.msid2ssrc = {};
745
 
754
 
746
                 navigator.webkitGetUserMedia(lqConstraints, function (lqStream) {
755
                 navigator.webkitGetUserMedia(lqConstraints, function (lqStream) {
747
 
756
 
757
+                    displayedLocalVideoStream = lqStream;
758
+
748
                     // NOTE(gp) The specification says Array.forEach() will visit
759
                     // NOTE(gp) The specification says Array.forEach() will visit
749
                     // the array elements in numeric order, and that it doesn't
760
                     // the array elements in numeric order, and that it doesn't
750
                     // visit elements that don't exist.
761
                     // visit elements that don't exist.
752
                     // add lq trackid to local map
763
                     // add lq trackid to local map
753
                     localMaps.msids.splice(0, 0, lqStream.getVideoTracks()[0].id);
764
                     localMaps.msids.splice(0, 0, lqStream.getVideoTracks()[0].id);
754
 
765
 
755
-                    hqStream.addTrack(lqStream.getVideoTracks()[0]);
756
-                    stream = hqStream;
757
-                    success(hqStream);
766
+                    localStream.addTrack(lqStream.getVideoTracks()[0]);
767
+                    success(localStream);
758
                 }, err);
768
                 }, err);
759
             }, err);
769
             }, err);
760
         } else {
770
         } else {
769
 
779
 
770
                 // add hq stream to local map
780
                 // add hq stream to local map
771
                 localMaps.msids.push(hqStream.getVideoTracks()[0].id);
781
                 localMaps.msids.push(hqStream.getVideoTracks()[0].id);
772
-                stream = hqStream;
773
-                success(hqStream);
782
+                displayedLocalVideoStream = localStream = hqStream;
783
+                success(localStream);
774
             }, err);
784
             }, err);
775
         }
785
         }
776
     };
786
     };
801
                 trackid = tid;
811
                 trackid = tid;
802
                 return true;
812
                 return true;
803
             }
813
             }
804
-        }) && stream.getVideoTracks().some(function(track) {
814
+        }) && localStream.getVideoTracks().some(function(track) {
805
             // Start/stop the track that corresponds to the track id
815
             // Start/stop the track that corresponds to the track id
806
             if (track.id === trackid) {
816
             if (track.id === trackid) {
807
                 track.enabled = enabled;
817
                 track.enabled = enabled;
818
     };
828
     };
819
 
829
 
820
     Simulcast.prototype.getLocalVideoStream = function() {
830
     Simulcast.prototype.getLocalVideoStream = function() {
821
-        var track;
822
-
823
-        stream.getVideoTracks().some(function(t) {
824
-            if ((track = t).enabled) {
825
-                return true;
826
-            }
827
-        });
828
-
829
-        return new webkitMediaStream([track]);
831
+        return displayedLocalVideoStream;
830
     };
832
     };
831
 
833
 
832
     $(document).bind('simulcastlayerschanged', function (event, endpointSimulcastLayers) {
834
     $(document).bind('simulcastlayerschanged', function (event, endpointSimulcastLayers) {

+ 91
- 85
videolayout.js Ver arquivo

3
     var currentDominantSpeaker = null;
3
     var currentDominantSpeaker = null;
4
     var lastNCount = config.channelLastN;
4
     var lastNCount = config.channelLastN;
5
     var lastNEndpointsCache = [];
5
     var lastNEndpointsCache = [];
6
-    var largeVideoNewSrc = '';
6
+    var largeVideoState = {
7
+        updateInProgress: false,
8
+        newSrc: ''
9
+    };
7
 
10
 
8
     my.changeLocalAudio = function(stream) {
11
     my.changeLocalAudio = function(stream) {
9
         connection.jingle.localAudio = stream;
12
         connection.jingle.localAudio = stream;
66
             localVideoSelector.addClass("flipVideoX");
69
             localVideoSelector.addClass("flipVideoX");
67
         }
70
         }
68
         // Attach WebRTC stream
71
         // Attach WebRTC stream
69
-        RTC.attachMediaStream(localVideoSelector, stream);
72
+        var simulcast = new Simulcast();
73
+        var videoStream = simulcast.getLocalVideoStream();
74
+        RTC.attachMediaStream(localVideoSelector, videoStream);
70
 
75
 
71
         localVideoSrc = localVideo.src;
76
         localVideoSrc = localVideo.src;
72
 
77
 
114
         console.log('hover in', newSrc);
119
         console.log('hover in', newSrc);
115
 
120
 
116
         if ($('#largeVideo').attr('src') != newSrc) {
121
         if ($('#largeVideo').attr('src') != newSrc) {
117
-            largeVideoNewSrc = newSrc;
118
-
119
-            var isVisible = $('#largeVideo').is(':visible');
120
 
122
 
121
-            // we need this here because after the fade the videoSrc may have
122
-            // changed.
123
-            var isDesktop = isVideoSrcDesktop(newSrc);
123
+            // Due to the simulcast the localVideoSrc may have changed when the
124
+            // fadeOut event triggers. In that case the getJidFromVideoSrc and
125
+            // isVideoSrcDesktop methods will not function correctly.
126
+            //
127
+            // Also, again due to the simulcast, the updateLargeVideo method can
128
+            // be called multiple times almost simultaneously. Therefore, we
129
+            // store the state here and update only once.
130
+
131
+            largeVideoState.newSrc = newSrc;
132
+            largeVideoState.isVisible = $('#largeVideo').is(':visible');
133
+            largeVideoState.isDesktop = isVideoSrcDesktop(newSrc);
134
+            largeVideoState.userJid = getJidFromVideoSrc(newSrc);
135
+
136
+            // Screen stream is already rotated
137
+            largeVideoState.flipX = (newSrc === localVideoSrc) && flipXLocalVideo;
138
+
139
+            var oldSrc = $('#largeVideo').attr('src');
140
+            largeVideoState.oldJid = getJidFromVideoSrc(oldSrc);
141
+
142
+            var fade = false;
143
+            if (largeVideoState.oldJid != largeVideoState.userJid) {
144
+                fade = true;
145
+                // we want the notification to trigger even if userJid is undefined,
146
+                // or null.
147
+                $(document).trigger("selectedendpointchanged", [largeVideoState.userJid]);
148
+            }
124
 
149
 
125
-            var userJid = getJidFromVideoSrc(newSrc);
126
-            // we want the notification to trigger even if userJid is undefined,
127
-            // or null.
128
-            $(document).trigger("selectedendpointchanged", [userJid]);
150
+            if (!largeVideoState.updateInProgress) {
151
+                largeVideoState.updateInProgress = true;
129
 
152
 
130
-            $('#largeVideo').fadeOut(300, function () {
131
-                var oldSrc = $(this).attr('src');
153
+                var doUpdate = function () {
132
 
154
 
133
-                $(this).attr('src', newSrc);
155
+                    $('#largeVideo').attr('src', largeVideoState.newSrc);
134
 
156
 
135
-                // Screen stream is already rotated
136
-                var flipX = (newSrc === localVideoSrc) && flipXLocalVideo;
157
+                    var videoTransform = document.getElementById('largeVideo')
158
+                        .style.webkitTransform;
137
 
159
 
138
-                var videoTransform = document.getElementById('largeVideo')
139
-                                        .style.webkitTransform;
160
+                    if (largeVideoState.flipX && videoTransform !== 'scaleX(-1)') {
161
+                        document.getElementById('largeVideo').style.webkitTransform
162
+                            = "scaleX(-1)";
163
+                    }
164
+                    else if (!largeVideoState.flipX && videoTransform === 'scaleX(-1)') {
165
+                        document.getElementById('largeVideo').style.webkitTransform
166
+                            = "none";
167
+                    }
140
 
168
 
141
-                if (flipX && videoTransform !== 'scaleX(-1)') {
142
-                    document.getElementById('largeVideo').style.webkitTransform
143
-                        = "scaleX(-1)";
144
-                }
145
-                else if (!flipX && videoTransform === 'scaleX(-1)') {
146
-                    document.getElementById('largeVideo').style.webkitTransform
147
-                        = "none";
148
-                }
169
+                    // Change the way we'll be measuring and positioning large video
170
+
171
+                    getVideoSize = largeVideoState.isDesktop
172
+                        ? getDesktopVideoSize
173
+                        : getCameraVideoSize;
174
+                    getVideoPosition = largeVideoState.isDesktop
175
+                        ? getDesktopVideoPosition
176
+                        : getCameraVideoPosition;
177
+
178
+                    if (largeVideoState.isVisible) {
179
+                        // Only if the large video is currently visible.
180
+                        // Disable previous dominant speaker video.
181
+                        if (largeVideoState.oldJid) {
182
+                            var oldResourceJid = Strophe.getResourceFromJid(largeVideoState.oldJid);
183
+                            VideoLayout.enableDominantSpeaker(oldResourceJid, false);
184
+                        }
149
 
185
 
150
-                // Change the way we'll be measuring and positioning large video
151
-
152
-                getVideoSize = isDesktop
153
-                                ? getDesktopVideoSize
154
-                                : getCameraVideoSize;
155
-                getVideoPosition = isDesktop
156
-                                    ? getDesktopVideoPosition
157
-                                    : getCameraVideoPosition;
158
-
159
-                if (isVisible) {
160
-                    // Only if the large video is currently visible.
161
-                    // Disable previous dominant speaker video.
162
-                    var oldJid = getJidFromVideoSrc(oldSrc);
163
-                    if (oldJid) {
164
-                        var oldResourceJid = Strophe.getResourceFromJid(oldJid);
165
-                        VideoLayout.enableDominantSpeaker(oldResourceJid, false);
166
-                    }
186
+                        // Enable new dominant speaker in the remote videos section.
187
+                        if (largeVideoState.userJid) {
188
+                            var resourceJid = Strophe.getResourceFromJid(largeVideoState.userJid);
189
+                            VideoLayout.enableDominantSpeaker(resourceJid, true);
190
+                        }
167
 
191
 
168
-                    // Enable new dominant speaker in the remote videos section.
169
-                    var userJid = getJidFromVideoSrc(newSrc);
170
-                    if (userJid)
171
-                    {
172
-                        var resourceJid = Strophe.getResourceFromJid(userJid);
173
-                        VideoLayout.enableDominantSpeaker(resourceJid, true);
192
+                        largeVideoState.updateInProgress = false;
193
+                        if (fade) {
194
+                            // using "this" should be ok because we're called
195
+                            // from within the fadeOut event.
196
+                            $(this).fadeIn(300);
197
+                        }
174
                     }
198
                     }
199
+                };
175
 
200
 
176
-                    $(this).fadeIn(300);
201
+                if (fade) {
202
+                    $('#largeVideo').fadeOut(300, doUpdate);
203
+                } else {
204
+                    doUpdate();
177
                 }
205
                 }
178
-            });
206
+            }
179
         }
207
         }
180
     };
208
     };
181
 
209
 
814
      * disabled
842
      * disabled
815
      */
843
      */
816
     my.enableDominantSpeaker = function(resourceJid, isEnable) {
844
     my.enableDominantSpeaker = function(resourceJid, isEnable) {
817
-        var displayName = resourceJid;
818
-        var nameSpan = $('#participant_' + resourceJid + '>span.displayname');
819
-        if (nameSpan.length > 0)
820
-            displayName = nameSpan.text();
821
-
822
-        console.log("UI enable dominant speaker",
823
-                    displayName,
824
-                    resourceJid,
825
-                    isEnable);
826
 
845
 
827
         var videoSpanId = null;
846
         var videoSpanId = null;
828
         var videoContainerId = null;
847
         var videoContainerId = null;
836
             videoContainerId = videoSpanId;
855
             videoContainerId = videoSpanId;
837
         }
856
         }
838
 
857
 
858
+        var displayName = resourceJid;
859
+        var nameSpan = $('#' + videoContainerId + '>span.displayname');
860
+        if (nameSpan.length > 0)
861
+            displayName = nameSpan.text();
862
+
863
+        console.log("UI enable dominant speaker",
864
+            displayName,
865
+            resourceJid,
866
+            isEnable);
867
+
839
         videoSpan = document.getElementById(videoContainerId);
868
         videoSpan = document.getElementById(videoContainerId);
840
 
869
 
841
         if (!videoSpan) {
870
         if (!videoSpan) {
1322
         }
1351
         }
1323
     });
1352
     });
1324
 
1353
 
1325
-    $(document).bind('simulcastlayerstarted simulcastlayerstopped', function(event) {
1326
-        var localVideoSelector = $('#' + 'localVideo_' + connection.jingle.localVideo.id);
1327
-        var simulcast = new Simulcast();
1328
-        var stream = simulcast.getLocalVideoStream();
1329
-
1330
-        var updateLargeVideo = (connection.emuc.myroomjid
1331
-            == getJidFromVideoSrc(largeVideoNewSrc));
1332
-        var updateFocusedVideoSrc = (localVideoSrc == focusedVideoSrc);
1333
-
1334
-        // Attach WebRTC stream
1335
-        RTC.attachMediaStream(localVideoSelector, stream);
1336
-
1337
-        localVideoSrc = $(localVideoSelector).attr('src');
1338
-
1339
-        if (updateLargeVideo) {
1340
-            VideoLayout.updateLargeVideo(localVideoSrc);
1341
-        }
1342
-
1343
-        if (updateFocusedVideoSrc) {
1344
-            focusedVideoSrc = localVideoSrc;
1345
-        }
1346
-    });
1347
-
1348
     /**
1354
     /**
1349
      * On simulcast layers changed event.
1355
      * On simulcast layers changed event.
1350
      */
1356
      */
1402
                 var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
1408
                 var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
1403
 
1409
 
1404
                 var updateLargeVideo = (ssrc2jid[videoSrcToSsrc[selRemoteVideo.attr('src')]]
1410
                 var updateLargeVideo = (ssrc2jid[videoSrcToSsrc[selRemoteVideo.attr('src')]]
1405
-                    == ssrc2jid[videoSrcToSsrc[largeVideoNewSrc]]);
1411
+                    == ssrc2jid[videoSrcToSsrc[largeVideoState.newSrc]]);
1406
                 var updateFocusedVideoSrc = (selRemoteVideo.attr('src') == focusedVideoSrc);
1412
                 var updateFocusedVideoSrc = (selRemoteVideo.attr('src') == focusedVideoSrc);
1407
 
1413
 
1408
                 var electedStreamUrl = webkitURL.createObjectURL(electedStream);
1414
                 var electedStreamUrl = webkitURL.createObjectURL(electedStream);

Carregando…
Cancelar
Salvar