@@ -185,13 +185,9 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
185185
186186 const getFreshMicTrack = async ( ) => {
187187 try {
188- const constraints = {
189- audio : selectedMicId . value
190- ? { deviceId : { exact : selectedMicId . value } }
191- : true ,
192- } ;
193- const freshStream =
194- await navigator . mediaDevices . getUserMedia ( constraints ) ;
188+ const { stream : freshStream } = await acquireUserMedia ( false , true , {
189+ micDeviceId : selectedMicId . value || null ,
190+ } ) ;
195191 const freshTrack = freshStream . getAudioTracks ( ) [ 0 ] ;
196192
197193 if ( ! freshTrack ) {
@@ -311,7 +307,12 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
311307
312308 const buildMediaConstraints = async ( videoEnabled , audioEnabled ) => {
313309 const constraints = { } ;
314- const deviceIds = { } ;
310+
311+ const audioConstraints = {
312+ echoCancellation : true ,
313+ noiseSuppression : true ,
314+ autoGainControl : true ,
315+ } ;
315316
316317 if ( videoEnabled ) {
317318 constraints . video = {
@@ -326,26 +327,59 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
326327 ) ;
327328 if ( validCameraId ) {
328329 constraints . video . deviceId = { exact : validCameraId } ;
329- deviceIds . camera = validCameraId ;
330330 }
331331 // If no valid device ID, let browser use its default
332332 }
333333
334334 if ( audioEnabled ) {
335- constraints . audio = { } ;
335+ constraints . audio = { ... audioConstraints } ;
336336
337337 const validMicId = await getValidDeviceId (
338338 selectedMicId . value ,
339339 "microphone" ,
340340 ) ;
341341 if ( validMicId ) {
342342 constraints . audio . deviceId = { exact : validMicId } ;
343- deviceIds . microphone = validMicId ;
344343 }
345344 // If no valid device ID, let browser use its default
346345 }
347346
348- return { constraints, deviceIds } ;
347+ return constraints ;
348+ } ;
349+
350+ const acquireUserMedia = async (
351+ videoEnabled ,
352+ audioEnabled ,
353+ deviceOverrides = { } ,
354+ ) => {
355+ const constraints = await buildMediaConstraints ( videoEnabled , audioEnabled ) ;
356+
357+ if ( videoEnabled && Object . hasOwn ( deviceOverrides , "cameraDeviceId" ) ) {
358+ const validCameraId = await getValidDeviceId (
359+ deviceOverrides . cameraDeviceId ,
360+ "camera" ,
361+ ) ;
362+ if ( validCameraId && constraints . video ) {
363+ constraints . video . deviceId = { exact : validCameraId } ;
364+ } else if ( constraints . video ?. deviceId ) {
365+ constraints . video . deviceId = undefined ;
366+ }
367+ }
368+
369+ if ( audioEnabled && Object . hasOwn ( deviceOverrides , "micDeviceId" ) ) {
370+ const validMicId = await getValidDeviceId (
371+ deviceOverrides . micDeviceId ,
372+ "microphone" ,
373+ ) ;
374+ if ( validMicId && constraints . audio ) {
375+ constraints . audio . deviceId = { exact : validMicId } ;
376+ } else if ( constraints . audio ?. deviceId ) {
377+ constraints . audio . deviceId = undefined ;
378+ }
379+ }
380+
381+ const stream = await navigator . mediaDevices . getUserMedia ( constraints ) ;
382+ return { stream, constraints } ;
349383 } ;
350384
351385 /**
@@ -382,12 +416,10 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
382416 meetingState . setMediaState ( prefMicEnabled . value , prefCameraEnabled . value ) ;
383417
384418 if ( meetingState . isCameraOn . value || meetingState . isMicOn . value ) {
385- const { constraints , deviceIds } = await buildMediaConstraints (
419+ const { stream } = await acquireUserMedia (
386420 meetingState . isCameraOn . value ,
387421 meetingState . isMicOn . value ,
388422 ) ;
389-
390- const stream = await navigator . mediaDevices . getUserMedia ( constraints ) ;
391423 meetingState . localStream . value = stream ;
392424 // Clear any stale connection error on successful media acquisition
393425 if ( meetingState . connectionError . value ) {
@@ -437,11 +469,11 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
437469 // Turning mic ON
438470 if ( ! stream ) {
439471 try {
440- const { constraints , deviceIds } = await buildMediaConstraints (
472+ const { stream : nextStream } = await acquireUserMedia (
441473 meetingState . isCameraOn . value ,
442474 enable ,
443475 ) ;
444- stream = await navigator . mediaDevices . getUserMedia ( constraints ) ;
476+ stream = nextStream ;
445477 meetingState . localStream . value = stream ;
446478 meetingState . cameraPermissionGranted . value = true ;
447479 meetingState . microphonePermissionGranted . value = true ;
@@ -462,12 +494,7 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
462494 const hasAudio = stream . getAudioTracks ( ) . length > 0 ;
463495 if ( ! hasAudio ) {
464496 try {
465- const { constraints, deviceIds } = await buildMediaConstraints (
466- false ,
467- true ,
468- ) ;
469- const audioOnly =
470- await navigator . mediaDevices . getUserMedia ( constraints ) ;
497+ const { stream : audioOnly } = await acquireUserMedia ( false , true ) ;
471498 const newTrack = audioOnly . getAudioTracks ( ) [ 0 ] ;
472499 if ( newTrack ) {
473500 stream . addTrack ( newTrack ) ;
@@ -491,12 +518,10 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
491518 if ( at . readyState === "ended" ) {
492519 // Track was stopped, get a new one
493520 try {
494- const { constraints , deviceIds } = await buildMediaConstraints (
521+ const { stream : audioOnly } = await acquireUserMedia (
495522 false ,
496523 true ,
497524 ) ;
498- const audioOnly =
499- await navigator . mediaDevices . getUserMedia ( constraints ) ;
500525 const newTrack = audioOnly . getAudioTracks ( ) [ 0 ] ;
501526 if ( newTrack ) {
502527 stream . removeTrack ( at ) ;
@@ -629,11 +654,11 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
629654 if ( ! stream ) {
630655 // No existing stream: request both video and current audio state
631656 try {
632- const { constraints , deviceIds } = await buildMediaConstraints (
657+ const { stream : nextStream } = await acquireUserMedia (
633658 true ,
634659 meetingState . isMicOn . value ,
635660 ) ;
636- stream = await navigator . mediaDevices . getUserMedia ( constraints ) ;
661+ stream = nextStream ;
637662 meetingState . localStream . value = stream ;
638663 meetingState . cameraPermissionGranted . value = true ;
639664 if ( meetingState . isMicOn . value ) {
@@ -656,12 +681,7 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
656681 const hasVideo = stream . getVideoTracks ( ) . length > 0 ;
657682 if ( ! hasVideo ) {
658683 try {
659- const { constraints, deviceIds } = await buildMediaConstraints (
660- true ,
661- false ,
662- ) ;
663- const videoOnly =
664- await navigator . mediaDevices . getUserMedia ( constraints ) ;
684+ const { stream : videoOnly } = await acquireUserMedia ( true , false ) ;
665685 const newTrack = videoOnly . getVideoTracks ( ) [ 0 ] ;
666686 if ( newTrack ) {
667687 stream . addTrack ( newTrack ) ;
@@ -694,12 +714,10 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
694714 if ( vt . readyState === "ended" ) {
695715 // Track was stopped, get a new one
696716 try {
697- const { constraints , deviceIds } = await buildMediaConstraints (
717+ const { stream : videoOnly } = await acquireUserMedia (
698718 true ,
699719 false ,
700720 ) ;
701- const videoOnly =
702- await navigator . mediaDevices . getUserMedia ( constraints ) ;
703721 const newTrack = videoOnly . getVideoTracks ( ) [ 0 ] ;
704722 if ( newTrack ) {
705723 stream . removeTrack ( vt ) ;
@@ -2232,6 +2250,7 @@ export function useMeetingLogic(meetingState, meetingId, options = {}) {
22322250
22332251 // Methods - Media
22342252 initializeCamera,
2253+ acquireUserMedia,
22352254 toggleMicrophone,
22362255 toggleCamera,
22372256 toggleScreenShare,
0 commit comments