From 610e7923945dd2e1dd8475b2e6df9fbe5187d118 Mon Sep 17 00:00:00 2001 From: Timo Date: Thu, 15 May 2025 17:20:12 +0200 Subject: [PATCH] rename setOutputDevices-> setAvailableOutputDevices --- docs/controls.md | 3 ++- src/controls.ts | 17 ++++++++++++----- src/livekit/MediaDevicesContext.tsx | 23 +++++++++++++++++------ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/docs/controls.md b/docs/controls.md index 2bc17b24..4405a9f3 100644 --- a/docs/controls.md +++ b/docs/controls.md @@ -12,7 +12,8 @@ A few aspects of Element Call's interface can be controlled through a global API These functions must be used in conjunction with the `controlledOutput` URL parameter in order to have any effect. -- `controls.setOutputDevices(devices: { id: string, name: string, forEarpiece?: boolean }[]): void` Sets the list of available audio outputs. `forEarpiece` is used on ios only. +- `controls.setAvailableOutputDevices(devices: { id: string, name: string, forEarpiece?: boolean }[]): void` Sets the list of available audio outputs. `forEarpiece` is used on ios only. It flags the device that should be used if the user selects earpice mode. This should be the main (stereo loudspeaker) of the device. - `controls.onOutputDeviceSelect: ((id: string) => void) | undefined` Callback called whenever the user or application selects a new audio output. +- `controls.setOutputDevice(id: string): void` Sets the selected audio device in EC menu. This should be used if the os decides to automatically switch to bluetooth. - `controls.setOutputEnabled(enabled: boolean)` Enables/disables all audio output from the application. This can be useful for temporarily pausing audio while the controlling application is switching output devices. Output is enabled by default. diff --git a/src/controls.ts b/src/controls.ts index 72f80260..2bf9933d 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -11,7 +11,8 @@ export interface Controls { canEnterPip(): boolean; enablePip(): void; disablePip(): void; - setOutputDevices(devices: OutputDevice[]): void; + setAvailableOutputDevices(devices: OutputDevice[]): void; + setOutputDevice(id: string): void; onOutputDeviceSelect?: (id: string) => void; setOutputEnabled(enabled: boolean): void; } @@ -23,7 +24,8 @@ export interface OutputDevice { } export const setPipEnabled$ = new Subject(); -export const setOutputDevices$ = new Subject(); +export const setAvailableOutputDevices$ = new Subject(); +export const setOutputDevice$ = new Subject(); export const setOutputEnabled$ = new Subject(); window.controls = { @@ -38,10 +40,15 @@ window.controls = { if (!setPipEnabled$.observed) throw new Error("No call is running"); setPipEnabled$.next(false); }, - setOutputDevices(devices: OutputDevice[]): void { - if (!setOutputDevices$.observed) + setAvailableOutputDevices(devices: OutputDevice[]): void { + if (!setAvailableOutputDevices$.observed) throw new Error("Output controls are disabled"); - setOutputDevices$.next(devices); + setAvailableOutputDevices$.next(devices); + }, + setOutputDevice(id: string): void { + if (!setOutputDevice$.observed) + throw new Error("Output controls are disabled"); + setOutputDevice$.next(id); }, setOutputEnabled(enabled: boolean): void { if (!setOutputEnabled$.observed) diff --git a/src/livekit/MediaDevicesContext.tsx b/src/livekit/MediaDevicesContext.tsx index 3a3dd081..319f492c 100644 --- a/src/livekit/MediaDevicesContext.tsx +++ b/src/livekit/MediaDevicesContext.tsx @@ -29,7 +29,11 @@ import { alwaysShowIphoneEarpiece as alwaysShowIphoneEarpieceSetting, type Setting, } from "../settings/settings"; -import { type OutputDevice, setOutputDevices$ } from "../controls"; +import { + type OutputDevice, + setAvailableOutputDevices$, + setOutputDevice$, +} from "../controls"; import { useUrlParams } from "../UrlParams"; export const EARPIECE_CONFIG_ID = "earpiece-id"; @@ -299,7 +303,7 @@ function useControlledOutput(): MediaDeviceHandle { startWith(alwaysShowIphoneEarpieceSetting.getValue()), map((v) => v || navigator.userAgent.includes("iPhone")), ); - const outputDeviceData$ = setOutputDevices$.pipe( + const outputDeviceData$ = setAvailableOutputDevices$.pipe( startWith([]), map((devices) => { const physicalDeviceForEarpiceMode = devices.find( @@ -316,15 +320,22 @@ function useControlledOutput(): MediaDeviceHandle { return combineLatest([outputDeviceData$, showEarpice$]).pipe( map(([{ devicesMap, physicalDeviceForEarpiceMode }, showEarpiece]) => { - if (showEarpiece && !!physicalDeviceForEarpiceMode) - devicesMap.set(EARPIECE_CONFIG_ID, { type: "earpiece" }); - return { available: devicesMap, physicalDeviceForEarpiceMode }; + let available = devicesMap; + if (showEarpiece && !!physicalDeviceForEarpiceMode) { + available = new Map([ + ...devicesMap.entries(), + [EARPIECE_CONFIG_ID, { type: "earpiece" }], + ]); + } + return { available, physicalDeviceForEarpiceMode }; }), ); }), ); - const [preferredId, setPreferredId] = useSetting(audioOutputSetting); + useEffect(() => { + setOutputDevice$.subscribe((id) => setPreferredId(id)); + }, [setPreferredId]); const selectedId = useMemo(() => { if (available.size) {