Firefox audio output issues fix (#1510)

---------

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo 2023-09-19 15:10:14 +02:00 committed by GitHub
parent c2bc4651cf
commit 4aec5c34f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 18 deletions

View File

@ -28,6 +28,7 @@ import { createMediaDeviceObserver } from "@livekit/components-core";
import { Observable } from "rxjs";
import {
isFirefox,
useAudioInput,
useAudioOutput,
useVideoInput,
@ -65,7 +66,8 @@ function useObservableState<T>(
function useMediaDevice(
kind: MediaDeviceKind,
fallbackDevice: string | undefined,
usingNames: boolean
usingNames: boolean,
alwaysDefault: boolean = false
): MediaDevice {
// Make sure we don't needlessly reset to a device observer without names,
// once permissions are already given
@ -86,18 +88,19 @@ function useMediaDevice(
const available = useObservableState(deviceObserver, []);
const [selectedId, select] = useState(fallbackDevice);
return useMemo(
() => ({
return useMemo(() => {
const devId = available.some((d) => d.deviceId === selectedId)
? selectedId
: available.some((d) => d.deviceId === fallbackDevice)
? fallbackDevice
: available.at(0)?.deviceId;
return {
available,
selectedId: available.some((d) => d.deviceId === selectedId)
? selectedId
: available.some((d) => d.deviceId === fallbackDevice)
? fallbackDevice
: available.at(0)?.deviceId,
selectedId: alwaysDefault ? undefined : devId,
select,
}),
[available, selectedId, fallbackDevice, select]
);
};
}, [available, selectedId, fallbackDevice, select, alwaysDefault]);
}
const deviceStub: MediaDevice = {
@ -120,10 +123,17 @@ interface Props {
}
export const MediaDevicesProvider: FC<Props> = ({ children }) => {
// Counts the number of callers currently using device names
// Counts the number of callers currently using device names.
const [numCallersUsingNames, setNumCallersUsingNames] = useState(0);
const usingNames = numCallersUsingNames > 0;
// Use output device names for output devices on all platforms except FF.
const useOutputNames = usingNames && !isFirefox();
// Setting the audio device to sth. else than 'undefined' breaks echo-cancellation
// and even can introduce multiple different output devices for one call.
const alwaysUseDefaultAudio = isFirefox();
const [audioInputSetting, setAudioInputSetting] = useAudioInput();
const [audioOutputSetting, setAudioOutputSetting] = useAudioOutput();
const [videoInputSetting, setVideoInputSetting] = useVideoInput();
@ -136,7 +146,8 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
const audioOutput = useMediaDevice(
"audiooutput",
audioOutputSetting,
usingNames
useOutputNames,
alwaysUseDefaultAudio
);
const videoInput = useMediaDevice(
"videoinput",
@ -150,7 +161,9 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
}, [setAudioInputSetting, audioInput.selectedId]);
useEffect(() => {
if (audioOutput.selectedId !== undefined)
// Skip setting state for ff output. Redundent since it is set to always return 'undefined'
// but makes it clear while debugging that this is not happening on FF. + perf ;)
if (audioOutput.selectedId !== undefined && !isFirefox())
setAudioOutputSetting(audioOutput.selectedId);
}, [setAudioOutputSetting, audioOutput.selectedId]);

View File

@ -224,7 +224,8 @@ export function GroupCallView({
leaveRTCSession(rtcSession);
if (widget) {
// we need to wait until the callEnded event is tracked. Otherwise the iFrame gets killed before the callEnded event got tracked.
// we need to wait until the callEnded event is tracked on posthog.
// Otherwise the iFrame gets killed before the callEnded event got tracked.
await new Promise((resolve) => window.setTimeout(resolve, 10)); // 10ms
widget.api.setAlwaysOnScreen(false);
PosthogAnalytics.instance.logout();

View File

@ -35,6 +35,7 @@ import {
useDeveloperSettingsTab,
useShowConnectionStats,
useEnableE2EE,
isFirefox,
} from "./useSetting";
import { FieldRow, InputField } from "../input/Input";
import { Button } from "../button";
@ -130,7 +131,8 @@ export const SettingsModal = (props: Props) => {
}
>
{generateDeviceSelection(devices.audioInput, t("Microphone"))}
{generateDeviceSelection(devices.audioOutput, t("Speaker"))}
{!isFirefox() &&
generateDeviceSelection(devices.audioOutput, t("Speaker"))}
</TabItem>
);

View File

@ -58,8 +58,12 @@ export const getSetting = <T>(name: string, defaultValue: T): T => {
export const setSetting = <T>(name: string, newValue: T) =>
setLocalStorageItem(getSettingKey(name), JSON.stringify(newValue));
const canEnableSpatialAudio = () => {
export const isFirefox = () => {
const { userAgent } = navigator;
return userAgent.includes("Firefox");
};
const canEnableSpatialAudio = () => {
// Spatial audio means routing audio through audio contexts. On Chrome,
// this bypasses the AEC processor and so breaks echo cancellation.
// We only allow spatial audio to be enabled on Firefox which we know
@ -69,7 +73,7 @@ const canEnableSpatialAudio = () => {
// widely enough, we can allow spatial audio everywhere. It's currently in a
// chrome flag, so we could enable this in Electron if we enabled the chrome flag
// in the Electron wrapper.
return userAgent.includes("Firefox");
return isFirefox();
};
export const useSpatialAudio = (): DisableableSetting<boolean> => {