From 6636fa32f67a592eb18208e89b2ae2ce226c00ac Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 26 May 2018 17:22:23 +0100 Subject: [PATCH] Allow selecting audio output for WebRTC Audio/Video calls Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/CallMediaHandler.js | 22 +++++++++++----- src/components/structures/UserSettings.js | 32 +++++++++++++++++++++-- src/settings/Settings.js | 4 +++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index cdc5c61921..2330f86b99 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -22,34 +22,44 @@ export default { // Only needed for Electron atm, though should work in modern browsers // once permission has been granted to the webapp return navigator.mediaDevices.enumerateDevices().then(function(devices) { - const audioIn = []; - const videoIn = []; + const audiooutput = []; + const audioinput = []; + const videoinput = []; if (devices.some((device) => !device.label)) return false; devices.forEach((device) => { switch (device.kind) { - case 'audioinput': audioIn.push(device); break; - case 'videoinput': videoIn.push(device); break; + case 'audiooutput': audiooutput.push(device); break; + case 'audioinput': audioinput.push(device); break; + case 'videoinput': videoinput.push(device); break; } }); // console.log("Loaded WebRTC Devices", mediaDevices); return { - audioinput: audioIn, - videoinput: videoIn, + audiooutput, + audioinput, + videoinput, }; }, (error) => { console.log('Unable to refresh WebRTC Devices: ', error); }); }, loadDevices: function() { + const audioOutDeviceId = SettingsStore.getValue("webrtc_audiooutput"); const audioDeviceId = SettingsStore.getValue("webrtc_audioinput"); const videoDeviceId = SettingsStore.getValue("webrtc_videoinput"); + Matrix.setMatrixCallAudioOutput(audioOutDeviceId); Matrix.setMatrixCallAudioInput(audioDeviceId); Matrix.setMatrixCallVideoInput(videoDeviceId); }, + setAudioOutput: function(deviceId) { + SettingsStore.setValue("webrtc_audiooutput", null, SettingLevel.DEVICE, deviceId); + Matrix.setMatrixCallAudioOutput(deviceId); + }, + setAudioInput: function(deviceId) { SettingsStore.setValue("webrtc_audioinput", null, SettingLevel.DEVICE, deviceId); Matrix.setMatrixCallAudioInput(deviceId); diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index c8ce79905d..0561071fce 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -292,6 +292,7 @@ module.exports = React.createClass({ if (this._unmounted) return; this.setState({ mediaDevices, + activeAudioOutput: SettingsStore.getValueAt(SettingLevel.DEVICE, 'webrtc_audiooutput'), activeAudioInput: SettingsStore.getValueAt(SettingLevel.DEVICE, 'webrtc_audioinput'), activeVideoInput: SettingsStore.getValueAt(SettingLevel.DEVICE, 'webrtc_videoinput'), }); @@ -970,6 +971,11 @@ module.exports = React.createClass({ return devices.map((device) => <span key={device.deviceId}>{ device.label }</span>); }, + _setAudioOutput: function(deviceId) { + this.setState({activeAudioOutput: deviceId}); + CallMediaHandler.setAudioOutput(deviceId); + }, + _setAudioInput: function(deviceId) { this.setState({activeAudioInput: deviceId}); CallMediaHandler.setAudioInput(deviceId); @@ -1010,6 +1016,7 @@ module.exports = React.createClass({ const Dropdown = sdk.getComponent('elements.Dropdown'); + let speakerDropdown = <p>{ _t('No Audio Outputs detected') }</p>; let microphoneDropdown = <p>{ _t('No Microphones detected') }</p>; let webcamDropdown = <p>{ _t('No Webcams detected') }</p>; @@ -1018,6 +1025,26 @@ module.exports = React.createClass({ label: _t('Default Device'), }; + const audioOutputs = this.state.mediaDevices.audiooutput.slice(0); + if (audioOutputs.length > 0) { + let defaultOutput = ''; + if (!audioOutputs.some((input) => input.deviceId === 'default')) { + audioOutputs.unshift(defaultOption); + } else { + defaultOutput = 'default'; + } + + speakerDropdown = <div> + <h4>{ _t('Audio Output') }</h4> + <Dropdown + className="mx_UserSettings_webRtcDevices_dropdown" + value={this.state.activeAudioOutput || defaultOutput} + onOptionChange={this._setAudioOutput}> + { this._mapWebRtcDevicesToSpans(audioOutputs) } + </Dropdown> + </div>; + } + const audioInputs = this.state.mediaDevices.audioinput.slice(0); if (audioInputs.length > 0) { let defaultInput = ''; @@ -1059,8 +1086,9 @@ module.exports = React.createClass({ } return <div> - { microphoneDropdown } - { webcamDropdown } + { speakerDropdown } + { microphoneDropdown } + { webcamDropdown } </div>; }, diff --git a/src/settings/Settings.js b/src/settings/Settings.js index b1bc4161fd..a222c29dda 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -201,6 +201,10 @@ export const SETTINGS = { displayName: _td('Disable Peer-to-Peer for 1:1 calls'), default: false, }, + "webrtc_audiooutput": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + default: null, + }, "webrtc_audioinput": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: null,