bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/audio/local-echo/component.jsx
prlanzarin 9070a651ec fix(audio): local echo not tracking output device changes
Commit 325887e325 split the local echo audio
element from the main audio element to allow concurrent playback without the
risk of interfering with one another.

This introduced a regression where local echo doesn't track output device
changes. The main audio element (i.e. the meeting's audio) is not affected by
this regression.

This commit ensures local echo reacts to output device changes as needed.
2024-09-05 23:57:09 +00:00

97 lines
2.6 KiB
JavaScript

import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import Styled from './styles';
import { getSettingsSingletonInstance } from '/imports/ui/services/settings';
const propTypes = {
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
stream: PropTypes.shape({
active: PropTypes.bool,
id: PropTypes.string,
}),
initialHearingState: PropTypes.bool,
playEchoStream: PropTypes.func.isRequired,
deattachEchoStream: PropTypes.func.isRequired,
shouldUseRTCLoopback: PropTypes.func.isRequired,
createAudioRTCLoopback: PropTypes.func.isRequired,
outputDeviceId: PropTypes.string,
setAudioSink: PropTypes.func.isRequired,
};
const intlMessages = defineMessages({
stopAudioFeedbackLabel: {
id: 'app.audio.stopAudioFeedback',
description: 'Stop audio feedback button label',
},
startAudioFeedback: {
id: 'app.audio.startAudioFeedback',
description: 'Start audio feedback button label',
},
});
const LocalEcho = ({
intl,
stream = null,
initialHearingState = false,
playEchoStream,
deattachEchoStream,
shouldUseRTCLoopback,
createAudioRTCLoopback,
outputDeviceId,
setAudioSink,
}) => {
const loopbackAgent = useRef(null);
const [hearing, setHearing] = useState(initialHearingState);
const Settings = getSettingsSingletonInstance();
const { animations } = Settings.application;
const icon = hearing ? 'no_audio' : 'listen';
const label = hearing ? intlMessages.stopAudioFeedbackLabel : intlMessages.startAudioFeedback;
const applyHearingState = (_stream) => {
if (hearing) {
playEchoStream(_stream, loopbackAgent.current);
} else {
deattachEchoStream();
}
};
const cleanup = () => {
if (loopbackAgent.current) loopbackAgent.current.stop();
deattachEchoStream();
};
useEffect(() => {
if (shouldUseRTCLoopback()) {
loopbackAgent.current = createAudioRTCLoopback();
}
return cleanup;
}, []);
useEffect(() => {
applyHearingState(stream);
}, [stream, hearing]);
useEffect(() => {
if (outputDeviceId) setAudioSink(outputDeviceId);
}, [outputDeviceId]);
return (
<Styled.LocalEchoTestButton
data-test={hearing ? 'stopHearingButton' : 'testSpeakerButton'}
label={intl.formatMessage(label)}
icon={icon}
size="md"
color="primary"
onClick={() => setHearing(!hearing)}
animations={animations}
/>
);
};
LocalEcho.propTypes = propTypes;
export default injectIntl(React.memo(LocalEcho));