From acb024b64a46022de4c9ce14077bd26545eb72ab Mon Sep 17 00:00:00 2001 From: Emmanuel Pelletier Date: Tue, 6 Aug 2024 12:33:44 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(join)=20add=20audio=20output=20select?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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? --- src/frontend/src/features/devices/index.ts | 1 + .../features/devices/utils/useAudioOutputs.ts | 36 +++++++++++++++++++ .../features/rooms/components/Conference.tsx | 9 ++++- .../rooms/components/HomemadeJoin.tsx | 27 +++++++++++++- .../src/features/settings/stores/settings.ts | 5 +++ src/frontend/src/locales/de/rooms.json | 1 + src/frontend/src/locales/en/rooms.json | 1 + src/frontend/src/locales/fr/rooms.json | 1 + 8 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/frontend/src/features/devices/utils/useAudioOutputs.ts diff --git a/src/frontend/src/features/devices/index.ts b/src/frontend/src/features/devices/index.ts index 3a4db185..a4ea34cb 100644 --- a/src/frontend/src/features/devices/index.ts +++ b/src/frontend/src/features/devices/index.ts @@ -1 +1,2 @@ +export { useAudioOutputs } from './utils/useAudioOutputs' export { usePersistedMediaDeviceSelect } from './utils/usePersistedMediaDeviceSelect' diff --git a/src/frontend/src/features/devices/utils/useAudioOutputs.ts b/src/frontend/src/features/devices/utils/useAudioOutputs.ts new file mode 100644 index 00000000..99319669 --- /dev/null +++ b/src/frontend/src/features/devices/utils/useAudioOutputs.ts @@ -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([]) + 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 +} diff --git a/src/frontend/src/features/rooms/components/Conference.tsx b/src/frontend/src/features/rooms/components/Conference.tsx index 126aa403..1110ff0c 100644 --- a/src/frontend/src/features/rooms/components/Conference.tsx +++ b/src/frontend/src/features/rooms/components/Conference.tsx @@ -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]) diff --git a/src/frontend/src/features/rooms/components/HomemadeJoin.tsx b/src/frontend/src/features/rooms/components/HomemadeJoin.tsx index db5e0e4f..cff67843 100644 --- a/src/frontend/src/features/rooms/components/HomemadeJoin.tsx +++ b/src/frontend/src/features/rooms/components/HomemadeJoin.tsx @@ -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 = ({ )} + {/* audio output dropdown */} + + + ({ + value: d.deviceId, + label: d.label, + }))} + selectedItem={settingsSnap.devices.speakerDeviceId} + onAction={(value) => { + settingsStore.devices.speakerDeviceId = value as string + }} + /> + + {/* audio input toggle + dropdown */} ( : { username: undefined, devices: { + speakerDeviceId: undefined, micDeviceId: undefined, cameraDeviceId: undefined, enableMic: false, diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index 4bfe63d6..fe57f7aa 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -10,6 +10,7 @@ "camlabel": "", "chooseCamera": "", "chooseMic": "", + "chooseSpeaker": "", "heading": "", "joinLabel": "", "joinMeeting": "", diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index 9ca31e3e..eaa058e7 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -10,6 +10,7 @@ "camlabel": "", "chooseCamera": "Select camera", "chooseMic": "Select microphone", + "chooseSpeaker": "Select speakers", "heading": "Verify your settings", "joinLabel": "", "joinMeeting": "Join meeting", diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 785c8175..a9a9abe7 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -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",