2017-09-26 16:55:09 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2017-06-16 14:02:39 +00:00
|
|
|
import React, { Component } from 'react';
|
|
|
|
|
|
|
|
import { translate } from '../../base/i18n';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* React {@code Component} for displaying connection statistics.
|
|
|
|
*
|
|
|
|
* @extends Component
|
|
|
|
*/
|
|
|
|
class ConnectionStatsTable extends Component {
|
|
|
|
/**
|
|
|
|
* {@code ConnectionStatsTable} component's property types.
|
|
|
|
*
|
|
|
|
* @static
|
|
|
|
*/
|
|
|
|
static propTypes = {
|
|
|
|
/**
|
|
|
|
* Statistics related to bandwidth.
|
|
|
|
* {{
|
|
|
|
* download: Number,
|
|
|
|
* upload: Number
|
|
|
|
* }}
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
bandwidth: PropTypes.object,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Statistics related to bitrate.
|
|
|
|
* {{
|
|
|
|
* download: Number,
|
|
|
|
* upload: Number
|
|
|
|
* }}
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
bitrate: PropTypes.object,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A message describing the connection quality.
|
|
|
|
*/
|
|
|
|
connectionSummary: PropTypes.string,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Statistics related to framerates for each ssrc.
|
|
|
|
* {{
|
|
|
|
* [ ssrc ]: Number
|
|
|
|
* }}
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
framerate: PropTypes.object,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not the statitics are for local video.
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
isLocalVideo: PropTypes.bool,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback to invoke when the show additional stats link is clicked.
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
onShowMore: PropTypes.func,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Statistics related to packet loss.
|
|
|
|
* {{
|
|
|
|
* download: Number,
|
|
|
|
* upload: Number
|
|
|
|
* }}
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
packetLoss: PropTypes.object,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Statistics related to display resolutions for each ssrc.
|
|
|
|
* {{
|
|
|
|
* [ ssrc ]: {
|
|
|
|
* height: Number,
|
|
|
|
* width: Number
|
|
|
|
* }
|
|
|
|
* }}
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
resolution: PropTypes.object,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not additional stats about bandwidth and transport should
|
|
|
|
* be displayed. Will not display even if true for remote participants.
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
shouldShowMore: PropTypes.bool,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked to obtain translated strings.
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
t: PropTypes.func,
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Statistics related to transports.
|
|
|
|
*/
|
2017-09-26 16:55:09 +00:00
|
|
|
transport: PropTypes.array
|
2017-06-16 14:02:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#render()}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
render() {
|
|
|
|
const { isLocalVideo } = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className = 'connection-info'>
|
|
|
|
{ this._renderStatistics() }
|
|
|
|
{ isLocalVideo ? this._renderShowMoreLink() : null }
|
|
|
|
{ isLocalVideo && this.props.shouldShowMore
|
|
|
|
? this._renderAdditionalStats() : null }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a table as ReactElement that will display additional statistics
|
|
|
|
* related to bandwidth and transport.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderAdditionalStats() {
|
|
|
|
return (
|
|
|
|
<table className = 'connection-info__container'>
|
|
|
|
<tbody>
|
|
|
|
{ this._renderBandwidth() }
|
|
|
|
{ this._renderTransport() }
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a table row as a ReactElement for displaying bandwidth related
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderBandwidth() {
|
|
|
|
const { download, upload } = this.props.bandwidth || {};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
{ this.props.t('connectionindicator.bandwidth') }
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<span className = 'connection-info__download'>
|
|
|
|
↓
|
|
|
|
</span>
|
|
|
|
{ download ? `${download} Kbps` : 'N/A' }
|
|
|
|
<span className = 'connection-info__upload'>
|
|
|
|
↑
|
|
|
|
</span>
|
|
|
|
{ upload ? `${upload} Kbps` : 'N/A' }
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a a table row as a ReactElement for displaying bitrate related
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderBitrate() {
|
|
|
|
const { download, upload } = this.props.bitrate || {};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
<span>
|
|
|
|
{ this.props.t('connectionindicator.bitrate') }
|
|
|
|
</span>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<span className = 'connection-info__download'>
|
|
|
|
↓
|
|
|
|
</span>
|
|
|
|
{ download ? `${download} Kbps` : 'N/A' }
|
|
|
|
<span className = 'connection-info__upload'>
|
|
|
|
↑
|
|
|
|
</span>
|
|
|
|
{ upload ? `${upload} Kbps` : 'N/A' }
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-09-26 16:55:09 +00:00
|
|
|
/**
|
|
|
|
* Creates a table row as a ReactElement for displaying a summary message
|
|
|
|
* about the current connection status.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderConnectionSummary() {
|
|
|
|
return (
|
|
|
|
<tr className = 'connection-info__status'>
|
|
|
|
<td>
|
|
|
|
<span>{ this.props.t('connectionindicator.status') }</span>
|
|
|
|
</td>
|
|
|
|
<td>{ this.props.connectionSummary }</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-06-16 14:02:39 +00:00
|
|
|
/**
|
|
|
|
* Creates a table row as a ReactElement for displaying frame rate related
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderFrameRate() {
|
|
|
|
const { framerate, t } = this.props;
|
|
|
|
const frameRateString = Object.keys(framerate || {})
|
|
|
|
.map(ssrc => framerate[ssrc])
|
|
|
|
.join(', ') || 'N/A';
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
<span>{ t('connectionindicator.framerate') }</span>
|
|
|
|
</td>
|
|
|
|
<td>{ frameRateString }</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a tables row as a ReactElement for displaying packet loss related
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderPacketLoss() {
|
|
|
|
const { packetLoss, t } = this.props;
|
|
|
|
let packetLossTableData;
|
|
|
|
|
|
|
|
if (packetLoss) {
|
|
|
|
const { download, upload } = packetLoss;
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-extra-parens
|
|
|
|
packetLossTableData = (
|
|
|
|
<td>
|
|
|
|
<span className = 'connection-info__download'>
|
|
|
|
↓
|
|
|
|
</span>
|
|
|
|
{ download === null ? 'N/A' : `${download}%` }
|
|
|
|
<span className = 'connection-info__upload'>
|
|
|
|
↑
|
|
|
|
</span>
|
|
|
|
{ upload === null ? 'N/A' : `${upload}%` }
|
|
|
|
</td>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
packetLossTableData = <td>N/A</td>;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
<span>
|
|
|
|
{ t('connectionindicator.packetloss') }
|
|
|
|
</span>
|
|
|
|
</td>
|
|
|
|
{ packetLossTableData }
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a table row as a ReactElement for displaying resolution related
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderResolution() {
|
|
|
|
const { resolution, t } = this.props;
|
|
|
|
const resolutionString = Object.keys(resolution || {})
|
|
|
|
.map(ssrc => {
|
|
|
|
const { width, height } = resolution[ssrc];
|
|
|
|
|
|
|
|
return `${width}x${height}`;
|
|
|
|
})
|
|
|
|
.join(', ') || 'N/A';
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
<span>{ t('connectionindicator.resolution') }</span>
|
|
|
|
</td>
|
|
|
|
<td>{ resolutionString }</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a ReactElement for display a link to toggle showing additional
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderShowMoreLink() {
|
|
|
|
const translationKey
|
|
|
|
= this.props.shouldShowMore
|
|
|
|
? 'connectionindicator.less'
|
|
|
|
: 'connectionindicator.more';
|
|
|
|
|
|
|
|
return (
|
|
|
|
<a
|
2017-08-14 15:02:58 +00:00
|
|
|
className = 'showmore link'
|
2017-06-16 14:02:39 +00:00
|
|
|
onClick = { this.props.onShowMore } >
|
|
|
|
{ this.props.t(translationKey) }
|
|
|
|
</a>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a table as a ReactElement for displaying connection statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderStatistics() {
|
|
|
|
return (
|
|
|
|
<table className = 'connection-info__container'>
|
|
|
|
<tbody>
|
2017-09-26 16:55:09 +00:00
|
|
|
{ this._renderConnectionSummary() }
|
2017-06-16 14:02:39 +00:00
|
|
|
{ this._renderBitrate() }
|
|
|
|
{ this._renderPacketLoss() }
|
|
|
|
{ this._renderResolution() }
|
|
|
|
{ this._renderFrameRate() }
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates table rows as ReactElements for displaying transport related
|
|
|
|
* statistics.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement[]}
|
|
|
|
*/
|
|
|
|
_renderTransport() {
|
|
|
|
const { t, transport } = this.props;
|
|
|
|
|
|
|
|
if (!transport || transport.length === 0) {
|
|
|
|
// eslint-disable-next-line no-extra-parens
|
|
|
|
const NA = (
|
|
|
|
<tr key = 'address'>
|
|
|
|
<td>
|
|
|
|
<span>{ t('connectionindicator.address') }</span>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
N/A
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
|
|
|
|
return [ NA ];
|
|
|
|
}
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
localIP: [],
|
|
|
|
localPort: [],
|
|
|
|
remoteIP: [],
|
|
|
|
remotePort: [],
|
|
|
|
transportType: []
|
|
|
|
};
|
|
|
|
|
|
|
|
for (let i = 0; i < transport.length; i++) {
|
|
|
|
const ip = getIP(transport[i].ip);
|
|
|
|
const localIP = getIP(transport[i].localip);
|
|
|
|
const localPort = getPort(transport[i].localip);
|
|
|
|
const port = getPort(transport[i].ip);
|
|
|
|
|
|
|
|
if (!data.remoteIP.includes(ip)) {
|
|
|
|
data.remoteIP.push(ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data.localIP.includes(localIP)) {
|
|
|
|
data.localIP.push(localIP);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data.localPort.includes(localPort)) {
|
|
|
|
data.localPort.push(localPort);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data.remotePort.includes(port)) {
|
|
|
|
data.remotePort.push(port);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data.transportType.includes(transport[i].type)) {
|
|
|
|
data.transportType.push(transport[i].type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All of the transports should be either P2P or JVB
|
2017-07-12 11:19:07 +00:00
|
|
|
let isP2P = false, isTURN = false;
|
|
|
|
|
|
|
|
if (transport.length) {
|
|
|
|
isP2P = transport[0].p2p;
|
|
|
|
isTURN = transport[0].localCandidateType === 'relay'
|
|
|
|
|| transport[0].remoteCandidateType === 'relay';
|
|
|
|
}
|
|
|
|
|
|
|
|
let additionalData = null;
|
|
|
|
|
|
|
|
if (isP2P) {
|
|
|
|
additionalData = isTURN
|
|
|
|
? <span>{ t('connectionindicator.turn') }</span>
|
|
|
|
: <span>{ t('connectionindicator.peer_to_peer') }</span>;
|
|
|
|
}
|
2017-06-16 14:02:39 +00:00
|
|
|
|
|
|
|
// First show remote statistics, then local, and then transport type.
|
|
|
|
const tableRowConfigurations = [
|
|
|
|
{
|
2017-07-12 11:19:07 +00:00
|
|
|
additionalData,
|
2017-06-16 14:02:39 +00:00
|
|
|
data: data.remoteIP,
|
|
|
|
key: 'remoteaddress',
|
|
|
|
label: t('connectionindicator.remoteaddress',
|
|
|
|
{ count: data.remoteIP.length })
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: data.remotePort,
|
|
|
|
key: 'remoteport',
|
|
|
|
label: t('connectionindicator.remoteport',
|
|
|
|
{ count: transport.length })
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: data.localIP,
|
|
|
|
key: 'localaddress',
|
|
|
|
label: t('connectionindicator.localaddress',
|
|
|
|
{ count: data.localIP.length })
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: data.localPort,
|
|
|
|
key: 'localport',
|
|
|
|
label: t('connectionindicator.localport',
|
|
|
|
{ count: transport.length })
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: data.transportType,
|
|
|
|
key: 'transport',
|
|
|
|
label: t('connectionindicator.transport',
|
|
|
|
{ count: data.transportType.length })
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
return tableRowConfigurations.map(this._renderTransportTableRow);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a table row as a ReactElement for displaying a transport related
|
|
|
|
* statistic.
|
|
|
|
*
|
|
|
|
* @param {Object} config - Describes the contents of the row.
|
|
|
|
* @param {ReactElement} config.additionalData - Extra data to display next
|
|
|
|
* to the passed in config.data.
|
|
|
|
* @param {Array} config.data - The transport statistics to display.
|
|
|
|
* @param {string} config.key - The ReactElement's key. Must be unique for
|
|
|
|
* iterating over multiple child rows.
|
|
|
|
* @param {string} config.label - The text to display describing the data.
|
|
|
|
* @private
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderTransportTableRow(config) {
|
|
|
|
const { additionalData, data, key, label } = config;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr key = { key }>
|
|
|
|
<td>
|
|
|
|
<span>
|
|
|
|
{ label }
|
|
|
|
</span>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
{ getStringFromArray(data) }
|
|
|
|
{ additionalData || null }
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility for getting the IP from a transport statistics object's
|
|
|
|
* representation of an IP.
|
|
|
|
*
|
|
|
|
* @param {string} value - The transport's IP to parse.
|
|
|
|
* @private
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
function getIP(value) {
|
2017-08-28 21:49:24 +00:00
|
|
|
if (!value) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2017-06-16 14:02:39 +00:00
|
|
|
return value.substring(0, value.lastIndexOf(':'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility for getting the port from a transport statistics object's
|
|
|
|
* representation of an IP.
|
|
|
|
*
|
|
|
|
* @param {string} value - The transport's IP to parse.
|
|
|
|
* @private
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
function getPort(value) {
|
2017-08-28 21:49:24 +00:00
|
|
|
if (!value) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2017-06-16 14:02:39 +00:00
|
|
|
return value.substring(value.lastIndexOf(':') + 1, value.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility for concatenating values in an array into a comma separated string.
|
|
|
|
*
|
|
|
|
* @param {Array} array - Transport statistics to concatenate.
|
|
|
|
* @private
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
function getStringFromArray(array) {
|
|
|
|
let res = '';
|
|
|
|
|
|
|
|
for (let i = 0; i < array.length; i++) {
|
|
|
|
res += (i === 0 ? '' : ', ') + array[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default translate(ConnectionStatsTable);
|