feat(room): выбор устройства аудиовывода в настройках

Третий селект в DeviceSettingsDialog (динамики/наушники/bluetooth) рядом
с уже существующими микрофоном и камерой — на useMediaDeviceSelect(kind:
'audiooutput'). Выбор персистится отдельно от usePersistentUserChoices
(LocalUserChoices LiveKit про аудиовыход не знает) и применяется заново
при следующем подключении через RoomOptions.audioOutput.

Поддержка определяется по наличию setSinkId в рантайме, не по User-Agent:
там, где браузер не умеет переключать вывод (iOS Safari), список скрыт,
вместо него — подсказка, что маршрутизация звука на стороне системы.
This commit is contained in:
2026-07-27 17:31:35 +03:00
parent 35a1455645
commit e07217657f
4 changed files with 94 additions and 4 deletions

View File

@@ -0,0 +1,33 @@
const STORAGE_KEY = 'vidconf-audio-output-device'
/**
* Поддержка выбора устройства аудиовывода — определяем по наличию
* `setSinkId` в рантайме, НЕ по User-Agent: в iOS Safari метода нет вообще
* (маршрутизация звука там целиком на стороне ОС), в Android Chrome есть.
*/
export function isAudioOutputSelectable(): boolean {
return typeof HTMLMediaElement !== 'undefined' && 'setSinkId' in HTMLMediaElement.prototype
}
/**
* Персист выбранного устройства вывода — отдельно от `usePersistentUserChoices`
* LiveKit: их `LocalUserChoices` знает только про вход (микрофон/камера),
* поля для аудиовыхода там нет.
*/
export function loadAudioOutputDeviceId(): string {
try {
return localStorage.getItem(STORAGE_KEY) ?? ''
} catch {
// localStorage недоступен (приватный режим/политики браузера) — без
// сохранённого выбора, устройство по умолчанию.
return ''
}
}
export function saveAudioOutputDeviceId(deviceId: string): void {
try {
localStorage.setItem(STORAGE_KEY, deviceId)
} catch {
// Сохранение недоступно — выбор продержится до конца сессии в комнате.
}
}