From d3b25765d141a8aa7eccee041acf1ca172d400b6 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Tue, 28 Aug 2018 11:24:26 +0200 Subject: [PATCH] openvidu-browser: filter refactoring (methods from Session to Stream/Filter) --- openvidu-browser/src/OpenVidu/Filter.ts | 130 +++++++++++++- openvidu-browser/src/OpenVidu/Session.ts | 159 ++---------------- openvidu-browser/src/OpenVidu/Stream.ts | 79 ++++++++- .../OpenViduInternal/Enums/OpenViduError.ts | 2 +- .../src/OpenViduInternal/Events/Event.ts | 5 +- .../OpenViduInternal/Events/FilterEvent.ts | 19 +-- .../Events/StreamPropertyChangedEvent.ts | 2 +- 7 files changed, 227 insertions(+), 169 deletions(-) diff --git a/openvidu-browser/src/OpenVidu/Filter.ts b/openvidu-browser/src/OpenVidu/Filter.ts index be82edd0..a7b08eba 100644 --- a/openvidu-browser/src/OpenVidu/Filter.ts +++ b/openvidu-browser/src/OpenVidu/Filter.ts @@ -15,9 +15,16 @@ * */ +import { Stream } from './Stream'; +import { FilterEvent } from '../OpenViduInternal/Events/FilterEvent'; +import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; +import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; +import { ObjMap } from '../OpenViduInternal/Interfaces/Private/ObjMap'; + + /** * **WARNING**: experimental option. This interface may change in the near future - * + * * Video/audio filter applied to a Stream. See [[Stream.applyFilter]] */ export class Filter { @@ -51,6 +58,16 @@ export class Filter { method: string, params: Object }; + /** + * @hidden + */ + handlers: ObjMap<(event: FilterEvent) => void> = {}; + + /** + * @hidden + */ + stream: Stream; + /** * @hidden @@ -60,4 +77,115 @@ export class Filter { this.options = options; } + + /** + * Executes a filter method. Available methods are specific for each filter + * + * @param method Name of the method + * @param params Parameters of the method + */ + execMethod(method: string, params: Object): Promise { + return new Promise((resolve, reject) => { + console.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"; + console.error(errorMsg); + reject(errorMsg); + } + } else { + stringParams = params; + } + this.stream.session.openvidu.sendRequest( + 'execFilterMethod', + { streamId: this.stream.streamId, method, params: stringParams }, + (error, response) => { + if (error) { + console.error('Error executing filter method for Stream ' + this.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 ' + 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')]); + resolve(); + } + } + ); + }); + } + + + /** + * Subscribe to certain filter event. Available events are specific for each filter + * + * @param eventType Event to which subscribe to. + * @param handler Function to execute upon event dispatched. It receives as parameter a [[FilterEvent]] object + * + * @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully attached to the filter and rejected with an Error object if not + */ + addEventListener(eventType: string, handler: (event: FilterEvent) => void): Promise { + return new Promise((resolve, reject) => { + console.info('Adding filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId); + this.stream.session.openvidu.sendRequest( + 'addFilterEventListener', + { streamId: this.stream.streamId, type: eventType }, + (error, response) => { + if (error) { + console.error('Error adding filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId, error); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to add a filter event listener")); + } else { + reject(error); + } + } else { + this.handlers[eventType] = handler; + console.info('Filter event listener to event ' + eventType + ' successfully applied on Stream ' + this.stream.streamId); + resolve(); + } + } + ); + }); + } + + + /** + * Removes certain filter event listener previously set. + * + * @param eventType Event to unsubscribe from. + * + * @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully removed from the filter and rejected with an Error object in other case + */ + removeEventListener(eventType: string): Promise { + return new Promise((resolve, reject) => { + console.info('Removing filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId); + this.stream.session.openvidu.sendRequest( + 'removeFilterEventListener', + { streamId: this.stream.streamId, type: eventType }, + (error, response) => { + if (error) { + console.error('Error removing filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId, error); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to add a filter event listener")); + } else { + reject(error); + } + } else { + delete this.handlers[eventType]; + console.info('Filter event listener to event ' + eventType + ' successfully removed on Stream ' + this.stream.streamId); + resolve(); + } + } + ); + }); + } + } \ No newline at end of file diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index 5be6913d..edf92a77 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -494,148 +494,6 @@ export class Session implements EventDispatcher { }); } - applyFilter(stream: Stream, type: string, options: Object): Promise { - return new Promise((resolve, reject) => { - console.info('Applying filter to stream ' + stream.streamId); - options = !!options ? options : {}; - if (typeof options !== 'string') { - options = JSON.stringify(options); - } - 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: Filter = stream.filter; - stream.filter = new 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)); - delete stream.filter; - 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(); - } - } - ); - }); - } - - addFilterEventListener(stream: Stream, eventType: string): Promise { - return new Promise((resolve, reject) => { - console.info('Adding filter event listener to event ' + eventType + ' to stream ' + stream.streamId); - this.openvidu.sendRequest( - 'addFilterEventListener', - { streamId: stream.streamId, type: eventType }, - (error, response) => { - if (error) { - console.error('Error adding filter event listener to event ' + eventType + 'for Stream ' + stream.streamId, error); - if (error.code === 401) { - reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to add a filter event listener")); - } else { - reject(error); - } - } else { - console.info('Filter event listener to event ' + eventType + ' successfully applied on Stream ' + stream.streamId); - resolve(); - } - } - ); - }); - } - - removeFilterEventListener(stream: Stream, eventType: string): Promise { - return new Promise((resolve, reject) => { - console.info('Removing filter event listener to event ' + eventType + ' to stream ' + stream.streamId); - this.openvidu.sendRequest( - 'removeFilterEventListener', - { streamId: stream.streamId, type: eventType }, - (error, response) => { - if (error) { - console.error('Error removing filter event listener to event ' + eventType + 'for Stream ' + stream.streamId, error); - if (error.code === 401) { - reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to add a filter event listener")); - } else { - reject(error); - } - } else { - console.info('Filter event listener to event ' + eventType + ' successfully removed on Stream ' + stream.streamId); - resolve(); - } - } - ); - }); - } /** * Sends one signal. `signal` object has the following optional properties: @@ -685,7 +543,7 @@ export class Session implements EventDispatcher { /** * See [[EventDispatcher.on]] */ - on(type: string, handler: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent | FilterEvent) => void): EventDispatcher { + on(type: string, handler: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent) => void): EventDispatcher { this.ee.on(type, event => { if (event) { @@ -939,7 +797,16 @@ export class Session implements EventDispatcher { case 'filter': oldValue = stream.filter; msg.newValue = (Object.keys(msg.newValue).length > 0) ? msg.newValue : undefined; - stream.filter = msg.newValue; + if (msg.newValue !== undefined) { + stream.filter = new Filter(msg.newValue.type, msg.newValue.options); + stream.filter.stream = stream; + if (msg.newValue.lastExecMethod) { + stream.filter.lastExecMethod = msg.newValue.lastExecMethod; + } + } else { + delete stream.filter; + } + msg.newValue = stream.filter; break; } @@ -1063,9 +930,9 @@ export class Session implements EventDispatcher { const streamId: string = response.streamId; this.getConnection(connectionId, 'No connection found for connectionId ' + connectionId) .then(connection => { + console.info('Filter event dispatched'); const stream: Stream = connection.stream; - stream.ee.emitEvent('filterEventDispatched', [new FilterEvent(stream, stream.filter, response.eventType, response.data)]); - this.ee.emitEvent('filterEventDispatched', [new FilterEvent(stream, stream.filter, response.eventType, response.data)]); + stream.filter.handlers[response.eventType](new FilterEvent(stream.filter, response.eventType, response.data)); }); } diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts index afb97d56..c217d19c 100644 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ b/openvidu-browser/src/OpenVidu/Stream.ts @@ -26,6 +26,7 @@ import { OutboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/Ou import { WebRtcPeer, WebRtcPeerSendonly, WebRtcPeerRecvonly, WebRtcPeerSendrecv } from '../OpenViduInternal/WebRtcPeer/WebRtcPeer'; import { WebRtcStats } from '../OpenViduInternal/WebRtcStats/WebRtcStats'; import { PublisherSpeakingEvent } from '../OpenViduInternal/Events/PublisherSpeakingEvent'; +import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; import EventEmitter = require('wolfy87-eventemitter'); import hark = require('hark'); @@ -106,8 +107,8 @@ export class Stream implements EventDispatcher { /** * **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 + * Filter applied to the Stream. You can apply filters by calling [[Stream.applyFilter]], execute methods of the applied filter with + * [[Filter.execMethod]] and remove it with [[Stream.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: Filter; @@ -257,6 +258,80 @@ export class Stream implements EventDispatcher { } + /** + * + * Applies an audio/video filter to the stream. + * + * @param type Type of filter applied. See [[Filter.type]] + * @param options Parameters used to initialize the filter. See [[Filter.options]] + * + * @returns A Promise (to which you can optionally subscribe to) that is resolved to the applied filter if success and rejected with an Error object if not + */ + applyFilter(type: string, options: Object): Promise { + return new Promise((resolve, reject) => { + console.info('Applying filter to stream ' + this.streamId); + options = !!options ? options : {}; + if (typeof options !== 'string') { + options = JSON.stringify(options); + } + this.session.openvidu.sendRequest( + 'applyFilter', + { streamId: this.streamId, type, options }, + (error, response) => { + if (error) { + console.error('Error applying filter for Stream ' + this.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 ' + this.streamId); + const oldValue: Filter = this.filter; + this.filter = new Filter(type, options); + this.filter.stream = this; + this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter, oldValue, 'applyFilter')]); + this.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter, oldValue, 'applyFilter')]); + resolve(this.filter); + } + } + ); + }); + } + + /** + * Removes an audio/video filter previously applied. + * + * @returns A Promise (to which you can optionally subscribe to) that is resolved if the previously applied filter has been successfully removed and rejected with an Error object in other case + */ + removeFilter(): Promise { + return new Promise((resolve, reject) => { + console.info('Removing filter of stream ' + this.streamId); + this.session.openvidu.sendRequest( + 'removeFilter', + { streamId: this.streamId }, + (error, response) => { + if (error) { + console.error('Error removing filter for Stream ' + this.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 ' + this.streamId); + const oldValue = this.filter; + delete this.filter; + this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter, oldValue, 'applyFilter')]); + this.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter, oldValue, 'applyFilter')]); + resolve(); + } + } + ); + }); + } + + /* Hidden methods */ /** diff --git a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts b/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts index 5c64469b..8f1a3cf3 100644 --- a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts +++ b/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts @@ -83,7 +83,7 @@ export enum OpenViduErrorName { /** * The client tried to call a method without the required permissions. This can occur for methods [[Session.publish]], - * [[Session.forceUnpublish]] and [[Session.forceDisconnect]] + * [[Session.forceUnpublish]], [[Session.forceDisconnect]], [[Stream.applyFilter]], [[Stream.removeFilter]] */ OPENVIDU_PERMISSION_DENIED = 'OPENVIDU_PERMISSION_DENIED', diff --git a/openvidu-browser/src/OpenViduInternal/Events/Event.ts b/openvidu-browser/src/OpenViduInternal/Events/Event.ts index 565d71a0..b51f975d 100644 --- a/openvidu-browser/src/OpenViduInternal/Events/Event.ts +++ b/openvidu-browser/src/OpenViduInternal/Events/Event.ts @@ -15,6 +15,7 @@ * */ +import { Filter } from '../../OpenVidu/Filter'; import { StreamManager } from '../../OpenVidu/StreamManager'; import { Session } from '../../OpenVidu/Session'; import { Stream } from '../../OpenVidu/Stream'; @@ -29,7 +30,7 @@ export abstract class Event { /** * The object that dispatched the event */ - target: Session | Stream | StreamManager; + target: Session | StreamManager | Filter; /** * The type of event. This is the same string you pass as first parameter when calling method `on()` of any object implementing [[EventDispatcher]] interface @@ -41,7 +42,7 @@ export abstract class Event { /** * @hidden */ - constructor(cancelable: boolean, target: Session | Stream | StreamManager, type: string) { + constructor(cancelable: boolean, target: Session | StreamManager | Filter, type: string) { this.cancelable = cancelable; this.target = target; this.type = type; diff --git a/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts index 84eaef6f..a05907aa 100644 --- a/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts +++ b/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts @@ -21,21 +21,10 @@ import { Filter } from '../../OpenVidu/Filter'; /** - * Defines the following events: - * - `filterEventDispatched`: dispatched by [[Stream]]. Only triggered if `Stream.on('filterEventDispatched', event => {})` was called + * Defines every event dispatched by audio/video stream filters. You can subscribe to filter events by calling [[Filter.addEventListener]] */ export class FilterEvent extends Event { - /** - * Filter affected - */ - filter: Filter; - - /** - * Type of the event - */ - eventType: string; - /** * Data of the event */ @@ -44,10 +33,8 @@ export class FilterEvent extends Event { /** * @hidden */ - constructor(target: Stream, filter: Filter, eventType: string, data: Object) { - super(false, target, 'filterEventDispatched'); - this.filter = filter; - this.eventType = eventType; + constructor(target: Filter, eventType: string, data: Object) { + super(false, target, eventType); this.data = data; } diff --git a/openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts index a0cc59eb..fcffb93b 100644 --- a/openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts +++ b/openvidu-browser/src/OpenViduInternal/Events/StreamPropertyChangedEvent.ts @@ -33,7 +33,7 @@ export class StreamPropertyChangedEvent extends Event { stream: Stream; /** - * The property of the stream that changed. This value is either `"videoActive"`, `"audioActive"` or `"videoDimensions"` + * The property of the stream that changed. This value is either `"videoActive"`, `"audioActive"`, `"videoDimensions"` or `"filter"` */ changedProperty: string;