@techsee/techsee-media-service 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/MediaConstants.d.ts +6 -3
- package/lib/MediaConstants.d.ts.map +1 -1
- package/lib/MediaConstants.js +7 -3
- package/lib/MediaConstants.js.map +1 -1
- package/lib/MediaContracts.d.ts +12 -6
- package/lib/MediaContracts.d.ts.map +1 -1
- package/lib/MediaServiceBase.d.ts +3 -2
- package/lib/MediaServiceBase.d.ts.map +1 -1
- package/lib/MediaServiceBase.js +31 -15
- package/lib/MediaServiceBase.js.map +1 -1
- package/lib/MediaSession/MediaSessionBase.d.ts +6 -4
- package/lib/MediaSession/MediaSessionBase.d.ts.map +1 -1
- package/lib/MediaSession/MediaSessionBase.js +18 -6
- package/lib/MediaSession/MediaSessionBase.js.map +1 -1
- package/lib/MediaSession/SessionOpentok.d.ts +2 -2
- package/lib/MediaSession/SessionOpentok.d.ts.map +1 -1
- package/lib/MediaSession/SessionOpentok.js +5 -3
- package/lib/MediaSession/SessionOpentok.js.map +1 -1
- package/lib/MediaSession/SessionTurn.d.ts +5 -3
- package/lib/MediaSession/SessionTurn.d.ts.map +1 -1
- package/lib/MediaSession/SessionTurn.js +47 -37
- package/lib/MediaSession/SessionTurn.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/MediaSession/SessionOpentok.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EACH,aAAa,EACb,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAmB,iBAAiB,EAAoB,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAC,iBAAiB,EAAE,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAKvE,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,EAAE,EAAE,GAAG,CAAC;KACX;CACJ;AA+BD,aAAK,WAAW,GAAG,GAAG,CAAA;AAEtB,aAAK,YAAY,GAAG,GAAG,CAAA;AAEvB,UAAU,YAAY;IAClB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,gBAAiB,SAAQ,iBAAiB;IAChD,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IACtC,UAAU,EAAE,YAAY,CAAC;IACzB,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACjC,UAAU,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;CACtC;AAED,UAAU,cAAc;IACpB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC;CACpB;AAGD,qBAAa,cAAe,SAAQ,gBAAgB,CAAC,gBAAgB,CAAE,YAAW,aAAa;IAC3F,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAM;IAEnC,OAAO,CAAC,QAAQ,CAA8B;aAE1C,EAAE,EAAI,MAAM;qBAIJ,SAAS;gBAIT,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,sBAAsB;IAiBpF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwCxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,sBAAsB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpE,oBAAoB,CAAC,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5F,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuB5E,OAAO,CAAC,wBAAwB;IA0EhC,OAAO,CAAC,0BAA0B;IAalC,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,oBAAoB;IAgC5B,OAAO,CAAC,gCAAgC;IAexC,OAAO,CAAC,qBAAqB;IA0B7B,OAAO,CAAC,iCAAiC;IAczC,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,kBAAkB;CAK7B","file":"SessionOpentok.d.ts","sourcesContent":["import {Nullable} from '@techsee/techsee-common';\n\nimport {\n IMediaSession,\n ISessionStreamsManager,\n MediaSessionParams,\n RemoteTrackStats\n} from '../MediaContracts';\n\nimport {KnownMediaStream, SessionClientRole, SessionClientType} from '../MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {IMediaSessionPeer, MediaSessionBase} from './MediaSessionBase';\nimport {getMediaTracer} from '../MediaUtils/MediaTracer';\n\nconst trace = getMediaTracer('OpentokSessionV3');\n\ndeclare global {\n interface Window {\n OT: any;\n }\n}\n\ninterface IOTSession {\n\n sessionId: string;\n\n connection: Nullable<OTConnection>;\n\n isConnected(): boolean;\n\n connect(token: string, cb: any): void;\n\n disconnect(): void;\n\n publish(publisher: OTPublisher, cb: any): void;\n\n unpublish(publisher: OTPublisher): void;\n\n subscribe(stream: any, container: any, uiProps: any, doneCb: any): OTSubscriber;\n\n unsubscribe(subscriber: OTSubscriber): void;\n\n getSubscribersForStream(stream: any): OTSubscriber[];\n\n off(): void;\n\n on(param: string, cb: any): void;\n\n once(param: string, cb: any): void;\n}\n\ntype OTPublisher = any\n\ntype OTSubscriber = any\n\ninterface OTConnection {\n connectionId: string;\n}\n\ninterface OTPeerConnection extends IMediaSessionPeer {\n localStream: Nullable<OTStreamSource>;\n connection: OTConnection;\n publisher: Nullable<OTPublisher>;\n subscriber: Nullable<OTSubscriber>;\n}\n\ninterface OTStreamSource {\n videoSource: any;\n audioSource: any;\n}\n\n//Currently this version supports subscribing only. Publishing will be implemented as needed.\nexport class OpentokSession extends MediaSessionBase<OTPeerConnection> implements IMediaSession {\n private readonly _credentials: any;\n\n private _session: Nullable<IOTSession> = null;\n\n get id(): string {\n return this._session && this._session.sessionId ? this._session.sessionId : '';\n }\n\n private get ownPeerId(): string {\n return this._session && this._session.connection ? this._session.connection.connectionId : '';\n }\n\n constructor(sessionParams: MediaSessionParams, streamFactory: ISessionStreamsManager) {\n super(sessionParams, streamFactory);\n this._credentials = sessionParams.credentials;\n\n this.connect = this.connect.bind(this);\n this.disconnect = this.disconnect.bind(this);\n this.onMediaStreamDestroyed = this.onMediaStreamDestroyed.bind(this);\n this.onMediaStreamRenewed = this.onMediaStreamRenewed.bind(this);\n\n this.createStreamForPublishing = this.createStreamForPublishing.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.connectionCreatedHandler = this.connectionCreatedHandler.bind(this);\n this.connectionDestroyedHandler = this.connectionDestroyedHandler.bind(this);\n this.streamCreatedHandler = this.streamCreatedHandler.bind(this);\n this.streamDestroyedHandler = this.streamDestroyedHandler.bind(this);\n }\n\n connect(): Promise<void> {\n trace.info('Connect to session', this._credentials);\n\n if (this._session) {\n trace.error('Session already exists');\n\n return Promise.reject('Cannot connect to session while another session is active');\n }\n\n return new Promise((resolve, reject) => {\n const handleError = (error: any): void => {\n this._session && this._session!.off();\n this._session = null;\n trace.error('handleConnect error:', error);\n reject(error);\n };\n\n const {apiKey, sessionId} = this._sessionParams.credentials;\n\n this._session = (window as any).OTSESSION = window.OT.initSession(apiKey, sessionId);\n\n this._session!.on('connectionCreated', this.connectionCreatedHandler);\n this._session!.on('connectionDestroyed', this.connectionDestroyedHandler);\n this._session!.on('streamCreated', this.streamCreatedHandler);\n this._session!.on('streamDestroyed', this.streamDestroyedHandler);\n\n const connectHandler = (error: any): void => {\n if (error) {\n handleError(error);\n\n return;\n }\n\n resolve();\n };\n\n this._session!.connect(this._credentials.token, connectHandler);\n });\n }\n\n disconnect(): Promise<void> {\n return new Promise((resolve) => {\n trace.info('Disconnecting from session');\n const currentSession = this._session;\n\n this._session = null;\n\n this._peerConnections.forEach((peerConnection) => {\n this.destroyPeerConnection(peerConnection);\n });\n\n if (!currentSession) {\n trace.info('No active session - resolving');\n resolve();\n\n return;\n }\n currentSession.off();\n currentSession.disconnect();\n resolve();\n });\n }\n\n onMediaStreamDestroyed(clientRole: SessionClientRole): Promise<void> {\n const promises: Promise<void>[] = [];\n\n this._peerConnections.forEach((peerConnection) => {\n if (peerConnection.peerRole === clientRole) {\n promises.push(this.destroyPublisherOnPeerConnection(peerConnection));\n }\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n onMediaStreamRenewed(clientRole: SessionClientRole, mediaStream: MediaStream): Promise<void> {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation (in OT API)\n return Promise.resolve();\n }\n\n getRemoteTrackStats(mediaTrack: MediaStreamTrack): Promise<RemoteTrackStats> {\n return new Promise((resolve: any, reject: any) => {\n const peerConnectionOfTrack = this.getPeerConnectionByMediaTrack(mediaTrack);\n\n if (!peerConnectionOfTrack || !peerConnectionOfTrack.subscriber) {\n reject(new Error('Cannot find peer or subscriber for getting stats'));\n }\n\n //https://tokbox.com/developer/sdks/js/reference/Subscriber.html#getStats\n peerConnectionOfTrack!.subscriber.getStats((error: any, stats: any) => {\n if (error) {\n trace.warn('Error getting stats', error);\n resolve({trackId: mediaTrack.id, trackStats: {}});\n\n return;\n }\n resolve({trackId: mediaTrack.id, trackStats: stats});\n });\n });\n }\n\n //#region OpenTok Events Handlers\n\n private connectionCreatedHandler(event: any): void {\n trace.info('connectionCreatedHandler', event);\n const {connection} = event;\n\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n\n //In case we got our own connection, we have nothing to do with it\n if (connection.connectionId === this.ownPeerId) {\n return;\n }\n\n const destinationRole = this.calculateDestinationRole();\n\n this.createStreamForPublishing(destinationRole)\n .then((streams: OTStreamSource) => {\n const peerConnection: OTPeerConnection = {\n peerId: connection.connectionId,\n peerRole: destinationRole,\n peerType: this._sessionParams.clientType === SessionClientType.INITIATOR\n ? SessionClientType.GUEST : SessionClientType.INITIATOR,\n connection: connection,\n publisher: null,\n subscriber: null,\n remoteMediaTracks: [],\n localStream: streams\n };\n\n if (!streams.videoSource && !streams.audioSource) {\n trace.info('Peer connection without publisher was created', peerConnection);\n\n return peerConnection;\n }\n\n const streamName = destinationRole === SessionClientRole.USER\n ? KnownMediaStream.AGENT_AUDIO_STREAM : KnownMediaStream.USER_VIDEO_STREAM;\n\n\n return this.createMediaPublisher(streams, streamName)\n .then((publisher: OTPublisher) => {\n peerConnection.publisher = publisher;\n trace.info('Peer connection with publisher was created', peerConnection);\n\n return peerConnection;\n });\n })\n .then((peerConnection: OTPeerConnection) => {\n this._peerConnections.set(peerConnection.peerId, peerConnection);\n\n if (peerConnection.publisher) {\n return new Promise<void>((resolve, reject) => {\n trace.info('Starting publish to peerConnection.publisher');\n this._session!.publish(peerConnection.publisher, (error: any) => {\n if (error) {\n trace.error('Publish to peerConnection error', error);\n\n return reject(error);\n }\n trace.info('Publish to peerConnection success');\n\n return resolve();\n });\n });\n }\n\n return Promise.resolve();\n })\n .catch((error: any) => {\n //Need to handle fail cases during reliability\n trace.error('Error handling new peer connection', error);\n });\n }\n\n private connectionDestroyedHandler(event: any): void {\n trace.info('connectionDestroyedHandler', event);\n const {connection} = event;\n\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n\n if (this._peerConnections.has(connection.connectionId)) {\n this.destroyPeerConnection(this._peerConnections.get(connection.connectionId)!);\n }\n }\n\n private streamCreatedHandler(event: any): void {\n trace.info('streamCreatedHandler', event.stream);\n const {stream} = event;\n const {connection} = stream || {connection: null};\n\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n\n return;\n }\n\n const peerConnection = this._peerConnections.get(connection.connectionId)!;\n\n trace.info('Subscribing the stream');\n\n this.createMediaSubscriber(stream).then((subcriber: OTSubscriber) => {\n peerConnection.subscriber = subcriber;\n peerConnection.subscriber.on('videoElementCreated', (args: any) => {\n const {element} = args;\n const otMediaStream: MediaStream = element.srcObject;\n\n if (!otMediaStream) {\n trace.warn('MediaStream not found in OT video element');\n\n return;\n }\n\n trace.info('Registering remote media tracks', peerConnection.subscriber, otMediaStream);\n otMediaStream.getTracks().forEach((mediaTrack: MediaStreamTrack) => {\n this.registerMediaTrackFromPeer(peerConnection, mediaTrack);\n });\n });\n });\n }\n\n private streamDestroyedHandler(event: any): void {\n trace.info('streamDestroyedHandler', event);\n const {stream} = event;\n\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n\n const peerConnection = this._peerConnections.get(stream.connection.connectionId);\n\n if (!peerConnection) {\n trace.warn('Peer connection for remote stream not found');\n\n return;\n }\n\n this.destroySubscriberOnPeerConnection(peerConnection).catch(() => undefined);\n }\n\n //#endregion\n\n //#region PeerConnection\n\n private destroyPeerConnection(peerConnection: OTPeerConnection): void {\n this._peerConnections.delete(peerConnection.peerId);\n this.destroyPublisherOnPeerConnection(peerConnection).catch(() => undefined);\n this.destroySubscriberOnPeerConnection(peerConnection).catch(() => undefined);\n }\n\n //#endregion\n\n //#region Publisher\n\n private createStreamForPublishing(destinationRole: SessionClientRole): Promise<OTStreamSource> {\n trace.info('createStreamForPublishing');\n\n return this._sessionStreamsManager.getMediaStreamForRole(destinationRole)\n .then((mediaStream: Nullable<MediaStream>) => {\n\n const stream: OTStreamSource = {videoSource: null, audioSource: null};\n\n if (mediaStream) {\n trace.info('Media stream found for publishing', mediaStream);\n mediaStream.getTracks().forEach((track) => {\n if (track.kind === 'video') {\n stream.videoSource = track;\n } else if (track.kind === 'audio') {\n stream.audioSource = track;\n }\n });\n } else if (this._sessionParams.clientRole === SessionClientRole.USER) {\n trace.warn('No local media was found for publishing to destinationRole:', destinationRole);\n }\n\n return stream;\n });\n }\n\n private createMediaPublisher(stream: OTStreamSource, streamName: string): Promise<OTPublisher> {\n const otParams = {\n videoSource: stream.videoSource ? stream.videoSource : false,\n audioSource: stream.audioSource ? stream.audioSource : false,\n publishAudio: !!stream.audioSource,\n publishVideo: !!stream.videoSource,\n showControls: false,\n name: streamName\n };\n\n return new Promise((resolve, reject) => {\n const otPublisher = window.OT.initPublisher(undefined, otParams, (error: any) => {\n if (error) {\n reject(error);\n\n return;\n }\n otPublisher.on('streamDestroyed', (event: any) => {\n trace.info('Publisher stream destroyed', event);\n //We preventing the default, in order to prevent stop the stream by OT.\n //We managing streams by ourselves and if we allow OT to close the stream, it cannot be reused.\n event.preventDefault();\n });\n otPublisher.on('videoElementCreated', (event: any) => {\n document.getElementById(otPublisher.id)!.style.display = 'none';\n event.element.muted = true;\n });\n resolve(otPublisher);\n });\n });\n }\n\n private destroyPublisherOnPeerConnection(peerConnection: OTPeerConnection): Promise<void> {\n if (this.sessionExistsGuard(false) && peerConnection.publisher) {\n this._session!.unpublish(peerConnection.publisher);\n }\n\n peerConnection.publisher = null;\n peerConnection.localStream = null;\n\n return Promise.resolve();\n }\n\n //#endregion\n\n //#region Subscriber\n\n private createMediaSubscriber(remoteStream: any): Promise<OTSubscriber> {\n return new Promise((resolve: any, reject: any) => {\n if (!this.sessionExistsGuard(false)) {\n reject('Session not exists');\n\n return;\n }\n\n const subscriber: OTSubscriber = this._session!.subscribe(remoteStream, null, null, (err: any) => {\n if (err) {\n trace.warn('Error creating subscriber', err);\n reject(err);\n\n return;\n }\n\n subscriber.on('videoElementCreated', (event: any) => {\n document.getElementById(subscriber.id)!.style.display = 'none';\n event.element.muted = true;\n });\n\n resolve(subscriber);\n });\n });\n }\n\n private destroySubscriberOnPeerConnection(peerConnection: OTPeerConnection): Promise<void> {\n if (this.sessionExistsGuard(false) && peerConnection.subscriber) {\n this._session!.unsubscribe(peerConnection.subscriber);\n }\n\n return this.unregisterPeersRemoteMediaTracks(peerConnection)\n .catch(() => trace.warn('Error removing remote streams from peerConnection'))\n .then(() => {\n peerConnection.subscriber = null;\n });\n }\n\n //#endregion\n\n private calculateDestinationRole(): SessionClientRole {\n if (this._sessionParams.clientRole === SessionClientRole.USER) {\n const isAgentConnected = false;\n\n return !isAgentConnected ? SessionClientRole.AGENT : SessionClientRole.OBSERVER;\n }\n\n return SessionClientRole.USER;\n }\n\n private sessionExistsGuard(shouldThrow = true): boolean {\n const connected = !!(this._session && this._session.connection && this._session.connection.connectionId);\n\n return throwableGuard(connected, 'There no OpenTok session exists', shouldThrow);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/MediaSession/SessionOpentok.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EACH,aAAa,EACb,sBAAsB,EAAE,yBAAyB,EACjD,kBAAkB,EAClB,gBAAgB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAmB,iBAAiB,EAAoB,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAC,iBAAiB,EAAE,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAKvE,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,EAAE,EAAE,GAAG,CAAC;KACX;CACJ;AA+BD,aAAK,WAAW,GAAG,GAAG,CAAA;AAEtB,aAAK,YAAY,GAAG,GAAG,CAAA;AAEvB,UAAU,YAAY;IAClB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,gBAAiB,SAAQ,iBAAiB;IAChD,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IACtC,UAAU,EAAE,YAAY,CAAC;IACzB,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACjC,UAAU,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;CACtC;AAED,UAAU,cAAc;IACpB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC;CACpB;AAGD,qBAAa,cAAe,SAAQ,gBAAgB,CAAC,gBAAgB,CAAE,YAAW,aAAa;IAC3F,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAM;IAEnC,OAAO,CAAC,QAAQ,CAA8B;aAE1C,EAAE,EAAI,MAAM;qBAIJ,SAAS;gBAIT,aAAa,EAAE,kBAAkB,GAAG,yBAAyB,EAAE,aAAa,EAAE,sBAAsB;IAiBhH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwCxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,sBAAsB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpE,oBAAoB,CAAC,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5F,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuB5E,OAAO,CAAC,wBAAwB;IA2EhC,OAAO,CAAC,0BAA0B;IAalC,OAAO,CAAC,oBAAoB;IAyC5B,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,oBAAoB;IAgC5B,OAAO,CAAC,gCAAgC;IAexC,OAAO,CAAC,qBAAqB;IA0B7B,OAAO,CAAC,iCAAiC;IAczC,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,kBAAkB;CAK7B","file":"SessionOpentok.d.ts","sourcesContent":["import {Nullable} from '@techsee/techsee-common';\n\nimport {\n IMediaSession,\n ISessionStreamsManager, MediaSessionConfiguration,\n MediaSessionParams,\n RemoteTrackStats\n} from '../MediaContracts';\n\nimport {KnownMediaStream, SessionClientRole, SessionClientType} from '../MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {IMediaSessionPeer, MediaSessionBase} from './MediaSessionBase';\nimport {getMediaTracer} from '../MediaUtils/MediaTracer';\n\nconst trace = getMediaTracer('OpentokSession');\n\ndeclare global {\n interface Window {\n OT: any;\n }\n}\n\ninterface IOTSession {\n\n sessionId: string;\n\n connection: Nullable<OTConnection>;\n\n isConnected(): boolean;\n\n connect(token: string, cb: any): void;\n\n disconnect(): void;\n\n publish(publisher: OTPublisher, cb: any): void;\n\n unpublish(publisher: OTPublisher): void;\n\n subscribe(stream: any, container: any, uiProps: any, doneCb: any): OTSubscriber;\n\n unsubscribe(subscriber: OTSubscriber): void;\n\n getSubscribersForStream(stream: any): OTSubscriber[];\n\n off(): void;\n\n on(param: string, cb: any): void;\n\n once(param: string, cb: any): void;\n}\n\ntype OTPublisher = any\n\ntype OTSubscriber = any\n\ninterface OTConnection {\n connectionId: string;\n}\n\ninterface OTPeerConnection extends IMediaSessionPeer {\n localStream: Nullable<OTStreamSource>;\n connection: OTConnection;\n publisher: Nullable<OTPublisher>;\n subscriber: Nullable<OTSubscriber>;\n}\n\ninterface OTStreamSource {\n videoSource: any;\n audioSource: any;\n}\n\n//Currently this version supports subscribing only. Publishing will be implemented as needed.\nexport class OpentokSession extends MediaSessionBase<OTPeerConnection> implements IMediaSession {\n private readonly _credentials: any;\n\n private _session: Nullable<IOTSession> = null;\n\n get id(): string {\n return this._session && this._session.sessionId ? this._session.sessionId : '';\n }\n\n private get ownPeerId(): string {\n return this._session && this._session.connection ? this._session.connection.connectionId : '';\n }\n\n constructor(sessionParams: MediaSessionParams & MediaSessionConfiguration, streamFactory: ISessionStreamsManager) {\n super(sessionParams, streamFactory);\n this._credentials = sessionParams.credentials;\n\n this.connect = this.connect.bind(this);\n this.disconnect = this.disconnect.bind(this);\n this.onMediaStreamDestroyed = this.onMediaStreamDestroyed.bind(this);\n this.onMediaStreamRenewed = this.onMediaStreamRenewed.bind(this);\n\n this.createStreamForPublishing = this.createStreamForPublishing.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.connectionCreatedHandler = this.connectionCreatedHandler.bind(this);\n this.connectionDestroyedHandler = this.connectionDestroyedHandler.bind(this);\n this.streamCreatedHandler = this.streamCreatedHandler.bind(this);\n this.streamDestroyedHandler = this.streamDestroyedHandler.bind(this);\n }\n\n connect(): Promise<void> {\n trace.info('Connect to session', this._credentials);\n\n if (this._session) {\n trace.error('Session already exists');\n\n return Promise.reject('Cannot connect to session while another session is active');\n }\n\n return new Promise((resolve, reject) => {\n const handleError = (error: any): void => {\n this._session && this._session!.off();\n this._session = null;\n trace.error('handleConnect error:', error);\n reject(error);\n };\n\n const {apiKey, sessionId} = this._sessionParams.credentials;\n\n this._session = (window as any).OTSESSION = window.OT.initSession(apiKey, sessionId);\n\n this._session!.on('connectionCreated', this.connectionCreatedHandler);\n this._session!.on('connectionDestroyed', this.connectionDestroyedHandler);\n this._session!.on('streamCreated', this.streamCreatedHandler);\n this._session!.on('streamDestroyed', this.streamDestroyedHandler);\n\n const connectHandler = (error: any): void => {\n if (error) {\n handleError(error);\n\n return;\n }\n\n resolve();\n };\n\n this._session!.connect(this._credentials.token, connectHandler);\n });\n }\n\n disconnect(): Promise<void> {\n return new Promise((resolve) => {\n trace.info('Disconnecting from session');\n const currentSession = this._session;\n\n this._session = null;\n\n this._peerConnections.forEach((peerConnection) => {\n this.destroyPeerConnection(peerConnection);\n });\n\n if (!currentSession) {\n trace.info('No active session - resolving');\n resolve();\n\n return;\n }\n currentSession.off();\n currentSession.disconnect();\n resolve();\n });\n }\n\n onMediaStreamDestroyed(clientRole: SessionClientRole): Promise<void> {\n const promises: Promise<void>[] = [];\n\n this._peerConnections.forEach((peerConnection) => {\n if (peerConnection.peerRole === clientRole) {\n promises.push(this.destroyPublisherOnPeerConnection(peerConnection));\n }\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n onMediaStreamRenewed(clientRole: SessionClientRole, mediaStream: MediaStream): Promise<void> {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation (in OT API)\n return Promise.resolve();\n }\n\n getRemoteTrackStats(mediaTrack: MediaStreamTrack): Promise<RemoteTrackStats> {\n return new Promise((resolve: any, reject: any) => {\n const peerConnectionOfTrack = this.getPeerConnectionByMediaTrack(mediaTrack);\n\n if (!peerConnectionOfTrack || !peerConnectionOfTrack.subscriber) {\n reject(new Error('Cannot find peer or subscriber for getting stats'));\n }\n\n //https://tokbox.com/developer/sdks/js/reference/Subscriber.html#getStats\n peerConnectionOfTrack!.subscriber.getStats((error: any, stats: any) => {\n if (error) {\n trace.warn('Error getting stats', error);\n resolve({trackId: mediaTrack.id, trackStats: {}});\n\n return;\n }\n resolve({trackId: mediaTrack.id, trackStats: stats});\n });\n });\n }\n\n //#region OpenTok Events Handlers\n\n private connectionCreatedHandler(event: any): void {\n trace.info('connectionCreatedHandler', event);\n const {connection} = event;\n\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n\n //In case we got our own connection, we have nothing to do with it\n if (connection.connectionId === this.ownPeerId) {\n return;\n }\n\n const destinationRole = this.calculateDestinationRole();\n\n this.createStreamForPublishing(destinationRole)\n .then((streams: OTStreamSource) => {\n const peerConnection: OTPeerConnection = {\n peerId: connection.connectionId,\n peerRole: destinationRole,\n peerType: this._sessionParams.clientType === SessionClientType.INITIATOR\n ? SessionClientType.GUEST : SessionClientType.INITIATOR,\n connection: connection,\n publisher: null,\n subscriber: null,\n remoteMediaTracks: [],\n localStream: streams,\n connectionTimoutPrt: null\n };\n\n if (!streams.videoSource && !streams.audioSource) {\n trace.info('Peer connection without publisher was created', peerConnection);\n\n return peerConnection;\n }\n\n const streamName = destinationRole === SessionClientRole.USER\n ? KnownMediaStream.AGENT_AUDIO_STREAM : KnownMediaStream.USER_VIDEO_STREAM;\n\n\n return this.createMediaPublisher(streams, streamName)\n .then((publisher: OTPublisher) => {\n peerConnection.publisher = publisher;\n trace.info('Peer connection with publisher was created', peerConnection);\n\n return peerConnection;\n });\n })\n .then((peerConnection: OTPeerConnection) => {\n this._peerConnections.set(peerConnection.peerId, peerConnection);\n\n if (peerConnection.publisher) {\n return new Promise<void>((resolve, reject) => {\n trace.info('Starting publish to peerConnection.publisher');\n this._session!.publish(peerConnection.publisher, (error: any) => {\n if (error) {\n trace.error('Publish to peerConnection error', error);\n\n return reject(error);\n }\n trace.info('Publish to peerConnection success');\n\n return resolve();\n });\n });\n }\n\n return Promise.resolve();\n })\n .catch((error: any) => {\n //Need to handle fail cases during reliability\n trace.error('Error handling new peer connection', error);\n });\n }\n\n private connectionDestroyedHandler(event: any): void {\n trace.info('connectionDestroyedHandler', event);\n const {connection} = event;\n\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n\n if (this._peerConnections.has(connection.connectionId)) {\n this.destroyPeerConnection(this._peerConnections.get(connection.connectionId)!);\n }\n }\n\n private streamCreatedHandler(event: any): void {\n trace.info('streamCreatedHandler', event.stream);\n const {stream} = event;\n const {connection} = stream || {connection: null};\n\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n\n return;\n }\n\n const peerConnection = this._peerConnections.get(connection.connectionId)!;\n\n trace.info('Subscribing the stream');\n\n this.createMediaSubscriber(stream).then((subcriber: OTSubscriber) => {\n peerConnection.subscriber = subcriber;\n peerConnection.subscriber.on('videoElementCreated', (args: any) => {\n const {element} = args;\n const otMediaStream: MediaStream = element.srcObject;\n\n if (!otMediaStream) {\n trace.warn('MediaStream not found in OT video element');\n\n return;\n }\n\n trace.info('Registering remote media tracks', peerConnection.subscriber, otMediaStream);\n otMediaStream.getTracks().forEach((mediaTrack: MediaStreamTrack) => {\n this.addRemoteTrackToPeer(peerConnection, mediaTrack);\n });\n\n this.registerPeersRemoteTracks(peerConnection);\n });\n });\n }\n\n private streamDestroyedHandler(event: any): void {\n trace.info('streamDestroyedHandler', event);\n const {stream} = event;\n\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n\n const peerConnection = this._peerConnections.get(stream.connection.connectionId);\n\n if (!peerConnection) {\n trace.warn('Peer connection for remote stream not found');\n\n return;\n }\n\n this.destroySubscriberOnPeerConnection(peerConnection).catch(() => undefined);\n }\n\n //#endregion\n\n //#region PeerConnection\n\n private destroyPeerConnection(peerConnection: OTPeerConnection): void {\n this._peerConnections.delete(peerConnection.peerId);\n this.destroyPublisherOnPeerConnection(peerConnection).catch(() => undefined);\n this.destroySubscriberOnPeerConnection(peerConnection).catch(() => undefined);\n }\n\n //#endregion\n\n //#region Publisher\n\n private createStreamForPublishing(destinationRole: SessionClientRole): Promise<OTStreamSource> {\n trace.info('createStreamForPublishing');\n\n return this._sessionStreamsManager.getMediaStreamForRole(destinationRole)\n .then((mediaStream: Nullable<MediaStream>) => {\n\n const stream: OTStreamSource = {videoSource: null, audioSource: null};\n\n if (mediaStream) {\n trace.info('Media stream found for publishing', mediaStream);\n mediaStream.getTracks().forEach((track) => {\n if (track.kind === 'video') {\n stream.videoSource = track;\n } else if (track.kind === 'audio') {\n stream.audioSource = track;\n }\n });\n } else if (this._sessionParams.clientRole === SessionClientRole.USER) {\n trace.warn('No local media was found for publishing to destinationRole:', destinationRole);\n }\n\n return stream;\n });\n }\n\n private createMediaPublisher(stream: OTStreamSource, streamName: string): Promise<OTPublisher> {\n const otParams = {\n videoSource: stream.videoSource ? stream.videoSource : false,\n audioSource: stream.audioSource ? stream.audioSource : false,\n publishAudio: !!stream.audioSource,\n publishVideo: !!stream.videoSource,\n showControls: false,\n name: streamName\n };\n\n return new Promise((resolve, reject) => {\n const otPublisher = window.OT.initPublisher(undefined, otParams, (error: any) => {\n if (error) {\n reject(error);\n\n return;\n }\n otPublisher.on('streamDestroyed', (event: any) => {\n trace.info('Publisher stream destroyed', event);\n //We preventing the default, in order to prevent stop the stream by OT.\n //We managing streams by ourselves and if we allow OT to close the stream, it cannot be reused.\n event.preventDefault();\n });\n otPublisher.on('videoElementCreated', (event: any) => {\n document.getElementById(otPublisher.id)!.style.display = 'none';\n event.element.muted = true;\n });\n resolve(otPublisher);\n });\n });\n }\n\n private destroyPublisherOnPeerConnection(peerConnection: OTPeerConnection): Promise<void> {\n if (this.sessionExistsGuard(false) && peerConnection.publisher) {\n this._session!.unpublish(peerConnection.publisher);\n }\n\n peerConnection.publisher = null;\n peerConnection.localStream = null;\n\n return Promise.resolve();\n }\n\n //#endregion\n\n //#region Subscriber\n\n private createMediaSubscriber(remoteStream: any): Promise<OTSubscriber> {\n return new Promise((resolve: any, reject: any) => {\n if (!this.sessionExistsGuard(false)) {\n reject('Session not exists');\n\n return;\n }\n\n const subscriber: OTSubscriber = this._session!.subscribe(remoteStream, null, null, (err: any) => {\n if (err) {\n trace.warn('Error creating subscriber', err);\n reject(err);\n\n return;\n }\n\n subscriber.on('videoElementCreated', (event: any) => {\n document.getElementById(subscriber.id)!.style.display = 'none';\n event.element.muted = true;\n });\n\n resolve(subscriber);\n });\n });\n }\n\n private destroySubscriberOnPeerConnection(peerConnection: OTPeerConnection): Promise<void> {\n if (this.sessionExistsGuard(false) && peerConnection.subscriber) {\n this._session!.unsubscribe(peerConnection.subscriber);\n }\n\n return this.unregisterPeersRemoteMediaTracks(peerConnection)\n .catch(() => trace.warn('Error removing remote streams from peerConnection'))\n .then(() => {\n peerConnection.subscriber = null;\n });\n }\n\n //#endregion\n\n private calculateDestinationRole(): SessionClientRole {\n if (this._sessionParams.clientRole === SessionClientRole.USER) {\n const isAgentConnected = false;\n\n return !isAgentConnected ? SessionClientRole.AGENT : SessionClientRole.OBSERVER;\n }\n\n return SessionClientRole.USER;\n }\n\n private sessionExistsGuard(shouldThrow = true): boolean {\n const connected = !!(this._session && this._session.connection && this._session.connection.connectionId);\n\n return throwableGuard(connected, 'There no OpenTok session exists', shouldThrow);\n }\n}\n"]}
|
|
@@ -24,7 +24,7 @@ var MediaConstants_1 = require("../MediaConstants");
|
|
|
24
24
|
var guards_1 = require("@techsee/techsee-common/lib/core/guards");
|
|
25
25
|
var MediaSessionBase_1 = require("./MediaSessionBase");
|
|
26
26
|
var MediaTracer_1 = require("../MediaUtils/MediaTracer");
|
|
27
|
-
var trace = MediaTracer_1.getMediaTracer('
|
|
27
|
+
var trace = MediaTracer_1.getMediaTracer('OpentokSession');
|
|
28
28
|
//Currently this version supports subscribing only. Publishing will be implemented as needed.
|
|
29
29
|
var OpentokSession = /** @class */function (_super) {
|
|
30
30
|
__extends(OpentokSession, _super);
|
|
@@ -165,7 +165,8 @@ var OpentokSession = /** @class */function (_super) {
|
|
|
165
165
|
publisher: null,
|
|
166
166
|
subscriber: null,
|
|
167
167
|
remoteMediaTracks: [],
|
|
168
|
-
localStream: streams
|
|
168
|
+
localStream: streams,
|
|
169
|
+
connectionTimoutPrt: null
|
|
169
170
|
};
|
|
170
171
|
if (!streams.videoSource && !streams.audioSource) {
|
|
171
172
|
trace.info('Peer connection without publisher was created', peerConnection);
|
|
@@ -233,8 +234,9 @@ var OpentokSession = /** @class */function (_super) {
|
|
|
233
234
|
}
|
|
234
235
|
trace.info('Registering remote media tracks', peerConnection.subscriber, otMediaStream);
|
|
235
236
|
otMediaStream.getTracks().forEach(function (mediaTrack) {
|
|
236
|
-
_this.
|
|
237
|
+
_this.addRemoteTrackToPeer(peerConnection, mediaTrack);
|
|
237
238
|
});
|
|
239
|
+
_this.registerPeersRemoteTracks(peerConnection);
|
|
238
240
|
});
|
|
239
241
|
});
|
|
240
242
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["MediaSession/SessionOpentok.js"],"names":["__extends","extendStatics","d","b","Object","setPrototypeOf","__proto__","Array","p","hasOwnProperty","__","constructor","prototype","create","defineProperty","exports","value","MediaConstants_1","require","guards_1","MediaSessionBase_1","MediaTracer_1","trace","getMediaTracer","OpentokSession","_super","sessionParams","streamFactory","_this","call","_session","_credentials","credentials","connect","bind","disconnect","onMediaStreamDestroyed","onMediaStreamRenewed","createStreamForPublishing","createMediaPublisher","connectionCreatedHandler","connectionDestroyedHandler","streamCreatedHandler","streamDestroyedHandler","get","sessionId","enumerable","configurable","connection","connectionId","info","error","Promise","reject","resolve","handleError","off","_a","_sessionParams","apiKey","window","OTSESSION","OT","initSession","on","connectHandler","token","currentSession","_peerConnections","forEach","peerConnection","destroyPeerConnection","clientRole","promises","peerRole","push","destroyPublisherOnPeerConnection","all","then","undefined","mediaStream","getRemoteTrackStats","mediaTrack","peerConnectionOfTrack","getPeerConnectionByMediaTrack","subscriber","Error","getStats","stats","warn","trackId","id","trackStats","event","sessionExistsGuard","ownPeerId","destinationRole","calculateDestinationRole","streams","peerId","peerType","clientType","SessionClientType","INITIATOR","GUEST","publisher","remoteMediaTracks","localStream","videoSource","audioSource","streamName","SessionClientRole","USER","KnownMediaStream","AGENT_AUDIO_STREAM","USER_VIDEO_STREAM","set","publish","catch","has","stream","createMediaSubscriber","subcriber","args","element","otMediaStream","srcObject","getTracks","registerMediaTrackFromPeer","destroySubscriberOnPeerConnection","delete","_sessionStreamsManager","getMediaStreamForRole","track","kind","otParams","publishAudio","publishVideo","showControls","name","otPublisher","initPublisher","preventDefault","document","getElementById","style","display","muted","unpublish","remoteStream","subscribe","err","unsubscribe","unregisterPeersRemoteMediaTracks","isAgentConnected","AGENT","OBSERVER","shouldThrow","connected","throwableGuard","MediaSessionBase"],"mappings":"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","file":"SessionOpentok.js","sourcesContent":["\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar MediaConstants_1 = require(\"../MediaConstants\");\nvar guards_1 = require(\"@techsee/techsee-common/lib/core/guards\");\nvar MediaSessionBase_1 = require(\"./MediaSessionBase\");\nvar MediaTracer_1 = require(\"../MediaUtils/MediaTracer\");\nvar trace = MediaTracer_1.getMediaTracer('OpentokSessionV3');\n//Currently this version supports subscribing only. Publishing will be implemented as needed.\nvar OpentokSession = /** @class */ (function (_super) {\n __extends(OpentokSession, _super);\n function OpentokSession(sessionParams, streamFactory) {\n var _this = _super.call(this, sessionParams, streamFactory) || this;\n _this._session = null;\n _this._credentials = sessionParams.credentials;\n _this.connect = _this.connect.bind(_this);\n _this.disconnect = _this.disconnect.bind(_this);\n _this.onMediaStreamDestroyed = _this.onMediaStreamDestroyed.bind(_this);\n _this.onMediaStreamRenewed = _this.onMediaStreamRenewed.bind(_this);\n _this.createStreamForPublishing = _this.createStreamForPublishing.bind(_this);\n _this.createMediaPublisher = _this.createMediaPublisher.bind(_this);\n _this.connectionCreatedHandler = _this.connectionCreatedHandler.bind(_this);\n _this.connectionDestroyedHandler = _this.connectionDestroyedHandler.bind(_this);\n _this.streamCreatedHandler = _this.streamCreatedHandler.bind(_this);\n _this.streamDestroyedHandler = _this.streamDestroyedHandler.bind(_this);\n return _this;\n }\n Object.defineProperty(OpentokSession.prototype, \"id\", {\n get: function () {\n return this._session && this._session.sessionId ? this._session.sessionId : '';\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(OpentokSession.prototype, \"ownPeerId\", {\n get: function () {\n return this._session && this._session.connection ? this._session.connection.connectionId : '';\n },\n enumerable: true,\n configurable: true\n });\n OpentokSession.prototype.connect = function () {\n var _this = this;\n trace.info('Connect to session', this._credentials);\n if (this._session) {\n trace.error('Session already exists');\n return Promise.reject('Cannot connect to session while another session is active');\n }\n return new Promise(function (resolve, reject) {\n var handleError = function (error) {\n _this._session && _this._session.off();\n _this._session = null;\n trace.error('handleConnect error:', error);\n reject(error);\n };\n var _a = _this._sessionParams.credentials, apiKey = _a.apiKey, sessionId = _a.sessionId;\n _this._session = window.OTSESSION = window.OT.initSession(apiKey, sessionId);\n _this._session.on('connectionCreated', _this.connectionCreatedHandler);\n _this._session.on('connectionDestroyed', _this.connectionDestroyedHandler);\n _this._session.on('streamCreated', _this.streamCreatedHandler);\n _this._session.on('streamDestroyed', _this.streamDestroyedHandler);\n var connectHandler = function (error) {\n if (error) {\n handleError(error);\n return;\n }\n resolve();\n };\n _this._session.connect(_this._credentials.token, connectHandler);\n });\n };\n OpentokSession.prototype.disconnect = function () {\n var _this = this;\n return new Promise(function (resolve) {\n trace.info('Disconnecting from session');\n var currentSession = _this._session;\n _this._session = null;\n _this._peerConnections.forEach(function (peerConnection) {\n _this.destroyPeerConnection(peerConnection);\n });\n if (!currentSession) {\n trace.info('No active session - resolving');\n resolve();\n return;\n }\n currentSession.off();\n currentSession.disconnect();\n resolve();\n });\n };\n OpentokSession.prototype.onMediaStreamDestroyed = function (clientRole) {\n var _this = this;\n var promises = [];\n this._peerConnections.forEach(function (peerConnection) {\n if (peerConnection.peerRole === clientRole) {\n promises.push(_this.destroyPublisherOnPeerConnection(peerConnection));\n }\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n OpentokSession.prototype.onMediaStreamRenewed = function (clientRole, mediaStream) {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation (in OT API)\n return Promise.resolve();\n };\n OpentokSession.prototype.getRemoteTrackStats = function (mediaTrack) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n var peerConnectionOfTrack = _this.getPeerConnectionByMediaTrack(mediaTrack);\n if (!peerConnectionOfTrack || !peerConnectionOfTrack.subscriber) {\n reject(new Error('Cannot find peer or subscriber for getting stats'));\n }\n //https://tokbox.com/developer/sdks/js/reference/Subscriber.html#getStats\n peerConnectionOfTrack.subscriber.getStats(function (error, stats) {\n if (error) {\n trace.warn('Error getting stats', error);\n resolve({ trackId: mediaTrack.id, trackStats: {} });\n return;\n }\n resolve({ trackId: mediaTrack.id, trackStats: stats });\n });\n });\n };\n //#region OpenTok Events Handlers\n OpentokSession.prototype.connectionCreatedHandler = function (event) {\n var _this = this;\n trace.info('connectionCreatedHandler', event);\n var connection = event.connection;\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n //In case we got our own connection, we have nothing to do with it\n if (connection.connectionId === this.ownPeerId) {\n return;\n }\n var destinationRole = this.calculateDestinationRole();\n this.createStreamForPublishing(destinationRole)\n .then(function (streams) {\n var peerConnection = {\n peerId: connection.connectionId,\n peerRole: destinationRole,\n peerType: _this._sessionParams.clientType === MediaConstants_1.SessionClientType.INITIATOR\n ? MediaConstants_1.SessionClientType.GUEST : MediaConstants_1.SessionClientType.INITIATOR,\n connection: connection,\n publisher: null,\n subscriber: null,\n remoteMediaTracks: [],\n localStream: streams\n };\n if (!streams.videoSource && !streams.audioSource) {\n trace.info('Peer connection without publisher was created', peerConnection);\n return peerConnection;\n }\n var streamName = destinationRole === MediaConstants_1.SessionClientRole.USER\n ? MediaConstants_1.KnownMediaStream.AGENT_AUDIO_STREAM : MediaConstants_1.KnownMediaStream.USER_VIDEO_STREAM;\n return _this.createMediaPublisher(streams, streamName)\n .then(function (publisher) {\n peerConnection.publisher = publisher;\n trace.info('Peer connection with publisher was created', peerConnection);\n return peerConnection;\n });\n })\n .then(function (peerConnection) {\n _this._peerConnections.set(peerConnection.peerId, peerConnection);\n if (peerConnection.publisher) {\n return new Promise(function (resolve, reject) {\n trace.info('Starting publish to peerConnection.publisher');\n _this._session.publish(peerConnection.publisher, function (error) {\n if (error) {\n trace.error('Publish to peerConnection error', error);\n return reject(error);\n }\n trace.info('Publish to peerConnection success');\n return resolve();\n });\n });\n }\n return Promise.resolve();\n })\n .catch(function (error) {\n //Need to handle fail cases during reliability\n trace.error('Error handling new peer connection', error);\n });\n };\n OpentokSession.prototype.connectionDestroyedHandler = function (event) {\n trace.info('connectionDestroyedHandler', event);\n var connection = event.connection;\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n if (this._peerConnections.has(connection.connectionId)) {\n this.destroyPeerConnection(this._peerConnections.get(connection.connectionId));\n }\n };\n OpentokSession.prototype.streamCreatedHandler = function (event) {\n var _this = this;\n trace.info('streamCreatedHandler', event.stream);\n var stream = event.stream;\n var connection = (stream || { connection: null }).connection;\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n return;\n }\n var peerConnection = this._peerConnections.get(connection.connectionId);\n trace.info('Subscribing the stream');\n this.createMediaSubscriber(stream).then(function (subcriber) {\n peerConnection.subscriber = subcriber;\n peerConnection.subscriber.on('videoElementCreated', function (args) {\n var element = args.element;\n var otMediaStream = element.srcObject;\n if (!otMediaStream) {\n trace.warn('MediaStream not found in OT video element');\n return;\n }\n trace.info('Registering remote media tracks', peerConnection.subscriber, otMediaStream);\n otMediaStream.getTracks().forEach(function (mediaTrack) {\n _this.registerMediaTrackFromPeer(peerConnection, mediaTrack);\n });\n });\n });\n };\n OpentokSession.prototype.streamDestroyedHandler = function (event) {\n trace.info('streamDestroyedHandler', event);\n var stream = event.stream;\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n var peerConnection = this._peerConnections.get(stream.connection.connectionId);\n if (!peerConnection) {\n trace.warn('Peer connection for remote stream not found');\n return;\n }\n this.destroySubscriberOnPeerConnection(peerConnection).catch(function () { return undefined; });\n };\n //#endregion\n //#region PeerConnection\n OpentokSession.prototype.destroyPeerConnection = function (peerConnection) {\n this._peerConnections.delete(peerConnection.peerId);\n this.destroyPublisherOnPeerConnection(peerConnection).catch(function () { return undefined; });\n this.destroySubscriberOnPeerConnection(peerConnection).catch(function () { return undefined; });\n };\n //#endregion\n //#region Publisher\n OpentokSession.prototype.createStreamForPublishing = function (destinationRole) {\n var _this = this;\n trace.info('createStreamForPublishing');\n return this._sessionStreamsManager.getMediaStreamForRole(destinationRole)\n .then(function (mediaStream) {\n var stream = { videoSource: null, audioSource: null };\n if (mediaStream) {\n trace.info('Media stream found for publishing', mediaStream);\n mediaStream.getTracks().forEach(function (track) {\n if (track.kind === 'video') {\n stream.videoSource = track;\n }\n else if (track.kind === 'audio') {\n stream.audioSource = track;\n }\n });\n }\n else if (_this._sessionParams.clientRole === MediaConstants_1.SessionClientRole.USER) {\n trace.warn('No local media was found for publishing to destinationRole:', destinationRole);\n }\n return stream;\n });\n };\n OpentokSession.prototype.createMediaPublisher = function (stream, streamName) {\n var otParams = {\n videoSource: stream.videoSource ? stream.videoSource : false,\n audioSource: stream.audioSource ? stream.audioSource : false,\n publishAudio: !!stream.audioSource,\n publishVideo: !!stream.videoSource,\n showControls: false,\n name: streamName\n };\n return new Promise(function (resolve, reject) {\n var otPublisher = window.OT.initPublisher(undefined, otParams, function (error) {\n if (error) {\n reject(error);\n return;\n }\n otPublisher.on('streamDestroyed', function (event) {\n trace.info('Publisher stream destroyed', event);\n //We preventing the default, in order to prevent stop the stream by OT.\n //We managing streams by ourselves and if we allow OT to close the stream, it cannot be reused.\n event.preventDefault();\n });\n otPublisher.on('videoElementCreated', function (event) {\n document.getElementById(otPublisher.id).style.display = 'none';\n event.element.muted = true;\n });\n resolve(otPublisher);\n });\n });\n };\n OpentokSession.prototype.destroyPublisherOnPeerConnection = function (peerConnection) {\n if (this.sessionExistsGuard(false) && peerConnection.publisher) {\n this._session.unpublish(peerConnection.publisher);\n }\n peerConnection.publisher = null;\n peerConnection.localStream = null;\n return Promise.resolve();\n };\n //#endregion\n //#region Subscriber\n OpentokSession.prototype.createMediaSubscriber = function (remoteStream) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n if (!_this.sessionExistsGuard(false)) {\n reject('Session not exists');\n return;\n }\n var subscriber = _this._session.subscribe(remoteStream, null, null, function (err) {\n if (err) {\n trace.warn('Error creating subscriber', err);\n reject(err);\n return;\n }\n subscriber.on('videoElementCreated', function (event) {\n document.getElementById(subscriber.id).style.display = 'none';\n event.element.muted = true;\n });\n resolve(subscriber);\n });\n });\n };\n OpentokSession.prototype.destroySubscriberOnPeerConnection = function (peerConnection) {\n if (this.sessionExistsGuard(false) && peerConnection.subscriber) {\n this._session.unsubscribe(peerConnection.subscriber);\n }\n return this.unregisterPeersRemoteMediaTracks(peerConnection)\n .catch(function () { return trace.warn('Error removing remote streams from peerConnection'); })\n .then(function () {\n peerConnection.subscriber = null;\n });\n };\n //#endregion\n OpentokSession.prototype.calculateDestinationRole = function () {\n if (this._sessionParams.clientRole === MediaConstants_1.SessionClientRole.USER) {\n var isAgentConnected = false;\n return !isAgentConnected ? MediaConstants_1.SessionClientRole.AGENT : MediaConstants_1.SessionClientRole.OBSERVER;\n }\n return MediaConstants_1.SessionClientRole.USER;\n };\n OpentokSession.prototype.sessionExistsGuard = function (shouldThrow) {\n if (shouldThrow === void 0) { shouldThrow = true; }\n var connected = !!(this._session && this._session.connection && this._session.connection.connectionId);\n return guards_1.throwableGuard(connected, 'There no OpenTok session exists', shouldThrow);\n };\n return OpentokSession;\n}(MediaSessionBase_1.MediaSessionBase));\nexports.OpentokSession = OpentokSession;\n\n//# sourceMappingURL=SessionOpentok.js.map\n"]}
|
|
1
|
+
{"version":3,"sources":["MediaSession/SessionOpentok.js"],"names":["__extends","extendStatics","d","b","Object","setPrototypeOf","__proto__","Array","p","hasOwnProperty","__","constructor","prototype","create","defineProperty","exports","value","MediaConstants_1","require","guards_1","MediaSessionBase_1","MediaTracer_1","trace","getMediaTracer","OpentokSession","_super","sessionParams","streamFactory","_this","call","_session","_credentials","credentials","connect","bind","disconnect","onMediaStreamDestroyed","onMediaStreamRenewed","createStreamForPublishing","createMediaPublisher","connectionCreatedHandler","connectionDestroyedHandler","streamCreatedHandler","streamDestroyedHandler","get","sessionId","enumerable","configurable","connection","connectionId","info","error","Promise","reject","resolve","handleError","off","_a","_sessionParams","apiKey","window","OTSESSION","OT","initSession","on","connectHandler","token","currentSession","_peerConnections","forEach","peerConnection","destroyPeerConnection","clientRole","promises","peerRole","push","destroyPublisherOnPeerConnection","all","then","undefined","mediaStream","getRemoteTrackStats","mediaTrack","peerConnectionOfTrack","getPeerConnectionByMediaTrack","subscriber","Error","getStats","stats","warn","trackId","id","trackStats","event","sessionExistsGuard","ownPeerId","destinationRole","calculateDestinationRole","streams","peerId","peerType","clientType","SessionClientType","INITIATOR","GUEST","publisher","remoteMediaTracks","localStream","connectionTimoutPrt","videoSource","audioSource","streamName","SessionClientRole","USER","KnownMediaStream","AGENT_AUDIO_STREAM","USER_VIDEO_STREAM","set","publish","catch","has","stream","createMediaSubscriber","subcriber","args","element","otMediaStream","srcObject","getTracks","addRemoteTrackToPeer","registerPeersRemoteTracks","destroySubscriberOnPeerConnection","delete","_sessionStreamsManager","getMediaStreamForRole","track","kind","otParams","publishAudio","publishVideo","showControls","name","otPublisher","initPublisher","preventDefault","document","getElementById","style","display","muted","unpublish","remoteStream","subscribe","err","unsubscribe","unregisterPeersRemoteMediaTracks","isAgentConnected","AGENT","OBSERVER","shouldThrow","connected","throwableGuard","MediaSessionBase"],"mappings":"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","file":"SessionOpentok.js","sourcesContent":["\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar MediaConstants_1 = require(\"../MediaConstants\");\nvar guards_1 = require(\"@techsee/techsee-common/lib/core/guards\");\nvar MediaSessionBase_1 = require(\"./MediaSessionBase\");\nvar MediaTracer_1 = require(\"../MediaUtils/MediaTracer\");\nvar trace = MediaTracer_1.getMediaTracer('OpentokSession');\n//Currently this version supports subscribing only. Publishing will be implemented as needed.\nvar OpentokSession = /** @class */ (function (_super) {\n __extends(OpentokSession, _super);\n function OpentokSession(sessionParams, streamFactory) {\n var _this = _super.call(this, sessionParams, streamFactory) || this;\n _this._session = null;\n _this._credentials = sessionParams.credentials;\n _this.connect = _this.connect.bind(_this);\n _this.disconnect = _this.disconnect.bind(_this);\n _this.onMediaStreamDestroyed = _this.onMediaStreamDestroyed.bind(_this);\n _this.onMediaStreamRenewed = _this.onMediaStreamRenewed.bind(_this);\n _this.createStreamForPublishing = _this.createStreamForPublishing.bind(_this);\n _this.createMediaPublisher = _this.createMediaPublisher.bind(_this);\n _this.connectionCreatedHandler = _this.connectionCreatedHandler.bind(_this);\n _this.connectionDestroyedHandler = _this.connectionDestroyedHandler.bind(_this);\n _this.streamCreatedHandler = _this.streamCreatedHandler.bind(_this);\n _this.streamDestroyedHandler = _this.streamDestroyedHandler.bind(_this);\n return _this;\n }\n Object.defineProperty(OpentokSession.prototype, \"id\", {\n get: function () {\n return this._session && this._session.sessionId ? this._session.sessionId : '';\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(OpentokSession.prototype, \"ownPeerId\", {\n get: function () {\n return this._session && this._session.connection ? this._session.connection.connectionId : '';\n },\n enumerable: true,\n configurable: true\n });\n OpentokSession.prototype.connect = function () {\n var _this = this;\n trace.info('Connect to session', this._credentials);\n if (this._session) {\n trace.error('Session already exists');\n return Promise.reject('Cannot connect to session while another session is active');\n }\n return new Promise(function (resolve, reject) {\n var handleError = function (error) {\n _this._session && _this._session.off();\n _this._session = null;\n trace.error('handleConnect error:', error);\n reject(error);\n };\n var _a = _this._sessionParams.credentials, apiKey = _a.apiKey, sessionId = _a.sessionId;\n _this._session = window.OTSESSION = window.OT.initSession(apiKey, sessionId);\n _this._session.on('connectionCreated', _this.connectionCreatedHandler);\n _this._session.on('connectionDestroyed', _this.connectionDestroyedHandler);\n _this._session.on('streamCreated', _this.streamCreatedHandler);\n _this._session.on('streamDestroyed', _this.streamDestroyedHandler);\n var connectHandler = function (error) {\n if (error) {\n handleError(error);\n return;\n }\n resolve();\n };\n _this._session.connect(_this._credentials.token, connectHandler);\n });\n };\n OpentokSession.prototype.disconnect = function () {\n var _this = this;\n return new Promise(function (resolve) {\n trace.info('Disconnecting from session');\n var currentSession = _this._session;\n _this._session = null;\n _this._peerConnections.forEach(function (peerConnection) {\n _this.destroyPeerConnection(peerConnection);\n });\n if (!currentSession) {\n trace.info('No active session - resolving');\n resolve();\n return;\n }\n currentSession.off();\n currentSession.disconnect();\n resolve();\n });\n };\n OpentokSession.prototype.onMediaStreamDestroyed = function (clientRole) {\n var _this = this;\n var promises = [];\n this._peerConnections.forEach(function (peerConnection) {\n if (peerConnection.peerRole === clientRole) {\n promises.push(_this.destroyPublisherOnPeerConnection(peerConnection));\n }\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n OpentokSession.prototype.onMediaStreamRenewed = function (clientRole, mediaStream) {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation (in OT API)\n return Promise.resolve();\n };\n OpentokSession.prototype.getRemoteTrackStats = function (mediaTrack) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n var peerConnectionOfTrack = _this.getPeerConnectionByMediaTrack(mediaTrack);\n if (!peerConnectionOfTrack || !peerConnectionOfTrack.subscriber) {\n reject(new Error('Cannot find peer or subscriber for getting stats'));\n }\n //https://tokbox.com/developer/sdks/js/reference/Subscriber.html#getStats\n peerConnectionOfTrack.subscriber.getStats(function (error, stats) {\n if (error) {\n trace.warn('Error getting stats', error);\n resolve({ trackId: mediaTrack.id, trackStats: {} });\n return;\n }\n resolve({ trackId: mediaTrack.id, trackStats: stats });\n });\n });\n };\n //#region OpenTok Events Handlers\n OpentokSession.prototype.connectionCreatedHandler = function (event) {\n var _this = this;\n trace.info('connectionCreatedHandler', event);\n var connection = event.connection;\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n //In case we got our own connection, we have nothing to do with it\n if (connection.connectionId === this.ownPeerId) {\n return;\n }\n var destinationRole = this.calculateDestinationRole();\n this.createStreamForPublishing(destinationRole)\n .then(function (streams) {\n var peerConnection = {\n peerId: connection.connectionId,\n peerRole: destinationRole,\n peerType: _this._sessionParams.clientType === MediaConstants_1.SessionClientType.INITIATOR\n ? MediaConstants_1.SessionClientType.GUEST : MediaConstants_1.SessionClientType.INITIATOR,\n connection: connection,\n publisher: null,\n subscriber: null,\n remoteMediaTracks: [],\n localStream: streams,\n connectionTimoutPrt: null\n };\n if (!streams.videoSource && !streams.audioSource) {\n trace.info('Peer connection without publisher was created', peerConnection);\n return peerConnection;\n }\n var streamName = destinationRole === MediaConstants_1.SessionClientRole.USER\n ? MediaConstants_1.KnownMediaStream.AGENT_AUDIO_STREAM : MediaConstants_1.KnownMediaStream.USER_VIDEO_STREAM;\n return _this.createMediaPublisher(streams, streamName)\n .then(function (publisher) {\n peerConnection.publisher = publisher;\n trace.info('Peer connection with publisher was created', peerConnection);\n return peerConnection;\n });\n })\n .then(function (peerConnection) {\n _this._peerConnections.set(peerConnection.peerId, peerConnection);\n if (peerConnection.publisher) {\n return new Promise(function (resolve, reject) {\n trace.info('Starting publish to peerConnection.publisher');\n _this._session.publish(peerConnection.publisher, function (error) {\n if (error) {\n trace.error('Publish to peerConnection error', error);\n return reject(error);\n }\n trace.info('Publish to peerConnection success');\n return resolve();\n });\n });\n }\n return Promise.resolve();\n })\n .catch(function (error) {\n //Need to handle fail cases during reliability\n trace.error('Error handling new peer connection', error);\n });\n };\n OpentokSession.prototype.connectionDestroyedHandler = function (event) {\n trace.info('connectionDestroyedHandler', event);\n var connection = event.connection;\n if (!this.sessionExistsGuard(false) || !connection) {\n return;\n }\n if (this._peerConnections.has(connection.connectionId)) {\n this.destroyPeerConnection(this._peerConnections.get(connection.connectionId));\n }\n };\n OpentokSession.prototype.streamCreatedHandler = function (event) {\n var _this = this;\n trace.info('streamCreatedHandler', event.stream);\n var stream = event.stream;\n var connection = (stream || { connection: null }).connection;\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n return;\n }\n var peerConnection = this._peerConnections.get(connection.connectionId);\n trace.info('Subscribing the stream');\n this.createMediaSubscriber(stream).then(function (subcriber) {\n peerConnection.subscriber = subcriber;\n peerConnection.subscriber.on('videoElementCreated', function (args) {\n var element = args.element;\n var otMediaStream = element.srcObject;\n if (!otMediaStream) {\n trace.warn('MediaStream not found in OT video element');\n return;\n }\n trace.info('Registering remote media tracks', peerConnection.subscriber, otMediaStream);\n otMediaStream.getTracks().forEach(function (mediaTrack) {\n _this.addRemoteTrackToPeer(peerConnection, mediaTrack);\n });\n _this.registerPeersRemoteTracks(peerConnection);\n });\n });\n };\n OpentokSession.prototype.streamDestroyedHandler = function (event) {\n trace.info('streamDestroyedHandler', event);\n var stream = event.stream;\n if (!this.sessionExistsGuard(false) || !stream) {\n return;\n }\n var peerConnection = this._peerConnections.get(stream.connection.connectionId);\n if (!peerConnection) {\n trace.warn('Peer connection for remote stream not found');\n return;\n }\n this.destroySubscriberOnPeerConnection(peerConnection).catch(function () { return undefined; });\n };\n //#endregion\n //#region PeerConnection\n OpentokSession.prototype.destroyPeerConnection = function (peerConnection) {\n this._peerConnections.delete(peerConnection.peerId);\n this.destroyPublisherOnPeerConnection(peerConnection).catch(function () { return undefined; });\n this.destroySubscriberOnPeerConnection(peerConnection).catch(function () { return undefined; });\n };\n //#endregion\n //#region Publisher\n OpentokSession.prototype.createStreamForPublishing = function (destinationRole) {\n var _this = this;\n trace.info('createStreamForPublishing');\n return this._sessionStreamsManager.getMediaStreamForRole(destinationRole)\n .then(function (mediaStream) {\n var stream = { videoSource: null, audioSource: null };\n if (mediaStream) {\n trace.info('Media stream found for publishing', mediaStream);\n mediaStream.getTracks().forEach(function (track) {\n if (track.kind === 'video') {\n stream.videoSource = track;\n }\n else if (track.kind === 'audio') {\n stream.audioSource = track;\n }\n });\n }\n else if (_this._sessionParams.clientRole === MediaConstants_1.SessionClientRole.USER) {\n trace.warn('No local media was found for publishing to destinationRole:', destinationRole);\n }\n return stream;\n });\n };\n OpentokSession.prototype.createMediaPublisher = function (stream, streamName) {\n var otParams = {\n videoSource: stream.videoSource ? stream.videoSource : false,\n audioSource: stream.audioSource ? stream.audioSource : false,\n publishAudio: !!stream.audioSource,\n publishVideo: !!stream.videoSource,\n showControls: false,\n name: streamName\n };\n return new Promise(function (resolve, reject) {\n var otPublisher = window.OT.initPublisher(undefined, otParams, function (error) {\n if (error) {\n reject(error);\n return;\n }\n otPublisher.on('streamDestroyed', function (event) {\n trace.info('Publisher stream destroyed', event);\n //We preventing the default, in order to prevent stop the stream by OT.\n //We managing streams by ourselves and if we allow OT to close the stream, it cannot be reused.\n event.preventDefault();\n });\n otPublisher.on('videoElementCreated', function (event) {\n document.getElementById(otPublisher.id).style.display = 'none';\n event.element.muted = true;\n });\n resolve(otPublisher);\n });\n });\n };\n OpentokSession.prototype.destroyPublisherOnPeerConnection = function (peerConnection) {\n if (this.sessionExistsGuard(false) && peerConnection.publisher) {\n this._session.unpublish(peerConnection.publisher);\n }\n peerConnection.publisher = null;\n peerConnection.localStream = null;\n return Promise.resolve();\n };\n //#endregion\n //#region Subscriber\n OpentokSession.prototype.createMediaSubscriber = function (remoteStream) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n if (!_this.sessionExistsGuard(false)) {\n reject('Session not exists');\n return;\n }\n var subscriber = _this._session.subscribe(remoteStream, null, null, function (err) {\n if (err) {\n trace.warn('Error creating subscriber', err);\n reject(err);\n return;\n }\n subscriber.on('videoElementCreated', function (event) {\n document.getElementById(subscriber.id).style.display = 'none';\n event.element.muted = true;\n });\n resolve(subscriber);\n });\n });\n };\n OpentokSession.prototype.destroySubscriberOnPeerConnection = function (peerConnection) {\n if (this.sessionExistsGuard(false) && peerConnection.subscriber) {\n this._session.unsubscribe(peerConnection.subscriber);\n }\n return this.unregisterPeersRemoteMediaTracks(peerConnection)\n .catch(function () { return trace.warn('Error removing remote streams from peerConnection'); })\n .then(function () {\n peerConnection.subscriber = null;\n });\n };\n //#endregion\n OpentokSession.prototype.calculateDestinationRole = function () {\n if (this._sessionParams.clientRole === MediaConstants_1.SessionClientRole.USER) {\n var isAgentConnected = false;\n return !isAgentConnected ? MediaConstants_1.SessionClientRole.AGENT : MediaConstants_1.SessionClientRole.OBSERVER;\n }\n return MediaConstants_1.SessionClientRole.USER;\n };\n OpentokSession.prototype.sessionExistsGuard = function (shouldThrow) {\n if (shouldThrow === void 0) { shouldThrow = true; }\n var connected = !!(this._session && this._session.connection && this._session.connection.connectionId);\n return guards_1.throwableGuard(connected, 'There no OpenTok session exists', shouldThrow);\n };\n return OpentokSession;\n}(MediaSessionBase_1.MediaSessionBase));\nexports.OpentokSession = OpentokSession;\n\n//# sourceMappingURL=SessionOpentok.js.map\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IMediaSession, ISessionStreamsManager, MediaSessionHandlers, MediaSessionParams, RemoteTrackStats } from '../MediaContracts';
|
|
1
|
+
import { IMediaSession, ISessionStreamsManager, MediaSessionConfiguration, MediaSessionHandlers, MediaSessionParams, RemoteTrackStats } from '../MediaContracts';
|
|
2
2
|
import { SessionClientRole } from '../MediaConstants';
|
|
3
3
|
import { MediaSessionBase } from './MediaSessionBase';
|
|
4
4
|
export declare class TurnWebRtcSession extends MediaSessionBase<RTCPeerConnection> implements IMediaSession {
|
|
@@ -7,7 +7,7 @@ export declare class TurnWebRtcSession extends MediaSessionBase<RTCPeerConnectio
|
|
|
7
7
|
private readonly _configuration;
|
|
8
8
|
private _socket;
|
|
9
9
|
readonly id: string;
|
|
10
|
-
constructor(sessionParams: MediaSessionParams, sessionHandlers: MediaSessionHandlers, streamFactory: ISessionStreamsManager);
|
|
10
|
+
constructor(sessionParams: MediaSessionParams & MediaSessionConfiguration, sessionHandlers: MediaSessionHandlers, streamFactory: ISessionStreamsManager);
|
|
11
11
|
connect(connectOnly?: boolean): Promise<void>;
|
|
12
12
|
disconnect(): Promise<void>;
|
|
13
13
|
_connectSocket(url: string, connectOnly: boolean): Promise<void>;
|
|
@@ -16,12 +16,14 @@ export declare class TurnWebRtcSession extends MediaSessionBase<RTCPeerConnectio
|
|
|
16
16
|
_joinResponseHandler(joinResponseData: any): void;
|
|
17
17
|
_peerDisconnectedHandler(peerDisconnectedEvent: any): void;
|
|
18
18
|
_sdpOfferHandler(sdpOfferEvent: any): void;
|
|
19
|
-
|
|
19
|
+
_initiatorPeerReconnectHandler(): void;
|
|
20
20
|
onMediaStreamDestroyed(clientRole: SessionClientRole): Promise<void>;
|
|
21
21
|
onMediaStreamRenewed(clientRole: SessionClientRole, mediaStream: MediaStream): Promise<void>;
|
|
22
22
|
getRemoteTrackStats(mediaTrack: MediaStreamTrack): Promise<RemoteTrackStats>;
|
|
23
23
|
private _disconnectInternal;
|
|
24
24
|
private _createPeerConnection;
|
|
25
|
+
private _setPeerConnectionMonitor;
|
|
26
|
+
private _handlePeerConnectionTimeout;
|
|
25
27
|
private _destroyPeerConnection;
|
|
26
28
|
private _withPeerConnection;
|
|
27
29
|
_iceCandidateReceivedHandler(iceCandidateData: any): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/MediaSession/SessionTurn.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EACA,gBAAgB,EACrC,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAGH,iBAAiB,EAEpB,MAAM,mBAAmB,CAAC;AAU3B,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAKpD,qBAAa,iBAAkB,SAAQ,gBAAgB,CAAC,iBAAiB,CAAE,YAAW,aAAa;IAC/F,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuB;IACxD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAM;IACrC,OAAO,CAAC,OAAO,CAAM;aAEjB,EAAE,EAAI,MAAM;gBAIJ,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,sBAAsB;IA4B3H,OAAO,CAAC,WAAW,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB3C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAkChE,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgBlD,iBAAiB,CAAC,kBAAkB,EAAE,GAAG,GAAG,IAAI;IAsDhD,oBAAoB,CAAC,gBAAgB,EAAE,GAAG,GAAG,IAAI;IAwBjD,wBAAwB,CAAC,qBAAqB,EAAE,GAAG,GAAG,IAAI;IAe1D,gBAAgB,CAAC,aAAa,EAAE,GAAG,GAAG,IAAI;IA2E1C,yBAAyB,IAAI,IAAI;IAcjC,sBAAsB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IASpE,oBAAoB,CAAC,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5F,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY5E,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,qBAAqB;IA8C7B,OAAO,CAAC,sBAAsB;IAc9B,OAAO,CAAC,mBAAmB;IAY3B,4BAA4B,CAAC,gBAAgB,EAAE,GAAG,GAAG,IAAI;IAuBzD,sBAAsB,CAAC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvG,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAejD,kBAAkB,CAAC,YAAY,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAQvE,qBAAqB,CAAC,cAAc,EAAE,iBAAiB,GAAG,IAAI;CAKjE","file":"SessionTurn.d.ts","sourcesContent":["import * as io from 'socket.io-client';\nimport once from 'lodash/once';\nimport {Nullable} from '@techsee/techsee-common';\n\nimport {\n IMediaSession,\n ISessionStreamsManager,\n MediaSessionHandlers,\n MediaSessionParams,\n RemoteMediaTrack, RemoteTrackStats\n} from '../MediaContracts';\n\nimport {\n KnownMediaStream,\n MediaSessionDisconnectReason,\n SessionClientRole,\n SessionClientType\n} from '../MediaConstants';\n\nimport {\n SOCKET_EVENTS, SOCKET_PATH,\n SOCKET_RECONNECTION_ATTEMPTS,\n SOCKET_RECONNECTION_DELAY,\n SOCKET_RECONNECTION_DELAY_MAX\n} from './TurnConstants';\n\nimport {getMediaTracer} from '../MediaUtils/MediaTracer';\nimport {MediaSessionBase} from './MediaSessionBase';\n\nconst trace = getMediaTracer('TurnSessionV3');\n\n//Currently this version supports subscribing only. Publishing will be implemented as needed.\nexport class TurnWebRtcSession extends MediaSessionBase<RTCPeerConnection> implements IMediaSession {\n private readonly _sessionHandlers: MediaSessionHandlers;\n private readonly _pendingPeersIce: any;\n private readonly _configuration: any;\n private _socket: any;\n\n get id(): string {\n return this._sessionParams.sessionId;\n }\n\n constructor(sessionParams: MediaSessionParams, sessionHandlers: MediaSessionHandlers, streamFactory: ISessionStreamsManager) {\n super(sessionParams, streamFactory);\n this._sessionHandlers = sessionHandlers;\n this._socket = null;\n this._pendingPeersIce = {};\n\n const iceServers = sessionParams.credentials.ice.map((item: any) => ({\n urls: item.url,\n username: item.login,\n credential: item.password\n }));\n\n this._configuration = {\n iceServers,\n iceTransportPolicy: 'relay'\n };\n\n this._joinResponseHandler = this._joinResponseHandler.bind(this);\n this._iceCandidateReceivedHandler = this._iceCandidateReceivedHandler.bind(this);\n this._sendOfferHandler = this._sendOfferHandler.bind(this);\n this._sdpOfferHandler = this._sdpOfferHandler.bind(this);\n this._userPeerReconnectHandler = this._userPeerReconnectHandler.bind(this);\n this._peerDisconnectedHandler = this._peerDisconnectedHandler.bind(this);\n this._onSocketDisconnectedHandler = this._onSocketDisconnectedHandler.bind(this);\n }\n\n //#region Socket Connectivity\n\n connect(connectOnly = false): Promise<void> {\n trace.info('Connect to session');\n\n return this._connectSocket(this._sessionParams.credentials.url, connectOnly)\n .then(() => {\n trace.info('Joining SessionId: ' + this._sessionParams.sessionId);\n\n this._socket.emit(SOCKET_EVENTS.OUTGOING.JOIN_ROOM, {\n clientType: this._sessionParams.clientType,\n sessionId: this._sessionParams.sessionId\n });\n })\n .catch((error) => {\n throw error;\n });\n }\n\n disconnect(): Promise<void> {\n return this._disconnectInternal(MediaSessionDisconnectReason.ForcedByConsumer);\n }\n\n _connectSocket(url: string, connectOnly: boolean): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = this._socket = io.connect(url, {\n 'forceNew': true,\n 'reconnectionDelay': SOCKET_RECONNECTION_DELAY,\n 'reconnectionDelayMax': SOCKET_RECONNECTION_DELAY_MAX,\n 'reconnectionAttempts': SOCKET_RECONNECTION_ATTEMPTS,\n 'path': SOCKET_PATH\n });\n\n const done = once((err?: any) => {\n err ? reject(err) : resolve();\n });\n\n socket.once('connect', () => done());\n socket.on(SOCKET_EVENTS.INCOMING.ERROR, done);\n socket.on('connect_error', done);\n socket.on('connect_timeout', done);\n socket.on('disconnect', this._onSocketDisconnectedHandler);\n\n if (!connectOnly) {\n trace.info('Subscribing socket events');\n\n //PUBLISHING NOT SUPPORTED YET\n socket.on(SOCKET_EVENTS.INCOMING.SEND_OFFER, this._sendOfferHandler);\n socket.on(SOCKET_EVENTS.INCOMING.JOIN_RESPONSE, this._joinResponseHandler);\n socket.on(SOCKET_EVENTS.INCOMING.PEER_DISCONNECTED, this._peerDisconnectedHandler);\n socket.on(SOCKET_EVENTS.INCOMING.SDP_OFFER, this._sdpOfferHandler);\n socket.on(SOCKET_EVENTS.INCOMING.PUBLISHER_RECONNECT, this._userPeerReconnectHandler);\n socket.on(SOCKET_EVENTS.INCOMING.ICE_CANDIDATE, this._iceCandidateReceivedHandler);\n }\n });\n }\n\n _onSocketDisconnectedHandler(reason: string): void {\n if (this._socket) {\n // The initial example of differentiating between \"forced\" disconnect and \"network\" disconnect is taken from:\n // https://socket.io/docs/client-api/#Event-%E2%80%98disconnect%E2%80%99\n if (reason !== 'io server disconnect' && reason !== 'io client disconnect') {\n // the disconnection was initiated by the server or by client, you need to call (connect) manually\n this._disconnectInternal(MediaSessionDisconnectReason.SignalingChannelDisconnect).catch(() => undefined);\n }\n }\n }\n\n //#endregion\n\n //#region WebRTC Initiator - session Initiator (User on mobile device)\n\n // Received by Initiator, causing him to emit SDP_OFFER\n _sendOfferHandler(sendOfferEventData: any): void {\n const {uid, peerId} = sendOfferEventData;\n\n trace.info('_sendOfferHandler');\n\n //Currently we not support of passing role through signaling, so we assume every connection is \"AGENT\" like\n const peerConnection = this._createPeerConnection(peerId, SessionClientType.GUEST, SessionClientRole.AGENT);\n const offerParams = {offerToReceiveAudio: true, offerToReceiveVideo: true};\n\n peerConnection.ontrack = (trackEvent: any) => {\n const track: MediaStreamTrack = trackEvent.track;\n\n trace.info(`MediaStreamTrack arrived from peerConnection: ${track.kind}`, track);\n this.registerMediaTrackFromPeer(peerConnection, track);\n };\n\n //TODO - Alex: Currently we not sending \"role\" through signaling, and by default we assume that the one who asks fot offer, is agent.\n //On later stages we can send or the client \"role\" or the list of \"streams\" that peer is interested in.\n this._sessionStreamsManager.getMediaStreamForRole(SessionClientRole.AGENT)\n .then((mediaStream: Nullable<MediaStream>) => {\n trace.info('_sendOfferHandler stream and peer created');\n\n if (mediaStream) {\n trace.info('Adding local tracks to peer connection');\n mediaStream.getTracks().forEach((track) => {\n peerConnection.addTrack(track, mediaStream);\n trace.info(`Local ${track.kind} track added to peer connection`);\n });\n } else {\n trace.info('No media stream was found for publishing');\n }\n })\n .then(() => peerConnection.createOffer(offerParams))\n .then((offerDesc) => {\n trace.info('Initiator has created sdp offer', {offerDesc});\n\n this._socket.emit(SOCKET_EVENTS.OUTGOING.SDP_OFFER, {\n uid,\n peerId,\n sessionId: this._sessionParams.sessionId,\n sdpOffer: offerDesc.sdp\n });\n\n return peerConnection.setLocalDescription(offerDesc)\n .then(() => {\n trace.info('Local description was set on peerConnection', peerConnection);\n });\n })\n .catch((e) => {\n trace.warn('Error', e);\n });\n }\n\n // Received by the Initiator: Indicates that publish should be started\n _joinResponseHandler(joinResponseData: any): void {\n const {sdpAnswer, uid, peerId} = joinResponseData;\n\n trace.info('joinResponse Received', {sdpAnswer});\n\n this._withPeerConnection(peerId, (peerConnection: RTCPeerConnection) => {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: sdpAnswer\n });\n\n peerConnection.setRemoteDescription(answer)\n .then(() => {\n trace.info('Remote description was set', answer);\n\n this._processQueuedIceCandidates(peerId);\n })\n .catch((e) => {\n trace.warn('Error', e);\n });\n });\n }\n\n // Received by Initiator after a guests socket has been disconnected\n _peerDisconnectedHandler(peerDisconnectedEvent: any): void {\n const {peerId} = peerDisconnectedEvent;\n\n trace.info('_peerDisconnectedHandler');\n\n this._withPeerConnection(peerId, (peerConnection: any) => {\n this._disconnectInternal(MediaSessionDisconnectReason.PeerDisconnected).catch(() => undefined);\n });\n }\n\n //#endregion WebRTC Initiator - session Initiator (User on mobile device)\n\n //#region WebRTC Guest - session Guest (Agent on dashboard platform)\n\n // Received by Guest, causing him to emit SDP_ANSWER\n _sdpOfferHandler(sdpOfferEvent: any): void {\n const {sdpOffer, peerId, uid} = sdpOfferEvent;\n\n trace.info('sdpOffer Received', {sdpOffer});\n\n const peerConnection = this._createPeerConnection(peerId, SessionClientType.INITIATOR, SessionClientRole.USER);\n\n peerConnection.ontrack = (trackEvent: any) => {\n const {track} = trackEvent;\n\n trace.info(`MediaStreamTrack arrived from peerConnection: ${track.kind}`, track);\n\n const remoteTrack: RemoteMediaTrack = {\n mediaTrack: track,\n trackType: track.kind === 'video'\n //Although we can have desktop share stream, we still name it USER_VIDEO_STREAM in dashboard.\n //Because currently we support only one video stream to be streamed at the time, it's not an issue.\n //But when we will need to stream more than one stream of a kind from single peer, this will be need to change.\n ? KnownMediaStream.USER_VIDEO_STREAM : KnownMediaStream.USER_AUDIO_STREAM\n };\n\n peerConnection.remoteMediaTracks.push(remoteTrack);\n this._sessionStreamsManager.addRemoteMediaTrack(remoteTrack)\n .catch(() => {\n trace.warn('Error adding remote track to stream manager', remoteTrack);\n });\n };\n\n this._sessionStreamsManager.getMediaStreamForRole(SessionClientRole.USER)\n .then((mediaStream: Nullable<MediaStream>) => {\n if (mediaStream) {\n trace.info('Adding local tracks to peer connection');\n mediaStream.getTracks().forEach((track) => {\n peerConnection.addTrack(track, mediaStream);\n trace.info(`Local ${track.kind} track added to peer connection`);\n });\n } else {\n trace.info('No media stream was found for publishing');\n }\n })\n .then(() => {\n const remoteDesc = new RTCSessionDescription({type: 'offer', sdp: sdpOffer});\n\n return peerConnection.setRemoteDescription(remoteDesc)\n .then(() => {\n trace.info('Remote description was set', remoteDesc);\n\n return peerConnection.createAnswer()\n .then((sdpAnswer) => {\n trace.info('_sdpOfferHandler answer created', sdpAnswer);\n\n this._socket.emit(SOCKET_EVENTS.OUTGOING.SDP_ANSWER, {\n uid,\n peerId,\n sessionId: this._sessionParams.sessionId,\n sdpAnswer: sdpAnswer.sdp\n });\n\n return peerConnection.setLocalDescription(sdpAnswer)\n .then(() => {\n trace.info('_sdpOfferHandler local desc was set');\n\n this._processQueuedIceCandidates(peerId);\n });\n });\n });\n\n })\n .catch((e) => {\n trace.error('Error in _sdpOfferHandler', e);\n throw e;\n });\n }\n\n // Received by Guest after an Initiator reconnects to room\n _userPeerReconnectHandler(): void {\n trace.info('_userPeerReconnectHandler');\n this._disconnectInternal(MediaSessionDisconnectReason.UserPeerReconnected)\n .catch((error: any) => {\n trace.warn('Error while disconnecting during userPeerReconnectHandler.', error);\n });\n }\n\n //#endregion WebRTC Guest - session Guest (Agent on dashboard platform)\n\n //#region Peer Connection\n\n //#region Streams Handling\n\n onMediaStreamDestroyed(clientRole: SessionClientRole): Promise<void> {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation\n this._peerConnections.forEach((peerConnection: RTCPeerConnection) => {\n this._disconnectInternal(MediaSessionDisconnectReason.PublishedStreamDestroyed).catch(() => undefined);\n });\n\n return Promise.resolve();\n }\n\n onMediaStreamRenewed(clientRole: SessionClientRole, mediaStream: MediaStream): Promise<void> {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation\n return Promise.resolve();\n }\n\n getRemoteTrackStats(mediaTrack: MediaStreamTrack): Promise<RemoteTrackStats> {\n const peerConnectionOfTrack = this.getPeerConnectionByMediaTrack(mediaTrack);\n\n if (peerConnectionOfTrack !== null) {\n return (peerConnectionOfTrack as any).getStats(mediaTrack);\n }\n\n return Promise.reject(new Error('Provided track is not remote track'));\n }\n\n //#endregion\n\n private _disconnectInternal(reason: MediaSessionDisconnectReason): Promise<void> {\n this._peerConnections.forEach((peerConnection: RTCPeerConnection) => {\n this._destroyPeerConnection(peerConnection);\n });\n\n trace.info('Disconnecting from session', reason);\n\n if (this._socket) {\n this._socket.off();\n this._socket.disconnect();\n }\n\n this._socket = null;\n setTimeout(() => this._sessionHandlers.onDisconnectedHandler(reason));\n\n return Promise.resolve();\n }\n\n private _createPeerConnection(peerId: string, peerConnectionType: SessionClientType, peerConnectionRole: SessionClientRole): RTCPeerConnection {\n if (this._peerConnections.get(peerId)) {\n throw new Error(`${this._sessionParams.clientType} peer connection for ${peerId} already exists`);\n }\n\n const peerConnection: any = new RTCPeerConnection(this._configuration);\n\n peerConnection.peerId = peerId;\n peerConnection.peerType = peerConnectionType;\n peerConnection.remoteMediaTracks = [];\n peerConnection.peerRole = peerConnectionRole;\n\n peerConnection.onconnectionstatechange = () => {\n trace.info('onconnectionstatechange', peerConnection);\n };\n\n peerConnection.oniceconnectionstatechange = (state: any) => {\n trace.info(`ICE Connection state for peer ${peerId} was changed to: ${state.currentTarget.iceConnectionState}`, state);\n const cnState = state.currentTarget.iceConnectionState;\n\n if (['closed', 'disconnected', 'failed'].indexOf(cnState) > -1) {\n this._iceStateDisconnected(peerConnection);\n }\n };\n\n peerConnection.onicecandidate = (event: any) => {\n if (event.candidate) {\n trace.info(`${this._sessionParams.clientType} peerConn.onicecandidate`, event.candidate);\n this._socket.emit(SOCKET_EVENTS.OUTGOING.ICE_CANDIDATE, {\n sessionId: this._sessionParams.sessionId,\n peerId: peerId,\n candidate: event.candidate,\n clientType: this._sessionParams.clientType\n });\n } else {\n trace.info('ICE Candidate is null in peerConnection', event);\n }\n };\n\n this._peerConnections.set(peerId, peerConnection);\n\n trace.info(`${peerConnectionType} peerConnection created`);\n\n return peerConnection;\n }\n\n private _destroyPeerConnection(peerConnection: RTCPeerConnection): void {\n trace.info('Destroying PeerConnection', peerConnection.peerType);\n peerConnection.ontrack = null;\n peerConnection.onicecandidate = null;\n peerConnection.onconnectionstatechange = null;\n peerConnection.oniceconnectionstatechange = null;\n peerConnection.onicegatheringstatechange = null;\n\n this.unregisterPeersRemoteMediaTracks(peerConnection);\n\n this._peerConnections.delete(peerConnection.peerId);\n peerConnection.close();\n }\n\n private _withPeerConnection(peerId: any, callback: any): void | undefined {\n if (this._peerConnections.get(peerId)) {\n return callback(this._peerConnections.get(peerId));\n }\n\n return undefined;\n }\n\n //#endregion\n\n //#region ICE Candidates\n\n _iceCandidateReceivedHandler(iceCandidateData: any): void {\n const {candidate, peerId} = iceCandidateData;\n\n trace.info('_iceCandidateReceivedHandler', candidate);\n if (!candidate) {\n trace.info('Unexpected case, iceCandidate message arrived without candidate');\n\n return;\n }\n\n const peerConnection = this._peerConnections.get(peerId);\n\n if (!peerConnection || !peerConnection.remoteDescription) {\n trace.info('Queueing iceCandidate', candidate);\n this._queueIceCandidate(candidate, peerId);\n\n return;\n }\n\n trace.info('Going to add iceCandidate', candidate);\n this._addIceCandidateToPeer(candidate, peerConnection).catch(() => undefined);\n }\n\n _addIceCandidateToPeer(iceCandidate: RTCIceCandidate, peerConnection: RTCPeerConnection): Promise<void> {\n return peerConnection.addIceCandidate(iceCandidate)\n .then(() => {\n trace.info('ICE Candidate add to peer connection');\n })\n .catch((e) => {\n trace.warn('Error adding ICE Candidate to peer connection', e);\n throw e;\n });\n }\n\n _processQueuedIceCandidates(peerId: string): void {\n this._withPeerConnection(peerId, (peerConnection: RTCPeerConnection) => {\n const pendingIces = this._pendingPeersIce[peerId];\n\n if (pendingIces) {\n pendingIces.forEach((pendingIce: RTCIceCandidate) => {\n trace.info('Adding pending ICE to peer connections');\n this._addIceCandidateToPeer(pendingIce, peerConnection).catch(() => undefined);\n });\n } else {\n trace.info('No pending ICE found for peer', peerId);\n }\n });\n }\n\n _queueIceCandidate(iceCandidate: RTCIceCandidate, peerId: string): void {\n if (!this._pendingPeersIce[peerId]) {\n this._pendingPeersIce[peerId] = [];\n }\n\n this._pendingPeersIce[peerId].push(iceCandidate);\n }\n\n _iceStateDisconnected(peerConnection: RTCPeerConnection): void {\n this._disconnectInternal(MediaSessionDisconnectReason.PeerConnectionInterrupted).catch(() => undefined);\n }\n\n //#endregion\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/MediaSession/SessionTurn.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,EAElB,gBAAgB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAiD,iBAAiB,EAAoB,MAAM,mBAAmB,CAAC;AAWvH,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAKpD,qBAAa,iBAAkB,SAAQ,gBAAgB,CAAC,iBAAiB,CAAE,YAAW,aAAa;IAC/F,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuB;IACxD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAM;IACrC,OAAO,CAAC,OAAO,CAAM;aAEjB,EAAE,EAAI,MAAM;gBAIJ,aAAa,EAAE,kBAAkB,GAAG,yBAAyB,EACrE,eAAe,EAAE,oBAAoB,EACrC,aAAa,EAAE,sBAAsB;IA4BzC,OAAO,CAAC,WAAW,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB3C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiChE,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgBlD,iBAAiB,CAAC,kBAAkB,EAAE,GAAG,GAAG,IAAI;IAgDhD,oBAAoB,CAAC,gBAAgB,EAAE,GAAG,GAAG,IAAI;IAwBjD,wBAAwB,CAAC,qBAAqB,EAAE,GAAG,GAAG,IAAI;IAgB1D,gBAAgB,CAAC,aAAa,EAAE,GAAG,GAAG,IAAI;IAuD1C,8BAA8B,IAAI,IAAI;IActC,sBAAsB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IASpE,oBAAoB,CAAC,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5F,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY5E,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,qBAAqB;IA8D7B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,4BAA4B;IAMpC,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,mBAAmB;IAY3B,4BAA4B,CAAC,gBAAgB,EAAE,GAAG,GAAG,IAAI;IAuBzD,sBAAsB,CAAC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvG,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAejD,kBAAkB,CAAC,YAAY,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAQvE,qBAAqB,CAAC,cAAc,EAAE,iBAAiB,GAAG,IAAI;CAKjE","file":"SessionTurn.d.ts","sourcesContent":["import * as io from 'socket.io-client';\nimport once from 'lodash/once';\nimport {Nullable} from '@techsee/techsee-common';\n\nimport {\n IMediaSession,\n ISessionStreamsManager,\n MediaSessionConfiguration,\n MediaSessionHandlers,\n MediaSessionParams,\n RemoteMediaTrack,\n RemoteTrackStats\n} from '../MediaContracts';\n\nimport {KnownMediaStream, MediaSessionDisconnectReason, SessionClientRole, SessionClientType} from '../MediaConstants';\n\nimport {\n SOCKET_EVENTS,\n SOCKET_PATH,\n SOCKET_RECONNECTION_ATTEMPTS,\n SOCKET_RECONNECTION_DELAY,\n SOCKET_RECONNECTION_DELAY_MAX\n} from './TurnConstants';\n\nimport {getMediaTracer} from '../MediaUtils/MediaTracer';\nimport {MediaSessionBase} from './MediaSessionBase';\n\nconst trace = getMediaTracer('TurnWebRtcSession');\n\n//Currently this version supports subscribing only. Publishing will be implemented as needed.\nexport class TurnWebRtcSession extends MediaSessionBase<RTCPeerConnection> implements IMediaSession {\n private readonly _sessionHandlers: MediaSessionHandlers;\n private readonly _pendingPeersIce: any;\n private readonly _configuration: any;\n private _socket: any;\n\n get id(): string {\n return this._sessionParams.sessionId;\n }\n\n constructor(sessionParams: MediaSessionParams & MediaSessionConfiguration,\n sessionHandlers: MediaSessionHandlers,\n streamFactory: ISessionStreamsManager) {\n super(sessionParams, streamFactory);\n this._sessionHandlers = sessionHandlers;\n this._socket = null;\n this._pendingPeersIce = {};\n\n const iceServers = sessionParams.credentials.ice.map((item: any) => ({\n urls: item.url,\n username: item.login,\n credential: item.password\n }));\n\n this._configuration = {\n iceServers,\n iceTransportPolicy: 'relay'\n };\n\n this._joinResponseHandler = this._joinResponseHandler.bind(this);\n this._iceCandidateReceivedHandler = this._iceCandidateReceivedHandler.bind(this);\n this._sendOfferHandler = this._sendOfferHandler.bind(this);\n this._sdpOfferHandler = this._sdpOfferHandler.bind(this);\n this._initiatorPeerReconnectHandler = this._initiatorPeerReconnectHandler.bind(this);\n this._peerDisconnectedHandler = this._peerDisconnectedHandler.bind(this);\n this._onSocketDisconnectedHandler = this._onSocketDisconnectedHandler.bind(this);\n }\n\n //#region Socket Connectivity\n\n connect(connectOnly = false): Promise<void> {\n trace.info('Connect to session');\n\n return this._connectSocket(this._sessionParams.credentials.url, connectOnly)\n .then(() => {\n trace.info('Joining SessionId: ' + this._sessionParams.sessionId);\n\n this._socket.emit(SOCKET_EVENTS.OUTGOING.JOIN_ROOM, {\n clientType: this._sessionParams.clientType,\n sessionId: this._sessionParams.sessionId\n });\n })\n .catch((error) => {\n throw error;\n });\n }\n\n disconnect(): Promise<void> {\n return this._disconnectInternal(MediaSessionDisconnectReason.ForcedByConsumer);\n }\n\n _connectSocket(url: string, connectOnly: boolean): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = this._socket = io.connect(url, {\n 'forceNew': true,\n 'reconnectionDelay': SOCKET_RECONNECTION_DELAY,\n 'reconnectionDelayMax': SOCKET_RECONNECTION_DELAY_MAX,\n 'reconnectionAttempts': SOCKET_RECONNECTION_ATTEMPTS,\n 'path': SOCKET_PATH\n });\n\n const done = once((err?: any) => {\n err ? reject(err) : resolve();\n });\n\n socket.once('connect', () => done());\n socket.on(SOCKET_EVENTS.INCOMING.ERROR, done);\n socket.on('connect_error', done);\n socket.on('connect_timeout', done);\n socket.on('disconnect', this._onSocketDisconnectedHandler);\n\n if (!connectOnly) {\n trace.info('Subscribing socket events');\n\n socket.on(SOCKET_EVENTS.INCOMING.SEND_OFFER, this._sendOfferHandler);\n socket.on(SOCKET_EVENTS.INCOMING.JOIN_RESPONSE, this._joinResponseHandler);\n socket.on(SOCKET_EVENTS.INCOMING.PEER_DISCONNECTED, this._peerDisconnectedHandler);\n socket.on(SOCKET_EVENTS.INCOMING.SDP_OFFER, this._sdpOfferHandler);\n socket.on(SOCKET_EVENTS.INCOMING.PUBLISHER_RECONNECT, this._initiatorPeerReconnectHandler);\n socket.on(SOCKET_EVENTS.INCOMING.ICE_CANDIDATE, this._iceCandidateReceivedHandler);\n }\n });\n }\n\n _onSocketDisconnectedHandler(reason: string): void {\n if (this._socket) {\n // The initial example of differentiating between \"forced\" disconnect and \"network\" disconnect is taken from:\n // https://socket.io/docs/client-api/#Event-%E2%80%98disconnect%E2%80%99\n if (reason !== 'io server disconnect' && reason !== 'io client disconnect') {\n // the disconnection was initiated by the server or by client, you need to call (connect) manually\n this._disconnectInternal(MediaSessionDisconnectReason.SignalingChannelDisconnect).catch(() => undefined);\n }\n }\n }\n\n //#endregion\n\n //#region WebRTC Initiator - session Initiator (User on mobile device)\n\n // Received by Initiator, causing him to emit SDP_OFFER\n _sendOfferHandler(sendOfferEventData: any): void {\n const {uid, peerId} = sendOfferEventData;\n\n trace.info('_sendOfferHandler');\n\n //Currently we not support of passing role through signaling, so we assume every connection is \"AGENT\" like\n const peerConnection = this._createPeerConnection(peerId, SessionClientType.GUEST, SessionClientRole.AGENT);\n const offerParams = {offerToReceiveAudio: true, offerToReceiveVideo: true};\n\n //TODO - Alex: Currently we not sending \"role\" through signaling, and by default we assume that the one who asks fot offer, is agent.\n //On later stages we can send or the client \"role\" or the list of \"streams\" that peer is interested in.\n this._sessionStreamsManager.getMediaStreamForRole(SessionClientRole.AGENT)\n .then((mediaStream: Nullable<MediaStream>) => {\n trace.info('_sendOfferHandler stream and peer created');\n\n if (mediaStream) {\n trace.info('Adding local tracks to peer connection');\n mediaStream.getTracks().forEach((track) => {\n peerConnection.addTrack(track, mediaStream);\n trace.info(`Local ${track.kind} track added to peer connection`);\n });\n } else {\n trace.info('No media stream was found for publishing');\n }\n })\n .then(() => peerConnection.createOffer(offerParams))\n .then((offerDesc) => {\n trace.info('Initiator has created sdp offer', {offerDesc});\n\n this._socket.emit(SOCKET_EVENTS.OUTGOING.SDP_OFFER, {\n uid,\n peerId,\n sessionId: this._sessionParams.sessionId,\n sdpOffer: offerDesc.sdp\n });\n\n return peerConnection.setLocalDescription(offerDesc)\n .then(() => {\n trace.info('Local description was set on peerConnection', peerConnection);\n this._setPeerConnectionMonitor(peerConnection);\n });\n })\n .catch((e) => {\n trace.warn('Error', e);\n });\n }\n\n // Received by the Initiator: Indicates that publish should be started\n _joinResponseHandler(joinResponseData: any): void {\n const {sdpAnswer, uid, peerId} = joinResponseData;\n\n trace.info('joinResponse Received', {sdpAnswer});\n\n this._withPeerConnection(peerId, (peerConnection: RTCPeerConnection) => {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: sdpAnswer\n });\n\n peerConnection.setRemoteDescription(answer)\n .then(() => {\n trace.info('Remote description was set', answer);\n\n this._processQueuedIceCandidates(peerId);\n })\n .catch((e) => {\n trace.warn('Error', e);\n });\n });\n }\n\n // Received by Initiator after a guests socket has been disconnected\n _peerDisconnectedHandler(peerDisconnectedEvent: any): void {\n const {peerId} = peerDisconnectedEvent;\n\n trace.info('_peerDisconnectedHandler');\n\n this._withPeerConnection(peerId, (peerConnection: any) => {\n this._destroyPeerConnection(peerConnection);\n //this._disconnectInternal(MediaSessionDisconnectReason.PeerDisconnected).catch(() => undefined);\n });\n }\n\n //#endregion WebRTC Initiator - session Initiator (User on mobile device)\n\n //#region WebRTC Guest - session Guest (Agent on dashboard platform)\n\n // Received by Guest, causing him to emit SDP_ANSWER\n _sdpOfferHandler(sdpOfferEvent: any): void {\n const {sdpOffer, peerId, uid} = sdpOfferEvent;\n\n trace.info('sdpOffer Received', {sdpOffer});\n\n const peerConnection = this._createPeerConnection(peerId, SessionClientType.INITIATOR, SessionClientRole.USER);\n\n this._sessionStreamsManager.getMediaStreamForRole(SessionClientRole.USER)\n .then((mediaStream: Nullable<MediaStream>) => {\n if (mediaStream) {\n trace.info('Adding local tracks to peer connection');\n mediaStream.getTracks().forEach((track) => {\n peerConnection.addTrack(track, mediaStream);\n trace.info(`Local ${track.kind} track added to peer connection`);\n });\n } else {\n trace.info('No media stream was found for publishing');\n }\n })\n .then(() => {\n const remoteDesc = new RTCSessionDescription({type: 'offer', sdp: sdpOffer});\n\n return peerConnection.setRemoteDescription(remoteDesc)\n .then(() => {\n trace.info('Remote description was set', remoteDesc);\n\n return peerConnection.createAnswer()\n .then((sdpAnswer) => {\n trace.info('_sdpOfferHandler answer created', sdpAnswer);\n\n this._socket.emit(SOCKET_EVENTS.OUTGOING.SDP_ANSWER, {\n uid,\n peerId,\n sessionId: this._sessionParams.sessionId,\n sdpAnswer: sdpAnswer.sdp\n });\n\n return peerConnection.setLocalDescription(sdpAnswer)\n .then(() => {\n trace.info('_sdpOfferHandler local desc was set');\n\n this._processQueuedIceCandidates(peerId);\n this._setPeerConnectionMonitor(peerConnection);\n });\n });\n });\n\n })\n .catch((e) => {\n trace.error('Error in _sdpOfferHandler', e);\n throw e;\n });\n }\n\n // Received by Guest after an Initiator reconnects to room\n _initiatorPeerReconnectHandler(): void {\n trace.info('_initiatorPeerReconnectHandler');\n this._disconnectInternal(MediaSessionDisconnectReason.InitiatorPeerReconnected)\n .catch((error: any) => {\n trace.warn('Error while disconnecting during userPeerReconnectHandler.', error);\n });\n }\n\n //#endregion WebRTC Guest - session Guest (Agent on dashboard platform)\n\n //#region Peer Connection\n\n //#region Streams Handling\n\n onMediaStreamDestroyed(clientRole: SessionClientRole): Promise<void> {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation\n this._peerConnections.forEach((peerConnection: RTCPeerConnection) => {\n this._disconnectInternal(MediaSessionDisconnectReason.PublishedStreamDestroyed).catch(() => undefined);\n });\n\n return Promise.resolve();\n }\n\n onMediaStreamRenewed(clientRole: SessionClientRole, mediaStream: MediaStream): Promise<void> {\n //TODO - Alex: need to implement adding new tracks to peer connection and forcing negotiation\n return Promise.resolve();\n }\n\n getRemoteTrackStats(mediaTrack: MediaStreamTrack): Promise<RemoteTrackStats> {\n const peerConnectionOfTrack = this.getPeerConnectionByMediaTrack(mediaTrack);\n\n if (peerConnectionOfTrack !== null) {\n return (peerConnectionOfTrack as any).getStats(mediaTrack);\n }\n\n return Promise.reject(new Error('Provided track is not remote track'));\n }\n\n //#endregion\n\n private _disconnectInternal(reason: MediaSessionDisconnectReason): Promise<void> {\n this._peerConnections.forEach((peerConnection: RTCPeerConnection) => {\n this._destroyPeerConnection(peerConnection);\n });\n\n if (this._socket) {\n trace.info('Disconnecting from session', reason);\n this._socket.off();\n this._socket.disconnect();\n this._socket = null;\n setTimeout(() => this._sessionHandlers.onDisconnectedHandler(reason));\n }\n\n return Promise.resolve();\n }\n\n private _createPeerConnection(peerId: string, peerConnectionType: SessionClientType, peerConnectionRole: SessionClientRole): RTCPeerConnection {\n if (this._peerConnections.get(peerId)) {\n throw new Error(`${this._sessionParams.clientType} peer connection for ${peerId} already exists`);\n }\n\n const peerConnection: any = new RTCPeerConnection(this._configuration);\n\n peerConnection.peerId = peerId;\n peerConnection.peerType = peerConnectionType;\n peerConnection.remoteMediaTracks = [];\n peerConnection.peerRole = peerConnectionRole;\n peerConnection.connectionTimoutPrt = null;\n\n peerConnection.onconnectionstatechange = () => {\n trace.info('onconnectionstatechange', peerConnection);\n if (peerConnection.connectionState === 'connected') {\n this.registerPeersRemoteTracks(peerConnection);\n }\n };\n\n peerConnection.oniceconnectionstatechange = (state: any) => {\n trace.info(`ICE Connection state for peer ${peerId} was changed to: ${state.currentTarget.iceConnectionState}`, state);\n const cnState = state.currentTarget.iceConnectionState;\n\n clearTimeout(peerConnection.connectionTimoutPrt);\n\n if (['closed', 'disconnected', 'failed'].indexOf(cnState) > -1) {\n this._iceStateDisconnected(peerConnection);\n }\n };\n\n peerConnection.onicecandidate = (event: any) => {\n if (event.candidate) {\n trace.info(`${this._sessionParams.clientType} peerConn.onicecandidate`, event.candidate);\n this._socket.emit(SOCKET_EVENTS.OUTGOING.ICE_CANDIDATE, {\n sessionId: this._sessionParams.sessionId,\n peerId: peerId,\n candidate: event.candidate,\n clientType: this._sessionParams.clientType\n });\n } else {\n trace.info('ICE Candidate is null in peerConnection', event);\n }\n };\n\n peerConnection.ontrack = (trackEvent: any) => {\n const track: MediaStreamTrack = trackEvent.track;\n\n trace.info(`MediaStreamTrack arrived from peerConnection: ${track.kind}`, track);\n this.addRemoteTrackToPeer(peerConnection, track);\n if (peerConnection.connectionState === 'connected') {\n this.registerPeersRemoteTracks(peerConnection);\n }\n };\n\n this._peerConnections.set(peerId, peerConnection);\n\n trace.info(`${peerConnectionType} peerConnection created`);\n\n return peerConnection;\n }\n\n private _setPeerConnectionMonitor(peerConnection: RTCPeerConnection) {\n clearTimeout(peerConnection.connectionTimoutPrt);\n peerConnection.connectionTimoutPrt = setTimeout(() => {\n trace.info('PeerConnection ICE state timeout', peerConnection);\n this._handlePeerConnectionTimeout(peerConnection);\n }, this._sessionParams.peerConnectivityTimeoutSeconds * 1000);\n }\n\n private _handlePeerConnectionTimeout(peerConnection: RTCPeerConnection) {\n if (peerConnection.peerRole === SessionClientRole.USER) {\n this._disconnectInternal(MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout).catch(() => undefined);\n }\n }\n\n private _destroyPeerConnection(peerConnection: RTCPeerConnection): void {\n trace.info('Destroying PeerConnection', peerConnection.peerType);\n peerConnection.ontrack = null;\n peerConnection.onicecandidate = null;\n peerConnection.onconnectionstatechange = null;\n peerConnection.oniceconnectionstatechange = null;\n peerConnection.onicegatheringstatechange = null;\n\n clearTimeout(peerConnection.connectionTimoutPrt);\n this.unregisterPeersRemoteMediaTracks(peerConnection).catch(() => undefined);\n\n this._peerConnections.delete(peerConnection.peerId);\n peerConnection.close();\n }\n\n private _withPeerConnection(peerId: any, callback: any): void | undefined {\n if (this._peerConnections.get(peerId)) {\n return callback(this._peerConnections.get(peerId));\n }\n\n return undefined;\n }\n\n //#endregion\n\n //#region ICE Candidates\n\n _iceCandidateReceivedHandler(iceCandidateData: any): void {\n const {candidate, peerId} = iceCandidateData;\n\n trace.info('_iceCandidateReceivedHandler', candidate);\n if (!candidate) {\n trace.info('Unexpected case, iceCandidate message arrived without candidate');\n\n return;\n }\n\n const peerConnection = this._peerConnections.get(peerId);\n\n if (!peerConnection || !peerConnection.remoteDescription) {\n trace.info('Queueing iceCandidate', candidate);\n this._queueIceCandidate(candidate, peerId);\n\n return;\n }\n\n trace.info('Going to add iceCandidate', candidate);\n this._addIceCandidateToPeer(candidate, peerConnection).catch(() => undefined);\n }\n\n _addIceCandidateToPeer(iceCandidate: RTCIceCandidate, peerConnection: RTCPeerConnection): Promise<void> {\n return peerConnection.addIceCandidate(iceCandidate)\n .then(() => {\n trace.info('ICE Candidate add to peer connection');\n })\n .catch((e) => {\n trace.warn('Error adding ICE Candidate to peer connection', e);\n throw e;\n });\n }\n\n _processQueuedIceCandidates(peerId: string): void {\n this._withPeerConnection(peerId, (peerConnection: RTCPeerConnection) => {\n const pendingIces = this._pendingPeersIce[peerId];\n\n if (pendingIces) {\n pendingIces.forEach((pendingIce: RTCIceCandidate) => {\n trace.info('Adding pending ICE to peer connections');\n this._addIceCandidateToPeer(pendingIce, peerConnection).catch(() => undefined);\n });\n } else {\n trace.info('No pending ICE found for peer', peerId);\n }\n });\n }\n\n _queueIceCandidate(iceCandidate: RTCIceCandidate, peerId: string): void {\n if (!this._pendingPeersIce[peerId]) {\n this._pendingPeersIce[peerId] = [];\n }\n\n this._pendingPeersIce[peerId].push(iceCandidate);\n }\n\n _iceStateDisconnected(peerConnection: RTCPeerConnection): void {\n this._disconnectInternal(MediaSessionDisconnectReason.PeerConnectionInterrupted).catch(() => undefined);\n }\n\n //#endregion\n}\n"]}
|