(join) add audio output selection

new button to select audio output and pass it to the conference.

this is in its own commit because we might not want to add this directly
in the code: we can choose output in the join screen but not in the
conference screen for now. this might be a bit misleading and better to
not have it entirely for now?
This commit is contained in:
Emmanuel Pelletier
2024-08-06 13:16:44 +02:00
parent 31dcbddcf3
commit acb024b64a
8 changed files with 79 additions and 2 deletions
@@ -1 +1,2 @@
export { useAudioOutputs } from './utils/useAudioOutputs'
export { usePersistedMediaDeviceSelect } from './utils/usePersistedMediaDeviceSelect'
@@ -0,0 +1,36 @@
import { useState, useEffect } from 'react'
const getOutputDevices = () => {
return navigator.mediaDevices
.getUserMedia({ audio: true, video: false })
.then(() => navigator.mediaDevices.enumerateDevices())
.then((devices) => devices.filter(({ kind }) => kind === 'audiooutput'))
}
/**
* custom hook to fetch audio outputs
*
* this is used instead of livekit's useMediaDevices because the livekit integrated one seems to request
* outputs in a weird order, resulting in empty results in firefox if we didn't ask for input before
*/
export const useAudioOutputs = () => {
const [audioOutputs, setAudioOutputs] = useState<MediaDeviceInfo[]>([])
useEffect(() => {
const retrieveOutputDevices = () => {
getOutputDevices()
.then(setAudioOutputs)
.catch((error) => {
console.error('Audio outputs retrieval error :', error)
})
}
retrieveOutputDevices()
const onDeviceChange = () => {
retrieveOutputDevices()
}
navigator?.mediaDevices?.addEventListener('devicechange', onDeviceChange)
return () => {
navigator.mediaDevices.removeEventListener('devicechange', onDeviceChange)
}
}, [])
return audioOutputs
}
@@ -73,9 +73,16 @@ export const Conference = ({
audioCaptureDefaults: {
deviceId: userConfig.devices.micDeviceId ?? undefined,
},
audioOutput: {
deviceId: userConfig.devices.speakerDeviceId ?? undefined,
},
}
// do not rely on the userConfig object directly as its reference may change on every render
}, [userConfig.devices.cameraDeviceId, userConfig.devices.micDeviceId])
}, [
userConfig.devices.cameraDeviceId,
userConfig.devices.micDeviceId,
userConfig.devices.speakerDeviceId,
])
const room = useMemo(() => new Room(roomOptions), [roomOptions])
@@ -27,7 +27,10 @@ import { Track, LocalVideoTrack, LocalAudioTrack } from 'livekit-client'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useSnapshot } from 'valtio'
import { usePersistedMediaDeviceSelect } from '@/features/devices'
import {
usePersistedMediaDeviceSelect,
useAudioOutputs,
} from '@/features/devices'
import { settingsStore, type SettingsState } from '@/features/settings'
import { css } from '@/styled-system/css'
@@ -102,6 +105,7 @@ export const HomemadeJoin = ({
track: videoTrack,
requestPermissions: true,
})
const speakerDevices = useAudioOutputs()
useEffect(() => {
if (settingsStore.devices.micDeviceId) {
@@ -171,6 +175,27 @@ export const HomemadeJoin = ({
)}
</Center>
<HStack gap={1} justify="center" flexWrap={'wrap'}>
{/* audio output dropdown */}
<Menu>
<Button
tooltip={t('chooseSpeaker')}
aria-label={t('chooseSpeaker')}
>
<RiVolumeUpLine />
<RiArrowDropDownLine />
</Button>
<MenuList
items={speakerDevices.map((d) => ({
value: d.deviceId,
label: d.label,
}))}
selectedItem={settingsSnap.devices.speakerDeviceId}
onAction={(value) => {
settingsStore.devices.speakerDeviceId = value as string
}}
/>
</Menu>
{/* audio input toggle + dropdown */}
<HStack gap={0}>
<ToggleButton
@@ -4,6 +4,10 @@ import { devtools } from 'valtio/utils'
export type SettingsState = {
username: string | undefined
devices: {
/**
* MediaDeviceInfo id
*/
speakerDeviceId: string | undefined
/**
* MediaDeviceInfo id
*/
@@ -28,6 +32,7 @@ export const settingsStore = proxy<SettingsState>(
: {
username: undefined,
devices: {
speakerDeviceId: undefined,
micDeviceId: undefined,
cameraDeviceId: undefined,
enableMic: false,
+1
View File
@@ -10,6 +10,7 @@
"camlabel": "",
"chooseCamera": "",
"chooseMic": "",
"chooseSpeaker": "",
"heading": "",
"joinLabel": "",
"joinMeeting": "",
+1
View File
@@ -10,6 +10,7 @@
"camlabel": "",
"chooseCamera": "Select camera",
"chooseMic": "Select microphone",
"chooseSpeaker": "Select speakers",
"heading": "Verify your settings",
"joinLabel": "",
"joinMeeting": "Join meeting",
+1
View File
@@ -10,6 +10,7 @@
"camlabel": "",
"chooseCamera": "Choisir la webcam",
"chooseMic": "Choisir le micro",
"chooseSpeaker": "Choisir la sortie audio",
"heading": "Vérifiez vos paramètres",
"joinLabel": "",
"joinMeeting": "Rejoindre la réjoindre",