fix(connection-indicator) use SSRCs to match tiles to stats
This commit is contained in:
parent
73160de3b7
commit
9b1e662a93
|
@ -2,10 +2,9 @@
|
|||
|
||||
import { Component } from 'react';
|
||||
|
||||
import { getVirtualScreenshareParticipantOwnerId } from '../../base/participants/functions';
|
||||
import statsEmitter from '../statsEmitter';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
const defaultAutoHideTimeout = 5000;
|
||||
|
||||
/**
|
||||
|
@ -26,6 +25,11 @@ export type Props = {
|
|||
*/
|
||||
_autoHideTimeout: number,
|
||||
|
||||
/**
|
||||
* Whether or not the statistics are for screen share.
|
||||
*/
|
||||
_isVirtualScreenshareParticipant: boolean,
|
||||
|
||||
/**
|
||||
* The ID of the participant associated with the displayed connection indication and
|
||||
* stats.
|
||||
|
@ -90,7 +94,7 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
|
|||
* returns {void}
|
||||
*/
|
||||
componentDidMount() {
|
||||
statsEmitter.subscribeToClientStats(this.props.participantId, this._onStatsUpdated);
|
||||
statsEmitter.subscribeToClientStats(this._getRealParticipantId(this.props), this._onStatsUpdated);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,11 +104,12 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
|
|||
* returns {void}
|
||||
*/
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (prevProps.participantId !== this.props.participantId) {
|
||||
statsEmitter.unsubscribeToClientStats(
|
||||
prevProps.participantId, this._onStatsUpdated);
|
||||
statsEmitter.subscribeToClientStats(
|
||||
this.props.participantId, this._onStatsUpdated);
|
||||
const prevParticipantId = this._getRealParticipantId(prevProps);
|
||||
const participantId = this._getRealParticipantId(this.props);
|
||||
|
||||
if (prevParticipantId !== participantId) {
|
||||
statsEmitter.unsubscribeToClientStats(prevParticipantId, this._onStatsUpdated);
|
||||
statsEmitter.subscribeToClientStats(participantId, this._onStatsUpdated);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,11 +121,25 @@ class AbstractConnectionIndicator<P: Props, S: State> extends Component<P, S> {
|
|||
* @returns {void}
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
statsEmitter.unsubscribeToClientStats(this.props.participantId, this._onStatsUpdated);
|
||||
statsEmitter.unsubscribeToClientStats(this._getRealParticipantId(this.props), this._onStatsUpdated);
|
||||
|
||||
clearTimeout(this.autoHideTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "real" participant ID. FOr a virtual screenshare participant, that is its "owner".
|
||||
*
|
||||
* @param {Props} props - The props where to extract the data from.
|
||||
* @returns {string | undefined } The resolved participant ID.
|
||||
*/
|
||||
_getRealParticipantId(props: Props) {
|
||||
if (props._isVirtualScreenshareParticipant) {
|
||||
return getVirtualScreenshareParticipantOwnerId(props.participantId);
|
||||
}
|
||||
|
||||
return props.participantId;
|
||||
}
|
||||
|
||||
_onStatsUpdated: (Object) => void;
|
||||
|
||||
/**
|
||||
|
|
|
@ -415,13 +415,15 @@ export function _mapStateToProps(state: IReduxState, ownProps: Props) {
|
|||
|
||||
return {
|
||||
_connectionIndicatorInactiveDisabled:
|
||||
Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
|
||||
Boolean(state['features/base/config'].connectionIndicators?.inactiveDisabled),
|
||||
_isVirtualScreenshareParticipant: sourceNameSignalingEnabled && isScreenShareParticipant(participant),
|
||||
_popoverDisabled: state['features/base/config'].connectionIndicators?.disableDetails,
|
||||
_videoTrack: firstVideoTrack,
|
||||
_isConnectionStatusInactive,
|
||||
_isConnectionStatusInterrupted
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(
|
||||
// @ts-ignore
|
||||
withStyles(styles)(ConnectionIndicator)));
|
||||
|
|
|
@ -8,7 +8,10 @@ import { translate } from '../../../base/i18n';
|
|||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
import { getLocalParticipant, getParticipantById, isScreenShareParticipant } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
|
||||
import {
|
||||
getTrackByMediaTypeAndParticipant,
|
||||
getVirtualScreenshareParticipantTrack
|
||||
} from '../../../base/tracks/functions';
|
||||
import { ConnectionStatsTable } from '../../../connection-stats';
|
||||
import { saveLogs } from '../../actions';
|
||||
import {
|
||||
|
@ -67,7 +70,7 @@ type Props = AbstractProps & {
|
|||
/**
|
||||
* The audio SSRC of this client.
|
||||
*/
|
||||
_audioSsrc: number,
|
||||
_audioSsrc: number,
|
||||
|
||||
/**
|
||||
* The current condition of the user's connection, matching one of the
|
||||
|
@ -87,12 +90,6 @@ type Props = AbstractProps & {
|
|||
*/
|
||||
_enableSaveLogs: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the displays stats are for screen share. This prop is behind the sourceNameSignaling feature
|
||||
* flag.
|
||||
*/
|
||||
_isVirtualScreenshareParticipant: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the displays stats are for local video.
|
||||
*/
|
||||
|
@ -323,19 +320,26 @@ 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 tracks = state['features/base/tracks'];
|
||||
const audioTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantId);
|
||||
let videoTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, participantId);
|
||||
|
||||
if (sourceNameSignalingEnabled && isScreenShareParticipant(participant)) {
|
||||
videoTrack = getVirtualScreenshareParticipantTrack(tracks, participant?.id);
|
||||
}
|
||||
|
||||
const _isConnectionStatusInactive = sourceNameSignalingEnabled
|
||||
? isTrackStreamingStatusInactive(firstVideoTrack)
|
||||
? isTrackStreamingStatusInactive(videoTrack)
|
||||
: isParticipantConnectionStatusInactive(participant);
|
||||
|
||||
const _isConnectionStatusInterrupted = sourceNameSignalingEnabled
|
||||
? isTrackStreamingStatusInterrupted(firstVideoTrack)
|
||||
? isTrackStreamingStatusInterrupted(videoTrack)
|
||||
: isParticipantConnectionStatusInterrupted(participant);
|
||||
|
||||
const props = {
|
||||
return {
|
||||
_audioSsrc: audioTrack ? conference?.getSsrcByTrack(audioTrack.jitsiTrack) : undefined,
|
||||
_connectionStatus: participant?.connectionStatus,
|
||||
_enableSaveLogs: state['features/base/config'].enableSaveLogs,
|
||||
_disableShowMoreStats: state['features/base/config'].disableShowMoreStats,
|
||||
|
@ -343,20 +347,9 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
|||
_isConnectionStatusInterrupted,
|
||||
_isVirtualScreenshareParticipant: sourceNameSignalingEnabled && isScreenShareParticipant(participant),
|
||||
_isLocalVideo: participant?.local,
|
||||
_region: participant?.region
|
||||
_region: participant?.region,
|
||||
_videoSsrc: videoTrack ? conference?.getSsrcByTrack(videoTrack.jitsiTrack) : undefined
|
||||
};
|
||||
|
||||
if (conference) {
|
||||
const firstAudioTrack = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'], MEDIA_TYPE.AUDIO, participantId);
|
||||
|
||||
return {
|
||||
...props,
|
||||
_audioSsrc: firstAudioTrack ? conference.getSsrcByTrack(firstAudioTrack.jitsiTrack) : undefined,
|
||||
_videoSsrc: firstVideoTrack ? conference.getSsrcByTrack(firstVideoTrack.jitsiTrack) : undefined
|
||||
};
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(ConnectionIndicatorContent));
|
||||
|
|
|
@ -84,7 +84,9 @@ interface IProps extends WithTranslation {
|
|||
* [ ssrc ]: Number
|
||||
* }}.
|
||||
*/
|
||||
framerate: Object;
|
||||
framerate: {
|
||||
[ssrc: string]: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not the statistics are for local video.
|
||||
|
@ -461,21 +463,16 @@ class ConnectionStatsTable extends Component<IProps> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderCodecs() {
|
||||
const { codec, t } = this.props;
|
||||
const { audioSsrc, codec, t, videoSsrc } = this.props;
|
||||
|
||||
let codecString = 'N/A';
|
||||
|
||||
if (codec) {
|
||||
const audioCodecs = Object.values(codec)
|
||||
.map(c => c.audio)
|
||||
.filter(Boolean);
|
||||
const videoCodecs = Object.values(codec)
|
||||
.map(c => c.video)
|
||||
.filter(Boolean);
|
||||
const audioCodec = codec[audioSsrc]?.audio;
|
||||
const videoCodec = codec[videoSsrc]?.video;
|
||||
|
||||
if (audioCodecs.length || videoCodecs.length) {
|
||||
// Use a Set to eliminate duplicates.
|
||||
codecString = Array.from(new Set([ ...audioCodecs, ...videoCodecs ])).join(', ');
|
||||
if (audioCodec || videoCodec) {
|
||||
codecString = [ audioCodec, videoCodec ].filter(Boolean).join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,11 +570,12 @@ class ConnectionStatsTable extends Component<IProps> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderFrameRate() {
|
||||
const { framerate, t } = this.props;
|
||||
const { framerate, t, videoSsrc } = this.props;
|
||||
let frameRateString = 'N/A';
|
||||
|
||||
const frameRateString = Object.keys(framerate || {})
|
||||
.map(ssrc => framerate[ssrc as keyof typeof framerate])
|
||||
.join(', ') || 'N/A';
|
||||
if (framerate) {
|
||||
frameRateString = String(framerate[videoSsrc] ?? 'N/A');
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
|
@ -639,20 +637,21 @@ class ConnectionStatsTable extends Component<IProps> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderResolution() {
|
||||
const { resolution, maxEnabledResolution, t } = this.props;
|
||||
const { resolution, maxEnabledResolution, t, videoSsrc } = this.props;
|
||||
let resolutionString = 'N/A';
|
||||
|
||||
let resolutionString = Object.keys(resolution || {})
|
||||
.map(ssrc => {
|
||||
const { width, height } = resolution[ssrc];
|
||||
if (resolution && videoSsrc) {
|
||||
const { width, height } = resolution[videoSsrc] ?? { };
|
||||
|
||||
return `${width}x${height}`;
|
||||
})
|
||||
.join(', ') || 'N/A';
|
||||
if (width && height) {
|
||||
resolutionString = `${width}x${height}`;
|
||||
|
||||
if (maxEnabledResolution && maxEnabledResolution < 720) {
|
||||
const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution');
|
||||
if (maxEnabledResolution && maxEnabledResolution < 720) {
|
||||
const maxEnabledResolutionTitle = t('connectionindicator.maxEnabledResolution');
|
||||
|
||||
resolutionString += ` (${maxEnabledResolutionTitle} ${maxEnabledResolution}p)`;
|
||||
resolutionString += ` (${maxEnabledResolutionTitle} ${maxEnabledResolution}p)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
Loading…
Reference in New Issue