@@ -45,7 +45,6 @@ interface DeviceButtonProps {
4545 devices : MediaDeviceInfo [ ] ;
4646 setDevice : ( device : MediaDeviceInfo ) => void ;
4747 deviceListLabel : string ;
48- fallbackDeviceLabel : ( n : number ) => string ;
4948 muted : boolean ;
5049 disabled : boolean ;
5150 toggle : ( ) => void ;
@@ -54,7 +53,7 @@ interface DeviceButtonProps {
5453}
5554
5655const DeviceButton : FC < DeviceButtonProps > = ( {
57- kind, devices, setDevice, deviceListLabel, fallbackDeviceLabel , muted, disabled, toggle, unmutedTitle, mutedTitle,
56+ kind, devices, setDevice, deviceListLabel, muted, disabled, toggle, unmutedTitle, mutedTitle,
5857} ) => {
5958 const [ showMenu , buttonRef , openMenu , closeMenu ] = useContextMenu ( ) ;
6059 const selectDevice = useCallback ( ( device : MediaDeviceInfo ) => {
@@ -67,10 +66,10 @@ const DeviceButton: FC<DeviceButtonProps> = ({
6766 const buttonRect = buttonRef . current ! . getBoundingClientRect ( ) ;
6867 contextMenu = < IconizedContextMenu { ...aboveLeftOf ( buttonRect ) } onFinished = { closeMenu } >
6968 < IconizedContextMenuOptionList >
70- { devices . map ( ( d , index ) =>
69+ { devices . map ( ( d ) =>
7170 < IconizedContextMenuOption
7271 key = { d . deviceId }
73- label = { d . label || fallbackDeviceLabel ( index + 1 ) }
72+ label = { d . label }
7473 onClick = { ( ) => selectDevice ( d ) }
7574 /> ,
7675 ) }
@@ -119,26 +118,8 @@ export const Lobby: FC<LobbyProps> = ({ room, connect, children }) => {
119118 const me = useMemo ( ( ) => room . getMember ( room . myUserId ) ! , [ room ] ) ;
120119 const videoRef = useRef < HTMLVideoElement > ( null ) ;
121120
122- const [ audioInputs , videoInputs ] = useAsyncMemo ( async ( ) => {
123- try {
124- const devices = await MediaDeviceHandler . getDevices ( ) ;
125- return [ devices [ MediaDeviceKindEnum . AudioInput ] , devices [ MediaDeviceKindEnum . VideoInput ] ] ;
126- } catch ( e ) {
127- logger . warn ( `Failed to get media device list` , e ) ;
128- return [ [ ] , [ ] ] ;
129- }
130- } , [ ] , [ [ ] , [ ] ] ) ;
131-
132121 const [ videoInputId , setVideoInputId ] = useState < string > ( ( ) => MediaDeviceHandler . getVideoInput ( ) ) ;
133122
134- const setAudioInput = useCallback ( ( device : MediaDeviceInfo ) => {
135- MediaDeviceHandler . instance . setAudioInput ( device . deviceId ) ;
136- } , [ ] ) ;
137- const setVideoInput = useCallback ( ( device : MediaDeviceInfo ) => {
138- MediaDeviceHandler . instance . setVideoInput ( device . deviceId ) ;
139- setVideoInputId ( device . deviceId ) ;
140- } , [ ] ) ;
141-
142123 const [ audioMuted , setAudioMuted ] = useState ( ( ) => MediaDeviceHandler . startWithAudioMuted ) ;
143124 const [ videoMuted , setVideoMuted ] = useState ( ( ) => MediaDeviceHandler . startWithVideoMuted ) ;
144125
@@ -151,18 +132,46 @@ export const Lobby: FC<LobbyProps> = ({ room, connect, children }) => {
151132 setVideoMuted ( ! videoMuted ) ;
152133 } , [ videoMuted , setVideoMuted ] ) ;
153134
154- const videoStream = useAsyncMemo ( async ( ) => {
155- if ( videoInputId && ! videoMuted ) {
156- try {
157- return await navigator . mediaDevices . getUserMedia ( {
158- video : { deviceId : videoInputId } ,
159- } ) ;
160- } catch ( e ) {
161- logger . error ( `Failed to get stream for device ${ videoInputId } ` , e ) ;
162- }
135+ const [ videoStream , audioInputs , videoInputs ] = useAsyncMemo ( async ( ) => {
136+ let previewStream : MediaStream ;
137+ try {
138+ // We get the preview stream before requesting devices: this is because
139+ // we need (in some browsers) an active media stream in order to get
140+ // non-blank labels for the devices. According to the docs, we
141+ // need a stream of each type (audio + video) if we want to enumerate
142+ // audio & video devices, although this didn't seem to be the case
143+ // in practice for me. We request both anyway.
144+ // For similar reasons, we also request a stream even if video is muted,
145+ // which could be a bit strange but allows us to get the device list
146+ // reliably. One option could be to try & get devices without a stream,
147+ // then try again with a stream if we get blank deviceids, but... ew.
148+ previewStream = await navigator . mediaDevices . getUserMedia ( {
149+ video : { deviceId : videoInputId } ,
150+ audio : { deviceId : MediaDeviceHandler . getAudioInput ( ) } ,
151+ } ) ;
152+ } catch ( e ) {
153+ logger . error ( `Failed to get stream for device ${ videoInputId } ` , e ) ;
154+ }
155+
156+ const devices = await MediaDeviceHandler . getDevices ( ) ;
157+
158+ // If video is muted, we don't actually want the stream, so we can get rid of
159+ // it now.
160+ if ( videoMuted ) {
161+ previewStream . getTracks ( ) . forEach ( t => t . stop ( ) ) ;
162+ previewStream = undefined ;
163163 }
164- return null ;
165- } , [ videoInputId , videoMuted ] ) ;
164+
165+ return [ previewStream , devices [ MediaDeviceKindEnum . AudioInput ] , devices [ MediaDeviceKindEnum . VideoInput ] ] ;
166+ } , [ videoInputId , videoMuted ] , [ null , [ ] , [ ] ] ) ;
167+
168+ const setAudioInput = useCallback ( ( device : MediaDeviceInfo ) => {
169+ MediaDeviceHandler . instance . setAudioInput ( device . deviceId ) ;
170+ } , [ ] ) ;
171+ const setVideoInput = useCallback ( ( device : MediaDeviceInfo ) => {
172+ MediaDeviceHandler . instance . setVideoInput ( device . deviceId ) ;
173+ setVideoInputId ( device . deviceId ) ;
174+ } , [ ] ) ;
166175
167176 useEffect ( ( ) => {
168177 if ( videoStream ) {
@@ -205,7 +214,6 @@ export const Lobby: FC<LobbyProps> = ({ room, connect, children }) => {
205214 devices = { audioInputs }
206215 setDevice = { setAudioInput }
207216 deviceListLabel = { _t ( "Audio devices" ) }
208- fallbackDeviceLabel = { n => _t ( "Audio input %(n)s" , { n } ) }
209217 muted = { audioMuted }
210218 disabled = { connecting }
211219 toggle = { toggleAudio }
@@ -217,7 +225,6 @@ export const Lobby: FC<LobbyProps> = ({ room, connect, children }) => {
217225 devices = { videoInputs }
218226 setDevice = { setVideoInput }
219227 deviceListLabel = { _t ( "Video devices" ) }
220- fallbackDeviceLabel = { n => _t ( "Video input %(n)s" , { n } ) }
221228 muted = { videoMuted }
222229 disabled = { connecting }
223230 toggle = { toggleVideo }
0 commit comments