Use observables for more of the media devices logic

This commit is contained in:
Robin
2024-12-13 15:37:29 -05:00
parent de276b1fc3
commit f9e3fe3176

View File

@@ -16,7 +16,7 @@ import {
useState, useState,
} from "react"; } from "react";
import { createMediaDeviceObserver } from "@livekit/components-core"; import { createMediaDeviceObserver } from "@livekit/components-core";
import { startWith } from "rxjs"; import { map, startWith } from "rxjs";
import { useObservableEagerState } from "observable-hooks"; import { useObservableEagerState } from "observable-hooks";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
@@ -83,36 +83,43 @@ function useMediaDevice(
).pipe(startWith([])), ).pipe(startWith([])),
[kind, requestPermissions], [kind, requestPermissions],
); );
const availableRaw = useObservableEagerState(deviceObserver); const available = useObservableEagerState(
const available = useMemo(() => { useMemo(
// Sometimes browsers (particularly Firefox) can return multiple device () =>
// entries for the exact same device ID; using a map deduplicates them deviceObserver.pipe(
let available = new Map<string, DeviceLabel>( map((availableRaw) => {
availableRaw.map((d, i) => [ // Sometimes browsers (particularly Firefox) can return multiple device
d.deviceId, // entries for the exact same device ID; using a map deduplicates them
d.label let available = new Map<string, DeviceLabel>(
? { type: "name", name: d.label } availableRaw.map((d, i) => [
: { type: "number", number: i + 1 }, d.deviceId,
]), d.label
); ? { type: "name", name: d.label }
// Create a virtual default audio output for browsers that don't have one. : { type: "number", number: i + 1 },
// Its device ID must be the empty string because that's what setSinkId ]),
// recognizes. );
if ( // Create a virtual default audio output for browsers that don't have one.
kind === "audiooutput" && // Its device ID must be the empty string because that's what setSinkId
available.size && // recognizes.
!available.has("") && if (
!available.has("default") kind === "audiooutput" &&
) available.size &&
available = new Map([ !available.has("") &&
["", { type: "default", name: availableRaw[0]?.label || null }], !available.has("default")
...available, )
]); available = new Map([
// Note: creating virtual default input devices would be another problem ["", { type: "default", name: availableRaw[0]?.label || null }],
// entirely, because requesting a media stream from deviceId "" won't ...available,
// automatically track the default device. ]);
return available; // Note: creating virtual default input devices would be another problem
}, [kind, availableRaw]); // entirely, because requesting a media stream from deviceId "" won't
// automatically track the default device.
return available;
}),
),
[kind, deviceObserver],
),
);
const [preferredId, select] = useSetting(setting); const [preferredId, select] = useSetting(setting);
const selectedId = useMemo(() => { const selectedId = useMemo(() => {
@@ -130,9 +137,17 @@ function useMediaDevice(
} }
return undefined; return undefined;
}, [available, preferredId]); }, [available, preferredId]);
const selectedGroupId = useMemo( const selectedGroupId = useObservableEagerState(
() => availableRaw.find((d) => d.deviceId === selectedId)?.groupId, useMemo(
[availableRaw, selectedId], () =>
deviceObserver.pipe(
map(
(availableRaw) =>
availableRaw.find((d) => d.deviceId === selectedId)?.groupId,
),
),
[deviceObserver, selectedId],
),
); );
return useMemo( return useMemo(