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

Open
wants to merge 1 commit into
base: livekit
Choose a base branch
from
Open
Changes from all commits
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;
}

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