Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't fall back as eagerly to unselected devices #2802

Merged
merged 4 commits into from
Nov 21, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 26 additions & 35 deletions src/livekit/MediaDevicesContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
audioInput as audioInputSetting,
audioOutput as audioOutputSetting,
videoInput as videoInputSetting,
Setting,
} from "../settings/settings";
import { isFirefox } from "../Platform";

Expand Down Expand Up @@ -58,7 +59,7 @@ function useObservableState<T>(

function useMediaDevice(
kind: MediaDeviceKind,
fallbackDevice: string | undefined,
setting: Setting<string | undefined>,
usingNames: boolean,
alwaysDefault: boolean = false,
): MediaDevice {
Expand All @@ -84,15 +85,19 @@ function useMediaDevice(
[kind, requestPermissions],
);
const available = useObservableState(deviceObserver, []);
const [selectedId, select] = useState(fallbackDevice);
const [selectedId, select] = useSetting(setting);

return useMemo(() => {
let devId;
if (available) {
devId = available.some((d) => d.deviceId === selectedId)
? selectedId
: available.some((d) => d.deviceId === fallbackDevice)
? fallbackDevice
let devId: string | undefined = undefined;
if (!alwaysDefault && available) {
// If the selected device is available, use it. Or if every available
// device ID is falsy, the browser is probably just being paranoid about
// fingerprinting and we should still try using the selected device.
// Otherwise, fall back to the first available device.
devId =
available.some((d) => d.deviceId === selectedId) ||
available.every((d) => d.deviceId === "")
? selectedId
: available.at(0)?.deviceId;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the fallback happening?
If all devices are "" we would still end up using selectedId. What if that is not available? If the browser is paranoid but also the mic has been disconnected?
Where do we catch that?


Expand All @@ -102,10 +107,10 @@ function useMediaDevice(
// device entries for the exact same device ID; deduplicate them
[...new Map(available.map((d) => [d.deviceId, d])).values()]
: [],
selectedId: alwaysDefault ? undefined : devId,
selectedId: devId,
select,
};
}, [available, selectedId, fallbackDevice, select, alwaysDefault]);
}, [available, selectedId, select, alwaysDefault]);
}

const deviceStub: MediaDevice = {
Expand Down Expand Up @@ -141,36 +146,22 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
// for ouput devices because the selector wont be shown on FF.
const useOutputNames = usingNames && !isFirefox();

const [storedAudioInput, setStoredAudioInput] = useSetting(audioInputSetting);
const [storedAudioOutput, setStoredAudioOutput] =
useSetting(audioOutputSetting);
const [storedVideoInput, setStoredVideoInput] = useSetting(videoInputSetting);

const audioInput = useMediaDevice("audioinput", storedAudioInput, usingNames);
const audioInput = useMediaDevice(
"audioinput",
audioInputSetting,
usingNames,
);
const audioOutput = useMediaDevice(
"audiooutput",
storedAudioOutput,
audioOutputSetting,
useOutputNames,
alwaysUseDefaultAudio,
);
const videoInput = useMediaDevice("videoinput", storedVideoInput, usingNames);

useEffect(() => {
if (audioInput.selectedId !== undefined)
setStoredAudioInput(audioInput.selectedId);
}, [setStoredAudioInput, audioInput.selectedId]);

useEffect(() => {
// 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())
setStoredAudioOutput(audioOutput.selectedId);
}, [setStoredAudioOutput, audioOutput.selectedId]);

useEffect(() => {
if (videoInput.selectedId !== undefined)
setStoredVideoInput(videoInput.selectedId);
}, [setStoredVideoInput, videoInput.selectedId]);
const videoInput = useMediaDevice(
"videoinput",
videoInputSetting,
usingNames,
);

const startUsingDeviceNames = useCallback(
() => setNumCallersUsingNames((n) => n + 1),
Expand Down