mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-30 00:50:48 +08:00
Merge pull request #2802 from robintown/device-fall-back
Don't fall back as eagerly to unselected devices
This commit is contained in:
commit
720c400e5f
@ -24,6 +24,7 @@ import {
|
||||
audioInput as audioInputSetting,
|
||||
audioOutput as audioOutputSetting,
|
||||
videoInput as videoInputSetting,
|
||||
Setting,
|
||||
} from "../settings/settings";
|
||||
import { isFirefox } from "../Platform";
|
||||
|
||||
@ -58,7 +59,7 @@ function useObservableState<T>(
|
||||
|
||||
function useMediaDevice(
|
||||
kind: MediaDeviceKind,
|
||||
fallbackDevice: string | undefined,
|
||||
setting: Setting<string | undefined>,
|
||||
usingNames: boolean,
|
||||
alwaysDefault: boolean = false,
|
||||
): MediaDevice {
|
||||
@ -84,15 +85,21 @@ function useMediaDevice(
|
||||
[kind, requestPermissions],
|
||||
);
|
||||
const available = useObservableState(deviceObserver, []);
|
||||
const [selectedId, select] = useState(fallbackDevice);
|
||||
const [preferredId, 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 selectedId: string | undefined = undefined;
|
||||
if (!alwaysDefault && available) {
|
||||
// If the preferred 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 preferred device.
|
||||
// Worst case it is not available and the browser will gracefully fall
|
||||
// back to some other device for us when requesting the media stream.
|
||||
// Otherwise, select the first available device.
|
||||
selectedId =
|
||||
available.some((d) => d.deviceId === preferredId) ||
|
||||
available.every((d) => d.deviceId === "")
|
||||
? preferredId
|
||||
: available.at(0)?.deviceId;
|
||||
}
|
||||
|
||||
@ -102,10 +109,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,
|
||||
select,
|
||||
};
|
||||
}, [available, selectedId, fallbackDevice, select, alwaysDefault]);
|
||||
}, [available, preferredId, select, alwaysDefault]);
|
||||
}
|
||||
|
||||
const deviceStub: MediaDevice = {
|
||||
@ -141,36 +148,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),
|
||||
|
@ -20,7 +20,6 @@ import {
|
||||
TrackEvent,
|
||||
} from "livekit-client";
|
||||
import { useObservable, useObservableEagerState } from "observable-hooks";
|
||||
import { useEffect } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { useMediaDevices } from "../livekit/MediaDevicesContext";
|
||||
@ -35,6 +34,7 @@ export function useSwitchCamera(
|
||||
video: Observable<LocalVideoTrack | null>,
|
||||
): (() => void) | null {
|
||||
const mediaDevices = useMediaDevices();
|
||||
const setVideoInput = useLatest(mediaDevices.videoInput.select);
|
||||
|
||||
// Produce an observable like the input 'video' observable, except make it
|
||||
// emit whenever the track is muted or the device changes
|
||||
@ -75,6 +75,12 @@ export function useSwitchCamera(
|
||||
.restartTrack({
|
||||
facingMode: facingMode === "user" ? "environment" : "user",
|
||||
})
|
||||
.then(() => {
|
||||
// Inform the MediaDeviceContext which camera was chosen
|
||||
const deviceId =
|
||||
track.mediaStreamTrack.getSettings().deviceId;
|
||||
if (deviceId !== undefined) setVideoInput.current(deviceId);
|
||||
})
|
||||
.catch((e) =>
|
||||
logger.error("Failed to switch camera", facingMode, e),
|
||||
);
|
||||
@ -83,16 +89,5 @@ export function useSwitchCamera(
|
||||
[videoTrack],
|
||||
);
|
||||
|
||||
const setVideoInput = useLatest(mediaDevices.videoInput.select);
|
||||
useEffect(() => {
|
||||
// Watch for device changes due to switching the camera and feed them back
|
||||
// into the MediaDeviceContext
|
||||
const subscription = videoTrack.subscribe((track) => {
|
||||
const deviceId = track?.mediaStreamTrack.getSettings().deviceId;
|
||||
if (deviceId !== undefined) setVideoInput.current(deviceId);
|
||||
});
|
||||
return (): void => subscription.unsubscribe();
|
||||
}, [videoTrack, setVideoInput]);
|
||||
|
||||
return useObservableEagerState(switchCamera);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user