Merge branch 'develop' into feat-unsupportedbrowser

auto_join_3.3
Mészáros Mihály 2020-05-19 15:07:25 +02:00
commit ee1a691642
38 changed files with 621 additions and 333 deletions

2
app/.env 100644
View File

@ -0,0 +1,2 @@
REACT_APP_VERSION=$npm_package_version
REACT_APP_NAME=$npm_package_name

View File

@ -37,7 +37,8 @@ var config =
'opera' 'opera'
], ],
// Socket.io request timeout // Socket.io request timeout
requestTimeout : 10000, requestTimeout : 20000,
requestRetries : 3,
transportOptions : transportOptions :
{ {
tcp : true tcp : true
@ -50,6 +51,7 @@ var config =
autoGainControl : true, autoGainControl : true,
echoCancellation : true, echoCancellation : true,
noiseSuppression : true, noiseSuppression : true,
voiceActivityMute : false,
sampleSize : 16 sampleSize : 16
}, },

View File

@ -1,6 +1,7 @@
import Logger from './Logger'; import Logger from './Logger';
import hark from 'hark'; import hark from 'hark';
import { getSignalingUrl } from './urlFactory'; import { getSignalingUrl } from './urlFactory';
import { SocketTimeoutError } from './utils';
import * as requestActions from './actions/requestActions'; import * as requestActions from './actions/requestActions';
import * as meActions from './actions/meActions'; import * as meActions from './actions/meActions';
import * as roomActions from './actions/roomActions'; import * as roomActions from './actions/roomActions';
@ -574,7 +575,7 @@ export default class RoomClient
if (called) if (called)
return; return;
called = true; called = true;
callback(new Error('Request timeout.')); callback(new SocketTimeoutError('Request timed out'));
}, },
ROOM_OPTIONS.requestTimeout ROOM_OPTIONS.requestTimeout
); );
@ -590,13 +591,13 @@ export default class RoomClient
}; };
} }
sendRequest(method, data) _sendRequest(method, data)
{ {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
if (!this._signalingSocket) if (!this._signalingSocket)
{ {
reject('No socket connection.'); reject('No socket connection');
} }
else else
{ {
@ -606,19 +607,42 @@ export default class RoomClient
this.timeoutCallback((err, response) => this.timeoutCallback((err, response) =>
{ {
if (err) if (err)
{
reject(err); reject(err);
}
else else
{
resolve(response); resolve(response);
}
}) })
); );
} }
}); });
} }
async sendRequest(method, data)
{
logger.debug('sendRequest() [method:"%s", data:"%o"]', method, data);
const {
requestRetries = 3
} = window.config;
for (let tries = 0; tries < requestRetries; tries++)
{
try
{
return await this._sendRequest(method, data);
}
catch (error)
{
if (
error instanceof SocketTimeoutError &&
tries < requestRetries
)
logger.warn('sendRequest() | timeout, retrying [attempt:"%s"]', tries);
else
throw error;
}
}
}
async changeDisplayName(displayName) async changeDisplayName(displayName)
{ {
logger.debug('changeDisplayName() [displayName:"%s"]', displayName); logger.debug('changeDisplayName() [displayName:"%s"]', displayName);
@ -1003,37 +1027,50 @@ export default class RoomClient
if (!this._harkStream.getAudioTracks()[0]) if (!this._harkStream.getAudioTracks()[0])
throw new Error('getMicStream():something went wrong with hark'); throw new Error('getMicStream():something went wrong with hark');
this._hark = hark(this._harkStream, { play: false }); this._hark = hark(this._harkStream,
// eslint-disable-next-line no-unused-vars
this._hark.on('volume_change', (dBs, threshold) =>
{ {
// The exact formula to convert from dBs (-100..0) to linear (0..1) is: play : false,
// Math.pow(10, dBs / 20) interval : 5,
// However it does not produce a visually useful output, so let exagerate threshold : store.getState().settings.noiseThreshold,
// it a bit. Also, let convert it from 0..1 to 0..10 and avoid value 1 to history : 300
// minimize component renderings. });
let volume = Math.round(Math.pow(10, dBs / 85) * 10); this._hark.lastVolume = -100;
if (volume === 1)
volume = 0;
this._hark.on('volume_change', (volume) =>
{
volume = Math.round(volume); volume = Math.round(volume);
if (this._micProducer && volume !== Math.round(this._hark.lastVolume))
if (this._micProducer && volume !== this._micProducer.volume)
{ {
this._micProducer.volume = volume; if (volume < this._hark.lastVolume * 1.02)
{
volume = this._hark.lastVolume * 1.02;
}
this._hark.lastVolume = volume;
store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume)); store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume));
} }
}); });
this._hark.on('speaking', function() this._hark.on('speaking', () =>
{ {
store.dispatch(meActions.setIsSpeaking(true)); store.dispatch(meActions.setIsSpeaking(true));
if ((store.getState().settings.voiceActivatedUnmute ||
store.getState().me.isAutoMuted) &&
this._micProducer &&
this._micProducer.paused)
{
this._micProducer.resume();
}
store.dispatch(meActions.setAutoMuted(false)); // sanity action
}); });
this._hark.on('stopped_speaking', function() this._hark.on('stopped_speaking', () =>
{ {
store.dispatch(meActions.setIsSpeaking(false)); store.dispatch(meActions.setIsSpeaking(false));
if (store.getState().settings.voiceActivatedUnmute &&
this._micProducer &&
!this._micProducer.paused)
{
this._micProducer.pause();
store.dispatch(meActions.setAutoMuted(true));
}
}); });
} }
@ -1962,23 +1999,12 @@ export default class RoomClient
producerPaused producerPaused
} = request.data; } = request.data;
let codecOptions;
if (kind === 'audio')
{
codecOptions =
{
opusStereo : 1
};
}
const consumer = await this._recvTransport.consume( const consumer = await this._recvTransport.consume(
{ {
id, id,
producerId, producerId,
kind, kind,
rtpParameters, rtpParameters,
codecOptions,
appData : { ...appData, peerId } // Trick. appData : { ...appData, peerId } // Trick.
}); });
@ -2031,19 +2057,8 @@ export default class RoomClient
consumer.hark = hark(stream, { play: false }); consumer.hark = hark(stream, { play: false });
// eslint-disable-next-line no-unused-vars consumer.hark.on('volume_change', (volume) =>
consumer.hark.on('volume_change', (dBs, threshold) =>
{ {
// The exact formula to convert from dBs (-100..0) to linear (0..1) is:
// Math.pow(10, dBs / 20)
// However it does not produce a visually useful output, so let exaggerate
// it a bit. Also, let convert it from 0..1 to 0..10 and avoid value 1 to
// minimize component renderings.
let volume = Math.round(Math.pow(10, dBs / 85) * 10);
if (volume === 1)
volume = 0;
volume = Math.round(volume); volume = Math.round(volume);
if (consumer && volume !== consumer.volume) if (consumer && volume !== consumer.volume)
@ -2667,7 +2682,7 @@ export default class RoomClient
store.dispatch(requestActions.notify( store.dispatch(requestActions.notify(
{ {
text : intl.formatMessage({ text : intl.formatMessage({
id : 'moderator.muteScreenSharingModerator', id : 'moderator.stopScreenSharing',
defaultMessage : 'Moderator stopped your screen sharing' defaultMessage : 'Moderator stopped your screen sharing'
}) })
})); }));
@ -3888,6 +3903,14 @@ export default class RoomClient
store.dispatch(meActions.setWebcamInProgress(false)); store.dispatch(meActions.setWebcamInProgress(false));
} }
async _setNoiseThreshold(threshold)
{
logger.debug('_setNoiseThreshold:%s', threshold);
this._hark.setThreshold(threshold);
store.dispatch(
settingsActions.setNoiseThreshold(threshold));
}
async _updateAudioDevices() async _updateAudioDevices()
{ {
logger.debug('_updateAudioDevices()'); logger.debug('_updateAudioDevices()');

View File

@ -110,3 +110,9 @@ export const setIsSpeaking = (flag) =>
type : 'SET_IS_SPEAKING', type : 'SET_IS_SPEAKING',
payload : { flag } payload : { flag }
}); });
export const setAutoMuted = (flag) =>
({
type : 'SET_AUTO_MUTED',
payload : { flag }
});

View File

@ -71,6 +71,18 @@ export const setNoiseSuppression = (noiseSuppression) =>
payload : { noiseSuppression } payload : { noiseSuppression }
}); });
export const setVoiceActivatedUnmute = (voiceActivatedUnmute) =>
({
type: 'SET_VOICE_ACTIVATED_UNMUTE',
payload: { voiceActivatedUnmute }
});
export const setNoiseThreshold = (noiseThreshold) =>
({
type: 'SET_NOISE_THRESHOLD',
payload: { noiseThreshold }
});
export const setDefaultAudio = (audio) => export const setDefaultAudio = (audio) =>
({ ({
type : 'SET_DEFAULT_AUDIO', type : 'SET_DEFAULT_AUDIO',

View File

@ -137,10 +137,10 @@ const styles = (theme) =>
transform : 'translate(-50%, 0%)', transform : 'translate(-50%, 0%)',
color : 'rgba(255, 255, 255, 0.7)', color : 'rgba(255, 255, 255, 0.7)',
fontSize : '1.3em', fontSize : '1.3em',
backgroundColor : 'rgba(255, 0, 0, 0.9)', backgroundColor : 'rgba(245, 0, 87, 0.70)',
margin : '4px', margin : '4px',
padding : theme.spacing(2), padding : theme.spacing(2),
zIndex : 31, zIndex : 1200,
borderRadius : '20px', borderRadius : '20px',
textAlign : 'center', textAlign : 'center',
opacity : 0, opacity : 0,
@ -176,6 +176,7 @@ const Me = (props) =>
screenProducer, screenProducer,
extraVideoProducers, extraVideoProducers,
canShareScreen, canShareScreen,
noiseVolume,
classes classes
} = props; } = props;
@ -440,7 +441,12 @@ const Me = (props) =>
})} })}
className={classes.smallContainer} className={classes.smallContainer}
disabled={!me.canSendMic || me.audioInProgress} disabled={!me.canSendMic || me.audioInProgress}
color={micState === 'on' ? 'primary' : 'secondary'} color={
micState === 'on' ?
settings.voiceActivatedUnmute && !me.isAutoMuted ?
'primary'
: 'default'
: 'secondary'}
size='small' size='small'
onClick={() => onClick={() =>
{ {
@ -453,7 +459,10 @@ const Me = (props) =>
}} }}
> >
{ micState === 'on' ? { micState === 'on' ?
<MicIcon /> <MicIcon
color={me.isAutoMuted ? 'secondary' : 'primary'}
style={{ opacity: noiseVolume }}
/>
: :
<MicOffIcon /> <MicOffIcon />
} }
@ -468,7 +477,10 @@ const Me = (props) =>
})} })}
className={classes.fab} className={classes.fab}
disabled={!me.canSendMic || me.audioInProgress} disabled={!me.canSendMic || me.audioInProgress}
color={micState === 'on' ? 'default' : 'secondary'} color={micState === 'on' ?
settings.voiceActivatedUnmute && !me.isAutoMuted? 'primary'
: 'default'
: 'secondary'}
size='large' size='large'
onClick={() => onClick={() =>
{ {
@ -481,7 +493,11 @@ const Me = (props) =>
}} }}
> >
{ micState === 'on' ? { micState === 'on' ?
<MicIcon /> <MicIcon
color={me.isAutoMuted && settings.voiceActivatedUnmute ? 'secondary' : 'primary'}
style={me.isAutoMuted && settings.voiceActivatedUnmute ? { opacity: noiseVolume }
: { opacity: 1 }}
/>
: :
<MicOffIcon /> <MicOffIcon />
} }
@ -868,6 +884,7 @@ Me.propTypes =
style : PropTypes.object, style : PropTypes.object,
smallContainer : PropTypes.bool, smallContainer : PropTypes.bool,
canShareScreen : PropTypes.bool.isRequired, canShareScreen : PropTypes.bool.isRequired,
noiseVolume : PropTypes.number,
classes : PropTypes.object.isRequired, classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired theme : PropTypes.object.isRequired
}; };
@ -878,12 +895,26 @@ const makeMapStateToProps = () =>
const mapStateToProps = (state) => const mapStateToProps = (state) =>
{ {
let volume;
// noiseVolume under threshold
if (state.peerVolumes[state.me.id] < state.settings.noiseThreshold)
{
// noiseVolume mapped to range 0.5 ... 1 (threshold switch)
volume = 1 + ((Math.abs(state.peerVolumes[state.me.id] -
state.settings.noiseThreshold) / (-120 -
state.settings.noiseThreshold)));
}
// noiseVolume over threshold: no noise but voice
else { volume = 0; }
return { return {
me : state.me, me : state.me,
...meProducersSelector(state), ...meProducersSelector(state),
settings : state.settings, settings : state.settings,
activeSpeaker : state.me.id === state.room.activeSpeakerId, activeSpeaker : state.me.id === state.room.activeSpeakerId,
canShareScreen : hasPermission(state) canShareScreen : hasPermission(state),
noiseVolume : volume
}; };
}; };
@ -900,6 +931,8 @@ export default withRoomContext(connect(
return ( return (
prev.room === next.room && prev.room === next.room &&
prev.me === next.me && prev.me === next.me &&
Math.round(prev.peerVolumes[prev.me.id]) ===
Math.round(next.peerVolumes[next.me.id]) &&
prev.peers === next.peers && prev.peers === next.peers &&
prev.producers === next.producers && prev.producers === next.producers &&
prev.settings === next.settings prev.settings === next.settings

View File

@ -94,17 +94,17 @@ const styles = () =>
smallBar : smallBar :
{ {
flex : '0 0 auto', flex : '0 0 auto',
margin : '0.3rem',
backgroundSize : '75%', backgroundSize : '75%',
backgroundRepeat : 'no-repeat', backgroundRepeat : 'no-repeat',
backgroundColor : 'rgba(0, 0, 0, 1)', backgroundColor : 'rgba(0, 0, 0, 1)',
cursor : 'pointer', cursor : 'pointer',
transitionProperty : 'opacity, background-color', transitionProperty : 'opacity, background-color',
width : 3, width : 3,
borderRadius : 6, borderRadius : 2,
transitionDuration : '0.25s', transitionDuration : '0.25s',
position : 'absolute', position : 'absolute',
bottom : 0, top : '50%',
transform : 'translateY(-50%)',
'&.level0' : { height: 0 }, '&.level0' : { height: 0 },
'&.level1' : { height: '0.2vh' }, '&.level1' : { height: '0.2vh' },
'&.level2' : { height: '0.4vh' }, '&.level2' : { height: '0.4vh' },
@ -148,10 +148,17 @@ Volume.propTypes =
const makeMapStateToProps = (initialState, props) => const makeMapStateToProps = (initialState, props) =>
{ {
const mapStateToProps = (state) => const mapStateToProps = (state) =>
{
if (state.peerVolumes[props.id]>state.settings.noiseThreshold)
{ {
return { return {
volume : state.peerVolumes[props.id] volume : Math.round((state.peerVolumes[props.id]+100) / 10)
}; };
}
else
{
return { volume: 0 };
}
}; };
return mapStateToProps; return mapStateToProps;

View File

@ -43,7 +43,8 @@ const styles = (theme) =>
link : link :
{ {
display : 'block', display : 'block',
textAlign : 'center' textAlign : 'center',
marginBottom : theme.spacing(1)
} }
}); });
@ -68,15 +69,16 @@ const About = ({
/> />
</DialogTitle> </DialogTitle>
<DialogContent dividers='true'> <DialogContent dividers='true'>
<DialogContentText> <DialogContentText paragraph>
Contributions to this work were made on behalf of the GÉANT Contributions to this work were made on behalf of the GÉANT
project, a project that has received funding from the project, a project that has received funding from the
European Unions Horizon 2020 research and innovation European Unions Horizon 2020 research and innovation
programme under Grant Agreement No. 731122 (GN4-2). programme under Grant Agreement No. 731122 (GN4-2).
On behalf of GÉANT project, GÉANT Association is the sole On behalf of GÉANT project, GÉANT Association is the sole
owner of the copyright in all material which was developed owner of the copyright in all material which was developed
by a member of the GÉANT project.<br /> by a member of the GÉANT project.
<br /> </DialogContentText>
<DialogContentText paragraph>
GÉANT Vereniging (Association) is registered with the GÉANT Vereniging (Association) is registered with the
Chamber of Commerce in Amsterdam with registration number Chamber of Commerce in Amsterdam with registration number
40535155 and operates in the UK as a branch of GÉANT 40535155 and operates in the UK as a branch of GÉANT
@ -87,6 +89,13 @@ const About = ({
<Link href='https://edumeet.org' target='_blank' rel='noreferrer' color='secondary' variant='h6' className={classes.link}> <Link href='https://edumeet.org' target='_blank' rel='noreferrer' color='secondary' variant='h6' className={classes.link}>
https://edumeet.org https://edumeet.org
</Link> </Link>
<DialogContentText align='center' variant='h7'>
<FormattedMessage
id='label.version'
defaultMessage='Version'
/>
:{` ${process.env.REACT_APP_VERSION}`}
</DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> } { window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }

View File

@ -472,14 +472,6 @@ const TopBar = (props) =>
} }
</div> </div>
<div className={classes.sectionMobile}> <div className={classes.sectionMobile}>
<IconButton
aria-haspopup='true'
onClick={handleMobileMenuOpen}
color='inherit'
>
<MoreIcon />
</IconButton>
</div>
{ lobbyPeers.length > 0 && { lobbyPeers.length > 0 &&
<Tooltip <Tooltip
title={intl.formatMessage({ title={intl.formatMessage({
@ -508,6 +500,14 @@ const TopBar = (props) =>
</span> </span>
</Tooltip> </Tooltip>
} }
<IconButton
aria-haspopup='true'
onClick={handleMobileMenuOpen}
color='inherit'
>
<MoreIcon />
</IconButton>
</div>
<div className={classes.divider} /> <div className={classes.divider} />
<Button <Button
aria-label={intl.formatMessage({ aria-label={intl.formatMessage({
@ -697,33 +697,6 @@ const TopBar = (props) =>
/> />
</p> </p>
</MenuItem> </MenuItem>
{ lobbyPeers.length > 0 &&
<MenuItem
aria-label={intl.formatMessage({
id : 'tooltip.lobby',
defaultMessage : 'Show lobby'
})}
disabled={!canPromote}
onClick={() =>
{
handleMenuClose();
setLockDialogOpen(!room.lockDialogOpen);
}}
>
<PulsingBadge
color='secondary'
badgeContent={lobbyPeers.length}
>
<SecurityIcon />
</PulsingBadge>
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.lobby'
defaultMessage='Show lobby'
/>
</p>
</MenuItem>
}
<MenuItem <MenuItem
aria-label={intl.formatMessage({ aria-label={intl.formatMessage({
id : 'tooltip.participants', id : 'tooltip.participants',

View File

@ -6,19 +6,52 @@ import { withRoomContext } from '../../RoomContext';
import * as settingsActions from '../../actions/settingsActions'; import * as settingsActions from '../../actions/settingsActions';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl'; import { useIntl, FormattedMessage } from 'react-intl';
import classnames from 'classnames';
import MenuItem from '@material-ui/core/MenuItem'; import MenuItem from '@material-ui/core/MenuItem';
import FormHelperText from '@material-ui/core/FormHelperText'; import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl'; import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel'; import FormControlLabel from '@material-ui/core/FormControlLabel';
import Select from '@material-ui/core/Select'; import Select from '@material-ui/core/Select';
import Checkbox from '@material-ui/core/Checkbox'; import Checkbox from '@material-ui/core/Checkbox';
import Slider from '@material-ui/core/Slider';
import Typography from '@material-ui/core/Typography';
const styles = (theme) => const NoiseSlider = withStyles(
({ {
root :
{
color : '#3880ff',
height : 2,
padding : '15px 0'
},
track : {
height : 2
},
rail : {
height : 2,
opacity : 0.2
},
mark : {
backgroundColor : '#bfbfbf',
height : 10,
width : 3,
marginTop : -3
},
markActive : {
opacity : 1,
backgroundColor : 'currentColor'
}
})(Slider);
const styles = (theme) => ({
setting : setting :
{ {
padding : theme.spacing(2) padding : theme.spacing(2)
}, },
margin :
{
height : theme.spacing(3)
},
formControl : formControl :
{ {
display : 'flex' display : 'flex'
@ -29,8 +62,10 @@ const MediaSettings = ({
setEchoCancellation, setEchoCancellation,
setAutoGainControl, setAutoGainControl,
setNoiseSuppression, setNoiseSuppression,
setVoiceActivatedUnmute,
roomClient, roomClient,
me, me,
volume,
settings, settings,
classes classes
}) => }) =>
@ -135,6 +170,34 @@ const MediaSettings = ({
} }
</FormHelperText> </FormHelperText>
</FormControl> </FormControl>
<FormControl className={classes.formControl}>
<Select
value={settings.resolution || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.changeVideoResolution(event.target.value);
}}
name='Video resolution'
autoWidth
className={classes.selectEmpty}
>
{resolutions.map((resolution, index) =>
{
return (
<MenuItem key={index} value={resolution.value}>
{resolution.label}
</MenuItem>
);
})}
</Select>
<FormHelperText>
<FormattedMessage
id='settings.resolution'
defaultMessage='Select your video resolution'
/>
</FormHelperText>
</FormControl>
</form> </form>
<form className={classes.setting} autoComplete='off'> <form className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}> <FormControl className={classes.formControl}>
@ -148,7 +211,7 @@ const MediaSettings = ({
displayEmpty displayEmpty
name={intl.formatMessage({ name={intl.formatMessage({
id : 'settings.audio', id : 'settings.audio',
defaultMessage : 'Audio device' defaultMessage : 'Audio input device'
})} })}
autoWidth autoWidth
className={classes.selectEmpty} className={classes.selectEmpty}
@ -165,12 +228,12 @@ const MediaSettings = ({
{ audioDevices.length > 0 ? { audioDevices.length > 0 ?
intl.formatMessage({ intl.formatMessage({
id : 'settings.selectAudio', id : 'settings.selectAudio',
defaultMessage : 'Select audio device' defaultMessage : 'Select audio input device'
}) })
: :
intl.formatMessage({ intl.formatMessage({
id : 'settings.cantSelectAudio', id : 'settings.cantSelectAudio',
defaultMessage : 'Unable to select audio device' defaultMessage : 'Unable to select audio input device'
}) })
} }
</FormHelperText> </FormHelperText>
@ -225,34 +288,6 @@ const MediaSettings = ({
</form> </form>
} }
<form className={classes.setting} autoComplete='off'> <form className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}>
<Select
value={settings.resolution || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.changeVideoResolution(event.target.value);
}}
name='Video resolution'
autoWidth
className={classes.selectEmpty}
>
{ resolutions.map((resolution, index) =>
{
return (
<MenuItem key={index} value={resolution.value}>
{resolution.label}
</MenuItem>
);
})}
</Select>
<FormHelperText>
<FormattedMessage
id='settings.resolution'
defaultMessage='Select your video resolution'
/>
</FormHelperText>
</FormControl>
<FormControlLabel <FormControlLabel
className={classes.setting} className={classes.setting}
control={ control={
@ -298,6 +333,43 @@ const MediaSettings = ({
defaultMessage : 'Noise suppression' defaultMessage : 'Noise suppression'
})} })}
/> />
<FormControlLabel
className={classes.setting}
control={
<Checkbox checked={settings.voiceActivatedUnmute} onChange={
(event) =>
{
setVoiceActivatedUnmute(event.target.checked);
}}
/>}
label={intl.formatMessage({
id : 'settings.voiceActivatedUnmute',
defaultMessage : 'Voice activated unmute'
})}
/>
<div className={classes.margin} />
<Typography gutterBottom>
{
intl.formatMessage({
id : 'settings.noiseThreshold',
defaultMessage : 'Noise threshold:'
})
}
</Typography>
<NoiseSlider className={classnames(classes.slider, classnames.setting)}
key={'noise-threshold-slider'}
min={-100}
value={settings.noiseThreshold}
max={0}
valueLabelDisplay={'off'}
onChange={
(event, value) =>
{
roomClient._setNoiseThreshold(value);
}}
marks={[ { value: volume, label: 'level' } ]}
/>
<div className={classes.margin} />
</form> </form>
</React.Fragment> </React.Fragment>
); );
@ -309,7 +381,9 @@ MediaSettings.propTypes =
setEchoCancellation : PropTypes.func.isRequired, setEchoCancellation : PropTypes.func.isRequired,
setAutoGainControl : PropTypes.func.isRequired, setAutoGainControl : PropTypes.func.isRequired,
setNoiseSuppression : PropTypes.func.isRequired, setNoiseSuppression : PropTypes.func.isRequired,
setVoiceActivatedUnmute : PropTypes.func.isRequired,
me : appPropTypes.Me.isRequired, me : appPropTypes.Me.isRequired,
volume : PropTypes.number,
settings : PropTypes.object.isRequired, settings : PropTypes.object.isRequired,
classes : PropTypes.object.isRequired classes : PropTypes.object.isRequired
}; };
@ -318,6 +392,7 @@ const mapStateToProps = (state) =>
{ {
return { return {
me : state.me, me : state.me,
volume : state.peerVolumes[state.me.id],
settings : state.settings settings : state.settings
}; };
}; };
@ -325,7 +400,8 @@ const mapStateToProps = (state) =>
const mapDispatchToProps = { const mapDispatchToProps = {
setEchoCancellation : settingsActions.setEchoCancellation, setEchoCancellation : settingsActions.setEchoCancellation,
setAutoGainControl : settingsActions.toggleAutoGainControl, setAutoGainControl : settingsActions.toggleAutoGainControl,
setNoiseSuppression : settingsActions.toggleNoiseSuppression setNoiseSuppression : settingsActions.toggleNoiseSuppression,
setVoiceActivatedUnmute : settingsActions.setVoiceActivatedUnmute
}; };
export default withRoomContext(connect( export default withRoomContext(connect(
@ -337,7 +413,8 @@ export default withRoomContext(connect(
{ {
return ( return (
prev.me === next.me && prev.me === next.me &&
prev.settings === next.settings prev.settings === next.settings &&
prev.peerVolumes[prev.me.id] === next[next.me.id]
); );
} }
} }

View File

@ -18,7 +18,8 @@ const initialState =
raisedHand : false, raisedHand : false,
raisedHandInProgress : false, raisedHandInProgress : false,
loggedIn : false, loggedIn : false,
isSpeaking : false isSpeaking : false,
isAutoMuted : true
}; };
const me = (state = initialState, action) => const me = (state = initialState, action) =>
@ -162,6 +163,13 @@ const me = (state = initialState, action) =>
return { ...state, isSpeaking: flag }; return { ...state, isSpeaking: flag };
} }
case 'SET_AUTO_MUTED':
{
const { flag } = action.payload;
return { ...state, isAutoMuted: flag };
}
default: default:
return state; return state;
} }

View File

@ -31,9 +31,10 @@ const peerVolumes = (state = initialState, action) =>
case 'SET_PEER_VOLUME': case 'SET_PEER_VOLUME':
{ {
const { peerId, volume } = action.payload; const { peerId } = action.payload;
const dBs = action.payload.volume < -100 ? -100 : action.payload.volume;
return { ...state, [peerId]: volume }; return { ...state, [peerId]: Math.round(dBs) };
} }
default: default:

View File

@ -7,9 +7,11 @@ const initialState =
sampleRate : 48000, sampleRate : 48000,
channelCount : 1, channelCount : 1,
volume : 1.0, volume : 1.0,
autoGainControl : true, autoGainControl : false,
echoCancellation : true, echoCancellation : true,
noiseSuppression : true, noiseSuppression : true,
voiceActivatedUnmute : false,
noiseThreshold : -50,
sampleSize : 16, sampleSize : 16,
// low, medium, high, veryhigh, ultra // low, medium, high, veryhigh, ultra
resolution : window.config.defaultResolution || 'medium', resolution : window.config.defaultResolution || 'medium',
@ -99,6 +101,20 @@ const settings = (state = initialState, action) =>
return { ...state, noiseSuppression }; return { ...state, noiseSuppression };
} }
case 'SET_VOICE_ACTIVATED_UNMUTE':
{
const { voiceActivatedUnmute } = action.payload;
return { ...state, voiceActivatedUnmute };
}
case 'SET_NOISE_THRESHOLD':
{
const { noiseThreshold } = action.payload;
return { ...state, noiseThreshold };
}
case 'SET_DEFAULT_AUDIO': case 'SET_DEFAULT_AUDIO':
{ {
const { audio } = action.payload; const { audio } = action.payload;

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "设置", "settings.settings": "设置",
"settings.camera": "视频设备", "settings.camera": "视频设备",
@ -190,6 +191,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -118,6 +118,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Nastavení", "settings.settings": "Nastavení",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -189,6 +190,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": "Video hinzufügen", "label.addVideo": "Video hinzufügen",
"label.promoteAllPeers": "Alle Teilnehmer reinlassen", "label.promoteAllPeers": "Alle Teilnehmer reinlassen",
"label.moreActions": "Weitere Aktionen", "label.moreActions": "Weitere Aktionen",
"label.version": null,
"settings.settings": "Einstellungen", "settings.settings": "Einstellungen",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -190,6 +191,7 @@
"moderator.muteAudio": "Moderator hat dich stummgeschaltet", "moderator.muteAudio": "Moderator hat dich stummgeschaltet",
"moderator.muteVideo": "Moderator hat dein Video gestoppt", "moderator.muteVideo": "Moderator hat dein Video gestoppt",
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Indstillinger", "settings.settings": "Indstillinger",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -190,6 +191,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Ρυθμίσεις", "settings.settings": "Ρυθμίσεις",
"settings.camera": "Κάμερα", "settings.camera": "Κάμερα",
@ -190,6 +191,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": "Add video", "label.addVideo": "Add video",
"label.promoteAllPeers": "Promote all", "label.promoteAllPeers": "Promote all",
"label.moreActions": "More actions", "label.moreActions": "More actions",
"label.version": "Version",
"settings.settings": "Settings", "settings.settings": "Settings",
"settings.camera": "Camera", "settings.camera": "Camera",
@ -190,6 +191,7 @@
"moderator.muteAudio": "Moderator muted your audio", "moderator.muteAudio": "Moderator muted your audio",
"moderator.muteVideo": "Moderator muted your video", "moderator.muteVideo": "Moderator muted your video",
"moderator.muteScreenSharing": "Moderator muted your screen sharing", "moderator.muteScreenSharing": "Moderator muted your screen sharing",
"moderator.stopScreenSharing": "Moderator stopped your screen sharing",
"unsupportedBrowser.titleUsnsupportedBrowser": "Detected unsupported browser!", "unsupportedBrowser.titleUsnsupportedBrowser": "Detected unsupported browser!",
"unsupportedBrowser.titlewebrtcUnavailable": "Required functionality not available in your browser!", "unsupportedBrowser.titlewebrtcUnavailable": "Required functionality not available in your browser!",

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Ajustes", "settings.settings": "Ajustes",
"settings.camera": "Cámara", "settings.camera": "Cámara",
@ -190,6 +191,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Paramètres", "settings.settings": "Paramètres",
"settings.camera": "Caméra", "settings.camera": "Caméra",
@ -189,6 +190,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": "Dodaj video", "label.addVideo": "Dodaj video",
"label.promoteAllPeers": "Promoviraj sve", "label.promoteAllPeers": "Promoviraj sve",
"label.moreActions": "Više akcija", "label.moreActions": "Više akcija",
"label.version": null,
"settings.settings": "Postavke", "settings.settings": "Postavke",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -190,6 +191,7 @@
"moderator.muteAudio": "Moderator je utišao tvoj zvuk", "moderator.muteAudio": "Moderator je utišao tvoj zvuk",
"moderator.muteVideo": "Moderator je zaustavio tvoj video", "moderator.muteVideo": "Moderator je zaustavio tvoj video",
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -66,7 +66,7 @@
"room.browsePeersSpotlight": "Résztvevők böngészése", "room.browsePeersSpotlight": "Résztvevők böngészése",
"room.stopAllScreenSharing": "Összes képernyőmegosztás leállítása", "room.stopAllScreenSharing": "Összes képernyőmegosztás leállítása",
"me.mutedPTT": "Némítva vagy, ha beszélnél nyomd le a szóköz billentyűt", "me.mutedPTT": "Némítva vagy, ha beszélnél nyomd le a SZÓKÖZ billentyűt",
"roles.gotRole": "{role} szerepet kaptál", "roles.gotRole": "{role} szerepet kaptál",
"roles.lostRole": "Elvesztetted a {role} szerepet", "roles.lostRole": "Elvesztetted a {role} szerepet",
@ -119,6 +119,7 @@
"label.addVideo": "Videó hozzáadása", "label.addVideo": "Videó hozzáadása",
"label.promoteAllPeers": "Mindenkit beengedek", "label.promoteAllPeers": "Mindenkit beengedek",
"label.moreActions": "További műveletek", "label.moreActions": "További műveletek",
"label.version": "Verzió",
"settings.settings": "Beállítások", "settings.settings": "Beállítások",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -189,10 +190,10 @@
"moderator.clearFiles": "A moderátor kiürítette a file megosztás történelmet", "moderator.clearFiles": "A moderátor kiürítette a file megosztás történelmet",
"moderator.muteAudio": "A moderátor elnémította a hangod", "moderator.muteAudio": "A moderátor elnémította a hangod",
"moderator.muteVideo": "A moderátor elnémította a videód", "moderator.muteVideo": "A moderátor elnémította a videód",
"moderator.muteScreenSharing": "A moderátor leállította képernyőmegosztásod", "moderator.muteScreenSharing": "A moderátor szünetelteti képernyőmegosztásod",
"moderator.stopScreenSharing": "A moderátor leállította a képernyőmegosztásod",
"unsupportedBrowser.titleUnsupportedBrowser": "A bőngésző verziód sajnos nem támogatott! :-(", "unsupportedBrowser.titleUnsupportedBrowser": "A bőngésző verziód sajnos nem támogatott! :-(",
"unsupportedBrowser.titlewebrtcUnavailable": "A böngésződ egy szükséges funkciója nem elérhető!", "unsupportedBrowser.titlewebrtcUnavailable": "A böngésződ egy szükséges funkciója nem elérhető!",
"unsupportedBrowser.bodyText": "Kérlek frissítsd a böngésződ, válts másik böngészőre, vagy ellenőrizd a böngésződ beállításait! Támogatott böngészők:" "unsupportedBrowser.bodyText": "Kérlek frissítsd a böngésződ, válts másik böngészőre, vagy ellenőrizd a böngésződ beállításait! Támogatott böngészők:"
} }

View File

@ -119,6 +119,7 @@
"label.addVideo": "Aggiungi video", "label.addVideo": "Aggiungi video",
"label.promoteAllPeers": "Promuovi tutti", "label.promoteAllPeers": "Promuovi tutti",
"label.moreActions": "Altre azioni", "label.moreActions": "Altre azioni",
"label.version": null,
"settings.settings": "Impostazioni", "settings.settings": "Impostazioni",
"settings.camera": "Videocamera", "settings.camera": "Videocamera",
@ -190,9 +191,9 @@
"moderator.muteAudio": "Il moderatore ha mutato il tuo audio", "moderator.muteAudio": "Il moderatore ha mutato il tuo audio",
"moderator.muteVideo": "Il moderatore ha fermato il tuo video", "moderator.muteVideo": "Il moderatore ha fermato il tuo video",
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null "unsupportedBrowser.bodyText": null
} }

View File

@ -116,6 +116,7 @@
"label.advanced": "Advancēts", "label.advanced": "Advancēts",
"label.addVideo": "Pievienot video", "label.addVideo": "Pievienot video",
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Iestatījumi", "settings.settings": "Iestatījumi",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -184,6 +185,7 @@
"moderator.muteAudio": "Moderators noklusināja jūsu mikrofonu", "moderator.muteAudio": "Moderators noklusināja jūsu mikrofonu",
"moderator.muteVideo": "Moderators atslēdza jūsu kameru", "moderator.muteVideo": "Moderators atslēdza jūsu kameru",
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": "Legg til video", "label.addVideo": "Legg til video",
"label.promoteAllPeers": "Slipp inn alle", "label.promoteAllPeers": "Slipp inn alle",
"label.moreActions": "Flere handlinger", "label.moreActions": "Flere handlinger",
"label.version": null,
"settings.settings": "Innstillinger", "settings.settings": "Innstillinger",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -190,6 +191,7 @@
"moderator.muteAudio": "Moderator mutet lyden din", "moderator.muteAudio": "Moderator mutet lyden din",
"moderator.muteVideo": "Moderator mutet videoen din", "moderator.muteVideo": "Moderator mutet videoen din",
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": "Dodaj wideo", "label.addVideo": "Dodaj wideo",
"label.promoteAllPeers": "Wpuść wszystkich", "label.promoteAllPeers": "Wpuść wszystkich",
"label.moreActions": "Więcej akcji", "label.moreActions": "Więcej akcji",
"label.version": null,
"settings.settings": "Ustawienia", "settings.settings": "Ustawienia",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -190,6 +191,7 @@
"moderator.muteAudio": "Moderator wyciszył audio", "moderator.muteAudio": "Moderator wyciszył audio",
"moderator.muteVideo": "Moderator wyciszył twoje video", "moderator.muteVideo": "Moderator wyciszył twoje video",
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Definições", "settings.settings": "Definições",
"settings.camera": "Camera", "settings.camera": "Camera",
@ -190,6 +191,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Setări", "settings.settings": "Setări",
"settings.camera": "Cameră video", "settings.camera": "Cameră video",
@ -190,6 +191,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Ayarlar", "settings.settings": "Ayarlar",
"settings.camera": "Kamera", "settings.camera": "Kamera",
@ -181,7 +182,13 @@
"devices.cameraDisconnected": "Kamera bağlı değil", "devices.cameraDisconnected": "Kamera bağlı değil",
"devices.cameraError": "Kameranıza erişilirken bir hata oluştu", "devices.cameraError": "Kameranıza erişilirken bir hata oluştu",
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -118,6 +118,7 @@
"label.addVideo": "新增視訊", "label.addVideo": "新增視訊",
"label.promoteAllPeers": "提升全部", "label.promoteAllPeers": "提升全部",
"label.moreActions": "更多", "label.moreActions": "更多",
"label.version": null,
"settings.settings": "設置", "settings.settings": "設置",
"settings.camera": "視訊來源", "settings.camera": "視訊來源",
@ -189,6 +190,7 @@
"moderator.muteAudio": "您已被管理員靜音", "moderator.muteAudio": "您已被管理員靜音",
"moderator.muteVideo": "您的視訊已被管理員關閉", "moderator.muteVideo": "您的視訊已被管理員關閉",
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -119,6 +119,7 @@
"label.addVideo": null, "label.addVideo": null,
"label.promoteAllPeers": null, "label.promoteAllPeers": null,
"label.moreActions": null, "label.moreActions": null,
"label.version": null,
"settings.settings": "Налаштування", "settings.settings": "Налаштування",
"settings.camera": "Камера", "settings.camera": "Камера",
@ -190,6 +191,7 @@
"moderator.muteAudio": null, "moderator.muteAudio": null,
"moderator.muteVideo": null, "moderator.muteVideo": null,
"moderator.muteScreenSharing": null, "moderator.muteScreenSharing": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null, "unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null, "unsupportedBrowser.titlewebrtcUnavailable": null,

View File

@ -17,3 +17,21 @@ export const idle = (callback, delay) =>
handle = setTimeout(callback, delay); handle = setTimeout(callback, delay);
}; };
}; };
/**
* Error produced when a socket request has a timeout.
*/
export class SocketTimeoutError extends Error
{
constructor(message)
{
super(message);
this.name = 'SocketTimeoutError';
if (Error.hasOwnProperty('captureStackTrace')) // Just in V8.
Error.captureStackTrace(this, SocketTimeoutError);
else
this.stack = (new Error(message)).stack;
}
}

View File

@ -106,6 +106,12 @@
"no-invalid-regexp": 2, "no-invalid-regexp": 2,
"no-invalid-this": 2, "no-invalid-this": 2,
"no-irregular-whitespace": 2, "no-irregular-whitespace": 2,
"no-trailing-spaces": [
"error",
{
"ignoreComments": true
}
],
"no-lonely-if": 2, "no-lonely-if": 2,
"no-mixed-operators": 2, "no-mixed-operators": 2,
"no-mixed-spaces-and-tabs": 2, "no-mixed-spaces-and-tabs": 2,

View File

@ -276,6 +276,10 @@ module.exports =
// maxUsersPerRoom : 20, // maxUsersPerRoom : 20,
// Room size before spreading to new router // Room size before spreading to new router
routerScaleSize : 40, routerScaleSize : 40,
// Socket timout value
requestTimeout : 20000,
// Socket retries when timeout
requestRetries : 3,
// Mediasoup settings // Mediasoup settings
mediasoup : mediasoup :
{ {

View File

@ -3,6 +3,7 @@ const AwaitQueue = require('awaitqueue');
const axios = require('axios'); const axios = require('axios');
const Logger = require('./Logger'); const Logger = require('./Logger');
const Lobby = require('./Lobby'); const Lobby = require('./Lobby');
const { SocketTimeoutError } = require('./errors');
const { v4: uuidv4 } = require('uuid'); const { v4: uuidv4 } = require('uuid');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const userRoles = require('../userRoles'); const userRoles = require('../userRoles');
@ -1759,9 +1760,9 @@ class Room extends EventEmitter
if (called) if (called)
return; return;
called = true; called = true;
callback(new Error('Request timeout.')); callback(new SocketTimeoutError('Request timed out'));
}, },
10000 config.requestTimeout || 20000
); );
return (...args) => return (...args) =>
@ -1775,7 +1776,7 @@ class Room extends EventEmitter
}; };
} }
_request(socket, method, data = {}) _sendRequest(socket, method, data = {})
{ {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
@ -1797,6 +1798,33 @@ class Room extends EventEmitter
}); });
} }
async _request(socket, method, data)
{
logger.debug('_request() [method:"%s", data:"%o"]', method, data);
const {
requestRetries = 3
} = config;
for (let tries = 0; tries < requestRetries; tries++)
{
try
{
return await this._sendRequest(socket, method, data);
}
catch (error)
{
if (
error instanceof SocketTimeoutError &&
tries < requestRetries
)
logger.warn('_request() | timeout, retrying [attempt:"%s"]', tries);
else
throw error;
}
}
}
_notification(socket, method, data = {}, broadcast = false, includeSender = false) _notification(socket, method, data = {}, broadcast = false, includeSender = false)
{ {
if (broadcast) if (broadcast)

View File

@ -0,0 +1,22 @@
/**
* Error produced when a socket request has a timeout.
*/
class SocketTimeoutError extends Error
{
constructor(message)
{
super(message);
this.name = 'SocketTimeoutError';
if (Error.hasOwnProperty('captureStackTrace')) // Just in V8.
Error.captureStackTrace(this, SocketTimeoutError);
else
this.stack = (new Error(message)).stack;
}
}
module.exports =
{
SocketTimeoutError
};