Procházet zdrojové kódy

Adds peer connection statuses (active/inactive/interrupted/restoring). (#430)

* Adds peer connection statuses (active/inactive/interrupted/restoring).

The peer connection used to be check with is connection active. Adding new states like active, inactive, when the connection was intentionally stopped by the bridge, interrupted due to network problem and restoring, which is the state of a peer which was inactive as being out of lastN and is now entering lastM and eventually will become active.

* Adds timers to track restoring status.

If restoring too long we set the connection status to interrupted. We save timestamps for all participants entering lastN and set the status for the connection to restoring for 5 seconds, or till it goes into active or exits lastN.

* Adds initial connectionStatus of newly created participant - active.

* Fixes some jsdocs.

* Uses symbols for strings and use of map, fixing comments on PR.

* Removes symbols.
dev1
Дамян Минков před 8 roky
rodič
revize
f1e7378952

+ 2
- 2
JitsiConference.js Zobrazit soubor

@@ -15,7 +15,7 @@ import JitsiTrackError from './JitsiTrackError';
15 15
 import * as JitsiTrackErrors from './JitsiTrackErrors';
16 16
 import * as JitsiTrackEvents from './JitsiTrackEvents';
17 17
 import * as MediaType from './service/RTC/MediaType';
18
-import ParticipantConnectionStatus
18
+import ParticipantConnectionStatusHandler
19 19
     from './modules/connectivity/ParticipantConnectionStatus';
20 20
 import RTC from './modules/RTC/RTC';
21 21
 import RTCBrowserType from './modules/RTC/RTCBrowserType';
@@ -206,7 +206,7 @@ JitsiConference.prototype._init = function(options = {}) {
206 206
     }
207 207
 
208 208
     this.participantConnectionStatus
209
-        = new ParticipantConnectionStatus(
209
+        = new ParticipantConnectionStatusHandler(
210 210
                 this.rtc, this,
211 211
                 options.config.peerDisconnectedThroughRtcTimeout);
212 212
     this.participantConnectionStatus.init();

+ 10
- 4
JitsiConferenceEvents.js Zobrazit soubor

@@ -110,11 +110,17 @@ export const MESSAGE_RECEIVED = 'conference.messageReceived';
110 110
 
111 111
 /**
112 112
  * Event fired when JVB sends notification about interrupted/restored user's
113
- * ICE connection status. First argument is the ID of the participant and
114
- * the seconds is a boolean indicating if the connection is currently
115
- * active(true = active, false = interrupted).
113
+ * ICE connection status or we detect local problem with the video track.
114
+ * First argument is the ID of the participant and
115
+ * the seconds is a string indicating if the connection is currently
116
+ * - active - the connection is active
117
+ * - inactive - the connection is inactive, was intentionally interrupted by
118
+ * the bridge
119
+ * - interrupted - a network problem occurred
120
+ * - restoring - the connection was inactive and is restoring now
121
+ *
116 122
  * The current status value can be obtained by calling
117
- * JitsiParticipant.isConnectionActive().
123
+ * JitsiParticipant.getConnectionStatus().
118 124
  */
119 125
 export const PARTICIPANT_CONN_STATUS_CHANGED
120 126
     = 'conference.participant_conn_status_changed';

+ 3
- 0
JitsiMeetJS.js Zobrazit soubor

@@ -18,6 +18,8 @@ import * as JitsiTrackEvents from './JitsiTrackEvents';
18 18
 import Logger from 'jitsi-meet-logger';
19 19
 import * as MediaType from './service/RTC/MediaType';
20 20
 import Resolutions from './service/RTC/Resolutions';
21
+import { ParticipantConnectionStatus }
22
+    from './modules/connectivity/ParticipantConnectionStatus';
21 23
 import RTC from './modules/RTC/RTC';
22 24
 import RTCBrowserType from './modules/RTC/RTCBrowserType';
23 25
 import RTCUIHelper from './modules/RTC/RTCUIHelper';
@@ -90,6 +92,7 @@ const LibJitsiMeet = {
90 92
 
91 93
     JitsiConnection,
92 94
     constants: {
95
+        participantConnectionStatus: ParticipantConnectionStatus,
93 96
         sipVideoGW: VideoSIPGWConstants
94 97
     },
95 98
     events: {

+ 13
- 10
JitsiParticipant.js Zobrazit soubor

@@ -1,5 +1,7 @@
1 1
 /* global Strophe */
2 2
 import * as JitsiConferenceEvents from './JitsiConferenceEvents';
3
+import { ParticipantConnectionStatus }
4
+    from './modules/connectivity/ParticipantConnectionStatus';
3 5
 import * as MediaType from './service/RTC/MediaType';
4 6
 
5 7
 /**
@@ -33,7 +35,7 @@ export default class JitsiParticipant {
33 35
             video: undefined
34 36
         };
35 37
         this._hidden = hidden;
36
-        this._isConnectionActive = true;
38
+        this._connectionStatus = ParticipantConnectionStatus.ACTIVE;
37 39
         this._properties = {};
38 40
     }
39 41
 
@@ -72,22 +74,23 @@ export default class JitsiParticipant {
72 74
 
73 75
     /**
74 76
      * Updates participant's connection status.
75
-     * @param {boolean} isActive true if the user's connection is fine or false
76
-     * when the user is having connectivity issues.
77
+     * @param {string} state the current participant connection state.
78
+     * {@link ParticipantConnectionStatus}.
77 79
      * @private
78 80
      */
79
-    _setIsConnectionActive(isActive) {
80
-        this._isConnectionActive = isActive;
81
+    _setConnectionStatus(status) {
82
+        this._connectionStatus = status;
81 83
     }
82 84
 
83 85
     /**
84
-     * Checks participant's connectivity status.
86
+     * Return participant's connectivity status.
85 87
      *
86
-     * @returns {boolean} true if the connection is currently ok or false when
87
-     * the user is having connectivity issues.
88
+     * @returns {string} the connection status
89
+     * <tt>ParticipantConnectionStatus</tt> of the user.
90
+     * {@link ParticipantConnectionStatus}.
88 91
      */
89
-    isConnectionActive() {
90
-        return this._isConnectionActive;
92
+    getConnectionStatus() {
93
+        return this._connectionStatus;
91 94
     }
92 95
 
93 96
     /**

+ 190
- 16
modules/connectivity/ParticipantConnectionStatus.js Zobrazit soubor

@@ -17,11 +17,56 @@ const logger = getLogger(__filename);
17 17
  */
18 18
 const DEFAULT_RTC_MUTE_TIMEOUT = 2000;
19 19
 
20
+/**
21
+ * The time to wait a track to be restored. Track which was out of lastN
22
+ * should be inactive and when entering lastN it becomes restoring and when
23
+ * data is received from bridge it will become active, but if no data is
24
+ * received for some time we set status of that participant connection to
25
+ * interrupted.
26
+ * @type {number}
27
+ */
28
+const DEFAULT_RESTORING_TIMEOUT = 5000;
29
+
30
+/**
31
+ * Participant connection statuses.
32
+ *
33
+ * @type {{
34
+ *      ACTIVE: string,
35
+ *      INACTIVE: string,
36
+ *      INTERRUPTED: string,
37
+ *      RESTORING: string
38
+ * }}
39
+ */
40
+export const ParticipantConnectionStatus = {
41
+    /**
42
+     * Status indicating that connection is currently active.
43
+     */
44
+    ACTIVE: 'active',
45
+
46
+    /**
47
+     * Status indicating that connection is currently inactive.
48
+     * Inactive means the connection was stopped on purpose from the bridge,
49
+     * like exiting lastN or adaptivity decided to drop video because of not
50
+     * enough bandwidth.
51
+     */
52
+    INACTIVE: 'inactive',
53
+
54
+    /**
55
+     * Status indicating that connection is currently interrupted.
56
+     */
57
+    INTERRUPTED: 'interrupted',
58
+
59
+    /**
60
+     * Status indicating that connection is currently restoring.
61
+     */
62
+    RESTORING: 'restoring'
63
+};
64
+
20 65
 /**
21 66
  * Class is responsible for emitting
22 67
  * JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED events.
23 68
  */
24
-export default class ParticipantConnectionStatus {
69
+export default class ParticipantConnectionStatusHandler {
25 70
     /**
26 71
      * Creates new instance of <tt>ParticipantConnectionStatus</tt>.
27 72
      *
@@ -86,6 +131,27 @@ export default class ParticipantConnectionStatus {
86 131
          */
87 132
         this.rtcMutedTimestamp = { };
88 133
         logger.info(`RtcMuteTimeout set to: ${this.rtcMuteTimeout}`);
134
+
135
+        /**
136
+         * This map holds the timestamps indicating when participant's video
137
+         * entered lastN set. Participants entering lastN will have connection
138
+         * status restoring and when we start receiving video will become
139
+         * active, but if video is not received for certain time
140
+         * {@link DEFAULT_RESTORING_TIMEOUT} that participant connection status
141
+         * will become interrupted.
142
+         *
143
+         * @type {Map<string, number>}
144
+         */
145
+        this.enteredLastNTimestamp = new Map();
146
+
147
+        /**
148
+         * A map of the "endpoint ID"(which corresponds to the resource part
149
+         * of MUC JID(nickname)) to the restoring timeout callback IDs
150
+         * scheduled using window.setTimeout.
151
+         *
152
+         * @type {Map<string, number>}
153
+         */
154
+        this.restoringTimers = new Map();
89 155
     }
90 156
 
91 157
     /**
@@ -135,6 +201,11 @@ export default class ParticipantConnectionStatus {
135 201
             this._onSignallingMuteChanged
136 202
                 = this.onSignallingMuteChanged.bind(this);
137 203
         }
204
+
205
+        this._onLastNChanged = this._onLastNChanged.bind(this);
206
+        this.conference.on(
207
+            JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,
208
+            this._onLastNChanged);
138 209
     }
139 210
 
140 211
     /**
@@ -163,6 +234,10 @@ export default class ParticipantConnectionStatus {
163 234
                 this._onRemoteTrackRemoved);
164 235
         }
165 236
 
237
+        this.conference.off(
238
+            JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,
239
+            this._onLastNChanged);
240
+
166 241
         this.conference.off(
167 242
             JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);
168 243
 
@@ -181,8 +256,8 @@ export default class ParticipantConnectionStatus {
181 256
      * Handles RTCEvents.ENDPOINT_CONN_STATUS_CHANGED triggered when we receive
182 257
      * notification over the data channel from the bridge about endpoint's
183 258
      * connection status update.
184
-     * @param endpointId {string} the endpoint ID(MUC nickname/resource JID)
185
-     * @param isActive {boolean} true if the connection is OK or false otherwise
259
+     * @param {string} endpointId the endpoint ID(MUC nickname/resource JID)
260
+     * @param {boolean} isActive true if the connection is OK or false otherwise
186 261
      */
187 262
     onEndpointConnStatusChanged(endpointId, isActive) {
188 263
 
@@ -199,16 +274,16 @@ export default class ParticipantConnectionStatus {
199 274
     }
200 275
 
201 276
     /**
202
-     *
203
-     * @param participant
277
+     * Changes connection status.
278
+     * @param {JitsiParticipant} participant
204 279
      * @param newStatus
205 280
      */
206 281
     _changeConnectionStatus(participant, newStatus) {
207
-        if (participant.isConnectionActive() !== newStatus) {
282
+        if (participant.getConnectionStatus() !== newStatus) {
208 283
 
209 284
             const endpointId = participant.getId();
210 285
 
211
-            participant._setIsConnectionActive(newStatus);
286
+            participant._setConnectionStatus(newStatus);
212 287
 
213 288
             logger.debug(
214 289
                 `Emit endpoint conn status(${Date.now()}) ${endpointId}: ${
@@ -236,7 +311,7 @@ export default class ParticipantConnectionStatus {
236 311
      * Reset the postponed "connection interrupted" event which was previously
237 312
      * scheduled as a timeout on RTC 'onmute' event.
238 313
      *
239
-     * @param participantId the participant for which the "connection
314
+     * @param {string} participantId the participant for which the "connection
240 315
      * interrupted" timeout was scheduled
241 316
      */
242 317
     clearTimeout(participantId) {
@@ -248,8 +323,8 @@ export default class ParticipantConnectionStatus {
248 323
 
249 324
     /**
250 325
      * Clears the timestamp of the RTC muted event for participant's video track
251
-     * @param participantId the id of the conference participant which is
252
-     * the same as the Colibri endpoint ID of the video channel allocated for
326
+     * @param {string} participantId the id of the conference participant which
327
+     * is the same as the Colibri endpoint ID of the video channel allocated for
253 328
      * the user on the videobridge.
254 329
      */
255 330
     clearRtcMutedTimestamp(participantId) {
@@ -380,18 +455,117 @@ export default class ParticipantConnectionStatus {
380 455
             isConnActiveByJvb = true;
381 456
         }
382 457
 
383
-        const isConnectionActive
384
-            = isConnActiveByJvb
385
-                && (isVideoMuted || (isInLastN && !isVideoTrackFrozen));
458
+        let newState = ParticipantConnectionStatus.INACTIVE;
459
+
460
+        if (isConnActiveByJvb) {
461
+            if (isInLastN) {
462
+                if (isVideoTrackFrozen) {
463
+                    newState = this._isRestoringTimedout(id)
464
+                        ? ParticipantConnectionStatus.INTERRUPTED
465
+                            : ParticipantConnectionStatus.RESTORING;
466
+                } else {
467
+                    newState = ParticipantConnectionStatus.ACTIVE;
468
+                }
469
+            }
470
+        } else {
471
+            // when there is a connection problem signaled from jvb
472
+            // it means no media was flowing for at least 15secs, so everything
473
+            // should be interrupted, when in p2p mode we will never end up here
474
+            newState = ParticipantConnectionStatus.INTERRUPTED;
475
+        }
476
+
477
+        // if the new state is not restoring clear timers and timestamps
478
+        // that we use to track the restoring state
479
+        if (newState !== ParticipantConnectionStatus.RESTORING) {
480
+            this._clearRestoringTimer(id);
481
+        }
386 482
 
387 483
         logger.debug(
388
-            `Figure out conn status, is video muted: ${isVideoMuted
484
+            `Figure out conn status for ${id}, is video muted: ${isVideoMuted
389 485
                  } is active(jvb): ${isConnActiveByJvb
390 486
                  } video track frozen: ${isVideoTrackFrozen
391 487
                  } is in last N: ${isInLastN
392
-                 } => ${isConnectionActive}`);
488
+                 } currentStatus => newStatus: 
489
+                    ${participant.getConnectionStatus()} => ${newState}`);
490
+
491
+        this._changeConnectionStatus(participant, newState);
492
+    }
493
+
494
+    /**
495
+     * On change in Last N set check all leaving and entering participants to
496
+     * change their corresponding statuses.
497
+     *
498
+     * @param {Array<string>} leavingLastN array of ids leaving lastN.
499
+     * @param {Array<string>} enteringLastN array of ids entering lastN.
500
+     * @private
501
+     */
502
+    _onLastNChanged(leavingLastN = [], enteringLastN = []) {
503
+        for (const id of leavingLastN) {
504
+            this.enteredLastNTimestamp.delete(id);
505
+            this._clearRestoringTimer(id);
506
+            this.figureOutConnectionStatus(id);
507
+        }
508
+        for (const id of enteringLastN) {
509
+            // store the timestamp this id is entering lastN
510
+            this.enteredLastNTimestamp.set(id, Date.now());
511
+
512
+            this.figureOutConnectionStatus(id);
513
+        }
514
+    }
515
+
516
+    /**
517
+     * Clears the restoring timer for participant's video track and the
518
+     * timestamp for entering lastN.
519
+     *
520
+     * @param {string} participantId the id of the conference participant which
521
+     * is the same as the Colibri endpoint ID of the video channel allocated for
522
+     * the user on the videobridge.
523
+     */
524
+    _clearRestoringTimer(participantId) {
525
+        const rTimer = this.restoringTimers.get(participantId);
526
+
527
+        if (rTimer) {
528
+            clearTimeout(rTimer);
529
+            this.restoringTimers.delete(participantId);
530
+        }
531
+    }
532
+
533
+    /**
534
+     * Checks whether a track had stayed enough in restoring state, compares
535
+     * current time and the time the track entered in lastN. If it hasn't
536
+     * timedout and there is no timer added, add new timer in order to give it
537
+     * more time to become active or mark it as interrupted on next check.
538
+     *
539
+     * @param {string} participantId the id of the conference participant which
540
+     * is the same as the Colibri endpoint ID of the video channel allocated for
541
+     * the user on the videobridge.
542
+     * @returns {boolean} <tt>true</tt> if the track was in restoring state
543
+     * more than the timeout ({@link DEFAULT_RESTORING_TIMEOUT}.) in order to
544
+     * set its status to interrupted.
545
+     * @private
546
+     */
547
+    _isRestoringTimedout(participantId) {
548
+        const enteredLastNTimestamp
549
+            = this.enteredLastNTimestamp.get(participantId);
550
+
551
+        if (enteredLastNTimestamp
552
+            && (Date.now() - enteredLastNTimestamp)
553
+                >= DEFAULT_RESTORING_TIMEOUT) {
554
+            return true;
555
+        }
556
+
557
+        // still haven't reached timeout, if there is no timer scheduled,
558
+        // schedule one so we can track the restoring state and change it after
559
+        // reaching the timeout
560
+        const rTimer = this.restoringTimers.get(participantId);
561
+
562
+        if (!rTimer) {
563
+            this.restoringTimers.set(participantId, setTimeout(
564
+                () => this.figureOutConnectionStatus(participantId),
565
+                DEFAULT_RESTORING_TIMEOUT));
566
+        }
393 567
 
394
-        this._changeConnectionStatus(participant, isConnectionActive);
568
+        return false;
395 569
     }
396 570
 
397 571
     /**

Načítá se…
Zrušit
Uložit