Merge pull request #20978 from prlanzarin/u30/fix/audio-ff-perm-api
fix(audio): ensure correct audio device labels in Firefox
This commit is contained in:
commit
a0fb450522
@ -280,6 +280,9 @@ class AudioSettings extends React.Component {
|
|||||||
// Only generate input streams if they're going to be used with something
|
// Only generate input streams if they're going to be used with something
|
||||||
// In this case, the volume meter or local echo test.
|
// In this case, the volume meter or local echo test.
|
||||||
if (produceStreams) {
|
if (produceStreams) {
|
||||||
|
this.setState({
|
||||||
|
producingStreams: true,
|
||||||
|
});
|
||||||
this.generateInputStream(deviceId).then((stream) => {
|
this.generateInputStream(deviceId).then((stream) => {
|
||||||
// Extract the deviceId again from the stream to guarantee consistency
|
// Extract the deviceId again from the stream to guarantee consistency
|
||||||
// between stream DID vs chosen DID. That's necessary in scenarios where,
|
// between stream DID vs chosen DID. That's necessary in scenarios where,
|
||||||
@ -302,8 +305,13 @@ class AudioSettings extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
inputDeviceId: extractedDeviceId,
|
inputDeviceId: extractedDeviceId,
|
||||||
stream,
|
stream,
|
||||||
producingStreams: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update the device list after the stream has been generated.
|
||||||
|
// This is necessary to guarantee the device list is up-to-date, mainly
|
||||||
|
// in Firefox as it omit labels if no active stream is present (even if
|
||||||
|
// gUM permission is flagged as granted).
|
||||||
|
this.updateDeviceList();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
logger.warn({
|
logger.warn({
|
||||||
logCode: 'audiosettings_gum_failed',
|
logCode: 'audiosettings_gum_failed',
|
||||||
@ -314,6 +322,13 @@ class AudioSettings extends React.Component {
|
|||||||
},
|
},
|
||||||
}, `Audio settings gUM failed: ${error.name}`);
|
}, `Audio settings gUM failed: ${error.name}`);
|
||||||
handleGUMFailure(error);
|
handleGUMFailure(error);
|
||||||
|
}).finally(() => {
|
||||||
|
// Component unmounted after gUM resolution -> skip echo rendering
|
||||||
|
if (!this._isMounted) return;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
producingStreams: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -378,6 +393,15 @@ class AudioSettings extends React.Component {
|
|||||||
audioInputDevices,
|
audioInputDevices,
|
||||||
audioOutputDevices,
|
audioOutputDevices,
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.warn({
|
||||||
|
logCode: 'audiosettings_enumerate_devices_error',
|
||||||
|
extraInfo: {
|
||||||
|
errorName: error.name,
|
||||||
|
errorMessage: error.message,
|
||||||
|
},
|
||||||
|
}, `Audio settings: error enumerating devices - {${error.name}: ${error.message}}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +73,45 @@ const useIsUsingAudio = () => {
|
|||||||
return Boolean(isConnected || isConnecting || isHangingUp || isEchoTest);
|
return Boolean(isConnected || isConnecting || isHangingUp || isEchoTest);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user has granted permission to use the microphone.
|
||||||
|
*
|
||||||
|
* @param {Object} options - Options object.
|
||||||
|
* @param {string} options.permissionStatus - The current permission status.
|
||||||
|
* @param {boolean} options.gumOnPrompt - Whether to check microphone permission by attempting to
|
||||||
|
* get a media stream.
|
||||||
|
* @returns {Promise<boolean|null>} - A promise that resolves to a boolean indicating whether the
|
||||||
|
* user has granted permission to use the microphone. If the permission status is unknown, the
|
||||||
|
* promise resolves to null.
|
||||||
|
*/
|
||||||
const hasMicrophonePermission = async ({
|
const hasMicrophonePermission = async ({
|
||||||
permissionStatus,
|
permissionStatus = null,
|
||||||
gumOnPrompt = false,
|
gumOnPrompt = false,
|
||||||
}) => {
|
}) => {
|
||||||
|
const checkWithGUM = () => {
|
||||||
|
if (!gumOnPrompt) return Promise.resolve(null);
|
||||||
|
|
||||||
|
return doGUM({ audio: getAudioConstraints() })
|
||||||
|
.then((stream) => {
|
||||||
|
// Close the stream and remove all tracks - this is just a permission check
|
||||||
|
stream.getTracks().forEach((track) => {
|
||||||
|
track.stop();
|
||||||
|
stream.removeTrack(track);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.name === 'NotAllowedError') return false;
|
||||||
|
|
||||||
|
// Give it the benefit of the doubt. It might be a device mismatch
|
||||||
|
// or something else that's not a permissions issue, so let's try
|
||||||
|
// to proceed. Rollbacks that happen downstream might fix the issue,
|
||||||
|
// otherwise we'll land on the Help screen anyways
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let status = permissionStatus;
|
let status = permissionStatus;
|
||||||
|
|
||||||
@ -91,36 +126,19 @@ const hasMicrophonePermission = async ({
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case 'denied':
|
case 'denied':
|
||||||
return false;
|
return false;
|
||||||
case 'prompt':
|
|
||||||
// Prompt without any subsequent action is considered unknown
|
|
||||||
if (!gumOnPrompt) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return doGUM({ audio: getAudioConstraints() }).then((stream) => {
|
|
||||||
stream.getTracks().forEach((track) => {
|
|
||||||
track.stop();
|
|
||||||
stream.removeTrack(track);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}).catch((error) => {
|
|
||||||
if (error.name === 'NotAllowedError') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give it the benefit of the doubt. It might be a device mismatch
|
|
||||||
// or something else that's not a permissions issue, so let's try
|
|
||||||
// to proceed. Rollbacks that happen downstream might fix the issue,
|
|
||||||
// otherwise we'll land on the Help screen anyways
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
case 'granted':
|
case 'granted':
|
||||||
default:
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case null:
|
||||||
|
case 'prompt':
|
||||||
|
return checkWithGUM();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error({
|
logger.warn({
|
||||||
logCode: 'audio_check_microphone_permission_error',
|
logCode: 'audio_check_microphone_permission_error',
|
||||||
extraInfo: {
|
extraInfo: {
|
||||||
errorName: error.name,
|
errorName: error.name,
|
||||||
@ -128,8 +146,7 @@ const hasMicrophonePermission = async ({
|
|||||||
},
|
},
|
||||||
}, `Error checking microphone permission: ${error.message}`);
|
}, `Error checking microphone permission: ${error.message}`);
|
||||||
|
|
||||||
// Null = could not determine permission status
|
return checkWithGUM();
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user