2018-04-26 15:33:47 +02:00
/ *
2020-02-04 11:25:54 +01:00
* ( C ) Copyright 2017 - 2020 OpenVidu ( https : //openvidu.io)
2018-04-26 15:33:47 +02:00
*
* 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 .
*
* /
2018-05-08 13:01:34 +02:00
import { Connection } from './Connection' ;
2018-08-27 17:06:14 +02:00
import { Filter } from './Filter' ;
2018-05-08 13:01:34 +02:00
import { OpenVidu } from './OpenVidu' ;
import { Publisher } from './Publisher' ;
import { Stream } from './Stream' ;
2018-05-29 18:28:58 +02:00
import { StreamManager } from './StreamManager' ;
2018-05-08 13:01:34 +02:00
import { Subscriber } from './Subscriber' ;
2018-07-04 15:04:47 +02:00
import { Capabilities } from '../OpenViduInternal/Interfaces/Public/Capabilities' ;
2020-04-26 22:22:39 +02:00
import { EventDispatcher } from './EventDispatcher' ;
2018-04-26 15:33:47 +02:00
import { SignalOptions } from '../OpenViduInternal/Interfaces/Public/SignalOptions' ;
import { SubscriberProperties } from '../OpenViduInternal/Interfaces/Public/SubscriberProperties' ;
import { ConnectionOptions } from '../OpenViduInternal/Interfaces/Private/ConnectionOptions' ;
import { ObjMap } from '../OpenViduInternal/Interfaces/Private/ObjMap' ;
import { SessionOptions } from '../OpenViduInternal/Interfaces/Private/SessionOptions' ;
import { ConnectionEvent } from '../OpenViduInternal/Events/ConnectionEvent' ;
2018-08-03 14:56:36 +02:00
import { FilterEvent } from '../OpenViduInternal/Events/FilterEvent' ;
2018-04-26 15:33:47 +02:00
import { PublisherSpeakingEvent } from '../OpenViduInternal/Events/PublisherSpeakingEvent' ;
import { RecordingEvent } from '../OpenViduInternal/Events/RecordingEvent' ;
import { SessionDisconnectedEvent } from '../OpenViduInternal/Events/SessionDisconnectedEvent' ;
import { SignalEvent } from '../OpenViduInternal/Events/SignalEvent' ;
import { StreamEvent } from '../OpenViduInternal/Events/StreamEvent' ;
2018-07-03 15:35:08 +02:00
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent' ;
2018-04-26 15:33:47 +02:00
import { OpenViduError , OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError' ;
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode' ;
2018-12-04 09:55:00 +01:00
import platform = require ( 'platform' ) ;
2020-05-04 20:01:56 +02:00
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger' ;
/ * *
* @hidden
* /
const logger : OpenViduLogger = OpenViduLogger . getInstance ( ) ;
2018-04-26 15:33:47 +02:00
/ * *
* Represents a video call . It can also be seen as a videoconference room where multiple users can connect .
2018-06-01 14:39:38 +02:00
* Participants who publish their videos to a session can be seen by the rest of users connected to that specific session .
2020-02-23 12:05:05 +01:00
* Initialized with [ [ OpenVidu . initSession ] ] method .
2020-05-04 20:01:56 +02:00
*
2020-02-23 12:05:05 +01:00
* # # # Available event listeners ( and events dispatched )
2020-05-04 20:01:56 +02:00
*
2020-02-23 12:05:05 +01:00
* - connectionCreated ( [ [ ConnectionEvent ] ] )
* - connectionDestroyed ( [ [ ConnectionEvent ] ] )
* - sessionDisconnected ( [ [ SessionDisconnectedEvent ] ] )
* - streamCreated ( [ [ StreamEvent ] ] )
* - streamDestroyed ( [ [ StreamEvent ] ] )
* - streamPropertyChanged ( [ [ StreamPropertyChangedEvent ] ] )
* - publisherStartSpeaking ( [ [ PublisherSpeakingEvent ] ] )
* - publisherStopSpeaking ( [ [ PublisherSpeakingEvent ] ] )
* - signal ( [ [ SignalEvent ] ] )
* - recordingStarted ( [ [ RecordingEvent ] ] )
* - recordingStopped ( [ [ RecordingEvent ] ] )
* - reconnecting
* - reconnected
2020-05-04 20:01:56 +02:00
*
2018-04-26 15:33:47 +02:00
* /
2020-04-26 22:22:39 +02:00
export class Session extends EventDispatcher {
2018-04-26 15:33:47 +02:00
/ * *
* Local connection to the Session . This object is defined only after [ [ Session . connect ] ] has been successfully executed , and can be retrieved subscribing to ` connectionCreated ` event
* /
connection : Connection ;
/ * *
2018-06-01 14:39:38 +02:00
* Unique identifier of the Session
2018-04-26 15:33:47 +02:00
* /
sessionId : string ;
2018-05-29 18:28:58 +02:00
/ * *
2018-06-01 14:39:38 +02:00
* Collection of all StreamManagers of this Session ( [ [ Publisher ] ] and [ [ Subscriber ] ] )
2018-05-29 18:28:58 +02:00
* /
streamManagers : StreamManager [ ] = [ ] ;
2018-07-04 15:04:47 +02:00
/ * *
* Object defining the methods that the client is able to call . These are defined by the role of the token used to connect to the Session .
* This object is only defined after [ [ Session . connect ] ] has been successfully resolved
* /
capabilities : Capabilities ;
2018-04-26 15:33:47 +02:00
// This map is only used to avoid race condition between 'joinRoom' response and 'onParticipantPublished' notification
/ * *
* @hidden
* /
remoteStreamsCreated : ObjMap < boolean > = { } ;
2020-01-25 16:53:10 +01:00
/ * *
* @hidden
2019-07-04 16:28:14 +02:00
* /
isFirstIonicIosSubscriber = true ;
2020-01-25 16:53:10 +01:00
/ * *
* @hidden
2019-07-04 16:28:14 +02:00
* /
countDownForIonicIosSubscribersActive = true ;
2018-04-26 15:33:47 +02:00
/ * *
* @hidden
* /
remoteConnections : ObjMap < Connection > = { } ;
/ * *
* @hidden
* /
openvidu : OpenVidu ;
/ * *
* @hidden
* /
options : SessionOptions ;
/ * *
* @hidden
* /
2019-12-05 14:18:46 +01:00
startSpeakingEventsEnabled = false ;
/ * *
* @hidden
* /
startSpeakingEventsEnabledOnce = false ;
/ * *
* @hidden
* /
stopSpeakingEventsEnabled = false ;
/ * *
* @hidden
* /
stopSpeakingEventsEnabledOnce = false ;
2018-04-26 15:33:47 +02:00
/ * *
* @hidden
* /
2018-05-03 10:58:26 +02:00
constructor ( openvidu : OpenVidu ) {
2020-04-26 22:22:39 +02:00
super ( ) ;
2018-04-26 15:33:47 +02:00
this . openvidu = openvidu ;
}
connect ( token : string ) : Promise < any > ;
connect ( token : string , metadata : any ) : Promise < any > ;
/ * *
* Connects to the session using ` token ` . Parameter ` metadata ` allows you to pass extra data to share with other users when
2018-08-28 11:50:37 +02:00
* they receive ` streamCreated ` event . The structure of ` metadata ` string is up to you ( maybe some standardized format
2018-12-11 14:29:06 +01:00
* as JSON or XML is a good idea ) .
2018-04-26 15:33:47 +02:00
*
2018-08-28 11:50:37 +02:00
* This metadata is not considered secure , as it is generated in the client side . To pass secure data , add it as a parameter in the
2018-04-26 15:33:47 +02:00
* token generation operation ( through the API REST , openvidu - java - client or openvidu - node - client ) .
*
* Only after the returned Promise is successfully resolved [ [ Session . connection ] ] object will be available and properly defined .
*
* # # # # Events dispatched
*
* The [ [ Session ] ] object of the local participant will first dispatch one or more ` connectionCreated ` events upon successful termination of this method :
* - First one for your own local Connection object , so you can retrieve [ [ Session . connection ] ] property .
* - Then one for each remote Connection previously connected to the Session , if any . Any other remote user connecting to the Session after you have
* successfully connected will also dispatch a ` connectionCreated ` event when they do so .
*
2018-06-01 14:39:38 +02:00
* The [ [ Session ] ] object of the local participant will also dispatch a ` streamCreated ` event for each remote active [ [ Publisher ] ] that was already streaming
* when connecting , just after dispatching all remote ` connectionCreated ` events .
2018-04-26 15:33:47 +02:00
*
* The [ [ Session ] ] object of every other participant connected to the session will dispatch a ` connectionCreated ` event .
*
* See [ [ ConnectionEvent ] ] and [ [ StreamEvent ] ] to learn more .
*
2018-06-01 14:39:38 +02:00
* @returns A Promise to which you must subscribe that is resolved if the the connection to the Session was successful and rejected with an Error object if not
2018-04-26 15:33:47 +02:00
*
* /
2018-05-08 10:40:46 +02:00
connect ( token : string , metadata? : any ) : Promise < any > {
2019-05-20 14:11:14 +02:00
return new Promise ( ( resolve , reject ) = > {
2018-04-26 15:33:47 +02:00
2019-05-20 14:11:14 +02:00
this . processToken ( token ) ;
2018-05-08 10:40:46 +02:00
if ( this . openvidu . checkSystemRequirements ( ) ) {
// Early configuration to deactivate automatic subscription to streams
this . options = {
sessionId : this.sessionId ,
participantId : token ,
metadata : ! ! metadata ? this . stringClientMetadata ( metadata ) : ''
} ;
this . connectAux ( token ) . then ( ( ) = > {
resolve ( ) ;
} ) . catch ( error = > {
reject ( error ) ;
} ) ;
} else {
2019-05-10 10:36:10 +02:00
reject ( new OpenViduError ( OpenViduErrorName . BROWSER_NOT_SUPPORTED , 'Browser ' + platform . name + ' (version ' + platform . version + ') for ' + platform . os ! ! . family + ' is not supported in OpenVidu' ) ) ;
2018-05-08 10:40:46 +02:00
}
} ) ;
2018-04-26 15:33:47 +02:00
}
/ * *
* Leaves the session , destroying all streams and deleting the user as a participant .
*
* # # # # Events dispatched
*
* The [ [ Session ] ] object of the local participant will dispatch a ` sessionDisconnected ` event .
* This event will automatically unsubscribe the leaving participant from every Subscriber object of the session ( this includes closing the WebRTCPeer connection and disposing all MediaStreamTracks )
2020-04-05 21:34:10 +02:00
* and also deletes any HTML video element associated to each Subscriber ( only those [ created by OpenVidu Browser ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # l e t - o p e n v i d u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s ) ) .
2018-06-01 14:39:38 +02:00
* For every video removed , each Subscriber object will dispatch a ` videoElementDestroyed ` event .
2018-07-03 15:35:08 +02:00
* Call ` event.preventDefault() ` upon event ` sessionDisconnected ` to avoid this behavior and take care of disposing and cleaning all the Subscriber objects yourself .
2018-06-01 14:39:38 +02:00
* See [ [ SessionDisconnectedEvent ] ] and [ [ VideoElementEvent ] ] to learn more to learn more .
2018-04-26 15:33:47 +02:00
*
* The [ [ Publisher ] ] object of the local participant will dispatch a ` streamDestroyed ` event if there is a [ [ Publisher ] ] object publishing to the session .
2020-04-05 21:34:10 +02:00
* This event will automatically stop all media tracks and delete any HTML video element associated to it ( only those [ created by OpenVidu Browser ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # l e t - o p e n v i d u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s ) ) .
2018-06-01 14:39:38 +02:00
* For every video removed , the Publisher object will dispatch a ` videoElementDestroyed ` event .
2018-07-03 15:35:08 +02:00
* Call ` event.preventDefault() ` upon event ` streamDestroyed ` if you want to clean the Publisher object on your own or re - publish it in a different Session ( to do so it is a mandatory requirement to call ` Session.unpublish() `
2018-06-01 14:39:38 +02:00
* or / and ` Session.disconnect() ` in the previous session ) . See [ [ StreamEvent ] ] and [ [ VideoElementEvent ] ] to learn more .
2018-04-26 15:33:47 +02:00
*
* The [ [ Session ] ] object of every other participant connected to the session will dispatch a ` streamDestroyed ` event if the disconnected participant was publishing .
* This event will automatically unsubscribe the Subscriber object from the session ( this includes closing the WebRTCPeer connection and disposing all MediaStreamTracks )
2020-04-05 21:34:10 +02:00
* and also deletes any HTML video element associated to that Subscriber ( only those [ created by OpenVidu Browser ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # l e t - o p e n v i d u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s ) ) .
2018-06-01 14:39:38 +02:00
* For every video removed , the Subscriber object will dispatch a ` videoElementDestroyed ` event .
2018-07-03 15:35:08 +02:00
* Call ` event.preventDefault() ` upon event ` streamDestroyed ` to avoid this default behavior and take care of disposing and cleaning the Subscriber object yourself .
2018-06-01 14:39:38 +02:00
* See [ [ StreamEvent ] ] and [ [ VideoElementEvent ] ] to learn more .
2018-04-26 15:33:47 +02:00
*
* The [ [ Session ] ] object of every other participant connected to the session will dispatch a ` connectionDestroyed ` event in any case . See [ [ ConnectionEvent ] ] to learn more .
* /
disconnect ( ) : void {
this . leave ( false , 'disconnect' ) ;
}
subscribe ( stream : Stream , targetElement : string | HTMLElement ) : Subscriber ;
subscribe ( stream : Stream , targetElement : string | HTMLElement , properties : SubscriberProperties ) : Subscriber ;
subscribe ( stream : Stream , targetElement : string | HTMLElement , completionHandler : ( error : Error | undefined ) = > void ) : Subscriber ;
subscribe ( stream : Stream , targetElement : string | HTMLElement , properties : SubscriberProperties , completionHandler : ( error : Error | undefined ) = > void ) : Subscriber ;
/ * *
* Subscribes to a ` stream ` , adding a new HTML video element to DOM with ` subscriberProperties ` settings . This method is usually called in the callback of ` streamCreated ` event .
*
* # # # # Events dispatched
*
2018-06-01 14:39:38 +02:00
* The [ [ Subscriber ] ] object will dispatch a ` videoElementCreated ` event once the HTML video element has been added to DOM ( only if you
2020-04-05 21:34:10 +02:00
* [ let OpenVidu take care of the video players ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # l e t - o p e n v i d u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s ) ) . S e e [ [ V i d e o E l e m e n t E v e n t ] ] t o l e a r n m o r e .
2018-04-26 15:33:47 +02:00
*
2018-06-01 14:39:38 +02:00
* The [ [ Subscriber ] ] object will dispatch a ` streamPlaying ` event once the remote stream starts playing . See [ [ StreamManagerEvent ] ] to learn more .
2018-04-26 15:33:47 +02:00
*
* @param stream Stream object to subscribe to
2018-06-01 14:39:38 +02:00
* @param targetElement HTML DOM element ( or its ` id ` attribute ) in which the video element of the Subscriber will be inserted ( see [ [ SubscriberProperties . insertMode ] ] ) . If * null * or * undefined * no default video will be created for this Subscriber .
2020-04-05 21:34:10 +02:00
* You can always call method [ [ Subscriber . addVideoElement ] ] or [ [ Subscriber . createVideoElement ] ] to manage the video elements on your own ( see [ Manage video players ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s ) s e c t i o n )
2018-04-26 15:33:47 +02:00
* @param completionHandler ` error ` parameter is null if ` subscribe ` succeeds , and is defined if it fails .
* /
subscribe ( stream : Stream , targetElement : string | HTMLElement , param3 ? : ( ( error : Error | undefined ) = > void ) | SubscriberProperties , param4 ? : ( ( error : Error | undefined ) = > void ) ) : Subscriber {
let properties : SubscriberProperties = { } ;
if ( ! ! param3 && typeof param3 !== 'function' ) {
properties = {
2018-04-27 11:08:03 +02:00
insertMode : ( typeof param3 . insertMode !== 'undefined' ) ? ( ( typeof param3 . insertMode === 'string' ) ? VideoInsertMode [ param3 . insertMode ] : properties . insertMode ) : VideoInsertMode . APPEND ,
2018-04-26 15:33:47 +02:00
subscribeToAudio : ( typeof param3 . subscribeToAudio !== 'undefined' ) ? param3.subscribeToAudio : true ,
subscribeToVideo : ( typeof param3 . subscribeToVideo !== 'undefined' ) ? param3.subscribeToVideo : true
} ;
} else {
properties = {
insertMode : VideoInsertMode.APPEND ,
subscribeToAudio : true ,
subscribeToVideo : true
} ;
}
let completionHandler : ( error : Error | undefined ) = > void ;
if ( ! ! param3 && ( typeof param3 === 'function' ) ) {
completionHandler = param3 ;
} else if ( ! ! param4 ) {
completionHandler = param4 ;
}
2020-05-04 20:01:56 +02:00
logger . info ( 'Subscribing to ' + stream . connection . connectionId ) ;
2018-04-26 15:33:47 +02:00
stream . subscribe ( )
. then ( ( ) = > {
2020-05-04 20:01:56 +02:00
logger . info ( 'Subscribed correctly to ' + stream . connection . connectionId ) ;
2018-04-26 15:33:47 +02:00
if ( completionHandler !== undefined ) {
completionHandler ( undefined ) ;
}
} )
. catch ( error = > {
if ( completionHandler !== undefined ) {
completionHandler ( error ) ;
}
} ) ;
const subscriber = new Subscriber ( stream , targetElement , properties ) ;
2018-05-29 18:28:58 +02:00
if ( ! ! subscriber . targetElement ) {
stream . streamManager . createVideoElement ( subscriber . targetElement , < VideoInsertMode > properties . insertMode ) ;
}
2018-04-26 15:33:47 +02:00
return subscriber ;
}
/ * *
* Promisified version of [ [ Session . subscribe ] ]
* /
subscribeAsync ( stream : Stream , targetElement : string | HTMLElement ) : Promise < Subscriber > ;
subscribeAsync ( stream : Stream , targetElement : string | HTMLElement , properties : SubscriberProperties ) : Promise < Subscriber > ;
subscribeAsync ( stream : Stream , targetElement : string | HTMLElement , properties? : SubscriberProperties ) : Promise < Subscriber > {
return new Promise < Subscriber > ( ( resolve , reject ) = > {
let subscriber : Subscriber ;
const callback = ( error : Error ) = > {
if ( ! ! error ) {
reject ( error ) ;
} else {
resolve ( subscriber ) ;
}
} ;
if ( ! ! properties ) {
subscriber = this . subscribe ( stream , targetElement , properties , callback ) ;
} else {
subscriber = this . subscribe ( stream , targetElement , callback ) ;
}
} ) ;
}
/ * *
2018-06-01 14:39:38 +02:00
* Unsubscribes from ` subscriber ` , automatically removing its associated HTML video elements .
2018-04-26 15:33:47 +02:00
*
* # # # # Events dispatched
*
2018-06-01 14:39:38 +02:00
* The [ [ Subscriber ] ] object will dispatch a ` videoElementDestroyed ` event for each video associated to it that was removed from DOM .
2020-04-05 21:34:10 +02:00
* Only videos [ created by OpenVidu Browser ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # l e t - o p e n v i d u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s ) ) w i l l b e a u t o m a t i c a l l y r e m o v e d
2018-06-01 14:39:38 +02:00
*
* See [ [ VideoElementEvent ] ] to learn more
2018-04-26 15:33:47 +02:00
* /
unsubscribe ( subscriber : Subscriber ) : void {
const connectionId = subscriber . stream . connection . connectionId ;
2020-05-04 20:01:56 +02:00
logger . info ( 'Unsubscribing from ' + connectionId ) ;
2018-04-26 15:33:47 +02:00
2018-05-23 15:01:40 +02:00
this . openvidu . sendRequest (
'unsubscribeFromVideo' ,
{ sender : subscriber.stream.connection.connectionId } ,
2018-04-26 15:33:47 +02:00
( error , response ) = > {
if ( error ) {
2020-05-04 20:01:56 +02:00
logger . error ( 'Error unsubscribing from ' + connectionId , error ) ;
2018-04-26 15:33:47 +02:00
} else {
2020-05-04 20:01:56 +02:00
logger . info ( 'Unsubscribed correctly from ' + connectionId ) ;
2018-04-26 15:33:47 +02:00
}
subscriber . stream . disposeWebRtcPeer ( ) ;
subscriber . stream . disposeMediaStream ( ) ;
2018-05-23 15:01:40 +02:00
}
) ;
2018-05-29 18:28:58 +02:00
subscriber . stream . streamManager . removeAllVideos ( ) ;
2018-04-26 15:33:47 +02:00
}
/ * *
2018-06-01 14:39:38 +02:00
* Publishes to the Session the Publisher object
2018-04-26 15:33:47 +02:00
*
* # # # # Events dispatched
*
* The local [ [ Publisher ] ] object will dispatch a ` streamCreated ` event upon successful termination of this method . See [ [ StreamEvent ] ] to learn more .
*
2018-05-29 18:28:58 +02:00
* The local [ [ Publisher ] ] object will dispatch a ` streamPlaying ` once the media stream starts playing . See [ [ StreamManagerEvent ] ] to learn more .
2018-04-26 15:33:47 +02:00
*
* The [ [ Session ] ] object of every other participant connected to the session will dispatch a ` streamCreated ` event so they can subscribe to it . See [ [ StreamEvent ] ] to learn more .
*
2018-06-01 14:39:38 +02:00
* @returns A Promise ( to which you can optionally subscribe to ) that is resolved only after the publisher was successfully published and rejected with an Error object if not
2018-04-26 15:33:47 +02:00
* /
publish ( publisher : Publisher ) : Promise < any > {
return new Promise ( ( resolve , reject ) = > {
publisher . session = this ;
publisher . stream . session = this ;
2018-07-09 15:45:20 +02:00
if ( ! publisher . stream . publishedOnce ) {
2018-04-26 15:33:47 +02:00
// 'Session.unpublish(Publisher)' has NOT been called
this . connection . addStream ( publisher . stream ) ;
publisher . stream . publish ( )
. then ( ( ) = > {
resolve ( ) ;
} )
. catch ( error = > {
reject ( error ) ;
} ) ;
} else {
// 'Session.unpublish(Publisher)' has been called. Must initialize again Publisher
publisher . initialize ( )
. then ( ( ) = > {
this . connection . addStream ( publisher . stream ) ;
2018-05-30 16:36:27 +02:00
publisher . reestablishStreamPlayingEvent ( ) ;
2018-04-26 15:33:47 +02:00
publisher . stream . publish ( )
. then ( ( ) = > {
resolve ( ) ;
} )
. catch ( error = > {
reject ( error ) ;
} ) ;
} ) . catch ( ( error ) = > {
reject ( error ) ;
} ) ;
}
} ) ;
}
/ * *
2018-06-01 14:39:38 +02:00
* Unpublishes from the Session the Publisher object .
2018-04-26 15:33:47 +02:00
*
* # # # # Events dispatched
*
* The [ [ Publisher ] ] object of the local participant will dispatch a ` streamDestroyed ` event .
2018-06-01 14:39:38 +02:00
* This event will automatically stop all media tracks and delete any HTML video element associated to this Publisher
2020-04-05 21:34:10 +02:00
* ( only those videos [ created by OpenVidu Browser ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # l e t - o p e n v i d u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s ) ) .
2018-06-01 14:39:38 +02:00
* For every video removed , the Publisher object will dispatch a ` videoElementDestroyed ` event .
2018-07-03 15:35:08 +02:00
* Call ` event.preventDefault() ` upon event ` streamDestroyed ` if you want to clean the Publisher object on your own or re - publish it in a different Session .
2018-04-26 15:33:47 +02:00
*
* The [ [ Session ] ] object of every other participant connected to the session will dispatch a ` streamDestroyed ` event .
2018-06-01 14:39:38 +02:00
* This event will automatically unsubscribe the Subscriber object from the session ( this includes closing the WebRTCPeer connection and disposing all MediaStreamTracks ) and
2020-04-05 21:34:10 +02:00
* delete any HTML video element associated to it ( only those [ created by OpenVidu Browser ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # l e t - o p e n v i d u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s ) ) .
2018-06-01 14:39:38 +02:00
* For every video removed , the Subscriber object will dispatch a ` videoElementDestroyed ` event .
2018-07-03 15:35:08 +02:00
* Call ` event.preventDefault() ` upon event ` streamDestroyed ` to avoid this default behavior and take care of disposing and cleaning the Subscriber object on your own .
2018-04-26 15:33:47 +02:00
*
2018-06-01 14:39:38 +02:00
* See [ [ StreamEvent ] ] and [ [ VideoElementEvent ] ] to learn more .
2018-04-26 15:33:47 +02:00
* /
unpublish ( publisher : Publisher ) : void {
const stream = publisher . stream ;
if ( ! stream . connection ) {
2020-05-04 20:01:56 +02:00
logger . error ( 'The associated Connection object of this Publisher is null' , stream ) ;
2018-04-26 15:33:47 +02:00
return ;
} else if ( stream . connection !== this . connection ) {
2020-05-04 20:01:56 +02:00
logger . error ( 'The associated Connection object of this Publisher is not your local Connection.' +
2018-04-26 15:33:47 +02:00
"Only moderators can force unpublish on remote Streams via 'forceUnpublish' method" , stream ) ;
return ;
} else {
2020-05-04 20:01:56 +02:00
logger . info ( 'Unpublishing local media (' + stream . connection . connectionId + ')' ) ;
2018-04-26 15:33:47 +02:00
this . openvidu . sendRequest ( 'unpublishVideo' , ( error , response ) = > {
if ( error ) {
2020-05-04 20:01:56 +02:00
logger . error ( error ) ;
2018-04-26 15:33:47 +02:00
} else {
2020-05-04 20:01:56 +02:00
logger . info ( 'Media unpublished correctly' ) ;
2018-04-26 15:33:47 +02:00
}
} ) ;
stream . disposeWebRtcPeer ( ) ;
delete stream . connection . stream ;
const streamEvent = new StreamEvent ( true , publisher , 'streamDestroyed' , publisher . stream , 'unpublish' ) ;
publisher . emitEvent ( 'streamDestroyed' , [ streamEvent ] ) ;
2018-07-03 15:35:08 +02:00
streamEvent . callDefaultBehavior ( ) ;
2018-04-26 15:33:47 +02:00
}
}
2018-07-09 15:45:20 +02:00
/ * *
* Forces some user to leave the session
*
* # # # # Events dispatched
*
* The behavior is the same as when some user calls [ [ Session . disconnect ] ] , but ` reason ` property in all events will be ` "forceDisconnectByUser" ` .
*
2018-07-10 13:02:52 +02:00
* The [ [ Session ] ] object of every participant will dispatch a ` streamDestroyed ` event if the evicted user was publishing a stream , with property ` reason ` set to ` "forceDisconnectByUser" ` .
* The [ [ Session ] ] object of every participant except the evicted one will dispatch a ` connectionDestroyed ` event for the evicted user , with property ` reason ` set to ` "forceDisconnectByUser" ` .
2018-07-09 15:45:20 +02:00
*
2018-07-10 13:02:52 +02:00
* If any , the [ [ Publisher ] ] object of the evicted participant will also dispatch a ` streamDestroyed ` event with property ` reason ` set to ` "forceDisconnectByUser" ` .
* The [ [ Session ] ] object of the evicted participant will dispatch a ` sessionDisconnected ` event with property ` reason ` set to ` "forceDisconnectByUser" ` .
2018-07-09 15:45:20 +02:00
*
2018-07-10 13:02:52 +02:00
* See [ [ StreamEvent ] ] , [ [ ConnectionEvent ] ] and [ [ SessionDisconnectedEvent ] ] to learn more .
2018-07-09 15:45:20 +02:00
*
* @returns A Promise ( to which you can optionally subscribe to ) that is resolved only after the participant has been successfully evicted from the session and rejected with an Error object if not
* /
forceDisconnect ( connection : Connection ) : Promise < any > {
return new Promise ( ( resolve , reject ) = > {
2020-05-04 20:01:56 +02:00
logger . info ( 'Forcing disconnect for connection ' + connection . connectionId ) ;
2018-07-09 15:45:20 +02:00
this . openvidu . sendRequest (
'forceDisconnect' ,
{ connectionId : connection.connectionId } ,
( error , response ) = > {
if ( error ) {
2020-05-04 20:01:56 +02:00
logger . error ( 'Error forcing disconnect for Connection ' + connection . connectionId , error ) ;
2018-07-09 15:45:20 +02:00
if ( error . code === 401 ) {
reject ( new OpenViduError ( OpenViduErrorName . OPENVIDU_PERMISSION_DENIED , "You don't have permissions to force a disconnection" ) ) ;
} else {
reject ( error ) ;
}
} else {
2020-05-04 20:01:56 +02:00
logger . info ( 'Forcing disconnect correctly for Connection ' + connection . connectionId ) ;
2018-07-09 15:45:20 +02:00
resolve ( ) ;
}
}
) ;
} ) ;
}
/ * *
* Forces some user to unpublish a Stream
*
* # # # # Events dispatched
*
2018-07-10 13:02:52 +02:00
* The behavior is the same as when some user calls [ [ Session . unpublish ] ] , but ` reason ` property in all events will be ` "forceUnpublishByUser" `
2018-07-09 15:45:20 +02:00
*
2018-07-10 13:02:52 +02:00
* The [ [ Session ] ] object of every participant will dispatch a ` streamDestroyed ` event with property ` reason ` set to ` "forceDisconnectByUser" `
2018-07-09 15:45:20 +02:00
*
* The [ [ Publisher ] ] object of the affected participant will also dispatch a ` streamDestroyed ` event with property ` reason ` set to ` "forceDisconnectByUser" `
*
2018-07-10 13:02:52 +02:00
* See [ [ StreamEvent ] ] to learn more .
*
2018-07-09 15:45:20 +02:00
* @returns A Promise ( to which you can optionally subscribe to ) that is resolved only after the remote Stream has been successfully unpublished from the session and rejected with an Error object if not
* /
forceUnpublish ( stream : Stream ) : Promise < any > {
return new Promise ( ( resolve , reject ) = > {
2020-05-04 20:01:56 +02:00
logger . info ( 'Forcing unpublish for stream ' + stream . streamId ) ;
2018-07-09 15:45:20 +02:00
this . openvidu . sendRequest (
'forceUnpublish' ,
{ streamId : stream.streamId } ,
( error , response ) = > {
if ( error ) {
2020-05-04 20:01:56 +02:00
logger . error ( 'Error forcing unpublish for Stream ' + stream . streamId , error ) ;
2018-07-09 15:45:20 +02:00
if ( error . code === 401 ) {
reject ( new OpenViduError ( OpenViduErrorName . OPENVIDU_PERMISSION_DENIED , "You don't have permissions to force an unpublishing" ) ) ;
} else {
reject ( error ) ;
}
} else {
2020-05-04 20:01:56 +02:00
logger . info ( 'Forcing unpublish correctly for Stream ' + stream . streamId ) ;
2018-07-09 15:45:20 +02:00
resolve ( ) ;
}
}
) ;
} ) ;
}
2018-04-26 15:33:47 +02:00
/ * *
* Sends one signal . ` signal ` object has the following optional properties :
* ` ` ` json
* { data :string , to :Connection [ ] , type : string }
* ` ` `
* All users subscribed to that signal ( ` session.on('signal:type', ...) ` or ` session.on('signal', ...) ` for all signals ) and whose Connection objects are in ` to ` array will receive it . Their local
* Session objects will dispatch a ` signal ` or ` signal:type ` event . See [ [ SignalEvent ] ] to learn more .
*
* @returns A Promise ( to which you can optionally subscribe to ) that is resolved if the message successfully reached openvidu - server and rejected with an Error object if not . _This doesn ' t
* mean that openvidu - server could resend the message to all the listed receivers . _
* /
/* tslint:disable:no-string-literal */
2018-05-08 10:40:46 +02:00
signal ( signal : SignalOptions ) : Promise < any > {
return new Promise ( ( resolve , reject ) = > {
2018-04-26 15:33:47 +02:00
2018-05-08 10:40:46 +02:00
const signalMessage = { } ;
2018-04-27 11:08:03 +02:00
2018-05-08 10:40:46 +02:00
if ( signal . to && signal . to . length > 0 ) {
const connectionIds : string [ ] = [ ] ;
signal . to . forEach ( connection = > {
2019-10-29 11:41:03 +01:00
if ( ! ! connection . connectionId ) {
connectionIds . push ( connection . connectionId ) ;
}
2018-04-27 11:08:03 +02:00
} ) ;
2018-05-08 10:40:46 +02:00
signalMessage [ 'to' ] = connectionIds ;
} else {
signalMessage [ 'to' ] = [ ] ;
}
2018-04-27 11:08:03 +02:00
2018-05-08 10:40:46 +02:00
signalMessage [ 'data' ] = signal . data ? signal . data : '' ;
2019-02-06 15:04:49 +01:00
let typeAux : string = signal . type ? signal . type : 'signal' ;
if ( ! ! typeAux ) {
if ( typeAux . substring ( 0 , 7 ) !== 'signal:' ) {
typeAux = 'signal:' + typeAux ;
}
}
signalMessage [ 'type' ] = typeAux ;
2018-04-27 11:08:03 +02:00
2018-05-08 10:40:46 +02:00
this . openvidu . sendRequest ( 'sendMessage' , {
message : JSON.stringify ( signalMessage )
} , ( error , response ) = > {
if ( ! ! error ) {
reject ( error ) ;
} else {
resolve ( ) ;
}
} ) ;
} ) ;
2018-04-26 15:33:47 +02:00
}
/* tslint:enable:no-string-literal */
/ * *
* See [ [ EventDispatcher . on ] ]
* /
2018-08-28 11:24:26 +02:00
on ( type : string , handler : ( event : SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent ) = > void ) : EventDispatcher {
2018-04-26 15:33:47 +02:00
2020-04-26 22:22:39 +02:00
super . onAux ( type , "Event '" + type + "' triggered by 'Session'" , handler ) ;
2018-04-26 15:33:47 +02:00
2019-12-05 14:18:46 +01:00
if ( type === 'publisherStartSpeaking' ) {
this . startSpeakingEventsEnabled = true ;
2018-04-26 15:33:47 +02:00
// If there are already available remote streams, enable hark 'speaking' event in all of them
for ( const connectionId in this . remoteConnections ) {
const str = this . remoteConnections [ connectionId ] . stream ;
2018-12-07 11:22:21 +01:00
if ( ! ! str && str . hasAudio ) {
2019-12-05 14:18:46 +01:00
str . enableStartSpeakingEvent ( ) ;
}
}
}
if ( type === 'publisherStopSpeaking' ) {
this . stopSpeakingEventsEnabled = true ;
// If there are already available remote streams, enable hark 'stopped_speaking' event in all of them
for ( const connectionId in this . remoteConnections ) {
const str = this . remoteConnections [ connectionId ] . stream ;
if ( ! ! str && str . hasAudio ) {
str . enableStopSpeakingEvent ( ) ;
2018-04-26 15:33:47 +02:00
}
}
}
return this ;
}
/ * *
* See [ [ EventDispatcher . once ] ]
* /
2018-04-27 11:08:03 +02:00
once ( type : string , handler : ( event : SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent ) = > void ) : Session {
2018-07-19 17:31:30 +02:00
2020-04-26 22:22:39 +02:00
super . onceAux ( type , "Event '" + type + "' triggered once by 'Session'" , handler ) ;
2018-04-26 15:33:47 +02:00
2019-12-05 14:18:46 +01:00
if ( type === 'publisherStartSpeaking' ) {
this . startSpeakingEventsEnabledOnce = true ;
// If there are already available remote streams, enable hark 'speaking' event in all of them once
for ( const connectionId in this . remoteConnections ) {
const str = this . remoteConnections [ connectionId ] . stream ;
if ( ! ! str && str . hasAudio ) {
str . enableOnceStartSpeakingEvent ( ) ;
}
}
}
if ( type === 'publisherStopSpeaking' ) {
this . stopSpeakingEventsEnabledOnce = true ;
// If there are already available remote streams, enable hark 'stopped_speaking' event in all of them once
2018-04-26 15:33:47 +02:00
for ( const connectionId in this . remoteConnections ) {
const str = this . remoteConnections [ connectionId ] . stream ;
2018-12-07 11:22:21 +01:00
if ( ! ! str && str . hasAudio ) {
2019-12-05 14:18:46 +01:00
str . enableOnceStopSpeakingEvent ( ) ;
2018-04-26 15:33:47 +02:00
}
}
}
return this ;
}
/ * *
* See [ [ EventDispatcher . off ] ]
* /
2018-04-27 11:08:03 +02:00
off ( type : string , handler ? : ( event : SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent ) = > void ) : Session {
2020-04-26 22:22:39 +02:00
super . off ( type , handler ) ;
2018-04-26 15:33:47 +02:00
2019-12-05 14:18:46 +01:00
if ( type === 'publisherStartSpeaking' ) {
let remainingStartSpeakingListeners = this . ee . getListeners ( type ) . length ;
if ( remainingStartSpeakingListeners === 0 ) {
this . startSpeakingEventsEnabled = false ;
// If there are already available remote streams, disable hark in all of them
for ( const connectionId in this . remoteConnections ) {
const str = this . remoteConnections [ connectionId ] . stream ;
if ( ! ! str ) {
str . disableStartSpeakingEvent ( false ) ;
}
}
}
}
if ( type === 'publisherStopSpeaking' ) {
let remainingStopSpeakingListeners = this . ee . getListeners ( type ) . length ;
if ( remainingStopSpeakingListeners === 0 ) {
this . stopSpeakingEventsEnabled = false ;
// If there are already available remote streams, disable hark in all of them
for ( const connectionId in this . remoteConnections ) {
const str = this . remoteConnections [ connectionId ] . stream ;
if ( ! ! str ) {
str . disableStopSpeakingEvent ( false ) ;
}
2018-04-26 15:33:47 +02:00
}
}
}
return this ;
}
/* Hidden methods */
/ * *
* @hidden
* /
onParticipantJoined ( response : ConnectionOptions ) : void {
// Connection shouldn't exist
this . getConnection ( response . id , '' )
. then ( connection = > {
2020-05-04 20:01:56 +02:00
logger . warn ( 'Connection ' + response . id + ' already exists in connections list' ) ;
2018-04-26 15:33:47 +02:00
} )
. catch ( openViduError = > {
const connection = new Connection ( this , response ) ;
this . remoteConnections [ response . id ] = connection ;
this . ee . emitEvent ( 'connectionCreated' , [ new ConnectionEvent ( false , this , 'connectionCreated' , connection , '' ) ] ) ;
} ) ;
}
/ * *
* @hidden
* /
onParticipantLeft ( msg ) : void {
2018-07-05 14:05:09 +02:00
this . getRemoteConnection ( msg . connectionId , 'Remote connection ' + msg . connectionId + " unknown when 'onParticipantLeft'. " +
2018-04-26 15:33:47 +02:00
'Existing remote connections: ' + JSON . stringify ( Object . keys ( this . remoteConnections ) ) )
. then ( connection = > {
if ( ! ! connection . stream ) {
const stream = connection . stream ;
const streamEvent = new StreamEvent ( true , this , 'streamDestroyed' , stream , msg . reason ) ;
this . ee . emitEvent ( 'streamDestroyed' , [ streamEvent ] ) ;
2018-07-03 15:35:08 +02:00
streamEvent . callDefaultBehavior ( ) ;
2018-04-26 15:33:47 +02:00
delete this . remoteStreamsCreated [ stream . streamId ] ;
2019-07-04 16:28:14 +02:00
if ( Object . keys ( this . remoteStreamsCreated ) . length === 0 ) {
this . isFirstIonicIosSubscriber = true ;
this . countDownForIonicIosSubscribersActive = true ;
}
2018-04-26 15:33:47 +02:00
}
delete this . remoteConnections [ connection . connectionId ] ;
this . ee . emitEvent ( 'connectionDestroyed' , [ new ConnectionEvent ( false , this , 'connectionDestroyed' , connection , msg . reason ) ] ) ;
} )
. catch ( openViduError = > {
2020-05-04 20:01:56 +02:00
logger . error ( openViduError ) ;
2018-04-26 15:33:47 +02:00
} ) ;
}
/ * *
* @hidden
* /
onParticipantPublished ( response : ConnectionOptions ) : void {
const afterConnectionFound = ( connection ) = > {
this . remoteConnections [ connection . connectionId ] = connection ;
if ( ! this . remoteStreamsCreated [ connection . stream . streamId ] ) {
// Avoid race condition between stream.subscribe() in "onParticipantPublished" and in "joinRoom" rpc callback
// This condition is false if openvidu-server sends "participantPublished" event to a subscriber participant that has
// already subscribed to certain stream in the callback of "joinRoom" method
this . ee . emitEvent ( 'streamCreated' , [ new StreamEvent ( false , this , 'streamCreated' , connection . stream , '' ) ] ) ;
}
this . remoteStreamsCreated [ connection . stream . streamId ] = true ;
} ;
// Get the existing Connection created on 'onParticipantJoined' for
// existing participants or create a new one for new participants
let connection : Connection ;
this . getRemoteConnection ( response . id , "Remote connection '" + response . id + "' unknown when 'onParticipantPublished'. " +
'Existing remote connections: ' + JSON . stringify ( Object . keys ( this . remoteConnections ) ) )
. then ( con = > {
// Update existing Connection
connection = con ;
response . metadata = con . data ;
connection . options = response ;
connection . initRemoteStreams ( response . streams ) ;
afterConnectionFound ( connection ) ;
} )
. catch ( openViduError = > {
// Create new Connection
connection = new Connection ( this , response ) ;
afterConnectionFound ( connection ) ;
} ) ;
}
/ * *
* @hidden
* /
onParticipantUnpublished ( msg ) : void {
2018-07-05 17:50:49 +02:00
if ( msg . connectionId === this . connection . connectionId ) {
// Your stream has been forcedly unpublished from the session
this . stopPublisherStream ( msg . reason ) ;
} else {
this . getRemoteConnection ( msg . connectionId , "Remote connection '" + msg . connectionId + "' unknown when 'onParticipantUnpublished'. " +
'Existing remote connections: ' + JSON . stringify ( Object . keys ( this . remoteConnections ) ) )
2018-04-26 15:33:47 +02:00
2018-07-05 17:50:49 +02:00
. then ( connection = > {
2018-04-26 15:33:47 +02:00
2018-07-05 17:50:49 +02:00
const streamEvent = new StreamEvent ( true , this , 'streamDestroyed' , connection . stream , msg . reason ) ;
this . ee . emitEvent ( 'streamDestroyed' , [ streamEvent ] ) ;
streamEvent . callDefaultBehavior ( ) ;
2018-04-26 15:33:47 +02:00
2018-07-05 17:50:49 +02:00
// Deleting the remote stream
const streamId : string = connection . stream . streamId ;
delete this . remoteStreamsCreated [ streamId ] ;
2019-07-04 16:28:14 +02:00
if ( Object . keys ( this . remoteStreamsCreated ) . length === 0 ) {
this . isFirstIonicIosSubscriber = true ;
this . countDownForIonicIosSubscribersActive = true ;
}
2018-07-05 17:50:49 +02:00
connection . removeStream ( streamId ) ;
} )
. catch ( openViduError = > {
2020-05-04 20:01:56 +02:00
logger . error ( openViduError ) ;
2018-07-05 17:50:49 +02:00
} ) ;
}
2018-04-26 15:33:47 +02:00
}
/ * *
* @hidden
* /
onParticipantEvicted ( msg ) : void {
2018-07-05 14:05:09 +02:00
if ( msg . connectionId === this . connection . connectionId ) {
// You have been evicted from the session
if ( ! ! this . sessionId && ! this . connection . disposed ) {
this . leave ( true , msg . reason ) ;
}
}
2018-04-26 15:33:47 +02:00
}
/ * *
* @hidden
* /
onNewMessage ( msg ) : void {
2020-05-04 20:01:56 +02:00
logger . info ( 'New signal: ' + JSON . stringify ( msg ) ) ;
2018-04-26 15:33:47 +02:00
2019-09-16 10:00:38 +02:00
const strippedType : string = ! ! msg . type ? msg . type . replace ( /^(signal:)/ , '' ) : undefined ;
2018-04-26 15:33:47 +02:00
2019-09-16 10:00:38 +02:00
if ( ! ! msg . from ) {
// Signal sent by other client
this . getConnection ( msg . from , "Connection '" + msg . from + "' unknow when 'onNewMessage'. Existing remote connections: "
+ JSON . stringify ( Object . keys ( this . remoteConnections ) ) + '. Existing local connection: ' + this . connection . connectionId )
. then ( connection = > {
this . ee . emitEvent ( 'signal' , [ new SignalEvent ( this , strippedType , msg . data , connection ) ] ) ;
if ( msg . type !== 'signal' ) {
this . ee . emitEvent ( msg . type , [ new SignalEvent ( this , strippedType , msg . data , connection ) ] ) ;
}
} )
. catch ( openViduError = > {
2020-05-04 20:01:56 +02:00
logger . error ( openViduError ) ;
2019-09-16 10:00:38 +02:00
} ) ;
} else {
// Signal sent by server
this . ee . emitEvent ( 'signal' , [ new SignalEvent ( this , strippedType , msg . data , undefined ) ] ) ;
if ( msg . type !== 'signal' ) {
this . ee . emitEvent ( msg . type , [ new SignalEvent ( this , strippedType , msg . data , undefined ) ] ) ;
}
}
2018-04-26 15:33:47 +02:00
}
2018-07-03 15:35:08 +02:00
/ * *
* @hidden
* /
onStreamPropertyChanged ( msg ) : void {
2018-08-28 17:44:03 +02:00
const callback = ( connection : Connection ) = > {
if ( ! ! connection . stream && connection . stream . streamId === msg . streamId ) {
const stream = connection . stream ;
let oldValue ;
switch ( msg . property ) {
case 'audioActive' :
oldValue = stream . audioActive ;
msg . newValue = msg . newValue === 'true' ;
stream . audioActive = msg . newValue ;
break ;
case 'videoActive' :
oldValue = stream . videoActive ;
msg . newValue = msg . newValue === 'true' ;
stream . videoActive = msg . newValue ;
break ;
case 'videoDimensions' :
oldValue = stream . videoDimensions ;
msg . newValue = JSON . parse ( JSON . parse ( msg . newValue ) ) ;
stream . videoDimensions = msg . newValue ;
break ;
case 'filter' :
oldValue = stream . filter ;
msg . newValue = ( Object . keys ( msg . newValue ) . length > 0 ) ? msg.newValue : undefined ;
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 ;
2018-08-28 11:24:26 +02:00
}
2018-08-28 17:44:03 +02:00
} else {
delete stream . filter ;
}
msg . newValue = stream . filter ;
break ;
2018-07-03 15:35:08 +02:00
}
2018-08-28 17:44:03 +02:00
this . ee . emitEvent ( 'streamPropertyChanged' , [ new StreamPropertyChangedEvent ( this , stream , msg . property , msg . newValue , oldValue , msg . reason ) ] ) ;
2019-03-05 10:22:44 +01:00
if ( ! ! stream . streamManager ) {
stream . streamManager . emitEvent ( 'streamPropertyChanged' , [ new StreamPropertyChangedEvent ( stream . streamManager , stream , msg . property , msg . newValue , oldValue , msg . reason ) ] ) ;
}
2018-08-28 17:44:03 +02:00
} else {
2020-05-04 20:01:56 +02:00
logger . error ( "No stream with streamId '" + msg . streamId + "' found for connection '" + msg . connectionId + "' on 'streamPropertyChanged' event" ) ;
2018-08-28 17:44:03 +02:00
}
} ;
if ( msg . connectionId === this . connection . connectionId ) {
// Your stream has been forcedly changed (filter feature)
callback ( this . connection ) ;
} else {
this . getRemoteConnection ( msg . connectionId , 'Remote connection ' + msg . connectionId + " unknown when 'onStreamPropertyChanged'. " +
'Existing remote connections: ' + JSON . stringify ( Object . keys ( this . remoteConnections ) ) )
. then ( connection = > {
callback ( connection ) ;
} )
. catch ( openViduError = > {
2020-05-04 20:01:56 +02:00
logger . error ( openViduError ) ;
2018-08-28 17:44:03 +02:00
} ) ;
}
2018-07-03 15:35:08 +02:00
}
2018-04-26 15:33:47 +02:00
/ * *
* @hidden
* /
recvIceCandidate ( msg ) : void {
2018-08-31 15:07:34 +02:00
const candidate : RTCIceCandidate = {
2018-04-26 15:33:47 +02:00
candidate : msg.candidate ,
2018-08-31 15:07:34 +02:00
component : msg.component ,
foundation : msg.foundation ,
port : msg.port ,
priority : msg.priority ,
protocol : msg.protocol ,
relatedAddress : msg.relatedAddress ,
relatedPort : msg.relatedPort ,
2018-04-26 15:33:47 +02:00
sdpMid : msg.sdpMid ,
2018-06-11 13:08:30 +02:00
sdpMLineIndex : msg.sdpMLineIndex ,
2018-08-31 15:07:34 +02:00
tcpType : msg.tcpType ,
usernameFragment : msg.usernameFragment ,
type : msg . type ,
2018-06-11 13:08:30 +02:00
toJSON : ( ) = > {
return { candidate : msg.candidate } ;
}
2018-04-26 15:33:47 +02:00
} ;
2019-02-26 14:33:26 +01:00
this . getConnection ( msg . senderConnectionId , 'Connection not found for connectionId ' + msg . senderConnectionId + ' owning endpoint ' + msg . endpointName + '. Ice candidate will be ignored: ' + candidate )
2018-06-11 13:08:30 +02:00
. then ( connection = > {
const stream = connection . stream ;
2019-06-03 17:16:18 +02:00
stream . getWebRtcPeer ( ) . addIceCandidate ( candidate ) . catch ( error = > {
2020-05-04 20:01:56 +02:00
logger . error ( 'Error adding candidate for ' + stream . streamId
2019-06-03 17:16:18 +02:00
+ ' stream of endpoint ' + msg . endpointName + ': ' + error ) ;
} ) ;
2018-06-11 13:08:30 +02:00
} )
2018-04-26 15:33:47 +02:00
. catch ( openViduError = > {
2020-05-04 20:01:56 +02:00
logger . error ( openViduError ) ;
2018-04-26 15:33:47 +02:00
} ) ;
}
/ * *
* @hidden
* /
onSessionClosed ( msg ) : void {
2020-05-04 20:01:56 +02:00
logger . info ( 'Session closed: ' + JSON . stringify ( msg ) ) ;
2018-07-05 14:05:09 +02:00
const s = msg . sessionId ;
2018-04-26 15:33:47 +02:00
if ( s !== undefined ) {
this . ee . emitEvent ( 'session-closed' , [ {
session : s
} ] ) ;
} else {
2020-05-04 20:01:56 +02:00
logger . warn ( 'Session undefined on session closed' , msg ) ;
2018-04-26 15:33:47 +02:00
}
}
/ * *
* @hidden
* /
2019-01-14 10:18:04 +01:00
onLostConnection ( reason : string ) : void {
2020-05-04 20:01:56 +02:00
logger . warn ( 'Lost connection in Session ' + this . sessionId ) ;
2018-04-26 15:33:47 +02:00
if ( ! ! this . sessionId && ! this . connection . disposed ) {
2019-01-14 10:18:04 +01:00
this . leave ( true , reason ) ;
2018-04-26 15:33:47 +02:00
}
}
2018-07-10 12:40:14 +02:00
/ * *
2018-07-10 10:35:14 +02:00
* @hidden
* /
onRecoveredConnection ( ) : void {
2020-05-04 20:01:56 +02:00
logger . info ( 'Recovered connection in Session ' + this . sessionId ) ;
2020-02-14 20:51:52 +01:00
this . reconnectBrokenStreams ( ) ;
this . ee . emitEvent ( 'reconnected' , [ ] ) ;
2018-07-10 10:35:14 +02:00
}
2018-04-26 15:33:47 +02:00
/ * *
* @hidden
* /
onMediaError ( params ) : void {
2020-05-04 20:01:56 +02:00
logger . error ( 'Media error: ' + JSON . stringify ( params ) ) ;
2018-04-26 15:33:47 +02:00
const err = params . error ;
if ( err ) {
this . ee . emitEvent ( 'error-media' , [ {
error : err
} ] ) ;
} else {
2020-05-04 20:01:56 +02:00
logger . warn ( 'Received undefined media error. Params:' , params ) ;
2018-04-26 15:33:47 +02:00
}
}
/ * *
* @hidden
* /
onRecordingStarted ( response ) : void {
this . ee . emitEvent ( 'recordingStarted' , [ new RecordingEvent ( this , 'recordingStarted' , response . id , response . name ) ] ) ;
}
/ * *
* @hidden
* /
onRecordingStopped ( response ) : void {
2019-02-21 14:48:26 +01:00
this . ee . emitEvent ( 'recordingStopped' , [ new RecordingEvent ( this , 'recordingStopped' , response . id , response . name , response . reason ) ] ) ;
2018-04-26 15:33:47 +02:00
}
2018-08-03 14:56:36 +02:00
/ * *
* @hidden
* response = { connectionId : string , streamId : string , type : string , data : Object }
* /
onFilterEventDispatched ( response ) : void {
const connectionId : string = response . connectionId ;
const streamId : string = response . streamId ;
this . getConnection ( connectionId , 'No connection found for connectionId ' + connectionId )
. then ( connection = > {
2020-05-04 20:01:56 +02:00
logger . info ( 'Filter event dispatched' ) ;
2018-08-03 14:56:36 +02:00
const stream : Stream = connection . stream ;
2018-08-28 11:24:26 +02:00
stream . filter . handlers [ response . eventType ] ( new FilterEvent ( stream . filter , response . eventType , response . data ) ) ;
2018-08-03 14:56:36 +02:00
} ) ;
}
2020-02-14 20:51:52 +01:00
/ * *
* @hidden
* /
reconnectBrokenStreams ( ) : void {
2020-05-04 20:01:56 +02:00
logger . info ( 'Re-establishing media connections...' ) ;
2020-02-14 21:54:23 +01:00
let someReconnection = false ;
2020-02-14 20:51:52 +01:00
// Re-establish Publisher stream
if ( ! ! this . connection . stream && this . connection . stream . streamIceConnectionStateBroken ( ) ) {
2020-05-04 20:01:56 +02:00
logger . warn ( 'Re-establishing Publisher ' + this . connection . stream . streamId ) ;
2020-02-14 20:51:52 +01:00
this . connection . stream . initWebRtcPeerSend ( true ) ;
2020-02-14 21:54:23 +01:00
someReconnection = true ;
2020-02-14 20:51:52 +01:00
}
// Re-establish Subscriber streams
for ( let remoteConnection of Object . values ( this . remoteConnections ) ) {
if ( ! ! remoteConnection . stream && remoteConnection . stream . streamIceConnectionStateBroken ( ) ) {
2020-05-04 20:01:56 +02:00
logger . warn ( 'Re-establishing Subscriber ' + remoteConnection . stream . streamId ) ;
2020-02-14 20:51:52 +01:00
remoteConnection . stream . initWebRtcPeerReceive ( true ) ;
2020-02-14 21:54:23 +01:00
someReconnection = true ;
2020-02-14 20:51:52 +01:00
}
}
2020-02-14 21:54:23 +01:00
if ( ! someReconnection ) {
2020-05-04 20:01:56 +02:00
logger . info ( 'There were no media streams in need of a reconnection' ) ;
2020-02-14 21:54:23 +01:00
}
2020-02-14 20:51:52 +01:00
}
2018-04-26 15:33:47 +02:00
/ * *
* @hidden
* /
emitEvent ( type : string , eventArray : any [ ] ) : void {
this . ee . emitEvent ( type , eventArray ) ;
}
/ * *
* @hidden
* /
leave ( forced : boolean , reason : string ) : void {
forced = ! ! forced ;
2020-05-04 20:01:56 +02:00
logger . info ( 'Leaving Session (forced=' + forced + ')' ) ;
2018-04-26 15:33:47 +02:00
2018-05-03 10:58:26 +02:00
if ( ! ! this . connection ) {
if ( ! this . connection . disposed && ! forced ) {
this . openvidu . sendRequest ( 'leaveRoom' , ( error , response ) = > {
if ( error ) {
2020-05-04 20:01:56 +02:00
logger . error ( error ) ;
2018-05-03 10:58:26 +02:00
}
this . openvidu . closeWs ( ) ;
} ) ;
} else {
2018-04-26 15:33:47 +02:00
this . openvidu . closeWs ( ) ;
2018-05-03 10:58:26 +02:00
}
2018-04-26 15:33:47 +02:00
2018-07-05 17:50:49 +02:00
this . stopPublisherStream ( reason ) ;
2018-04-26 15:33:47 +02:00
2018-05-03 10:58:26 +02:00
if ( ! this . connection . disposed ) {
// Make Session object dispatch 'sessionDisconnected' event (if it is not already disposed)
const sessionDisconnectEvent = new SessionDisconnectedEvent ( this , reason ) ;
this . ee . emitEvent ( 'sessionDisconnected' , [ sessionDisconnectEvent ] ) ;
2018-07-03 15:35:08 +02:00
sessionDisconnectEvent . callDefaultBehavior ( ) ;
2018-05-03 10:58:26 +02:00
}
} else {
2020-05-04 20:01:56 +02:00
logger . warn ( 'You were not connected to the session ' + this . sessionId ) ;
2018-04-26 15:33:47 +02:00
}
}
2020-06-30 10:48:55 +02:00
/ * *
* @hidden
* /
initializeParams ( token : string ) {
const joinParams = {
token : ( ! ! token ) ? token : '' ,
session : this.sessionId ,
platform : ! ! platform . description ? platform . description : 'unknown' ,
metadata : ! ! this . options . metadata ? this . options . metadata : '' ,
secret : this.openvidu.getSecret ( ) ,
recorder : this.openvidu.getRecorder ( )
} ;
return joinParams ;
}
2018-04-26 15:33:47 +02:00
/* Private methods */
private connectAux ( token : string ) : Promise < any > {
return new Promise ( ( resolve , reject ) = > {
this . openvidu . startWs ( ( error ) = > {
if ( ! ! error ) {
reject ( error ) ;
} else {
2020-06-30 10:48:55 +02:00
const joinParams = this . initializeParams ( token ) ;
2018-04-26 15:33:47 +02:00
this . openvidu . sendRequest ( 'joinRoom' , joinParams , ( error , response ) = > {
2018-05-03 10:58:26 +02:00
if ( ! ! error ) {
reject ( error ) ;
} else {
2018-07-04 15:04:47 +02:00
// Initialize capabilities object with the role
this . capabilities = {
2018-07-04 19:10:21 +02:00
subscribe : true ,
2018-07-09 15:45:20 +02:00
publish : this.openvidu.role !== 'SUBSCRIBER' ,
forceUnpublish : this.openvidu.role === 'MODERATOR' ,
forceDisconnect : this.openvidu.role === 'MODERATOR'
2018-07-04 15:04:47 +02:00
} ;
2018-05-03 10:58:26 +02:00
// Initialize local Connection object with values returned by openvidu-server
this . connection = new Connection ( this ) ;
this . connection . connectionId = response . id ;
2019-01-09 17:35:34 +01:00
this . connection . creationTime = response . createdAt ;
2018-05-03 10:58:26 +02:00
this . connection . data = response . metadata ;
2019-01-14 10:18:04 +01:00
this . connection . rpcSessionId = response . sessionId ;
2018-05-03 10:58:26 +02:00
// Initialize remote Connections with value returned by openvidu-server
const events = {
connections : new Array < Connection > ( ) ,
streams : new Array < Stream > ( )
} ;
const existingParticipants : ConnectionOptions [ ] = response . value ;
existingParticipants . forEach ( participant = > {
const connection = new Connection ( this , participant ) ;
this . remoteConnections [ connection . connectionId ] = connection ;
events . connections . push ( connection ) ;
if ( ! ! connection . stream ) {
this . remoteStreamsCreated [ connection . stream . streamId ] = true ;
events . streams . push ( connection . stream ) ;
}
} ) ;
2018-04-26 15:33:47 +02:00
2018-05-03 10:58:26 +02:00
// Own 'connectionCreated' event
this . ee . emitEvent ( 'connectionCreated' , [ new ConnectionEvent ( false , this , 'connectionCreated' , this . connection , '' ) ] ) ;
2018-04-26 15:33:47 +02:00
2018-05-03 10:58:26 +02:00
// One 'connectionCreated' event for each existing connection in the session
events . connections . forEach ( connection = > {
this . ee . emitEvent ( 'connectionCreated' , [ new ConnectionEvent ( false , this , 'connectionCreated' , connection , '' ) ] ) ;
} ) ;
// One 'streamCreated' event for each active stream in the session
events . streams . forEach ( stream = > {
this . ee . emitEvent ( 'streamCreated' , [ new StreamEvent ( false , this , 'streamCreated' , stream , '' ) ] ) ;
} ) ;
resolve ( ) ;
}
2018-04-26 15:33:47 +02:00
} ) ;
}
} ) ;
} ) ;
}
2018-07-05 17:50:49 +02:00
private stopPublisherStream ( reason : string ) {
if ( ! ! this . connection . stream ) {
// Dispose Publisher's local stream
this . connection . stream . disposeWebRtcPeer ( ) ;
if ( this . connection . stream . isLocalStreamPublished ) {
// Make Publisher object dispatch 'streamDestroyed' event if the Stream was published
this . connection . stream . ee . emitEvent ( 'local-stream-destroyed' , [ reason ] ) ;
}
}
}
2018-04-26 15:33:47 +02:00
private stringClientMetadata ( metadata : any ) : string {
if ( typeof metadata !== 'string' ) {
return JSON . stringify ( metadata ) ;
} else {
return metadata ;
}
}
2020-06-30 10:48:55 +02:00
protected getConnection ( connectionId : string , errorMessage : string ) : Promise < Connection > {
2018-04-26 15:33:47 +02:00
return new Promise < Connection > ( ( resolve , reject ) = > {
const connection = this . remoteConnections [ connectionId ] ;
if ( ! ! connection ) {
// Resolve remote connection
resolve ( connection ) ;
} else {
if ( this . connection . connectionId === connectionId ) {
// Resolve local connection
resolve ( this . connection ) ;
} else {
// Connection not found. Reject with OpenViduError
reject ( new OpenViduError ( OpenViduErrorName . GENERIC_ERROR , errorMessage ) ) ;
}
}
} ) ;
}
private getRemoteConnection ( connectionId : string , errorMessage : string ) : Promise < Connection > {
return new Promise < Connection > ( ( resolve , reject ) = > {
const connection = this . remoteConnections [ connectionId ] ;
if ( ! ! connection ) {
// Resolve remote connection
resolve ( connection ) ;
} else {
// Remote connection not found. Reject with OpenViduError
reject ( new OpenViduError ( OpenViduErrorName . GENERIC_ERROR , errorMessage ) ) ;
}
} ) ;
}
2019-05-20 14:11:14 +02:00
private processToken ( token : string ) : void {
const match = token . match ( /^(wss?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/ ) ;
if ( ! ! match ) {
const url = {
protocol : match [ 1 ] ,
host : match [ 2 ] ,
hostname : match [ 3 ] ,
port : match [ 4 ] ,
pathname : match [ 5 ] ,
search : match [ 6 ] ,
hash : match [ 7 ]
2019-05-10 13:24:07 +02:00
} ;
2019-05-20 14:11:14 +02:00
const params = token . split ( '?' ) ;
const queryParams = decodeURI ( params [ 1 ] )
. split ( '&' )
. map ( param = > param . split ( '=' ) )
. reduce ( ( values , [ key , value ] ) = > {
values [ key ] = value
return values
} , { } ) ;
this . sessionId = < string > queryParams [ 'sessionId' ] ;
const secret = queryParams [ 'secret' ] ;
const recorder = queryParams [ 'recorder' ] ;
2020-01-25 16:53:10 +01:00
const coturnIp = queryParams [ 'coturnIp' ] ;
2019-05-20 14:11:14 +02:00
const turnUsername = queryParams [ 'turnUsername' ] ;
const turnCredential = queryParams [ 'turnCredential' ] ;
const role = queryParams [ 'role' ] ;
const webrtcStatsInterval = queryParams [ 'webrtcStatsInterval' ] ;
const openviduServerVersion = queryParams [ 'version' ] ;
if ( ! ! secret ) {
this . openvidu . secret = secret ;
}
if ( ! ! recorder ) {
this . openvidu . recorder = true ;
}
if ( ! ! turnUsername && ! ! turnCredential ) {
2020-01-25 16:53:10 +01:00
const stunUrl = 'stun:' + coturnIp + ':3478' ;
const turnUrl1 = 'turn:' + coturnIp + ':3478' ;
2019-05-20 14:11:14 +02:00
const turnUrl2 = turnUrl1 + '?transport=tcp' ;
this . openvidu . iceServers = [
{ urls : [ stunUrl ] } ,
{ urls : [ turnUrl1 , turnUrl2 ] , username : turnUsername , credential : turnCredential }
] ;
2020-05-04 20:01:56 +02:00
logger . log ( "STUN/TURN server IP: " + coturnIp ) ;
logger . log ( 'TURN temp credentials [' + turnUsername + ':' + turnCredential + ']' ) ;
2019-05-20 14:11:14 +02:00
}
if ( ! ! role ) {
this . openvidu . role = role ;
}
if ( ! ! webrtcStatsInterval ) {
this . openvidu . webrtcStatsInterval = + webrtcStatsInterval ;
}
if ( ! ! openviduServerVersion ) {
2020-05-04 20:01:56 +02:00
logger . info ( "openvidu-server version: " + openviduServerVersion ) ;
2019-05-20 14:11:14 +02:00
if ( openviduServerVersion !== this . openvidu . libraryVersion ) {
2020-05-04 20:01:56 +02:00
logger . error ( 'OpenVidu Server (' + openviduServerVersion +
2019-05-20 14:11:14 +02:00
') and OpenVidu Browser (' + this . openvidu . libraryVersion +
') versions do NOT match. There may be incompatibilities' )
2019-05-10 13:24:07 +02:00
}
}
2019-05-20 14:11:14 +02:00
this . openvidu . wsUri = 'wss://' + url . host + '/openvidu' ;
this . openvidu . httpUri = 'https://' + url . host ;
} else {
2020-05-04 20:01:56 +02:00
logger . error ( 'Token "' + token + '" is not valid' )
2019-05-20 14:11:14 +02:00
}
2018-05-03 10:58:26 +02:00
}
2019-03-05 10:22:44 +01:00
}