Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit daf097e

Browse files
authored
Fix joining calls without audio or video inputs (#9486)
The lobby view was requesting a stream with both video and audio, even if the system lacked video or audio devices. Requesting one of audio or video is enough to get all device labels.
1 parent eafc2d2 commit daf097e

File tree

1 file changed

+26
-24
lines changed

1 file changed

+26
-24
lines changed

src/components/views/voip/CallView.tsx

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
3333
import AppTile from "../elements/AppTile";
3434
import { _t } from "../../../languageHandler";
3535
import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
36-
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../MediaDeviceHandler";
36+
import MediaDeviceHandler from "../../../MediaDeviceHandler";
3737
import { CallStore } from "../../../stores/CallStore";
3838
import IconizedContextMenu, {
3939
IconizedContextMenuOption,
@@ -141,36 +141,38 @@ export const Lobby: FC<LobbyProps> = ({ room, joinCallButtonDisabled, joinCallBu
141141
}, [videoMuted, setVideoMuted]);
142142

143143
const [videoStream, audioInputs, videoInputs] = useAsyncMemo(async () => {
144-
let previewStream: MediaStream;
144+
let devices = await MediaDeviceHandler.getDevices();
145+
146+
// We get the preview stream before requesting devices: this is because
147+
// we need (in some browsers) an active media stream in order to get
148+
// non-blank labels for the devices.
149+
let stream: MediaStream | null = null;
145150
try {
146-
// We get the preview stream before requesting devices: this is because
147-
// we need (in some browsers) an active media stream in order to get
148-
// non-blank labels for the devices. According to the docs, we
149-
// need a stream of each type (audio + video) if we want to enumerate
150-
// audio & video devices, although this didn't seem to be the case
151-
// in practice for me. We request both anyway.
152-
// For similar reasons, we also request a stream even if video is muted,
153-
// which could be a bit strange but allows us to get the device list
154-
// reliably. One option could be to try & get devices without a stream,
155-
// then try again with a stream if we get blank deviceids, but... ew.
156-
previewStream = await navigator.mediaDevices.getUserMedia({
157-
video: { deviceId: videoInputId },
158-
audio: { deviceId: MediaDeviceHandler.getAudioInput() },
159-
});
151+
if (devices.audioinput.length > 0) {
152+
// Holding just an audio stream will be enough to get us all device labels, so
153+
// if video is muted, don't bother requesting video.
154+
stream = await navigator.mediaDevices.getUserMedia({
155+
audio: true,
156+
video: !videoMuted && devices.videoinput.length > 0 && { deviceId: videoInputId },
157+
});
158+
} else if (devices.videoinput.length > 0) {
159+
// We have to resort to a video stream, even if video is supposed to be muted.
160+
stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: videoInputId } });
161+
}
160162
} catch (e) {
161163
logger.error(`Failed to get stream for device ${videoInputId}`, e);
162164
}
163165

164-
const devices = await MediaDeviceHandler.getDevices();
166+
// Refresh the devices now that we hold a stream
167+
if (stream !== null) devices = await MediaDeviceHandler.getDevices();
165168

166-
// If video is muted, we don't actually want the stream, so we can get rid of
167-
// it now.
169+
// If video is muted, we don't actually want the stream, so we can get rid of it now.
168170
if (videoMuted) {
169-
previewStream.getTracks().forEach(t => t.stop());
170-
previewStream = undefined;
171+
stream?.getTracks().forEach(t => t.stop());
172+
stream = null;
171173
}
172174

173-
return [previewStream, devices[MediaDeviceKindEnum.AudioInput], devices[MediaDeviceKindEnum.VideoInput]];
175+
return [stream, devices.audioinput, devices.videoinput];
174176
}, [videoInputId, videoMuted], [null, [], []]);
175177

176178
const setAudioInput = useCallback((device: MediaDeviceInfo) => {
@@ -188,7 +190,7 @@ export const Lobby: FC<LobbyProps> = ({ room, joinCallButtonDisabled, joinCallBu
188190
videoElement.play();
189191

190192
return () => {
191-
videoStream?.getTracks().forEach(track => track.stop());
193+
videoStream.getTracks().forEach(track => track.stop());
192194
videoElement.srcObject = null;
193195
};
194196
}
@@ -358,7 +360,7 @@ const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call }) => {
358360
lobby = <Lobby
359361
room={room}
360362
connect={connect}
361-
joinCallButtonTooltip={joinCallButtonTooltip}
363+
joinCallButtonTooltip={joinCallButtonTooltip ?? undefined}
362364
joinCallButtonDisabled={joinCallButtonDisabled}
363365
>
364366
{ facePile }

0 commit comments

Comments
 (0)