Sfoglia il codice sorgente

Fixes flickering issue with simulcast.

master
George Politis 10 anni fa
parent
commit
71c08450bb
3 ha cambiato i file con 115 aggiunte e 108 eliminazioni
  1. 0
    1
      config.js
  2. 24
    22
      simulcast.js
  3. 91
    85
      videolayout.js

+ 0
- 1
config.js Vedi File

@@ -26,6 +26,5 @@ var config = {
26 26
     enableRecording: false,
27 27
     enableWelcomePage: false,
28 28
     enableSimulcast: false,
29
-    useNativeSimulcast: false,
30 29
     isBrand: false
31 30
 };

+ 24
- 22
simulcast.js Vedi File

@@ -8,7 +8,14 @@ function Simulcast() {
8 8
     "use strict";
9 9
 
10 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 21
 (function () {
@@ -446,7 +453,7 @@ function Simulcast() {
446 453
      * @returns {*}
447 454
      */
448 455
     Simulcast.prototype.transformAnswer = function (desc) {
449
-        if (config.enableSimulcast && config.useNativeSimulcast) {
456
+        if (config.enableSimulcast && this.useNativeSimulcast) {
450 457
 
451 458
             var sb = desc.sdp.split('\r\n');
452 459
 
@@ -523,7 +530,7 @@ function Simulcast() {
523 530
 
524 531
         if (config.enableSimulcast) {
525 532
 
526
-            if (config.useNativeSimulcast) {
533
+            if (this.useNativeSimulcast) {
527 534
                 sb = desc.sdp.split('\r\n');
528 535
 
529 536
                 this._explodeLocalSimulcastSources(sb);
@@ -595,7 +602,7 @@ function Simulcast() {
595 602
      * @returns {*}
596 603
      */
597 604
     Simulcast.prototype.transformLocalDescription = function (desc) {
598
-        if (config.enableSimulcast && !config.useNativeSimulcast) {
605
+        if (config.enableSimulcast && !this.useNativeSimulcast) {
599 606
 
600 607
             var sb = desc.sdp.split('\r\n');
601 608
 
@@ -632,7 +639,7 @@ function Simulcast() {
632 639
             this._cacheRemoteVideoSources(sb);
633 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 643
                 // We don't need the goog conference flag if we're not doing
637 644
                 // native simulcast.
638 645
                 this._ensureGoogConference(sb);
@@ -700,7 +707,7 @@ function Simulcast() {
700 707
             : stream;
701 708
     };
702 709
 
703
-    var stream;
710
+    var localStream, displayedLocalVideoStream;
704 711
 
705 712
     /**
706 713
      * GUM for simulcast.
@@ -727,7 +734,7 @@ function Simulcast() {
727 734
         console.log('HQ constraints: ', constraints);
728 735
         console.log('LQ constraints: ', lqConstraints);
729 736
 
730
-        if (config.enableSimulcast && !config.useNativeSimulcast) {
737
+        if (config.enableSimulcast && !this.useNativeSimulcast) {
731 738
 
732 739
             // NOTE(gp) if we request the lq stream first webkitGetUserMedia
733 740
             // fails randomly. Tested with Chrome 37. As fippo suggested, the
@@ -736,6 +743,8 @@ function Simulcast() {
736 743
 
737 744
             navigator.webkitGetUserMedia(constraints, function (hqStream) {
738 745
 
746
+                localStream = hqStream;
747
+
739 748
                 // reset local maps.
740 749
                 localMaps.msids = [];
741 750
                 localMaps.msid2ssrc = {};
@@ -745,6 +754,8 @@ function Simulcast() {
745 754
 
746 755
                 navigator.webkitGetUserMedia(lqConstraints, function (lqStream) {
747 756
 
757
+                    displayedLocalVideoStream = lqStream;
758
+
748 759
                     // NOTE(gp) The specification says Array.forEach() will visit
749 760
                     // the array elements in numeric order, and that it doesn't
750 761
                     // visit elements that don't exist.
@@ -752,9 +763,8 @@ function Simulcast() {
752 763
                     // add lq trackid to local map
753 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 768
                 }, err);
759 769
             }, err);
760 770
         } else {
@@ -769,8 +779,8 @@ function Simulcast() {
769 779
 
770 780
                 // add hq stream to local map
771 781
                 localMaps.msids.push(hqStream.getVideoTracks()[0].id);
772
-                stream = hqStream;
773
-                success(hqStream);
782
+                displayedLocalVideoStream = localStream = hqStream;
783
+                success(localStream);
774 784
             }, err);
775 785
         }
776 786
     };
@@ -801,7 +811,7 @@ function Simulcast() {
801 811
                 trackid = tid;
802 812
                 return true;
803 813
             }
804
-        }) && stream.getVideoTracks().some(function(track) {
814
+        }) && localStream.getVideoTracks().some(function(track) {
805 815
             // Start/stop the track that corresponds to the track id
806 816
             if (track.id === trackid) {
807 817
                 track.enabled = enabled;
@@ -818,15 +828,7 @@ function Simulcast() {
818 828
     };
819 829
 
820 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 834
     $(document).bind('simulcastlayerschanged', function (event, endpointSimulcastLayers) {

+ 91
- 85
videolayout.js Vedi File

@@ -3,7 +3,10 @@ var VideoLayout = (function (my) {
3 3
     var currentDominantSpeaker = null;
4 4
     var lastNCount = config.channelLastN;
5 5
     var lastNEndpointsCache = [];
6
-    var largeVideoNewSrc = '';
6
+    var largeVideoState = {
7
+        updateInProgress: false,
8
+        newSrc: ''
9
+    };
7 10
 
8 11
     my.changeLocalAudio = function(stream) {
9 12
         connection.jingle.localAudio = stream;
@@ -66,7 +69,9 @@ var VideoLayout = (function (my) {
66 69
             localVideoSelector.addClass("flipVideoX");
67 70
         }
68 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 76
         localVideoSrc = localVideo.src;
72 77
 
@@ -114,68 +119,91 @@ var VideoLayout = (function (my) {
114 119
         console.log('hover in', newSrc);
115 120
 
116 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,15 +842,6 @@ var VideoLayout = (function (my) {
814 842
      * disabled
815 843
      */
816 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 846
         var videoSpanId = null;
828 847
         var videoContainerId = null;
@@ -836,6 +855,16 @@ var VideoLayout = (function (my) {
836 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 868
         videoSpan = document.getElementById(videoContainerId);
840 869
 
841 870
         if (!videoSpan) {
@@ -1322,29 +1351,6 @@ var VideoLayout = (function (my) {
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 1355
      * On simulcast layers changed event.
1350 1356
      */
@@ -1402,7 +1408,7 @@ var VideoLayout = (function (my) {
1402 1408
                 var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
1403 1409
 
1404 1410
                 var updateLargeVideo = (ssrc2jid[videoSrcToSsrc[selRemoteVideo.attr('src')]]
1405
-                    == ssrc2jid[videoSrcToSsrc[largeVideoNewSrc]]);
1411
+                    == ssrc2jid[videoSrcToSsrc[largeVideoState.newSrc]]);
1406 1412
                 var updateFocusedVideoSrc = (selRemoteVideo.attr('src') == focusedVideoSrc);
1407 1413
 
1408 1414
                 var electedStreamUrl = webkitURL.createObjectURL(electedStream);

Loading…
Annulla
Salva