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
 import { isMobileBrowser } from '../environment/utils';
7
 import { isMobileBrowser } from '../environment/utils';
8
 import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
8
 import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
9
 import { MEDIA_TYPE, VIDEO_TYPE, setAudioMuted } from '../media';
9
 import { MEDIA_TYPE, VIDEO_TYPE, setAudioMuted } from '../media';
10
-import { getVirtualScreenshareParticipantOwnerId } from '../participants';
10
+import { getParticipantByIdOrUndefined, getVirtualScreenshareParticipantOwnerId } from '../participants';
11
 import { toState } from '../redux';
11
 import { toState } from '../redux';
12
 import {
12
 import {
13
     getUserSelectedCameraDeviceId,
13
     getUserSelectedCameraDeviceId,
436
     return getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participant.id);
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
  * Returns track of specified media type for specified participant id.
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
      * The ID of the participant associated with the displayed connection indication and
30
      * The ID of the participant associated with the displayed connection indication and
31
      * stats.
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
     componentDidMount() {
97
     componentDidMount() {
88
         statsEmitter.subscribeToClientStats(
98
         statsEmitter.subscribeToClientStats(
89
             this.props.participantId, this._onStatsUpdated);
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
             statsEmitter.subscribeToClientStats(
117
             statsEmitter.subscribeToClientStats(
103
                 this.props.participantId, this._onStatsUpdated);
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
         statsEmitter.unsubscribeToClientStats(
139
         statsEmitter.unsubscribeToClientStats(
116
             this.props.participantId, this._onStatsUpdated);
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
         clearTimeout(this.autoHideTimeout);
147
         clearTimeout(this.autoHideTimeout);
119
     }
148
     }
120
 
149
 

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

13
 import { connect } from '../../../base/redux';
13
 import { connect } from '../../../base/redux';
14
 import {
14
 import {
15
     getVirtualScreenshareParticipantTrack,
15
     getVirtualScreenshareParticipantTrack,
16
+    getSourceNameByParticipantId,
16
     getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
17
     getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
17
 import {
18
 import {
18
     isParticipantConnectionStatusInactive,
19
     isParticipantConnectionStatusInactive,
126
      * Invoked to obtain translated strings.
127
      * Invoked to obtain translated strings.
127
      */
128
      */
128
     t: Function,
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
 type State = AbstractState & {
142
 type State = AbstractState & {
394
         _popoverDisabled: state['features/base/config'].connectionIndicators?.disableDetails,
405
         _popoverDisabled: state['features/base/config'].connectionIndicators?.disableDetails,
395
         _videoTrack: firstVideoTrack,
406
         _videoTrack: firstVideoTrack,
396
         _isConnectionStatusInactive,
407
         _isConnectionStatusInactive,
397
-        _isConnectionStatusInterrupted
408
+        _isConnectionStatusInterrupted,
409
+        _sourceName: getSourceNameByParticipantId(state, participantId),
410
+        _sourceNameSignalingEnabled: sourceNameSignalingEnabled
398
     };
411
     };
399
 }
412
 }
400
 export default translate(connect(_mapStateToProps)(
413
 export default translate(connect(_mapStateToProps)(

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

8
 import { MEDIA_TYPE } from '../../../base/media';
8
 import { MEDIA_TYPE } from '../../../base/media';
9
 import { getLocalParticipant, getParticipantById } from '../../../base/participants';
9
 import { getLocalParticipant, getParticipantById } from '../../../base/participants';
10
 import { connect } from '../../../base/redux';
10
 import { connect } from '../../../base/redux';
11
-import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
11
+import { getSourceNameByParticipantId, getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
12
 import { ConnectionStatsTable } from '../../../connection-stats';
12
 import { ConnectionStatsTable } from '../../../connection-stats';
13
 import { saveLogs } from '../../actions';
13
 import { saveLogs } from '../../actions';
14
 import {
14
 import {
131
     /**
131
     /**
132
      * Invoked to obtain translated strings.
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
                 resolution = { resolution }
226
                 resolution = { resolution }
217
                 serverRegion = { serverRegion }
227
                 serverRegion = { serverRegion }
218
                 shouldShowMore = { this.state.showMoreStats }
228
                 shouldShowMore = { this.state.showMoreStats }
229
+                sourceNameSignalingEnabled = { this.props._sourceNameSignalingEnabled }
219
                 transport = { transport }
230
                 transport = { transport }
220
                 videoSsrc = { this.props._videoSsrc } />
231
                 videoSsrc = { this.props._videoSsrc } />
221
         );
232
         );
345
         _isConnectionStatusInterrupted,
356
         _isConnectionStatusInterrupted,
346
         _isVirtualScreenshareParticipant: sourceNameSignalingEnabled && participant?.isVirtualScreenshareParticipant,
357
         _isVirtualScreenshareParticipant: sourceNameSignalingEnabled && participant?.isVirtualScreenshareParticipant,
347
         _isLocalVideo: participant?.local,
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
     if (conference) {
364
     if (conference) {

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

161
     /**
161
     /**
162
      * Statistics related to transports.
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
      * @returns {ReactElement}
455
      * @returns {ReactElement}
451
      */
456
      */
452
     _renderCodecs() {
457
     _renderCodecs() {
453
-        const { codec, t } = this.props;
458
+        const { codec, t, sourceNameSignalingEnabled } = this.props;
454
 
459
 
455
         if (!codec) {
460
         if (!codec) {
456
             return;
461
             return;
458
 
463
 
459
         let codecString;
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
         if (!codecString) {
478
         if (!codecString) {
470
             codecString = 'N/A';
479
             codecString = 'N/A';
585
      * @returns {ReactElement}
594
      * @returns {ReactElement}
586
      */
595
      */
587
     _renderFrameRate() {
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
         return (
609
         return (
594
             <tr>
610
             <tr>
650
      * @returns {ReactElement}
666
      * @returns {ReactElement}
651
      */
667
      */
652
     _renderResolution() {
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
         if (maxEnabledResolution && maxEnabledResolution < 720) {
684
         if (maxEnabledResolution && maxEnabledResolution < 720) {
663
             const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution');
685
             const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution');

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

7
 
7
 
8
 import { Avatar } from '../../../base/avatar';
8
 import { Avatar } from '../../../base/avatar';
9
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
9
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
10
+import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
10
 import { BottomSheet, isDialogOpen, hideDialog } from '../../../base/dialog';
11
 import { BottomSheet, isDialogOpen, hideDialog } from '../../../base/dialog';
11
 import { translate } from '../../../base/i18n';
12
 import { translate } from '../../../base/i18n';
12
 import { IconArrowDownLarge, IconArrowUpLarge } from '../../../base/icons';
13
 import { IconArrowDownLarge, IconArrowUpLarge } from '../../../base/icons';
15
 import { connect } from '../../../base/redux';
16
 import { connect } from '../../../base/redux';
16
 import { StyleType } from '../../../base/styles';
17
 import { StyleType } from '../../../base/styles';
17
 import statsEmitter from '../../../connection-indicator/statsEmitter';
18
 import statsEmitter from '../../../connection-indicator/statsEmitter';
19
+import { getSourceNameByParticipantId } from '../../base/tracks';
18
 
20
 
19
 import styles from './styles';
21
 import styles from './styles';
20
 
22
 
64
     /**
66
     /**
65
      * Theme used for styles.
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
     componentDidMount() {
223
     componentDidMount() {
212
         statsEmitter.subscribeToClientStats(
224
         statsEmitter.subscribeToClientStats(
213
             this.props.participantID, this._onStatsUpdated);
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
             statsEmitter.subscribeToClientStats(
243
             statsEmitter.subscribeToClientStats(
227
                 this.props.participantID, this._onStatsUpdated);
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
     _onStatsUpdated: Object => void;
257
     _onStatsUpdated: Object => void;
280
      */
306
      */
281
     _extractResolutionString(stats) {
307
     _extractResolutionString(stats) {
282
         const { framerate, resolution } = stats;
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
         return resolutionString && frameRateString ? `${resolutionString}@${frameRateString}fps` : undefined;
328
         return resolutionString && frameRateString ? `${resolutionString}@${frameRateString}fps` : undefined;
297
     }
329
     }
341
 
373
 
342
         let codecString;
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
         return codecString;
391
         return codecString;
353
     }
392
     }
381
         statsEmitter.unsubscribeToClientStats(
420
         statsEmitter.unsubscribeToClientStats(
382
             this.props.participantID, this._onStatsUpdated);
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
         if (this.props._isOpen) {
428
         if (this.props._isOpen) {
385
             this.props.dispatch(hideDialog(ConnectionStatusComponent_));
429
             this.props.dispatch(hideDialog(ConnectionStatusComponent_));
386
 
430
 
430
     return {
474
     return {
431
         _bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
475
         _bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
432
         _isOpen: isDialogOpen(state, ConnectionStatusComponent_),
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