create component "NetworkIndicator"
add to VideoView for Me, receive data from server / getTransportStats, estimate of connection/value [wip] show recv/send bitrate in advanced modeauto_join_3.3
parent
cb5f4cd48e
commit
b73b8c1aa0
|
|
@ -32,6 +32,7 @@
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"react-wakelock-react16": "0.0.7",
|
"react-wakelock-react16": "0.0.7",
|
||||||
|
"react-wifi-indicator": "^1.0.1",
|
||||||
"redux": "^4.0.4",
|
"redux": "^4.0.4",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,7 @@ export default class RoomClient
|
||||||
this._startKeyListener();
|
this._startKeyListener();
|
||||||
|
|
||||||
this._startDevicesListener();
|
this._startDevicesListener();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close()
|
close()
|
||||||
|
|
@ -594,6 +595,21 @@ export default class RoomClient
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTransportStats(transportId)
|
||||||
|
{
|
||||||
|
|
||||||
|
logger.debug('getTransportStats() [transportId: "%s"]', transportId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await this.sendRequest('getTransportStats', { transportId: transportId });
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('getTransportStats() | failed: %o', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async changeDisplayName(displayName)
|
async changeDisplayName(displayName)
|
||||||
{
|
{
|
||||||
logger.debug('changeDisplayName() [displayName:"%s"]', displayName);
|
logger.debug('changeDisplayName() [displayName:"%s"]', displayName);
|
||||||
|
|
@ -1959,6 +1975,7 @@ export default class RoomClient
|
||||||
{
|
{
|
||||||
switch (notification.method)
|
switch (notification.method)
|
||||||
{
|
{
|
||||||
|
|
||||||
case 'enteredLobby':
|
case 'enteredLobby':
|
||||||
{
|
{
|
||||||
store.dispatch(roomActions.setInLobby(true));
|
store.dispatch(roomActions.setInLobby(true));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,259 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
peersLengthSelector
|
||||||
|
} from '../Selectors';
|
||||||
|
import * as appPropTypes from '../appPropTypes';
|
||||||
|
import { withRoomContext } from '../../RoomContext';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import WifiIndicator from 'react-wifi-indicator';
|
||||||
|
import Logger from '../../Logger';
|
||||||
|
|
||||||
|
const logger = new Logger('NetworkIndicator');
|
||||||
|
|
||||||
|
const styles = () =>
|
||||||
|
({
|
||||||
|
root : {
|
||||||
|
verticalAlign : 'middle',
|
||||||
|
'& img' : {
|
||||||
|
display : 'inline',
|
||||||
|
width : '1.7em',
|
||||||
|
height : '1.7em',
|
||||||
|
margin : '10px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
label : {
|
||||||
|
color : 'white'
|
||||||
|
},
|
||||||
|
|
||||||
|
strength :
|
||||||
|
{
|
||||||
|
margin : 0,
|
||||||
|
padding : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
class NetworkIndicator extends React.Component
|
||||||
|
{
|
||||||
|
constructor(props)
|
||||||
|
{
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
strengthScale : {
|
||||||
|
1 : 'EXCELLENT',
|
||||||
|
2 : 'GREAT',
|
||||||
|
3 : 'OKAY',
|
||||||
|
4 : 'WEAK',
|
||||||
|
5 : 'UNUSABLE',
|
||||||
|
6 : 'DISCONNECTED'
|
||||||
|
},
|
||||||
|
strength : 6,
|
||||||
|
bitrate : null,
|
||||||
|
recv : {},
|
||||||
|
send : {},
|
||||||
|
probe : [],
|
||||||
|
currBitrate : 0,
|
||||||
|
maxBitrate : 0,
|
||||||
|
avgBitrate : 0,
|
||||||
|
medBitrate : 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// const intl = useIntl();
|
||||||
|
async handleUpdateStrength()
|
||||||
|
{
|
||||||
|
// if (this.props.peersLength == 0)
|
||||||
|
// {
|
||||||
|
|
||||||
|
const percent = this.state.percent;
|
||||||
|
|
||||||
|
logger.warn('[percent: "%s"]', percent);
|
||||||
|
|
||||||
|
switch (true)
|
||||||
|
{
|
||||||
|
case (percent <= 20):
|
||||||
|
|
||||||
|
await this.setState({ strength: 5 });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (percent <= 40):
|
||||||
|
|
||||||
|
await this.setState({ strength: 4 });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (percent <= 60):
|
||||||
|
|
||||||
|
await this.setState({ strength: 3 });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (percent <= 80):
|
||||||
|
|
||||||
|
await this.setState({ strength: 2 });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (percent <= 100):
|
||||||
|
|
||||||
|
await this.setState({ strength: 1 });
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// this.setState({ strength: 6 });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleGetData()
|
||||||
|
{
|
||||||
|
const rc = this.props.roomClient;
|
||||||
|
|
||||||
|
const recv = await rc.getTransportStats(rc._recvTransport.id);
|
||||||
|
|
||||||
|
const send = await rc.getTransportStats(rc._sendTransport.id);
|
||||||
|
|
||||||
|
// current
|
||||||
|
const currBitrate = Math.round(send[0].recvBitrate / 1024 / 8); // in kb
|
||||||
|
|
||||||
|
// probe
|
||||||
|
const probe = [ ...this.state.probe ]; // clone
|
||||||
|
|
||||||
|
const sec = new Date().getSeconds()
|
||||||
|
.toString()
|
||||||
|
.split('')
|
||||||
|
.map(Number)[1];
|
||||||
|
|
||||||
|
probe[sec] = currBitrate; // add/update next element
|
||||||
|
|
||||||
|
// median
|
||||||
|
const med = (arr) =>
|
||||||
|
{
|
||||||
|
const mid = Math.floor(arr.length / 2);
|
||||||
|
const nums = [ ...arr ].sort((a, b) => a - b);
|
||||||
|
|
||||||
|
return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
const medBitrate = med([ ...probe ]);
|
||||||
|
|
||||||
|
// maximum
|
||||||
|
let maxBitrate = Math.max(...probe);
|
||||||
|
|
||||||
|
maxBitrate = (currBitrate > maxBitrate) ? currBitrate : maxBitrate;
|
||||||
|
|
||||||
|
// average
|
||||||
|
const avgBitrate = [ ...probe ]
|
||||||
|
.map((x, i, avgBitrate) => x/avgBitrate.length)
|
||||||
|
.reduce((a, b) => a + b);
|
||||||
|
|
||||||
|
const percent =
|
||||||
|
await Math.round(this.state.currBitrate / this.state.medBitrate * 100);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
recv : recv[0],
|
||||||
|
send : send[0],
|
||||||
|
probe,
|
||||||
|
currBitrate,
|
||||||
|
maxBitrate,
|
||||||
|
avgBitrate,
|
||||||
|
medBitrate,
|
||||||
|
percent
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.warn('[currBitrate: "%s"]', currBitrate);
|
||||||
|
logger.warn('[maxBitrate: "%s"]', maxBitrate);
|
||||||
|
logger.warn('[medBitrate: "%s"]', medBitrate);
|
||||||
|
logger.warn('[avgBitrate: "%s"]', avgBitrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount()
|
||||||
|
{
|
||||||
|
this.update = setInterval(async () =>
|
||||||
|
{
|
||||||
|
await this.handleGetData();
|
||||||
|
await this.handleUpdateStrength();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount()
|
||||||
|
{
|
||||||
|
clearInterval(this.update);
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
classes,
|
||||||
|
advancedMode
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<span className={classes.root}>
|
||||||
|
<WifiIndicator
|
||||||
|
strength={this.state.strengthScale[this.state.strength]}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{advancedMode &&
|
||||||
|
<span className={classes.label}>
|
||||||
|
{/* rr:{ Math.round(this.state.recv.recvBitrate / 1024 /8) || 0}, */}
|
||||||
|
{ Math.round(this.state.recv.sendBitrate / 1024 /8) || 0}kb ⇙ |
|
||||||
|
{ Math.round(this.state.send.recvBitrate / 1024 /8) || 0}kb ⇗
|
||||||
|
{/* ss:{ Math.round(this.state.send.sendBitrate / 1024) /8 || 0} */}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkIndicator.propTypes =
|
||||||
|
{
|
||||||
|
roomClient : PropTypes.object.isRequired,
|
||||||
|
room : appPropTypes.Room.isRequired,
|
||||||
|
peersLength : PropTypes.number,
|
||||||
|
theme : PropTypes.object.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired,
|
||||||
|
me : PropTypes.object.isRequired,
|
||||||
|
advancedMode : PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state) =>
|
||||||
|
({
|
||||||
|
room : state.room,
|
||||||
|
advancedMode : state.settings.advancedMode,
|
||||||
|
peersLength : peersLengthSelector(state),
|
||||||
|
me : state.me
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) =>
|
||||||
|
({
|
||||||
|
// toggleToolArea : () =>
|
||||||
|
// {
|
||||||
|
// dispatch(toolareaActions.toggleToolArea());
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withRoomContext(connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
areStatesEqual : (next, prev) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
prev.room === next.room &&
|
||||||
|
prev.peers === next.peers &&
|
||||||
|
prev.settings.advancedMode === next.settings.advancedMode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)(withStyles(styles, { withTheme: true })(NetworkIndicator)));
|
||||||
|
|
@ -10,6 +10,7 @@ import SignalCellular1BarIcon from '@material-ui/icons/SignalCellular1Bar';
|
||||||
import SignalCellular2BarIcon from '@material-ui/icons/SignalCellular2Bar';
|
import SignalCellular2BarIcon from '@material-ui/icons/SignalCellular2Bar';
|
||||||
import SignalCellular3BarIcon from '@material-ui/icons/SignalCellular3Bar';
|
import SignalCellular3BarIcon from '@material-ui/icons/SignalCellular3Bar';
|
||||||
import SignalCellularAltIcon from '@material-ui/icons/SignalCellularAlt';
|
import SignalCellularAltIcon from '@material-ui/icons/SignalCellularAlt';
|
||||||
|
import NetworkIndicator from '../Controls/NetworkIndicator';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
|
|
@ -267,6 +268,7 @@ class VideoView extends React.PureComponent
|
||||||
<div className={classes.peer}>
|
<div className={classes.peer}>
|
||||||
<div className={classes.box}>
|
<div className={classes.box}>
|
||||||
{ isMe ?
|
{ isMe ?
|
||||||
|
<React.Fragment>
|
||||||
<EditableInput
|
<EditableInput
|
||||||
value={displayName}
|
value={displayName}
|
||||||
propName='newDisplayName'
|
propName='newDisplayName'
|
||||||
|
|
@ -279,8 +281,12 @@ class VideoView extends React.PureComponent
|
||||||
autoCorrect : 'off',
|
autoCorrect : 'off',
|
||||||
spellCheck : false
|
spellCheck : false
|
||||||
}}
|
}}
|
||||||
onChange={({ newDisplayName }) => onChangeDisplayName(newDisplayName)}
|
onChange={
|
||||||
|
({ newDisplayName }) =>
|
||||||
|
onChangeDisplayName(newDisplayName)}
|
||||||
/>
|
/>
|
||||||
|
<NetworkIndicator />
|
||||||
|
</React.Fragment>
|
||||||
:
|
:
|
||||||
<span className={classes.displayNameStatic}>
|
<span className={classes.displayNameStatic}>
|
||||||
{displayName}
|
{displayName}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue