diff --git a/src/livekit/MediaDevicesContext.tsx b/src/livekit/MediaDevicesContext.tsx index 636f5a6d..8a19395b 100644 --- a/src/livekit/MediaDevicesContext.tsx +++ b/src/livekit/MediaDevicesContext.tsx @@ -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(isEqual)), [kind, requestPermissions], ); const available = useObservableEagerState(