Added support for setting audio input device, not working on linux at the moment. Updated webcam selection.
parent
7e1b391fe1
commit
fcb15e706d
|
|
@ -79,7 +79,9 @@ export default class RoomClient
|
||||||
|
|
||||||
// Map of webcam MediaDeviceInfos indexed by deviceId.
|
// Map of webcam MediaDeviceInfos indexed by deviceId.
|
||||||
// @type {Map<String, MediaDeviceInfos>}
|
// @type {Map<String, MediaDeviceInfos>}
|
||||||
this._webcams = new Map();
|
this._webcams = {};
|
||||||
|
|
||||||
|
this._audioDevices = {};
|
||||||
|
|
||||||
// Local Webcam. Object with:
|
// Local Webcam. Object with:
|
||||||
// - {MediaDeviceInfo} [device]
|
// - {MediaDeviceInfo} [device]
|
||||||
|
|
@ -89,6 +91,10 @@ export default class RoomClient
|
||||||
resolution : 'hd'
|
resolution : 'hd'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._audioDevice = {
|
||||||
|
device : null
|
||||||
|
};
|
||||||
|
|
||||||
this._screenSharing = ScreenShare.create();
|
this._screenSharing = ScreenShare.create();
|
||||||
|
|
||||||
this._screenSharingProducer = null;
|
this._screenSharingProducer = null;
|
||||||
|
|
@ -366,6 +372,72 @@ export default class RoomClient
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeAudioDevice(deviceId)
|
||||||
|
{
|
||||||
|
logger.debug('changeAudioDevice() [deviceId: %s]', deviceId);
|
||||||
|
|
||||||
|
this._dispatch(
|
||||||
|
stateActions.setAudioInProgress(true));
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
this._audioDevice.device = this._audioDevices[deviceId];
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
'changeAudioDevice() | new selected webcam [device:%o]',
|
||||||
|
this._audioDevice.device);
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
const { device } = this._audioDevice;
|
||||||
|
|
||||||
|
if (!device)
|
||||||
|
throw new Error('no audio devices');
|
||||||
|
|
||||||
|
logger.debug('changeAudioDevice() | calling getUserMedia()');
|
||||||
|
|
||||||
|
return navigator.mediaDevices.getUserMedia(
|
||||||
|
{
|
||||||
|
audio :
|
||||||
|
{
|
||||||
|
deviceId : { exact: device.deviceId }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((stream) =>
|
||||||
|
{
|
||||||
|
const track = stream.getAudioTracks()[0];
|
||||||
|
|
||||||
|
return this._micProducer.replaceTrack(track)
|
||||||
|
.then((newTrack) =>
|
||||||
|
{
|
||||||
|
track.stop();
|
||||||
|
|
||||||
|
return newTrack;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((newTrack) =>
|
||||||
|
{
|
||||||
|
this._dispatch(
|
||||||
|
stateActions.setProducerTrack(this._micProducer.id, newTrack));
|
||||||
|
|
||||||
|
return this._updateAudioDevices();
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
this._dispatch(
|
||||||
|
stateActions.setAudioInProgress(false));
|
||||||
|
})
|
||||||
|
.catch((error) =>
|
||||||
|
{
|
||||||
|
logger.error('changeAudioDevice() failed: %o', error);
|
||||||
|
|
||||||
|
this._dispatch(
|
||||||
|
stateActions.setAudioInProgress(false));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
changeWebcam(deviceId)
|
changeWebcam(deviceId)
|
||||||
{
|
{
|
||||||
logger.debug('changeWebcam() [deviceId: %s]', deviceId);
|
logger.debug('changeWebcam() [deviceId: %s]', deviceId);
|
||||||
|
|
@ -376,20 +448,7 @@ export default class RoomClient
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() =>
|
.then(() =>
|
||||||
{
|
{
|
||||||
logger.debug('changeWebcam() | calling enumerateDevices()');
|
this._webcam.device = this._webcams[deviceId];
|
||||||
|
|
||||||
return navigator.mediaDevices.enumerateDevices();
|
|
||||||
})
|
|
||||||
.then((devices) =>
|
|
||||||
{
|
|
||||||
for (const device of devices)
|
|
||||||
{
|
|
||||||
if (device.kind !== 'videoinput')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (device.deviceId == deviceId)
|
|
||||||
this._webcam.device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'changeWebcam() | new selected webcam [device:%o]',
|
'changeWebcam() | new selected webcam [device:%o]',
|
||||||
|
|
@ -433,6 +492,10 @@ export default class RoomClient
|
||||||
this._dispatch(
|
this._dispatch(
|
||||||
stateActions.setProducerTrack(this._webcamProducer.id, newTrack));
|
stateActions.setProducerTrack(this._webcamProducer.id, newTrack));
|
||||||
|
|
||||||
|
return this._updateWebcams();
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
this._dispatch(
|
this._dispatch(
|
||||||
stateActions.setWebcamInProgress(false));
|
stateActions.setWebcamInProgress(false));
|
||||||
})
|
})
|
||||||
|
|
@ -1161,6 +1224,12 @@ export default class RoomClient
|
||||||
let producer;
|
let producer;
|
||||||
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
logger.debug('_setMicProducer() | calling _updateAudioDevices()');
|
||||||
|
|
||||||
|
return this._updateAudioDevices();
|
||||||
|
})
|
||||||
.then(() =>
|
.then(() =>
|
||||||
{
|
{
|
||||||
logger.debug('_setMicProducer() | calling getUserMedia()');
|
logger.debug('_setMicProducer() | calling getUserMedia()');
|
||||||
|
|
@ -1480,6 +1549,58 @@ export default class RoomClient
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateAudioDevices()
|
||||||
|
{
|
||||||
|
logger.debug('_updateAudioDevices()');
|
||||||
|
|
||||||
|
// Reset the list.
|
||||||
|
this._audioDevices = {};
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
logger.debug('_updateAudioDevices() | calling enumerateDevices()');
|
||||||
|
|
||||||
|
return navigator.mediaDevices.enumerateDevices();
|
||||||
|
})
|
||||||
|
.then((devices) =>
|
||||||
|
{
|
||||||
|
for (const device of devices)
|
||||||
|
{
|
||||||
|
if (device.kind !== 'audioinput')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this._audioDevices[device.deviceId] = {
|
||||||
|
value : device.deviceId,
|
||||||
|
label : device.label,
|
||||||
|
deviceId : device.deviceId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
const currentAudioDeviceId =
|
||||||
|
this._audioDevice.device ? this._audioDevice.device.deviceId : undefined;
|
||||||
|
|
||||||
|
logger.debug('_updateAudioDevices() [audiodevices:%o]', this._audioDevices);
|
||||||
|
|
||||||
|
const len = Object.keys(this._audioDevices).length;
|
||||||
|
|
||||||
|
if (len === 0)
|
||||||
|
this._audioDevice.device = null;
|
||||||
|
else if (!this._audioDevices[currentAudioDeviceId])
|
||||||
|
for (this._audioDevice.device in this._audioDevices)
|
||||||
|
if (this._audioDevices.hasOwnProperty(this._audioDevice.device))
|
||||||
|
break;
|
||||||
|
|
||||||
|
this._dispatch(
|
||||||
|
stateActions.setCanChangeAudioDevice(len >= 2));
|
||||||
|
if (len >= 1)
|
||||||
|
this._dispatch(
|
||||||
|
stateActions.setAudioDevices(this._audioDevices));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_updateWebcams()
|
_updateWebcams()
|
||||||
{
|
{
|
||||||
logger.debug('_updateWebcams()');
|
logger.debug('_updateWebcams()');
|
||||||
|
|
@ -1502,8 +1623,9 @@ export default class RoomClient
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
this._webcams[device.deviceId] = {
|
this._webcams[device.deviceId] = {
|
||||||
value : device.deviceId,
|
value : device.deviceId,
|
||||||
label : device.label
|
label : device.label,
|
||||||
|
deviceId : device.deviceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,9 @@ class Me extends React.Component
|
||||||
{connected ?
|
{connected ?
|
||||||
<div className='controls'>
|
<div className='controls'>
|
||||||
<div
|
<div
|
||||||
className={classnames('button', 'mic', micState)}
|
className={classnames('button', 'mic', micState, {
|
||||||
|
disabled : me.audioInProgress
|
||||||
|
})}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
micState === 'on' ? onMuteMic() : onUnmuteMic();
|
micState === 'on' ? onMuteMic() : onUnmuteMic();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { connect } from 'react-redux';
|
||||||
import * as appPropTypes from './appPropTypes';
|
import * as appPropTypes from './appPropTypes';
|
||||||
import * as requestActions from '../redux/requestActions';
|
import * as requestActions from '../redux/requestActions';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Appear } from './transitions';
|
||||||
import Dropdown from 'react-dropdown';
|
import Dropdown from 'react-dropdown';
|
||||||
|
|
||||||
class Settings extends React.Component
|
class Settings extends React.Component
|
||||||
|
|
@ -18,49 +19,82 @@ class Settings extends React.Component
|
||||||
room,
|
room,
|
||||||
me,
|
me,
|
||||||
handleChangeWebcam,
|
handleChangeWebcam,
|
||||||
|
handleChangeAudioDevice,
|
||||||
onToggleSettings
|
onToggleSettings
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!room.showSettings)
|
if (!room.showSettings)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const webcams = Object.values(me.webcamDevices);
|
let webcams;
|
||||||
|
let webcamText;
|
||||||
|
|
||||||
|
if (me.canChangeWebcam)
|
||||||
|
webcamText = 'Select camera';
|
||||||
|
else
|
||||||
|
webcamText = 'Unable to select camera';
|
||||||
|
|
||||||
|
if (me.webcamDevices)
|
||||||
|
webcams = Object.values(me.webcamDevices);
|
||||||
|
else
|
||||||
|
webcams = [];
|
||||||
|
|
||||||
|
let audioDevices;
|
||||||
|
let audioDevicesText;
|
||||||
|
|
||||||
|
if (me.canChangeAudioDevice)
|
||||||
|
audioDevicesText = 'Select audio input device';
|
||||||
|
else
|
||||||
|
audioDevicesText = 'Unable to select audio input device';
|
||||||
|
|
||||||
|
if (me.audioDevices)
|
||||||
|
audioDevices = Object.values(me.audioDevices);
|
||||||
|
else
|
||||||
|
audioDevices = [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-component='Settings'>
|
<Appear duration={500}>
|
||||||
<div className='dialog'>
|
<div data-component='Settings'>
|
||||||
<div className='header'>
|
<div className='dialog'>
|
||||||
<span>Settings</span>
|
<div className='header'>
|
||||||
</div>
|
<span>Settings</span>
|
||||||
<div className='settings'>
|
</div>
|
||||||
<Dropdown
|
<div className='settings'>
|
||||||
disabled={!me.canChangeWebcam}
|
<Dropdown
|
||||||
options={webcams}
|
disabled={!me.canChangeWebcam}
|
||||||
onChange={handleChangeWebcam}
|
options={webcams}
|
||||||
value={webcams[0]}
|
onChange={handleChangeWebcam}
|
||||||
placeholder='No other cameras detected'
|
placeholder={webcamText}
|
||||||
/>
|
/>
|
||||||
</div>
|
<Dropdown
|
||||||
<div className='footer'>
|
disabled={!me.canChangeAudioDevice}
|
||||||
<span
|
options={audioDevices}
|
||||||
className='button'
|
onChange={handleChangeAudioDevice}
|
||||||
onClick={() => onToggleSettings()}
|
placeholder={audioDevicesText}
|
||||||
>
|
/>
|
||||||
Close
|
</div>
|
||||||
</span>
|
<div className='footer'>
|
||||||
|
<span
|
||||||
|
className='button'
|
||||||
|
onClick={() => onToggleSettings()}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Appear>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings.propTypes =
|
Settings.propTypes =
|
||||||
{
|
{
|
||||||
me : appPropTypes.Me.isRequired,
|
me : appPropTypes.Me.isRequired,
|
||||||
room : appPropTypes.Room.isRequired,
|
room : appPropTypes.Room.isRequired,
|
||||||
onToggleSettings : PropTypes.func.isRequired,
|
onToggleSettings : PropTypes.func.isRequired,
|
||||||
handleChangeWebcam : PropTypes.func.isRequired
|
handleChangeWebcam : PropTypes.func.isRequired,
|
||||||
|
handleChangeAudioDevice : PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
|
|
@ -77,6 +111,10 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
handleChangeWebcam : (device) =>
|
handleChangeWebcam : (device) =>
|
||||||
{
|
{
|
||||||
dispatch(requestActions.changeWebcam(device.value));
|
dispatch(requestActions.changeWebcam(device.value));
|
||||||
|
},
|
||||||
|
handleChangeAudioDevice : (device) =>
|
||||||
|
{
|
||||||
|
dispatch(requestActions.changeAudioDevice(device.value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,12 @@ const initialState =
|
||||||
canSendWebcam : false,
|
canSendWebcam : false,
|
||||||
canShareScreen : false,
|
canShareScreen : false,
|
||||||
needExtension : false,
|
needExtension : false,
|
||||||
|
canChangeAudioDevice : false,
|
||||||
|
audioDevices : null,
|
||||||
canChangeWebcam : false,
|
canChangeWebcam : false,
|
||||||
webcamDevices : null,
|
webcamDevices : null,
|
||||||
webcamInProgress : false,
|
webcamInProgress : false,
|
||||||
|
audioInProgress : false,
|
||||||
screenShareInProgress : false,
|
screenShareInProgress : false,
|
||||||
audioOnly : false,
|
audioOnly : false,
|
||||||
audioOnlyInProgress : false,
|
audioOnlyInProgress : false,
|
||||||
|
|
@ -44,6 +47,20 @@ const me = (state = initialState, action) =>
|
||||||
return { ...state, canShareScreen, needExtension };
|
return { ...state, canShareScreen, needExtension };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_CAN_CHANGE_AUDIO_DEVICE':
|
||||||
|
{
|
||||||
|
const canChangeAudioDevice = action.payload;
|
||||||
|
|
||||||
|
return { ...state, canChangeAudioDevice };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_AUDIO_DEVICES':
|
||||||
|
{
|
||||||
|
const { devices } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, audioDevices: devices };
|
||||||
|
}
|
||||||
|
|
||||||
case 'SET_CAN_CHANGE_WEBCAM':
|
case 'SET_CAN_CHANGE_WEBCAM':
|
||||||
{
|
{
|
||||||
const canChangeWebcam = action.payload;
|
const canChangeWebcam = action.payload;
|
||||||
|
|
@ -53,11 +70,18 @@ const me = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'SET_WEBCAM_DEVICES':
|
case 'SET_WEBCAM_DEVICES':
|
||||||
{
|
{
|
||||||
const devices = action.payload;
|
const { devices } = action.payload;
|
||||||
|
|
||||||
return { ...state, webcamDevices: devices };
|
return { ...state, webcamDevices: devices };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_AUDIO_IN_PROGRESS':
|
||||||
|
{
|
||||||
|
const { flag } = action.payload;
|
||||||
|
|
||||||
|
return { ...state, audioInProgress: flag };
|
||||||
|
}
|
||||||
|
|
||||||
case 'SET_WEBCAM_IN_PROGRESS':
|
case 'SET_WEBCAM_IN_PROGRESS':
|
||||||
{
|
{
|
||||||
const { flag } = action.payload;
|
const { flag } = action.payload;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,14 @@ export const changeWebcam = (deviceId) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const changeAudioDevice = (deviceId) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type : 'CHANGE_AUDIO_DEVICE',
|
||||||
|
payload : { deviceId }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const enableAudioOnly = () =>
|
export const enableAudioOnly = () =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,15 @@ export default ({ dispatch, getState }) => (next) =>
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'CHANGE_AUDIO_DEVICE':
|
||||||
|
{
|
||||||
|
const { deviceId } = action.payload;
|
||||||
|
|
||||||
|
client.changeAudioDevice(deviceId);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'ENABLE_AUDIO_ONLY':
|
case 'ENABLE_AUDIO_ONLY':
|
||||||
{
|
{
|
||||||
client.enableAudioOnly();
|
client.enableAudioOnly();
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,22 @@ export const setScreenCapabilities = ({ canShareScreen, needExtension }) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setCanChangeAudioDevice = (flag) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type : 'SET_CAN_CHANGE_AUDIO_DEVICE',
|
||||||
|
payload : flag
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setAudioDevices = (devices) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type : 'SET_AUDIO_DEVICES',
|
||||||
|
payload : { devices }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const setCanChangeWebcam = (flag) =>
|
export const setCanChangeWebcam = (flag) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
|
@ -66,7 +82,7 @@ export const setWebcamDevices = (devices) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_WEBCAM_DEVICES',
|
type : 'SET_WEBCAM_DEVICES',
|
||||||
payload : devices
|
payload : { devices }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -189,6 +205,14 @@ export const setProducerTrack = (producerId, track) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setAudioInProgress = (flag) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type : 'SET_AUDIO_IN_PROGRESS',
|
||||||
|
payload : { flag }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const setWebcamInProgress = (flag) =>
|
export const setWebcamInProgress = (flag) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
z-index: 19999;
|
z-index: 19999;
|
||||||
background-color: rgba(000, 000, 000, 0.5);
|
background-color: rgba(000, 000, 000, 0.5);
|
||||||
|
|
||||||
|
AppearFadeIn(500ms);
|
||||||
|
|
||||||
> .dialog {
|
> .dialog {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 50vmin;
|
height: 50vmin;
|
||||||
|
|
@ -29,6 +31,8 @@
|
||||||
> .settings {
|
> .settings {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding-top: 1vmin;
|
||||||
|
padding-bottom: 1vmin;
|
||||||
|
|
||||||
.Dropdown-root {
|
.Dropdown-root {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue