♻️(frontend) refactor preview track to avoid unnecessary resets

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.
This commit is contained in:
lebaudantoine
2025-08-12 12:18:39 +02:00
committed by aleb_the_flash
parent 4e655a0a64
commit e926b407b1

View File

@@ -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<LocalUserChoices | null>(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 = ({
<SelectDevice
kind="audioinput"
id={audioDeviceId}
onSubmit={saveAudioInputDeviceId}
onSubmit={async (id) => {
try {
saveAudioInputDeviceId(id)
if (audioTrack) {
await audioTrack.setDeviceId({ exact: id })
}
} catch (err) {
console.error('Failed to switch microphone device', err)
}
}}
/>
</div>
{!isSafari() && (
@@ -623,7 +649,16 @@ export const Join = ({
<SelectDevice
kind="videoinput"
id={videoDeviceId}
onSubmit={saveVideoInputDeviceId}
onSubmit={async (id) => {
try {
saveVideoInputDeviceId(id)
if (videoTrack) {
await await videoTrack.setDeviceId({ exact: id })
}
} catch (err) {
console.error('Failed to switch camera device', err)
}
}}
/>
</div>
</div>