Adds new persistent state for devices user selection.

The state about currently opened devices is filtered and not stored, where we only store when user selects a device preferences.
Also allow changing input devices for Firefox when we are not in a conference.
This commit is contained in:
damencho 2019-05-01 15:13:25 +01:00 committed by Дамян Минков
parent 2d45709a6a
commit 740c1eb84f
8 changed files with 79 additions and 22 deletions

View File

@ -2394,7 +2394,15 @@ export default {
: this.useVideoStream.bind(this); : this.useVideoStream.bind(this);
// Use the new stream or null if we failed to obtain it. // Use the new stream or null if we failed to obtain it.
return useStream(tracks.find(track => track.getType() === mediaType) || null); return useStream(tracks.find(track => track.getType() === mediaType) || null)
.then(() => {
const settings
= mediaType === 'audio'
? { micDeviceId: newDevices.audioinput }
: { cameraDeviceId: newDevices.videoinput };
APP.store.dispatch(updateSettings(settings));
});
} }
return Promise.resolve(); return Promise.resolve();

View File

@ -39,7 +39,7 @@ function getNewAudioInputDevice(newDevices, localAudio) {
const availableAudioInputDevices = newDevices.filter( const availableAudioInputDevices = newDevices.filter(
d => d.kind === 'audioinput'); d => d.kind === 'audioinput');
const settings = APP.store.getState()['features/base/settings']; const settings = APP.store.getState()['features/base/settings'];
const selectedAudioInputDeviceId = settings.micDeviceId; const selectedAudioInputDeviceId = settings.userSelectedMicDeviceId;
const selectedAudioInputDevice = availableAudioInputDevices.find( const selectedAudioInputDevice = availableAudioInputDevices.find(
d => d.deviceId === selectedAudioInputDeviceId); d => d.deviceId === selectedAudioInputDeviceId);
@ -78,7 +78,7 @@ function getNewVideoInputDevice(newDevices, localVideo) {
const availableVideoInputDevices = newDevices.filter( const availableVideoInputDevices = newDevices.filter(
d => d.kind === 'videoinput'); d => d.kind === 'videoinput');
const settings = APP.store.getState()['features/base/settings']; const settings = APP.store.getState()['features/base/settings'];
const selectedVideoInputDeviceId = settings.cameraDeviceId; const selectedVideoInputDeviceId = settings.userSelectedCameraDeviceId;
const selectedVideoInputDevice = availableVideoInputDevices.find( const selectedVideoInputDevice = availableVideoInputDevices.find(
d => d.deviceId === selectedVideoInputDeviceId); d => d.deviceId === selectedVideoInputDeviceId);

View File

@ -90,10 +90,10 @@ export function configureInitialDevices() {
return updateSettingsPromise return updateSettingsPromise
.then(() => { .then(() => {
const { audioOutputDeviceId } const { userSelectedAudioOutputDeviceId }
= getState()['features/base/settings']; = getState()['features/base/settings'];
return setAudioOutputDeviceId(audioOutputDeviceId, dispatch) return setAudioOutputDeviceId(userSelectedAudioOutputDeviceId, dispatch)
.catch(ex => logger.warn(`Failed to set audio output device. .catch(ex => logger.warn(`Failed to set audio output device.
Default audio output device will be used instead ${ex}`)); Default audio output device will be used instead ${ex}`));
}); });

View File

@ -117,14 +117,27 @@ export function groupDevicesByKind(devices: Object[]): Object {
* *
* @param {string} newId - New audio output device id. * @param {string} newId - New audio output device id.
* @param {Function} dispatch - The Redux dispatch function. * @param {Function} dispatch - The Redux dispatch function.
* @param {boolean} userSelection - Whether this is a user selection update.
* @returns {Promise} * @returns {Promise}
*/ */
export function setAudioOutputDeviceId( export function setAudioOutputDeviceId(
newId: string = 'default', newId: string = 'default',
dispatch: Function): Promise<*> { dispatch: Function,
userSelection: boolean = false): Promise<*> {
return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId) return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId)
.then(() => .then(() => {
dispatch(updateSettings({ const newSettings = {
audioOutputDeviceId: newId audioOutputDeviceId: newId,
}))); userSelectedAudioOutputDeviceId: undefined
};
if (userSelection) {
newSettings.userSelectedAudioOutputDeviceId = newId;
} else {
// a flow workaround, I needed to add 'userSelectedAudioOutputDeviceId: undefined'
delete newSettings.userSelectedAudioOutputDeviceId;
}
return dispatch(updateSettings(newSettings));
});
} }

View File

@ -30,7 +30,10 @@ const DEFAULT_STATE = {
serverURL: undefined, serverURL: undefined,
startAudioOnly: false, startAudioOnly: false,
startWithAudioMuted: false, startWithAudioMuted: false,
startWithVideoMuted: false startWithVideoMuted: false,
userSelectedAudioOutputDeviceId: undefined,
userSelectedCameraDeviceId: undefined,
userSelectedMicDeviceId: undefined
}; };
const STORE_NAME = 'features/base/settings'; const STORE_NAME = 'features/base/settings';
@ -38,7 +41,20 @@ const STORE_NAME = 'features/base/settings';
/** /**
* Sets up the persistence of the feature {@code base/settings}. * Sets up the persistence of the feature {@code base/settings}.
*/ */
PersistenceRegistry.register(STORE_NAME); const filterSubtree = {};
// start with the default state
Object.keys(DEFAULT_STATE).forEach(key => {
filterSubtree[key] = true;
});
// we want to filter these props, to not be stored as they represent
// what is currently opened/used as devices
filterSubtree.audioOutputDeviceId = false;
filterSubtree.cameraDeviceId = false;
filterSubtree.micDeviceId = false;
PersistenceRegistry.register(STORE_NAME, filterSubtree);
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => { ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
switch (action.type) { switch (action.type) {

View File

@ -40,10 +40,10 @@ export function createLocalTracksF(
const settings = store.getState()['features/base/settings']; const settings = store.getState()['features/base/settings'];
if (typeof cameraDeviceId === 'undefined' || cameraDeviceId === null) { if (typeof cameraDeviceId === 'undefined' || cameraDeviceId === null) {
cameraDeviceId = settings.cameraDeviceId; cameraDeviceId = settings.userSelectedCameraDeviceId;
} }
if (typeof micDeviceId === 'undefined' || micDeviceId === null) { if (typeof micDeviceId === 'undefined' || micDeviceId === null) {
micDeviceId = settings.micDeviceId; micDeviceId = settings.userSelectedMicDeviceId;
} }
} }

View File

@ -112,7 +112,8 @@ export function submitDeviceSelectionTab(newState) {
&& newState.selectedVideoInputId && newState.selectedVideoInputId
!== currentState.selectedVideoInputId) { !== currentState.selectedVideoInputId) {
dispatch(updateSettings({ dispatch(updateSettings({
cameraDeviceId: newState.selectedVideoInputId cameraDeviceId: newState.selectedVideoInputId,
userSelectedCameraDeviceId: newState.selectedVideoInputId
})); }));
dispatch( dispatch(
@ -123,7 +124,8 @@ export function submitDeviceSelectionTab(newState) {
&& newState.selectedAudioInputId && newState.selectedAudioInputId
!== currentState.selectedAudioInputId) { !== currentState.selectedAudioInputId) {
dispatch(updateSettings({ dispatch(updateSettings({
micDeviceId: newState.selectedAudioInputId micDeviceId: newState.selectedAudioInputId,
userSelectedMicDeviceId: newState.selectedAudioInputId
})); }));
dispatch( dispatch(
@ -137,7 +139,8 @@ export function submitDeviceSelectionTab(newState) {
setAudioOutputDeviceId( setAudioOutputDeviceId(
newState.selectedAudioOutputId, newState.selectedAudioOutputId,
dispatch) dispatch,
true)
.then(() => logger.log('changed audio output device')) .then(() => logger.log('changed audio output device'))
.catch(err => { .catch(err => {
logger.warn( logger.warn(

View File

@ -26,20 +26,37 @@ import { toState } from '../base/redux';
export function getDeviceSelectionDialogProps(stateful: Object | Function) { export function getDeviceSelectionDialogProps(stateful: Object | Function) {
const state = toState(stateful); const state = toState(stateful);
const settings = state['features/base/settings']; const settings = state['features/base/settings'];
const { conference } = state['features/base/conference'];
let disableAudioInputChange = !JitsiMeetJS.mediaDevices.isMultipleAudioInputSupported();
let selectedAudioInputId = settings.micDeviceId;
let selectedAudioOutputId = getAudioOutputDeviceId();
let selectedVideoInputId = settings.cameraDeviceId;
// audio input change will be a problem only when we are in a
// conference and this is not supported, when we open device selection on
// welcome page changing input devices will not be a problem
// on welcome page we also show only what we have saved as user selected devices
if (!conference) {
disableAudioInputChange = false;
selectedAudioInputId = settings.userSelectedMicDeviceId;
selectedAudioOutputId = settings.userSelectedAudioOutputDeviceId;
selectedVideoInputId = settings.userSelectedCameraDeviceId;
}
// we fill the device selection dialog with the devices that are currently
// used or if none are currently used with what we have in settings(user selected)
return { return {
availableDevices: state['features/base/devices'].availableDevices, availableDevices: state['features/base/devices'].availableDevices,
disableAudioInputChange: disableAudioInputChange,
!JitsiMeetJS.isMultipleAudioInputSupported(),
disableDeviceChange: disableDeviceChange:
!JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(), !JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(),
hideAudioInputPreview: hideAudioInputPreview:
!JitsiMeetJS.isCollectingLocalStats(), !JitsiMeetJS.isCollectingLocalStats(),
hideAudioOutputSelect: !JitsiMeetJS.mediaDevices hideAudioOutputSelect: !JitsiMeetJS.mediaDevices
.isDeviceChangeAvailable('output'), .isDeviceChangeAvailable('output'),
selectedAudioInputId: settings.micDeviceId, selectedAudioInputId,
selectedAudioOutputId: getAudioOutputDeviceId(), selectedAudioOutputId,
selectedVideoInputId: settings.cameraDeviceId selectedVideoInputId
}; };
} }