瀏覽代碼

Handle last n in the client (#1389)

* Handle last n in the client

* fix(LargeVideoManager.js): Fixes check for low bandwidth. Needs more work

* fix(LargeVideoManager.js): Fixes the Shared Video test.

* fix(LargeVideoManager): Fix shared video view and remove last n checks.

* fix(LargeVideoManager): Fixes jsdoc comment

* fix(RemoteVideo): Fix connection status check

* fix(LargeVideoManager,RemoteVideo): Syntax errors
master
yanas 8 年之前
父節點
當前提交
704e14f008

+ 15
- 1
conference.js 查看文件

@@ -1283,8 +1283,9 @@ export default {
1283 1283
 */
1284 1284
         room.on(
1285 1285
             ConferenceEvents.LAST_N_ENDPOINTS_CHANGED, (ids, enteringIds) => {
1286
-            APP.UI.handleLastNEndpoints(ids, enteringIds);
1286
+                APP.UI.handleLastNEndpoints(ids, enteringIds);
1287 1287
         });
1288
+
1288 1289
         room.on(
1289 1290
             ConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
1290 1291
             (id, isActive) => {
@@ -1940,5 +1941,18 @@ export default {
1940 1941
      */
1941 1942
     removeListener (eventName, listener) {
1942 1943
         eventEmitter.removeListener(eventName, listener);
1944
+    },
1945
+
1946
+    /**
1947
+     * Checks if the participant given by participantId is currently in the
1948
+     * last N set if there's one supported.
1949
+     *
1950
+     * @param participantId the identifier of the participant
1951
+     * @returns {boolean} {true} if the participant given by the participantId
1952
+     * is currently in the last N set or if there's no last N set at this point
1953
+     * and {false} otherwise
1954
+     */
1955
+    isInLastN (participantId) {
1956
+        return room.isInLastN(participantId);
1943 1957
     }
1944 1958
 };

+ 3
- 1
lang/main.json 查看文件

@@ -382,7 +382,9 @@
382 382
         "FETCH_SESSION_ID": "Obtaining session-id...",
383 383
         "GOT_SESSION_ID": "Obtaining session-id... Done",
384 384
         "GET_SESSION_ID_ERROR": "Get session-id error: __code__",
385
-        "USER_CONNECTION_INTERRUPTED": "__displayName__ is having connectivity issues..."
385
+        "USER_CONNECTION_INTERRUPTED": "__displayName__ is having connectivity issues...",
386
+        "LOW_BANDWIDTH": "Video for __displayName__ has been turned off to save bandwidth"
387
+
386 388
     },
387 389
     "recording":
388 390
     {

+ 11
- 11
modules/UI/videolayout/ConnectionIndicator.js 查看文件

@@ -348,17 +348,17 @@ ConnectionIndicator.prototype.remove = function() {
348 348
  * the user is having connectivity issues.
349 349
  */
350 350
 ConnectionIndicator.prototype.updateConnectionStatusIndicator
351
-= function (isActive) {
352
-    this.isConnectionActive = isActive;
353
-    if (this.isConnectionActive) {
354
-        $(this.interruptedIndicator).hide();
355
-        $(this.emptyIcon).show();
356
-        $(this.fullIcon).show();
357
-    } else {
358
-        $(this.interruptedIndicator).show();
359
-        $(this.emptyIcon).hide();
360
-        $(this.fullIcon).hide();
361
-    }
351
+    = function (isActive) {
352
+        this.isConnectionActive = isActive;
353
+        if (this.isConnectionActive) {
354
+            $(this.interruptedIndicator).hide();
355
+            $(this.emptyIcon).show();
356
+            $(this.fullIcon).show();
357
+        } else {
358
+            $(this.interruptedIndicator).show();
359
+            $(this.emptyIcon).hide();
360
+            $(this.fullIcon).hide();
361
+        }
362 362
 };
363 363
 
364 364
 /**

+ 15
- 7
modules/UI/videolayout/LargeVideoManager.js 查看文件

@@ -127,7 +127,9 @@ export default class LargeVideoManager {
127 127
             // the video was not rendered, before the connection has failed.
128 128
             const isHavingConnectivityIssues
129 129
                 = APP.conference.isParticipantConnectionActive(id) === false;
130
-            if (isHavingConnectivityIssues
130
+
131
+            if (videoType === VIDEO_CONTAINER_TYPE
132
+                    && isHavingConnectivityIssues
131 133
                     && (isUserSwitch || !container.wasVideoRendered)) {
132 134
                 showAvatar = true;
133 135
             }
@@ -155,10 +157,15 @@ export default class LargeVideoManager {
155 157
 
156 158
             // Make sure no notification about remote failure is shown as
157 159
             // its UI conflicts with the one for local connection interrupted.
160
+            const isConnected = APP.conference.isConnectionInterrupted()
161
+                                || !isHavingConnectivityIssues;
162
+
158 163
             this.updateParticipantConnStatusIndication(
159 164
                     id,
160
-                    APP.conference.isConnectionInterrupted()
161
-                        || !isHavingConnectivityIssues);
165
+                    isConnected,
166
+                    (isHavingConnectivityIssues)
167
+                        ? "connection.USER_CONNECTION_INTERRUPTED"
168
+                        : "connection.LOW_BANDWIDTH");
162 169
 
163 170
             // resolve updateLargeVideo promise after everything is done
164 171
             promise.then(resolve);
@@ -180,10 +187,11 @@ export default class LargeVideoManager {
180 187
      * @param {string} id the id of remote participant(MUC nickname)
181 188
      * @param {boolean} isConnected true if the connection is active or false
182 189
      * when the user is having connectivity issues.
190
+     * @param {string} messageKey the i18n key of the message
183 191
      *
184 192
      * @private
185 193
      */
186
-    updateParticipantConnStatusIndication (id, isConnected) {
194
+    updateParticipantConnStatusIndication (id, isConnected, messageKey) {
187 195
 
188 196
         // Apply grey filter on the large video
189 197
         this.videoContainer.showRemoteConnectionProblemIndicator(!isConnected);
@@ -196,7 +204,7 @@ export default class LargeVideoManager {
196 204
             let displayName
197 205
                 = APP.conference.getParticipantDisplayName(id);
198 206
             this._setRemoteConnectionMessage(
199
-                "connection.USER_CONNECTION_INTERRUPTED",
207
+                messageKey,
200 208
                 { displayName: displayName });
201 209
 
202 210
             // Show it now only if the VideoContainer is on top
@@ -332,7 +340,7 @@ export default class LargeVideoManager {
332 340
      */
333 341
     showRemoteConnectionMessage (show) {
334 342
         if (typeof show !== 'boolean') {
335
-            show = APP.conference.isParticipantConnectionActive(this.id);
343
+            show = !APP.conference.isParticipantConnectionActive(this.id);
336 344
         }
337 345
 
338 346
         if (show) {
@@ -458,7 +466,7 @@ export default class LargeVideoManager {
458 466
                 // "avatar" and "video connection" can not be displayed both
459 467
                 // at the same time, but the latter is of higher priority and it
460 468
                 // will hide the avatar one if will be displayed.
461
-                this.showRemoteConnectionMessage(/* fet the current state */);
469
+                this.showRemoteConnectionMessage(/* fetch the current state */);
462 470
                 this.showLocalConnectionMessage(/* fetch the current state */);
463 471
             }
464 472
         });

+ 13
- 58
modules/UI/videolayout/RemoteVideo.js 查看文件

@@ -460,12 +460,12 @@ RemoteVideo.prototype.setMutedView = function(isMuted) {
460 460
  * @private
461 461
  */
462 462
 RemoteVideo.prototype._figureOutMutedWhileDisconnected
463
-= function(isDisconnected) {
464
-    if (isDisconnected && this.isVideoMuted) {
465
-        this.mutedWhileDisconnected = true;
466
-    } else if (!isDisconnected && !this.isVideoMuted) {
467
-        this.mutedWhileDisconnected = false;
468
-    }
463
+    = function(isDisconnected) {
464
+        if (isDisconnected && this.isVideoMuted) {
465
+            this.mutedWhileDisconnected = true;
466
+        } else if (!isDisconnected && !this.isVideoMuted) {
467
+            this.mutedWhileDisconnected = false;
468
+        }
469 469
 };
470 470
 
471 471
 /**
@@ -554,8 +554,7 @@ RemoteVideo.prototype.isVideoPlayable = function () {
554 554
  */
555 555
 RemoteVideo.prototype.updateView = function () {
556 556
 
557
-    this.updateConnectionStatusIndicator(
558
-        null /* will obtain the status from 'conference' */);
557
+    this.updateConnectionStatusIndicator();
559 558
 
560 559
     // This must be called after 'updateConnectionStatusIndicator' because it
561 560
     // affects the display mode by modifying 'mutedWhileDisconnected' flag
@@ -564,19 +563,13 @@ RemoteVideo.prototype.updateView = function () {
564 563
 
565 564
 /**
566 565
  * Updates the UI to reflect user's connectivity status.
567
- * @param isActive {boolean|null} 'true' if user's connection is active or
568
- * 'false' when the use is having some connectivity issues and a warning
569
- * should be displayed. When 'null' is passed then the current value will be
570
- * obtained from the conference instance.
571 566
  */
572
-RemoteVideo.prototype.updateConnectionStatusIndicator = function (isActive) {
573
-    // Check for initial value if 'isActive' is not defined
574
-    if (typeof isActive !== "boolean") {
575
-        isActive = this.isConnectionActive();
576
-        if (isActive === null) {
577
-            // Cancel processing at this point - no update
578
-            return;
579
-        }
567
+RemoteVideo.prototype.updateConnectionStatusIndicator = function () {
568
+    const isActive = this.isConnectionActive();
569
+
570
+    if (isActive === null) {
571
+        // Cancel processing at this point - no update
572
+        return;
580 573
     }
581 574
 
582 575
     logger.debug(this.id + " thumbnail is connection active ? " + isActive);
@@ -700,44 +693,6 @@ RemoteVideo.prototype.addRemoteStreamElement = function (stream) {
700 693
     }
701 694
 };
702 695
 
703
-/**
704
- * Show/hide peer container for the given id.
705
- */
706
-RemoteVideo.prototype.showPeerContainer = function (state) {
707
-    if (!this.container)
708
-        return;
709
-
710
-    var isHide = state === 'hide';
711
-    var resizeThumbnails = false;
712
-
713
-    if (!isHide) {
714
-        if (!$(this.container).is(':visible')) {
715
-            resizeThumbnails = true;
716
-            $(this.container).show();
717
-        }
718
-        // Call updateView, so that we'll figure out if avatar
719
-        // should be displayed based on video muted status and whether or not
720
-        // it's in the lastN set
721
-        this.updateView();
722
-    }
723
-    else if ($(this.container).is(':visible') && isHide)
724
-    {
725
-        resizeThumbnails = true;
726
-        $(this.container).hide();
727
-        if(this.connectionIndicator)
728
-            this.connectionIndicator.hide();
729
-    }
730
-
731
-    if (resizeThumbnails) {
732
-        this.VideoLayout.resizeThumbnails();
733
-    }
734
-
735
-    // We want to be able to pin a participant from the contact list, even
736
-    // if he's not in the lastN set!
737
-    // ContactList.setClickable(id, !isHide);
738
-
739
-};
740
-
741 696
 RemoteVideo.prototype.updateResolution = function (resolution) {
742 697
     if (this.connectionIndicator) {
743 698
         this.connectionIndicator.updateResolution(resolution);

+ 2
- 2
modules/UI/videolayout/SmallVideo.js 查看文件

@@ -1,4 +1,4 @@
1
-/* global $, JitsiMeetJS, interfaceConfig */
1
+/* global $, APP, JitsiMeetJS, interfaceConfig */
2 2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
3 3
 
4 4
 import Avatar from "../avatar/Avatar";
@@ -446,7 +446,7 @@ SmallVideo.prototype.isCurrentlyOnLargeVideo = function () {
446 446
 SmallVideo.prototype.isVideoPlayable = function() {
447 447
     return this.videoStream // Is there anything to display ?
448 448
         && !this.isVideoMuted && !this.videoStream.isMuted() // Muted ?
449
-        && (this.isLocal || this.VideoLayout.isInLastN(this.id));
449
+        && (this.isLocal || APP.conference.isInLastN(this.id));
450 450
 };
451 451
 
452 452
 /**

+ 15
- 156
modules/UI/videolayout/VideoLayout.js 查看文件

@@ -1,4 +1,4 @@
1
-/* global config, APP, $, interfaceConfig */
1
+/* global APP, $, interfaceConfig */
2 2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
3 3
 
4 4
 import FilmStrip from "./FilmStrip";
@@ -14,10 +14,6 @@ var remoteVideos = {};
14 14
 var localVideoThumbnail = null;
15 15
 
16 16
 var currentDominantSpeaker = null;
17
-var localLastNCount = config.channelLastN;
18
-var localLastNSet = [];
19
-var lastNEndpointsCache = [];
20
-var lastNPickupId = null;
21 17
 
22 18
 var eventEmitter = null;
23 19
 
@@ -60,8 +56,6 @@ function onContactClicked (id) {
60 56
             // let the bridge adjust its lastN set for myjid and store
61 57
             // the pinned user in the lastNPickupId variable to be
62 58
             // picked up later by the lastN changed event handler.
63
-
64
-            lastNPickupId = id;
65 59
             eventEmitter.emit(UIEvents.PINNED_ENDPOINT, remoteVideo, true);
66 60
         }
67 61
     }
@@ -114,8 +108,6 @@ var VideoLayout = {
114 108
         // the local video thumb maybe one pixel
115 109
         this.resizeThumbnails(false, true);
116 110
 
117
-        this.lastNCount = config.channelLastN;
118
-
119 111
         this.registerListeners();
120 112
     },
121 113
 
@@ -162,14 +154,6 @@ var VideoLayout = {
162 154
             largeVideo.updateLargeVideoAudioLevel(lvl);
163 155
     },
164 156
 
165
-    isInLastN (resource) {
166
-        return this.lastNCount < 0 || // lastN is disabled
167
-             // lastNEndpoints cache not built yet
168
-            (this.lastNCount > 0 && !lastNEndpointsCache.length) ||
169
-            (lastNEndpointsCache &&
170
-                lastNEndpointsCache.indexOf(resource) !== -1);
171
-    },
172
-
173 157
     changeLocalAudio (stream) {
174 158
         let localAudio = document.getElementById('localAudio');
175 159
         localAudio = stream.attach(localAudio);
@@ -457,13 +441,8 @@ var VideoLayout = {
457 441
             remoteVideo.setVideoType(VIDEO_CONTAINER_TYPE);
458 442
         }
459 443
 
460
-        // In case this is not currently in the last n we don't show it.
461
-        if (localLastNCount && localLastNCount > 0 &&
462
-            FilmStrip.getThumbs().remoteThumbs.length >= localLastNCount + 2) {
463
-            remoteVideo.showPeerContainer('hide');
464
-        } else {
465
-            VideoLayout.resizeThumbnails(false, true);
466
-        }
444
+        VideoLayout.resizeThumbnails(false, true);
445
+
467 446
         // Initialize the view
468 447
         remoteVideo.updateView();
469 448
     },
@@ -713,142 +692,22 @@ var VideoLayout = {
713 692
      * endpoints
714 693
      */
715 694
     onLastNEndpointsChanged (lastNEndpoints, endpointsEnteringLastN) {
716
-        if (this.lastNCount !== lastNEndpoints.length)
717
-            this.lastNCount = lastNEndpoints.length;
718
-
719
-        lastNEndpointsCache = lastNEndpoints;
720
-
721
-        // Say A, B, C, D, E, and F are in a conference and LastN = 3.
722
-        //
723
-        // If LastN drops to, say, 2, because of adaptivity, then E should see
724
-        // thumbnails for A, B and C. A and B are in E's server side LastN set,
725
-        // so E sees them. C is only in E's local LastN set.
726
-        //
727
-        // If F starts talking and LastN = 3, then E should see thumbnails for
728
-        // F, A, B. B gets "ejected" from E's server side LastN set, but it
729
-        // enters E's local LastN ejecting C.
730
-
731
-        // Increase the local LastN set size, if necessary.
732
-        if (this.lastNCount > localLastNCount) {
733
-            localLastNCount = this.lastNCount;
734
-        }
735
-
736
-        // Update the local LastN set preserving the order in which the
737
-        // endpoints appeared in the LastN/local LastN set.
738
-        var nextLocalLastNSet = lastNEndpoints.slice(0);
739
-        for (var i = 0; i < localLastNSet.length; i++) {
740
-            if (nextLocalLastNSet.length >= localLastNCount) {
741
-                break;
742
-            }
743
-
744
-            var resourceJid = localLastNSet[i];
745
-            if (nextLocalLastNSet.indexOf(resourceJid) === -1) {
746
-                nextLocalLastNSet.push(resourceJid);
747
-            }
748
-        }
749
-
750
-        localLastNSet = nextLocalLastNSet;
751
-        var updateLargeVideo = false;
752
-
753
-        // Handle LastN/local LastN changes.
754
-        FilmStrip.getThumbs().remoteThumbs.each(( index, element ) => {
755
-            var resourceJid = getPeerContainerResourceId(element);
756
-            var smallVideo = remoteVideos[resourceJid];
757
-
758
-            // We do not want to process any logic for our own(local) video
759
-            // because the local participant is never in the lastN set.
760
-            // The code of this function might detect that the local participant
761
-            // has been dropped out of the lastN set and will update the large
762
-            // video
763
-            // Detected from avatar tests, where lastN event override
764
-            // local video pinning
765
-            if(APP.conference.isLocalId(resourceJid))
766
-                return;
767
-
768
-            var isReceived = true;
769
-            if (resourceJid &&
770
-                lastNEndpoints.indexOf(resourceJid) < 0 &&
771
-                localLastNSet.indexOf(resourceJid) < 0) {
772
-                logger.log("Remove from last N", resourceJid);
773
-                if (smallVideo)
774
-                    smallVideo.showPeerContainer('hide');
775
-                else if (!APP.conference.isLocalId(resourceJid))
776
-                    logger.error("No remote video for: " + resourceJid);
777
-                isReceived = false;
778
-            } else if (resourceJid &&
779
-                //TOFIX: smallVideo may be undefined
780
-                smallVideo.isVisible() &&
781
-                lastNEndpoints.indexOf(resourceJid) < 0 &&
782
-                localLastNSet.indexOf(resourceJid) >= 0) {
783
-
784
-                // TOFIX: if we're here we already know that the smallVideo
785
-                // exists. Look at the previous FIX above.
786
-                if (smallVideo)
787
-                    smallVideo.showPeerContainer('avatar');
788
-                else if (!APP.conference.isLocalId(resourceJid))
789
-                    logger.error("No remote video for: " + resourceJid);
790
-                isReceived = false;
791
-            }
792
-
793
-            if (!isReceived) {
794
-                // resourceJid has dropped out of the server side lastN set, so
795
-                // it is no longer being received. If resourceJid was being
796
-                // displayed in the large video we have to switch to another
797
-                // user.
798
-                if (!updateLargeVideo &&
799
-                    this.isCurrentlyOnLarge(resourceJid)) {
800
-                    updateLargeVideo = true;
801
-                }
802
-            }
803
-        });
804
-
805
-        if (!endpointsEnteringLastN || endpointsEnteringLastN.length < 0)
806
-            endpointsEnteringLastN = lastNEndpoints;
807
-
808
-        if (endpointsEnteringLastN && endpointsEnteringLastN.length > 0) {
809
-            endpointsEnteringLastN.forEach(function (resourceJid) {
810
-
811
-                var remoteVideo = remoteVideos[resourceJid];
812
-                if (remoteVideo)
813
-                    remoteVideo.showPeerContainer('show');
814 695
 
815
-                if (!remoteVideo.isVisible()) {
816
-                    logger.log("Add to last N", resourceJid);
817
-
818
-                    remoteVideo.addRemoteStreamElement(remoteVideo.videoStream);
819
-
820
-                    if (lastNPickupId == resourceJid) {
821
-                        // Clean up the lastN pickup id.
822
-                        lastNPickupId = null;
823
-
824
-                        VideoLayout.handleVideoThumbClicked(resourceJid);
825
-
826
-                        updateLargeVideo = false;
696
+        Object.keys(remoteVideos).forEach(
697
+            id => {
698
+                if (lastNEndpoints.length > 0
699
+                    && lastNEndpoints.indexOf(id) < 0
700
+                    || endpointsEnteringLastN.length > 0
701
+                        && endpointsEnteringLastN.indexOf(id) > 0) {
702
+
703
+                    let remoteVideo = (id) ? remoteVideos[id] : null;
704
+                    if (remoteVideo) {
705
+                        remoteVideo.updateView();
706
+                        if (remoteVideo.isCurrentlyOnLargeVideo())
707
+                            this.updateLargeVideo(id);
827 708
                     }
828
-                    remoteVideo.waitForPlayback(
829
-                        remoteVideo.selectVideoElement()[0],
830
-                        remoteVideo.videoStream);
831 709
                 }
832 710
             });
833
-        }
834
-
835
-        // The endpoint that was being shown in the large video has dropped out
836
-        // of the lastN set and there was no lastN pickup jid. We need to update
837
-        // the large video now.
838
-
839
-        if (updateLargeVideo) {
840
-            var resource;
841
-            // Find out which endpoint to show in the large video.
842
-            for (i = 0; i < lastNEndpoints.length; i++) {
843
-                resource = lastNEndpoints[i];
844
-                if (!resource || APP.conference.isLocalId(resource))
845
-                    continue;
846
-
847
-                // videoSrcToSsrc needs to be update for this call to succeed.
848
-                this.updateLargeVideo(resource);
849
-                break;
850
-            }
851
-        }
852 711
     },
853 712
 
854 713
     /**

Loading…
取消
儲存