feat(multi-stream-support) Replace participant connection status logic with track streaming status (#10934)
This commit is contained in:
parent
fa65a54f50
commit
05dc018671
|
@ -9,10 +9,8 @@ import { Provider } from 'react-redux';
|
|||
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../react/features/analytics';
|
||||
import { Avatar } from '../../../react/features/base/avatar';
|
||||
import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
|
||||
import { getSourceNameSignalingFeatureFlag } from '../../../react/features/base/config';
|
||||
import { i18next } from '../../../react/features/base/i18n';
|
||||
import {
|
||||
JitsiParticipantConnectionStatus
|
||||
} from '../../../react/features/base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media';
|
||||
import {
|
||||
getParticipantById,
|
||||
|
@ -20,6 +18,14 @@ import {
|
|||
} from '../../../react/features/base/participants';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../../../react/features/base/tracks';
|
||||
import { CHAT_SIZE } from '../../../react/features/chat';
|
||||
import {
|
||||
isParticipantConnectionStatusActive,
|
||||
isParticipantConnectionStatusInactive,
|
||||
isParticipantConnectionStatusInterrupted,
|
||||
isTrackStreamingStatusActive,
|
||||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../../react/features/connection-indicator/functions';
|
||||
import {
|
||||
updateKnownLargeVideoResolution
|
||||
} from '../../../react/features/large-video/actions';
|
||||
|
@ -226,8 +232,20 @@ export default class LargeVideoManager {
|
|||
const state = APP.store.getState();
|
||||
const participant = getParticipantById(state, id);
|
||||
const connectionStatus = participant?.connectionStatus;
|
||||
const isVideoRenderable = !isVideoMuted
|
||||
&& (APP.conference.isLocalId(id) || connectionStatus === JitsiParticipantConnectionStatus.ACTIVE);
|
||||
|
||||
let isVideoRenderable;
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
const videoTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.VIDEO, id);
|
||||
|
||||
isVideoRenderable = !isVideoMuted
|
||||
&& (APP.conference.isLocalId(id) || isTrackStreamingStatusActive(videoTrack));
|
||||
} else {
|
||||
isVideoRenderable = !isVideoMuted
|
||||
&& (APP.conference.isLocalId(id) || isParticipantConnectionStatusActive(participant));
|
||||
}
|
||||
|
||||
const isAudioOnly = APP.conference.isAudioOnly();
|
||||
const showAvatar
|
||||
= isVideoContainer
|
||||
|
@ -278,8 +296,16 @@ export default class LargeVideoManager {
|
|||
this.updateLargeVideoAudioLevel(0);
|
||||
}
|
||||
|
||||
const messageKey
|
||||
= connectionStatus === JitsiParticipantConnectionStatus.INACTIVE ? 'connection.LOW_BANDWIDTH' : null;
|
||||
let messageKey;
|
||||
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
const videoTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.VIDEO, id);
|
||||
|
||||
messageKey = isTrackStreamingStatusInactive(videoTrack) ? 'connection.LOW_BANDWIDTH' : null;
|
||||
} else {
|
||||
messageKey = isParticipantConnectionStatusInactive(participant) ? 'connection.LOW_BANDWIDTH' : null;
|
||||
}
|
||||
|
||||
// Do not show connection status message in the audio only mode,
|
||||
// because it's based on the video playback status.
|
||||
|
@ -505,13 +531,22 @@ export default class LargeVideoManager {
|
|||
showRemoteConnectionMessage(show) {
|
||||
if (typeof show !== 'boolean') {
|
||||
const participant = getParticipantById(APP.store.getState(), this.id);
|
||||
const connStatus = participant?.connectionStatus;
|
||||
const state = APP.store.getState();
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
show = !APP.conference.isLocalId(this.id)
|
||||
&& (connStatus === JitsiParticipantConnectionStatus.INTERRUPTED
|
||||
|| connStatus
|
||||
=== JitsiParticipantConnectionStatus.INACTIVE);
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
const videoTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.VIDEO, this.id);
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
show = !APP.conference.isLocalId(this.id)
|
||||
&& (isTrackStreamingStatusInterrupted(videoTrack)
|
||||
|| isTrackStreamingStatusInactive(videoTrack));
|
||||
} else {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
show = !APP.conference.isLocalId(this.id)
|
||||
&& (isParticipantConnectionStatusInterrupted(participant)
|
||||
|| isParticipantConnectionStatusInactive(participant));
|
||||
}
|
||||
}
|
||||
|
||||
if (show) {
|
||||
|
|
|
@ -19,6 +19,7 @@ export const JitsiE2ePingEvents = JitsiMeetJS.events.e2eping;
|
|||
export const JitsiMediaDevicesEvents = JitsiMeetJS.events.mediaDevices;
|
||||
export const JitsiParticipantConnectionStatus
|
||||
= JitsiMeetJS.constants.participantConnectionStatus;
|
||||
export const JitsiTrackStreamingStatus = JitsiMeetJS.constants.trackStreamingStatus;
|
||||
export const JitsiRecordingConstants = JitsiMeetJS.constants.recording;
|
||||
export const JitsiSIPVideoGWStatus = JitsiMeetJS.constants.sipVideoGW;
|
||||
export const JitsiTrackErrors = JitsiMeetJS.errors.track;
|
||||
|
|
|
@ -553,6 +553,26 @@ export function trackVideoTypeChanged(track, videoType) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action for when track streaming status changes.
|
||||
*
|
||||
* @param {(JitsiRemoteTrack)} track - JitsiTrack instance.
|
||||
* @param {string} streamingStatus - The new streaming status of the track.
|
||||
* @returns {{
|
||||
* type: TRACK_UPDATED,
|
||||
* track: Track
|
||||
* }}
|
||||
*/
|
||||
export function trackStreamingStatusChanged(track, streamingStatus) {
|
||||
return {
|
||||
type: TRACK_UPDATED,
|
||||
track: {
|
||||
jitsiTrack: track,
|
||||
streamingStatus
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals passed tracks to be added.
|
||||
*
|
||||
|
|
|
@ -5,12 +5,19 @@ import clsx from 'clsx';
|
|||
import React from 'react';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconConnectionActive, IconConnectionInactive } from '../../../base/icons';
|
||||
import { JitsiParticipantConnectionStatus } from '../../../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
import { getLocalParticipant, getParticipantById } from '../../../base/participants';
|
||||
import { Popover } from '../../../base/popover';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
|
||||
import {
|
||||
isParticipantConnectionStatusInactive,
|
||||
isParticipantConnectionStatusInterrupted,
|
||||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../functions';
|
||||
import AbstractConnectionIndicator, {
|
||||
INDICATOR_DISPLAY_THRESHOLD,
|
||||
type Props as AbstractProps,
|
||||
|
@ -18,6 +25,7 @@ import AbstractConnectionIndicator, {
|
|||
} from '../AbstractConnectionIndicator';
|
||||
|
||||
import ConnectionIndicatorContent from './ConnectionIndicatorContent';
|
||||
import { ConnectionIndicatorIcon } from './ConnectionIndicatorIcon';
|
||||
|
||||
/**
|
||||
* An array of display configurations for the connection indicator and its bars.
|
||||
|
@ -237,17 +245,22 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
|||
* @returns {string}
|
||||
*/
|
||||
_getConnectionColorClass() {
|
||||
const { _connectionStatus } = this.props;
|
||||
// TODO We currently do not have logic to emit and handle stats changes for tracks.
|
||||
const { percent } = this.state.stats;
|
||||
const { INACTIVE, INTERRUPTED } = JitsiParticipantConnectionStatus;
|
||||
|
||||
if (_connectionStatus === INACTIVE) {
|
||||
if (this.props._connectionIndicatorInactiveDisabled) {
|
||||
const {
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted,
|
||||
_connectionIndicatorInactiveDisabled
|
||||
} = this.props;
|
||||
|
||||
if (_isConnectionStatusInactive) {
|
||||
if (_connectionIndicatorInactiveDisabled) {
|
||||
return 'status-disabled';
|
||||
}
|
||||
|
||||
return 'status-other';
|
||||
} else if (_connectionStatus === INTERRUPTED) {
|
||||
} else if (_isConnectionStatusInterrupted) {
|
||||
return 'status-lost';
|
||||
} else if (typeof percent === 'undefined') {
|
||||
return 'status-high';
|
||||
|
@ -279,12 +292,12 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
|||
* @returns {string}
|
||||
*/
|
||||
_getVisibilityClass() {
|
||||
const { _connectionStatus, classes } = this.props;
|
||||
const { _isConnectionStatusInactive, _isConnectionStatusInterrupted, classes } = this.props;
|
||||
|
||||
return this.state.showIndicator
|
||||
|| this.props.alwaysVisible
|
||||
|| _connectionStatus === JitsiParticipantConnectionStatus.INTERRUPTED
|
||||
|| _connectionStatus === JitsiParticipantConnectionStatus.INACTIVE
|
||||
|| _isConnectionStatusInterrupted
|
||||
|| _isConnectionStatusInactive
|
||||
? '' : classes.hidden;
|
||||
}
|
||||
|
||||
|
@ -300,49 +313,6 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
|||
this.setState({ popoverVisible: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ReactElement for displaying an icon that represents the current
|
||||
* connection quality.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderIcon() {
|
||||
const colorClass = this._getConnectionColorClass();
|
||||
|
||||
if (this.props._connectionStatus === JitsiParticipantConnectionStatus.INACTIVE) {
|
||||
if (this.props._connectionIndicatorInactiveDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className = 'connection_ninja'>
|
||||
<Icon
|
||||
className = { clsx(this.props.classes.icon, this.props.classes.inactiveIcon, colorClass) }
|
||||
size = { 24 }
|
||||
src = { IconConnectionInactive } />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
let emptyIconWrapperClassName = 'connection_empty';
|
||||
|
||||
if (this.props._connectionStatus
|
||||
=== JitsiParticipantConnectionStatus.INTERRUPTED) {
|
||||
|
||||
// emptyIconWrapperClassName is used by the torture tests to
|
||||
// identify lost connection status handling.
|
||||
emptyIconWrapperClassName = 'connection_lost';
|
||||
}
|
||||
|
||||
return (
|
||||
<span className = { emptyIconWrapperClassName }>
|
||||
<Icon
|
||||
className = { clsx(this.props.classes.icon, colorClass) }
|
||||
size = { 12 }
|
||||
src = { IconConnectionActive } />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
_onShowPopover: () => void;
|
||||
|
||||
|
@ -363,10 +333,25 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderIndicator() {
|
||||
const {
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted,
|
||||
_connectionIndicatorInactiveDisabled,
|
||||
_videoTrack,
|
||||
classes,
|
||||
iconSize
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
style = {{ fontSize: this.props.iconSize }}>
|
||||
{this._renderIcon()}
|
||||
style = {{ fontSize: iconSize }}>
|
||||
<ConnectionIndicatorIcon
|
||||
classes = { classes }
|
||||
colorClass = { this._getConnectionColorClass() }
|
||||
connectionIndicatorInactiveDisabled = { _connectionIndicatorInactiveDisabled }
|
||||
isConnectionStatusInactive = { _isConnectionStatusInactive }
|
||||
isConnectionStatusInterrupted = { _isConnectionStatusInterrupted }
|
||||
track = { _videoTrack } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -381,14 +366,27 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
|||
*/
|
||||
export function _mapStateToProps(state: Object, ownProps: Props) {
|
||||
const { participantId } = ownProps;
|
||||
const participant
|
||||
= participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
|
||||
const sourceNameSignalingEnabled = getSourceNameSignalingFeatureFlag(state);
|
||||
const firstVideoTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.VIDEO, participantId);
|
||||
|
||||
const participant = participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
|
||||
|
||||
const _isConnectionStatusInactive = sourceNameSignalingEnabled
|
||||
? isTrackStreamingStatusInactive(firstVideoTrack)
|
||||
: isParticipantConnectionStatusInactive(participant);
|
||||
|
||||
const _isConnectionStatusInterrupted = sourceNameSignalingEnabled
|
||||
? isTrackStreamingStatusInterrupted(firstVideoTrack)
|
||||
: isParticipantConnectionStatusInterrupted(participant);
|
||||
|
||||
return {
|
||||
_connectionIndicatorInactiveDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
|
||||
_popoverDisabled: state['features/base/config'].connectionIndicators?.disableDetails,
|
||||
_connectionStatus: participant?.connectionStatus
|
||||
_videoTrack: firstVideoTrack,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
};
|
||||
}
|
||||
export default translate(connect(_mapStateToProps)(
|
||||
|
|
|
@ -3,14 +3,20 @@
|
|||
import React from 'react';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { JitsiParticipantConnectionStatus } from '../../../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
import { getLocalParticipant, getParticipantById } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
|
||||
import { ConnectionStatsTable } from '../../../connection-stats';
|
||||
import { saveLogs } from '../../actions';
|
||||
import {
|
||||
isParticipantConnectionStatusInactive,
|
||||
isParticipantConnectionStatusInterrupted,
|
||||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../functions';
|
||||
import AbstractConnectionIndicator, {
|
||||
INDICATOR_DISPLAY_THRESHOLD,
|
||||
type Props as AbstractProps,
|
||||
|
@ -217,12 +223,14 @@ class ConnectionIndicatorContent extends AbstractConnectionIndicator<Props, Stat
|
|||
_getConnectionStatusTip() {
|
||||
let tipKey;
|
||||
|
||||
switch (this.props._connectionStatus) {
|
||||
case JitsiParticipantConnectionStatus.INTERRUPTED:
|
||||
const { _isConnectionStatusInactive, _isConnectionStatusInterrupted } = this.props;
|
||||
|
||||
switch (true) {
|
||||
case _isConnectionStatusInterrupted:
|
||||
tipKey = 'connectionindicator.quality.lost';
|
||||
break;
|
||||
|
||||
case JitsiParticipantConnectionStatus.INACTIVE:
|
||||
case _isConnectionStatusInactive:
|
||||
tipKey = 'connectionindicator.quality.inactive';
|
||||
break;
|
||||
|
||||
|
@ -310,17 +318,29 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
|||
const conference = state['features/base/conference'].conference;
|
||||
const participant
|
||||
= participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
|
||||
const firstVideoTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.VIDEO, participantId);
|
||||
const sourceNameSignalingEnabled = getSourceNameSignalingFeatureFlag(state);
|
||||
|
||||
const _isConnectionStatusInactive = sourceNameSignalingEnabled
|
||||
? isTrackStreamingStatusInactive(firstVideoTrack)
|
||||
: isParticipantConnectionStatusInactive(participant);
|
||||
|
||||
const _isConnectionStatusInterrupted = sourceNameSignalingEnabled
|
||||
? isTrackStreamingStatusInterrupted(firstVideoTrack)
|
||||
: isParticipantConnectionStatusInterrupted(participant);
|
||||
|
||||
const props = {
|
||||
_connectionStatus: participant?.connectionStatus,
|
||||
_enableSaveLogs: state['features/base/config'].enableSaveLogs,
|
||||
_disableShowMoreStats: state['features/base/config'].disableShowMoreStats,
|
||||
_isLocalVideo: participant?.local,
|
||||
_region: participant?.region
|
||||
_region: participant?.region,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
};
|
||||
|
||||
if (conference) {
|
||||
const firstVideoTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.VIDEO, participantId);
|
||||
const firstAudioTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.AUDIO, participantId);
|
||||
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
// @flow
|
||||
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
|
||||
import { Icon, IconConnectionActive, IconConnectionInactive } from '../../../base/icons';
|
||||
import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
|
||||
import { trackStreamingStatusChanged } from '../../../base/tracks';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* An object containing the CSS classes.
|
||||
*/
|
||||
classes: Object,
|
||||
|
||||
/**
|
||||
* A CSS class that interprets the current connection status as a color.
|
||||
*/
|
||||
colorClass: string,
|
||||
|
||||
/**
|
||||
* Disable/enable inactive indicator.
|
||||
*/
|
||||
connectionIndicatorInactiveDisabled: boolean,
|
||||
|
||||
/**
|
||||
* JitsiTrack instance.
|
||||
*/
|
||||
track: Object,
|
||||
|
||||
/**
|
||||
* Whether or not the connection status is inactive.
|
||||
*/
|
||||
isConnectionStatusInactive: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the connection status is interrupted.
|
||||
*/
|
||||
isConnectionStatusInterrupted: boolean,
|
||||
}
|
||||
|
||||
export const ConnectionIndicatorIcon = ({
|
||||
classes,
|
||||
colorClass,
|
||||
connectionIndicatorInactiveDisabled,
|
||||
isConnectionStatusInactive,
|
||||
isConnectionStatusInterrupted,
|
||||
track
|
||||
}: Props) => {
|
||||
const sourceNameSignalingEnabled = useSelector(state => getSourceNameSignalingFeatureFlag(state));
|
||||
const dispatch = useDispatch();
|
||||
const sourceName = track?.jitsiTrack?.getSourceName?.();
|
||||
|
||||
const handleTrackStreamingStatusChanged = streamingStatus => {
|
||||
dispatch(trackStreamingStatusChanged(track.jitsiTrack, streamingStatus));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (track && !track.local && sourceNameSignalingEnabled) {
|
||||
track.jitsiTrack.on(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, handleTrackStreamingStatusChanged);
|
||||
|
||||
dispatch(trackStreamingStatusChanged(track.jitsiTrack, track.jitsiTrack.getTrackStreamingStatus?.()));
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (track && !track.local && sourceNameSignalingEnabled) {
|
||||
track.jitsiTrack.off(
|
||||
JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED,
|
||||
handleTrackStreamingStatusChanged
|
||||
);
|
||||
|
||||
dispatch(trackStreamingStatusChanged(track.jitsiTrack, track.jitsiTrack.getTrackStreamingStatus?.()));
|
||||
}
|
||||
};
|
||||
}, [ sourceName ]);
|
||||
|
||||
if (isConnectionStatusInactive) {
|
||||
if (connectionIndicatorInactiveDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className = 'connection_ninja'>
|
||||
<Icon
|
||||
className = { clsx(classes.icon, classes.inactiveIcon, colorClass) }
|
||||
size = { 24 }
|
||||
src = { IconConnectionInactive } />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
let emptyIconWrapperClassName = 'connection_empty';
|
||||
|
||||
if (isConnectionStatusInterrupted) {
|
||||
// emptyIconWrapperClassName is used by the torture tests to identify lost connection status handling.
|
||||
emptyIconWrapperClassName = 'connection_lost';
|
||||
}
|
||||
|
||||
return (
|
||||
<span className = { emptyIconWrapperClassName }>
|
||||
<Icon
|
||||
className = { clsx(classes.icon, colorClass) }
|
||||
size = { 12 }
|
||||
src = { IconConnectionActive } />
|
||||
</span>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
import { JitsiParticipantConnectionStatus, JitsiTrackStreamingStatus } from '../base/lib-jitsi-meet';
|
||||
|
||||
/**
|
||||
* Checks if the passed track's streaming status is active.
|
||||
*
|
||||
* @param {Object} videoTrack - Track reference.
|
||||
* @returns {boolean} - Is streaming status active.
|
||||
*/
|
||||
export function isTrackStreamingStatusActive(videoTrack) {
|
||||
const streamingStatus = videoTrack?.streamingStatus;
|
||||
|
||||
return streamingStatus === JitsiTrackStreamingStatus.ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the passed track's streaming status is inactive.
|
||||
*
|
||||
* @param {Object} videoTrack - Track reference.
|
||||
* @returns {boolean} - Is streaming status inactive.
|
||||
*/
|
||||
export function isTrackStreamingStatusInactive(videoTrack) {
|
||||
const streamingStatus = videoTrack?.streamingStatus;
|
||||
|
||||
return streamingStatus === JitsiTrackStreamingStatus.INACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the passed track's streaming status is interrupted.
|
||||
*
|
||||
* @param {Object} videoTrack - Track reference.
|
||||
* @returns {boolean} - Is streaming status interrupted.
|
||||
*/
|
||||
export function isTrackStreamingStatusInterrupted(videoTrack) {
|
||||
const streamingStatus = videoTrack?.streamingStatus;
|
||||
|
||||
return streamingStatus === JitsiTrackStreamingStatus.INTERRUPTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the passed participant's connecton status is active.
|
||||
*
|
||||
* @param {Object} participant - Participant reference.
|
||||
* @returns {boolean} - Is connection status active.
|
||||
*/
|
||||
export function isParticipantConnectionStatusActive(participant) {
|
||||
const connectionStatus = participant?.connectionStatus;
|
||||
|
||||
return connectionStatus === JitsiParticipantConnectionStatus.ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the passed participant's connecton status is inactive.
|
||||
*
|
||||
* @param {Object} participant - Participant reference.
|
||||
* @returns {boolean} - Is connection status inactive.
|
||||
*/
|
||||
export function isParticipantConnectionStatusInactive(participant) {
|
||||
const connectionStatus = participant?.connectionStatus;
|
||||
|
||||
return connectionStatus === JitsiParticipantConnectionStatus.INACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the passed participant's connecton status is interrupted.
|
||||
*
|
||||
* @param {Object} participant - Participant reference.
|
||||
* @returns {boolean} - Is connection status interrupted.
|
||||
*/
|
||||
export function isParticipantConnectionStatusInterrupted(participant) {
|
||||
const connectionStatus = participant?.connectionStatus;
|
||||
|
||||
return connectionStatus === JitsiParticipantConnectionStatus.INTERRUPTED;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import { JitsiParticipantConnectionStatus } from '../base/lib-jitsi-meet';
|
||||
import { getSourceNameSignalingFeatureFlag } from '../base/config';
|
||||
import { MEDIA_TYPE } from '../base/media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
|
@ -15,6 +15,7 @@ import {
|
|||
isLocalTrackMuted,
|
||||
isRemoteTrackMuted
|
||||
} from '../base/tracks/functions';
|
||||
import { isTrackStreamingStatusActive, isParticipantConnectionStatusActive } from '../connection-indicator/functions';
|
||||
import { LAYOUTS } from '../video-layout';
|
||||
|
||||
import {
|
||||
|
@ -105,7 +106,7 @@ export function isVideoPlayable(stateful: Object | Function, id: String) {
|
|||
const tracks = state['features/base/tracks'];
|
||||
const participant = id ? getParticipantById(state, id) : getLocalParticipant(state);
|
||||
const isLocal = participant?.local ?? true;
|
||||
const { connectionStatus } = participant || {};
|
||||
|
||||
const videoTrack
|
||||
= isLocal ? getLocalVideoTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
|
||||
const isAudioOnly = Boolean(state['features/base/audio-only'].enabled);
|
||||
|
@ -118,8 +119,13 @@ export function isVideoPlayable(stateful: Object | Function, id: String) {
|
|||
} else if (!participant?.isFakeParticipant) { // remote participants excluding shared video
|
||||
const isVideoMuted = isRemoteTrackMuted(tracks, MEDIA_TYPE.VIDEO, id);
|
||||
|
||||
isPlayable = Boolean(videoTrack) && !isVideoMuted && !isAudioOnly
|
||||
&& connectionStatus === JitsiParticipantConnectionStatus.ACTIVE;
|
||||
if (getSourceNameSignalingFeatureFlag(state)) {
|
||||
isPlayable = Boolean(videoTrack) && !isVideoMuted && !isAudioOnly
|
||||
&& isTrackStreamingStatusActive(videoTrack);
|
||||
} else {
|
||||
isPlayable = Boolean(videoTrack) && !isVideoMuted && !isAudioOnly
|
||||
&& isParticipantConnectionStatusActive(participant);
|
||||
}
|
||||
}
|
||||
|
||||
return isPlayable;
|
||||
|
|
Loading…
Reference in New Issue