diff --git a/bigbluebutton-html5/.eslintrc.js b/bigbluebutton-html5/.eslintrc.js
index 9ab00665e1abf9f9c13f79704363d0f33b4c6c51..7ccce5d1981409146dad2730811d520a7539bcf5 100644
--- a/bigbluebutton-html5/.eslintrc.js
+++ b/bigbluebutton-html5/.eslintrc.js
@@ -19,6 +19,7 @@ module.exports = {
       "import/no-unresolved": 0,
       "import/no-extraneous-dependencies": 1,
       "react/prop-types": 1,
+      "jsx-a11y/no-access-key": 0,
     },
     "globals": {
       "browser": "writable",
diff --git a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js
index f1fd1d1d79631cf4651f4e1a2ec840b4f94bc797..d4310df49544e25f88d3648c2547c8568143ec8d 100755
--- a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js
+++ b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js
@@ -18,6 +18,7 @@ import VoiceCallStates from '/imports/api/voice-call-states';
 import CallStateOptions from '/imports/api/voice-call-states/utils/callStates';
 import Auth from '/imports/ui/services/auth';
 import Settings from '/imports/ui/services/settings';
+import Storage from '/imports/ui/services/storage/session';
 
 const MEDIA = Meteor.settings.public.media;
 const MEDIA_TAG = MEDIA.mediaTag;
@@ -40,6 +41,12 @@ const TRACE_SIP = MEDIA.traceSip || false;
 const AUDIO_MICROPHONE_CONSTRAINTS = Meteor.settings.public.app.defaultSettings
   .application.microphoneConstraints;
 
+const DEFAULT_INPUT_DEVICE_ID = 'default';
+const DEFAULT_OUTPUT_DEVICE_ID = 'default';
+
+const INPUT_DEVICE_ID_KEY = 'audioInputDeviceId';
+const OUTPUT_DEVICE_ID_KEY = 'audioOutputDeviceId';
+
 const getAudioSessionNumber = () => {
   let currItem = parseInt(sessionStorage.getItem(AUDIO_SESSION_NUM_KEY), 10);
   if (!currItem) {
@@ -81,7 +88,8 @@ class SIPSession {
     this.reconnectAttempt = reconnectAttempt;
     this.currentSession = null;
     this.remoteStream = null;
-    this.inputDeviceId = null;
+    this._inputDeviceId = DEFAULT_INPUT_DEVICE_ID;
+    this._outputDeviceId = null;
     this._hangupFlag = false;
     this._reconnecting = false;
     this._currentSessionState = null;
@@ -94,6 +102,85 @@ class SIPSession {
     return null;
   }
 
+  getAudioConstraints() {
+    const userSettingsConstraints = Settings.application.microphoneConstraints;
+    const audioDeviceConstraints = userSettingsConstraints
+      || AUDIO_MICROPHONE_CONSTRAINTS || {};
+
+    const matchConstraints = this.filterSupportedConstraints(
+      audioDeviceConstraints,
+    );
+
+    if (this.inputDeviceId) {
+      matchConstraints.deviceId = this.inputDeviceId;
+    }
+
+    return matchConstraints;
+  }
+
+  setInputStream(stream) {
+    if (!this.currentSession
+      || !this.currentSession.sessionDescriptionHandler
+    ) return;
+
+
+    this.currentSession.sessionDescriptionHandler.setLocalMediaStream(stream);
+  }
+
+
+  liveChangeInputDevice(deviceId) {
+    this.inputDeviceId = deviceId;
+
+    const constraints = {
+      audio: this.getAudioConstraints(),
+    };
+
+    this.inputStream.getAudioTracks().forEach(t => t.stop());
+
+    return navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
+      this.setInputStream(stream);
+    });
+  }
+
+  get inputDeviceId() {
+    if (!this._inputDeviceId) {
+      const stream = this.inputStream;
+
+      if (stream) {
+        const track = stream.getAudioTracks().find(
+          t => t.getSettings().deviceId,
+        );
+
+        if (track && (typeof track.getSettings === 'function')) {
+          const { deviceId } = track.getSettings();
+          return deviceId || DEFAULT_INPUT_DEVICE_ID;
+        }
+      }
+    }
+
+    return (this._inputDeviceId || DEFAULT_INPUT_DEVICE_ID);
+  }
+
+  set inputDeviceId(deviceId) {
+    this._inputDeviceId = deviceId;
+  }
+
+  get outputDeviceId() {
+    if (!this._outputDeviceId) {
+      const audioElement = document.querySelector(MEDIA_TAG);
+      if (audioElement && audioElement.sinkId) {
+        return audioElement.sinkId;
+      }
+    }
+
+    return this._outputDeviceId || DEFAULT_OUTPUT_DEVICE_ID;
+  }
+
+  set outputDeviceId(deviceId) {
+    this._outputDeviceId = deviceId;
+    Storage.setItem(OUTPUT_DEVICE_ID_KEY, deviceId);
+  }
+
   joinAudio({ isListenOnly, extension, inputDeviceId }, managerCallback) {
     return new Promise((resolve, reject) => {
       const callExtension = extension ? `${extension}${this.userData.voiceBridge}` : this.userData.voiceBridge;
@@ -588,17 +675,7 @@ class SIPSession {
 
       const target = SIP.UserAgent.makeURI(`sip:${callExtension}@${hostname}`);
 
-      const userSettingsConstraints = Settings.application.microphoneConstraints;
-      const audioDeviceConstraints = userSettingsConstraints
-        || AUDIO_MICROPHONE_CONSTRAINTS || {};
-
-      const matchConstraints = this.filterSupportedConstraints(
-        audioDeviceConstraints,
-      );
-
-      if (this.inputDeviceId) {
-        matchConstraints.deviceId = this.inputDeviceId;
-      }
+      const matchConstraints = this.getAudioConstraints();
 
       const inviterOptions = {
         sessionDescriptionHandlerOptions: {
@@ -1068,7 +1145,57 @@ export default class SIPBridge extends BaseAudioBridge {
   }
 
   get inputDeviceId() {
-    return this.media.inputDevice ? this.media.inputDevice.inputDeviceId : null;
+    const sessionInputDeviceId = Storage.getItem(INPUT_DEVICE_ID_KEY);
+
+    if (sessionInputDeviceId) {
+      return sessionInputDeviceId;
+    }
+
+    if (this.media.inputDeviceId) {
+      return this.media.inputDeviceId;
+    }
+
+    if (this.activeSession) {
+      return this.activeSession.inputDeviceId;
+    }
+
+    return DEFAULT_INPUT_DEVICE_ID;
+  }
+
+  set inputDeviceId(deviceId) {
+    Storage.setItem(INPUT_DEVICE_ID_KEY, deviceId);
+    this.media.inputDeviceId = deviceId;
+
+    if (this.activeSession) {
+      this.activeSession.inputDeviceId = deviceId;
+    }
+  }
+
+  get outputDeviceId() {
+    const sessionOutputDeviceId = Storage.getItem(OUTPUT_DEVICE_ID_KEY);
+
+    if (sessionOutputDeviceId) {
+      return sessionOutputDeviceId;
+    }
+
+    if (this.media.outputDeviceId) {
+      return this.media.outputDeviceId;
+    }
+
+    if (this.activeSession) {
+      return this.activeSession.outputDeviceId;
+    }
+
+    return DEFAULT_OUTPUT_DEVICE_ID;
+  }
+
+  set outputDeviceId(deviceId) {
+    Storage.setItem(OUTPUT_DEVICE_ID_KEY, deviceId);
+    this.media.outputDeviceId = deviceId;
+
+    if (this.activeSession) {
+      this.activeSession.outputDeviceId = deviceId;
+    }
   }
 
   get inputStream() {
@@ -1107,7 +1234,7 @@ export default class SIPBridge extends BaseAudioBridge {
             const fallbackExtension = this.activeSession.inEchoTest ? extension : undefined;
             this.activeSession = new SIPSession(this.user, this.userData, this.protocol,
               hostname, this.baseCallStates, this.baseErrorCodes, true);
-            const { inputDeviceId } = this.media.inputDevice;
+            const { inputDeviceId } = this;
             this.activeSession.joinAudio({
               isListenOnly,
               extension: fallbackExtension,
@@ -1124,7 +1251,7 @@ export default class SIPBridge extends BaseAudioBridge {
         return managerCallback(message);
       };
 
-      const { inputDeviceId } = this.media.inputDevice;
+      const { inputDeviceId } = this;
       this.activeSession.joinAudio({
         isListenOnly,
         extension,
@@ -1155,7 +1282,7 @@ export default class SIPBridge extends BaseAudioBridge {
   }
 
   setDefaultInputDevice() {
-    this.media.inputDevice.inputDeviceId = DEFAULT_INPUT_DEVICE_ID;
+    this.inputDeviceId = DEFAULT_INPUT_DEVICE_ID;
   }
 
   async changeInputDeviceId(inputDeviceId) {
@@ -1163,33 +1290,27 @@ export default class SIPBridge extends BaseAudioBridge {
       throw new Error();
     }
 
-    this.media.inputDevice.inputDeviceId = inputDeviceId;
+    this.inputDeviceId = inputDeviceId;
     return inputDeviceId;
   }
 
   liveChangeInputDevice(deviceId) {
-    const constraints = {
-      audio: {
-        deviceId,
-      },
-    };
-
-    return navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
-      const peer = this.getPeerConnection();
-      const senders = peer.getSenders()[0];
-      const firstTrack = stream.getAudioTracks()[0];
-      senders.replaceTrack(firstTrack);
-    });
+    this.inputDeviceId = deviceId;
+    return this.activeSession.liveChangeInputDevice(deviceId);
   }
 
-  async changeOutputDevice(value) {
+  async changeOutputDevice(value, isLive) {
     const audioContext = document.querySelector(MEDIA_TAG);
 
     if (audioContext.setSinkId) {
       try {
+        if (!isLive) {
+          audioContext.srcObject = null;
+        }
+
         await audioContext.setSinkId(value);
         audioContext.load();
-        this.media.outputDeviceId = value;
+        this.outputDeviceId = value;
       } catch (err) {
         logger.error({
           logCode: 'audio_sip_changeoutputdevice_error',
@@ -1199,7 +1320,7 @@ export default class SIPBridge extends BaseAudioBridge {
       }
     }
 
-    return this.media.outputDeviceId || value;
+    return this.outputDeviceId;
   }
 
   async updateAudioConstraints(constraints) {
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx
index b0aac6ad7cd443fb37796f19aa88f5592fca39de..3ec223266110f5f46460c8fbfad8d7523933b9ee 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx
@@ -14,10 +14,6 @@ const intlMessages = defineMessages({
     id: 'app.audio.joinAudio',
     description: 'Join audio button label',
   },
-  leaveAudio: {
-    id: 'app.audio.leaveAudio',
-    description: 'Leave audio button label',
-  },
   muteAudio: {
     id: 'app.actionsBar.muteLabel',
     description: 'Mute audio button label',
@@ -57,29 +53,7 @@ class AudioControls extends PureComponent {
   }
 
   renderLeaveButton() {
-    const {
-      listenOnly,
-      inAudio,
-      isVoiceUser,
-      handleLeaveAudio,
-      shortcuts,
-      disable,
-      intl,
-    } = this.props;
-    return (inAudio && isVoiceUser && listenOnly) ? (
-      <Button
-        onClick={handleLeaveAudio}
-        disabled={disable}
-        hideLabel
-        aria-label={intl.formatMessage(intlMessages.leaveAudio)}
-        label={intl.formatMessage(intlMessages.leaveAudio)}
-        color="primary"
-        icon="listen"
-        size="lg"
-        circle
-        accessKey={shortcuts.leaveAudio}
-      />
-    ) : (<InputStreamLiveSelectorContainer />);
+    return (<InputStreamLiveSelectorContainer />);
   }
 
   render() {
@@ -147,7 +121,7 @@ class AudioControls extends PureComponent {
                 icon={'audio_off'}
                 size="lg"
                 circle
-                accessKey={shortcuts.joinAudio}
+                accessKey={shortcuts.joinaudio}
               />
             )
         }
@@ -158,4 +132,4 @@ class AudioControls extends PureComponent {
 
 AudioControls.propTypes = propTypes;
 
-export default withShortcutHelper(injectIntl(AudioControls), ['joinAudio', 'leaveAudio', 'toggleMute']);
+export default withShortcutHelper(injectIntl(AudioControls), ['joinAudio', 'toggleMute']);
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
index 3deefe3d9eed3f4607b6a9401c48df0a8a1c634e..d3b22aa03314d88e0292017e4edc9666812fc169 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
@@ -1,4 +1,6 @@
-import React, { Component, Fragment } from 'react';
+import React, { Component } from 'react';
+import logger from '/imports/startup/client/logger';
+import Auth from '/imports/ui/services/auth';
 import { defineMessages, injectIntl } from 'react-intl';
 import PropTypes from 'prop-types';
 import Button from '/imports/ui/components/button/component';
@@ -15,6 +17,8 @@ import { styles } from '../styles';
 
 const AUDIO_INPUT = 'audioinput';
 const AUDIO_OUTPUT = 'audiooutput';
+const DEFAULT_DEVICE = 'default';
+const DEVICE_LABEL_MAX_LENGTH = 40;
 
 const intlMessages = defineMessages({
   changeLeaveAudio: {
@@ -29,12 +33,16 @@ const intlMessages = defineMessages({
     id: 'app.audio.loading',
     description: 'Loading audio dropdown item label',
   },
-  input: {
-    id: 'app.audio.input',
+  noDeviceFound: {
+    id: 'app.audio.noDeviceFound',
+    description: 'No device found',
+  },
+  microphones: {
+    id: 'app.audio.microphones',
     description: 'Input audio dropdown item label',
   },
-  output: {
-    id: 'app.audio.output',
+  speakers: {
+    id: 'app.audio.speakers',
     description: 'Output audio dropdown item label',
   },
 });
@@ -43,110 +51,275 @@ const propTypes = {
   liveChangeInputDevice: PropTypes.func.isRequired,
   exitAudio: PropTypes.func.isRequired,
   liveChangeOutputDevice: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired,
-  shortcuts: PropTypes.object.isRequired,
+  intl: PropTypes.shape({
+    formatMessage: PropTypes.func.isRequired,
+  }).isRequired,
+  shortcuts: PropTypes.objectOf(PropTypes.string).isRequired,
+  currentInputDeviceId: PropTypes.string.isRequired,
+  currentOutputDeviceId: PropTypes.string.isRequired,
+  isListenOnly: PropTypes.bool.isRequired,
+  isAudioConnected: PropTypes.bool.isRequired,
 };
 
 class InputStreamLiveSelector extends Component {
+  static truncateDeviceName(deviceName) {
+    if (deviceName && (deviceName.length <= DEVICE_LABEL_MAX_LENGTH)) {
+      return deviceName;
+    }
+
+    return `${deviceName.substring(0, DEVICE_LABEL_MAX_LENGTH - 3)}...`;
+  }
+
   constructor(props) {
     super(props);
-    this.setInputDevices = this.setInputDevices.bind(this);
-    this.setOutputDevices = this.setOutputDevices.bind(this);
+    this.updateDeviceList = this.updateDeviceList.bind(this);
     this.renderDeviceList = this.renderDeviceList.bind(this);
     this.state = {
       audioInputDevices: null,
       audioOutputDevices: null,
+      selectedInputDeviceId: null,
+      selectedOutputDeviceId: null,
     };
   }
 
   componentDidMount() {
-    this.setInputDevices();
-    this.setOutputDevices();
+    this.updateDeviceList().then(() => {
+      navigator.mediaDevices
+        .addEventListener('devicechange', this.updateDeviceList);
+      this.setCurrentDevices();
+    });
   }
 
-  setInputDevices() {
-    navigator.mediaDevices.enumerateDevices()
-      .then((devices) => {
-        this.setState({
-          audioInputDevices: devices.filter(i => i.kind === AUDIO_INPUT),
-        });
-      });
+  componentWillUnmount() {
+    navigator.mediaDevices.removeEventListener('devicechange',
+      this.updateDeviceList);
   }
 
-  setOutputDevices() {
-    navigator.mediaDevices.enumerateDevices()
+  onDeviceListClick(deviceId, deviceKind, callback) {
+    if (!deviceId) return;
+
+    if (deviceKind === AUDIO_INPUT) {
+      this.setState({ selectedInputDeviceId: deviceId });
+      callback(deviceId);
+    } else {
+      this.setState({ selectedOutputDeviceId: deviceId });
+      callback(deviceId, true);
+    }
+  }
+
+  setCurrentDevices() {
+    const {
+      currentInputDeviceId,
+      currentOutputDeviceId,
+    } = this.props;
+
+    const {
+      audioInputDevices,
+      audioOutputDevices,
+    } = this.state;
+
+    if (!audioInputDevices
+      || !audioInputDevices[0]
+      || !audioOutputDevices
+      || !audioOutputDevices[0]) return;
+
+    const _currentInputDeviceId = audioInputDevices.find(
+      d => d.deviceId === currentInputDeviceId,
+    ) ? currentInputDeviceId : audioInputDevices[0].deviceId;
+
+    const _currentOutputDeviceId = audioOutputDevices.find(
+      d => d.deviceId === currentOutputDeviceId,
+    ) ? currentOutputDeviceId : audioOutputDevices[0].deviceId;
+
+    this.setState({
+      selectedInputDeviceId: _currentInputDeviceId,
+      selectedOutputDeviceId: _currentOutputDeviceId,
+    });
+  }
+
+  fallbackInputDevice(fallbackDevice) {
+    if (!fallbackDevice || !fallbackDevice.deviceId) return;
+
+    const {
+      liveChangeInputDevice,
+    } = this.props;
+
+    logger.info({
+      logCode: 'audio_device_live_selector',
+      extraInfo: {
+        userId: Auth.userID,
+        meetingId: Auth.meetingID,
+      },
+    }, 'Current input device was removed. Fallback to default device');
+    this.setState({ selectedInputDeviceId: fallbackDevice.deviceId });
+    liveChangeInputDevice(fallbackDevice.deviceId);
+  }
+
+  fallbackOutputDevice(fallbackDevice) {
+    if (!fallbackDevice || !fallbackDevice.deviceId) return;
+
+    const {
+      liveChangeOutputDevice,
+    } = this.props;
+
+    logger.info({
+      logCode: 'audio_device_live_selector',
+      extraInfo: {
+        userId: Auth.userID,
+        meetingId: Auth.meetingID,
+      },
+    }, 'Current output device was removed. Fallback to default device');
+    this.setState({ selectedOutputDeviceId: fallbackDevice.deviceId });
+    liveChangeOutputDevice(fallbackDevice.deviceId, true);
+  }
+
+  updateRemovedDevices(audioInputDevices, audioOutputDevices) {
+    const {
+      selectedInputDeviceId,
+      selectedOutputDeviceId,
+    } = this.state;
+
+    if (selectedInputDeviceId
+      && (selectedInputDeviceId !== DEFAULT_DEVICE)
+      && !audioInputDevices.find(d => d.deviceId === selectedInputDeviceId)) {
+      this.fallbackInputDevice(audioInputDevices[0]);
+    }
+
+    if (selectedOutputDeviceId
+      && (selectedOutputDeviceId !== DEFAULT_DEVICE)
+      && !audioOutputDevices.find(d => d.deviceId === selectedOutputDeviceId)) {
+      this.fallbackOutputDevice(audioOutputDevices[0]);
+    }
+  }
+
+  updateDeviceList() {
+    const {
+      isAudioConnected,
+    } = this.props;
+
+    return navigator.mediaDevices.enumerateDevices()
       .then((devices) => {
+        const audioInputDevices = devices.filter(i => i.kind === AUDIO_INPUT);
+        const audioOutputDevices = devices.filter(i => i.kind === AUDIO_OUTPUT);
+
         this.setState({
-          audioOutputDevices: devices.filter(i => i.kind === AUDIO_OUTPUT),
+          audioInputDevices,
+          audioOutputDevices,
         });
+
+        if (isAudioConnected) {
+          this.updateRemovedDevices(audioInputDevices, audioOutputDevices);
+        }
       });
   }
 
-  renderDeviceList(list, callback, title) {
-    const { intl } = this.props;
-    return [
-      <DropdownListTitle>{title}</DropdownListTitle>,
-      list ? list.map(device => (
+  renderDeviceList(deviceKind, list, callback, title, currentDeviceId) {
+    const {
+      intl,
+    } = this.props;
+
+    const listLenght = list ? list.length : -1;
+
+    const listTitle = [
+      <DropdownListTitle key={`audioDeviceList-${deviceKind}`}>
+        {title}
+      </DropdownListTitle>,
+    ];
+
+    const deviceList = (listLenght > 0)
+      ? list.map(device => (
         <DropdownListItem
-          key={device.deviceId}
-          label={device.label}
-          onClick={() => callback(device.deviceId)}
+          key={`${device.deviceId}-${deviceKind}`}
+          label={InputStreamLiveSelector.truncateDeviceName(device.label)}
+          onClick={() => this.onDeviceListClick(device.deviceId, deviceKind,
+            callback)}
+          className={(device.deviceId === currentDeviceId)
+            ? styles.selectedDevice : ''}
         />
       ))
-        : (
-          <DropdownListItem
-            label={intl.formatMessage(intlMessages.loading)}
-          />
-        ),
-      <DropdownListSeparator />,
+      : [
+        <DropdownListItem
+          key={`noDeviceFoundKey-${deviceKind}-`}
+          className={styles.disableDeviceSelection}
+          label={
+            listLenght < 0
+              ? intl.formatMessage(intlMessages.loading)
+              : intl.formatMessage(intlMessages.noDeviceFound)
+          }
+        />,
+      ];
+
+    const listSeparator = [
+      <DropdownListSeparator key={`audioDeviceListSeparator-${deviceKind}`} />,
     ];
+
+    return listTitle.concat(deviceList).concat(listSeparator);
   }
 
   render() {
-    const { audioInputDevices, audioOutputDevices } = this.state;
+    const {
+      audioInputDevices,
+      audioOutputDevices,
+      selectedInputDeviceId,
+      selectedOutputDeviceId,
+    } = this.state;
+
     const {
       liveChangeInputDevice,
       exitAudio,
       liveChangeOutputDevice,
       intl,
       shortcuts,
+      currentInputDeviceId,
+      currentOutputDeviceId,
+      isListenOnly,
     } = this.props;
+
+    const inputDeviceList = !isListenOnly
+      ? this.renderDeviceList(
+        AUDIO_INPUT,
+        audioInputDevices,
+        liveChangeInputDevice,
+        intl.formatMessage(intlMessages.microphones),
+        selectedInputDeviceId || currentInputDeviceId,
+      ) : [];
+
+    const outputDeviceList = this.renderDeviceList(
+      AUDIO_OUTPUT,
+      audioOutputDevices,
+      liveChangeOutputDevice,
+      intl.formatMessage(intlMessages.speakers),
+      selectedOutputDeviceId || currentOutputDeviceId,
+    );
+
+    const dropdownListComplete = inputDeviceList.concat(outputDeviceList)
+      .concat([
+        <DropdownListItem
+          key="leaveAudioButtonKey"
+          className={styles.stopButton}
+          label={intl.formatMessage(intlMessages.leaveAudio)}
+          onClick={() => exitAudio()}
+          accessKey={shortcuts.leaveaudio}
+        />,
+      ]);
+
     return (
       <Dropdown>
-        <DropdownTrigger
-          onClick={() => {
-            this.setInputDevices();
-            this.setOutputDevices();
-          }}
-        >
+        <DropdownTrigger>
           <Button
             aria-label={intl.formatMessage(intlMessages.changeLeaveAudio)}
             label={intl.formatMessage(intlMessages.changeLeaveAudio)}
             hideLabel
             color="primary"
-            icon="audio_on"
+            icon={isListenOnly ? 'listen' : 'audio_on'}
             size="lg"
             circle
-            accessKey={shortcuts.leaveAudio}
+            onClick={() => {}}
           />
         </DropdownTrigger>
         <DropdownContent>
           <DropdownList className={styles.dropdownListContainer}>
-            {this.renderDeviceList(
-              audioInputDevices,
-              liveChangeInputDevice,
-              intl.formatMessage(intlMessages.input),
-            )}
-            {this.renderDeviceList(
-              audioOutputDevices,
-              liveChangeOutputDevice,
-              intl.formatMessage(intlMessages.output)
-            )}
-            <DropdownListItem
-              className={styles.stopButton}
-              label={intl.formatMessage(intlMessages.leaveAudio)}
-              onClick={() => exitAudio()}
-            />
+            {dropdownListComplete}
           </DropdownList>
         </DropdownContent>
       </Dropdown>
@@ -156,4 +329,5 @@ class InputStreamLiveSelector extends Component {
 
 InputStreamLiveSelector.propTypes = propTypes;
 
-export default withShortcutHelper(injectIntl(InputStreamLiveSelector));
+export default withShortcutHelper(injectIntl(InputStreamLiveSelector),
+  ['leaveAudio']);
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/container.jsx
index 25d30c32ee8c2195bccbd9c90843a089d1124a19..4c0c04c0ed989a21fc23275bcce2d162ecd7deea 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/container.jsx
@@ -13,6 +13,10 @@ class InputStreamLiveSelectorContainer extends PureComponent {
 
 export default withTracker(() => {
   return {
+    isAudioConnected: Service.isConnected(),
+    isListenOnly: Service.isListenOnly(),
+    currentInputDeviceId: Service.inputDeviceId(),
+    currentOutputDeviceId: Service.outputDeviceId(),
     liveChangeInputDevice: Service.liveChangeInputDevice,
     liveChangeOutputDevice: Service.changeOutputDevice,
     exitAudio: Service.exitAudio,
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss
index 7a82e1fbe555fa7dc88f45f9822f9b775a3fb5e3..314fea4027070318b07d8485884e028df77b6566 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/styles.scss
@@ -77,4 +77,12 @@
   & > span {
     color: var(--color-danger);
   }
-}
\ No newline at end of file
+}
+
+.disableDeviceSelection {
+  pointer-events: none;
+}
+
+.selectedDevice {
+  font-weight: bold;
+}
diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js
index dfebcb5484116fd73f29477df251df6a7da30338..0f13a71374cb2320351a4976f995a08dc1f546bd 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/service.js
+++ b/bigbluebutton-html5/imports/ui/components/audio/service.js
@@ -105,9 +105,9 @@ export default {
   toggleMuteMicrophone: debounce(toggleMuteMicrophone, 500, { leading: true, trailing: false }),
   changeInputDevice: inputDeviceId => AudioManager.changeInputDevice(inputDeviceId),
   liveChangeInputDevice: inputDeviceId => AudioManager.liveChangeInputDevice(inputDeviceId),
-  changeOutputDevice: (outputDeviceId) => {
+  changeOutputDevice: (outputDeviceId, isLive) => {
     if (AudioManager.outputDeviceId !== outputDeviceId) {
-      AudioManager.changeOutputDevice(outputDeviceId);
+      AudioManager.changeOutputDevice(outputDeviceId, isLive);
     }
   },
   isConnected: () => AudioManager.isConnected,
diff --git a/bigbluebutton-html5/imports/ui/components/dropdown/list/item/component.jsx b/bigbluebutton-html5/imports/ui/components/dropdown/list/item/component.jsx
index 1e52dfffc87e3e68d8538eb18d64ccaf52f781e7..14c4fc2ea8e6c186339b2956e4fe15fd43351e14 100644
--- a/bigbluebutton-html5/imports/ui/components/dropdown/list/item/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/dropdown/list/item/component.jsx
@@ -10,6 +10,7 @@ const propTypes = {
   icon: PropTypes.string,
   label: PropTypes.string,
   description: PropTypes.string,
+  accessKey: PropTypes.string,
 };
 
 const defaultProps = {
@@ -17,6 +18,7 @@ const defaultProps = {
   label: '',
   description: '',
   tabIndex: 0,
+  accessKey: null,
 };
 
 const messages = defineMessages({
@@ -33,11 +35,17 @@ class DropdownListItem extends Component {
   }
 
   renderDefault() {
-    const { icon, label, iconRight } = this.props;
+    const {
+      icon, label, iconRight, accessKey,
+    } = this.props;
 
     return [
       (icon ? <Icon iconName={icon} key="icon" className={styles.itemIcon} /> : null),
-      (<span className={styles.itemLabel} key="label">{label}</span>),
+      (
+        <span className={styles.itemLabel} key="label" accessKey={accessKey}>
+          {label}
+        </span>
+      ),
       (iconRight ? <Icon iconName={iconRight} key="iconRight" className={styles.iconRight} /> : null),
     ];
   }
diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
index 205a25581cf8ba1e87d7e6f4f4bd7c009f23c49b..6ce430ec611b6b75a9940d6fa746e709bfe4c099 100755
--- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
+++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
@@ -517,10 +517,10 @@ class AudioManager {
     this.bridge.liveChangeInputDevice(deviceId).then(handleChangeInputDeviceSuccess);
   }
 
-  async changeOutputDevice(deviceId) {
+  async changeOutputDevice(deviceId, isLive) {
     this.outputDeviceId = await this
       .bridge
-      .changeOutputDevice(deviceId || DEFAULT_OUTPUT_DEVICE_ID);
+      .changeOutputDevice(deviceId || DEFAULT_OUTPUT_DEVICE_ID, isLive);
   }
 
   set inputDevice(value) {
@@ -542,6 +542,11 @@ class AudioManager {
       ? this.bridge.inputDeviceId : DEFAULT_INPUT_DEVICE_ID;
   }
 
+  get outputDeviceId() {
+    return (this.bridge && this.bridge.outputDeviceId)
+      ? this.bridge.outputDeviceId : DEFAULT_OUTPUT_DEVICE_ID;
+  }
+
   get returningFromBreakoutAudioTransfer() {
     return this._returningFromBreakoutAudioTransfer;
   }
diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json
index 045da387876e0ac171c4fc0cc6c469f3d3e8cedf..6c824805a74e977e17b9eeffe7542cdd0d2c8744 100755
--- a/bigbluebutton-html5/public/locales/en.json
+++ b/bigbluebutton-html5/public/locales/en.json
@@ -491,8 +491,9 @@
     "app.audio.playSoundLabel": "Play sound",
     "app.audio.backLabel": "Back",
     "app.audio.loading": "Loading",
-    "app.audio.input": "Input",
-    "app.audio.output": "Output",
+    "app.audio.microphones": "Microphones",
+    "app.audio.speakers": "Speakers",
+    "app.audio.noDeviceFound": "No device found",
     "app.audio.audioSettings.titleLabel": "Choose your audio settings",
     "app.audio.audioSettings.descriptionLabel": "Please note, a dialog will appear in your browser, requiring you to accept sharing your microphone.",
     "app.audio.audioSettings.microphoneSourceLabel": "Microphone source",
diff --git a/bigbluebutton-html5/public/locales/pt.json b/bigbluebutton-html5/public/locales/pt.json
index 0f517f69b26c338ca583991b69223d21ace76c19..0bfe31166c1acfe6bcf293e2129cd018ac5eda42 100644
--- a/bigbluebutton-html5/public/locales/pt.json
+++ b/bigbluebutton-html5/public/locales/pt.json
@@ -459,6 +459,9 @@
     "app.audio.enterSessionLabel": "Entrar na sessão",
     "app.audio.playSoundLabel": "Reproduzir som",
     "app.audio.backLabel": "Voltar",
+    "app.audio.microphones": "Microfones",
+    "app.audio.speakers": "Alto-falantes",
+    "app.audio.noDeviceFound": "Nenhum dispositivo encontrado",
     "app.audio.audioSettings.titleLabel": "Configurações de áudio",
     "app.audio.audioSettings.descriptionLabel": "Por favor, note que aparecerá uma caixa de diálogo no seu navegador, a solicitar autorização para partilhar o seu microfone.",
     "app.audio.audioSettings.microphoneSourceLabel": "Selecionar microfone",
@@ -732,4 +735,3 @@
     "app.debugWindow.form.enableAutoarrangeLayoutDescription": "(será desactivado se arrastar ou redimensionar a área das webcams)"
 
 }
-