2022-09-13 10:36:00 +03:00
|
|
|
import { Theme } from '@mui/material';
|
|
|
|
|
import { withStyles } from '@mui/styles';
|
2021-05-06 09:54:23 +03:00
|
|
|
import React, { PureComponent } from 'react';
|
2022-08-25 14:35:19 +03:00
|
|
|
import { WithTranslation } from 'react-i18next';
|
2022-10-11 13:47:54 +03:00
|
|
|
import { connect } from 'react-redux';
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2022-10-20 12:11:27 +03:00
|
|
|
import { IReduxState } from '../../app/types';
|
2022-10-11 13:47:54 +03:00
|
|
|
import { hideDialog } from '../../base/dialog/actions';
|
|
|
|
|
import { translate } from '../../base/i18n/functions';
|
2023-04-04 17:08:59 +03:00
|
|
|
import { Video } from '../../base/media/components/index';
|
2022-10-11 13:47:54 +03:00
|
|
|
import { equals } from '../../base/redux/functions';
|
|
|
|
|
import { getCurrentCameraDeviceId } from '../../base/settings/functions.web';
|
2021-05-06 09:54:23 +03:00
|
|
|
import { createLocalTracksF } from '../../base/tracks/functions';
|
2023-03-10 15:35:55 +02:00
|
|
|
import Spinner from '../../base/ui/components/web/Spinner';
|
2021-08-26 16:33:43 +03:00
|
|
|
import { showWarningNotification } from '../../notifications/actions';
|
2022-09-06 20:32:20 +03:00
|
|
|
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
2021-05-06 09:54:23 +03:00
|
|
|
import { toggleBackgroundEffect } from '../actions';
|
2021-08-26 16:33:43 +03:00
|
|
|
import logger from '../logger';
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2021-07-08 20:20:12 +03:00
|
|
|
const videoClassName = 'video-preview-video';
|
2021-05-06 09:54:23 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The type of the React {@code PureComponent} props of {@link VirtualBackgroundPreview}.
|
|
|
|
|
*/
|
2022-11-03 10:35:51 +02:00
|
|
|
export interface IProps extends WithTranslation {
|
2021-05-06 09:54:23 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The deviceId of the camera device currently being used.
|
|
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
_currentCameraDeviceId: string;
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2022-03-29 13:37:55 +04:30
|
|
|
/**
|
|
|
|
|
* An object containing the CSS classes.
|
|
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
classes: any;
|
2022-03-29 13:37:55 +04:30
|
|
|
|
2021-05-06 09:54:23 +03:00
|
|
|
/**
|
|
|
|
|
* The redux {@code dispatch} function.
|
|
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
dispatch: Function;
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2021-08-26 16:33:43 +03:00
|
|
|
/**
|
|
|
|
|
* Dialog callback that indicates if the background preview was loaded.
|
|
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
loadedPreview: Function;
|
2021-08-26 16:33:43 +03:00
|
|
|
|
2021-05-06 09:54:23 +03:00
|
|
|
/**
|
2022-07-14 03:10:08 -04:00
|
|
|
* Represents the virtual background set options.
|
2021-05-06 09:54:23 +03:00
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
options: any;
|
2022-11-03 10:35:51 +02:00
|
|
|
}
|
2021-05-06 09:54:23 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The type of the React {@code Component} state of {@link VirtualBackgroundPreview}.
|
|
|
|
|
*/
|
2022-11-03 10:35:51 +02:00
|
|
|
interface IState {
|
2021-05-06 09:54:23 +03:00
|
|
|
|
|
|
|
|
/**
|
2022-08-25 14:35:19 +03:00
|
|
|
* Activate the selected device camera only.
|
2021-05-06 09:54:23 +03:00
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
jitsiTrack: Object | null;
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2021-08-26 16:33:43 +03:00
|
|
|
/**
|
2022-08-25 14:35:19 +03:00
|
|
|
* Loader activated on setting virtual background.
|
2021-08-26 16:33:43 +03:00
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
loading: boolean;
|
2021-08-26 16:33:43 +03:00
|
|
|
|
2021-05-06 09:54:23 +03:00
|
|
|
/**
|
2022-08-25 14:35:19 +03:00
|
|
|
* Flag that indicates if the local track was loaded.
|
2021-05-06 09:54:23 +03:00
|
|
|
*/
|
2022-09-08 12:52:36 +03:00
|
|
|
localTrackLoaded: boolean;
|
2022-11-03 10:35:51 +02:00
|
|
|
}
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2022-03-29 13:37:55 +04:30
|
|
|
/**
|
|
|
|
|
* Creates the styles for the component.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} theme - The current UI theme.
|
|
|
|
|
*
|
|
|
|
|
* @returns {Object}
|
|
|
|
|
*/
|
2022-09-13 10:36:00 +03:00
|
|
|
const styles = (theme: Theme) => {
|
2022-03-29 13:37:55 +04:30
|
|
|
return {
|
|
|
|
|
virtualBackgroundPreview: {
|
2023-03-08 13:15:07 +02:00
|
|
|
height: 'auto',
|
|
|
|
|
width: '100%',
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
marginBottom: theme.spacing(3),
|
|
|
|
|
zIndex: 2,
|
|
|
|
|
borderRadius: '3px',
|
|
|
|
|
backgroundColor: theme.palette.uiBackground,
|
|
|
|
|
position: 'relative' as const,
|
2022-03-29 13:37:55 +04:30
|
|
|
|
|
|
|
|
'& .video-preview-loader': {
|
2023-03-08 13:15:07 +02:00
|
|
|
height: '220px',
|
2022-03-29 13:37:55 +04:30
|
|
|
|
|
|
|
|
'& svg': {
|
2023-03-08 13:15:07 +02:00
|
|
|
position: 'absolute' as const,
|
2022-03-29 13:37:55 +04:30
|
|
|
top: '40%',
|
|
|
|
|
left: '45%'
|
|
|
|
|
}
|
2023-03-08 13:15:07 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
'& .video-preview-error': {
|
|
|
|
|
height: '220px',
|
|
|
|
|
position: 'relative'
|
2022-03-29 13:37:55 +04:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-06 09:54:23 +03:00
|
|
|
/**
|
|
|
|
|
* Implements a React {@link PureComponent} which displays the virtual
|
|
|
|
|
* background preview.
|
|
|
|
|
*
|
2021-11-04 22:10:43 +01:00
|
|
|
* @augments PureComponent
|
2021-05-06 09:54:23 +03:00
|
|
|
*/
|
2022-11-03 10:35:51 +02:00
|
|
|
class VirtualBackgroundPreview extends PureComponent<IProps, IState> {
|
2021-05-06 09:54:23 +03:00
|
|
|
_componentWasUnmounted: boolean;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initializes a new {@code VirtualBackgroundPreview} instance.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} props - The read-only properties with which the new
|
|
|
|
|
* instance is to be initialized.
|
|
|
|
|
*/
|
2022-11-03 10:35:51 +02:00
|
|
|
constructor(props: IProps) {
|
2021-05-06 09:54:23 +03:00
|
|
|
super(props);
|
|
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
|
loading: false,
|
2021-08-26 16:33:43 +03:00
|
|
|
localTrackLoaded: false,
|
2021-05-06 09:54:23 +03:00
|
|
|
jitsiTrack: null
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-12 12:36:49 +03:00
|
|
|
/**
|
|
|
|
|
* Destroys the jitsiTrack object.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} jitsiTrack - The track that needs to be disposed.
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
2022-08-25 14:35:19 +03:00
|
|
|
_stopStream(jitsiTrack: any) {
|
2021-05-12 12:36:49 +03:00
|
|
|
if (jitsiTrack) {
|
|
|
|
|
jitsiTrack.dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 09:54:23 +03:00
|
|
|
/**
|
|
|
|
|
* Creates and updates the track data.
|
|
|
|
|
*
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
async _setTracks() {
|
2021-08-26 16:33:43 +03:00
|
|
|
try {
|
|
|
|
|
this.setState({ loading: true });
|
|
|
|
|
const [ jitsiTrack ] = await createLocalTracksF({
|
|
|
|
|
cameraDeviceId: this.props._currentCameraDeviceId,
|
|
|
|
|
devices: [ 'video' ]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.setState({ localTrackLoaded: true });
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2021-08-26 16:33:43 +03:00
|
|
|
// In case the component gets unmounted before the tracks are created
|
|
|
|
|
// avoid a leak by not setting the state
|
|
|
|
|
if (this._componentWasUnmounted) {
|
|
|
|
|
this._stopStream(jitsiTrack);
|
2021-05-12 12:36:49 +03:00
|
|
|
|
2021-08-26 16:33:43 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.setState({
|
|
|
|
|
jitsiTrack,
|
|
|
|
|
loading: false
|
|
|
|
|
});
|
|
|
|
|
this.props.loadedPreview(true);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this.props.dispatch(hideDialog());
|
|
|
|
|
this.props.dispatch(
|
|
|
|
|
showWarningNotification({
|
|
|
|
|
titleKey: 'virtualBackground.backgroundEffectError',
|
|
|
|
|
description: 'Failed to access camera device.'
|
2021-11-24 13:05:27 +02:00
|
|
|
}, NOTIFICATION_TIMEOUT_TYPE.LONG)
|
2021-08-26 16:33:43 +03:00
|
|
|
);
|
|
|
|
|
logger.error('Failed to access camera device. Error on apply background effect.');
|
2021-05-12 12:36:49 +03:00
|
|
|
|
2021-05-06 09:54:23 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply background effect on video preview.
|
|
|
|
|
*
|
|
|
|
|
* @returns {Promise}
|
|
|
|
|
*/
|
|
|
|
|
async _applyBackgroundEffect() {
|
|
|
|
|
this.setState({ loading: true });
|
2021-08-26 16:33:43 +03:00
|
|
|
this.props.loadedPreview(false);
|
2021-05-06 09:54:23 +03:00
|
|
|
await this.props.dispatch(toggleBackgroundEffect(this.props.options, this.state.jitsiTrack));
|
2021-08-26 16:33:43 +03:00
|
|
|
this.props.loadedPreview(true);
|
2021-05-06 09:54:23 +03:00
|
|
|
this.setState({ loading: false });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply video preview loader.
|
|
|
|
|
*
|
|
|
|
|
* @returns {Promise}
|
|
|
|
|
*/
|
|
|
|
|
_loadVideoPreview() {
|
|
|
|
|
return (
|
|
|
|
|
<div className = 'video-preview-loader'>
|
2023-03-10 15:35:55 +02:00
|
|
|
<Spinner size = 'large' />
|
2021-05-06 09:54:23 +03:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Renders a preview entry.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} data - The track data.
|
|
|
|
|
* @returns {React$Node}
|
|
|
|
|
*/
|
2022-08-25 14:35:19 +03:00
|
|
|
_renderPreviewEntry(data: Object) {
|
2021-05-06 09:54:23 +03:00
|
|
|
const { t } = this.props;
|
|
|
|
|
|
|
|
|
|
if (this.state.loading) {
|
|
|
|
|
return this._loadVideoPreview();
|
|
|
|
|
}
|
|
|
|
|
if (!data) {
|
|
|
|
|
return (
|
2023-03-08 13:15:07 +02:00
|
|
|
<div className = 'video-preview-error'>{t('deviceSelection.previewUnavailable')}</div>
|
2021-05-06 09:54:23 +03:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2023-03-08 13:15:07 +02:00
|
|
|
<Video
|
|
|
|
|
className = { videoClassName }
|
|
|
|
|
playsinline = { true }
|
|
|
|
|
videoTrack = {{ jitsiTrack: data }} />
|
2021-05-06 09:54:23 +03:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements React's {@link Component#componentDidMount}.
|
|
|
|
|
*
|
|
|
|
|
* @inheritdoc
|
|
|
|
|
*/
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
this._setTracks();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements React's {@link Component#componentWillUnmount}.
|
|
|
|
|
*
|
|
|
|
|
* @inheritdoc
|
|
|
|
|
*/
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
|
this._componentWasUnmounted = true;
|
2021-05-12 12:36:49 +03:00
|
|
|
this._stopStream(this.state.jitsiTrack);
|
2021-05-06 09:54:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements React's {@link Component#componentDidUpdate}.
|
|
|
|
|
*
|
|
|
|
|
* @inheritdoc
|
|
|
|
|
*/
|
2022-11-03 10:35:51 +02:00
|
|
|
async componentDidUpdate(prevProps: IProps) {
|
2021-05-06 09:54:23 +03:00
|
|
|
if (!equals(this.props._currentCameraDeviceId, prevProps._currentCameraDeviceId)) {
|
|
|
|
|
this._setTracks();
|
|
|
|
|
}
|
2021-08-26 16:33:43 +03:00
|
|
|
if (!equals(this.props.options, prevProps.options) && this.state.localTrackLoaded) {
|
2021-05-06 09:54:23 +03:00
|
|
|
this._applyBackgroundEffect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements React's {@link Component#render}.
|
|
|
|
|
*
|
|
|
|
|
* @inheritdoc
|
|
|
|
|
*/
|
|
|
|
|
render() {
|
2022-03-29 14:14:23 +03:00
|
|
|
const { jitsiTrack } = this.state;
|
|
|
|
|
const { classes } = this.props;
|
2021-05-06 09:54:23 +03:00
|
|
|
|
2022-03-29 13:37:55 +04:30
|
|
|
return (<div className = { classes.virtualBackgroundPreview }>
|
|
|
|
|
{jitsiTrack
|
2023-03-08 13:15:07 +02:00
|
|
|
? this._renderPreviewEntry(jitsiTrack)
|
|
|
|
|
: this._loadVideoPreview()
|
2022-03-29 13:37:55 +04:30
|
|
|
}</div>);
|
2021-05-06 09:54:23 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Maps (parts of) the redux state to the associated props for the
|
|
|
|
|
* {@code VirtualBackgroundPreview} component.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} state - The Redux state.
|
|
|
|
|
* @private
|
|
|
|
|
* @returns {{Props}}
|
|
|
|
|
*/
|
2022-10-20 12:11:27 +03:00
|
|
|
function _mapStateToProps(state: IReduxState) {
|
2021-05-06 09:54:23 +03:00
|
|
|
return {
|
|
|
|
|
_currentCameraDeviceId: getCurrentCameraDeviceId(state)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-29 13:37:55 +04:30
|
|
|
export default translate(connect(_mapStateToProps)(withStyles(styles)(VirtualBackgroundPreview)));
|