Преглед на файлове

Merge pull request #539 from jitsi/feat-local-remote-relayed

Treats local/remote relayed addresses differently.
dev1
George Politis преди 8 години
родител
ревизия
ff22221a15
променени са 4 файла, в които са добавени 161 реда и са изтрити 98 реда
  1. 33
    2
      JitsiConference.js
  2. 99
    59
      modules/statistics/AvgRTPStatsReporter.js
  3. 17
    36
      modules/statistics/RTPStatsCollector.js
  4. 12
    1
      modules/xmpp/JingleSessionPC.js

+ 33
- 2
JitsiConference.js Целия файл

2091
  */
2091
  */
2092
 JitsiConference.prototype._onIceConnectionEstablished
2092
 JitsiConference.prototype._onIceConnectionEstablished
2093
 = function(jingleSession) {
2093
 = function(jingleSession) {
2094
+
2095
+    if (this.p2pJingleSession !== null) {
2096
+        // store the establishment time of the p2p session as a field of the
2097
+        // JitsiConference because the p2pJingleSession might get disposed (thus
2098
+        // the value is lost).
2099
+        this.p2pEstablishmentDuration
2100
+            = this.p2pJingleSession.establishmentDuration;
2101
+    }
2102
+
2103
+    if (this.jvbJingleSession !== null) {
2104
+        this.jvbEstablishmentDuration
2105
+            = this.jvbJingleSession.establishmentDuration;
2106
+    }
2107
+
2108
+    let done = false;
2094
     const forceJVB121Ratio = this.options.config.forceJVB121Ratio;
2109
     const forceJVB121Ratio = this.options.config.forceJVB121Ratio;
2095
 
2110
 
2096
     // We don't care about the JVB case, there's nothing to be done
2111
     // We don't care about the JVB case, there's nothing to be done
2097
     if (!jingleSession.isP2P) {
2112
     if (!jingleSession.isP2P) {
2098
-        return;
2113
+        done = true;
2099
     } else if (this.p2pJingleSession !== jingleSession) {
2114
     } else if (this.p2pJingleSession !== jingleSession) {
2100
         logger.error('CONNECTION_ESTABLISHED - wrong P2P session instance ?!');
2115
         logger.error('CONNECTION_ESTABLISHED - wrong P2P session instance ?!');
2101
 
2116
 
2102
-        return;
2117
+        done = true;
2103
     } else if (!jingleSession.isInitiator
2118
     } else if (!jingleSession.isInitiator
2104
         && typeof forceJVB121Ratio === 'number'
2119
         && typeof forceJVB121Ratio === 'number'
2105
         && Math.random() < forceJVB121Ratio) {
2120
         && Math.random() < forceJVB121Ratio) {
2107
         Statistics.analytics.addPermanentProperties({ forceJvb121: true });
2122
         Statistics.analytics.addPermanentProperties({ forceJvb121: true });
2108
         this._stopP2PSession('decline', 'force JVB121');
2123
         this._stopP2PSession('decline', 'force JVB121');
2109
 
2124
 
2125
+        done = true;
2126
+    }
2127
+
2128
+    if (!isNaN(this.p2pEstablishmentDuration)
2129
+        && !isNaN(this.jvbEstablishmentDuration)) {
2130
+        const establishmentDurationDiff
2131
+            = this.p2pEstablishmentDuration - this.jvbEstablishmentDuration;
2132
+
2133
+        Statistics.analytics.sendEvent({
2134
+            'name': 'ice.establishmentDurationDiff',
2135
+            'value': establishmentDurationDiff
2136
+        });
2137
+    }
2138
+
2139
+    if (done) {
2140
+
2110
         return;
2141
         return;
2111
     }
2142
     }
2112
 
2143
 

+ 99
- 59
modules/statistics/AvgRTPStatsReporter.js Целия файл

17
  *
17
  *
18
  * {
18
  * {
19
  *   p2p: true,
19
  *   p2p: true,
20
- *   conferenceSize: 1,
21
- *   relayed: true,
20
+ *   conferenceSize: 2,
21
+ *   localCandidateType: "relay",
22
+ *   remoteCandidateType: "relay",
23
+ *   transportType: "udp",
22
  *
24
  *
23
  *   "stat_avg_rtt": {
25
  *   "stat_avg_rtt": {
24
  *     value: 200,
26
  *     value: 200,
92
      * Appends the report to the analytics "data" object. The object will be
94
      * Appends the report to the analytics "data" object. The object will be
93
      * set under <tt>prefix</tt> + {@link this.name} key.
95
      * set under <tt>prefix</tt> + {@link this.name} key.
94
      * @param {Object} report the analytics "data" object
96
      * @param {Object} report the analytics "data" object
95
-     * @param {Object} extra properties to append to the analytics "data" object
96
      */
97
      */
97
-    appendReport(report, props) {
98
-
99
-        const reportValue = {
98
+    appendReport(report) {
99
+        report[this.name] = {
100
             value: this.calculate(),
100
             value: this.calculate(),
101
             samples: this.samples
101
             samples: this.samples
102
         };
102
         };
103
-
104
-        Object.assign(reportValue, props);
105
-
106
-        report[this.name] = reportValue;
107
     }
103
     }
108
 
104
 
109
     /**
105
     /**
125
 class ConnectionAvgStats {
121
 class ConnectionAvgStats {
126
     /**
122
     /**
127
      * Creates new <tt>ConnectionAvgStats</tt>
123
      * Creates new <tt>ConnectionAvgStats</tt>
128
-     * @param {JitsiConference} conference
124
+     * @param {AvgRTPStatsReporter} avgRtpStatsReporter
129
      * @param {boolean} isP2P
125
      * @param {boolean} isP2P
130
      * @param {number} n the number of samples, before arithmetic mean is to be
126
      * @param {number} n the number of samples, before arithmetic mean is to be
131
      * calculated and values submitted to the analytics module.
127
      * calculated and values submitted to the analytics module.
132
      */
128
      */
133
-    constructor(conference, isP2P, n) {
129
+    constructor(avgRtpStatsReporter, isP2P, n) {
134
         /**
130
         /**
135
          * Is this instance for JVB or P2P connection ?
131
          * Is this instance for JVB or P2P connection ?
136
          * @type {boolean}
132
          * @type {boolean}
175
          * @type {JitsiConference}
171
          * @type {JitsiConference}
176
          * @private
172
          * @private
177
          */
173
          */
178
-        this._conference = conference;
174
+        this._avgRtpStatsReporter = avgRtpStatsReporter;
175
+
176
+        /**
177
+         * The latest average E2E RTT for the JVB connection only.
178
+         *
179
+         * This is used only when {@link ConnectionAvgStats.isP2P} equals to
180
+         * <tt>false</tt>.
181
+         *
182
+         * @type {number}
183
+         */
184
+        this._avgEnd2EndRTT = undefined;
179
 
185
 
180
         this._onConnectionStats = (tpc, stats) => {
186
         this._onConnectionStats = (tpc, stats) => {
181
             if (this.isP2P === tpc.isP2P) {
187
             if (this.isP2P === tpc.isP2P) {
182
                 this._calculateAvgStats(stats);
188
                 this._calculateAvgStats(stats);
183
             }
189
             }
184
         };
190
         };
191
+
192
+        const conference = avgRtpStatsReporter._conference;
193
+
185
         conference.statistics.addConnectionStatsListener(
194
         conference.statistics.addConnectionStatsListener(
186
             this._onConnectionStats);
195
             this._onConnectionStats);
187
 
196
 
219
 
228
 
220
         if (this._sampleIdx >= this._n) {
229
         if (this._sampleIdx >= this._n) {
221
             if (RTCBrowserType.supportsRTTStatistics()) {
230
             if (RTCBrowserType.supportsRTTStatistics()) {
222
-                const batchReport = { };
223
-                const props = {
224
-                    relayed: data.relayed,
231
+                const conference = this._avgRtpStatsReporter._conference;
232
+
233
+                const batchReport = {
225
                     p2p: this.isP2P,
234
                     p2p: this.isP2P,
226
-                    size: this._conference.getParticipantCount()
235
+                    size: conference.getParticipantCount()
227
                 };
236
                 };
228
 
237
 
229
-                this._avgRTT.appendReport(batchReport, props);
238
+                if (data.transport && data.transport.length) {
239
+                    Object.assign(batchReport, {
240
+                        localCandidateType:
241
+                            data.transport[0].localCandidateType,
242
+                        remoteCandidateType:
243
+                            data.transport[0].remoteCandidateType,
244
+                        transportType: data.transport[0].type
245
+                    });
246
+                }
230
 
247
 
231
-                // Report end to end RTT only for JVB
232
-                if (!this.isP2P) {
233
-                    const avgRemoteRTT = this._calculateAvgRemoteRTT();
234
-                    const avgLocalRTT = this._avgRTT.calculate();
248
+                this._avgRTT.appendReport(batchReport);
235
 
249
 
236
-                    if (!isNaN(avgLocalRTT) && !isNaN(avgRemoteRTT)) {
237
-                        // eslint-disable-next-line camelcase
238
-                        const reportValue = {
239
-                            value: avgLocalRTT + avgRemoteRTT
250
+                if (this.isP2P) {
251
+                    // Report RTT diff only for P2P.
252
+                    const jvbEnd2EndRTT = this
253
+                        ._avgRtpStatsReporter.jvbStatsMonitor._avgEnd2EndRTT;
254
+
255
+                    if (!isNaN(jvbEnd2EndRTT)) {
256
+                        const avgRTTDiff
257
+                            = this._avgRTT.calculate() - jvbEnd2EndRTT;
258
+
259
+                        // eslint-disable-next-line dot-notation
260
+                        batchReport['stat_avg_rtt_diff'] = {
261
+                            value: avgRTTDiff
240
                         };
262
                         };
263
+                    }
264
+                } else {
265
+                    // Report end to end RTT only for JVB.
266
+                    const avgRemoteRTT = this._calculateAvgRemoteRTT();
267
+                    const avgLocalRTT = this._avgRTT.calculate();
241
 
268
 
242
-                        Object.assign(reportValue, props);
269
+                    this._avgEnd2EndRTT = avgLocalRTT + avgRemoteRTT;
243
 
270
 
271
+                    if (!isNaN(avgLocalRTT) && !isNaN(avgRemoteRTT)) {
244
                         // eslint-disable-next-line dot-notation
272
                         // eslint-disable-next-line dot-notation
245
-                        batchReport['stat_avg_end2endrtt'] = reportValue;
273
+                        batchReport['stat_avg_end2endrtt'] = {
274
+                            value: this._avgEnd2EndRTT
275
+                        };
246
                     }
276
                     }
247
                 }
277
                 }
248
 
278
 
317
      *
347
      *
318
      */
348
      */
319
     dispose() {
349
     dispose() {
320
-        this._conference.statistics.removeConnectionStatsListener(
350
+
351
+        const conference = this._avgRtpStatsReporter._conference;
352
+
353
+        conference.statistics.removeConnectionStatsListener(
321
             this._onConnectionStats);
354
             this._onConnectionStats);
322
         if (!this.isP2P) {
355
         if (!this.isP2P) {
323
-            this._conference.off(
356
+            conference.off(
324
                 ConnectionQualityEvents.REMOTE_STATS_UPDATED,
357
                 ConnectionQualityEvents.REMOTE_STATS_UPDATED,
325
                 this._onRemoteStatsUpdated);
358
                 this._onRemoteStatsUpdated);
326
-            this._conference.off(
359
+            conference.off(
327
                 ConferenceEvents.USER_LEFT,
360
                 ConferenceEvents.USER_LEFT,
328
                 this._onUserLeft);
361
                 this._onUserLeft);
329
         }
362
         }
519
             this._onJvb121StatusChanged);
552
             this._onJvb121StatusChanged);
520
 
553
 
521
         this.jvbStatsMonitor
554
         this.jvbStatsMonitor
522
-            = new ConnectionAvgStats(conference, false /* JVB */, n);
555
+            = new ConnectionAvgStats(this, false /* JVB */, n);
523
 
556
 
524
         this.p2pStatsMonitor
557
         this.p2pStatsMonitor
525
-            = new ConnectionAvgStats(conference, true /* P2P */, n);
558
+            = new ConnectionAvgStats(this, true /* P2P */, n);
526
     }
559
     }
527
 
560
 
528
     /**
561
     /**
533
      */
566
      */
534
     _calculateAvgStats(data) {
567
     _calculateAvgStats(data) {
535
 
568
 
569
+        if (!data) {
570
+            logger.error('No stats');
571
+
572
+            return;
573
+        }
574
+
536
         const isP2P = this._conference.isP2PActive();
575
         const isP2P = this._conference.isP2PActive();
537
         const confSize = this._conference.getParticipantCount();
576
         const confSize = this._conference.getParticipantCount();
538
-        const props = {
539
-            relayed: data.relayed,
540
-            p2p: isP2P,
541
-            size: confSize
542
-        };
543
 
577
 
544
         if (!isP2P && confSize < 2) {
578
         if (!isP2P && confSize < 2) {
545
 
579
 
556
             }
590
             }
557
         } */
591
         } */
558
 
592
 
559
-        if (!data) {
560
-            logger.error('No stats');
561
-
562
-            return;
563
-        }
564
-
565
         const bitrate = data.bitrate;
593
         const bitrate = data.bitrate;
566
         const bandwidth = data.bandwidth;
594
         const bandwidth = data.bandwidth;
567
         const packetLoss = data.packetLoss;
595
         const packetLoss = data.packetLoss;
621
         this._sampleIdx += 1;
649
         this._sampleIdx += 1;
622
 
650
 
623
         if (this._sampleIdx >= this._n) {
651
         if (this._sampleIdx >= this._n) {
624
-            const batchReport = { };
625
 
652
 
626
-            this._avgAudioBitrateUp.appendReport(batchReport, props);
627
-            this._avgAudioBitrateDown.appendReport(batchReport, props);
653
+            const batchReport = {
654
+                p2p: isP2P,
655
+                size: confSize
656
+            };
657
+
658
+            if (data.transport && data.transport.length) {
659
+                Object.assign(batchReport, {
660
+                    localCandidateType: data.transport[0].localCandidateType,
661
+                    remoteCandidateType: data.transport[0].remoteCandidateType,
662
+                    transportType: data.transport[0].type
663
+                });
664
+            }
665
+
666
+            this._avgAudioBitrateUp.appendReport(batchReport);
667
+            this._avgAudioBitrateDown.appendReport(batchReport);
628
 
668
 
629
-            this._avgVideoBitrateUp.appendReport(batchReport, props);
630
-            this._avgVideoBitrateDown.appendReport(batchReport, props);
669
+            this._avgVideoBitrateUp.appendReport(batchReport);
670
+            this._avgVideoBitrateDown.appendReport(batchReport);
631
 
671
 
632
             if (RTCBrowserType.supportsBandwidthStatistics()) {
672
             if (RTCBrowserType.supportsBandwidthStatistics()) {
633
-                this._avgBandwidthUp.appendReport(batchReport, props);
634
-                this._avgBandwidthDown.appendReport(batchReport, props);
673
+                this._avgBandwidthUp.appendReport(batchReport);
674
+                this._avgBandwidthDown.appendReport(batchReport);
635
             }
675
             }
636
-            this._avgPacketLossUp.appendReport(batchReport, props);
637
-            this._avgPacketLossDown.appendReport(batchReport, props);
638
-            this._avgPacketLossTotal.appendReport(batchReport, props);
676
+            this._avgPacketLossUp.appendReport(batchReport);
677
+            this._avgPacketLossDown.appendReport(batchReport);
678
+            this._avgPacketLossTotal.appendReport(batchReport);
639
 
679
 
640
-            this._avgRemoteFPS.appendReport(batchReport, props);
680
+            this._avgRemoteFPS.appendReport(batchReport);
641
             if (!isNaN(this._avgRemoteScreenFPS.calculate())) {
681
             if (!isNaN(this._avgRemoteScreenFPS.calculate())) {
642
-                this._avgRemoteScreenFPS.appendReport(batchReport, props);
682
+                this._avgRemoteScreenFPS.appendReport(batchReport);
643
             }
683
             }
644
-            this._avgLocalFPS.appendReport(batchReport, props);
684
+            this._avgLocalFPS.appendReport(batchReport);
645
             if (!isNaN(this._avgLocalScreenFPS.calculate())) {
685
             if (!isNaN(this._avgLocalScreenFPS.calculate())) {
646
-                this._avgLocalScreenFPS.appendReport(batchReport, props);
686
+                this._avgLocalScreenFPS.appendReport(batchReport);
647
             }
687
             }
648
 
688
 
649
-            this._avgCQ.appendReport(batchReport, props);
689
+            this._avgCQ.appendReport(batchReport);
650
 
690
 
651
             Statistics.analytics.sendEvent(AVG_RTP_STATS_EVENT, batchReport);
691
             Statistics.analytics.sendEvent(AVG_RTP_STATS_EVENT, batchReport);
652
 
692
 

+ 17
- 36
modules/statistics/RTPStatsCollector.js Целия файл

46
     'googFrameRateSent': 'googFrameRateSent',
46
     'googFrameRateSent': 'googFrameRateSent',
47
     'audioInputLevel': 'audioInputLevel',
47
     'audioInputLevel': 'audioInputLevel',
48
     'audioOutputLevel': 'audioOutputLevel',
48
     'audioOutputLevel': 'audioOutputLevel',
49
-    'currentRoundTripTime': 'googRtt'
49
+    'currentRoundTripTime': 'googRtt',
50
+    'remoteCandidateType': 'googRemoteCandidateType',
51
+    'localCandidateType': 'googLocalCandidateType'
50
 };
52
 };
51
 KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_EDGE] = {
53
 KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_EDGE] = {
52
     'sendBandwidth': 'googAvailableSendBandwidth',
54
     'sendBandwidth': 'googAvailableSendBandwidth',
453
     return Math.max(0, value);
455
     return Math.max(0, value);
454
 };
456
 };
455
 
457
 
456
-/**
457
- * Determines whether the local ICE candidate address is a relayed one.
458
- *
459
- * @param {TraceablePeerConnection} rtcPeerConnection the peer connection that
460
- * has the local ICE candidate address.
461
- * @param {Array} localAddressArray an array in the form
462
- * [ ip address, port ].
463
- * @return {boolean} true if the local ICE candidate is a relayed one, otherwise
464
- * false.
465
- */
466
-function isRelayed(rtcPeerConnection, localAddressArray) {
467
-    const localSdpLines
468
-        = rtcPeerConnection.localDescription.sdp.match(/[^\r\n]+/g);
469
-
470
-    for (const idx in localSdpLines) {
471
-        if (localSdpLines[idx].indexOf(localAddressArray[0])
472
-            && localSdpLines[idx].indexOf(localAddressArray[1])) {
473
-            return localSdpLines[idx].indexOf('relay') !== -1;
474
-        }
475
-    }
476
-
477
-    logger
478
-        .warn('The candidate address was not found in the local description.');
479
-
480
-    return false;
481
-}
482
-
483
 /* eslint-disable no-continue */
458
 /* eslint-disable no-continue */
484
 
459
 
485
 /**
460
 /**
517
         } catch (e) { /* not supported*/ }
492
         } catch (e) { /* not supported*/ }
518
 
493
 
519
         if (now.type === 'googCandidatePair') {
494
         if (now.type === 'googCandidatePair') {
520
-            let active, ip, localip, rtt, type;
521
-            let relayed = false;
495
+            let active, ip, localCandidateType, localip,
496
+                remoteCandidateType, rtt, type;
522
 
497
 
523
             try {
498
             try {
499
+                active = getStatValue(now, 'activeConnection');
500
+                if (!active) {
501
+                    continue;
502
+                }
503
+
524
                 ip = getStatValue(now, 'remoteAddress');
504
                 ip = getStatValue(now, 'remoteAddress');
525
                 type = getStatValue(now, 'transportType');
505
                 type = getStatValue(now, 'transportType');
526
                 localip = getStatValue(now, 'localAddress');
506
                 localip = getStatValue(now, 'localAddress');
527
-                relayed = isRelayed(this.peerconnection, localip.split(':'));
528
-                active = getStatValue(now, 'activeConnection');
507
+                localCandidateType = getStatValue(now, 'localCandidateType');
508
+                remoteCandidateType = getStatValue(now, 'remoteCandidateType');
529
                 rtt = this.getNonNegativeStat(now, 'currentRoundTripTime');
509
                 rtt = this.getNonNegativeStat(now, 'currentRoundTripTime');
530
             } catch (e) { /* not supported*/ }
510
             } catch (e) { /* not supported*/ }
531
             if (!ip || !type || !localip || active !== 'true') {
511
             if (!ip || !type || !localip || active !== 'true') {
545
                     type,
525
                     type,
546
                     localip,
526
                     localip,
547
                     p2p: this.peerconnection.isP2P,
527
                     p2p: this.peerconnection.isP2P,
548
-                    relayed,
528
+                    localCandidateType,
529
+                    remoteCandidateType,
549
                     rtt
530
                     rtt
550
                 });
531
                 });
551
             }
532
             }
565
                 ip: `${remote.ipAddress}:${remote.portNumber}`,
546
                 ip: `${remote.ipAddress}:${remote.portNumber}`,
566
                 type: local.transport,
547
                 type: local.transport,
567
                 localip: `${local.ipAddress}:${local.portNumber}`,
548
                 localip: `${local.ipAddress}:${local.portNumber}`,
568
-                relayed: isRelayed(
569
-                    this.peerconnection, [ local.ipAddress, local.portNumber ]),
570
-                p2p: this.peerconnection.isP2P
549
+                p2p: this.peerconnection.isP2P,
550
+                localCandidateType: local.candidateType,
551
+                remoteCandidateType: remote.candidateType
571
             });
552
             });
572
         }
553
         }
573
 
554
 

+ 12
- 1
modules/xmpp/JingleSessionPC.js Целия файл

199
          * @type {boolean}
199
          * @type {boolean}
200
          */
200
          */
201
         this.wasConnected = false;
201
         this.wasConnected = false;
202
+
203
+        /**
204
+         * Keeps track of how long (in ms) it took from ICE start to ICE
205
+         * connect.
206
+         *
207
+         * @type {number}
208
+         */
209
+        this.establishmentDuration = undefined;
202
     }
210
     }
203
 
211
 
204
     /* eslint-enable max-params */
212
     /* eslint-enable max-params */
381
                             this._iceCheckingStartedTimestamp,
389
                             this._iceCheckingStartedTimestamp,
382
                             this._gatheringStartedTimestamp);
390
                             this._gatheringStartedTimestamp);
383
 
391
 
392
+                    this.establishmentDuration = now - iceStarted;
393
+
384
                     Statistics.analytics.sendEvent(
394
                     Statistics.analytics.sendEvent(
385
                         `${eventName}establishmentDuration`,
395
                         `${eventName}establishmentDuration`,
386
                         {
396
                         {
387
-                            value: now - iceStarted
397
+                            value: this.establishmentDuration
388
                         });
398
                         });
389
                     this.wasConnected = true;
399
                     this.wasConnected = true;
390
                     this.room.eventEmitter.emit(
400
                     this.room.eventEmitter.emit(
1187
      */
1197
      */
1188
     onTerminated(reasonCondition, reasonText) {
1198
     onTerminated(reasonCondition, reasonText) {
1189
         this.state = JingleSessionState.ENDED;
1199
         this.state = JingleSessionState.ENDED;
1200
+        this.establishmentDuration = undefined;
1190
 
1201
 
1191
         // Do something with reason and reasonCondition when we start to care
1202
         // Do something with reason and reasonCondition when we start to care
1192
         // this.reasonCondition = reasonCondition;
1203
         // this.reasonCondition = reasonCondition;

Loading…
Отказ
Запис