Skip to content

Commit

Permalink
fix(device-selection): Add a workaround for a chrome bug with default…
Browse files Browse the repository at this point in the history
… mic

Pass the real deviceId to gUM instead of 'default' for Chrome to return the correct media stream
  • Loading branch information
jallamsetty1 committed Jun 8, 2020
1 parent 26f7951 commit f4bcad0
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 14 deletions.
35 changes: 33 additions & 2 deletions conference.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
import {
checkAndNotifyForNewDevice,
getAvailableDevices,
getDefaultDeviceId,
notifyCameraError,
notifyMicError,
setAudioOutputDeviceId,
Expand Down Expand Up @@ -2434,11 +2435,20 @@ export default {
micDeviceId => {
const audioWasMuted = this.isLocalAudioMuted();

// When the 'default' mic needs to be selected, we need to
// pass the real device id to gUM instead of 'default' in order
// to get the correct MediaStreamTrack from chrome because of the
// following bug.
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689
const hasDefaultMicChanged = micDeviceId === 'default';

sendAnalytics(createDeviceChangedEvent('audio', 'input'));
createLocalTracksF({
devices: [ 'audio' ],
cameraDeviceId: null,
micDeviceId
micDeviceId: hasDefaultMicChanged
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
: micDeviceId
})
.then(([ stream ]) => {
// if audio was muted before changing the device, mute
Expand All @@ -2462,6 +2472,12 @@ export default {
return this.useAudioStream(stream);
})
.then(() => {
if (hasDefaultMicChanged) {
// workaround for the default device to be shown as selected in the
// settings even when the real device id was passed to gUM because of the
// above mentioned chrome bug.
this.localAudio._realDeviceId = this.localAudio.deviceId = 'default';
}
logger.log(`switched local audio device: ${this.localAudio?.getDeviceId()}`);

this._updateAudioDeviceId();
Expand Down Expand Up @@ -2763,11 +2779,20 @@ export default {
checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
}

// When the 'default' mic needs to be selected, we need to
// pass the real device id to gUM instead of 'default' in order
// to get the correct MediaStreamTrack from chrome because of the
// following bug.
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689
const hasDefaultMicChanged = newDevices.audioinput === 'default';

promises.push(
mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
createLocalTracksF,
newDevices.videoinput,
newDevices.audioinput)
hasDefaultMicChanged
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
: newDevices.audioinput)
.then(tracks => {
// If audio or video muted before, or we unplugged current
// device and selected new one, then mute new track.
Expand All @@ -2792,6 +2817,12 @@ export default {
// Use the new stream or null if we failed to obtain it.
return useStream(tracks.find(track => track.getType() === mediaType) || null)
.then(() => {
if (hasDefaultMicChanged) {
// workaround for the default device to be shown as selected in the
// settings even when the real device id was passed to gUM because of
// the above mentioned chrome bug.
this.localAudio._realDeviceId = this.localAudio.deviceId = 'default';
}
mediaType === 'audio'
? this._updateAudioDeviceId()
: this._updateVideoDeviceId();
Expand Down
41 changes: 29 additions & 12 deletions react/features/base/devices/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import logger from './logger';

declare var APP: Object;

const webrtcKindToJitsiKindTranslator = {
audioinput: 'audioInput',
audiooutput: 'audioOutput',
videoinput: 'videoInput'
};

/**
* Detects the use case when the labels are not available if the A/V permissions
* are not yet granted.
Expand Down Expand Up @@ -41,6 +47,29 @@ export function getAudioOutputDeviceId() {
return JitsiMeetJS.mediaDevices.getAudioOutputDevice();
}

/**
* Finds the real device id of the default device of the given type.
*
* @param {Object} state - The redux state.
* @param {*} kind - The type of the device. One of "audioInput",
* "audioOutput", and "videoInput". Also supported is all lowercase versions
* of the preceding types.
* @returns {string|undefined}
*/
export function getDefaultDeviceId(state: Object, kind: string) {
const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;
const defaultDevice = (state['features/base/devices'].availableDevices[kindToSearch] || [])
.find(d => d.deviceId === 'default');

// Find the device with a matching group id.
const matchingDevice = (state['features/base/devices'].availableDevices[kindToSearch] || [])
.find(d => d.deviceId !== 'default' && d.groupId === defaultDevice.groupId);

if (matchingDevice) {
return matchingDevice.deviceId;
}
}

/**
* Finds a device with a label that matches the passed label and returns its id.
*
Expand All @@ -52,12 +81,6 @@ export function getAudioOutputDeviceId() {
* @returns {string|undefined}
*/
export function getDeviceIdByLabel(state: Object, label: string, kind: string) {
const webrtcKindToJitsiKindTranslator = {
audioinput: 'audioInput',
audiooutput: 'audioOutput',
videoinput: 'videoInput'
};

const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;

const device
Expand All @@ -80,12 +103,6 @@ export function getDeviceIdByLabel(state: Object, label: string, kind: string) {
* @returns {string|undefined}
*/
export function getDeviceLabelById(state: Object, id: string, kind: string) {
const webrtcKindToJitsiKindTranslator = {
audioinput: 'audioInput',
audiooutput: 'audioOutput',
videoinput: 'videoInput'
};

const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;

const device
Expand Down

0 comments on commit f4bcad0

Please sign in to comment.