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 d88a477932..15abb5fec1 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -298,6 +298,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'), }); @@ -976,6 +977,11 @@ module.exports = React.createClass({ return devices.map((device) => { device.label }); }, + _setAudioOutput: function(deviceId) { + this.setState({activeAudioOutput: deviceId}); + CallMediaHandler.setAudioOutput(deviceId); + }, + _setAudioInput: function(deviceId) { this.setState({activeAudioInput: deviceId}); CallMediaHandler.setAudioInput(deviceId); @@ -1016,6 +1022,7 @@ module.exports = React.createClass({ const Dropdown = sdk.getComponent('elements.Dropdown'); + let speakerDropdown =

{ _t('No Audio Outputs detected') }

; let microphoneDropdown =

{ _t('No Microphones detected') }

; let webcamDropdown =

{ _t('No Webcams detected') }

; @@ -1024,6 +1031,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 =
+

{ _t('Audio Output') }

+ + { this._mapWebRtcDevicesToSpans(audioOutputs) } + +
; + } + const audioInputs = this.state.mediaDevices.audioinput.slice(0); if (audioInputs.length > 0) { let defaultInput = ''; @@ -1065,8 +1092,9 @@ module.exports = React.createClass({ } return
- { microphoneDropdown } - { webcamDropdown } + { speakerDropdown } + { microphoneDropdown } + { webcamDropdown }
; }, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1a761c2c5c..cda008ecbd 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1079,9 +1079,11 @@ "No media permissions": "No media permissions", "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", + "No Audio Outputs detected": "No Audio Outputs detected", "No Microphones detected": "No Microphones detected", "No Webcams detected": "No Webcams detected", "Default Device": "Default Device", + "Audio Output": "Audio Output", "Microphone": "Microphone", "Camera": "Camera", "VoIP": "VoIP", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 456665aeb8..d1d0cf19aa 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,