Browse Source

fix(stats) split stats for camera and screenshare in multi-stream mode (#11475)

* no ssrc when sourceNameSignalingEnabled
* conditionally use source name for stats
* update doc
* always subscribe to participant id
master
pangrr 3 years ago
parent
commit
3fc3a217eb
No account linked to committer's email address

+ 16
- 1
react/features/base/tracks/functions.js View File

@@ -7,7 +7,7 @@ import {
7 7
 import { isMobileBrowser } from '../environment/utils';
8 8
 import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
9 9
 import { MEDIA_TYPE, VIDEO_TYPE, setAudioMuted } from '../media';
10
-import { getVirtualScreenshareParticipantOwnerId } from '../participants';
10
+import { getParticipantByIdOrUndefined, getVirtualScreenshareParticipantOwnerId } from '../participants';
11 11
 import { toState } from '../redux';
12 12
 import {
13 13
     getUserSelectedCameraDeviceId,
@@ -436,6 +436,21 @@ export function getVideoTrackByParticipant(
436 436
     return getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participant.id);
437 437
 }
438 438
 
439
+/**
440
+ * Returns source name for specified participant id.
441
+ *
442
+ * @param {Object} state - The Redux state.
443
+ * @param {string} participantId - Participant ID.
444
+ * @returns {string | undefined}
445
+ */
446
+export function getSourceNameByParticipantId(state, participantId) {
447
+    const participant = getParticipantByIdOrUndefined(state, participantId);
448
+    const tracks = state['features/base/tracks'];
449
+    const track = getVideoTrackByParticipant(tracks, participant);
450
+
451
+    return track?.jitsiTrack?.getSourceName();
452
+}
453
+
439 454
 /**
440 455
  * Returns track of specified media type for specified participant id.
441 456
  *

+ 30
- 1
react/features/connection-indicator/components/AbstractConnectionIndicator.js View File

@@ -30,7 +30,17 @@ export type Props = {
30 30
      * The ID of the participant associated with the displayed connection indication and
31 31
      * stats.
32 32
      */
33
-    participantId: string
33
+    participantId: string,
34
+
35
+    /**
36
+     * The source name of the track.
37
+     */
38
+    _sourceName: string,
39
+
40
+    /**
41
+     * The flag whether source name signaling is enabled.
42
+     */
43
+    _sourceNameSignalingEnabled: string
34 44
 };
35 45
 
36 46
 /**
@@ -87,6 +97,11 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
87 97
     componentDidMount() {
88 98
         statsEmitter.subscribeToClientStats(
89 99
             this.props.participantId, this._onStatsUpdated);
100
+
101
+        if (this.props._sourceNameSignalingEnabled) {
102
+            statsEmitter.subscribeToClientStats(
103
+                this.props._sourceName, this._onStatsUpdated);
104
+        }
90 105
     }
91 106
 
92 107
     /**
@@ -102,6 +117,15 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
102 117
             statsEmitter.subscribeToClientStats(
103 118
                 this.props.participantId, this._onStatsUpdated);
104 119
         }
120
+
121
+        if (this.props._sourceNameSignalingEnabled) {
122
+            if (prevProps._sourceName !== this.props._sourceName) {
123
+                statsEmitter.unsubscribeToClientStats(
124
+                    prevProps._sourceName, this._onStatsUpdated);
125
+                statsEmitter.subscribeToClientStats(
126
+                    this.props._sourceName, this._onStatsUpdated);
127
+            }
128
+        }
105 129
     }
106 130
 
107 131
     /**
@@ -115,6 +139,11 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
115 139
         statsEmitter.unsubscribeToClientStats(
116 140
             this.props.participantId, this._onStatsUpdated);
117 141
 
142
+        if (this.props._sourceNameSignalingEnabled) {
143
+            statsEmitter.unsubscribeToClientStats(
144
+                this.props._sourceName, this._onStatsUpdated);
145
+        }
146
+
118 147
         clearTimeout(this.autoHideTimeout);
119 148
     }
120 149
 

+ 14
- 1
react/features/connection-indicator/components/web/ConnectionIndicator.js View File

@@ -13,6 +13,7 @@ import { Popover } from '../../../base/popover';
13 13
 import { connect } from '../../../base/redux';
14 14
 import {
15 15
     getVirtualScreenshareParticipantTrack,
16
+    getSourceNameByParticipantId,
16 17
     getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
17 18
 import {
18 19
     isParticipantConnectionStatusInactive,
@@ -126,6 +127,16 @@ type Props = AbstractProps & {
126 127
      * Invoked to obtain translated strings.
127 128
      */
128 129
     t: Function,
130
+
131
+    /**
132
+     * The source name of the track.
133
+     */
134
+    _sourceName: string,
135
+
136
+    /**
137
+     * Whether source name signaling is enabled.
138
+     */
139
+    _sourceNameSignalingEnabled: boolean
129 140
 };
130 141
 
131 142
 type State = AbstractState & {
@@ -394,7 +405,9 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
394 405
         _popoverDisabled: state['features/base/config'].connectionIndicators?.disableDetails,
395 406
         _videoTrack: firstVideoTrack,
396 407
         _isConnectionStatusInactive,
397
-        _isConnectionStatusInterrupted
408
+        _isConnectionStatusInterrupted,
409
+        _sourceName: getSourceNameByParticipantId(state, participantId),
410
+        _sourceNameSignalingEnabled: sourceNameSignalingEnabled
398 411
     };
399 412
 }
400 413
 export default translate(connect(_mapStateToProps)(

+ 16
- 3
react/features/connection-indicator/components/web/ConnectionIndicatorContent.js View File

@@ -8,7 +8,7 @@ import { translate } from '../../../base/i18n';
8 8
 import { MEDIA_TYPE } from '../../../base/media';
9 9
 import { getLocalParticipant, getParticipantById } from '../../../base/participants';
10 10
 import { connect } from '../../../base/redux';
11
-import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
11
+import { getSourceNameByParticipantId, getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
12 12
 import { ConnectionStatsTable } from '../../../connection-stats';
13 13
 import { saveLogs } from '../../actions';
14 14
 import {
@@ -131,7 +131,17 @@ type Props = AbstractProps & {
131 131
     /**
132 132
      * Invoked to obtain translated strings.
133 133
      */
134
-    t: Function
134
+    t: Function,
135
+
136
+    /**
137
+     * The source name of the track.
138
+     */
139
+    _sourceName: string,
140
+
141
+    /**
142
+     * Whether source name signaling is enabled.
143
+     */
144
+    _sourceNameSignalingEnabled: boolean
135 145
 };
136 146
 
137 147
 /**
@@ -216,6 +226,7 @@ class ConnectionIndicatorContent extends AbstractConnectionIndicator<Props, Stat
216 226
                 resolution = { resolution }
217 227
                 serverRegion = { serverRegion }
218 228
                 shouldShowMore = { this.state.showMoreStats }
229
+                sourceNameSignalingEnabled = { this.props._sourceNameSignalingEnabled }
219 230
                 transport = { transport }
220 231
                 videoSsrc = { this.props._videoSsrc } />
221 232
         );
@@ -345,7 +356,9 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
345 356
         _isConnectionStatusInterrupted,
346 357
         _isVirtualScreenshareParticipant: sourceNameSignalingEnabled && participant?.isVirtualScreenshareParticipant,
347 358
         _isLocalVideo: participant?.local,
348
-        _region: participant?.region
359
+        _region: participant?.region,
360
+        _sourceName: getSourceNameByParticipantId(state, participantId),
361
+        _sourceNameSignalingEnabled: sourceNameSignalingEnabled
349 362
     };
350 363
 
351 364
     if (conference) {

+ 42
- 20
react/features/connection-stats/components/ConnectionStatsTable.js View File

@@ -161,7 +161,12 @@ type Props = {
161 161
     /**
162 162
      * Statistics related to transports.
163 163
      */
164
-    transport: Array<Object>
164
+    transport: Array<Object>,
165
+
166
+    /**
167
+     * Whether source name signaling is enabled.
168
+     */
169
+    sourceNameSignalingEnabled: boolean
165 170
 };
166 171
 
167 172
 /**
@@ -450,7 +455,7 @@ class ConnectionStatsTable extends Component<Props> {
450 455
      * @returns {ReactElement}
451 456
      */
452 457
     _renderCodecs() {
453
-        const { codec, t } = this.props;
458
+        const { codec, t, sourceNameSignalingEnabled } = this.props;
454 459
 
455 460
         if (!codec) {
456 461
             return;
@@ -458,13 +463,17 @@ class ConnectionStatsTable extends Component<Props> {
458 463
 
459 464
         let codecString;
460 465
 
461
-        // Only report one codec, in case there are multiple for a user.
462
-        Object.keys(codec || {})
463
-            .forEach(ssrc => {
464
-                const { audio, video } = codec[ssrc];
466
+        if (sourceNameSignalingEnabled) {
467
+            codecString = `${codec.audio}, ${codec.video}`;
468
+        } else {
469
+            // Only report one codec, in case there are multiple for a user.
470
+            Object.keys(codec || {})
471
+                .forEach(ssrc => {
472
+                    const { audio, video } = codec[ssrc];
465 473
 
466
-                codecString = `${audio}, ${video}`;
467
-            });
474
+                    codecString = `${audio}, ${video}`;
475
+                });
476
+        }
468 477
 
469 478
         if (!codecString) {
470 479
             codecString = 'N/A';
@@ -585,10 +594,17 @@ class ConnectionStatsTable extends Component<Props> {
585 594
      * @returns {ReactElement}
586 595
      */
587 596
     _renderFrameRate() {
588
-        const { framerate, t } = this.props;
589
-        const frameRateString = Object.keys(framerate || {})
590
-            .map(ssrc => framerate[ssrc])
591
-            .join(', ') || 'N/A';
597
+        const { framerate, t, sourceNameSignalingEnabled } = this.props;
598
+
599
+        let frameRateString;
600
+
601
+        if (sourceNameSignalingEnabled) {
602
+            frameRateString = framerate || 'N/A';
603
+        } else {
604
+            frameRateString = Object.keys(framerate || {})
605
+                .map(ssrc => framerate[ssrc])
606
+                .join(', ') || 'N/A';
607
+        }
592 608
 
593 609
         return (
594 610
             <tr>
@@ -650,14 +666,20 @@ class ConnectionStatsTable extends Component<Props> {
650 666
      * @returns {ReactElement}
651 667
      */
652 668
     _renderResolution() {
653
-        const { resolution, maxEnabledResolution, t } = this.props;
654
-        let resolutionString = Object.keys(resolution || {})
655
-            .map(ssrc => {
656
-                const { width, height } = resolution[ssrc];
657
-
658
-                return `${width}x${height}`;
659
-            })
660
-            .join(', ') || 'N/A';
669
+        const { resolution, maxEnabledResolution, t, sourceNameSignalingEnabled } = this.props;
670
+        let resolutionString;
671
+
672
+        if (sourceNameSignalingEnabled) {
673
+            resolutionString = resolution ? `${resolution.width}x${resolution.height}` : 'N/A';
674
+        } else {
675
+            resolutionString = Object.keys(resolution || {})
676
+                .map(ssrc => {
677
+                    const { width, height } = resolution[ssrc];
678
+
679
+                    return `${width}x${height}`;
680
+                })
681
+                .join(', ') || 'N/A';
682
+        }
661 683
 
662 684
         if (maxEnabledResolution && maxEnabledResolution < 720) {
663 685
             const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution');

+ 66
- 20
react/features/video-menu/components/native/ConnectionStatusComponent.js View File

@@ -7,6 +7,7 @@ import { withTheme } from 'react-native-paper';
7 7
 
8 8
 import { Avatar } from '../../../base/avatar';
9 9
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
10
+import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
10 11
 import { BottomSheet, isDialogOpen, hideDialog } from '../../../base/dialog';
11 12
 import { translate } from '../../../base/i18n';
12 13
 import { IconArrowDownLarge, IconArrowUpLarge } from '../../../base/icons';
@@ -15,6 +16,7 @@ import { BaseIndicator } from '../../../base/react';
15 16
 import { connect } from '../../../base/redux';
16 17
 import { StyleType } from '../../../base/styles';
17 18
 import statsEmitter from '../../../connection-indicator/statsEmitter';
19
+import { getSourceNameByParticipantId } from '../../base/tracks';
18 20
 
19 21
 import styles from './styles';
20 22
 
@@ -64,7 +66,17 @@ export type Props = {
64 66
     /**
65 67
      * Theme used for styles.
66 68
      */
67
-    theme: Object
69
+    theme: Object,
70
+
71
+    /**
72
+     * The source name of the track.
73
+     */
74
+     _sourceName: string,
75
+
76
+    /**
77
+     * Whether source name signaling is enabled.
78
+     */
79
+    _sourceNameSignalingEnabled: boolean
68 80
 }
69 81
 
70 82
 /**
@@ -211,6 +223,11 @@ class ConnectionStatusComponent extends Component<Props, State> {
211 223
     componentDidMount() {
212 224
         statsEmitter.subscribeToClientStats(
213 225
             this.props.participantID, this._onStatsUpdated);
226
+
227
+        if (this.props._sourceNameSignalingEnabled) {
228
+            statsEmitter.subscribeToClientStats(
229
+                this.props._sourceName, this._onStatsUpdated);
230
+        }
214 231
     }
215 232
 
216 233
     /**
@@ -226,6 +243,15 @@ class ConnectionStatusComponent extends Component<Props, State> {
226 243
             statsEmitter.subscribeToClientStats(
227 244
                 this.props.participantID, this._onStatsUpdated);
228 245
         }
246
+
247
+        if (this.props._sourceNameSignalingEnabled) {
248
+            if (prevProps._sourceName !== this.props._sourceName) {
249
+                statsEmitter.unsubscribeToClientStats(
250
+                    prevProps._sourceName, this._onStatsUpdated);
251
+                statsEmitter.subscribeToClientStats(
252
+                    this.props._sourceName, this._onStatsUpdated);
253
+            }
254
+        }
229 255
     }
230 256
 
231 257
     _onStatsUpdated: Object => void;
@@ -280,18 +306,24 @@ class ConnectionStatusComponent extends Component<Props, State> {
280 306
      */
281 307
     _extractResolutionString(stats) {
282 308
         const { framerate, resolution } = stats;
283
-
284
-        const resolutionString = Object.keys(resolution || {})
285
-        .map(ssrc => {
286
-            const { width, height } = resolution[ssrc];
287
-
288
-            return `${width}x${height}`;
289
-        })
290
-        .join(', ') || null;
291
-
292
-        const frameRateString = Object.keys(framerate || {})
293
-            .map(ssrc => framerate[ssrc])
294
-            .join(', ') || null;
309
+        let frameRateString, resolutionString;
310
+
311
+        if (this.props._sourceNameSignalingEnabled) {
312
+            resolutionString = resolution ? `${resolution.width}x${resolution.height}` : null;
313
+            frameRateString = framerate || null;
314
+        } else {
315
+            resolutionString = Object.keys(resolution || {})
316
+                .map(ssrc => {
317
+                    const { width, height } = resolution[ssrc];
318
+
319
+                    return `${width}x${height}`;
320
+                })
321
+                .join(', ') || null;
322
+
323
+            frameRateString = Object.keys(framerate || {})
324
+                .map(ssrc => framerate[ssrc])
325
+                .join(', ') || null;
326
+        }
295 327
 
296 328
         return resolutionString && frameRateString ? `${resolutionString}@${frameRateString}fps` : undefined;
297 329
     }
@@ -341,13 +373,20 @@ class ConnectionStatusComponent extends Component<Props, State> {
341 373
 
342 374
         let codecString;
343 375
 
344
-        // Only report one codec, in case there are multiple for a user.
345
-        Object.keys(codec || {})
346
-            .forEach(ssrc => {
347
-                const { audio, video } = codec[ssrc];
376
+        if (this.props._sourceNameSignalingEnabled) {
377
+            if (codec) {
378
+                codecString = `${codec.audio}, ${codec.video}`;
379
+            }
380
+        } else {
381
+            // Only report one codec, in case there are multiple for a user.
382
+            Object.keys(codec || {})
383
+                .forEach(ssrc => {
384
+                    const { audio, video } = codec[ssrc];
385
+
386
+                    codecString = `${audio}, ${video}`;
387
+                });
388
+        }
348 389
 
349
-                codecString = `${audio}, ${video}`;
350
-            });
351 390
 
352 391
         return codecString;
353 392
     }
@@ -381,6 +420,11 @@ class ConnectionStatusComponent extends Component<Props, State> {
381 420
         statsEmitter.unsubscribeToClientStats(
382 421
             this.props.participantID, this._onStatsUpdated);
383 422
 
423
+        if (this.props._sourceNameSignalingEnabled) {
424
+            statsEmitter.unsubscribeToClientStats(
425
+                this.props._sourceName, this._onStatsUpdated);
426
+        }
427
+
384 428
         if (this.props._isOpen) {
385 429
             this.props.dispatch(hideDialog(ConnectionStatusComponent_));
386 430
 
@@ -430,7 +474,9 @@ function _mapStateToProps(state, ownProps) {
430 474
     return {
431 475
         _bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
432 476
         _isOpen: isDialogOpen(state, ConnectionStatusComponent_),
433
-        _participantDisplayName: getParticipantDisplayName(state, participantID)
477
+        _participantDisplayName: getParticipantDisplayName(state, participantID),
478
+        _sourceNameSignalingEnabled: getSourceNameSignalingFeatureFlag(state),
479
+        _sourceName: getSourceNameByParticipantId(state, ownProps.participantId)
434 480
     };
435 481
 }
436 482
 

Loading…
Cancel
Save