feat: Adds more debug information in the GSM bars popover (#7627)

This commit is contained in:
George Politis 2020-10-02 16:20:24 +03:00 committed by GitHub
parent aa488cb75c
commit b5310573fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 239 additions and 25 deletions

View File

@ -108,6 +108,7 @@ import {
getBackendSafePath,
getJitsiMeetGlobalNS
} from './react/features/base/util';
import { downloadJSON } from './react/features/base/util/downloadJSON';
import { showDesktopPicker } from './react/features/desktop-picker';
import { appendSuffix } from './react/features/display-name';
import {
@ -1221,19 +1222,8 @@ export default {
// this can be called from console and will not have reference to this
// that's why we reference the global var
const logs = APP.connection.getLogs();
const data = encodeURIComponent(JSON.stringify(logs, null, ' '));
const elem = document.createElement('a');
elem.download = filename;
elem.href = `data:application/json;charset=utf-8,\n${data}`;
elem.dataset.downloadurl
= [ 'text/json', elem.download, elem.href ].join(':');
elem.dispatchEvent(new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: false
}));
downloadJSON(logs, filename);
},
/**

View File

@ -45,10 +45,8 @@
@extend .connection-info__icon;
}
.showmore {
display: block;
.connection-actions {
margin: 10px auto;
text-align: center;
width: 90px;
}
}

View File

@ -99,6 +99,7 @@
},
"connectionindicator": {
"address": "Address:",
"audio_ssrc": "Audio SSRC:",
"bandwidth": "Estimated bandwidth:",
"bitrate": "Bitrate:",
"bridgeCount": "Server count: ",
@ -126,9 +127,12 @@
"remoteport": "Remote port:",
"remoteport_plural": "Remote ports:",
"resolution": "Resolution:",
"savelogs": "Save logs",
"participant_id": "Participant id:",
"status": "Connection:",
"transport": "Transport:",
"transport_plural": "Transports:"
"transport_plural": "Transports:",
"video_ssrc": "Video SSRC:"
},
"dateUtils": {
"earlier": "Earlier",

View File

@ -0,0 +1,23 @@
// @flow
/**
* Downloads a JSON object.
*
* @param {Object} json - The JSON object to download.
* @param {string} filename - The filename to give to the downloaded file.
* @returns {void}
*/
export function downloadJSON(json: Object, filename: string) {
const data = encodeURIComponent(JSON.stringify(json, null, ' '));
const elem = document.createElement('a');
elem.download = filename;
elem.href = `data:application/json;charset=utf-8,\n${data}`;
elem.dataset.downloadurl = [ 'text/json', elem.download, elem.href ].join(':');
elem.dispatchEvent(new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: false
}));
}

View File

@ -0,0 +1,18 @@
import getRoomName from '../base/config/getRoomName';
import { downloadJSON } from '../base/util/downloadJSON';
/**
* Create an action for saving the conference logs.
*
* @returns {Function}
*/
export function saveLogs() {
return (dispatch, getState) => {
const logs = getState()['features/base/connection'].connection.getLogs();
const roomName = getRoomName() || '';
downloadJSON(logs, `meetlog-${roomName}.json`);
};
}

View File

@ -1,12 +1,17 @@
// @flow
import React from 'react';
import type { Dispatch } from 'redux';
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 { Popover } from '../../../base/popover';
import { connect } from '../../../base/redux';
import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
import { ConnectionStatsTable } from '../../../connection-stats';
import { saveLogs } from '../../actions';
import AbstractConnectionIndicator, {
INDICATOR_DISPLAY_THRESHOLD,
type Props as AbstractProps,
@ -62,12 +67,22 @@ type Props = AbstractProps & {
*/
alwaysVisible: boolean,
/**
* The audio SSRC of this client.
*/
audioSsrc: number,
/**
* The current condition of the user's connection, matching one of the
* enumerated values in the library.
*/
connectionStatus: string,
/**
* The Redux dispatch function.
*/
dispatch: Dispatch<any>,
/**
* Whether or not clicking the indicator should display a popover for more
* details.
@ -93,7 +108,17 @@ type Props = AbstractProps & {
/**
* Invoked to obtain translated strings.
*/
t: Function
t: Function,
/**
* The video SSRC of this client.
*/
videoSsrc: number,
/**
* Invoked to save the conference logs.
*/
_onSaveLogs: Function
};
/**
@ -353,6 +378,7 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
return (
<ConnectionStatsTable
audioSsrc = { this.props.audioSsrc }
bandwidth = { bandwidth }
bitrate = { bitrate }
bridgeCount = { bridgeCount }
@ -362,15 +388,63 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
framerate = { framerate }
isLocalVideo = { this.props.isLocalVideo }
maxEnabledResolution = { maxEnabledResolution }
onSaveLogs = { this.props._onSaveLogs }
onShowMore = { this._onToggleShowMore }
packetLoss = { packetLoss }
participantId = { this.props.participantId }
region = { region }
resolution = { resolution }
serverRegion = { serverRegion }
shouldShowMore = { this.state.showMoreStats }
transport = { transport } />
transport = { transport }
videoSsrc = { this.props.videoSsrc } />
);
}
}
export default translate(ConnectionIndicator);
/**
* Maps redux actions to the props of the component.
*
* @param {Function} dispatch - The redux action {@code dispatch} function.
* @returns {{
* _onSaveLogs: Function,
* }}
* @private
*/
export function _mapDispatchToProps(dispatch: Dispatch<any>) {
return {
/**
* Saves the conference logs.
*
* @returns {Function}
*/
_onSaveLogs() {
dispatch(saveLogs());
}
};
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @param {Props} ownProps - The own props of the component.
* @returns {Props}
*/
export function _mapStateToProps(state: Object, ownProps: Props) {
const firstVideoTrack = getTrackByMediaTypeAndParticipant(
state['features/base/tracks'], MEDIA_TYPE.VIDEO, ownProps.participantId);
const firstAudioTrack = getTrackByMediaTypeAndParticipant(
state['features/base/tracks'], MEDIA_TYPE.AUDIO, ownProps.participantId);
return {
audioSsrc: firstAudioTrack
? state['features/base/conference'].conference.getSsrcByTrack(firstAudioTrack.jitsiTrack) : undefined,
videoSsrc: firstVideoTrack
? state['features/base/conference'].conference.getSsrcByTrack(firstVideoTrack.jitsiTrack) : undefined
};
}
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(ConnectionIndicator));

View File

@ -10,6 +10,11 @@ import { translate } from '../../base/i18n';
*/
type Props = {
/**
* The audio SSRC of this client.
*/
audioSsrc: number,
/**
* Statistics related to bandwidth.
* {{
@ -49,6 +54,11 @@ type Props = {
*/
e2eRtt: number,
/**
* The endpoint id of this client.
*/
participantId: string,
/**
* Statistics related to frame rates for each ssrc.
* {{
@ -68,6 +78,11 @@ type Props = {
*/
maxEnabledResolution: number,
/**
* Callback to invoke when the user clicks on the download logs link.
*/
onSaveLogs: Function,
/**
* Callback to invoke when the show additional stats link is clicked.
*/
@ -114,6 +129,11 @@ type Props = {
*/
t: Function,
/**
* The video SSRC of this client.
*/
videoSsrc: number,
/**
* Statistics related to transports.
*/
@ -138,9 +158,11 @@ class ConnectionStatsTable extends Component<Props> {
return (
<div className = 'connection-info'>
{ this._renderStatistics() }
{ isLocalVideo ? this._renderShowMoreLink() : null }
{ isLocalVideo && this.props.shouldShowMore
? this._renderAdditionalStats() : null }
<div className = 'connection-actions'>
{ isLocalVideo ? this._renderSaveLogs() : null}
{ this._renderShowMoreLink() }
</div>
{ this.props.shouldShowMore ? this._renderAdditionalStats() : null }
</div>
);
}
@ -153,12 +175,17 @@ class ConnectionStatsTable extends Component<Props> {
* @returns {ReactElement}
*/
_renderAdditionalStats() {
const { isLocalVideo } = this.props;
return (
<table className = 'connection-info__container'>
<tbody>
{ this._renderBandwidth() }
{ this._renderTransport() }
{ this._renderRegion() }
{ isLocalVideo ? this._renderBandwidth() : null }
{ isLocalVideo ? this._renderTransport() : null }
{ isLocalVideo ? this._renderRegion() : null }
{ this._renderAudioSsrc() }
{ this._renderVideoSsrc() }
{ this._renderParticipantId() }
</tbody>
</table>
);
@ -224,6 +251,66 @@ class ConnectionStatsTable extends Component<Props> {
);
}
/**
* Creates a table row as a ReactElement for displaying the audio ssrc.
* This will typically be something like "Audio SSRC: 12345".
*
* @returns {JSX.Element}
* @private
*/
_renderAudioSsrc() {
const { audioSsrc, t } = this.props;
return (
<tr>
<td>
<span>{ t('connectionindicator.audio_ssrc') }</span>
</td>
<td>{ audioSsrc || 'N/A' }</td>
</tr>
);
}
/**
* Creates a table row as a ReactElement for displaying the video ssrc.
* This will typically be something like "Video SSRC: 12345".
*
* @returns {JSX.Element}
* @private
*/
_renderVideoSsrc() {
const { videoSsrc, t } = this.props;
return (
<tr>
<td>
<span>{ t('connectionindicator.video_ssrc') }</span>
</td>
<td>{ videoSsrc || 'N/A' }</td>
</tr>
);
}
/**
* Creates a table row as a ReactElement for displaying the endpoint id.
* This will typically be something like "Endpoint id: 1e8fbg".
*
* @returns {JSX.Element}
* @private
*/
_renderParticipantId() {
const { participantId, t } = this.props;
return (
<tr>
<td>
<span>{ t('connectionindicator.participant_id') }</span>
</td>
<td>{ participantId || 'N/A' }</td>
</tr>
);
}
/**
* Creates a a table row as a ReactElement for displaying codec, if present.
* This will typically be something like "Codecs (A/V): Opus, vp8".
@ -455,6 +542,26 @@ class ConnectionStatsTable extends Component<Props> {
);
}
/**
* Creates a ReactElement for display a link to save the logs.
*
* @private
* @returns {ReactElement}
*/
_renderSaveLogs() {
return (
<span>
<a
className = 'savelogs link'
onClick = { this.props.onSaveLogs } >
{ this.props.t('connectionindicator.savelogs') }
</a>
<span> | </span>
</span>
);
}
/**
* Creates a ReactElement for display a link to toggle showing additional
* statistics.