You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
14 KiB
532 lines
14 KiB
3 months ago
|
import React from 'react';
|
||
|
import { connect } from 'react-redux';
|
||
|
import PropTypes from 'prop-types';
|
||
|
import classnames from 'classnames';
|
||
|
import { Appear } from './transitions';
|
||
|
import { withRoomContext } from '../RoomContext';
|
||
|
import * as stateActions from '../redux/stateActions';
|
||
|
|
||
|
class Stats extends React.Component
|
||
|
{
|
||
|
constructor(props)
|
||
|
{
|
||
|
super(props);
|
||
|
|
||
|
this.state =
|
||
|
{
|
||
|
sendTransportRemoteStats : null,
|
||
|
sendTransportLocalStats : null,
|
||
|
recvTransportRemoteStats : null,
|
||
|
recvTransportLocalStats : null,
|
||
|
audioProducerRemoteStats : null,
|
||
|
audioProducerLocalStats : null,
|
||
|
videoProducerRemoteStats : null,
|
||
|
videoProducerLocalStats : null,
|
||
|
chatDataProducerRemoteStats : null,
|
||
|
botDataProducerRemoteStats : null,
|
||
|
audioConsumerRemoteStats : null,
|
||
|
audioConsumerLocalStats : null,
|
||
|
videoConsumerRemoteStats : null,
|
||
|
videoConsumerLocalStats : null,
|
||
|
chatDataConsumerRemoteStats : null,
|
||
|
botDataConsumerRemoteStats : null
|
||
|
};
|
||
|
|
||
|
this._delayTimer = null;
|
||
|
}
|
||
|
|
||
|
render()
|
||
|
{
|
||
|
const {
|
||
|
peerId,
|
||
|
peerDisplayName,
|
||
|
isMe,
|
||
|
onClose
|
||
|
} = this.props;
|
||
|
|
||
|
const {
|
||
|
sendTransportRemoteStats,
|
||
|
sendTransportLocalStats,
|
||
|
recvTransportRemoteStats,
|
||
|
recvTransportLocalStats,
|
||
|
audioProducerRemoteStats,
|
||
|
audioProducerLocalStats,
|
||
|
videoProducerRemoteStats,
|
||
|
videoProducerLocalStats,
|
||
|
chatDataProducerRemoteStats,
|
||
|
botDataProducerRemoteStats,
|
||
|
audioConsumerRemoteStats,
|
||
|
audioConsumerLocalStats,
|
||
|
videoConsumerRemoteStats,
|
||
|
videoConsumerLocalStats,
|
||
|
chatDataConsumerRemoteStats,
|
||
|
botDataConsumerRemoteStats
|
||
|
} = this.state;
|
||
|
|
||
|
return (
|
||
|
<div data-component='Stats'>
|
||
|
<div className={classnames('content', { visible: peerId })}>
|
||
|
<div className='header'>
|
||
|
<div className='info'>
|
||
|
<div
|
||
|
className='close-icon'
|
||
|
onClick={onClose}
|
||
|
/>
|
||
|
|
||
|
<Choose>
|
||
|
<When condition={isMe}>
|
||
|
<h1>Your Stats</h1>
|
||
|
</When>
|
||
|
|
||
|
<Otherwise>
|
||
|
<h1>Stats of {peerDisplayName}</h1>
|
||
|
</Otherwise>
|
||
|
</Choose>
|
||
|
</div>
|
||
|
|
||
|
<div className='list'>
|
||
|
<If condition={sendTransportRemoteStats || sendTransportLocalStats}>
|
||
|
<p>
|
||
|
{'send transport stats: '}
|
||
|
<a href='#send-transport-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a href='#send-transport-local-stats'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={recvTransportRemoteStats || recvTransportLocalStats}>
|
||
|
<p>
|
||
|
{'recv transport stats: '}
|
||
|
<a href='#recv-transport-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a href='#recv-transport-local-stats'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={audioProducerRemoteStats || audioProducerLocalStats}>
|
||
|
<p>
|
||
|
{'audio producer stats: '}
|
||
|
<a href='#audio-producer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a href='#audio-producer-local-stats'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={videoProducerRemoteStats || videoProducerLocalStats}>
|
||
|
<p>
|
||
|
{'video producer stats: '}
|
||
|
<a href='#video-producer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a href='#video-producer-local-stats'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={chatDataProducerRemoteStats}>
|
||
|
<p>
|
||
|
{'chat dataproducer stats: '}
|
||
|
<a href='#chat-dataproducer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a className='disabled'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={botDataProducerRemoteStats}>
|
||
|
<p>
|
||
|
{'bot dataproducer stats: '}
|
||
|
<a href='#bot-dataproducer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a className='disabled'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={audioConsumerRemoteStats || audioConsumerLocalStats}>
|
||
|
<p>
|
||
|
{'audio consumer stats: '}
|
||
|
<a href='#audio-consumer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a href='#audio-consumer-local-stats'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={videoConsumerRemoteStats || videoConsumerLocalStats}>
|
||
|
<p>
|
||
|
{'video consumer stats: '}
|
||
|
<a href='#video-consumer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a href='#video-consumer-local-stats'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={chatDataConsumerRemoteStats}>
|
||
|
<p>
|
||
|
{'chat dataconsumer stats: '}
|
||
|
<a href='#chat-dataconsumer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a className='disabled'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
|
||
|
<If condition={botDataConsumerRemoteStats}>
|
||
|
<p>
|
||
|
{'bot dataconsumer stats: '}
|
||
|
<a href='#bot-dataconsumer-remote-stats'>[remote]</a>
|
||
|
<span>{' '}</span>
|
||
|
<a className='disabled'>[local]</a>
|
||
|
</p>
|
||
|
</If>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div className='stats'>
|
||
|
<If condition={sendTransportRemoteStats}>
|
||
|
{this._printStats('send transport remote stats', sendTransportRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={sendTransportLocalStats}>
|
||
|
{this._printStats('send transport local stats', sendTransportLocalStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={recvTransportRemoteStats}>
|
||
|
{this._printStats('recv transport remote stats', recvTransportRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={recvTransportLocalStats}>
|
||
|
{this._printStats('recv transport local stats', recvTransportLocalStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={audioProducerRemoteStats}>
|
||
|
{this._printStats('audio producer remote stats', audioProducerRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={audioProducerLocalStats}>
|
||
|
{this._printStats('audio producer local stats', audioProducerLocalStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={videoProducerRemoteStats}>
|
||
|
{this._printStats('video producer remote stats', videoProducerRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={videoProducerLocalStats}>
|
||
|
{this._printStats('video producer local stats', videoProducerLocalStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={chatDataProducerRemoteStats}>
|
||
|
{this._printStats('chat dataproducer remote stats', chatDataProducerRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={botDataProducerRemoteStats}>
|
||
|
{this._printStats('bot dataproducer remote stats', botDataProducerRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={audioConsumerRemoteStats}>
|
||
|
{this._printStats('audio consumer remote stats', audioConsumerRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={audioConsumerLocalStats}>
|
||
|
{this._printStats('audio consumer local stats', audioConsumerLocalStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={videoConsumerRemoteStats}>
|
||
|
{this._printStats('video consumer remote stats', videoConsumerRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={videoConsumerLocalStats}>
|
||
|
{this._printStats('video consumer local stats', videoConsumerLocalStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={chatDataConsumerRemoteStats}>
|
||
|
{this._printStats('chat dataconsumer remote stats', chatDataConsumerRemoteStats)}
|
||
|
</If>
|
||
|
|
||
|
<If condition={botDataConsumerRemoteStats}>
|
||
|
{this._printStats('bot dataconsumer remote stats', botDataConsumerRemoteStats)}
|
||
|
</If>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
componentDidUpdate(prevProps)
|
||
|
{
|
||
|
const { peerId } = this.props;
|
||
|
|
||
|
if (peerId && !prevProps.peerId)
|
||
|
{
|
||
|
this._delayTimer = setTimeout(() => this._start(), 250);
|
||
|
}
|
||
|
else if (!peerId && prevProps.peerId)
|
||
|
{
|
||
|
this._stop();
|
||
|
}
|
||
|
else if (peerId && prevProps.peerId && peerId !== prevProps.peerId)
|
||
|
{
|
||
|
this._stop();
|
||
|
this._start();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async _start()
|
||
|
{
|
||
|
const {
|
||
|
roomClient,
|
||
|
isMe,
|
||
|
audioConsumerId,
|
||
|
videoConsumerId,
|
||
|
chatDataConsumerId,
|
||
|
botDataConsumerId
|
||
|
} = this.props;
|
||
|
|
||
|
let sendTransportRemoteStats = null;
|
||
|
let sendTransportLocalStats = null;
|
||
|
let recvTransportRemoteStats = null;
|
||
|
let recvTransportLocalStats = null;
|
||
|
let audioProducerRemoteStats = null;
|
||
|
let audioProducerLocalStats = null;
|
||
|
let videoProducerRemoteStats = null;
|
||
|
let videoProducerLocalStats = null;
|
||
|
let chatDataProducerRemoteStats = null;
|
||
|
let botDataProducerRemoteStats = null;
|
||
|
let audioConsumerRemoteStats = null;
|
||
|
let audioConsumerLocalStats = null;
|
||
|
let videoConsumerRemoteStats = null;
|
||
|
let videoConsumerLocalStats = null;
|
||
|
let chatDataConsumerRemoteStats = null;
|
||
|
let botDataConsumerRemoteStats = null;
|
||
|
|
||
|
if (isMe)
|
||
|
{
|
||
|
sendTransportRemoteStats = await roomClient.getSendTransportRemoteStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
sendTransportLocalStats = await roomClient.getSendTransportLocalStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
recvTransportRemoteStats = await roomClient.getRecvTransportRemoteStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
recvTransportLocalStats = await roomClient.getRecvTransportLocalStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
audioProducerRemoteStats = await roomClient.getAudioRemoteStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
audioProducerLocalStats = await roomClient.getAudioLocalStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
videoProducerRemoteStats = await roomClient.getVideoRemoteStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
videoProducerLocalStats = await roomClient.getVideoLocalStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
chatDataProducerRemoteStats = await roomClient.getChatDataProducerRemoteStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
botDataProducerRemoteStats = await roomClient.getBotDataProducerRemoteStats()
|
||
|
.catch(() => {});
|
||
|
|
||
|
botDataConsumerRemoteStats =
|
||
|
await roomClient.getDataConsumerRemoteStats(botDataConsumerId)
|
||
|
.catch(() => {});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
audioConsumerRemoteStats = await roomClient.getConsumerRemoteStats(audioConsumerId)
|
||
|
.catch(() => {});
|
||
|
|
||
|
audioConsumerLocalStats = await roomClient.getConsumerLocalStats(audioConsumerId)
|
||
|
.catch(() => {});
|
||
|
|
||
|
videoConsumerRemoteStats = await roomClient.getConsumerRemoteStats(videoConsumerId)
|
||
|
.catch(() => {});
|
||
|
|
||
|
videoConsumerLocalStats = await roomClient.getConsumerLocalStats(videoConsumerId)
|
||
|
.catch(() => {});
|
||
|
|
||
|
chatDataConsumerRemoteStats =
|
||
|
await roomClient.getDataConsumerRemoteStats(chatDataConsumerId)
|
||
|
.catch(() => {});
|
||
|
}
|
||
|
|
||
|
this.setState(
|
||
|
{
|
||
|
sendTransportRemoteStats,
|
||
|
sendTransportLocalStats,
|
||
|
recvTransportRemoteStats,
|
||
|
recvTransportLocalStats,
|
||
|
audioProducerRemoteStats,
|
||
|
audioProducerLocalStats,
|
||
|
videoProducerRemoteStats,
|
||
|
videoProducerLocalStats,
|
||
|
chatDataProducerRemoteStats,
|
||
|
botDataProducerRemoteStats,
|
||
|
audioConsumerRemoteStats,
|
||
|
audioConsumerLocalStats,
|
||
|
videoConsumerRemoteStats,
|
||
|
videoConsumerLocalStats,
|
||
|
chatDataConsumerRemoteStats,
|
||
|
botDataConsumerRemoteStats
|
||
|
});
|
||
|
|
||
|
this._delayTimer = setTimeout(() => this._start(), 2500);
|
||
|
}
|
||
|
|
||
|
_stop()
|
||
|
{
|
||
|
clearTimeout(this._delayTimer);
|
||
|
|
||
|
this.setState(
|
||
|
{
|
||
|
sendTransportRemoteStats : null,
|
||
|
sendTransportLocalStats : null,
|
||
|
recvTransportRemoteStats : null,
|
||
|
recvTransportLocalStats : null,
|
||
|
audioProducerRemoteStats : null,
|
||
|
audioProducerLocalStats : null,
|
||
|
videoProducerRemoteStats : null,
|
||
|
videoProducerLocalStats : null,
|
||
|
chatDataProducerRemoteStats : null,
|
||
|
botDataProducerRemoteStats : null,
|
||
|
audioConsumerRemoteStats : null,
|
||
|
audioConsumerLocalStats : null,
|
||
|
videoConsumerRemoteStats : null,
|
||
|
videoConsumerLocalStats : null,
|
||
|
chatDataConsumerRemoteStats : null,
|
||
|
botDataConsumerRemoteStats : null
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_printStats(title, stats)
|
||
|
{
|
||
|
const anchor = title
|
||
|
.replace(/[ ]+/g, '-');
|
||
|
|
||
|
if (typeof stats.values === 'function')
|
||
|
stats = Array.from(stats.values());
|
||
|
|
||
|
return (
|
||
|
<Appear duration={150}>
|
||
|
<div className='items'>
|
||
|
<h2 id={anchor}>{title}</h2>
|
||
|
|
||
|
{
|
||
|
stats.map((item, idx) => (
|
||
|
<div className='item' key={idx}>
|
||
|
{
|
||
|
Object.keys(item).map((key) => (
|
||
|
<div className='line' key={key}>
|
||
|
<p className='key'>{key}</p>
|
||
|
<div className='value'>
|
||
|
<Choose>
|
||
|
<When condition={typeof item[key] === 'number'}>
|
||
|
{JSON.stringify(Math.round(item[key] * 100) / 100, null, ' ')}
|
||
|
</When>
|
||
|
|
||
|
<Otherwise>
|
||
|
<pre>{JSON.stringify(item[key], null, ' ')}</pre>
|
||
|
</Otherwise>
|
||
|
</Choose>
|
||
|
</div>
|
||
|
</div>
|
||
|
))
|
||
|
}
|
||
|
</div>
|
||
|
))
|
||
|
}
|
||
|
</div>
|
||
|
</Appear>
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Stats.propTypes =
|
||
|
{
|
||
|
roomClient : PropTypes.any.isRequired,
|
||
|
peerId : PropTypes.string,
|
||
|
peerDisplayName : PropTypes.string,
|
||
|
isMe : PropTypes.bool,
|
||
|
audioConsumerId : PropTypes.string,
|
||
|
videoConsumerId : PropTypes.string,
|
||
|
chatDataConsumerId : PropTypes.string,
|
||
|
botDataConsumerId : PropTypes.string,
|
||
|
onClose : PropTypes.func.isRequired
|
||
|
};
|
||
|
|
||
|
const mapStateToProps = (state) =>
|
||
|
{
|
||
|
const { room, me, peers, consumers, dataConsumers } = state;
|
||
|
const { statsPeerId } = room;
|
||
|
|
||
|
if (!statsPeerId)
|
||
|
return {};
|
||
|
|
||
|
const isMe = statsPeerId === me.id;
|
||
|
const peer = isMe ? me : peers[statsPeerId];
|
||
|
let audioConsumerId;
|
||
|
let videoConsumerId;
|
||
|
let chatDataConsumerId;
|
||
|
let botDataConsumerId;
|
||
|
|
||
|
if (isMe)
|
||
|
{
|
||
|
for (const dataConsumerId of Object.keys(dataConsumers))
|
||
|
{
|
||
|
const dataConsumer = dataConsumers[dataConsumerId];
|
||
|
|
||
|
if (dataConsumer.label === 'bot')
|
||
|
botDataConsumerId = dataConsumer.id;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (const consumerId of peer.consumers)
|
||
|
{
|
||
|
const consumer = consumers[consumerId];
|
||
|
|
||
|
switch (consumer.track.kind)
|
||
|
{
|
||
|
case 'audio':
|
||
|
audioConsumerId = consumer.id;
|
||
|
break;
|
||
|
|
||
|
case 'video':
|
||
|
videoConsumerId = consumer.id;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (const dataConsumerId of peer.dataConsumers)
|
||
|
{
|
||
|
const dataConsumer = dataConsumers[dataConsumerId];
|
||
|
|
||
|
if (dataConsumer.label === 'chat')
|
||
|
chatDataConsumerId = dataConsumer.id;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
peerId : peer.id,
|
||
|
peerDisplayName : peer.displayName,
|
||
|
isMe,
|
||
|
audioConsumerId,
|
||
|
videoConsumerId,
|
||
|
chatDataConsumerId,
|
||
|
botDataConsumerId
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const mapDispatchToProps = (dispatch) =>
|
||
|
{
|
||
|
return {
|
||
|
onClose : () => dispatch(stateActions.setRoomStatsPeerId(null))
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const StatsContainer = withRoomContext(connect(
|
||
|
mapStateToProps,
|
||
|
mapDispatchToProps
|
||
|
)(Stats));
|
||
|
|
||
|
export default StatsContainer;
|