fix(virtual-background) upload bkg image on poor connection
This commit is contained in:
parent
6c40329f6a
commit
eb4aefbca1
|
@ -386,7 +386,8 @@
|
||||||
"image7" : "Sunrise",
|
"image7" : "Sunrise",
|
||||||
"desktopShareError": "Could not create desktop share",
|
"desktopShareError": "Could not create desktop share",
|
||||||
"desktopShare":"Desktop share",
|
"desktopShare":"Desktop share",
|
||||||
"webAssemblyWarning": "WebAssembly not supported"
|
"webAssemblyWarning": "WebAssembly not supported",
|
||||||
|
"backgroundEffectError": "Failed to apply background effect."
|
||||||
},
|
},
|
||||||
"feedback": {
|
"feedback": {
|
||||||
"average": "Average",
|
"average": "Average",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { showWarningNotification } from '../../notifications/actions';
|
import { showWarningNotification } from '../../notifications/actions';
|
||||||
|
import { timeout } from '../../virtual-background/functions';
|
||||||
import logger from '../../virtual-background/logger';
|
import logger from '../../virtual-background/logger';
|
||||||
|
|
||||||
import JitsiStreamBackgroundEffect from './JitsiStreamBackgroundEffect';
|
import JitsiStreamBackgroundEffect from './JitsiStreamBackgroundEffect';
|
||||||
|
@ -44,17 +45,26 @@ export async function createVirtualBackgroundEffect(virtualBackground: Object, d
|
||||||
|
|
||||||
try {
|
try {
|
||||||
wasmCheck = require('wasm-check');
|
wasmCheck = require('wasm-check');
|
||||||
|
const tfliteTimeout = 10000;
|
||||||
|
|
||||||
if (wasmCheck?.feature?.simd) {
|
if (wasmCheck?.feature?.simd) {
|
||||||
tflite = await createTFLiteSIMDModule();
|
tflite = await timeout(tfliteTimeout, createTFLiteSIMDModule());
|
||||||
} else {
|
} else {
|
||||||
tflite = await createTFLiteModule();
|
tflite = await timeout(tfliteTimeout, createTFLiteModule());
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Looks like WebAssembly is disabled or not supported on this browser');
|
if (err?.message === '408') {
|
||||||
dispatch(showWarningNotification({
|
logger.error('Failed to download tflite model!');
|
||||||
titleKey: 'virtualBackground.webAssemblyWarning',
|
dispatch(showWarningNotification({
|
||||||
description: 'WebAssembly disabled or not supported by this browser'
|
titleKey: 'virtualBackground.backgroundEffectError'
|
||||||
}));
|
}));
|
||||||
|
} else {
|
||||||
|
logger.error('Looks like WebAssembly is disabled or not supported on this browser');
|
||||||
|
dispatch(showWarningNotification({
|
||||||
|
titleKey: 'virtualBackground.webAssemblyWarning',
|
||||||
|
description: 'WebAssembly disabled or not supported by this browser'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,7 @@ function VirtualBackground({
|
||||||
initialOptions,
|
initialOptions,
|
||||||
t
|
t
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const [ previewIsLoaded, setPreviewIsLoaded ] = useState(false);
|
||||||
const [ options, setOptions ] = useState({ ...initialOptions });
|
const [ options, setOptions ] = useState({ ...initialOptions });
|
||||||
const localImages = jitsiLocalStorage.getItem('virtualBackgrounds');
|
const localImages = jitsiLocalStorage.getItem('virtualBackgrounds');
|
||||||
const [ storedImages, setStoredImages ] = useState<Array<Image>>((localImages && Bourne.parse(localImages)) || []);
|
const [ storedImages, setStoredImages ] = useState<Array<Image>>((localImages && Bourne.parse(localImages)) || []);
|
||||||
|
@ -190,7 +191,6 @@ function VirtualBackground({
|
||||||
}
|
}
|
||||||
}, [ storedImages ]);
|
}, [ storedImages ]);
|
||||||
|
|
||||||
|
|
||||||
const enableBlur = useCallback(async () => {
|
const enableBlur = useCallback(async () => {
|
||||||
setOptions({
|
setOptions({
|
||||||
backgroundType: VIRTUAL_BACKGROUND_TYPE.BLUR,
|
backgroundType: VIRTUAL_BACKGROUND_TYPE.BLUR,
|
||||||
|
@ -425,15 +425,21 @@ function VirtualBackground({
|
||||||
dispatch(hideDialog());
|
dispatch(hideDialog());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const loadedPreviewState = useCallback(async loaded => {
|
||||||
|
await setPreviewIsLoaded(loaded);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
hideCancelButton = { false }
|
hideCancelButton = { false }
|
||||||
okKey = { 'virtualBackground.apply' }
|
okKey = { 'virtualBackground.apply' }
|
||||||
onCancel = { cancelVirtualBackground }
|
onCancel = { cancelVirtualBackground }
|
||||||
onSubmit = { applyVirtualBackground }
|
onSubmit = { applyVirtualBackground }
|
||||||
submitDisabled = { !options || loading }
|
submitDisabled = { !options || loading || !previewIsLoaded }
|
||||||
titleKey = { 'virtualBackground.title' } >
|
titleKey = { 'virtualBackground.title' } >
|
||||||
<VirtualBackgroundPreview options = { options } />
|
<VirtualBackgroundPreview
|
||||||
|
loadedPreview = { loadedPreviewState }
|
||||||
|
options = { options } />
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className = 'virtual-background-loading'>
|
<div className = 'virtual-background-loading'>
|
||||||
<Spinner
|
<Spinner
|
||||||
|
@ -442,7 +448,7 @@ function VirtualBackground({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<label
|
{previewIsLoaded && <label
|
||||||
aria-label = { t('virtualBackground.uploadImage') }
|
aria-label = { t('virtualBackground.uploadImage') }
|
||||||
className = 'file-upload-label'
|
className = 'file-upload-label'
|
||||||
htmlFor = 'file-upload'
|
htmlFor = 'file-upload'
|
||||||
|
@ -453,7 +459,7 @@ function VirtualBackground({
|
||||||
size = { 20 }
|
size = { 20 }
|
||||||
src = { IconPlusCircle } />
|
src = { IconPlusCircle } />
|
||||||
{t('virtualBackground.addBackground')}
|
{t('virtualBackground.addBackground')}
|
||||||
</label>
|
</label> }
|
||||||
<input
|
<input
|
||||||
accept = 'image/*'
|
accept = 'image/*'
|
||||||
className = 'file-upload-btn'
|
className = 'file-upload-btn'
|
||||||
|
|
|
@ -3,15 +3,18 @@
|
||||||
import Spinner from '@atlaskit/spinner';
|
import Spinner from '@atlaskit/spinner';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { hideDialog } from '../../base/dialog';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { VIDEO_TYPE } from '../../base/media';
|
import { VIDEO_TYPE } from '../../base/media';
|
||||||
import Video from '../../base/media/components/Video';
|
import Video from '../../base/media/components/Video';
|
||||||
import { connect, equals } from '../../base/redux';
|
import { connect, equals } from '../../base/redux';
|
||||||
import { getCurrentCameraDeviceId } from '../../base/settings';
|
import { getCurrentCameraDeviceId } from '../../base/settings';
|
||||||
import { createLocalTracksF } from '../../base/tracks/functions';
|
import { createLocalTracksF } from '../../base/tracks/functions';
|
||||||
|
import { showWarningNotification } from '../../notifications/actions';
|
||||||
import { toggleBackgroundEffect } from '../actions';
|
import { toggleBackgroundEffect } from '../actions';
|
||||||
import { VIRTUAL_BACKGROUND_TYPE } from '../constants';
|
import { VIRTUAL_BACKGROUND_TYPE } from '../constants';
|
||||||
import { localTrackStopped } from '../functions';
|
import { localTrackStopped } from '../functions';
|
||||||
|
import logger from '../logger';
|
||||||
|
|
||||||
const videoClassName = 'video-preview-video';
|
const videoClassName = 'video-preview-video';
|
||||||
|
|
||||||
|
@ -30,6 +33,11 @@ export type Props = {
|
||||||
*/
|
*/
|
||||||
dispatch: Function,
|
dispatch: Function,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog callback that indicates if the background preview was loaded.
|
||||||
|
*/
|
||||||
|
loadedPreview: Function,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the virtual background setted options.
|
* Represents the virtual background setted options.
|
||||||
*/
|
*/
|
||||||
|
@ -51,6 +59,11 @@ type State = {
|
||||||
*/
|
*/
|
||||||
loading: boolean,
|
loading: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag that indicates if the local track was loaded.
|
||||||
|
*/
|
||||||
|
localTrackLoaded: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate the selected device camera only.
|
* Activate the selected device camera only.
|
||||||
*/
|
*/
|
||||||
|
@ -77,6 +90,7 @@ class VirtualBackgroundPreview extends PureComponent<Props, State> {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
localTrackLoaded: false,
|
||||||
jitsiTrack: null
|
jitsiTrack: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -99,24 +113,42 @@ class VirtualBackgroundPreview extends PureComponent<Props, State> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
async _setTracks() {
|
async _setTracks() {
|
||||||
const [ jitsiTrack ] = await createLocalTracksF({
|
try {
|
||||||
cameraDeviceId: this.props._currentCameraDeviceId,
|
this.setState({ loading: true });
|
||||||
devices: [ 'video' ]
|
const [ jitsiTrack ] = await createLocalTracksF({
|
||||||
});
|
cameraDeviceId: this.props._currentCameraDeviceId,
|
||||||
|
devices: [ 'video' ]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({ localTrackLoaded: true });
|
||||||
|
|
||||||
// In case the component gets unmounted before the tracks are created
|
// In case the component gets unmounted before the tracks are created
|
||||||
// avoid a leak by not setting the state
|
// avoid a leak by not setting the state
|
||||||
if (this._componentWasUnmounted) {
|
if (this._componentWasUnmounted) {
|
||||||
this._stopStream(jitsiTrack);
|
this._stopStream(jitsiTrack);
|
||||||
|
|
||||||
|
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.'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
logger.error('Failed to access camera device. Error on apply background effect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({
|
|
||||||
jitsiTrack
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.props.options.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE) {
|
if (this.props.options.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE
|
||||||
|
&& this.state.localTrackLoaded) {
|
||||||
this._applyBackgroundEffect();
|
this._applyBackgroundEffect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +160,9 @@ class VirtualBackgroundPreview extends PureComponent<Props, State> {
|
||||||
*/
|
*/
|
||||||
async _applyBackgroundEffect() {
|
async _applyBackgroundEffect() {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
this.props.loadedPreview(false);
|
||||||
await this.props.dispatch(toggleBackgroundEffect(this.props.options, this.state.jitsiTrack));
|
await this.props.dispatch(toggleBackgroundEffect(this.props.options, this.state.jitsiTrack));
|
||||||
|
this.props.loadedPreview(true);
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +246,7 @@ class VirtualBackgroundPreview extends PureComponent<Props, State> {
|
||||||
if (!equals(this.props._currentCameraDeviceId, prevProps._currentCameraDeviceId)) {
|
if (!equals(this.props._currentCameraDeviceId, prevProps._currentCameraDeviceId)) {
|
||||||
this._setTracks();
|
this._setTracks();
|
||||||
}
|
}
|
||||||
if (!equals(this.props.options, prevProps.options)) {
|
if (!equals(this.props.options, prevProps.options) && this.state.localTrackLoaded) {
|
||||||
if (prevProps.options.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE) {
|
if (prevProps.options.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE) {
|
||||||
prevProps.options.url.dispose();
|
prevProps.options.url.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,3 +118,25 @@ export function localTrackStopped(dispatch: Function, desktopTrack: Object, curr
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creating a wrapper for promises on a specific time interval.
|
||||||
|
*
|
||||||
|
* @param {number} milliseconds - The number of milliseconds to wait the specified
|
||||||
|
* {@code promise} to settle before automatically rejecting the returned
|
||||||
|
* {@code Promise}.
|
||||||
|
* @param {Promise} promise - The {@code Promise} for which automatic rejecting
|
||||||
|
* after the specified timeout is to be implemented.
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function timeout(milliseconds: number, promise: Promise<*>): Promise<Object> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
reject(new Error('408'));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}, milliseconds);
|
||||||
|
|
||||||
|
promise.then(resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue