From e926b407b117f45842a6580e2902fab50787d2a6 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Tue, 12 Aug 2025 12:18:39 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20refactor=20previ?= =?UTF-8?q?ew=20track=20to=20avoid=20unnecessary=20resets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace usePreviewTrack approach with manual track management inspired by deprecated LiveKit implementation that only toggles what's needed instead of resetting entire preview track on config changes. Previous approach created new videoTrack when updating audio track, causing video preview to blink black during audio configuration changes. It was also acquiring video streams even when disabled. New implementation: * Initializes tracks once with user permissions for both devices * Manually handles muting/unmuting and device updates * Preserves muted state when changing device IDs * Uses exact device constraints on Chrome for proper device selection Prevents unnecessary track recreation and eliminates preview blinking. --- .../src/features/rooms/components/Join.tsx | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/features/rooms/components/Join.tsx b/src/frontend/src/features/rooms/components/Join.tsx index 3258bb85..b3b32a26 100644 --- a/src/frontend/src/features/rooms/components/Join.tsx +++ b/src/frontend/src/features/rooms/components/Join.tsx @@ -32,6 +32,7 @@ import { ToggleDevice } from './join/ToggleDevice' import { SelectDevice } from './join/SelectDevice' import { useResolveDefaultDeviceId } from '../livekit/hooks/useResolveDefaultDevice' import { isSafari } from '@/utils/livekit' +import type { LocalUserChoices } from '@/stores/userChoices' const onError = (e: Error) => console.error('ERROR', e) @@ -116,11 +117,27 @@ export const Join = ({ saveProcessorSerialized, } = usePersistentUserChoices() + const initialUserChoices = useRef(null) + + if (initialUserChoices.current === null) { + initialUserChoices.current = { + audioEnabled, + videoEnabled, + audioDeviceId, + audioOutputDeviceId, + videoDeviceId, + processorSerialized, + username, + } + } + const tracks = usePreviewTracks( { - audio: { deviceId: audioDeviceId }, - video: { - deviceId: videoDeviceId, + audio: !!initialUserChoices.current && { + deviceId: initialUserChoices.current.audioDeviceId, + }, + video: !!initialUserChoices.current && { + deviceId: initialUserChoices.current.videoDeviceId, processor: BackgroundProcessorFactory.deserializeProcessor(processorSerialized), }, @@ -599,7 +616,16 @@ export const Join = ({ { + try { + saveAudioInputDeviceId(id) + if (audioTrack) { + await audioTrack.setDeviceId({ exact: id }) + } + } catch (err) { + console.error('Failed to switch microphone device', err) + } + }} /> {!isSafari() && ( @@ -623,7 +649,16 @@ export const Join = ({ { + try { + saveVideoInputDeviceId(id) + if (videoTrack) { + await await videoTrack.setDeviceId({ exact: id }) + } + } catch (err) { + console.error('Failed to switch camera device', err) + } + }} />