diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-graphql/audio-controls/input-stream-live-selector/component.tsx b/bigbluebutton-html5/imports/ui/components/audio/audio-graphql/audio-controls/input-stream-live-selector/component.tsx
index c2d4d5c458..cc8d483458 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-graphql/audio-controls/input-stream-live-selector/component.tsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-graphql/audio-controls/input-stream-live-selector/component.tsx
@@ -79,6 +79,8 @@ interface InputStreamLiveSelectorProps extends InputStreamLiveSelectorContainerP
away: boolean;
permissionStatus: string;
supportsTransparentListenOnly: boolean;
+ updateInputDevices: (devices: InputDeviceInfo[]) => void;
+ updateOutputDevices: (devices: MediaDeviceInfo[]) => void;
}
const InputStreamLiveSelector: React.FC = ({
@@ -100,6 +102,8 @@ const InputStreamLiveSelector: React.FC = ({
permissionStatus,
supportsTransparentListenOnly,
openAudioSettings,
+ updateInputDevices,
+ updateOutputDevices,
}) => {
const intl = useIntl();
const toggleVoice = useToggleVoice();
@@ -164,6 +168,9 @@ const InputStreamLiveSelector: React.FC = ({
const audioOutputDevices = devices.filter((i) => i.kind === AUDIO_OUTPUT);
setInputDevices(audioInputDevices as InputDeviceInfo[]);
setOutputDevices(audioOutputDevices);
+ // Update audio devices in AudioManager
+ updateInputDevices(audioInputDevices as InputDeviceInfo[]);
+ updateOutputDevices(audioOutputDevices);
if (inAudio) updateRemovedDevices(audioInputDevices, audioOutputDevices);
})
@@ -308,6 +315,12 @@ const InputStreamLiveSelectorContainer: React.FC {
+ AudioManager.inputDevices = devices;
+ };
+ const updateOutputDevices = (devices: MediaDeviceInfo[] = []) => {
+ AudioManager.outputDevices = devices;
+ };
return (
);
};
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx
index 1cd29fd4f7..030ea1fa19 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx
@@ -38,6 +38,8 @@ const propTypes = {
leaveEchoTest: PropTypes.func.isRequired,
changeInputDevice: PropTypes.func.isRequired,
changeOutputDevice: PropTypes.func.isRequired,
+ updateInputDevices: PropTypes.func.isRequired,
+ updateOutputDevices: PropTypes.func.isRequired,
isEchoTest: PropTypes.bool.isRequired,
isConnecting: PropTypes.bool.isRequired,
isConnected: PropTypes.bool.isRequired,
@@ -207,6 +209,8 @@ const AudioModal = ({
unmuteOnExit = false,
permissionStatus = null,
isTranscriptionEnabled,
+ updateInputDevices,
+ updateOutputDevices,
}) => {
const [content, setContent] = useState(initialContent);
const [hasError, setHasError] = useState(false);
@@ -528,6 +532,8 @@ const AudioModal = ({
permissionStatus={permissionStatus}
isTranscriptionEnabled={isTranscriptionEnabled}
skipAudioOptions={skipAudioOptions}
+ updateInputDevices={updateInputDevices}
+ updateOutputDevices={updateOutputDevices}
/>
);
};
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx
index 9c7b34ae1f..b70ef05848 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx
@@ -129,6 +129,8 @@ const AudioModalContainer = (props) => {
liveChangeInputDevice={Service.liveChangeInputDevice}
changeInputStream={Service.changeInputStream}
changeOutputDevice={Service.changeOutputDevice}
+ updateInputDevices={Service.updateInputDevices}
+ updateOutputDevices={Service.updateOutputDevices}
joinEchoTest={Service.joinEchoTest}
exitAudio={Service.exitAudio}
localEchoEnabled={LOCAL_ECHO_TEST_ENABLED}
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx
index 9a75fc239e..ce2bee9bdd 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx
@@ -21,6 +21,8 @@ const propTypes = {
changeInputDevice: PropTypes.func.isRequired,
liveChangeInputDevice: PropTypes.func.isRequired,
changeOutputDevice: PropTypes.func.isRequired,
+ updateInputDevices: PropTypes.func.isRequired,
+ updateOutputDevices: PropTypes.func.isRequired,
handleBack: PropTypes.func.isRequired,
handleConfirmation: PropTypes.func.isRequired,
handleGUMFailure: PropTypes.func.isRequired,
@@ -384,11 +386,16 @@ class AudioSettings extends React.Component {
}
updateDeviceList() {
+ const { updateInputDevices, updateOutputDevices } = this.props;
+
return navigator.mediaDevices.enumerateDevices()
.then((devices) => {
const audioInputDevices = devices.filter((i) => i.kind === 'audioinput');
const audioOutputDevices = devices.filter((i) => i.kind === 'audiooutput');
+ // Update audio devices in AudioManager
+ updateInputDevices(audioInputDevices);
+ updateOutputDevices(audioOutputDevices);
this.setState({
audioInputDevices,
audioOutputDevices,
diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js
index 6c78a516e2..b59919b686 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/service.js
+++ b/bigbluebutton-html5/imports/ui/components/audio/service.js
@@ -186,6 +186,8 @@ export default {
outputDeviceId,
isLive,
) => AudioManager.changeOutputDevice(outputDeviceId, isLive),
+ updateInputDevices: (devices) => { AudioManager.inputDevices = devices },
+ updateOutputDevices: (devices) => { AudioManager.outputDevices = devices },
toggleMuteMicrophone,
toggleMuteMicrophoneSystem,
isConnectedToBreakout: () => {
diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
index 73c86583e0..e1435db7d4 100755
--- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
+++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
@@ -88,6 +88,8 @@ class AudioManager {
this._outputDeviceId = {
value: makeVar(null),
};
+ this._inputDevices = [];
+ this._outputDevices = [];
this.BREAKOUT_AUDIO_TRANSFER_STATES = BREAKOUT_AUDIO_TRANSFER_STATES;
this._voiceActivityObserver = null;
@@ -185,6 +187,34 @@ class AudioManager {
return this._outputDeviceId.value();
}
+ set inputDevices(value) {
+ if (value?.length) {
+ this._inputDevices = value;
+ }
+ }
+
+ get inputDevices() {
+ return this._inputDevices;
+ }
+
+ get inputDevicesJSON() {
+ return this.inputDevices.map((device) => device.toJSON());
+ }
+
+ set outputDevices(value) {
+ if (value?.length) {
+ this._outputDevices = value;
+ }
+ }
+
+ get outputDevices() {
+ return this._outputDevices;
+ }
+
+ get outputDevicesJSON() {
+ return this.outputDevices.map((device) => device.toJSON());
+ }
+
shouldBypassGUM() {
return this.supportsTransparentListenOnly() && this.inputDeviceId === 'listen-only';
}
@@ -432,15 +462,17 @@ class AudioManager {
break;
case 'NotFoundError':
errorPayload.errCode = AudioErrors.MIC_ERROR.DEVICE_NOT_FOUND;
- // Reset the input device ID so the user can select a new one
- this.changeInputDevice(null);
logger.error({
logCode: 'audiomanager_error_device_not_found',
extraInfo: {
errorName: error.name,
errorMessage: error.message,
+ inputDeviceId: this.inputDeviceId,
+ inputDevices: this.inputDevicesJSON,
},
}, `Error getting microphone - {${error.name}: ${error.message}}`);
+ // Reset the input device ID so the user can select a new one
+ this.changeInputDevice(null);
break;
default:
logger.error({
@@ -448,6 +480,11 @@ class AudioManager {
extraInfo: {
errorName: error.name,
errorMessage: error.message,
+ errorStack: error?.stack,
+ inputDeviceId: this.inputDeviceId,
+ inputDevices: this.inputDevicesJSON,
+ outputDeviceId: this.outputDeviceId,
+ outputDevices: this.outputDevicesJSON,
},
}, `Error enabling audio - {${name}: ${message}}`);
break;
@@ -607,10 +644,13 @@ class AudioManager {
extraInfo: {
secondsToActivateAudio,
inputDeviceId: this.inputDeviceId,
+ inputDevices: this.inputDevicesJSON,
outputDeviceId: this.outputDeviceId,
+ outputDevices: this.outputDevicesJSON,
isListenOnly: this.isListenOnly,
},
}, 'Audio Joined');
+
if (STATS.enabled) this.monitor();
this.audioEventHandler({
name: 'started',
@@ -674,8 +714,17 @@ class AudioManager {
breakoutMeetingId: '',
status: BREAKOUT_AUDIO_TRANSFER_STATES.DISCONNECTED,
});
- logger.info({ logCode: 'audio_ended' }, 'Audio ended without issue');
this.onAudioExit();
+ logger.info({
+ logCode: 'audio_ended',
+ extraInfo: {
+ inputDeviceId: this.inputDeviceId,
+ inputDevices: this.inputDevicesJSON,
+ outputDeviceId: this.outputDeviceId,
+ outputDevices: this.outputDevicesJSON,
+ isListenOnly: this.isListenOnly,
+ },
+ }, 'Audio ended without issue');
} else if (status === FAILED) {
this.isReconnecting = false;
this.setBreakoutAudioTransferStatus({
@@ -693,7 +742,9 @@ class AudioManager {
cause: bridgeError,
bridge,
inputDeviceId: this.inputDeviceId,
+ inputDevices: this.inputDevicesJSON,
outputDeviceId: this.outputDeviceId,
+ outputDevices: this.outputDevicesJSON,
isListenOnly: this.isListenOnly,
},
},
@@ -768,18 +819,16 @@ class AudioManager {
this.setSenderTrackEnabled(!this.isMuted);
})
.catch((error) => {
- logger.error(
- {
- logCode: 'audiomanager_input_live_device_change_failure',
- extraInfo: {
- errorName: error.name,
- errorMessage: error.message,
- deviceId: currentDeviceId,
- newDeviceId: deviceId,
- },
+ logger.error({
+ logCode: 'audiomanager_input_live_device_change_failure',
+ extraInfo: {
+ errorName: error.name,
+ errorMessage: error.message,
+ deviceId: currentDeviceId,
+ newDeviceId: deviceId,
+ inputDevices: this.inputDevicesJSON,
},
- `Input device live change failed - {${error.name}: ${error.message}}`
- );
+ }, `Input device live change failed - {${error.name}: ${error.message}}`);
throw error;
});
@@ -820,18 +869,16 @@ class AudioManager {
return this.outputDeviceId;
} catch (error) {
- logger.error(
- {
- logCode: 'audiomanager_output_device_change_failure',
- extraInfo: {
- errorName: error.name,
- errorMessage: error.message,
- deviceId: currentDeviceId,
- newDeviceId: targetDeviceId,
- },
- },
- `Error changing output device - {${error.name}: ${error.message}}`
- );
+ logger.error({
+ logCode: 'audiomanager_output_device_change_failure',
+ extraInfo: {
+ errorName: error.name,
+ errorMessage: error.message,
+ deviceId: currentDeviceId,
+ newDeviceId: targetDeviceId,
+ outputDevices: this.outputDevicesJSON,
+ }
+ }, `Error changing output device - {${error.name}: ${error.message}}`);
// Rollback/enforce current sinkId (if possible)
if (sinkIdSupported) {