multiparty-meeting/app/src/components/Settings/MediaSettings.js

481 lines
13 KiB
JavaScript

import React from 'react';
import { connect } from 'react-redux';
import * as appPropTypes from '../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../RoomContext';
import * as settingsActions from '../../actions/settingsActions';
import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl';
import classnames from 'classnames';
import MenuItem from '@material-ui/core/MenuItem';
import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Select from '@material-ui/core/Select';
import Slider from '@material-ui/core/Slider';
import Typography from '@material-ui/core/Typography';
import Collapse from '@material-ui/core/Collapse';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import Switch from '@material-ui/core/Switch';
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 :
{
padding : theme.spacing(2)
},
margin :
{
height : theme.spacing(3)
},
root : {
width : '100%',
backgroundColor : theme.palette.background.paper
},
switchLabel : {
justifyContent : 'space-between',
flex : 'auto',
display : 'flex',
padding : theme.spacing(1)
},
nested : {
paddingLeft : theme.spacing(2),
display : 'block',
paddingTop : 0,
paddingBottom : 0
},
formControl :
{
display : 'flex'
}
});
const MediaSettings = ({
setEchoCancellation,
setAutoGainControl,
setNoiseSuppression,
setVoiceActivatedUnmute,
roomClient,
me,
volume,
settings,
classes
}) =>
{
const intl = useIntl();
const resolutions = [ {
value : 'low',
label : intl.formatMessage({
id : 'label.low',
defaultMessage : 'Low'
})
},
{
value : 'medium',
label : intl.formatMessage({
id : 'label.medium',
defaultMessage : 'Medium'
})
},
{
value : 'high',
label : intl.formatMessage({
id : 'label.high',
defaultMessage : 'High (HD)'
})
},
{
value : 'veryhigh',
label : intl.formatMessage({
id : 'label.veryHigh',
defaultMessage : 'Very high (FHD)'
})
},
{
value : 'ultra',
label : intl.formatMessage({
id : 'label.ultra',
defaultMessage : 'Ultra (UHD)'
})
} ];
let webcams;
if (me.webcamDevices)
webcams = Object.values(me.webcamDevices);
else
webcams = [];
let audioDevices;
if (me.audioDevices)
audioDevices = Object.values(me.audioDevices);
else
audioDevices = [];
let audioOutputDevices;
if (me.audioOutputDevices)
audioOutputDevices = Object.values(me.audioOutputDevices);
else
audioOutputDevices = [];
const [ open, setOpen ] = React.useState(true);
const advancedAudioSettings = () =>
{
setOpen(!open);
};
return (
<React.Fragment>
<form className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}>
<Select
value={settings.selectedWebcam || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.changeWebcam(event.target.value);
}}
displayEmpty
name={intl.formatMessage({
id : 'settings.camera',
defaultMessage : 'Camera'
})}
autoWidth
className={classes.selectEmpty}
disabled={webcams.length === 0 || me.webcamInProgress}
>
{ webcams.map((webcam, index) =>
{
return (
<MenuItem key={index} value={webcam.deviceId}>{webcam.label}</MenuItem>
);
})}
</Select>
<FormHelperText>
{ webcams.length > 0 ?
intl.formatMessage({
id : 'settings.selectCamera',
defaultMessage : 'Select video device'
})
:
intl.formatMessage({
id : 'settings.cantSelectCamera',
defaultMessage : 'Unable to select video device'
})
}
</FormHelperText>
</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 className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}>
<Select
value={settings.selectedAudioDevice || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.changeAudioDevice(event.target.value);
}}
displayEmpty
name={intl.formatMessage({
id : 'settings.audio',
defaultMessage : 'Audio input device'
})}
autoWidth
className={classes.selectEmpty}
disabled={audioDevices.length === 0 || me.audioInProgress}
>
{ audioDevices.map((audio, index) =>
{
return (
<MenuItem key={index} value={audio.deviceId}>{audio.label}</MenuItem>
);
})}
</Select>
<FormHelperText>
{ audioDevices.length > 0 ?
intl.formatMessage({
id : 'settings.selectAudio',
defaultMessage : 'Select audio input device'
})
:
intl.formatMessage({
id : 'settings.cantSelectAudio',
defaultMessage : 'Unable to select audio input device'
})
}
</FormHelperText>
</FormControl>
</form>
{ 'audioOutputSupportedBrowsers' in window.config &&
window.config.audioOutputSupportedBrowsers.includes(me.browser.name) &&
<form className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}>
<Select
value={settings.selectedAudioOutputDevice || ''}
onChange={(event) =>
{
if (event.target.value)
roomClient.changeAudioOutputDevice(event.target.value);
}}
displayEmpty
name={intl.formatMessage({
id : 'settings.audioOutput',
defaultMessage : 'Audio output device'
})}
autoWidth
className={classes.selectEmpty}
disabled={audioOutputDevices.length === 0 || me.audioOutputInProgress}
>
{ audioOutputDevices.map((audioOutput, index) =>
{
return (
<MenuItem
key={index}
value={audioOutput.deviceId}
>
{audioOutput.label}
</MenuItem>
);
})}
</Select>
<FormHelperText>
{ audioOutputDevices.length > 0 ?
intl.formatMessage({
id : 'settings.selectAudioOutput',
defaultMessage : 'Select audio output device'
})
:
intl.formatMessage({
id : 'settings.cantSelectAudioOutput',
defaultMessage : 'Unable to select audio output device'
})
}
</FormHelperText>
</FormControl>
</form>
}
<List className={classes.root} component='nav'>
<ListItem button onClick={advancedAudioSettings}>
<ListItemText primary={intl.formatMessage({
id : 'settings.showAdvancedAudio',
defaultMessage : 'Show advanced audio settings'
})}
/>
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={!open} timeout='auto'>
<List component='div'>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.echoCancellation}
onChange={
(event) =>
{
setEchoCancellation(event.target.checked);
roomClient.changeAudioDevice(settings.selectedAudioDevice);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.echoCancellation',
defaultMessage : 'Echo cancellation'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.autoGainControl} onChange={
(event) =>
{
setAutoGainControl(event.target.checked);
roomClient.changeAudioDevice(settings.selectedAudioDevice);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.autoGainControl',
defaultMessage : 'Auto gain control'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.noiseSuppression} onChange={
(event) =>
{
setNoiseSuppression(event.target.checked);
roomClient.changeAudioDevice(settings.selectedAudioDevice);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.noiseSuppression',
defaultMessage : 'Noise suppression'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch color='secondary'
checked={settings.voiceActivatedUnmute} onChange={
(event) =>
{
setVoiceActivatedUnmute(event.target.checked);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.voiceActivatedUnmute',
defaultMessage : 'Voice activated unmute'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<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: volume+'dB' } ]}
/>
<div className={classes.margin} />
</ListItem>
</List>
</Collapse>
</List>
</React.Fragment>
);
};
MediaSettings.propTypes =
{
roomClient : PropTypes.any.isRequired,
setEchoCancellation : PropTypes.func.isRequired,
setAutoGainControl : PropTypes.func.isRequired,
setNoiseSuppression : PropTypes.func.isRequired,
setVoiceActivatedUnmute : PropTypes.func.isRequired,
me : appPropTypes.Me.isRequired,
volume : PropTypes.number,
settings : PropTypes.object.isRequired,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
{
return {
me : state.me,
volume : state.peerVolumes[state.me.id],
settings : state.settings
};
};
const mapDispatchToProps = {
setEchoCancellation : settingsActions.setEchoCancellation,
setAutoGainControl : settingsActions.setAutoGainControl,
setNoiseSuppression : settingsActions.setNoiseSuppression,
setVoiceActivatedUnmute : settingsActions.setVoiceActivatedUnmute
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.me === next.me &&
prev.settings === next.settings &&
prev.peerVolumes[prev.me.id] === next[next.me.id]
);
}
}
)(withStyles(styles)(MediaSettings)));