2017-05-10 10:55:31 +02:00
import { Stream } from './Stream' ;
import { OpenViduInternal } from './OpenViduInternal' ;
2017-05-17 11:46:32 +02:00
import { Connection , ConnectionOptions } from './Connection' ;
2017-05-10 10:55:31 +02:00
import EventEmitter = require ( 'wolfy87-eventemitter' ) ;
2017-08-29 17:31:34 +02:00
const SECRET_PARAM = '?secret=' ;
2017-05-10 10:55:31 +02:00
export interface SessionOptions {
sessionId : string ;
participantId : string ;
2017-05-16 16:57:36 +02:00
metadata : string ;
2017-05-10 10:55:31 +02:00
subscribeToStreams? : boolean ;
updateSpeakerInterval? : number ;
thresholdSpeaker? : number ;
}
export class SessionInternal {
private id : string ;
2017-08-29 17:31:34 +02:00
private sessionId : string ;
2017-05-10 10:55:31 +02:00
private ee = new EventEmitter ( ) ;
private streams = { } ;
private participants = { } ;
2017-05-17 11:46:32 +02:00
private participantsSpeaking : Connection [ ] = [ ] ;
2017-05-10 10:55:31 +02:00
private connected = false ;
2017-05-17 11:46:32 +02:00
public localParticipant : Connection ;
2017-05-10 10:55:31 +02:00
private subscribeToStreams : boolean ;
private updateSpeakerInterval : number ;
public thresholdSpeaker : number ;
2017-08-29 17:31:34 +02:00
private options : SessionOptions ;
2017-05-10 10:55:31 +02:00
2017-08-29 17:31:34 +02:00
constructor ( private openVidu : OpenViduInternal , sessionId : string ) {
this . sessionId = this . getUrlWithoutSecret ( sessionId ) ;
2017-05-17 11:46:32 +02:00
this . localParticipant = new Connection ( this . openVidu , true , this ) ;
2017-06-14 14:14:45 +02:00
if ( ! this . openVidu . getWsUri ( ) ) {
2017-08-29 17:31:34 +02:00
this . processOpenViduUrl ( sessionId ) ;
}
}
private processOpenViduUrl ( url : string ) {
this . openVidu . setSecret ( this . getSecretFromUrl ( url ) ) ;
this . openVidu . setWsUri ( this . getFinalUrl ( url ) ) ;
}
private getSecretFromUrl ( url : string ) : string {
let secret = '' ;
if ( url . indexOf ( SECRET_PARAM ) !== - 1 ) {
secret = url . substring ( url . lastIndexOf ( SECRET_PARAM ) + SECRET_PARAM . length , url . length ) ;
}
return secret ;
}
private getUrlWithoutSecret ( url : string ) : string {
2017-10-16 15:49:23 +02:00
if ( ! url ) {
console . error ( 'sessionId is not defined' ) ;
}
2017-08-29 17:31:34 +02:00
if ( url . indexOf ( SECRET_PARAM ) !== - 1 ) {
url = url . substring ( 0 , url . lastIndexOf ( SECRET_PARAM ) ) ;
2017-06-14 14:14:45 +02:00
}
2017-08-29 17:31:34 +02:00
return url ;
2017-05-10 10:55:31 +02:00
}
2017-08-29 17:31:34 +02:00
private getFinalUrl ( url : string ) : string {
url = this . getUrlWithoutSecret ( url ) . substring ( 0 , url . lastIndexOf ( '/' ) ) + '/room' ;
if ( url . indexOf ( ".ngrok.io" ) !== - 1 ) {
2017-07-12 18:56:13 +02:00
// OpenVidu server URL referes to a ngrok IP: secure wss protocol and delete port of URL
2017-08-29 17:31:34 +02:00
url = url . replace ( "ws://" , "wss://" ) ;
2017-07-12 18:56:13 +02:00
let regex = /\.ngrok\.io:\d+/ ;
2017-08-29 17:31:34 +02:00
url = url . replace ( regex , ".ngrok.io" ) ;
} else if ( ( url . indexOf ( "localhost" ) !== - 1 ) || ( url . indexOf ( "127.0.0.1" ) != - 1 ) ) {
2017-07-12 18:56:13 +02:00
// OpenVidu server URL referes to localhost IP
}
2017-08-29 17:31:34 +02:00
return url ;
2017-07-12 18:56:13 +02:00
}
2017-05-10 10:55:31 +02:00
/* NEW METHODS */
connect ( token , callback ) {
this . openVidu . connect ( ( error ) = > {
if ( error ) {
callback ( 'ERROR CONNECTING TO OPENVIDU' ) ;
}
else {
2017-09-12 11:43:44 +02:00
if ( ! token ) {
token = this . randomToken ( ) ;
}
2017-05-10 10:55:31 +02:00
let joinParams = {
2017-05-12 19:44:32 +02:00
token : token ,
session : this.sessionId ,
2017-05-16 16:57:36 +02:00
metadata : this.options.metadata ,
2017-08-29 17:31:34 +02:00
secret : this.openVidu.getSecret ( ) ,
2017-05-10 10:55:31 +02:00
dataChannels : false
}
if ( this . localParticipant ) {
if ( Object . keys ( this . localParticipant . getStreams ( ) ) . some ( streamId = >
this . streams [ streamId ] . isDataChannelEnabled ( ) ) ) {
joinParams . dataChannels = true ;
}
}
this . openVidu . sendRequest ( 'joinRoom' , joinParams , ( error , response ) = > {
if ( error ) {
2017-08-23 21:30:12 +02:00
callback ( error ) ;
2017-05-10 10:55:31 +02:00
} else {
this . connected = true ;
let exParticipants = response . value ;
2017-06-02 18:58:39 +02:00
// IMPORTANT: Update connectionId with value send by server
this . localParticipant . connectionId = response . id ;
this . participants [ response . id ] = this . localParticipant ;
2017-05-10 10:55:31 +02:00
let roomEvent = {
2017-05-17 11:46:32 +02:00
participants : new Array < Connection > ( ) ,
2017-05-10 10:55:31 +02:00
streams : new Array < Stream > ( )
}
let length = exParticipants . length ;
for ( let i = 0 ; i < length ; i ++ ) {
2017-05-22 22:30:08 +02:00
let connection = new Connection ( this . openVidu , false , this ,
2017-05-10 10:55:31 +02:00
exParticipants [ i ] ) ;
2017-05-22 22:30:08 +02:00
connection . creationTime = new Date ( ) . getTime ( ) ;
2017-05-10 10:55:31 +02:00
2017-06-02 18:58:39 +02:00
this . participants [ connection . connectionId ] = connection ;
2017-05-10 10:55:31 +02:00
2017-05-22 22:30:08 +02:00
roomEvent . participants . push ( connection ) ;
2017-05-10 10:55:31 +02:00
2017-05-22 22:30:08 +02:00
let streams = connection . getStreams ( ) ;
2017-05-10 10:55:31 +02:00
for ( let key in streams ) {
roomEvent . streams . push ( streams [ key ] ) ;
if ( this . subscribeToStreams ) {
streams [ key ] . subscribe ( ) ;
}
}
}
2017-05-17 11:46:32 +02:00
// Update local Connection object properties with values returned by server
this . localParticipant . data = response . metadata ;
this . localParticipant . creationTime = new Date ( ) . getTime ( ) ;
// Updates the value of property 'connection' in Session object
this . ee . emitEvent ( 'update-connection-object' , [ { connection : this.localParticipant } ] ) ;
// Own connection created event
this . ee . emitEvent ( 'connectionCreated' , [ { connection : this.localParticipant } ] ) ;
2017-05-22 22:30:08 +02:00
// One connection created event for each existing connection in the session
2017-05-16 16:57:36 +02:00
for ( let part of roomEvent . participants ) {
this . ee . emitEvent ( 'connectionCreated' , [ { connection : part } ] ) ;
}
2017-05-10 10:55:31 +02:00
//if (this.subscribeToStreams) {
for ( let stream of roomEvent . streams ) {
2017-06-13 12:29:15 +02:00
this . ee . emitEvent ( 'streamCreated' , [ { stream } ] ) ;
2017-05-10 10:55:31 +02:00
// Adding the remote stream to the OpenVidu object
this . openVidu . getRemoteStreams ( ) . push ( stream ) ;
}
callback ( undefined ) ;
}
} ) ;
}
} ) ;
}
publish() {
this . openVidu . getCamera ( ) . publish ( ) ;
}
/* NEW METHODS */
configure ( options : SessionOptions ) {
this . options = options ;
this . id = options . sessionId ;
this . subscribeToStreams = options . subscribeToStreams == null ? true : options . subscribeToStreams ;
this . updateSpeakerInterval = options . updateSpeakerInterval || 1500 ;
this . thresholdSpeaker = options . thresholdSpeaker || - 50 ;
this . activateUpdateMainSpeaker ( ) ;
}
getId() {
return this . id ;
}
getSessionId() {
return this . sessionId ;
}
private activateUpdateMainSpeaker() {
setInterval ( ( ) = > {
if ( this . participantsSpeaking . length > 0 ) {
this . ee . emitEvent ( 'update-main-speaker' , [ {
participantId : this.participantsSpeaking [ this . participantsSpeaking . length - 1 ]
} ] ) ;
}
} , this . updateSpeakerInterval ) ;
}
getLocalParticipant() {
return this . localParticipant ;
}
addEventListener ( eventName , listener ) {
this . ee . on ( eventName , listener ) ;
}
addOnceEventListener ( eventName , listener ) {
this . ee . once ( eventName , listener ) ;
}
removeListener ( eventName , listener ) {
this . ee . off ( eventName , listener ) ;
}
2017-05-31 17:31:26 +02:00
removeEvent ( eventName ) {
2017-05-22 23:02:28 +02:00
this . ee . removeEvent ( eventName ) ;
}
2017-05-10 10:55:31 +02:00
emitEvent ( eventName , eventsArray ) {
this . ee . emitEvent ( eventName , eventsArray ) ;
}
subscribe ( stream : Stream ) {
stream . subscribe ( ) ;
}
unsuscribe ( stream ) {
2017-09-22 15:57:59 +02:00
console . info ( "Unsubscribing from " + stream . getId ( ) ) ;
2017-05-10 10:55:31 +02:00
this . openVidu . sendRequest ( 'unsubscribeFromVideo' , {
sender : stream.getId ( )
} ,
function ( error , response ) {
if ( error ) {
2017-09-22 15:57:59 +02:00
console . error ( "Error unsubscribing from Subscriber" , error ) ;
2017-05-10 10:55:31 +02:00
} else {
console . info ( "Unsubscribed correctly from " + stream . getId ( ) ) ;
}
} ) ;
}
onParticipantPublished ( options ) {
2017-09-22 15:57:59 +02:00
// Get the existing Connection created on 'onParticipantJoined' for
// existing participants or create a new one for new participants
let connection = this . participants [ options . id ] ;
if ( connection ) {
// Update existing Connection
2017-10-02 15:17:47 +02:00
options . metadata = connection . data ;
2017-09-22 15:57:59 +02:00
connection . options = options ;
connection . initStreams ( options ) ;
} else {
// Create new Connection
connection = new Connection ( this . openVidu , false , this , options ) ;
}
2017-05-10 10:55:31 +02:00
2017-06-02 18:58:39 +02:00
let pid = connection . connectionId ;
2017-05-10 10:55:31 +02:00
if ( ! ( pid in this . participants ) ) {
2017-09-22 15:57:59 +02:00
console . debug ( "Remote Connection not found in connections list by its id [" + pid + "]" ) ;
2017-05-10 10:55:31 +02:00
} else {
2017-09-22 15:57:59 +02:00
console . debug ( "Remote Connection found in connections list by its id [" + pid + "]" ) ;
2017-05-10 10:55:31 +02:00
}
2017-09-22 15:57:59 +02:00
2017-10-16 15:49:23 +02:00
2017-05-22 22:30:08 +02:00
this . participants [ pid ] = connection ;
2017-05-10 10:55:31 +02:00
2017-05-22 22:30:08 +02:00
this . ee . emitEvent ( 'participant-published' , [ { connection } ] ) ;
2017-05-10 10:55:31 +02:00
2017-05-22 22:30:08 +02:00
let streams = connection . getStreams ( ) ;
2017-05-10 10:55:31 +02:00
for ( let key in streams ) {
let stream = streams [ key ] ;
if ( this . subscribeToStreams ) {
stream . subscribe ( ) ;
}
2017-06-13 12:29:15 +02:00
this . ee . emitEvent ( 'streamCreated' , [ { stream } ] ) ;
2017-05-10 10:55:31 +02:00
// Adding the remote stream to the OpenVidu object
this . openVidu . getRemoteStreams ( ) . push ( stream ) ;
}
}
onParticipantJoined ( msg ) {
2017-05-22 22:30:08 +02:00
let connection = new Connection ( this . openVidu , false , this , msg ) ;
connection . creationTime = new Date ( ) . getTime ( ) ;
2017-05-10 10:55:31 +02:00
2017-06-02 18:58:39 +02:00
let pid = connection . connectionId ;
2017-05-10 10:55:31 +02:00
if ( ! ( pid in this . participants ) ) {
2017-05-22 22:30:08 +02:00
this . participants [ pid ] = connection ;
2017-05-10 10:55:31 +02:00
} else {
//use existing so that we don't lose streams info
2017-09-22 15:57:59 +02:00
console . warn ( "Connection already exists in connections list with " +
"the same connectionId, old:" , this . participants [ pid ] , ", joined now:" , connection ) ;
2017-05-22 22:30:08 +02:00
connection = this . participants [ pid ] ;
2017-05-10 10:55:31 +02:00
}
this . ee . emitEvent ( 'participant-joined' , [ {
2017-05-22 22:30:08 +02:00
connection : connection
2017-05-10 10:55:31 +02:00
} ] ) ;
2017-05-16 16:57:36 +02:00
this . ee . emitEvent ( 'connectionCreated' , [ {
2017-05-22 22:30:08 +02:00
connection : connection
2017-05-16 16:57:36 +02:00
} ] ) ;
2017-05-31 17:31:26 +02:00
2017-05-10 10:55:31 +02:00
}
onParticipantLeft ( msg ) {
2017-05-22 22:30:08 +02:00
let connection = this . participants [ msg . name ] ;
2017-05-10 10:55:31 +02:00
2017-05-22 22:30:08 +02:00
if ( connection !== undefined ) {
2017-05-10 10:55:31 +02:00
delete this . participants [ msg . name ] ;
this . ee . emitEvent ( 'participant-left' , [ {
2017-05-22 22:30:08 +02:00
connection : connection
2017-05-10 10:55:31 +02:00
} ] ) ;
2017-05-22 22:30:08 +02:00
let streams = connection . getStreams ( ) ;
2017-05-10 10:55:31 +02:00
for ( let key in streams ) {
2017-06-13 12:29:15 +02:00
this . ee . emitEvent ( 'streamDestroyed' , [ {
2017-05-10 10:55:31 +02:00
stream : streams [ key ] ,
2017-06-13 12:29:15 +02:00
preventDefault : ( ) = > { this . ee . removeEvent ( 'stream-destroyed-default' ) ; }
2017-05-10 10:55:31 +02:00
} ] ) ;
2017-06-13 12:29:15 +02:00
this . ee . emitEvent ( 'stream-destroyed-default' , [ {
2017-05-10 10:55:31 +02:00
stream : streams [ key ]
} ] ) ;
// Deleting the removed stream from the OpenVidu object
let index = this . openVidu . getRemoteStreams ( ) . indexOf ( streams [ key ] ) ;
this . openVidu . getRemoteStreams ( ) . splice ( index , 1 ) ;
}
2017-05-22 22:30:08 +02:00
connection . dispose ( ) ;
2017-05-10 10:55:31 +02:00
2017-05-17 11:46:32 +02:00
this . ee . emitEvent ( 'connectionDestroyed' , [ {
2017-05-22 22:30:08 +02:00
connection : connection
2017-05-17 11:46:32 +02:00
} ] ) ;
2017-05-10 10:55:31 +02:00
} else {
console . warn ( "Participant " + msg . name
+ " unknown. Participants: "
+ JSON . stringify ( this . participants ) ) ;
}
} ;
onParticipantEvicted ( msg ) {
this . ee . emitEvent ( 'participant-evicted' , [ {
localParticipant : this.localParticipant
} ] ) ;
} ;
onNewMessage ( msg ) {
2017-09-22 15:57:59 +02:00
console . info ( "New message: " + JSON . stringify ( msg ) ) ;
2017-05-10 10:55:31 +02:00
let room = msg . room ;
let user = msg . user ;
let message = msg . message ;
if ( user !== undefined ) {
this . ee . emitEvent ( 'newMessage' , [ {
room : room ,
user : user ,
message : message
} ] ) ;
} else {
console . warn ( "User undefined in new message:" , msg ) ;
}
}
recvIceCandidate ( msg ) {
let candidate = {
candidate : msg.candidate ,
sdpMid : msg.sdpMid ,
sdpMLineIndex : msg.sdpMLineIndex
}
2017-05-22 22:30:08 +02:00
let connection = this . participants [ msg . endpointName ] ;
if ( ! connection ) {
2017-05-10 10:55:31 +02:00
console . error ( "Participant not found for endpoint " +
msg . endpointName + ". Ice candidate will be ignored." ,
candidate ) ;
return ;
}
2017-05-22 22:30:08 +02:00
let streams = connection . getStreams ( ) ;
2017-05-10 10:55:31 +02:00
for ( let key in streams ) {
let stream = streams [ key ] ;
stream . getWebRtcPeer ( ) . addIceCandidate ( candidate , function ( error ) {
if ( error ) {
console . error ( "Error adding candidate for " + key
+ " stream of endpoint " + msg . endpointName
+ ": " + error ) ;
}
} ) ;
}
}
onRoomClosed ( msg ) {
2017-09-22 15:57:59 +02:00
console . info ( "Room closed: " + JSON . stringify ( msg ) ) ;
2017-05-10 10:55:31 +02:00
let room = msg . room ;
if ( room !== undefined ) {
this . ee . emitEvent ( 'room-closed' , [ {
room : room
} ] ) ;
} else {
console . warn ( "Room undefined in on room closed" , msg ) ;
}
}
onLostConnection() {
if ( ! this . connected ) {
2017-05-31 17:31:26 +02:00
console . warn ( 'Not connected to room: if you are not debugging, this is probably a certificate error' ) ;
if ( window . confirm ( 'If you are not debugging, this is probably a certificate error at \"' + this . openVidu . getOpenViduServerURL ( ) + '\"\n\nClick OK to navigate and accept it' ) ) {
2017-06-05 11:13:25 +02:00
location . assign ( this . openVidu . getOpenViduServerURL ( ) + '/accept-certificate' ) ;
2017-05-31 17:31:26 +02:00
} ;
2017-05-10 10:55:31 +02:00
return ;
}
2017-09-22 15:57:59 +02:00
console . warn ( 'Lost connection in Session ' + this . id ) ;
2017-05-10 10:55:31 +02:00
let room = this . id ;
if ( room !== undefined ) {
this . ee . emitEvent ( 'lost-connection' , [ { room } ] ) ;
} else {
console . warn ( 'Room undefined when lost connection' ) ;
}
}
onMediaError ( params ) {
console . error ( "Media error: " + JSON . stringify ( params ) ) ;
let error = params . error ;
if ( error ) {
this . ee . emitEvent ( 'error-media' , [ {
error : error
} ] ) ;
} else {
console . warn ( "Received undefined media error. Params:" , params ) ;
}
}
/ *
* forced means the user was evicted , no need to send the 'leaveRoom' request
* /
leave ( forced , jsonRpcClient ) {
forced = ! ! forced ;
2017-09-22 15:57:59 +02:00
console . info ( "Leaving Session (forced=" + forced + ")" ) ;
2017-05-10 10:55:31 +02:00
if ( this . connected && ! forced ) {
this . openVidu . sendRequest ( 'leaveRoom' , function ( error , response ) {
if ( error ) {
console . error ( error ) ;
}
jsonRpcClient . close ( ) ;
} ) ;
} else {
jsonRpcClient . close ( ) ;
}
this . connected = false ;
if ( this . participants ) {
for ( let pid in this . participants ) {
this . participants [ pid ] . dispose ( ) ;
delete this . participants [ pid ] ;
}
}
}
disconnect ( stream : Stream ) {
2017-05-22 22:30:08 +02:00
let connection = stream . getParticipant ( ) ;
if ( ! connection ) {
2017-05-10 10:55:31 +02:00
console . error ( "Stream to disconnect has no participant" , stream ) ;
return ;
}
2017-06-02 18:58:39 +02:00
delete this . participants [ connection . connectionId ] ;
2017-05-22 22:30:08 +02:00
connection . dispose ( ) ;
2017-05-10 10:55:31 +02:00
2017-05-22 22:30:08 +02:00
if ( connection === this . localParticipant ) {
2017-05-10 10:55:31 +02:00
2017-09-22 15:57:59 +02:00
console . info ( "Unpublishing my media (I'm " + connection . connectionId + ")" ) ;
2017-05-10 10:55:31 +02:00
delete this . localParticipant ;
this . openVidu . sendRequest ( 'unpublishVideo' , function ( error , response ) {
if ( error ) {
console . error ( error ) ;
} else {
console . info ( "Media unpublished correctly" ) ;
}
} ) ;
} else {
this . unsuscribe ( stream ) ;
}
}
unpublish ( stream : Stream ) {
2017-05-22 22:30:08 +02:00
let connection = stream . getParticipant ( ) ;
if ( ! connection ) {
2017-05-10 10:55:31 +02:00
console . error ( "Stream to disconnect has no participant" , stream ) ;
return ;
}
2017-05-22 22:30:08 +02:00
if ( connection === this . localParticipant ) {
2017-05-10 10:55:31 +02:00
2017-06-02 18:58:39 +02:00
delete this . participants [ connection . connectionId ] ;
2017-05-22 22:30:08 +02:00
connection . dispose ( ) ;
2017-05-10 10:55:31 +02:00
2017-09-22 15:57:59 +02:00
console . info ( "Unpublishing my media (I'm " + connection . connectionId + ")" ) ;
2017-05-10 10:55:31 +02:00
delete this . localParticipant ;
this . openVidu . sendRequest ( 'unpublishVideo' , function ( error , response ) {
if ( error ) {
console . error ( error ) ;
} else {
console . info ( "Media unpublished correctly" ) ;
}
} ) ;
}
}
getStreams() {
return this . streams ;
}
addParticipantSpeaking ( participantId ) {
this . participantsSpeaking . push ( participantId ) ;
}
removeParticipantSpeaking ( participantId ) {
let pos = - 1 ;
for ( let i = 0 ; i < this . participantsSpeaking . length ; i ++ ) {
if ( this . participantsSpeaking [ i ] == participantId ) {
pos = i ;
break ;
}
}
if ( pos != - 1 ) {
this . participantsSpeaking . splice ( pos , 1 ) ;
}
}
2017-09-12 11:43:44 +02:00
stringClientMetadata ( metadata ) : string {
if ( ! ( typeof metadata === 'string' ) ) {
return JSON . stringify ( metadata ) ;
} else {
return metadata ;
}
}
2017-10-16 15:49:23 +02:00
2017-09-12 11:43:44 +02:00
private randomToken ( ) : string {
return Math . random ( ) . toString ( 36 ) . slice ( 2 ) + Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
}
2017-05-10 10:55:31 +02:00
}