import BaseSession from './BaseSession' ; import { IAudioSettings , IVideoSettings , BroadcastParams , SubscribeParams , IVertoOptions , } from './util/interfaces' ; import { registerOnce , trigger } from './services/Handler' ; import { SwEvent , SESSION_ID , TURN_SERVER , STUN_SERVER , } from './util/constants' ; import { State , DeviceType } from './webrtc/constants' ; import { getDevices , scanResolutions , removeUnsupportedConstraints , checkDeviceIdConstraints , destructSubscribeResponse , getUserMedia , assureDeviceId , } from './webrtc/helpers' ; import { findElementByType } from './util/helpers' ; import { Unsubscribe , Subscribe , Broadcast } from './messages/Verto' ; import { sessionStorage } from './util/storage' ; import { stopStream } from './util/webrtc' ; import { IWebRTCCall } from './webrtc/interfaces' ; import Call from './webrtc/Call' ; export default abstract class BrowserSession extends BaseSession { public calls : { [ callId ] : IWebRTCCall ; } = { } ; public micId : string ; public micLabel : string ; public camId : string ; public camLabel : string ; public autoRecoverCalls : boolean = true ; public ringtoneFile ? : string ; public ringbackFile ? : string ; private _iceServers : RTCIceServer [ ] = [ ] ; private _localElement : HTMLMediaElement = null ; private _remoteElement : HTMLMediaElement = null ; protected _jwtAuth : boolean = true ; protected _audioConstraints : boolean | MediaTrackConstraints = true ; protected _videoConstraints : boolean | MediaTrackConstraints = false ; protected _speaker : string = null ; constructor ( options ) { super ( options ) ; this . iceServers = options . iceServers ; this . ringtoneFile = options . ringtoneFile ; this . ringbackFile = options . ringbackFile ; } get reconnectDelay ( ) { return 1000 ; } examples async connect ( ) { this . sessionid = await sessionStorage . getItem ( SESSION_ID ) ; super . connect ( ) ; } param audio param video examples async checkPermissions ( audio = true , video = true ) { try { const stream = await getUserMedia ( { audio , video } ) ; stopStream ( stream ) ; return true ; } catch { return false ; } } deprecated logout ( ) { this . disconnect ( ) ; } examples async disconnect ( ) { Object . keys ( this . calls ) . forEach ( ( k ) => this . calls [ k ] . setState ( State . Purge ) ) ; this . calls = { } ; await super . disconnect ( ) ; } speedTest ( bytes ) { return new Promise ( ( resolve , reject ) => { registerOnce ( SwEvent . SpeedTest , ( speedTestResult ) => { const { upDur , downDur } = speedTestResult ; const upKps = upDur ? ( bytes * 8 ) / ( upDur / 1000 ) / 1024 : 0 ; const downKps = downDur ? ( bytes * 8 ) / ( downDur / 1000 ) / 1024 : 0 ; resolve ( { upDur , downDur , upKps : upKps . toFixed ( 0 ) , downKps : downKps . toFixed ( 0 ) , } ) ; } , this . uuid ) ; bytes = Number ( bytes ) ; if ( ! bytes ) { return reject ( `Invalid parameter 'bytes': ${ bytes }` ) ; } this . executeRaw ( `#SPU ${ bytes }` ) ; let loops = bytes / 1024 ; if ( bytes % 1024 ) { loops ++ ; } const dots = '.' . repeat ( 1024 ) ; for ( let i = 0 ; i < loops ; i ++ ) { this . executeRaw ( `#SPB ${ dots }` ) ; } this . executeRaw ( '#SPE' ) ; } ) ; } examples getDevices ( ) { return getDevices ( ) . catch ( ( error ) => { trigger ( SwEvent . MediaError , error , this . uuid ) ; return [ ] ; } ) ; } examples returns getVideoDevices ( ) { return getDevices ( DeviceType . Video ) . catch ( ( error ) => { trigger ( SwEvent . MediaError , error , this . uuid ) ; return [ ] ; } ) ; } examples returns getAudioInDevices ( ) { return getDevices ( DeviceType . AudioIn ) . catch ( ( error ) => { trigger ( SwEvent . MediaError , error , this . uuid ) ; return [ ] ; } ) ; } examples returns getAudioOutDevices ( ) { return getDevices ( DeviceType . AudioOut ) . catch ( ( error ) => { console . error ( 'getAudioOutDevices' , error ) ; trigger ( SwEvent . MediaError , error , this . uuid ) ; return [ ] ; } ) ; } validateDeviceId ( id , label , kind ) { return assureDeviceId ( id , label , kind ) ; } param deviceId examples async getDeviceResolutions ( deviceId ) { try { return await scanResolutions ( deviceId ) ; } catch ( error ) { throw error ; } } examples readonly get mediaConstraints ( ) : { audio : boolean | MediaTrackConstraints ; video : boolean | MediaTrackConstraints ; } { return { audio : this . _audioConstraints , video : this . _videoConstraints } ; } param settings return examples async setAudioSettings ( settings ) { if ( ! settings ) { throw new Error ( 'You need to provide the settings object' ) ; } const { micId , micLabel , ... constraints } = settings ; removeUnsupportedConstraints ( constraints ) ; this . _audioConstraints = await checkDeviceIdConstraints ( micId , micLabel , 'audioinput' , constraints ) ; this . micId = micId ; this . micLabel = micLabel ; return this . _audioConstraints ; } examples disableMicrophone ( ) { this . _audioConstraints = false ; } examples enableMicrophone ( ) { this . _audioConstraints = true ; } param settings return examples async setVideoSettings ( settings ) { if ( ! settings ) { throw new Error ( 'You need to provide the settings object' ) ; } const { camId , camLabel , ... constraints } = settings ; removeUnsupportedConstraints ( constraints ) ; this . _videoConstraints = await checkDeviceIdConstraints ( camId , camLabel , 'videoinput' , constraints ) ; this . camId = camId ; this . camLabel = camLabel ; return this . _videoConstraints ; } examples disableWebcam ( ) { this . _videoConstraints = false ; } examples enableWebcam ( ) { this . _videoConstraints = true ; } set iceServers ( servers ) { if ( typeof servers === 'boolean' ) { this . _iceServers = servers ? [ { urls : [ 'stun:stun.l.google.com:19302' ] } ] : [ ] ; } else { this . _iceServers = servers || [ TURN_SERVER , STUN_SERVER ] ; } } get iceServers ( ) { return this . _iceServers ; } example type { ( string | null ) } set speaker ( deviceId ) { this . _speaker = deviceId ; } example get speaker ( ) : string | null { return this . _speaker ; } example type { ( HTMLMediaElement | string | Function ) } set localElement ( tag ) { this . _localElement = findElementByType ( tag ) ; } example get localElement ( ) { return this . _localElement ; } example type { ( HTMLMediaElement | string | Function ) } set remoteElement ( tag ) { this . _remoteElement = findElementByType ( tag ) ; } example get remoteElement ( ) { return this . _remoteElement ; } vertoBroadcast ( { nodeId , channel : eventChannel = '' , data , } ) { if ( ! eventChannel ) { throw new Error ( `Invalid channel for broadcast: ${ eventChannel }` ) ; } const msg = new Broadcast ( { sessid : this . sessionid , eventChannel , data } ) ; if ( nodeId ) { msg . targetNodeId = nodeId ; } this . execute ( msg ) . catch ( ( error ) => error ) ; } async vertoSubscribe ( { nodeId , channels : eventChannel = [ ] , handler , } ) { eventChannel = eventChannel . filter ( ( channel ) => channel && ! this . _existsSubscription ( this . relayProtocol , channel ) ) ; if ( ! eventChannel . length ) { return { } ; } const msg = new Subscribe ( { sessid : this . sessionid , eventChannel } ) ; if ( nodeId ) { msg . targetNodeId = nodeId ; } const response = await this . execute ( msg ) ; const { unauthorized = [ ] , subscribed = [ ] } = destructSubscribeResponse ( response ) ; if ( unauthorized . length ) { unauthorized . forEach ( ( channel ) => this . _removeSubscription ( this . relayProtocol , channel ) ) ; } subscribed . forEach ( ( channel ) => this . _addSubscription ( this . relayProtocol , handler , channel ) ) ; return response ; } async vertoUnsubscribe ( { nodeId , channels : eventChannel = [ ] , } ) { eventChannel = eventChannel . filter ( ( channel ) => channel && this . _existsSubscription ( this . relayProtocol , channel ) ) ; if ( ! eventChannel . length ) { return { } ; } const msg = new Unsubscribe ( { sessid : this . sessionid , eventChannel } ) ; if ( nodeId ) { msg . targetNodeId = nodeId ; } const response = await this . execute ( msg ) ; const { unsubscribed = [ ] , notSubscribed = [ ] } = destructSubscribeResponse ( response ) ; unsubscribed . forEach ( ( channel ) => this . _removeSubscription ( this . relayProtocol , channel ) ) ; notSubscribed . forEach ( ( channel ) => this . _removeSubscription ( this . relayProtocol , channel ) ) ; return response ; } static telnyxStateCall ( call ) { return Call . setStateTelnyx ( call ) ; } }