From 8f424452fc89559cc87cec556da452a76fb2220b Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Fri, 23 May 2025 17:54:47 +0200 Subject: [PATCH] Disable device switching when in controlled audio devices mode (#3290) * Disable device switching when in controlled audio devices mode * Temporarily switch matrix-js-sdk to robin/embedded-no-update-state To allow us to test this change on Element X, which does not yet support the update_state action. * Also add a check for controlled audio devices in useAudioContext * use develop branch * fix tests --------- Co-authored-by: Robin --- src/livekit/useLivekit.ts | 11 +++++++++-- src/useAudioContext.test.tsx | 18 +++++++++++++----- src/useAudioContext.tsx | 11 ++++++++--- yarn.lock | 4 ++-- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/livekit/useLivekit.ts b/src/livekit/useLivekit.ts index 53f366d2..9e8f16a9 100644 --- a/src/livekit/useLivekit.ts +++ b/src/livekit/useLivekit.ts @@ -42,6 +42,7 @@ import { } from "./TrackProcessorContext"; import { useInitial } from "../useInitial"; import { observeTrackReference$ } from "../state/MediaViewModel"; +import { useUrlParams } from "../UrlParams"; interface UseLivekitResult { livekitRoom?: Room; @@ -54,6 +55,8 @@ export function useLivekit( sfuConfig: SFUConfig | undefined, e2eeSystem: EncryptionSystem, ): UseLivekitResult { + const { controlledAudioDevices } = useUrlParams(); + const e2eeOptions = useMemo((): E2EEManagerOptions | undefined => { if (e2eeSystem.kind === E2eeType.NONE) return undefined; @@ -303,7 +306,11 @@ export function useLivekit( useEffect(() => { // Sync the requested devices with LiveKit's devices - if (room !== undefined && connectionState === ConnectionState.Connected) { + if ( + room !== undefined && + connectionState === ConnectionState.Connected && + !controlledAudioDevices + ) { const syncDevice = ( kind: MediaDeviceKind, device: MediaDeviceHandle, @@ -363,7 +370,7 @@ export function useLivekit( syncDevice("audiooutput", devices.audioOutput); syncDevice("videoinput", devices.videoInput); } - }, [room, devices, connectionState]); + }, [room, devices, connectionState, controlledAudioDevices]); return { connState: connectionState, diff --git a/src/useAudioContext.test.tsx b/src/useAudioContext.test.tsx index f2e2efdb..814df8e9 100644 --- a/src/useAudioContext.test.tsx +++ b/src/useAudioContext.test.tsx @@ -9,6 +9,7 @@ import { expect, vi, afterEach, beforeEach, test } from "vitest"; import { type FC } from "react"; import { render } from "@testing-library/react"; import userEvent, { type UserEvent } from "@testing-library/user-event"; +import { BrowserRouter } from "react-router-dom"; import { deviceStub, MediaDevicesContext } from "./livekit/MediaDevicesContext"; import { useAudioContext } from "./useAudioContext"; @@ -38,6 +39,13 @@ const TestComponent: FC = () => { ); }; +const TestComponentWrapper: FC = () => { + return ( + + + + ); +}; const gainNode = vi.mocked( { @@ -94,13 +102,13 @@ afterEach(() => { }); test("can play a single sound", async () => { - const { findByText } = render(); + const { findByText } = render(); await user.click(await findByText("Valid sound")); expect(testAudioContext.createBufferSource).toHaveBeenCalledOnce(); }); test("will ignore sounds that are not registered", async () => { - const { findByText } = render(); + const { findByText } = render(); await user.click(await findByText("Invalid sound")); expect(testAudioContext.createBufferSource).not.toHaveBeenCalled(); }); @@ -122,7 +130,7 @@ test("will use the correct device", () => { stopUsingDeviceNames: () => {}, }} > - + , ); expect(testAudioContext.createBufferSource).not.toHaveBeenCalled(); @@ -131,7 +139,7 @@ test("will use the correct device", () => { test("will use the correct volume level", async () => { soundEffectVolumeSetting.setValue(0.33); - const { findByText } = render(); + const { findByText } = render(); await user.click(await findByText("Valid sound")); expect(testAudioContext.gain.gain.setValueAtTime).toHaveBeenCalledWith( 0.33, @@ -157,7 +165,7 @@ test("will use the pan if earpiece is selected", async () => { stopUsingDeviceNames: () => {}, }} > - + , ); await user.click(await findByText("Valid sound")); diff --git a/src/useAudioContext.tsx b/src/useAudioContext.tsx index 5a689fdf..23df0dbe 100644 --- a/src/useAudioContext.tsx +++ b/src/useAudioContext.tsx @@ -17,6 +17,7 @@ import { useMediaDevices, } from "./livekit/MediaDevicesContext"; import { type PrefetchedSounds } from "./soundUtils"; +import { useUrlParams } from "./UrlParams"; /** * Play a sound though a given AudioContext. Will take @@ -71,7 +72,7 @@ export function useAudioContext( ): UseAudioContext | null { const [soundEffectVolume] = useSetting(soundEffectVolumeSetting); const { audioOutput } = useMediaDevices(); - + const { controlledAudioDevices } = useUrlParams(); const [audioContext, setAudioContext] = useState(); const [audioBuffers, setAudioBuffers] = useState>(); @@ -110,14 +111,18 @@ export function useAudioContext( // Update the sink ID whenever we change devices. useEffect(() => { - if (audioContext && "setSinkId" in audioContext) { + if ( + audioContext && + "setSinkId" in audioContext && + !controlledAudioDevices + ) { // https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/setSinkId // @ts-expect-error - setSinkId doesn't exist yet in types, maybe because it's not supported everywhere. audioContext.setSinkId(audioOutput.selectedId).catch((ex) => { logger.warn("Unable to change sink for audio context", ex); }); } - }, [audioContext, audioOutput.selectedId]); + }, [audioContext, audioOutput.selectedId, controlledAudioDevices]); const { pan: earpiecePan, volume: earpieceVolume } = useEarpieceAudioConfig(); // Don't return a function until we're ready. diff --git a/yarn.lock b/yarn.lock index ea7324c3..49068506 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9612,7 +9612,7 @@ __metadata: "matrix-js-sdk@github:matrix-org/matrix-js-sdk#head=develop": version: 37.6.0 - resolution: "matrix-js-sdk@https://github.com/matrix-org/matrix-js-sdk.git#commit=93982716951ce2583904bfc26b27d6a86ba17a87" + resolution: "matrix-js-sdk@https://github.com/matrix-org/matrix-js-sdk.git#commit=bf6dc16ad32d47f2c6e167236f4c853ceef01d4f" dependencies: "@babel/runtime": "npm:^7.12.5" "@matrix-org/matrix-sdk-crypto-wasm": "npm:^14.2.0" @@ -9629,7 +9629,7 @@ __metadata: sdp-transform: "npm:^2.14.1" unhomoglyph: "npm:^1.0.6" uuid: "npm:11" - checksum: 10c0/22a28099d2deaf0ca7f609a5859fe00fbd20c314d3b607a95b4a623a8aa159e428310b1849c77ab7b9875a29fc2d924cddbb6fc83dc4e42a74c4713401dcb0be + checksum: 10c0/2877e7c5b2779200b48f3152bb7b510e58899b1e779e7e6d2bc1a236ed178fa8858f0547b644a2029f48f55dc7cc3954f48e2598c8963d45293c8280ccc23039 languageName: node linkType: hard