openvidu-browser: restrict updated values of VB to background image URL

pull/715/head
pabloFuente 2022-04-21 12:36:25 +02:00
parent 34be4d8c13
commit 5826032020
6 changed files with 94 additions and 179 deletions

View File

@ -92,39 +92,67 @@ export class Filter {
execMethod(method: string, params: Object): Promise<void> {
return new Promise((resolve, reject) => {
logger.info('Executing filter method to stream ' + this.stream.streamId);
let stringParams;
if (typeof params !== 'string') {
try {
stringParams = JSON.stringify(params);
} catch (error) {
const errorMsg = "'params' property must be a JSON formatted object";
logger.error(errorMsg);
return reject(errorMsg);
}
} else {
stringParams = <string>params;
}
this.stream.session.openvidu.sendRequest(
'execFilterMethod',
{ streamId: this.stream.streamId, method, params: stringParams },
(error, response) => {
if (error) {
logger.error('Error executing filter method for Stream ' + this.stream.streamId, error);
if (error.code === 401) {
return reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to execute a filter method"));
} else {
return reject(error);
}
} else {
logger.info('Filter method successfully executed on Stream ' + this.stream.streamId);
const oldValue = (<any>Object).assign({}, this.stream.filter);
this.stream.filter!.lastExecMethod = { method, params: JSON.parse(stringParams) };
this.stream.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.session, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
this.stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.streamManager, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
return resolve();
if (this.type.startsWith('VB:')) {
if (typeof params === 'string') {
try {
params = JSON.parse(params);
} catch (error) {
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'Wrong params syntax: ' + error));
}
}
);
if (method === 'update') {
if (!this.stream.virtualBackgroundSinkElements?.VB) {
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'There is no Virtual Background filter applied'));
} else {
try {
this.stream.virtualBackgroundSinkElements.VB.updateValues(params);
} catch (error) {
reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'Error updating values on Virtual Background filter: ' + error));
}
}
} else {
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, `Unknown Virtual Background method "${method}"`));
}
} else {
let stringParams;
if (typeof params !== 'string') {
try {
stringParams = JSON.stringify(params);
} catch (error) {
const errorMsg = "'params' property must be a JSON formatted object";
logger.error(errorMsg);
return reject(errorMsg);
}
} else {
stringParams = <string>params;
}
this.stream.session.openvidu.sendRequest(
'execFilterMethod',
{ streamId: this.stream.streamId, method, params: stringParams },
(error, response) => {
if (error) {
logger.error('Error executing filter method for Stream ' + this.stream.streamId, error);
if (error.code === 401) {
return reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to execute a filter method"));
} else {
return reject(error);
}
} else {
logger.info('Filter method successfully executed on Stream ' + this.stream.streamId);
const oldValue = (<any>Object).assign({}, this.stream.filter);
this.stream.filter!.lastExecMethod = { method, params: JSON.parse(stringParams) };
this.stream.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.session, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
this.stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.streamManager, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
return resolve();
}
}
);
}
});
}

View File

@ -21,9 +21,6 @@ import { Publisher } from './Publisher';
import { Session } from './Session';
import { StreamManager } from './StreamManager';
import { Subscriber } from './Subscriber';
import { VirtualBackgroundOptions } from '../OpenViduInternal/Interfaces/Public/VirtualBackgroundOptions';
import { VirtualBackgroundImageOptions } from '../OpenViduInternal/Interfaces/Public/VirtualBackgroundImageOptions';
import { VirtualBackgroundChromaOptions } from '../OpenViduInternal/Interfaces/Public/VirtualBackgroundChromaOptions';
import { InboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/InboundStreamOptions';
import { OutboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/OutboundStreamOptions';
import { WebRtcPeer, WebRtcPeerSendonly, WebRtcPeerRecvonly, WebRtcPeerSendrecv, WebRtcPeerConfiguration } from '../OpenViduInternal/WebRtcPeer/WebRtcPeer';
@ -157,8 +154,11 @@ export class Stream {
private isSubscribeToRemote = false;
private virtualBackgroundSourceElements: { videoClone: HTMLVideoElement, mediaStreamClone: MediaStream };
private virtualBackgroundSinkElements: { VB: any, video: HTMLVideoElement, canvas: HTMLCanvasElement };
private virtualBackgroundSourceElements?: { videoClone: HTMLVideoElement, mediaStreamClone: MediaStream };
/**
* @hidden
*/
virtualBackgroundSinkElements?: { VB: any, video: HTMLVideoElement, canvas: HTMLCanvasElement };
/**
* @hidden
@ -346,13 +346,16 @@ export class Stream {
return reject(this.session.notConnectedError());
}
if (!this.session.openvidu.isAtLeastPro) {
return reject(new OpenViduError(OpenViduErrorName.OPENVIDU_EDITION_NOT_SUPPORTED, 'OpenVidu Virtual Background API is available from OpenVidu Pro edition onwards'));
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'OpenVidu Virtual Background API is available from OpenVidu Pro edition onwards'));
}
if (!this.hasVideo) {
return reject(new OpenViduError(OpenViduErrorName.NO_VIDEO_TRACK, 'The Virtual Background filter requires a video track to be applied'));
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'The Virtual Background filter requires a video track to be applied'));
}
if (!this.mediaStream || this.streamManager.videos.length === 0) {
return reject(new OpenViduError(OpenViduErrorName.STREAM_MANAGER_HAS_NO_VIDEO_ELEMENT, 'The StreamManager requires some video element to be attached to it in order to apply a Virtual Background filter'));
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'The StreamManager requires some video element to be attached to it in order to apply a Virtual Background filter'));
}
if (!!this.virtualBackgroundSinkElements && !!this.virtualBackgroundSourceElements) {
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'There is already a Virtual Background filter applied to Stream ' + this.streamId));
}
logger.info('Applying Virtual Background to stream ' + this.streamId);
@ -379,25 +382,17 @@ export class Stream {
openviduServerUrl: new URL(this.session.openvidu.httpUri),
inputVideo: videoClone,
inputResolution: '160x96',
outputFramerate: 30
outputFramerate: 24
});
let response: { video: HTMLVideoElement, canvas: HTMLCanvasElement };
switch (type) {
case 'VB:blur': {
const optionsVB = options as VirtualBackgroundOptions;
response = await VB.backgroundBlur(optionsVB);
response = await VB.backgroundBlur(options);
break;
}
case 'VB:image': {
const optionsVB = options as VirtualBackgroundImageOptions;
response = await VB.backgroundImage(optionsVB);
break;
}
case 'VB:chroma': {
const optionsVB = options as VirtualBackgroundChromaOptions;
response = await VB.backgroundChroma(optionsVB);
response = await VB.backgroundImage(options);
break;
}
default:
@ -417,7 +412,11 @@ export class Stream {
resolveApplyFilter(undefined);
} catch (error) {
resolveApplyFilter(error);
if (error.name === OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR) {
resolveApplyFilter(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, error.message));
} else {
resolveApplyFilter(error);
}
}
}
@ -427,8 +426,12 @@ export class Stream {
script.type = "text/javascript";
script.src = this.session.openvidu.httpUri + '/virtual-background/openvidu-virtual-background.js';
script.onload = async () => {
await afterScriptLoaded();
resolve(new Filter(type, options));
try {
await afterScriptLoaded();
resolve(new Filter(type, options));
} catch (error) {
reject(error);
}
};
document.body.appendChild(script);
} else {
@ -496,20 +499,23 @@ export class Stream {
try {
this.virtualBackgroundSinkElements.VB.cleanUp();
const parent = this.virtualBackgroundSourceElements.videoClone.parentElement;
this.virtualBackgroundSourceElements.videoClone.remove();
this.virtualBackgroundSinkElements!.VB.cleanUp();
const parent = this.virtualBackgroundSourceElements!.videoClone.parentElement;
this.virtualBackgroundSourceElements!.videoClone.remove();
if (parent!.children.length === 0) {
// @ts-ignore
VirtualBackground.VirtualBackground.removeHiddenContainer();
}
if (this.streamManager.remote) {
await this.streamManager.replaceTrackInMediaStream(this.virtualBackgroundSourceElements.mediaStreamClone.getVideoTracks()[0]);
await this.streamManager.replaceTrackInMediaStream(this.virtualBackgroundSourceElements!.mediaStreamClone.getVideoTracks()[0]);
} else {
await (this.streamManager as Publisher).replaceTrack(this.virtualBackgroundSourceElements.mediaStreamClone.getVideoTracks()[0]);
await (this.streamManager as Publisher).replaceTrack(this.virtualBackgroundSourceElements!.mediaStreamClone.getVideoTracks()[0]);
}
delete this.virtualBackgroundSinkElements;
delete this.virtualBackgroundSourceElements;
return resolveRemoveFilter(undefined);
} catch (error) {

View File

@ -108,19 +108,9 @@ export enum OpenViduErrorName {
OPENVIDU_NOT_CONNECTED = 'OPENVIDU_NOT_CONNECTED',
/**
* The action performed is not supported for this OpenVidu edition.
* Error related to [Virtual Background](/en/stable/advanced-features/virtual-background/)
*/
OPENVIDU_EDITION_NOT_SUPPORTED = 'OPENVIDU_EDITION_NOT_SUPPORTED',
/**
* The action performed requires a video track to be present.
*/
NO_VIDEO_TRACK = 'NO_VIDEO_TRACK',
/**
* The action performed requires some video element to be attached to the [[StreamManager]].
*/
STREAM_MANAGER_HAS_NO_VIDEO_ELEMENT = 'STREAM_MANAGER_HAS_NO_VIDEO_ELEMENT',
VIRTUAL_BACKGROUND_ERROR = 'VIRTUAL_BACKGROUND_ERROR',
/**
* Generic error

View File

@ -1,40 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { VirtualBackgroundImageOptions } from './VirtualBackgroundImageOptions';
/**
* Options to apply to a Virtual Background Image filter. See [[Stream.applyFilter]]
*/
export interface VirtualBackgroundChromaOptions extends VirtualBackgroundImageOptions {
/**
* H component (Hue) range for the HSV chroma color. A pixel color must be inside this range to be replaced by the chroma filter
*/
chromaHRange: [number, number];
/**
* S component (Saturation) range for the HSV chroma color. A pixel color must be inside this range to be replaced by the chroma filter
*/
chromaSRange: [number, number];
/**
* V component (Value) range for the HSV chroma color. A pixel color must be inside this range to be replaced by the chroma filter
*/
chromaVRange: [number, number];
/**
* Whether to automatically detect the most probable chroma color or not
*/
chromaAuto: boolean;
}

View File

@ -1,28 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { VirtualBackgroundOptions } from './VirtualBackgroundOptions';
/**
* Options to apply to a Virtual Background Image filter. See [[Stream.applyFilter]]
*/
export interface VirtualBackgroundImageOptions extends VirtualBackgroundOptions {
/**
* URL to the image asset to be used as background
*/
url: string;
}

View File

@ -1,41 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* Options to apply to a Virtual Background filter. See [[Stream.applyFilter]]
*/
export interface VirtualBackgroundOptions {
/**
* Radius of the effect. Higher values mean less defined edges but a smoother transition between the person's mask and
* the background. Number between [0, 1] with 2 decimals
*/
maskRadius?: number;
/**
* Amplitude of the space between the person's mask and the background. Higher values mean the effect will be applied
* more tightly to the person's mask, but this may cause loss of pixel information of the person. Lower values mean the
* effect will be applied further from the person's mask, granting a full view of the person but at the cost of the accuracy
* of the person's mask. Number between [0, 1] with 2 decimals
*/
backgroundCoverage?: number;
/**
* Blends the background with the person's mask with a light effect. Higher values mean a more aggressive light blending
* Number between [0, 1] with 2 decimals
*/
lightWrapping?: number;
}