ソースを参照

feat(multi-stream-support) Replace participant connection status logic with track streaming status (#10934)

master
William Liang 3年前
コミット
05dc018671
コミッターのメールアドレスに関連付けられたアカウントが存在しません

+ 48
- 13
modules/UI/videolayout/LargeVideoManager.js ファイルの表示

@@ -9,10 +9,8 @@ import { Provider } from 'react-redux';
9 9
 import { createScreenSharingIssueEvent, sendAnalytics } from '../../../react/features/analytics';
10 10
 import { Avatar } from '../../../react/features/base/avatar';
11 11
 import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
12
+import { getSourceNameSignalingFeatureFlag } from '../../../react/features/base/config';
12 13
 import { i18next } from '../../../react/features/base/i18n';
13
-import {
14
-    JitsiParticipantConnectionStatus
15
-} from '../../../react/features/base/lib-jitsi-meet';
16 14
 import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media';
17 15
 import {
18 16
     getParticipantById,
@@ -20,6 +18,14 @@ import {
20 18
 } from '../../../react/features/base/participants';
21 19
 import { getTrackByMediaTypeAndParticipant } from '../../../react/features/base/tracks';
22 20
 import { CHAT_SIZE } from '../../../react/features/chat';
21
+import {
22
+    isParticipantConnectionStatusActive,
23
+    isParticipantConnectionStatusInactive,
24
+    isParticipantConnectionStatusInterrupted,
25
+    isTrackStreamingStatusActive,
26
+    isTrackStreamingStatusInactive,
27
+    isTrackStreamingStatusInterrupted
28
+} from '../../../react/features/connection-indicator/functions';
23 29
 import {
24 30
     updateKnownLargeVideoResolution
25 31
 } from '../../../react/features/large-video/actions';
@@ -226,8 +232,20 @@ export default class LargeVideoManager {
226 232
             const state = APP.store.getState();
227 233
             const participant = getParticipantById(state, id);
228 234
             const connectionStatus = participant?.connectionStatus;
229
-            const isVideoRenderable = !isVideoMuted
230
-                && (APP.conference.isLocalId(id) || connectionStatus === JitsiParticipantConnectionStatus.ACTIVE);
235
+
236
+            let isVideoRenderable;
237
+
238
+            if (getSourceNameSignalingFeatureFlag(state)) {
239
+                const videoTrack = getTrackByMediaTypeAndParticipant(
240
+                    state['features/base/tracks'], MEDIA_TYPE.VIDEO, id);
241
+
242
+                isVideoRenderable = !isVideoMuted
243
+                    && (APP.conference.isLocalId(id) || isTrackStreamingStatusActive(videoTrack));
244
+            } else {
245
+                isVideoRenderable = !isVideoMuted
246
+                    && (APP.conference.isLocalId(id) || isParticipantConnectionStatusActive(participant));
247
+            }
248
+
231 249
             const isAudioOnly = APP.conference.isAudioOnly();
232 250
             const showAvatar
233 251
                 = isVideoContainer
@@ -278,8 +296,16 @@ export default class LargeVideoManager {
278 296
                 this.updateLargeVideoAudioLevel(0);
279 297
             }
280 298
 
281
-            const messageKey
282
-                = connectionStatus === JitsiParticipantConnectionStatus.INACTIVE ? 'connection.LOW_BANDWIDTH' : null;
299
+            let messageKey;
300
+
301
+            if (getSourceNameSignalingFeatureFlag(state)) {
302
+                const videoTrack = getTrackByMediaTypeAndParticipant(
303
+                    state['features/base/tracks'], MEDIA_TYPE.VIDEO, id);
304
+
305
+                messageKey = isTrackStreamingStatusInactive(videoTrack) ? 'connection.LOW_BANDWIDTH' : null;
306
+            } else {
307
+                messageKey = isParticipantConnectionStatusInactive(participant) ? 'connection.LOW_BANDWIDTH' : null;
308
+            }
283 309
 
284 310
             // Do not show connection status message in the audio only mode,
285 311
             // because it's based on the video playback status.
@@ -505,13 +531,22 @@ export default class LargeVideoManager {
505 531
     showRemoteConnectionMessage(show) {
506 532
         if (typeof show !== 'boolean') {
507 533
             const participant = getParticipantById(APP.store.getState(), this.id);
508
-            const connStatus = participant?.connectionStatus;
534
+            const state = APP.store.getState();
535
+
536
+            if (getSourceNameSignalingFeatureFlag(state)) {
537
+                const videoTrack = getTrackByMediaTypeAndParticipant(
538
+                    state['features/base/tracks'], MEDIA_TYPE.VIDEO, this.id);
509 539
 
510
-            // eslint-disable-next-line no-param-reassign
511
-            show = !APP.conference.isLocalId(this.id)
512
-                && (connStatus === JitsiParticipantConnectionStatus.INTERRUPTED
513
-                    || connStatus
514
-                        === JitsiParticipantConnectionStatus.INACTIVE);
540
+                // eslint-disable-next-line no-param-reassign
541
+                show = !APP.conference.isLocalId(this.id)
542
+                    && (isTrackStreamingStatusInterrupted(videoTrack)
543
+                        || isTrackStreamingStatusInactive(videoTrack));
544
+            } else {
545
+                // eslint-disable-next-line no-param-reassign
546
+                show = !APP.conference.isLocalId(this.id)
547
+                    && (isParticipantConnectionStatusInterrupted(participant)
548
+                        || isParticipantConnectionStatusInactive(participant));
549
+            }
515 550
         }
516 551
 
517 552
         if (show) {

+ 1
- 0
react/features/base/lib-jitsi-meet/index.js ファイルの表示

@@ -19,6 +19,7 @@ export const JitsiE2ePingEvents = JitsiMeetJS.events.e2eping;
19 19
 export const JitsiMediaDevicesEvents = JitsiMeetJS.events.mediaDevices;
20 20
 export const JitsiParticipantConnectionStatus
21 21
     = JitsiMeetJS.constants.participantConnectionStatus;
22
+export const JitsiTrackStreamingStatus = JitsiMeetJS.constants.trackStreamingStatus;
22 23
 export const JitsiRecordingConstants = JitsiMeetJS.constants.recording;
23 24
 export const JitsiSIPVideoGWStatus = JitsiMeetJS.constants.sipVideoGW;
24 25
 export const JitsiTrackErrors = JitsiMeetJS.errors.track;

+ 20
- 0
react/features/base/tracks/actions.js ファイルの表示

@@ -553,6 +553,26 @@ export function trackVideoTypeChanged(track, videoType) {
553 553
     };
554 554
 }
555 555
 
556
+/**
557
+ * Create an action for when track streaming status changes.
558
+ *
559
+ * @param {(JitsiRemoteTrack)} track - JitsiTrack instance.
560
+ * @param {string} streamingStatus - The new streaming status of the track.
561
+ * @returns {{
562
+ *     type: TRACK_UPDATED,
563
+ *     track: Track
564
+ * }}
565
+ */
566
+export function trackStreamingStatusChanged(track, streamingStatus) {
567
+    return {
568
+        type: TRACK_UPDATED,
569
+        track: {
570
+            jitsiTrack: track,
571
+            streamingStatus
572
+        }
573
+    };
574
+}
575
+
556 576
 /**
557 577
  * Signals passed tracks to be added.
558 578
  *

+ 56
- 58
react/features/connection-indicator/components/web/ConnectionIndicator.js ファイルの表示

@@ -5,12 +5,19 @@ import clsx from 'clsx';
5 5
 import React from 'react';
6 6
 import type { Dispatch } from 'redux';
7 7
 
8
+import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
8 9
 import { translate } from '../../../base/i18n';
9
-import { Icon, IconConnectionActive, IconConnectionInactive } from '../../../base/icons';
10
-import { JitsiParticipantConnectionStatus } from '../../../base/lib-jitsi-meet';
10
+import { MEDIA_TYPE } from '../../../base/media';
11 11
 import { getLocalParticipant, getParticipantById } from '../../../base/participants';
12 12
 import { Popover } from '../../../base/popover';
13 13
 import { connect } from '../../../base/redux';
14
+import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
15
+import {
16
+    isParticipantConnectionStatusInactive,
17
+    isParticipantConnectionStatusInterrupted,
18
+    isTrackStreamingStatusInactive,
19
+    isTrackStreamingStatusInterrupted
20
+} from '../../functions';
14 21
 import AbstractConnectionIndicator, {
15 22
     INDICATOR_DISPLAY_THRESHOLD,
16 23
     type Props as AbstractProps,
@@ -18,6 +25,7 @@ import AbstractConnectionIndicator, {
18 25
 } from '../AbstractConnectionIndicator';
19 26
 
20 27
 import ConnectionIndicatorContent from './ConnectionIndicatorContent';
28
+import { ConnectionIndicatorIcon } from './ConnectionIndicatorIcon';
21 29
 
22 30
 /**
23 31
  * An array of display configurations for the connection indicator and its bars.
@@ -237,17 +245,22 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
237 245
      * @returns {string}
238 246
      */
239 247
     _getConnectionColorClass() {
240
-        const { _connectionStatus } = this.props;
248
+        // TODO We currently do not have logic to emit and handle stats changes for tracks.
241 249
         const { percent } = this.state.stats;
242
-        const { INACTIVE, INTERRUPTED } = JitsiParticipantConnectionStatus;
243 250
 
244
-        if (_connectionStatus === INACTIVE) {
245
-            if (this.props._connectionIndicatorInactiveDisabled) {
251
+        const {
252
+            _isConnectionStatusInactive,
253
+            _isConnectionStatusInterrupted,
254
+            _connectionIndicatorInactiveDisabled
255
+        } = this.props;
256
+
257
+        if (_isConnectionStatusInactive) {
258
+            if (_connectionIndicatorInactiveDisabled) {
246 259
                 return 'status-disabled';
247 260
             }
248 261
 
249 262
             return 'status-other';
250
-        } else if (_connectionStatus === INTERRUPTED) {
263
+        } else if (_isConnectionStatusInterrupted) {
251 264
             return 'status-lost';
252 265
         } else if (typeof percent === 'undefined') {
253 266
             return 'status-high';
@@ -279,12 +292,12 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
279 292
      * @returns {string}
280 293
      */
281 294
     _getVisibilityClass() {
282
-        const { _connectionStatus, classes } = this.props;
295
+        const { _isConnectionStatusInactive, _isConnectionStatusInterrupted, classes } = this.props;
283 296
 
284 297
         return this.state.showIndicator
285 298
             || this.props.alwaysVisible
286
-            || _connectionStatus === JitsiParticipantConnectionStatus.INTERRUPTED
287
-            || _connectionStatus === JitsiParticipantConnectionStatus.INACTIVE
299
+            || _isConnectionStatusInterrupted
300
+            || _isConnectionStatusInactive
288 301
             ? '' : classes.hidden;
289 302
     }
290 303
 
@@ -300,49 +313,6 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
300 313
         this.setState({ popoverVisible: false });
301 314
     }
302 315
 
303
-    /**
304
-     * Creates a ReactElement for displaying an icon that represents the current
305
-     * connection quality.
306
-     *
307
-     * @returns {ReactElement}
308
-     */
309
-    _renderIcon() {
310
-        const colorClass = this._getConnectionColorClass();
311
-
312
-        if (this.props._connectionStatus === JitsiParticipantConnectionStatus.INACTIVE) {
313
-            if (this.props._connectionIndicatorInactiveDisabled) {
314
-                return null;
315
-            }
316
-
317
-            return (
318
-                <span className = 'connection_ninja'>
319
-                    <Icon
320
-                        className = { clsx(this.props.classes.icon, this.props.classes.inactiveIcon, colorClass) }
321
-                        size = { 24 }
322
-                        src = { IconConnectionInactive } />
323
-                </span>
324
-            );
325
-        }
326
-
327
-        let emptyIconWrapperClassName = 'connection_empty';
328
-
329
-        if (this.props._connectionStatus
330
-            === JitsiParticipantConnectionStatus.INTERRUPTED) {
331
-
332
-            // emptyIconWrapperClassName is used by the torture tests to
333
-            // identify lost connection status handling.
334
-            emptyIconWrapperClassName = 'connection_lost';
335
-        }
336
-
337
-        return (
338
-            <span className = { emptyIconWrapperClassName }>
339
-                <Icon
340
-                    className = { clsx(this.props.classes.icon, colorClass) }
341
-                    size = { 12 }
342
-                    src = { IconConnectionActive } />
343
-            </span>
344
-        );
345
-    }
346 316
 
347 317
     _onShowPopover: () => void;
348 318
 
@@ -363,10 +333,25 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
363 333
      * @returns {ReactElement}
364 334
      */
365 335
     _renderIndicator() {
336
+        const {
337
+            _isConnectionStatusInactive,
338
+            _isConnectionStatusInterrupted,
339
+            _connectionIndicatorInactiveDisabled,
340
+            _videoTrack,
341
+            classes,
342
+            iconSize
343
+        } = this.props;
344
+
366 345
         return (
367 346
             <div
368
-                style = {{ fontSize: this.props.iconSize }}>
369
-                {this._renderIcon()}
347
+                style = {{ fontSize: iconSize }}>
348
+                <ConnectionIndicatorIcon
349
+                    classes = { classes }
350
+                    colorClass = { this._getConnectionColorClass() }
351
+                    connectionIndicatorInactiveDisabled = { _connectionIndicatorInactiveDisabled }
352
+                    isConnectionStatusInactive = { _isConnectionStatusInactive }
353
+                    isConnectionStatusInterrupted = { _isConnectionStatusInterrupted }
354
+                    track = { _videoTrack } />
370 355
             </div>
371 356
         );
372 357
     }
@@ -381,14 +366,27 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
381 366
  */
382 367
 export function _mapStateToProps(state: Object, ownProps: Props) {
383 368
     const { participantId } = ownProps;
384
-    const participant
385
-        = participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
369
+    const sourceNameSignalingEnabled = getSourceNameSignalingFeatureFlag(state);
370
+    const firstVideoTrack = getTrackByMediaTypeAndParticipant(
371
+        state['features/base/tracks'], MEDIA_TYPE.VIDEO, participantId);
372
+
373
+    const participant = participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
374
+
375
+    const _isConnectionStatusInactive = sourceNameSignalingEnabled
376
+        ? isTrackStreamingStatusInactive(firstVideoTrack)
377
+        : isParticipantConnectionStatusInactive(participant);
378
+
379
+    const _isConnectionStatusInterrupted = sourceNameSignalingEnabled
380
+        ? isTrackStreamingStatusInterrupted(firstVideoTrack)
381
+        : isParticipantConnectionStatusInterrupted(participant);
386 382
 
387 383
     return {
388 384
         _connectionIndicatorInactiveDisabled:
389 385
         Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
390 386
         _popoverDisabled: state['features/base/config'].connectionIndicators?.disableDetails,
391
-        _connectionStatus: participant?.connectionStatus
387
+        _videoTrack: firstVideoTrack,
388
+        _isConnectionStatusInactive,
389
+        _isConnectionStatusInterrupted
392 390
     };
393 391
 }
394 392
 export default translate(connect(_mapStateToProps)(

+ 27
- 7
react/features/connection-indicator/components/web/ConnectionIndicatorContent.js ファイルの表示

@@ -3,14 +3,20 @@
3 3
 import React from 'react';
4 4
 import type { Dispatch } from 'redux';
5 5
 
6
+import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
6 7
 import { translate } from '../../../base/i18n';
7
-import { JitsiParticipantConnectionStatus } from '../../../base/lib-jitsi-meet';
8 8
 import { MEDIA_TYPE } from '../../../base/media';
9 9
 import { getLocalParticipant, getParticipantById } from '../../../base/participants';
10 10
 import { connect } from '../../../base/redux';
11 11
 import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
12 12
 import { ConnectionStatsTable } from '../../../connection-stats';
13 13
 import { saveLogs } from '../../actions';
14
+import {
15
+    isParticipantConnectionStatusInactive,
16
+    isParticipantConnectionStatusInterrupted,
17
+    isTrackStreamingStatusInactive,
18
+    isTrackStreamingStatusInterrupted
19
+} from '../../functions';
14 20
 import AbstractConnectionIndicator, {
15 21
     INDICATOR_DISPLAY_THRESHOLD,
16 22
     type Props as AbstractProps,
@@ -217,12 +223,14 @@ class ConnectionIndicatorContent extends AbstractConnectionIndicator<Props, Stat
217 223
     _getConnectionStatusTip() {
218 224
         let tipKey;
219 225
 
220
-        switch (this.props._connectionStatus) {
221
-        case JitsiParticipantConnectionStatus.INTERRUPTED:
226
+        const { _isConnectionStatusInactive, _isConnectionStatusInterrupted } = this.props;
227
+
228
+        switch (true) {
229
+        case _isConnectionStatusInterrupted:
222 230
             tipKey = 'connectionindicator.quality.lost';
223 231
             break;
224 232
 
225
-        case JitsiParticipantConnectionStatus.INACTIVE:
233
+        case _isConnectionStatusInactive:
226 234
             tipKey = 'connectionindicator.quality.inactive';
227 235
             break;
228 236
 
@@ -310,17 +318,29 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
310 318
     const conference = state['features/base/conference'].conference;
311 319
     const participant
312 320
         = participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
321
+    const firstVideoTrack = getTrackByMediaTypeAndParticipant(
322
+        state['features/base/tracks'], MEDIA_TYPE.VIDEO, participantId);
323
+    const sourceNameSignalingEnabled = getSourceNameSignalingFeatureFlag(state);
324
+
325
+    const _isConnectionStatusInactive = sourceNameSignalingEnabled
326
+        ? isTrackStreamingStatusInactive(firstVideoTrack)
327
+        : isParticipantConnectionStatusInactive(participant);
328
+
329
+    const _isConnectionStatusInterrupted = sourceNameSignalingEnabled
330
+        ? isTrackStreamingStatusInterrupted(firstVideoTrack)
331
+        : isParticipantConnectionStatusInterrupted(participant);
332
+
313 333
     const props = {
314 334
         _connectionStatus: participant?.connectionStatus,
315 335
         _enableSaveLogs: state['features/base/config'].enableSaveLogs,
316 336
         _disableShowMoreStats: state['features/base/config'].disableShowMoreStats,
317 337
         _isLocalVideo: participant?.local,
318
-        _region: participant?.region
338
+        _region: participant?.region,
339
+        _isConnectionStatusInactive,
340
+        _isConnectionStatusInterrupted
319 341
     };
320 342
 
321 343
     if (conference) {
322
-        const firstVideoTrack = getTrackByMediaTypeAndParticipant(
323
-            state['features/base/tracks'], MEDIA_TYPE.VIDEO, participantId);
324 344
         const firstAudioTrack = getTrackByMediaTypeAndParticipant(
325 345
             state['features/base/tracks'], MEDIA_TYPE.AUDIO, participantId);
326 346
 

+ 110
- 0
react/features/connection-indicator/components/web/ConnectionIndicatorIcon.js ファイルの表示

@@ -0,0 +1,110 @@
1
+// @flow
2
+
3
+import clsx from 'clsx';
4
+import React, { useEffect } from 'react';
5
+import { useDispatch, useSelector } from 'react-redux';
6
+
7
+import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
8
+import { Icon, IconConnectionActive, IconConnectionInactive } from '../../../base/icons';
9
+import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
10
+import { trackStreamingStatusChanged } from '../../../base/tracks';
11
+
12
+type Props = {
13
+
14
+    /**
15
+     * An object containing the CSS classes.
16
+     */
17
+    classes: Object,
18
+
19
+    /**
20
+     * A CSS class that interprets the current connection status as a color.
21
+     */
22
+    colorClass: string,
23
+
24
+    /**
25
+     * Disable/enable inactive indicator.
26
+     */
27
+     connectionIndicatorInactiveDisabled: boolean,
28
+
29
+    /**
30
+     * JitsiTrack instance.
31
+     */
32
+    track: Object,
33
+
34
+    /**
35
+     * Whether or not the connection status is inactive.
36
+     */
37
+    isConnectionStatusInactive: boolean,
38
+
39
+    /**
40
+     * Whether or not the connection status is interrupted.
41
+     */
42
+    isConnectionStatusInterrupted: boolean,
43
+}
44
+
45
+export const ConnectionIndicatorIcon = ({
46
+    classes,
47
+    colorClass,
48
+    connectionIndicatorInactiveDisabled,
49
+    isConnectionStatusInactive,
50
+    isConnectionStatusInterrupted,
51
+    track
52
+}: Props) => {
53
+    const sourceNameSignalingEnabled = useSelector(state => getSourceNameSignalingFeatureFlag(state));
54
+    const dispatch = useDispatch();
55
+    const sourceName = track?.jitsiTrack?.getSourceName?.();
56
+
57
+    const handleTrackStreamingStatusChanged = streamingStatus => {
58
+        dispatch(trackStreamingStatusChanged(track.jitsiTrack, streamingStatus));
59
+    };
60
+
61
+    useEffect(() => {
62
+        if (track && !track.local && sourceNameSignalingEnabled) {
63
+            track.jitsiTrack.on(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, handleTrackStreamingStatusChanged);
64
+
65
+            dispatch(trackStreamingStatusChanged(track.jitsiTrack, track.jitsiTrack.getTrackStreamingStatus?.()));
66
+        }
67
+
68
+        return () => {
69
+            if (track && !track.local && sourceNameSignalingEnabled) {
70
+                track.jitsiTrack.off(
71
+                    JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED,
72
+                    handleTrackStreamingStatusChanged
73
+                );
74
+
75
+                dispatch(trackStreamingStatusChanged(track.jitsiTrack, track.jitsiTrack.getTrackStreamingStatus?.()));
76
+            }
77
+        };
78
+    }, [ sourceName ]);
79
+
80
+    if (isConnectionStatusInactive) {
81
+        if (connectionIndicatorInactiveDisabled) {
82
+            return null;
83
+        }
84
+
85
+        return (
86
+            <span className = 'connection_ninja'>
87
+                <Icon
88
+                    className = { clsx(classes.icon, classes.inactiveIcon, colorClass) }
89
+                    size = { 24 }
90
+                    src = { IconConnectionInactive } />
91
+            </span>
92
+        );
93
+    }
94
+
95
+    let emptyIconWrapperClassName = 'connection_empty';
96
+
97
+    if (isConnectionStatusInterrupted) {
98
+        // emptyIconWrapperClassName is used by the torture tests to identify lost connection status handling.
99
+        emptyIconWrapperClassName = 'connection_lost';
100
+    }
101
+
102
+    return (
103
+        <span className = { emptyIconWrapperClassName }>
104
+            <Icon
105
+                className = { clsx(classes.icon, colorClass) }
106
+                size = { 12 }
107
+                src = { IconConnectionActive } />
108
+        </span>
109
+    );
110
+};

+ 73
- 0
react/features/connection-indicator/functions.js ファイルの表示

@@ -0,0 +1,73 @@
1
+import { JitsiParticipantConnectionStatus, JitsiTrackStreamingStatus } from '../base/lib-jitsi-meet';
2
+
3
+/**
4
+ * Checks if the passed track's streaming status is active.
5
+ *
6
+ * @param {Object} videoTrack - Track reference.
7
+ * @returns {boolean} - Is streaming status active.
8
+ */
9
+export function isTrackStreamingStatusActive(videoTrack) {
10
+    const streamingStatus = videoTrack?.streamingStatus;
11
+
12
+    return streamingStatus === JitsiTrackStreamingStatus.ACTIVE;
13
+}
14
+
15
+/**
16
+ * Checks if the passed track's streaming status is inactive.
17
+ *
18
+ * @param {Object} videoTrack - Track reference.
19
+ * @returns {boolean} - Is streaming status inactive.
20
+ */
21
+export function isTrackStreamingStatusInactive(videoTrack) {
22
+    const streamingStatus = videoTrack?.streamingStatus;
23
+
24
+    return streamingStatus === JitsiTrackStreamingStatus.INACTIVE;
25
+}
26
+
27
+/**
28
+ * Checks if the passed track's streaming status is interrupted.
29
+ *
30
+ * @param {Object} videoTrack - Track reference.
31
+ * @returns {boolean} - Is streaming status interrupted.
32
+ */
33
+export function isTrackStreamingStatusInterrupted(videoTrack) {
34
+    const streamingStatus = videoTrack?.streamingStatus;
35
+
36
+    return streamingStatus === JitsiTrackStreamingStatus.INTERRUPTED;
37
+}
38
+
39
+/**
40
+ * Checks if the passed participant's connecton status is active.
41
+ *
42
+ * @param {Object} participant - Participant reference.
43
+ * @returns {boolean} - Is connection status active.
44
+ */
45
+export function isParticipantConnectionStatusActive(participant) {
46
+    const connectionStatus = participant?.connectionStatus;
47
+
48
+    return connectionStatus === JitsiParticipantConnectionStatus.ACTIVE;
49
+}
50
+
51
+/**
52
+ * Checks if the passed participant's connecton status is inactive.
53
+ *
54
+ * @param {Object} participant - Participant reference.
55
+ * @returns {boolean} - Is connection status inactive.
56
+ */
57
+export function isParticipantConnectionStatusInactive(participant) {
58
+    const connectionStatus = participant?.connectionStatus;
59
+
60
+    return connectionStatus === JitsiParticipantConnectionStatus.INACTIVE;
61
+}
62
+
63
+/**
64
+ * Checks if the passed participant's connecton status is interrupted.
65
+ *
66
+ * @param {Object} participant - Participant reference.
67
+ * @returns {boolean} - Is connection status interrupted.
68
+ */
69
+export function isParticipantConnectionStatusInterrupted(participant) {
70
+    const connectionStatus = participant?.connectionStatus;
71
+
72
+    return connectionStatus === JitsiParticipantConnectionStatus.INTERRUPTED;
73
+}

+ 10
- 4
react/features/filmstrip/functions.web.js ファイルの表示

@@ -1,6 +1,6 @@
1 1
 // @flow
2 2
 
3
-import { JitsiParticipantConnectionStatus } from '../base/lib-jitsi-meet';
3
+import { getSourceNameSignalingFeatureFlag } from '../base/config';
4 4
 import { MEDIA_TYPE } from '../base/media';
5 5
 import {
6 6
     getLocalParticipant,
@@ -15,6 +15,7 @@ import {
15 15
     isLocalTrackMuted,
16 16
     isRemoteTrackMuted
17 17
 } from '../base/tracks/functions';
18
+import { isTrackStreamingStatusActive, isParticipantConnectionStatusActive } from '../connection-indicator/functions';
18 19
 import { LAYOUTS } from '../video-layout';
19 20
 
20 21
 import {
@@ -105,7 +106,7 @@ export function isVideoPlayable(stateful: Object | Function, id: String) {
105 106
     const tracks = state['features/base/tracks'];
106 107
     const participant = id ? getParticipantById(state, id) : getLocalParticipant(state);
107 108
     const isLocal = participant?.local ?? true;
108
-    const { connectionStatus } = participant || {};
109
+
109 110
     const videoTrack
110 111
         = isLocal ? getLocalVideoTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
111 112
     const isAudioOnly = Boolean(state['features/base/audio-only'].enabled);
@@ -118,8 +119,13 @@ export function isVideoPlayable(stateful: Object | Function, id: String) {
118 119
     } else if (!participant?.isFakeParticipant) { // remote participants excluding shared video
119 120
         const isVideoMuted = isRemoteTrackMuted(tracks, MEDIA_TYPE.VIDEO, id);
120 121
 
121
-        isPlayable = Boolean(videoTrack) && !isVideoMuted && !isAudioOnly
122
-            && connectionStatus === JitsiParticipantConnectionStatus.ACTIVE;
122
+        if (getSourceNameSignalingFeatureFlag(state)) {
123
+            isPlayable = Boolean(videoTrack) && !isVideoMuted && !isAudioOnly
124
+                && isTrackStreamingStatusActive(videoTrack);
125
+        } else {
126
+            isPlayable = Boolean(videoTrack) && !isVideoMuted && !isAudioOnly
127
+                && isParticipantConnectionStatusActive(participant);
128
+        }
123 129
     }
124 130
 
125 131
     return isPlayable;

読み込み中…
キャンセル
保存