diff --git a/openvidu-browser/src/OpenVidu/Filter.ts b/openvidu-browser/src/OpenVidu/Filter.ts index 5ae01648..48163b28 100644 --- a/openvidu-browser/src/OpenVidu/Filter.ts +++ b/openvidu-browser/src/OpenVidu/Filter.ts @@ -92,39 +92,67 @@ export class Filter { execMethod(method: string, params: Object): Promise { 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 = 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 = (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 = 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 = (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(); + } + } + ); + } }); } diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts index 967c39e4..6c6f52d8 100644 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ b/openvidu-browser/src/OpenVidu/Stream.ts @@ -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) { diff --git a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts b/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts index 11fe9334..eadfb0c8 100644 --- a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts +++ b/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts @@ -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 diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundChromaOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundChromaOptions.ts deleted file mode 100644 index 1dbc3d26..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundChromaOptions.ts +++ /dev/null @@ -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; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundImageOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundImageOptions.ts deleted file mode 100644 index 4924b617..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundImageOptions.ts +++ /dev/null @@ -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; -} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundOptions.ts deleted file mode 100644 index 7efa5759..00000000 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/VirtualBackgroundOptions.ts +++ /dev/null @@ -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; -}