import AudioVideoControllerState from '../audiovideocontroller/AudioVideoControllerState' ; import MeetingSessionStatusCode from '../meetingsession/MeetingSessionStatusCode' ; import DefaultSDP from '../sdp/DefaultSDP' ; import BaseTask from './BaseTask' ; export default class FinishGatheringICECandidatesTask extends BaseTask { protected taskName = 'FinishGatheringICECandidatesTask' ; private static CHROME_VPN_TIMEOUT_MS = 5000 ; private startTimestampMs : number ; private cancelPromise : undefined | ( ( error : Error ) => void ) ; constructor ( private context , private chromeVpnTimeoutMs = FinishGatheringICECandidatesTask . CHROME_VPN_TIMEOUT_MS ) { super ( context . logger ) ; } private removeEventListener ( ) { if ( this . context . peer ) { this . context . peer . removeEventListener ( 'icecandidate' , this . context . iceCandidateHandler ) ; if ( ! this . context . turnCredentials ) { this . context . peer . removeEventListener ( 'icegatheringstatechange' , this . context . iceGatheringStateEventHandler ) ; } } } cancel ( ) { let error ; if ( this . context . browserBehavior . requiresIceCandidateGatheringTimeoutWorkaround ( ) ) { if ( this . chromeVpnTimeoutMs < this . context . meetingSessionConfiguration . connectionTimeoutMs ) { const duration = Date . now ( ) - this . startTimestampMs ; if ( duration > this . chromeVpnTimeoutMs ) { error = new Error ( `canceling ${ this . name ( ) } due to the meeting status code: ${ MeetingSessionStatusCode . ICEGatheringTimeoutWorkaround }` ) ; } } } if ( this . cancelPromise ) { error = error || new Error ( `canceling ${ this . name ( ) }` ) ; this . cancelPromise ( error ) ; delete this . cancelPromise ; } } async run ( ) { if ( ! this . context . peer ) { this . logAndThrow ( `session does not have peer connection; bypass ice gathering` ) ; } if ( this . context . browserBehavior . requiresCheckForSdpConnectionAttributes ( ) ) { if ( new DefaultSDP ( this . context . peer . localDescription . sdp ) . hasCandidatesForAllMLines ( ) ) { this . context . logger . info ( `ice gathering already complete; bypass gathering, current local description ${ this . context . peer . localDescription . sdp }` ) ; return ; } } else { this . context . logger . info ( `iOS device does not require checking for connection attributes in SDP, current local description ${ this . context . peer . localDescription . sdp }` ) ; } if ( ( this . context . browserBehavior . hasFirefoxWebRTC ( ) || this . context . peer . iceGatheringState === 'complete' ) && new DefaultSDP ( this . context . peer . localDescription . sdp ) . hasCandidates ( ) ) { this . context . logger . info ( 'ice gathering state is complete and candidates are in SDP; bypass gathering' ) ; return ; } try { await new Promise < void > ( ( resolve , reject ) => { this . cancelPromise = ( error ) => { this . removeEventListener ( ) ; reject ( error ) ; } ; if ( ! this . context . turnCredentials ) { this . context . iceGatheringStateEventHandler = ( ) => { if ( this . context . peer . iceGatheringState === 'complete' ) { this . removeEventListener ( ) ; resolve ( ) ; delete this . cancelPromise ; return ; } } ; this . context . peer . addEventListener ( 'icegatheringstatechange' , this . context . iceGatheringStateEventHandler ) ; } this . context . iceCandidateHandler = ( event ) => { this . context . logger . info ( `ice candidate: ${ event . candidate ? event . candidate . candidate : '(null)' } state: ${ this . context . peer . iceGatheringState }` ) ; if ( event . candidate ) { if ( DefaultSDP . isRTPCandidate ( event . candidate . candidate ) ) { this . context . iceCandidates . push ( event . candidate ) ; } if ( this . context . turnCredentials && this . context . iceCandidates . length >= 1 ) { this . context . logger . info ( 'gathered at least one relay candidate' ) ; this . removeEventListener ( ) ; resolve ( ) ; delete this . cancelPromise ; return ; } } if ( this . context . peer . iceGatheringState === 'complete' ) { this . context . logger . info ( 'done gathering ice candidates' ) ; this . removeEventListener ( ) ; if ( ! new DefaultSDP ( this . context . peer . localDescription . sdp ) . hasCandidates ( ) || this . context . iceCandidates . length === 0 ) { reject ( new Error ( 'no ice candidates were gathered' ) ) ; delete this . cancelPromise ; } else { resolve ( ) ; delete this . cancelPromise ; } } } ; this . context . peer . addEventListener ( 'icecandidate' , this . context . iceCandidateHandler ) ; this . startTimestampMs = Date . now ( ) ; } ) ; } catch ( error ) { throw error ; } finally { if ( this . startTimestampMs ) { this . context . iceGatheringDurationMs = Math . round ( Date . now ( ) - this . startTimestampMs ) ; } } } }