fix(conference) Fixes an issue where first unmute with disableInitialGUM=true was resulting in user staying muted.
When device list changes, create a new track with the preferred device only if the user is unmuted. If the user is audio/video muted, remove the existing track from conference. A new track will be created and replaced automatically when the user unmutes. Also since screensharing is a separate source, always check for updated camera devices.
This commit is contained in:
parent
955367a157
commit
298c4bd1e3
141
conference.js
141
conference.js
|
@ -2342,7 +2342,7 @@ export default {
|
||||||
* @param {MediaDeviceInfo[]} devices
|
* @param {MediaDeviceInfo[]} devices
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_onDeviceListChanged(devices) {
|
async _onDeviceListChanged(devices) {
|
||||||
const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
|
const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
|
||||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||||
|
@ -2356,13 +2356,10 @@ export default {
|
||||||
const newDevices
|
const newDevices
|
||||||
= mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
|
= mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
|
||||||
devices,
|
devices,
|
||||||
this.isSharingScreen,
|
|
||||||
localVideo,
|
localVideo,
|
||||||
localAudio,
|
localAudio,
|
||||||
newLabelsOnly);
|
newLabelsOnly);
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const audioWasMuted = this.isLocalAudioMuted();
|
|
||||||
const videoWasMuted = this.isLocalVideoMuted();
|
|
||||||
const requestedInput = {
|
const requestedInput = {
|
||||||
audio: Boolean(newDevices.audioinput),
|
audio: Boolean(newDevices.audioinput),
|
||||||
video: Boolean(newDevices.videoinput)
|
video: Boolean(newDevices.videoinput)
|
||||||
|
@ -2374,7 +2371,6 @@ export default {
|
||||||
= setAudioOutputDeviceId(newDevices.audiooutput, dispatch)
|
= setAudioOutputDeviceId(newDevices.audiooutput, dispatch)
|
||||||
.catch(); // Just ignore any errors in catch block.
|
.catch(); // Just ignore any errors in catch block.
|
||||||
|
|
||||||
|
|
||||||
promises.push(setAudioOutputPromise);
|
promises.push(setAudioOutputPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2391,8 +2387,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's handle unknown/non-preferred devices
|
// Let's handle unknown/non-preferred devices
|
||||||
const newAvailDevices
|
const newAvailDevices = APP.store.getState()['features/base/devices'].availableDevices;
|
||||||
= APP.store.getState()['features/base/devices'].availableDevices;
|
|
||||||
let newAudioDevices = [];
|
let newAudioDevices = [];
|
||||||
let oldAudioDevices = [];
|
let oldAudioDevices = [];
|
||||||
|
|
||||||
|
@ -2408,103 +2403,85 @@ export default {
|
||||||
|
|
||||||
// check for audio
|
// check for audio
|
||||||
if (newAudioDevices.length > 0) {
|
if (newAudioDevices.length > 0) {
|
||||||
APP.store.dispatch(
|
APP.store.dispatch(checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
|
||||||
checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for video
|
// check for video
|
||||||
if (!requestedInput.video) {
|
if (!requestedInput.video) {
|
||||||
APP.store.dispatch(
|
APP.store.dispatch(checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
|
||||||
checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the 'default' mic needs to be selected, we need to
|
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of 'default'
|
||||||
// pass the real device id to gUM instead of 'default' in order
|
// in order to get the correct MediaStreamTrack from chrome because of the following bug.
|
||||||
// to get the correct MediaStreamTrack from chrome because of the
|
|
||||||
// following bug.
|
|
||||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689
|
||||||
const hasDefaultMicChanged = newDevices.audioinput === 'default';
|
const hasDefaultMicChanged = newDevices.audioinput === 'default';
|
||||||
|
|
||||||
// This is the case when the local video is muted and a preferred device is connected.
|
// When the local video is muted and a preferred device is connected, update the settings and remove the track
|
||||||
|
// from the conference. A new track will be created and replaced when the user unmutes their camera.
|
||||||
if (requestedInput.video && this.isLocalVideoMuted()) {
|
if (requestedInput.video && this.isLocalVideoMuted()) {
|
||||||
// We want to avoid creating a new video track in order to prevent turning on the camera.
|
APP.store.dispatch(updateSettings({
|
||||||
requestedInput.video = false;
|
|
||||||
APP.store.dispatch(updateSettings({ // Update the current selected camera for the device selection dialog.
|
|
||||||
cameraDeviceId: newDevices.videoinput
|
cameraDeviceId: newDevices.videoinput
|
||||||
}));
|
}));
|
||||||
|
requestedInput.video = false;
|
||||||
delete newDevices.videoinput;
|
delete newDevices.videoinput;
|
||||||
|
|
||||||
// Removing the current video track in order to force the unmute to select the preferred device.
|
// Remove the track from the conference.
|
||||||
logger.debug('_onDeviceListChanged: Removing the current video track.');
|
if (localVideo) {
|
||||||
this.useVideoStream(null);
|
await this.useVideoStream(null);
|
||||||
|
logger.debug('_onDeviceListChanged: Removed the current video track.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
promises.push(
|
// When the local audio is muted and a preferred device is connected, update the settings and remove the track
|
||||||
mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
|
// from the conference. A new track will be created and replaced when the user unmutes their mic.
|
||||||
|
if (requestedInput.audio && this.isLocalAudioMuted()) {
|
||||||
|
APP.store.dispatch(updateSettings({
|
||||||
|
micDeviceId: newDevices.audioinput
|
||||||
|
}));
|
||||||
|
requestedInput.audio = false;
|
||||||
|
delete newDevices.audioinput;
|
||||||
|
|
||||||
|
// Remove the track from the conference.
|
||||||
|
if (localAudio) {
|
||||||
|
await this.useAudioStream(null);
|
||||||
|
logger.debug('_onDeviceListChanged: Removed the current audio track.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the tracks and replace them only if the user is unmuted.
|
||||||
|
if (requestedInput.audio || requestedInput.video) {
|
||||||
|
let tracks = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
tracks = await mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
|
||||||
createLocalTracksF,
|
createLocalTracksF,
|
||||||
newDevices.videoinput,
|
newDevices.videoinput,
|
||||||
hasDefaultMicChanged
|
hasDefaultMicChanged
|
||||||
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
|
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
|
||||||
: newDevices.audioinput)
|
: newDevices.audioinput);
|
||||||
.then(tracks => {
|
} catch (error) {
|
||||||
// If audio or video muted before, or we unplugged current
|
logger.error(`Track creation failed on device change, ${error}`);
|
||||||
// device and selected new one, then mute new track.
|
|
||||||
const muteSyncPromises = tracks.map(track => {
|
|
||||||
if ((track.isVideoTrack() && videoWasMuted)
|
|
||||||
|| (track.isAudioTrack() && audioWasMuted)) {
|
|
||||||
return track.mute();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.reject(error);
|
||||||
});
|
}
|
||||||
|
|
||||||
return Promise.all(muteSyncPromises)
|
for (const track of tracks) {
|
||||||
.then(() =>
|
if (track.isAudioTrack()) {
|
||||||
Promise.all(Object.keys(requestedInput).map(mediaType => {
|
promises.push(
|
||||||
if (requestedInput[mediaType]) {
|
this.useAudioStream(track)
|
||||||
const useStream
|
.then(() => {
|
||||||
= mediaType === 'audio'
|
hasDefaultMicChanged && (track._realDeviceId = track.deviceId = 'default');
|
||||||
? this.useAudioStream.bind(this)
|
this._updateAudioDeviceId();
|
||||||
: this.useVideoStream.bind(this);
|
}));
|
||||||
const track = tracks.find(t => t.getType() === mediaType) || null;
|
} else {
|
||||||
|
promises.push(
|
||||||
// Use the new stream or null if we failed to obtain it.
|
this.useVideoStream(track)
|
||||||
return useStream(track)
|
.then(() => {
|
||||||
.then(() => {
|
this._updateVideoDeviceId();
|
||||||
if (track?.isAudioTrack() && 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.
|
}
|
||||||
track._realDeviceId = track.deviceId = 'default';
|
|
||||||
}
|
|
||||||
mediaType === 'audio'
|
|
||||||
? this._updateAudioDeviceId()
|
|
||||||
: this._updateVideoDeviceId();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
})));
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// Log and sync known mute state.
|
|
||||||
if (audioWasMuted) {
|
|
||||||
sendAnalytics(createTrackMutedEvent(
|
|
||||||
'audio',
|
|
||||||
'device list changed'));
|
|
||||||
logger.log('Audio mute: device list changed');
|
|
||||||
muteLocalAudio(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.isSharingScreen && videoWasMuted) {
|
|
||||||
sendAnalytics(createTrackMutedEvent(
|
|
||||||
'video',
|
|
||||||
'device list changed'));
|
|
||||||
logger.log('Video mute: device list changed');
|
|
||||||
muteLocalVideo(true);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -158,7 +158,6 @@ export default {
|
||||||
* Determines if currently selected media devices should be changed after
|
* Determines if currently selected media devices should be changed after
|
||||||
* list of available devices has been changed.
|
* list of available devices has been changed.
|
||||||
* @param {MediaDeviceInfo[]} newDevices
|
* @param {MediaDeviceInfo[]} newDevices
|
||||||
* @param {boolean} isSharingScreen
|
|
||||||
* @param {JitsiLocalTrack} localVideo
|
* @param {JitsiLocalTrack} localVideo
|
||||||
* @param {JitsiLocalTrack} localAudio
|
* @param {JitsiLocalTrack} localAudio
|
||||||
* @returns {{
|
* @returns {{
|
||||||
|
@ -169,13 +168,12 @@ export default {
|
||||||
*/
|
*/
|
||||||
getNewMediaDevicesAfterDeviceListChanged( // eslint-disable-line max-params
|
getNewMediaDevicesAfterDeviceListChanged( // eslint-disable-line max-params
|
||||||
newDevices,
|
newDevices,
|
||||||
isSharingScreen,
|
|
||||||
localVideo,
|
localVideo,
|
||||||
localAudio,
|
localAudio,
|
||||||
newLabels) {
|
newLabels) {
|
||||||
return {
|
return {
|
||||||
audioinput: getNewAudioInputDevice(newDevices, localAudio, newLabels),
|
audioinput: getNewAudioInputDevice(newDevices, localAudio, newLabels),
|
||||||
videoinput: isSharingScreen ? undefined : getNewVideoInputDevice(newDevices, localVideo, newLabels),
|
videoinput: getNewVideoInputDevice(newDevices, localVideo, newLabels),
|
||||||
audiooutput: getNewAudioOutputDevice(newDevices)
|
audiooutput: getNewAudioOutputDevice(newDevices)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue