fix(connection-indicator) use SSRCs to match tiles to stats

This commit is contained in:
Saúl Ibarra Corretgé 2022-10-31 22:17:05 +01:00 committed by Saúl Ibarra Corretgé
parent 73160de3b7
commit 9b1e662a93
4 changed files with 75 additions and 62 deletions

View File

@ -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;
/**

View File

@ -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)));

View File

@ -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));

View File

@ -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 (