feat(virtual-background) add slight blur option
This commit is contained in:
parent
927b40ec71
commit
af28080058
|
@ -1,6 +1,6 @@
|
||||||
.virtual-background-dialog {
|
.virtual-background-dialog {
|
||||||
display: inline-grid;
|
display: inline-grid;
|
||||||
grid-template-columns: auto auto auto auto auto auto auto;
|
grid-template-columns: auto auto auto auto auto auto auto auto;
|
||||||
max-width: 370px;
|
max-width: 370px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.thumbnail {
|
.thumbnail {
|
||||||
|
|
|
@ -339,7 +339,8 @@
|
||||||
},
|
},
|
||||||
"virtualBackground": {
|
"virtualBackground": {
|
||||||
"title": "Backgrounds",
|
"title": "Backgrounds",
|
||||||
"enableBlur": "Enable blur",
|
"blur": "Blur",
|
||||||
|
"slightBlur": "Slight Blur",
|
||||||
"removeBackground": "Remove background",
|
"removeBackground": "Remove background",
|
||||||
"uploadImage": "Upload image",
|
"uploadImage": "Upload image",
|
||||||
"pleaseWait": "Please wait...",
|
"pleaseWait": "Please wait...",
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
SET_TIMEOUT,
|
SET_TIMEOUT,
|
||||||
timerWorkerScript
|
timerWorkerScript
|
||||||
} from './TimerWorker';
|
} from './TimerWorker';
|
||||||
const blurValue = '25px';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a modified MediaStream that adds effects to video background.
|
* Represents a modified MediaStream that adds effects to video background.
|
||||||
|
@ -40,7 +39,7 @@ export default class JitsiStreamBackgroundEffect {
|
||||||
constructor(model: Object, options: Object) {
|
constructor(model: Object, options: Object) {
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
|
||||||
if (this._options.virtualBackground.isVirtualBackground) {
|
if (this._options.virtualBackground.backgroundType === 'image') {
|
||||||
this._virtualImage = document.createElement('img');
|
this._virtualImage = document.createElement('img');
|
||||||
this._virtualImage.crossOrigin = 'anonymous';
|
this._virtualImage.crossOrigin = 'anonymous';
|
||||||
this._virtualImage.src = this._options.virtualBackground.virtualSource;
|
this._virtualImage.src = this._options.virtualBackground.virtualSource;
|
||||||
|
@ -65,9 +64,9 @@ export default class JitsiStreamBackgroundEffect {
|
||||||
* @param {EventHandler} response - The onmessage EventHandler parameter.
|
* @param {EventHandler} response - The onmessage EventHandler parameter.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
async _onMaskFrameTimer(response: Object) {
|
_onMaskFrameTimer(response: Object) {
|
||||||
if (response.data.id === TIMEOUT_TICK) {
|
if (response.data.id === TIMEOUT_TICK) {
|
||||||
await this._renderMask();
|
this._renderMask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +82,7 @@ export default class JitsiStreamBackgroundEffect {
|
||||||
//
|
//
|
||||||
|
|
||||||
// Smooth out the edges.
|
// Smooth out the edges.
|
||||||
if (this._options.virtualBackground.isVirtualBackground) {
|
if (this._options.virtualBackground.backgroundType === 'image') {
|
||||||
this._outputCanvasCtx.filter = 'blur(4px)';
|
this._outputCanvasCtx.filter = 'blur(4px)';
|
||||||
} else {
|
} else {
|
||||||
this._outputCanvasCtx.filter = 'blur(8px)';
|
this._outputCanvasCtx.filter = 'blur(8px)';
|
||||||
|
@ -112,7 +111,7 @@ export default class JitsiStreamBackgroundEffect {
|
||||||
//
|
//
|
||||||
|
|
||||||
this._outputCanvasCtx.globalCompositeOperation = 'destination-over';
|
this._outputCanvasCtx.globalCompositeOperation = 'destination-over';
|
||||||
if (this._options.virtualBackground.isVirtualBackground) {
|
if (this._options.virtualBackground.backgroundType === 'image') {
|
||||||
this._outputCanvasCtx.drawImage(
|
this._outputCanvasCtx.drawImage(
|
||||||
this._virtualImage,
|
this._virtualImage,
|
||||||
0,
|
0,
|
||||||
|
@ -121,7 +120,7 @@ export default class JitsiStreamBackgroundEffect {
|
||||||
this._inputVideoElement.height
|
this._inputVideoElement.height
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this._outputCanvasCtx.filter = `blur(${blurValue})`;
|
this._outputCanvasCtx.filter = `blur(${this._options.virtualBackground.blurValue}px)`;
|
||||||
this._outputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
|
this._outputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: BACKGROUND_ENABLED,
|
* type: BACKGROUND_ENABLED,
|
||||||
* backgroundEffectEnabled: boolean,
|
* backgroundEffectEnabled: boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export const BACKGROUND_ENABLED = 'BACKGROUND_ENABLED';
|
export const BACKGROUND_ENABLED = 'BACKGROUND_ENABLED';
|
||||||
|
@ -16,8 +16,8 @@ export const BACKGROUND_ENABLED = 'BACKGROUND_ENABLED';
|
||||||
*
|
*
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: SET_VIRTUAL_BACKGROUND,
|
* type: SET_VIRTUAL_BACKGROUND,
|
||||||
* isVirtualBackground: boolean,
|
|
||||||
* virtualSource: string,
|
* virtualSource: string,
|
||||||
|
* blurValue: number,
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export const SET_VIRTUAL_BACKGROUND = 'SET_VIRTUAL_BACKGROUND';
|
export const SET_VIRTUAL_BACKGROUND = 'SET_VIRTUAL_BACKGROUND';
|
||||||
|
|
|
@ -9,20 +9,20 @@ import logger from './logger';
|
||||||
/**
|
/**
|
||||||
* Signals the local participant activate the virtual background video or not.
|
* Signals the local participant activate the virtual background video or not.
|
||||||
*
|
*
|
||||||
* @param {boolean} enabled - If true enables video background, false otherwise.
|
* @param {Object} options - Represents the virtual background setted options.
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
export function toggleBackgroundEffect(enabled: boolean) {
|
export function toggleBackgroundEffect(options: Object) {
|
||||||
return async function(dispatch: Object => Object, getState: () => any) {
|
return async function(dispatch: Object => Object, getState: () => any) {
|
||||||
|
await dispatch(backgroundEnabled(options.enabled));
|
||||||
|
await dispatch(setVirtualBackground(options));
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const { jitsiTrack } = getLocalVideoTrack(state['features/base/tracks']);
|
const { jitsiTrack } = getLocalVideoTrack(state['features/base/tracks']);
|
||||||
const virtualBackground = state['features/virtual-background'];
|
const virtualBackground = state['features/virtual-background'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (enabled) {
|
if (options.enabled) {
|
||||||
await jitsiTrack.setEffect(await createVirtualBackgroundEffect(virtualBackground));
|
await jitsiTrack.setEffect(await createVirtualBackgroundEffect(virtualBackground));
|
||||||
dispatch(backgroundEnabled(true));
|
|
||||||
} else {
|
} else {
|
||||||
await jitsiTrack.setEffect(undefined);
|
await jitsiTrack.setEffect(undefined);
|
||||||
dispatch(backgroundEnabled(false));
|
dispatch(backgroundEnabled(false));
|
||||||
|
@ -37,19 +37,20 @@ export function toggleBackgroundEffect(enabled: boolean) {
|
||||||
/**
|
/**
|
||||||
* Sets the selected virtual background image object.
|
* Sets the selected virtual background image object.
|
||||||
*
|
*
|
||||||
* @param {Object} virtualSource - Virtual background image source.
|
* @param {Object} options - Represents the virtual background setted options.
|
||||||
* @param {boolean} isVirtualBackground - Indicate if virtual image is activated.
|
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: SET_VIRTUAL_BACKGROUND,
|
* type: SET_VIRTUAL_BACKGROUND,
|
||||||
* virtualSource: string,
|
* virtualSource: string,
|
||||||
* isVirtualBackground: boolean,
|
* blurValue: number,
|
||||||
|
* type: string,
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function setVirtualBackground(virtualSource: string, isVirtualBackground: boolean) {
|
export function setVirtualBackground(options: Object) {
|
||||||
return {
|
return {
|
||||||
type: SET_VIRTUAL_BACKGROUND,
|
type: SET_VIRTUAL_BACKGROUND,
|
||||||
virtualSource,
|
virtualSource: options?.url,
|
||||||
isVirtualBackground
|
blurValue: options?.blurValue,
|
||||||
|
backgroundType: options?.backgroundType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ export function setVirtualBackground(virtualSource: string, isVirtualBackground:
|
||||||
* @param {boolean} backgroundEffectEnabled - Indicate if virtual background effect is activated.
|
* @param {boolean} backgroundEffectEnabled - Indicate if virtual background effect is activated.
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: BACKGROUND_ENABLED,
|
* type: BACKGROUND_ENABLED,
|
||||||
* backgroundEffectEnabled: boolean,
|
* backgroundEffectEnabled: boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function backgroundEnabled(backgroundEffectEnabled: boolean) {
|
export function backgroundEnabled(backgroundEffectEnabled: boolean) {
|
||||||
|
|
|
@ -10,14 +10,14 @@ import { translate } from '../../base/i18n';
|
||||||
import { Icon, IconBlurBackground, IconCancelSelection } from '../../base/icons';
|
import { Icon, IconBlurBackground, IconCancelSelection } from '../../base/icons';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
import { Tooltip } from '../../base/tooltip';
|
import { Tooltip } from '../../base/tooltip';
|
||||||
import { toggleBackgroundEffect, setVirtualBackground } from '../actions';
|
import { toggleBackgroundEffect } from '../actions';
|
||||||
import { resizeImage, toDataURL } from '../functions';
|
import { resizeImage, toDataURL } from '../functions';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
// The limit of virtual background uploads is 21. When the number
|
// The limit of virtual background uploads is 24. When the number
|
||||||
// of uploads is 22 we trigger the deleteStoredImage function to delete
|
// of uploads is 25 we trigger the deleteStoredImage function to delete
|
||||||
// the first/oldest uploaded background.
|
// the first/oldest uploaded background.
|
||||||
const backgroundsLimit = 22;
|
const backgroundsLimit = 25;
|
||||||
const images = [
|
const images = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
@ -67,42 +67,67 @@ function VirtualBackground({ dispatch, t }: Props) {
|
||||||
* Updates stored images on local storage.
|
* Updates stored images on local storage.
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
jitsiLocalStorage.setItem('virtualBackgrounds', JSON.stringify(storedImages));
|
try {
|
||||||
|
jitsiLocalStorage.setItem('virtualBackgrounds', JSON.stringify(storedImages));
|
||||||
|
} catch (err) {
|
||||||
|
// Preventing localStorage QUOTA_EXCEEDED_ERR
|
||||||
|
err && deleteStoredImage(storedImages[0]);
|
||||||
|
}
|
||||||
if (storedImages.length === backgroundsLimit) {
|
if (storedImages.length === backgroundsLimit) {
|
||||||
deleteStoredImage(storedImages[0]);
|
deleteStoredImage(storedImages[0]);
|
||||||
}
|
}
|
||||||
}, [ storedImages ]);
|
}, [ storedImages ]);
|
||||||
|
|
||||||
const [ selected, setSelected ] = useState('');
|
const [ selected, setSelected ] = useState('');
|
||||||
const enableBlur = async () => {
|
const enableBlur = async (blurValue, selection) => {
|
||||||
isloading(true);
|
isloading(true);
|
||||||
setSelected('blur');
|
setSelected(selection);
|
||||||
await dispatch(setVirtualBackground('', false));
|
await dispatch(
|
||||||
await dispatch(toggleBackgroundEffect(true));
|
toggleBackgroundEffect({
|
||||||
|
backgroundType: 'blur',
|
||||||
|
enabled: true,
|
||||||
|
blurValue
|
||||||
|
})
|
||||||
|
);
|
||||||
isloading(false);
|
isloading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeBackground = async () => {
|
const removeBackground = async () => {
|
||||||
isloading(true);
|
isloading(true);
|
||||||
setSelected('none');
|
setSelected('none');
|
||||||
await dispatch(setVirtualBackground('', false));
|
await dispatch(
|
||||||
await dispatch(toggleBackgroundEffect(false));
|
toggleBackgroundEffect({
|
||||||
|
enabled: false
|
||||||
|
})
|
||||||
|
);
|
||||||
isloading(false);
|
isloading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setUploadedImageBackground = async image => {
|
const setUploadedImageBackground = async image => {
|
||||||
isloading(true);
|
isloading(true);
|
||||||
setSelected(image.id);
|
setSelected(image.id);
|
||||||
await dispatch(setVirtualBackground(image.src, true));
|
await dispatch(
|
||||||
await dispatch(toggleBackgroundEffect(true));
|
toggleBackgroundEffect({
|
||||||
|
backgroundType: 'image',
|
||||||
|
enabled: true,
|
||||||
|
url: image.src
|
||||||
|
})
|
||||||
|
);
|
||||||
isloading(false);
|
isloading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setImageBackground = async image => {
|
const setImageBackground = async image => {
|
||||||
isloading(true);
|
isloading(true);
|
||||||
setSelected(image.id);
|
setSelected(image.id);
|
||||||
await dispatch(setVirtualBackground(await toDataURL(image.src), true));
|
const url = await toDataURL(image.src);
|
||||||
await dispatch(toggleBackgroundEffect(true));
|
|
||||||
|
await dispatch(
|
||||||
|
toggleBackgroundEffect({
|
||||||
|
backgroundType: 'image',
|
||||||
|
enabled: true,
|
||||||
|
url
|
||||||
|
})
|
||||||
|
);
|
||||||
isloading(false);
|
isloading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,19 +136,23 @@ function VirtualBackground({ dispatch, t }: Props) {
|
||||||
|
|
||||||
reader.readAsDataURL(imageFile[0]);
|
reader.readAsDataURL(imageFile[0]);
|
||||||
reader.onload = async () => {
|
reader.onload = async () => {
|
||||||
const resizedImage = await resizeImage(reader.result);
|
const url = await resizeImage(reader.result);
|
||||||
|
|
||||||
isloading(true);
|
isloading(true);
|
||||||
setStoredImages([
|
setStoredImages([
|
||||||
...storedImages,
|
...storedImages,
|
||||||
{
|
{
|
||||||
id: uuid.v4(),
|
id: uuid.v4(),
|
||||||
src: resizedImage
|
src: url
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
await dispatch(
|
||||||
await dispatch(setVirtualBackground(resizedImage, true));
|
toggleBackgroundEffect({
|
||||||
await dispatch(toggleBackgroundEffect(true));
|
backgroundType: 'image',
|
||||||
|
enabled: true,
|
||||||
|
url
|
||||||
|
})
|
||||||
|
);
|
||||||
isloading(false);
|
isloading(false);
|
||||||
};
|
};
|
||||||
reader.onerror = () => {
|
reader.onerror = () => {
|
||||||
|
@ -137,7 +166,7 @@ function VirtualBackground({ dispatch, t }: Props) {
|
||||||
hideCancelButton = { true }
|
hideCancelButton = { true }
|
||||||
submitDisabled = { false }
|
submitDisabled = { false }
|
||||||
titleKey = { 'virtualBackground.title' }
|
titleKey = { 'virtualBackground.title' }
|
||||||
width = 'small'>
|
width = '450px'>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className = 'virtual-background-loading'>
|
<div className = 'virtual-background-loading'>
|
||||||
<span className = 'loading-content-text'>{t('virtualBackground.pleaseWait')}</span>
|
<span className = 'loading-content-text'>{t('virtualBackground.pleaseWait')}</span>
|
||||||
|
@ -158,11 +187,20 @@ function VirtualBackground({ dispatch, t }: Props) {
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content = { t('virtualBackground.enableBlur') }
|
content = { t('virtualBackground.slightBlur') }
|
||||||
|
position = { 'top' }>
|
||||||
|
<Icon
|
||||||
|
className = { selected === 'slight-blur' ? 'blur-selected' : '' }
|
||||||
|
onClick = { () => enableBlur(8, 'slight-blur') }
|
||||||
|
size = { 50 }
|
||||||
|
src = { IconBlurBackground } />
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
content = { t('virtualBackground.blur') }
|
||||||
position = { 'top' }>
|
position = { 'top' }>
|
||||||
<Icon
|
<Icon
|
||||||
className = { selected === 'blur' ? 'blur-selected' : '' }
|
className = { selected === 'blur' ? 'blur-selected' : '' }
|
||||||
onClick = { () => enableBlur() }
|
onClick = { () => enableBlur(25, 'blur') }
|
||||||
size = { 50 }
|
size = { 50 }
|
||||||
src = { IconBlurBackground } />
|
src = { IconBlurBackground } />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
|
@ -23,14 +23,15 @@ PersistenceRegistry.register(STORE_NAME, true);
|
||||||
* specified action.
|
* specified action.
|
||||||
*/
|
*/
|
||||||
ReducerRegistry.register(STORE_NAME, (state = {}, action) => {
|
ReducerRegistry.register(STORE_NAME, (state = {}, action) => {
|
||||||
const { virtualSource, isVirtualBackground, backgroundEffectEnabled } = action;
|
const { virtualSource, backgroundEffectEnabled, blurValue, backgroundType } = action;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SET_VIRTUAL_BACKGROUND: {
|
case SET_VIRTUAL_BACKGROUND: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
virtualSource,
|
virtualSource,
|
||||||
isVirtualBackground
|
blurValue,
|
||||||
|
backgroundType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case BACKGROUND_ENABLED: {
|
case BACKGROUND_ENABLED: {
|
||||||
|
|
Loading…
Reference in New Issue