Skip to content

Commit

Permalink
feat(setup) select preferred resolution (#1032)
Browse files Browse the repository at this point in the history
  • Loading branch information
mihhu authored Sep 6, 2023
1 parent 644ceb6 commit d0c190f
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 4 deletions.
2 changes: 2 additions & 0 deletions spot-client/src/common/app-state/setup/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const SET_JWT = 'SET_JWT_TOKEN';

export const SET_PREFERRED_DEVICES = 'SET_PREFERRED_DEVICES';

export const SET_PREFERRED_RESOLUTION = 'SET_PREFERRED_RESOLUTION';

export const SET_ROOM_ID = 'SET_ROOM_ID';

export const SET_TENANT = 'SET_TENANT';
14 changes: 14 additions & 0 deletions spot-client/src/common/app-state/setup/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SET_IS_SPOT,
SET_JWT,
SET_PREFERRED_DEVICES,
SET_PREFERRED_RESOLUTION,
SET_ROOM_ID,
SET_TENANT
} from './action-types';
Expand Down Expand Up @@ -111,6 +112,19 @@ export function setPreferredDevices(cameraLabel, micLabel, speakerLabel) {
};
}

/**
* Stores the video resolution that should be used when video conferencing.
*
* @param {string} resolution - The resolution value.
* @returns {Object}
*/
export function setPreferredResolution(resolution) {
return {
type: SET_PREFERRED_RESOLUTION,
resolution
};
}

/**
* Sets the room ID provided by the backend.
*
Expand Down
8 changes: 7 additions & 1 deletion spot-client/src/common/app-state/setup/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SET_IS_SPOT,
SET_JWT,
SET_PREFERRED_DEVICES,
SET_PREFERRED_RESOLUTION,
SET_TENANT
} from './action-types';

Expand All @@ -17,6 +18,7 @@ const DEFAULT_STATE = {
isSpot: false,
preferredCamera: undefined,
preferredMic: undefined,
preferredResolution: undefined,
preferredSpeaker: undefined,
startParams: {}
};
Expand Down Expand Up @@ -74,7 +76,11 @@ const setup = (state = DEFAULT_STATE, action) => {
preferredMic: action.micLabel,
preferredSpeaker: action.speakerLabel
};

case SET_PREFERRED_RESOLUTION:
return {
...state,
preferredResolution: action.resolution
};
case SET_TENANT:
return {
...state,
Expand Down
11 changes: 11 additions & 0 deletions spot-client/src/common/app-state/setup/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ export function getPreferredCamera(state) {
return state.setup.preferredCamera;
}

/**
* A selector which returns the resolution that should be
* used to compute video constraints.
*
* @param {Object} state - The Redux state.
* @returns {string}
*/
export function getPreferredResolution(state) {
return state.setup.preferredResolution;
}

/**
* A selector which returns the label for the microphone device that should be
* attempted to be used when starting a call.
Expand Down
12 changes: 12 additions & 0 deletions spot-client/src/common/app-state/setup/setup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,16 @@ describe('setup state', () => {
expect(selectors.getPreferredMic(state)).toEqual(micLabel);
expect(selectors.getPreferredSpeaker(state)).toEqual(speakerLabel);
});

it('saves preferred resolution', () => {
const resolution = '1080';

dispatch(actions.setPreferredResolution(
resolution
));

const state = getState();

expect(selectors.getPreferredResolution(state)).toEqual(resolution);
});
});
4 changes: 4 additions & 0 deletions spot-client/src/common/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@
"pairAsk": "Would you like to pair a permanent remote control with this room?",
"preview": "Preview",
"profile": "Room settings",
"resolutionLabel": "Preferred resolution",
"resolutionHighDef": "HD",
"resolutionFullHighDef": "Full HD",
"resolutionUltraHighDef": "4K",
"roomByEmail": "Enter an email:",
"roomSelect": "Select A Room",
"screenShare": "Screen share",
Expand Down
34 changes: 34 additions & 0 deletions spot-client/src/common/utils/meeting.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { parseURIString } from '@jitsi/js-utils/uri';

const ASPECT_RATIO = 16 / 9;

const MIN_HEIGHT = 180;

/**
* Extrapolates a url for a meeting from given strings and matching domains.
*
Expand Down Expand Up @@ -129,3 +133,33 @@ export function parseMeetingUrl(url) {
path: pathParts.join('/')
};
}

/**
* Builds the video constraints based on the preferred aspect ratio and resolution.
*
* @param {string} preferredResolution - The resolution of choice.
* @returns {Object}
*/
export const getVideoConstraints = preferredResolution => {
const resolution = Number(preferredResolution);

return {
video: {
aspectRatio: ASPECT_RATIO,
height: {
ideal: resolution,
max: resolution,
min: MIN_HEIGHT
},
width: {
ideal: resolution * ASPECT_RATIO,
max: resolution * ASPECT_RATIO,
min: MIN_HEIGHT * ASPECT_RATIO
},
frameRate: {
max: 30,
min: 15
}
}
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default class AbstractMeetingFrame extends React.Component {
onMeetingStart: PropTypes.func,
preferredCamera: PropTypes.string,
preferredMic: PropTypes.string,
preferredResolution: PropTypes.string,
preferredSpeaker: PropTypes.string,
remoteControlServer: PropTypes.object,
screenshareDevice: PropTypes.string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from 'common/app-state';
import { logger } from 'common/logger';
import { COMMANDS, MESSAGES } from 'common/remote-control';
import { parseMeetingUrl } from 'common/utils';
import { getVideoConstraints, parseMeetingUrl } from 'common/utils';
import bindAll from 'lodash.bindall';
import React from 'react';
import { connect } from 'react-redux';
Expand Down Expand Up @@ -129,6 +129,9 @@ export class JitsiMeetingFrame extends AbstractMeetingFrame {
this._jitsiApi = new JitsiMeetExternalAPI(`${host}${path}`, {
configOverwrite: {
_desktopSharingSourceDevice: this.props.screenshareDevice,
...Boolean(this.props.preferredResolution) && {
constraints: getVideoConstraints(this.props.preferredResolution)
},
desktopSharingFrameRate: {
max: this.props.maxDesktopSharingFramerate,
min: this.props.minDesktopSharingFramerate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import {
addNotification,
getPreferredCamera,
getPreferredMic,
getPreferredResolution,
getPreferredSpeaker,
getWiredScreenshareInputLabel,
setPreferredDevices,
setPreferredResolution,
setSpotTVState,
setWiredScreenshareInputIdleValue,
setWiredScreenshareInputLabel
Expand All @@ -17,7 +19,6 @@ import React from 'react';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';


import { wiredScreenshareService } from './../../../../wired-screenshare-service';
import CameraPreview from './camera-preview';
import MediaSelector from './media-selector';
Expand All @@ -43,6 +44,7 @@ class SelectMedia extends React.Component {
onSuccess: PropTypes.func,
preferredCamera: PropTypes.string,
preferredMic: PropTypes.string,
preferredResolution: PropTypes.string,
preferredScreenshareDongle: PropTypes.string,
preferredSpeaker: PropTypes.string,
t: PropTypes.func
Expand All @@ -62,13 +64,15 @@ class SelectMedia extends React.Component {
saving: false,
selectedCamera: props.preferredCamera,
selectedMic: props.preferredMic,
selectedResolution: props.preferredResolution,
selectedScreenshareDongle: props.preferredScreenshareDongle,
selectedSpeaker: props.preferredSpeaker
};

this._onCameraChange = this._onCameraChange.bind(this);
this._onDeviceListChange = this._onDeviceListChange.bind(this);
this._onMicChange = this._onMicChange.bind(this);
this._onResolutionChange = this._onResolutionChange.bind(this);
this._onScreenshareChange = this._onScreenshareChange.bind(this);
this._onSkip = this._onSkip.bind(this);
this._onSpeakerChange = this._onSpeakerChange.bind(this);
Expand Down Expand Up @@ -106,10 +110,12 @@ class SelectMedia extends React.Component {
const {
cameras,
mics,
resolutions,
saving,
screenshareDongles,
selectedCamera,
selectedMic,
selectedResolution,
selectedScreenshareDongle,
selectedSpeaker,
speakers
Expand All @@ -126,6 +132,15 @@ class SelectMedia extends React.Component {
onChange = { this._onCameraChange }
type = 'camera' />
);
const resolutionSelect = (
<MediaSelector
device = { selectedResolution }
devices = { resolutions }
key = 'resolution'
label = { t('setup.resolutionLabel') }
onChange = { this._onResolutionChange }
type = 'resolution' />
);
const micSelect = (
<MediaSelector
device = { selectedMic }
Expand Down Expand Up @@ -164,6 +179,7 @@ class SelectMedia extends React.Component {
<div className = 'columns'>
<div className = 'column'>
{ cameraSelect }
{ resolutionSelect }
{ micSelect }
{ speakerSelect }
{ screenshareSelect }
Expand Down Expand Up @@ -235,6 +251,21 @@ class SelectMedia extends React.Component {
_getDefaultDeviceListState() {
return {
cameras: [],
resolutions: [
{
label: this.props.t('setup.noSelection'),
value: ''
}, {
label: this.props.t('setup.resolutionHighDef'),
value: '720'
}, {
label: this.props.t('setup.resolutionFullHighDef'),
value: '1080'
}, {
label: this.props.t('setup.resolutionUltraHighDef'),
value: '2160'
}
],
mics: [],
screenshareDongles: [
{
Expand Down Expand Up @@ -327,6 +358,19 @@ class SelectMedia extends React.Component {
this.setState(newDeviceLists);
}

/**
* Callback invoked when the selected resolution has changed.
*
* @param {string} resolution - The selected resolution.
* @private
* @returns {void}
*/
_onResolutionChange(resolution) {
this.setState({
selectedResolution: resolution
});
}

/**
* Callback invoked when the selected mic (audioinput) has changed.
*
Expand Down Expand Up @@ -389,6 +433,7 @@ class SelectMedia extends React.Component {
const {
selectedCamera,
selectedMic,
selectedResolution,
selectedScreenshareDongle,
selectedSpeaker
} = this.state;
Expand Down Expand Up @@ -423,13 +468,17 @@ class SelectMedia extends React.Component {
selectedSpeaker
));

this.props.dispatch(setPreferredResolution(
selectedResolution
));

this.props.dispatch(setSpotTVState({
wiredScreensharingEnabled: Boolean(selectedScreenshareDongle)
}));
})
.then(() => this.props.onSuccess())
.catch(error => {
logger.error('Error while saving preferred devices', { error });
logger.error('Error while saving preferences', { error });

this.props.dispatch(addNotification('error', 'appEvents.devicesNotSaved'));

Expand All @@ -450,6 +499,7 @@ function mapStateToProps(state) {
return {
preferredCamera: getPreferredCamera(state),
preferredMic: getPreferredMic(state),
preferredResolution: getPreferredResolution(state),
preferredScreenshareDongle: getWiredScreenshareInputLabel(state),
preferredSpeaker: getPreferredSpeaker(state)
};
Expand Down
5 changes: 5 additions & 0 deletions spot-client/src/spot-tv/ui/views/meeting.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getMeetingJoinTimeout,
getPreferredCamera,
getPreferredMic,
getPreferredResolution,
getPreferredSpeaker,
getWiredScreenshareInputLabel,
leaveMeetingWithError,
Expand Down Expand Up @@ -57,6 +58,7 @@ export class Meeting extends React.Component {
onError: PropTypes.func,
preferredCamera: PropTypes.string,
preferredMic: PropTypes.string,
preferredResolution: PropTypes.string,
preferredSpeaker: PropTypes.string,
processMeetingSummary: PropTypes.func,
remoteControlServer: PropTypes.object,
Expand Down Expand Up @@ -141,6 +143,7 @@ export class Meeting extends React.Component {
minDesktopSharingFramerate,
preferredCamera,
preferredMic,
preferredResolution,
preferredSpeaker,
remoteControlServer,
screenshareDevice,
Expand Down Expand Up @@ -170,6 +173,7 @@ export class Meeting extends React.Component {
onMeetingStart = { this._onMeetingStart }
preferredCamera = { preferredCamera }
preferredMic = { preferredMic }
preferredResolution = { preferredResolution }
preferredSpeaker = { preferredSpeaker }
remoteControlServer = { remoteControlServer }
screenshareDevice = { screenshareDevice }
Expand Down Expand Up @@ -350,6 +354,7 @@ function mapStateToProps(state) {
showPasswordPrompt: needPassword,
preferredCamera: getPreferredCamera(state),
preferredMic: getPreferredMic(state),
preferredResolution: getPreferredResolution(state),
preferredSpeaker: getPreferredSpeaker(state),
screenshareDevice: getWiredScreenshareInputLabel(state),
waitingForMeetingStart
Expand Down

0 comments on commit d0c190f

Please sign in to comment.