Merge pull request #3311 from element-hq/robin/break-safari-loop

Break loop in acquiring media on Safari
This commit is contained in:
Robin
2025-06-04 11:58:56 -04:00
committed by GitHub
3 changed files with 16 additions and 7 deletions

View File

@@ -17,9 +17,10 @@ import {
type JSX,
} from "react";
import { createMediaDeviceObserver } from "@livekit/components-core";
import { combineLatest, map, startWith } from "rxjs";
import { combineLatest, distinctUntilChanged, map, startWith } from "rxjs";
import { useObservable, useObservableEagerState } from "observable-hooks";
import { logger } from "matrix-js-sdk/lib/logger";
import { isEqual } from "lodash-es";
import {
useSetting,
@@ -140,7 +141,13 @@ function useMediaDeviceHandle(
kind,
() => logger.error("Error creating MediaDeviceObserver"),
requestPermissions,
).pipe(startWith([])),
// This Observable emits new values whenever the browser fires a
// MediaDevices 'devicechange' event. One would think, innocently, that
// a 'devicechange' event means the devices have changed. But as of the
// time of writing, we are seeing mobile Safari firing spurious
// 'devicechange' events (where no change has actually occurred) when
// we call MediaDevices.getUserMedia. So, filter by deep equality.
).pipe(startWith([]), distinctUntilChanged<MediaDeviceInfo[]>(isEqual)),
[kind, requestPermissions],
);
const available = useObservableEagerState(

View File

@@ -53,6 +53,7 @@ import {
useTrackProcessorSync,
} from "../livekit/TrackProcessorContext";
import { usePageTitle } from "../usePageTitle";
import { useLatest } from "../useLatest";
interface Props {
client: MatrixClient;
@@ -159,13 +160,14 @@ export const LobbyView: FC<Props> = ({
],
);
const latestMuteStates = useLatest(muteStates);
const onError = useCallback(
(error: Error) => {
logger.error("Error while creating preview Tracks:", error);
muteStates.audio.setEnabled?.(false);
muteStates.video.setEnabled?.(false);
latestMuteStates.current.audio.setEnabled?.(false);
latestMuteStates.current.video.setEnabled?.(false);
},
[muteStates.audio, muteStates.video],
[latestMuteStates],
);
const tracks = usePreviewTracks(localTrackOptions, onError);

View File

@@ -60,7 +60,7 @@ function useMuteState(
// Determine the default value once devices are actually connected
(prev) =>
prev ?? (device.available.size > 0 ? enabledByDefault() : undefined),
[device],
[device.available.size],
);
return useMemo(
() =>
@@ -70,7 +70,7 @@ function useMuteState(
enabled: enabled ?? false,
setEnabled: setEnabled as Dispatch<SetStateAction<boolean>>,
},
[device, enabled, setEnabled],
[device.available.size, enabled, setEnabled],
);
}