Dynamically enables/disables the toolbar video button. Prior to that commit if we would start with no video there would be no way to enable it later on.
242 lines
9.3 KiB
JavaScript
242 lines
9.3 KiB
JavaScript
/* global APP, JitsiMeetJS */
|
|
|
|
let currentAudioInputDevices,
|
|
currentVideoInputDevices,
|
|
currentAudioOutputDevices;
|
|
|
|
/**
|
|
* Determines if currently selected audio output device should be changed after
|
|
* list of available devices has been changed.
|
|
* @param {MediaDeviceInfo[]} newDevices
|
|
* @returns {string|undefined} - ID of new audio output device to use, undefined
|
|
* if audio output device should not be changed.
|
|
*/
|
|
function getNewAudioOutputDevice(newDevices) {
|
|
if (!JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output')) {
|
|
return;
|
|
}
|
|
|
|
let selectedAudioOutputDeviceId = APP.settings.getAudioOutputDeviceId();
|
|
let availableAudioOutputDevices = newDevices.filter(
|
|
d => d.kind === 'audiooutput');
|
|
|
|
// Switch to 'default' audio output device if we don't have the selected one
|
|
// available anymore.
|
|
if (selectedAudioOutputDeviceId !== 'default' &&
|
|
!availableAudioOutputDevices.find(d =>
|
|
d.deviceId === selectedAudioOutputDeviceId)) {
|
|
return 'default';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if currently selected audio input device should be changed after
|
|
* list of available devices has been changed.
|
|
* @param {MediaDeviceInfo[]} newDevices
|
|
* @param {JitsiLocalTrack} localAudio
|
|
* @returns {string|undefined} - ID of new microphone device to use, undefined
|
|
* if audio input device should not be changed.
|
|
*/
|
|
function getNewAudioInputDevice(newDevices, localAudio) {
|
|
let availableAudioInputDevices = newDevices.filter(
|
|
d => d.kind === 'audioinput');
|
|
let selectedAudioInputDeviceId = APP.settings.getMicDeviceId();
|
|
let selectedAudioInputDevice = availableAudioInputDevices.find(
|
|
d => d.deviceId === selectedAudioInputDeviceId);
|
|
|
|
// Here we handle case when no device was initially plugged, but
|
|
// then it's connected OR new device was connected when previous
|
|
// track has ended.
|
|
if (!localAudio || localAudio.disposed || localAudio.isEnded()) {
|
|
// If we have new audio device and permission to use it was granted
|
|
// (label is not an empty string), then we will try to use the first
|
|
// available device.
|
|
if (availableAudioInputDevices.length &&
|
|
availableAudioInputDevices[0].label !== '') {
|
|
return availableAudioInputDevices[0].deviceId;
|
|
}
|
|
// Otherwise we assume that we don't have any audio input devices
|
|
// to use and that's why disable microphone button on UI.
|
|
else {
|
|
APP.UI.setMicrophoneButtonEnabled(false);
|
|
}
|
|
} else {
|
|
// And here we handle case when we already have some device working,
|
|
// but we plug-in a "preferred" (previously selected in settings, stored
|
|
// in local storage) device.
|
|
if (selectedAudioInputDevice &&
|
|
selectedAudioInputDeviceId !== localAudio.getDeviceId()) {
|
|
return selectedAudioInputDeviceId;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if currently selected video input device should be changed after
|
|
* list of available devices has been changed.
|
|
* @param {MediaDeviceInfo[]} newDevices
|
|
* @param {JitsiLocalTrack} localVideo
|
|
* @returns {string|undefined} - ID of new camera device to use, undefined
|
|
* if video input device should not be changed.
|
|
*/
|
|
function getNewVideoInputDevice(newDevices, localVideo) {
|
|
let availableVideoInputDevices = newDevices.filter(
|
|
d => d.kind === 'videoinput');
|
|
let selectedVideoInputDeviceId = APP.settings.getCameraDeviceId();
|
|
let selectedVideoInputDevice = availableVideoInputDevices.find(
|
|
d => d.deviceId === selectedVideoInputDeviceId);
|
|
|
|
// Here we handle case when no video input device was initially plugged,
|
|
// but then device is connected OR new device was connected when
|
|
// previous track has ended.
|
|
if (!localVideo || localVideo.disposed || localVideo.isEnded()) {
|
|
// If we have new video device and permission to use it was granted
|
|
// (label is not an empty string), then we will try to use the first
|
|
// available device.
|
|
if (availableVideoInputDevices.length &&
|
|
availableVideoInputDevices[0].label !== '') {
|
|
return availableVideoInputDevices[0].deviceId;
|
|
}
|
|
} else {
|
|
// And here we handle case when we already have some device working,
|
|
// but we plug-in a "preferred" (previously selected in settings, stored
|
|
// in local storage) device.
|
|
if (selectedVideoInputDevice &&
|
|
selectedVideoInputDeviceId !== localVideo.getDeviceId()) {
|
|
return selectedVideoInputDeviceId;
|
|
}
|
|
}
|
|
}
|
|
|
|
export default {
|
|
/**
|
|
* Returns list of devices of single kind.
|
|
* @param {MediaDeviceInfo[]} devices
|
|
* @param {'audioinput'|'audiooutput'|'videoinput'} kind
|
|
* @returns {MediaDeviceInfo[]}
|
|
*/
|
|
getDevicesFromListByKind(devices, kind) {
|
|
return devices.filter(d => d.kind === kind);
|
|
},
|
|
/**
|
|
* Stores lists of current 'audioinput', 'videoinput' and 'audiooutput'
|
|
* devices.
|
|
* @param {MediaDeviceInfo[]} devices
|
|
*/
|
|
setCurrentMediaDevices(devices) {
|
|
currentAudioInputDevices =
|
|
this.getDevicesFromListByKind(devices, 'audioinput');
|
|
currentVideoInputDevices =
|
|
this.getDevicesFromListByKind(devices, 'videoinput');
|
|
currentAudioOutputDevices =
|
|
this.getDevicesFromListByKind(devices, 'audiooutput');
|
|
},
|
|
/**
|
|
* Returns lists of current 'audioinput', 'videoinput' and 'audiooutput'
|
|
* devices.
|
|
* @returns {{
|
|
* audioinput: (MediaDeviceInfo[]|undefined),
|
|
* videoinput: (MediaDeviceInfo[]|undefined),
|
|
* audiooutput: (MediaDeviceInfo[]|undefined),
|
|
* }}
|
|
*/
|
|
getCurrentMediaDevices() {
|
|
return {
|
|
audioinput: currentAudioInputDevices,
|
|
videoinput: currentVideoInputDevices,
|
|
audiooutput: currentAudioOutputDevices
|
|
};
|
|
},
|
|
/**
|
|
* Determines if currently selected media devices should be changed after
|
|
* list of available devices has been changed.
|
|
* @param {MediaDeviceInfo[]} newDevices
|
|
* @param {boolean} isSharingScreen
|
|
* @param {JitsiLocalTrack} localVideo
|
|
* @param {JitsiLocalTrack} localAudio
|
|
* @returns {{
|
|
* audioinput: (string|undefined),
|
|
* videoinput: (string|undefined),
|
|
* audiooutput: (string|undefined)
|
|
* }}
|
|
*/
|
|
getNewMediaDevicesAfterDeviceListChanged(
|
|
newDevices, isSharingScreen, localVideo, localAudio) {
|
|
return {
|
|
audioinput: getNewAudioInputDevice(newDevices, localAudio),
|
|
videoinput: !isSharingScreen &&
|
|
getNewVideoInputDevice(newDevices, localVideo),
|
|
audiooutput: getNewAudioOutputDevice(newDevices)
|
|
};
|
|
},
|
|
/**
|
|
* Tries to create new local tracks for new devices obtained after device
|
|
* list changed. Shows error dialog in case of failures.
|
|
* @param {function} createLocalTracks
|
|
* @param {string} (cameraDeviceId)
|
|
* @param {string} (micDeviceId)
|
|
* @returns {Promise.<JitsiLocalTrack[]>}
|
|
*/
|
|
createLocalTracksAfterDeviceListChanged(
|
|
createLocalTracks, cameraDeviceId, micDeviceId) {
|
|
let audioTrackError;
|
|
let videoTrackError;
|
|
let audioRequested = !!micDeviceId;
|
|
let videoRequested = !!cameraDeviceId;
|
|
|
|
if (audioRequested && videoRequested) {
|
|
// First we try to create both audio and video tracks together.
|
|
return createLocalTracks({
|
|
devices: ['audio', 'video'],
|
|
cameraDeviceId: cameraDeviceId,
|
|
micDeviceId: micDeviceId
|
|
})
|
|
// If we fail to do this, try to create them separately.
|
|
.catch(() => Promise.all([
|
|
createAudioTrack(false).then(([stream]) => stream),
|
|
createVideoTrack(false).then(([stream]) => stream)
|
|
]))
|
|
.then(tracks => {
|
|
if (audioTrackError || videoTrackError) {
|
|
APP.UI.showDeviceErrorDialog(
|
|
audioTrackError, videoTrackError);
|
|
}
|
|
|
|
return tracks.filter(t => typeof t !== 'undefined');
|
|
});
|
|
} else if (videoRequested && !audioRequested) {
|
|
return createVideoTrack();
|
|
} else if (audioRequested && !videoRequested) {
|
|
return createAudioTrack();
|
|
} else {
|
|
return Promise.resolve([]);
|
|
}
|
|
|
|
function createAudioTrack(showError) {
|
|
return createLocalTracks({
|
|
devices: ['audio'],
|
|
cameraDeviceId: null,
|
|
micDeviceId: micDeviceId
|
|
})
|
|
.catch(err => {
|
|
audioTrackError = err;
|
|
showError && APP.UI.showDeviceErrorDialog(err, null);
|
|
return [];
|
|
});
|
|
}
|
|
|
|
function createVideoTrack(showError) {
|
|
return createLocalTracks({
|
|
devices: ['video'],
|
|
cameraDeviceId: cameraDeviceId,
|
|
micDeviceId: null
|
|
})
|
|
.catch(err => {
|
|
videoTrackError = err;
|
|
showError && APP.UI.showDeviceErrorDialog(null, err);
|
|
return [];
|
|
});
|
|
}
|
|
}
|
|
};
|