Przeglądaj źródła

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 lat temu
rodzic
commit
704e14f008

+ 15
- 1
conference.js Wyświetl plik

1283
 */
1283
 */
1284
         room.on(
1284
         room.on(
1285
             ConferenceEvents.LAST_N_ENDPOINTS_CHANGED, (ids, enteringIds) => {
1285
             ConferenceEvents.LAST_N_ENDPOINTS_CHANGED, (ids, enteringIds) => {
1286
-            APP.UI.handleLastNEndpoints(ids, enteringIds);
1286
+                APP.UI.handleLastNEndpoints(ids, enteringIds);
1287
         });
1287
         });
1288
+
1288
         room.on(
1289
         room.on(
1289
             ConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
1290
             ConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
1290
             (id, isActive) => {
1291
             (id, isActive) => {
1940
      */
1941
      */
1941
     removeListener (eventName, listener) {
1942
     removeListener (eventName, listener) {
1942
         eventEmitter.removeListener(eventName, listener);
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 Wyświetl plik

382
         "FETCH_SESSION_ID": "Obtaining session-id...",
382
         "FETCH_SESSION_ID": "Obtaining session-id...",
383
         "GOT_SESSION_ID": "Obtaining session-id... Done",
383
         "GOT_SESSION_ID": "Obtaining session-id... Done",
384
         "GET_SESSION_ID_ERROR": "Get session-id error: __code__",
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
     "recording":
389
     "recording":
388
     {
390
     {

+ 11
- 11
modules/UI/videolayout/ConnectionIndicator.js Wyświetl plik

348
  * the user is having connectivity issues.
348
  * the user is having connectivity issues.
349
  */
349
  */
350
 ConnectionIndicator.prototype.updateConnectionStatusIndicator
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 Wyświetl plik

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

+ 13
- 58
modules/UI/videolayout/RemoteVideo.js Wyświetl plik

460
  * @private
460
  * @private
461
  */
461
  */
462
 RemoteVideo.prototype._figureOutMutedWhileDisconnected
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
  */
554
  */
555
 RemoteVideo.prototype.updateView = function () {
555
 RemoteVideo.prototype.updateView = function () {
556
 
556
 
557
-    this.updateConnectionStatusIndicator(
558
-        null /* will obtain the status from 'conference' */);
557
+    this.updateConnectionStatusIndicator();
559
 
558
 
560
     // This must be called after 'updateConnectionStatusIndicator' because it
559
     // This must be called after 'updateConnectionStatusIndicator' because it
561
     // affects the display mode by modifying 'mutedWhileDisconnected' flag
560
     // affects the display mode by modifying 'mutedWhileDisconnected' flag
564
 
563
 
565
 /**
564
 /**
566
  * Updates the UI to reflect user's connectivity status.
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
     logger.debug(this.id + " thumbnail is connection active ? " + isActive);
575
     logger.debug(this.id + " thumbnail is connection active ? " + isActive);
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
 RemoteVideo.prototype.updateResolution = function (resolution) {
696
 RemoteVideo.prototype.updateResolution = function (resolution) {
742
     if (this.connectionIndicator) {
697
     if (this.connectionIndicator) {
743
         this.connectionIndicator.updateResolution(resolution);
698
         this.connectionIndicator.updateResolution(resolution);

+ 2
- 2
modules/UI/videolayout/SmallVideo.js Wyświetl plik

1
-/* global $, JitsiMeetJS, interfaceConfig */
1
+/* global $, APP, JitsiMeetJS, interfaceConfig */
2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
3
 
3
 
4
 import Avatar from "../avatar/Avatar";
4
 import Avatar from "../avatar/Avatar";
446
 SmallVideo.prototype.isVideoPlayable = function() {
446
 SmallVideo.prototype.isVideoPlayable = function() {
447
     return this.videoStream // Is there anything to display ?
447
     return this.videoStream // Is there anything to display ?
448
         && !this.isVideoMuted && !this.videoStream.isMuted() // Muted ?
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 Wyświetl plik

1
-/* global config, APP, $, interfaceConfig */
1
+/* global APP, $, interfaceConfig */
2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
3
 
3
 
4
 import FilmStrip from "./FilmStrip";
4
 import FilmStrip from "./FilmStrip";
14
 var localVideoThumbnail = null;
14
 var localVideoThumbnail = null;
15
 
15
 
16
 var currentDominantSpeaker = null;
16
 var currentDominantSpeaker = null;
17
-var localLastNCount = config.channelLastN;
18
-var localLastNSet = [];
19
-var lastNEndpointsCache = [];
20
-var lastNPickupId = null;
21
 
17
 
22
 var eventEmitter = null;
18
 var eventEmitter = null;
23
 
19
 
60
             // let the bridge adjust its lastN set for myjid and store
56
             // let the bridge adjust its lastN set for myjid and store
61
             // the pinned user in the lastNPickupId variable to be
57
             // the pinned user in the lastNPickupId variable to be
62
             // picked up later by the lastN changed event handler.
58
             // picked up later by the lastN changed event handler.
63
-
64
-            lastNPickupId = id;
65
             eventEmitter.emit(UIEvents.PINNED_ENDPOINT, remoteVideo, true);
59
             eventEmitter.emit(UIEvents.PINNED_ENDPOINT, remoteVideo, true);
66
         }
60
         }
67
     }
61
     }
114
         // the local video thumb maybe one pixel
108
         // the local video thumb maybe one pixel
115
         this.resizeThumbnails(false, true);
109
         this.resizeThumbnails(false, true);
116
 
110
 
117
-        this.lastNCount = config.channelLastN;
118
-
119
         this.registerListeners();
111
         this.registerListeners();
120
     },
112
     },
121
 
113
 
162
             largeVideo.updateLargeVideoAudioLevel(lvl);
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
     changeLocalAudio (stream) {
157
     changeLocalAudio (stream) {
174
         let localAudio = document.getElementById('localAudio');
158
         let localAudio = document.getElementById('localAudio');
175
         localAudio = stream.attach(localAudio);
159
         localAudio = stream.attach(localAudio);
457
             remoteVideo.setVideoType(VIDEO_CONTAINER_TYPE);
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
         // Initialize the view
446
         // Initialize the view
468
         remoteVideo.updateView();
447
         remoteVideo.updateView();
469
     },
448
     },
713
      * endpoints
692
      * endpoints
714
      */
693
      */
715
     onLastNEndpointsChanged (lastNEndpoints, endpointsEnteringLastN) {
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
     /**

Ładowanie…
Anuluj
Zapisz