import Event from "rx.mini" ; import { Candidate , Connection , IceOptions } from "../../../ice/src" ; import { candidateFromSdp , candidateToSdp } from "../sdp" ; export type IceState = "new" | "gathering" | "stateChange" | "completed" | "closed" | "checking" | "failed" | "disconnected" ; export class RTCIceGatherer { readonly subject = new Event < [ IceState ] > ( ) ; onIceCandidate : ( candidate : RTCIceCandidate ) => void = ( ) => { } ; readonly connection = new Connection ( false , this . options ) ; private _state : IceState = "new" ; constructor ( private options = { } ) { } get state ( ) { return this . _state ; } async gather ( ) { if ( this . _state === "new" ) { this . setState ( "gathering" ) ; await this . connection . gatherCandidates ( ( candidate ) => this . onIceCandidate ( candidateFromIce ( candidate ) ) ) ; this . setState ( "completed" ) ; } } get localCandidates ( ) { return this . connection . localCandidates . map ( candidateFromIce ) ; } get localParameters ( ) { const params = new RTCIceParameters ( { usernameFragment : this . connection . localUserName , password : this . connection . localPassword , } ) ; return params ; } private setState ( state ) { this . _state = state ; this . subject . execute ( state ) ; } } export function candidateFromIce ( c ) { const candidate = new RTCIceCandidate ( c . component , c . foundation , c . host , c . port , c . priority , c . transport , c . type ) ; candidate . relatedAddress = c . relatedAddress ; candidate . relatedPort = c . relatedPort ; candidate . tcpType = c . tcptype ; return candidate ; } export function candidateToIce ( x ) { return new Candidate ( x . foundation , x . component , x . protocol , x . priority , x . ip , x . port , x . type , x . relatedAddress , x . relatedPort , x . tcpType ) ; } export type RTCIceCandidateJSON = { candidate : string ; sdpMid : string ; sdpMLineIndex : number ; } ; export class RTCIceCandidate { public relatedAddress ? : string ; public relatedPort ? : number ; public sdpMid ? : string ; public sdpMLineIndex ? : number ; public tcpType ? : string ; constructor ( public component , public foundation , public ip , public port , public priority , public protocol , public type ) { } toJSON ( ) { return { candidate : candidateToSdp ( this ) , sdpMLineIndex : this . sdpMLineIndex ! , sdpMid : this . sdpMid ! , } ; } static fromJSON ( data ) { try { const candidate = candidateFromSdp ( data . candidate ) ; candidate . sdpMLineIndex = data . sdpMLineIndex ; candidate . sdpMid = data . sdpMid ; return candidate ; } catch ( error ) { } } } export class RTCIceParameters { iceLite : boolean = false ; usernameFragment ! : string ; password ! : string ; constructor ( props = { } ) { Object . assign ( this , props ) ; } } export class RTCIceTransport { iceState = new Event < [ IceState ] > ( ) ; private waitStart ? : Event < [ ] > ; private _state : IceState = "new" ; connection = this . gather . connection ; roleSet = false ; constructor ( private gather ) { this . connection . stateChanged . subscribe ( ( state ) => { this . setState ( state ) ; } ) ; } get iceGather ( ) { return this . gather ; } get role ( ) { if ( this . connection . iceControlling ) return "controlling" ; else return "controlled" ; } get state ( ) { return this . _state ; } private setState ( state ) { if ( state !== this . state ) { this . _state = state ; this . iceState . execute ( "stateChange" ) ; if ( state === "closed" ) { this . iceGather . subject . execute ( "closed" ) ; this . iceGather . subject . complete ( ) ; this . iceState . execute ( "closed" ) ; this . iceState . complete ( ) ; } } } addRemoteCandidate = async ( candidate ? ) => { if ( ! this . connection . remoteCandidatesEnd ) { if ( ! candidate ) { await this . connection . addRemoteCandidate ( undefined ) ; } else { await this . connection . addRemoteCandidate ( candidateToIce ( candidate ) ) ; } } } ; async start ( remoteParameters ) { if ( this . state === "closed" ) throw new Error ( "RTCIceTransport is closed" ) ; if ( this . waitStart ) await this . waitStart . asPromise ( ) ; this . waitStart = new Event ( ) ; this . setState ( "checking" ) ; this . connection . remoteUsername = remoteParameters . usernameFragment ; this . connection . remotePassword = remoteParameters . password ; try { await this . connection . connect ( ) ; this . setState ( "completed" ) ; } catch ( error ) { this . setState ( "failed" ) ; throw new Error ( error ) ; } this . waitStart . complete ( ) ; } async stop ( ) { if ( this . state !== "closed" ) { this . setState ( "closed" ) ; await this . connection . close ( ) ; } } }