From ccd8d7a8b9254638bc1aba48c14fa57920b5e254 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Fri, 27 Jul 2018 14:32:53 +0200 Subject: [PATCH] openvidu-browser: filter support --- openvidu-browser/src/OpenVidu/Connection.ts | 3 +- openvidu-browser/src/OpenVidu/OpenVidu.ts | 3 +- openvidu-browser/src/OpenVidu/Session.ts | 97 +++++++++++++++++++ openvidu-browser/src/OpenVidu/Stream.ts | 27 +++++- .../Private/InboundStreamOptions.ts | 2 + .../Interfaces/Private/StreamOptionsServer.ts | 3 + .../Interfaces/Public/Filter.ts | 47 +++++++++ .../Interfaces/Public/PublisherProperties.ts | 8 ++ openvidu-browser/src/index.ts | 1 + 9 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 openvidu-browser/src/OpenViduInternal/Interfaces/Public/Filter.ts diff --git a/openvidu-browser/src/OpenVidu/Connection.ts b/openvidu-browser/src/OpenVidu/Connection.ts index ab3903e0..121c9d36 100644 --- a/openvidu-browser/src/OpenVidu/Connection.ts +++ b/openvidu-browser/src/OpenVidu/Connection.ts @@ -129,7 +129,8 @@ export class Connection { videoActive: opts.videoActive, typeOfVideo: opts.typeOfVideo, frameRate: opts.frameRate, - videoDimensions: !!opts.videoDimensions ? JSON.parse(opts.videoDimensions) : undefined + videoDimensions: !!opts.videoDimensions ? JSON.parse(opts.videoDimensions) : undefined, + filter: !!opts.filter ? opts.filter : {} }; const stream = new Stream(this.session, streamOptions); diff --git a/openvidu-browser/src/OpenVidu/OpenVidu.ts b/openvidu-browser/src/OpenVidu/OpenVidu.ts index 7909928d..7fe6a461 100644 --- a/openvidu-browser/src/OpenVidu/OpenVidu.ts +++ b/openvidu-browser/src/OpenVidu/OpenVidu.ts @@ -188,7 +188,8 @@ export class OpenVidu { publishAudio: (typeof properties.publishAudio !== 'undefined') ? properties.publishAudio : true, publishVideo: (typeof properties.publishVideo !== 'undefined') ? properties.publishVideo : true, resolution: this.isMediaStreamTrack(properties.videoSource) ? undefined : ((typeof properties.resolution !== 'undefined') ? properties.resolution : '640x480'), - videoSource: (typeof properties.videoSource !== 'undefined') ? properties.videoSource : undefined + videoSource: (typeof properties.videoSource !== 'undefined') ? properties.videoSource : undefined, + filter: properties.filter }; } else { diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index b46cf097..b6fa1d6e 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -492,6 +492,99 @@ export class Session implements EventDispatcher { }); } + applyFilter(stream: Stream, type: string, options: string): Promise { + return new Promise((resolve, reject) => { + console.info('Applying filter to stream ' + stream.streamId); + this.openvidu.sendRequest( + 'applyFilter', + { streamId: stream.streamId, type, options }, + (error, response) => { + if (error) { + console.error('Error applying filter for Stream ' + stream.streamId, error); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to apply a filter")); + } else { + reject(error); + } + } else { + console.info('Filter successfully applied on Stream ' + stream.streamId); + const oldValue = JSON.parse(JSON.stringify(stream.filter)); + stream.filter = { type, options }; + this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, stream, 'filter', stream.filter, oldValue, 'applyFilter')]); + stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(stream.streamManager, stream, 'filter', stream.filter, oldValue, 'applyFilter')]); + resolve(); + } + } + ); + }); + } + + execFilterMethod(stream: Stream, method: string, params: Object): Promise { + return new Promise((resolve, reject) => { + console.info('Executing filter method to stream ' + 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"; + console.error(errorMsg); + reject(errorMsg); + } + } else { + stringParams = params; + } + this.openvidu.sendRequest( + 'execFilterMethod', + { streamId: stream.streamId, method, params: stringParams }, + (error, response) => { + if (error) { + console.error('Error executing filter method for Stream ' + stream.streamId, error); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to execute a filter method")); + } else { + reject(error); + } + } else { + console.info('Filter method successfully executed on Stream ' + stream.streamId); + const oldValue = JSON.parse(JSON.stringify(stream.filter)); + stream.filter.lastExecMethod = { method, params: JSON.parse(stringParams) }; + this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, stream, 'filter', stream.filter, oldValue, 'execFilterMethod')]); + stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(stream.streamManager, stream, 'filter', stream.filter, oldValue, 'execFilterMethod')]); + resolve(); + } + } + ); + }); + } + + removeFilter(stream: Stream): Promise { + return new Promise((resolve, reject) => { + console.info('Removing filter of stream ' + stream.streamId); + this.openvidu.sendRequest( + 'removeFilter', + { streamId: stream.streamId }, + (error, response) => { + if (error) { + console.error('Error removing filter for Stream ' + stream.streamId, error); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to remove a filter")); + } else { + reject(error); + } + } else { + console.info('Filter successfully removed from Stream ' + stream.streamId); + const oldValue = JSON.parse(JSON.stringify(stream.filter)); + stream.filter = new Object(); + this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, stream, 'filter', stream.filter, oldValue, 'applyFilter')]); + stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(stream.streamManager, stream, 'filter', stream.filter, oldValue, 'applyFilter')]); + resolve(); + } + } + ); + }); + } + /** * Sends one signal. `signal` object has the following optional properties: @@ -792,6 +885,10 @@ export class Session implements EventDispatcher { msg.newValue = JSON.parse(JSON.parse(msg.newValue)); stream.videoDimensions = msg.newValue; break; + case 'filter': + oldValue = stream.filter; + stream.filter = msg.newValue; + break; } this.ee.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, stream, msg.property, msg.newValue, oldValue, msg.reason)]); diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts index 1a46bf85..9f15f99b 100644 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ b/openvidu-browser/src/OpenVidu/Stream.ts @@ -100,6 +100,21 @@ export class Stream { */ videoDimensions: { width: number, height: number }; + /** + * **WARNING**: experimental option. This interface may change in the near future + * + * Filter applied to the Stream. You can apply filters by calling [[Session.applyFilter]], execute methods of the applied filter with + * [[Session.execFilterMethod]] and remove it with [[Session.removeFilter]]. Be aware that the client calling this methods must have the + * necessary permissions: the token owned by the client must have been initialized with the appropriated `allowedFilters` array. + */ + filter: { + type?: string, + options?: Object, + lastExecMethod?: { + method: string, params: Object + } + } = {}; + /** * @hidden */ @@ -163,6 +178,12 @@ export class Stream { this.frameRate = (this.inboundStreamOpts.frameRate === -1) ? undefined : this.inboundStreamOpts.frameRate; this.videoDimensions = this.inboundStreamOpts.videoDimensions; } + if (!!this.inboundStreamOpts.filter) { + if (!!this.inboundStreamOpts.filter.lastExecMethod && Object.keys(this.inboundStreamOpts.filter.lastExecMethod).length === 0) { + delete this.inboundStreamOpts.filter.lastExecMethod; + } + this.filter = this.inboundStreamOpts.filter; + } } else { // OutboundStreamOptions: stream belongs to a Publisher this.outboundStreamOpts = options; @@ -182,6 +203,9 @@ export class Stream { this.typeOfVideo = this.isSendScreen() ? 'SCREEN' : 'CAMERA'; } } + if (!!this.outboundStreamOpts.publisherProperties.filter) { + this.filter = this.outboundStreamOpts.publisherProperties.filter; + } } this.ee.on('mediastream-updated', () => { @@ -467,7 +491,8 @@ export class Stream { videoActive: this.videoActive, typeOfVideo, frameRate: !!this.frameRate ? this.frameRate : -1, - videoDimensions: JSON.stringify(this.videoDimensions) + videoDimensions: JSON.stringify(this.videoDimensions), + filter: this.outboundStreamOpts.publisherProperties.filter }, (error, response) => { if (error) { if (error.code === 401) { diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts index 55da3a0b..918e7b79 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/InboundStreamOptions.ts @@ -16,6 +16,7 @@ */ import { Connection } from '../../../OpenVidu/Connection'; +import { Filter } from '../Public/Filter'; export interface InboundStreamOptions { id: string; @@ -27,4 +28,5 @@ export interface InboundStreamOptions { typeOfVideo: string; frameRate: number; videoDimensions: { width: number, height: number }; + filter: Filter; } \ No newline at end of file diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts index 8da080a1..48717d75 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/StreamOptionsServer.ts @@ -15,6 +15,8 @@ * */ +import { Filter } from '../Public/Filter'; + export interface StreamOptionsServer { id: string; hasAudio: boolean; @@ -24,4 +26,5 @@ export interface StreamOptionsServer { typeOfVideo: string; frameRate: number; videoDimensions: string; + filter: Filter; } \ No newline at end of file diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Filter.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Filter.ts new file mode 100644 index 00000000..df1c7be7 --- /dev/null +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Filter.ts @@ -0,0 +1,47 @@ +/* + * (C) Copyright 2017-2018 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. + * + */ + +/** + * **WARNING**: experimental option. This interface may change in the near future. See [[Stream.filter]] + */ +export interface Filter { + + /** + * Type of filter applied. This is the name of the remote class identifying the filter to apply in Kurento Media Server. + * For example: `"FaceOverlayFilter"`, `"GStreamerFilter"`. If `undefined` no filter is applied to the Stream + */ + type?: string; + + /** + * Parameters used to initialized the filter. + * These correspond to the constructor parameters used in the filter in Kurento Media Server. + * For example: for `filter.type = "GStreamerFilter"` could be `filter.options = "pitch pitch=0.8 tempo=1.0"` + */ + options?: Object; + + /** + * Value passed the last time [[Session.execFilterMethod]] or [[Session.forceExecFilterMethod]] were called + * for the Stream owning this filter. If `undefined` those methods have not been called yet. + * + * You can use this value to know the current status of any applied filter + */ + lastExecMethod?: { + method: string, + params: Object + }; + +} \ No newline at end of file diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts index 2ee8cf52..687c89ad 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts @@ -15,6 +15,7 @@ * */ +import { Filter } from './Filter'; import { VideoInsertMode } from '../../Enums/VideoInsertMode'; /** @@ -80,4 +81,11 @@ export interface PublisherProperties { */ videoSource?: string | MediaStreamTrack | boolean; + /** + * **WARNING**: experimental option. This property may change in the near future + * + * Define a filter to apply in the Publisher's stream + */ + filter?: Filter; + } \ No newline at end of file diff --git a/openvidu-browser/src/index.ts b/openvidu-browser/src/index.ts index d7dac65a..cd39fe7c 100644 --- a/openvidu-browser/src/index.ts +++ b/openvidu-browser/src/index.ts @@ -25,6 +25,7 @@ export { StreamPropertyChangedEvent } from './OpenViduInternal/Events/StreamProp export { Capabilities } from './OpenViduInternal/Interfaces/Public/Capabilities'; export { Device } from './OpenViduInternal/Interfaces/Public/Device'; export { EventDispatcher } from './OpenViduInternal/Interfaces/Public/EventDispatcher'; +export { Filter } from './OpenViduInternal/Interfaces/Public/Filter'; export { OpenViduAdvancedConfiguration } from './OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration'; export { PublisherProperties } from './OpenViduInternal/Interfaces/Public/PublisherProperties'; export { SignalOptions } from './OpenViduInternal/Interfaces/Public/SignalOptions';