mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-24 00:38:31 +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,
|
audioInput as audioInputSetting,
|
||||||
audioOutput as audioOutputSetting,
|
audioOutput as audioOutputSetting,
|
||||||
videoInput as videoInputSetting,
|
videoInput as videoInputSetting,
|
||||||
|
Setting,
|
||||||
} from "../settings/settings";
|
} from "../settings/settings";
|
||||||
import { isFirefox } from "../Platform";
|
import { isFirefox } from "../Platform";
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ function useObservableState<T>(
|
|||||||
|
|
||||||
function useMediaDevice(
|
function useMediaDevice(
|
||||||
kind: MediaDeviceKind,
|
kind: MediaDeviceKind,
|
||||||
fallbackDevice: string | undefined,
|
setting: Setting<string | undefined>,
|
||||||
usingNames: boolean,
|
usingNames: boolean,
|
||||||
alwaysDefault: boolean = false,
|
alwaysDefault: boolean = false,
|
||||||
): MediaDevice {
|
): MediaDevice {
|
||||||
@ -84,15 +85,21 @@ function useMediaDevice(
|
|||||||
[kind, requestPermissions],
|
[kind, requestPermissions],
|
||||||
);
|
);
|
||||||
const available = useObservableState(deviceObserver, []);
|
const available = useObservableState(deviceObserver, []);
|
||||||
const [selectedId, select] = useState(fallbackDevice);
|
const [preferredId, select] = useSetting(setting);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
let devId;
|
let selectedId: string | undefined = undefined;
|
||||||
if (available) {
|
if (!alwaysDefault && available) {
|
||||||
devId = available.some((d) => d.deviceId === selectedId)
|
// If the preferred device is available, use it. Or if every available
|
||||||
? selectedId
|
// device ID is falsy, the browser is probably just being paranoid about
|
||||||
: available.some((d) => d.deviceId === fallbackDevice)
|
// fingerprinting and we should still try using the preferred device.
|
||||||
? fallbackDevice
|
// 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;
|
: available.at(0)?.deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,10 +109,10 @@ function useMediaDevice(
|
|||||||
// device entries for the exact same device ID; deduplicate them
|
// device entries for the exact same device ID; deduplicate them
|
||||||
[...new Map(available.map((d) => [d.deviceId, d])).values()]
|
[...new Map(available.map((d) => [d.deviceId, d])).values()]
|
||||||
: [],
|
: [],
|
||||||
selectedId: alwaysDefault ? undefined : devId,
|
selectedId,
|
||||||
select,
|
select,
|
||||||
};
|
};
|
||||||
}, [available, selectedId, fallbackDevice, select, alwaysDefault]);
|
}, [available, preferredId, select, alwaysDefault]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deviceStub: MediaDevice = {
|
const deviceStub: MediaDevice = {
|
||||||
@ -141,36 +148,22 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
|||||||
// for ouput devices because the selector wont be shown on FF.
|
// for ouput devices because the selector wont be shown on FF.
|
||||||
const useOutputNames = usingNames && !isFirefox();
|
const useOutputNames = usingNames && !isFirefox();
|
||||||
|
|
||||||
const [storedAudioInput, setStoredAudioInput] = useSetting(audioInputSetting);
|
const audioInput = useMediaDevice(
|
||||||
const [storedAudioOutput, setStoredAudioOutput] =
|
"audioinput",
|
||||||
useSetting(audioOutputSetting);
|
audioInputSetting,
|
||||||
const [storedVideoInput, setStoredVideoInput] = useSetting(videoInputSetting);
|
usingNames,
|
||||||
|
);
|
||||||
const audioInput = useMediaDevice("audioinput", storedAudioInput, usingNames);
|
|
||||||
const audioOutput = useMediaDevice(
|
const audioOutput = useMediaDevice(
|
||||||
"audiooutput",
|
"audiooutput",
|
||||||
storedAudioOutput,
|
audioOutputSetting,
|
||||||
useOutputNames,
|
useOutputNames,
|
||||||
alwaysUseDefaultAudio,
|
alwaysUseDefaultAudio,
|
||||||
);
|
);
|
||||||
const videoInput = useMediaDevice("videoinput", storedVideoInput, usingNames);
|
const videoInput = useMediaDevice(
|
||||||
|
"videoinput",
|
||||||
useEffect(() => {
|
videoInputSetting,
|
||||||
if (audioInput.selectedId !== undefined)
|
usingNames,
|
||||||
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 startUsingDeviceNames = useCallback(
|
const startUsingDeviceNames = useCallback(
|
||||||
() => setNumCallersUsingNames((n) => n + 1),
|
() => setNumCallersUsingNames((n) => n + 1),
|
||||||
|
@ -20,7 +20,6 @@ import {
|
|||||||
TrackEvent,
|
TrackEvent,
|
||||||
} from "livekit-client";
|
} from "livekit-client";
|
||||||
import { useObservable, useObservableEagerState } from "observable-hooks";
|
import { useObservable, useObservableEagerState } from "observable-hooks";
|
||||||
import { useEffect } from "react";
|
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import { useMediaDevices } from "../livekit/MediaDevicesContext";
|
import { useMediaDevices } from "../livekit/MediaDevicesContext";
|
||||||
@ -35,6 +34,7 @@ export function useSwitchCamera(
|
|||||||
video: Observable<LocalVideoTrack | null>,
|
video: Observable<LocalVideoTrack | null>,
|
||||||
): (() => void) | null {
|
): (() => void) | null {
|
||||||
const mediaDevices = useMediaDevices();
|
const mediaDevices = useMediaDevices();
|
||||||
|
const setVideoInput = useLatest(mediaDevices.videoInput.select);
|
||||||
|
|
||||||
// Produce an observable like the input 'video' observable, except make it
|
// Produce an observable like the input 'video' observable, except make it
|
||||||
// emit whenever the track is muted or the device changes
|
// emit whenever the track is muted or the device changes
|
||||||
@ -75,6 +75,12 @@ export function useSwitchCamera(
|
|||||||
.restartTrack({
|
.restartTrack({
|
||||||
facingMode: facingMode === "user" ? "environment" : "user",
|
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) =>
|
.catch((e) =>
|
||||||
logger.error("Failed to switch camera", facingMode, e),
|
logger.error("Failed to switch camera", facingMode, e),
|
||||||
);
|
);
|
||||||
@ -83,16 +89,5 @@ export function useSwitchCamera(
|
|||||||
[videoTrack],
|
[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);
|
return useObservableEagerState(switchCamera);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user