@techsee/techsee-media-service 999.15.10-alpha → 999.15.11-alpha

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.
@@ -39,6 +39,7 @@ export interface ISessionStreamsManager {
39
39
  removeMediaTrack(mediaTrack: MediaStreamTrack): Promise<void>;
40
40
  }
41
41
  export interface MediaServiceOptions {
42
+ clientRole?: SessionClientRole;
42
43
  mediaServiceType: MediaServiceType;
43
44
  peerConnectivityTimeoutSeconds?: number;
44
45
  videoNotReceivedTimeoutSeconds?: number;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/MediaContracts.ts"],"names":[],"mappings":"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","file":"MediaContracts.d.ts","sourcesContent":["import {Nullable} from '@techsee/techsee-common';\n\nexport interface IMediaEnvironment {\n isIOS: () => boolean;\n isIE11: () => boolean;\n majorVersion: () => number;\n}\n\nimport {\n ConstraintType,\n KnownMediaStream,\n LocalVideoSourceType,\n MediaRequestErrorCode,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole,\n SessionClientType\n} from './MediaConstants';\n\nexport interface VideoStreamResolution {\n resolution: string;\n constraintType: ConstraintType;\n}\n\nexport interface LocalVideoStreamConstraints {\n videoResolution?: string | VideoStreamResolution[];\n videoSourceType?: LocalVideoSourceType;\n facingMode?: string;\n}\n\nexport interface LocalMediaConstraints {\n video: boolean | LocalVideoStreamConstraints;\n audio?: boolean;\n}\n\nexport interface MediaRequestSuccessResult {\n mediaStream: MediaStream;\n constraint: MediaStreamConstraints;\n constraintType: ConstraintType;\n isNew: boolean;\n}\n\nexport interface MediaRequestFailResult {\n message: string;\n errorCode: MediaRequestErrorCode;\n}\n\nexport interface RemoteMediaTrack {\n mediaTrack: MediaStreamTrack;\n trackType: KnownMediaStream;\n isRegistered: boolean;\n}\n\nexport interface ISessionStreamsManager {\n getMediaStreamForRole(destinationRole: SessionClientRole): Promise<Nullable<MediaStream>>;\n\n addRemoteMediaTrack(mediaTrack: RemoteMediaTrack): Promise<void>;\n\n removeMediaTrack(mediaTrack: MediaStreamTrack): Promise<void>;\n}\n\nexport interface MediaServiceOptions {\n mediaServiceType: MediaServiceType;\n //How much to wait until raise \"peerConnectionTimeout\" if WebRTC connection events not arriving\n peerConnectivityTimeoutSeconds?: number;\n\n //How much to wait totally for video stream to arrive, before making fallback to image upload\n videoNotReceivedTimeoutSeconds?: number;\n}\n\nexport type MediaSessionIceCredentials = any;//TODO - Define type for credentials\n\nexport interface MediaSessionParams {\n sessionId: string;\n clientType: SessionClientType;\n clientRole: SessionClientRole;\n credentials: MediaSessionIceCredentials;\n initHandlers?: () => void;\n removeHandlers?: () => void;\n}\n\nexport interface MediaSessionConfiguration {\n //How much to wait until raise \"peerConnectionTimeout\" if WebRTC connection events not arriving\n peerConnectivityTimeoutSeconds: number;\n}\n\nexport interface MediaSubscriberParams {\n container: HTMLDivElement;\n streamType: KnownMediaStream;\n}\n\nexport interface MediaPublisherParams {\n streamTypes: KnownMediaStream[];\n destinationRole: SessionClientRole;\n}\n\nexport interface ClientWebRtcInfo {\n isWebRTCSupported: boolean;\n isWebsiteHasWebcamPermissions: boolean;\n isWebsiteHasMicrophonePermissions: boolean;\n isGetUserMediaSupported: boolean;\n isApplyConstraintsSupported: boolean;\n}\n\nexport interface IMediaSubscriber {\n readonly streamType: KnownMediaStream;\n readonly container: HTMLDivElement;\n readonly isPlaying: boolean;\n readonly isSoundMuted: boolean;\n readonly hasAudio: boolean;\n readonly hasVideo: boolean;\n readonly mediaElement: Nullable<HTMLVideoElement>;\n readonly videoWidth: number;\n readonly videoHeight: number;\n readonly renderWidth: number;\n readonly renderHeight: number;\n\n muteSound(isMuted: boolean): void;\n\n onStateChanged(callback: () => void): void;\n\n onDispose(callback: () => void): void;\n}\n\nexport type MediaSessionDisconnectedHandler = (reason: MediaSessionDisconnectReason) => void\n\nexport interface MediaSessionHandlers {\n onDisconnectedHandler: MediaSessionDisconnectedHandler;\n}\n\nexport interface DeviceSupportInfo {\n webRtcSupportInfo: ClientWebRtcInfo;\n hasCamera: boolean;\n hasMicrophone: boolean;\n videoPlayback: boolean;\n}\n\nexport interface RemoteTrackStats {\n trackId: string;\n trackStats: any;\n}\n\nexport interface IMediaSession {\n //Boolean parameter is used for supporting \"RTC Test\" application\n connect(connectOnly?: boolean): Promise<void>;\n\n disconnect(): Promise<void>;\n\n sessionDisconnect(): void;\n\n getRemoteTrackStats(mediaTrack: MediaStreamTrack): Promise<RemoteTrackStats>;\n\n onMediaStreamDestroyed(clientRole: SessionClientRole): Promise<void>;\n\n onMediaStreamRenewed(clientRole: SessionClientRole, mediaStream: MediaStream): Promise<void>;\n\n replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void>;\n\n registerEventCallback(event: string, cb: (eventArgs: any) => void): void;\n}\n\nexport interface SnapshotResult {\n base64img: string;\n objectUrl: string;\n imageBlob: Blob;\n mimeType: string;\n}\n\nexport interface SnapshotOptions {\n format?: string;\n quality?: number;\n x?: any;\n y?: any;\n w?: any;\n h?: any;\n}\n\nexport interface StreamDestroyedEventArgs {\n streamType: KnownMediaStream;\n reason: MediaStreamUnregisterReason;\n}\n\nexport interface StreamCreatedEventArgs {\n streamType: KnownMediaStream;\n}\n"]}
1
+ {"version":3,"sources":["../src/MediaContracts.ts"],"names":[],"mappings":"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","file":"MediaContracts.d.ts","sourcesContent":["import {Nullable} from '@techsee/techsee-common';\n\nexport interface IMediaEnvironment {\n isIOS: () => boolean;\n isIE11: () => boolean;\n majorVersion: () => number;\n}\n\nimport {\n ConstraintType,\n KnownMediaStream,\n LocalVideoSourceType,\n MediaRequestErrorCode,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole,\n SessionClientType\n} from './MediaConstants';\n\nexport interface VideoStreamResolution {\n resolution: string;\n constraintType: ConstraintType;\n}\n\nexport interface LocalVideoStreamConstraints {\n videoResolution?: string | VideoStreamResolution[];\n videoSourceType?: LocalVideoSourceType;\n facingMode?: string;\n}\n\nexport interface LocalMediaConstraints {\n video: boolean | LocalVideoStreamConstraints;\n audio?: boolean;\n}\n\nexport interface MediaRequestSuccessResult {\n mediaStream: MediaStream;\n constraint: MediaStreamConstraints;\n constraintType: ConstraintType;\n isNew: boolean;\n}\n\nexport interface MediaRequestFailResult {\n message: string;\n errorCode: MediaRequestErrorCode;\n}\n\nexport interface RemoteMediaTrack {\n mediaTrack: MediaStreamTrack;\n trackType: KnownMediaStream;\n isRegistered: boolean;\n}\n\nexport interface ISessionStreamsManager {\n getMediaStreamForRole(destinationRole: SessionClientRole): Promise<Nullable<MediaStream>>;\n\n addRemoteMediaTrack(mediaTrack: RemoteMediaTrack): Promise<void>;\n\n removeMediaTrack(mediaTrack: MediaStreamTrack): Promise<void>;\n}\n\nexport interface MediaServiceOptions {\n clientRole?: SessionClientRole;\n mediaServiceType: MediaServiceType;\n //How much to wait until raise \"peerConnectionTimeout\" if WebRTC connection events not arriving\n peerConnectivityTimeoutSeconds?: number;\n\n //How much to wait totally for video stream to arrive, before making fallback to image upload\n videoNotReceivedTimeoutSeconds?: number;\n}\n\nexport type MediaSessionIceCredentials = any;//TODO - Define type for credentials\n\nexport interface MediaSessionParams {\n sessionId: string;\n clientType: SessionClientType;\n clientRole: SessionClientRole;\n credentials: MediaSessionIceCredentials;\n initHandlers?: () => void;\n removeHandlers?: () => void;\n}\n\nexport interface MediaSessionConfiguration {\n //How much to wait until raise \"peerConnectionTimeout\" if WebRTC connection events not arriving\n peerConnectivityTimeoutSeconds: number;\n}\n\nexport interface MediaSubscriberParams {\n container: HTMLDivElement;\n streamType: KnownMediaStream;\n}\n\nexport interface MediaPublisherParams {\n streamTypes: KnownMediaStream[];\n destinationRole: SessionClientRole;\n}\n\nexport interface ClientWebRtcInfo {\n isWebRTCSupported: boolean;\n isWebsiteHasWebcamPermissions: boolean;\n isWebsiteHasMicrophonePermissions: boolean;\n isGetUserMediaSupported: boolean;\n isApplyConstraintsSupported: boolean;\n}\n\nexport interface IMediaSubscriber {\n readonly streamType: KnownMediaStream;\n readonly container: HTMLDivElement;\n readonly isPlaying: boolean;\n readonly isSoundMuted: boolean;\n readonly hasAudio: boolean;\n readonly hasVideo: boolean;\n readonly mediaElement: Nullable<HTMLVideoElement>;\n readonly videoWidth: number;\n readonly videoHeight: number;\n readonly renderWidth: number;\n readonly renderHeight: number;\n\n muteSound(isMuted: boolean): void;\n\n onStateChanged(callback: () => void): void;\n\n onDispose(callback: () => void): void;\n}\n\nexport type MediaSessionDisconnectedHandler = (reason: MediaSessionDisconnectReason) => void\n\nexport interface MediaSessionHandlers {\n onDisconnectedHandler: MediaSessionDisconnectedHandler;\n}\n\nexport interface DeviceSupportInfo {\n webRtcSupportInfo: ClientWebRtcInfo;\n hasCamera: boolean;\n hasMicrophone: boolean;\n videoPlayback: boolean;\n}\n\nexport interface RemoteTrackStats {\n trackId: string;\n trackStats: any;\n}\n\nexport interface IMediaSession {\n //Boolean parameter is used for supporting \"RTC Test\" application\n connect(connectOnly?: boolean): Promise<void>;\n\n disconnect(): Promise<void>;\n\n sessionDisconnect(): void;\n\n getRemoteTrackStats(mediaTrack: MediaStreamTrack): Promise<RemoteTrackStats>;\n\n onMediaStreamDestroyed(clientRole: SessionClientRole): Promise<void>;\n\n onMediaStreamRenewed(clientRole: SessionClientRole, mediaStream: MediaStream): Promise<void>;\n\n replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void>;\n\n registerEventCallback(event: string, cb: (eventArgs: any) => void): void;\n}\n\nexport interface SnapshotResult {\n base64img: string;\n objectUrl: string;\n imageBlob: Blob;\n mimeType: string;\n}\n\nexport interface SnapshotOptions {\n format?: string;\n quality?: number;\n x?: any;\n y?: any;\n w?: any;\n h?: any;\n}\n\nexport interface StreamDestroyedEventArgs {\n streamType: KnownMediaStream;\n reason: MediaStreamUnregisterReason;\n}\n\nexport interface StreamCreatedEventArgs {\n streamType: KnownMediaStream;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/MediaServiceBase.ts"],"names":[],"mappings":"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","file":"MediaServiceBase.d.ts","sourcesContent":["import filter from 'lodash/filter';\nimport cloneDeep from 'lodash/cloneDeep';\nimport values from 'lodash/values';\nimport debounce from 'lodash/debounce';\nimport includes from 'lodash/includes';\nimport {EventEmitter} from 'events';\nimport {Nullable} from '@techsee/techsee-common';\nimport {TechseeMediaSubscriber} from './MediaSubscriber';\nimport {TechseeMediaPublisher} from './MediaPublisher';\nimport {LocalStreamManager} from './LocalStreamManager';\nimport {TechseeMediaStream} from './TechseeMediaStream';\nimport {TurnWebRtcSession} from './MediaSession/SessionTurn';\nimport {getSnapshotFromMediaStream} from './MediaUtils/MediaDomUtils';\nimport {\n ClientWebRtcInfo,\n DeviceSupportInfo,\n IMediaEnvironment,\n IMediaSession,\n IMediaSubscriber,\n ISessionStreamsManager,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n MediaServiceOptions,\n MediaSessionConfiguration,\n MediaSessionHandlers,\n MediaSessionIceCredentials,\n MediaSessionParams,\n MediaSubscriberParams,\n RemoteMediaTrack,\n RemoteTrackStats,\n SnapshotOptions,\n SnapshotResult,\n StreamCreatedEventArgs,\n StreamDestroyedEventArgs\n} from './MediaContracts';\nimport {enumerateMediaDevices, isVideoPlaySupportedOnDevice} from './MediaUtils/Compatibility';\nimport {OpentokSession} from './MediaSession/SessionOpentok';\nimport {\n DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n LocalVideoSourceType,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {MediaServerSession} from './MediaSession/MediaServer';\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\nimport {recordingEvents} from './MediaSession/MediaSessionBase';\n\nconst trace = getMediaTracer('MediaServiceBase');\nconst traceStatsInfo = debounce(trace.info, 1000 * 5, {leading: true, maxWait: 1000 * 30});\n\nenum privateEvents {\n STREAM_CREATED = 'STREAM_CREATED',\n STREAM_DESTROYED = 'STREAM_DESTROYED'\n}\n\nexport abstract class TechseeMediaServiceBase {\n //This method should not be called. The only place that allowed to use it, is initLocalMediaStreams method.\n //Everyone who need to initialize MediaStream should use initLocalMediaStreams method.\n protected abstract getLocalMediaImplementation(): Promise<void>;\n\n protected abstract createMediaPublisher(destinationRole: SessionClientRole): Promise<Nullable<TechseeMediaPublisher>>;\n\n protected readonly _environment: IMediaEnvironment;\n protected readonly _sessionStreamsManager: ISessionStreamsManager;\n protected readonly _localStreamsManager: LocalStreamManager;\n\n private readonly _deviceSupportFlags: DeviceSupportInfo;\n private readonly _subscribers: Map<HTMLDivElement, TechseeMediaSubscriber> = new Map();\n private readonly _publishers: Map<SessionClientRole, TechseeMediaPublisher> = new Map();\n private readonly _publisherPromises: Map<SessionClientRole, Promise<Nullable<MediaStream>>> = new Map();\n protected _serviceOptions: Nullable<MediaServiceOptions> = null;\n private _isIOS_13_orLater = false;\n private _isIOS_14_orLater = false;\n private _autoReconnectEnabled = true;\n private readonly _emitter: EventEmitter;\n private _initLocalStreamsPromise: Nullable<Promise<void>> = null;\n private _initServicePromise: Nullable<Promise<void>> = null;\n private _registeredStreams: Map<KnownMediaStream, TechseeMediaStream> = new Map();\n private _isVoipEnabled = false;\n private _session: Nullable<{\n params: MediaSessionParams;\n connectPromise?: Promise<void>;\n instance?: IMediaSession;\n }> = null;\n\n protected constructor(environment: IMediaEnvironment, webRtcSupportInfo: ClientWebRtcInfo) {\n trace.info('TechseeMediaServiceBase created');\n\n this.bindClassMethods();\n\n this._emitter = new EventEmitter();\n this._environment = environment;\n this._deviceSupportFlags = {\n videoPlayback: false,\n hasCamera: false,\n hasMicrophone: false,\n webRtcSupportInfo: webRtcSupportInfo\n };\n\n this._localStreamsManager = new LocalStreamManager(this._environment);\n\n // TODO: Hack as IOS 13.3 and above sometimes do not return the video/audio device. This\n // is a hack to work around the issue until Apple resolve it\n this._isIOS_13_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 13));\n\n this._isIOS_14_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 14));\n\n trace.info(`TechseeMediaServiceBase: isIOS_13_orLater: ${this._isIOS_13_orLater}`);\n trace.info(`TechseeMediaServiceBase: isIOS_14_orLater: ${this._isIOS_14_orLater}`);\n\n this._sessionStreamsManager = {\n getMediaStreamForRole: this.getStreamForDestinationRole,\n addRemoteMediaTrack: this.registerRemoteMediaTrack,\n removeMediaTrack: this.unregisterRemoteMediaTrack\n };\n }\n\n get deviceSupportInfo(): DeviceSupportInfo {\n this.serviceInitGuard(false);\n\n return cloneDeep(this._deviceSupportFlags);\n }\n\n get isVoipEnabled(): boolean {\n return this._isVoipEnabled;\n }\n\n get isSessionActive(): boolean {\n return this._session !== null;\n }\n\n get isLocalStreamInitialized(): boolean {\n return this._initLocalStreamsPromise !== null;\n }\n\n /*\n Initializes media service. Before using anything from this service it should be initialized first.\n All of serviceOptions should be retrieved from account settings and by evaluating application state.\n For example, when initializing service, accountSettings is already loaded, and we know if FS session is audio enabled.\n */\n initMediaService(serviceOptions: MediaServiceOptions): Promise<void> {\n trace.info('TechseeMediaServiceBase.initMediaService', serviceOptions);\n const warnMessage = 'Multiple initialization does not supposed to happen. Not rejecting, but check the flow for correctness.';\n\n if (this._initServicePromise || this._serviceOptions) {\n console.warn(warnMessage);\n\n return this._initServicePromise ? this._initServicePromise : Promise.resolve();\n }\n\n this._serviceOptions = cloneDeep(serviceOptions);\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(this._serviceOptions.mediaServiceType)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n (this._isIOS_14_orLater ? Promise.resolve([]) : enumerateMediaDevices())\n .catch((error) => {\n trace.warn(error);\n\n return [];\n })\n .then((localDevicesList: MediaDeviceInfo[]) => {\n const groupedDevices = {\n video: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'videoinput'),\n audio: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'audioinput')\n };\n\n this._localStreamsManager.setGroupedDevices(groupedDevices);\n this._deviceSupportFlags.hasCamera = this._isIOS_13_orLater || (groupedDevices.video.length > 0);\n this._deviceSupportFlags.hasMicrophone = this._isIOS_13_orLater || (groupedDevices.audio.length > 0);\n\n })\n ]).then(() => undefined);\n\n\n return this._initServicePromise;\n }\n\n //#region Media Streams Management\n\n /*\n The main method to start the initialization of local media streams.\n This method can be executed many times and it will insure all calls are synced and only relevant stream will be created.\n DO NOT USE ANY OTHER METHOD THAT FORCES \"navigator.getUserMedia\" OTHER THAN THIS ONE.\n */\n initLocalMediaStreams(): Promise<void> {\n this.serviceInitGuard();\n\n // This hack for IOS 14 force a recreation of stream on seemingly unnecessary occasions (like agent page refresh).\n // we're not sure why but the stream breaks\n if (!this._initLocalStreamsPromise || this._isIOS_14_orLater) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error(`initLocalMediaStreams failure: ${mediaRequestFailResult}`);\n\n throw mediaRequestFailResult;\n });\n } else {\n trace.info('initLocalMediaStreams already done or in progress');\n }\n\n return this._initLocalStreamsPromise;\n }\n\n /*\n Creates an instance of subscriber that will \"listen\" to the requested stream.\n Whenever requested stream is available, subscriber will render it.\n */\n createSubscriber(subscriberParams: MediaSubscriberParams): Promise<IMediaSubscriber> {\n if (this._subscribers.has(subscriberParams.container)) {\n return Promise.reject('Subscriber for provided DIV element already exists');\n }\n\n const subscriber = new TechseeMediaSubscriber(subscriberParams);\n\n this._subscribers.set(subscriberParams.container, subscriber);\n const streamForSubscriber = this.getRegisteredStreamByType(subscriber.streamType);\n\n if (streamForSubscriber) {\n trace.info(`Stream for ${subscriberParams.streamType} is ready.`);\n subscriber.renderStream(streamForSubscriber);\n } else {\n trace.info(`Stream for ${subscriberParams.streamType} not created yet`);\n }\n\n return Promise.resolve(subscriber);\n }\n\n /*\n Destroys the subscriber for provided HTML DIV container\n */\n destroySubscriber(container: HTMLDivElement): Promise<void> {\n if (this._subscribers.has(container)) {\n const subscriber = this._subscribers.get(container);\n\n subscriber!.dispose();\n }\n\n this._subscribers.delete(container);\n\n return Promise.resolve();\n }\n\n onStreamDestroyed(callback: (eventArgs: StreamDestroyedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_DESTROYED, callback);\n }\n\n onStreamCreated(callback: (eventArgs: StreamCreatedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_CREATED, callback);\n }\n\n onRecordStarted(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STARTED, callback);\n }\n }\n\n onRecordStopped(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STOPPED, callback);\n }\n }\n\n onReconnecting(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECONNECTING, callback);\n }\n }\n\n protected getSwitchCameraConstraints() {\n const constraints = cloneDeep(window.latestLocalMediaConstraints);\n\n if (!constraints) {\n throw new Error('getSwitchCameraConstraints: unexpected use case constraints is null.');\n }\n\n trace.info('getSwitchCameraConstraints - Start switch camera with constraints:', constraints);\n\n const videoSourceType = (constraints.video as LocalVideoStreamConstraints).videoSourceType === LocalVideoSourceType.CAMERA\n ? LocalVideoSourceType.CAMERA_FRONT\n : LocalVideoSourceType.CAMERA;\n\n trace.info(`getSwitchCameraConstraints - switch to videoSourceType: ${videoSourceType}`);\n\n (constraints.video as LocalVideoStreamConstraints).videoSourceType = videoSourceType;\n\n return constraints;\n }\n\n switchCamera(revertCameraWhenFailed?: boolean): Promise<any> {\n trace.info('switchCamera: start');\n\n const latestLocalMediaConstraints = cloneDeep(window.latestLocalMediaConstraints);\n const constraints = !revertCameraWhenFailed ? this.getSwitchCameraConstraints() : latestLocalMediaConstraints;\n\n if (!constraints) {\n throw new Error('switchCamera - unexpected use case constraints is null.');\n }\n\n return this._localStreamsManager.destroyUserMediaStream()\n .then(() => this._localStreamsManager.getUserMediaStream(constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n const stream = streamResult.mediaStream;\n\n trace.info('switchCamera: new stream: ', stream);\n\n return this.replaceStreamTracks(stream)\n .then(() => this.registerStreamResult(constraints, streamResult, true)\n .then(() => ({\n revertCameraWhenFailed,\n constraints,\n streamResult\n })));\n }))\n .catch((err) => {\n trace.error('switchCamera: Failed to switch camera: ', err);\n\n if (!revertCameraWhenFailed) {\n window.latestLocalMediaConstraints = latestLocalMediaConstraints;\n\n return this.switchCamera(true);\n }\n\n return Promise.reject('Failed to switch camera.');\n });\n }\n\n private replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void> {\n if (this._session!.instance) {\n return this._session!.instance.replaceStreamTracks(mediaStream);\n }\n\n return Promise.reject('replaceStreamTracks - session instance is not exists.');\n }\n\n protected registerStreamResult(constraints: Nullable<LocalMediaConstraints>, streamResult: MediaRequestSuccessResult, switchCamera?: boolean, addStreamType?: KnownMediaStream) {\n const regPromises: any[] = [];\n\n if (streamResult.isNew) {\n trace.info('registerStreamResult: stream result from getUserMediaStream:', streamResult.mediaStream);\n streamResult.mediaStream.getTracks().forEach((mediaTrack) => {\n trace.info('registerStreamResult: stream result from mediaTrack:', mediaTrack);\n const streamType = addStreamType || (mediaTrack.kind === 'video'\n ? KnownMediaStream.USER_VIDEO_STREAM : KnownMediaStream.USER_AUDIO_STREAM);\n const newDedicatedStream = new TechseeMediaStream(mediaTrack, streamType, false);\n\n regPromises.push(switchCamera ? this.registerTrack(newDedicatedStream) : this.registerLocalMediaStream(newDedicatedStream));\n });\n }\n\n return Promise.all(regPromises);\n }\n\n disconnectFromMediaSession() {\n if (this._session && this._session.instance) {\n return this._session.instance.sessionDisconnect();\n }\n }\n\n get supportSwitchCameras() {\n return this._isIOS_13_orLater || this._localStreamsManager.groupedDevices.camerasCount > 1;\n }\n\n private _connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n this.serviceInitGuard();\n\n if (!this._session) {\n this._session = {params: cloneDeep(sessionParams)};\n this._session.connectPromise = new Promise((resolve, reject) => {\n const doAsyncReject = (error: any): void => {\n\n /*\n Reject with timeout, to change JS 'execution flow'.\n If timeout removed, session is assigned to null, before promise returns, and \"catch\" will not\n work on promise that is returned.\n */\n setTimeout(() => {\n this._session = null;\n reject(error);\n });\n };\n\n const sessionHandlers: MediaSessionHandlers = {\n onDisconnectedHandler: this.onSessionDisconnectHandler\n };\n const mediaSessionParams: MediaSessionParams & MediaSessionConfiguration = {\n ...sessionParams,\n peerConnectivityTimeoutSeconds: this._serviceOptions!.peerConnectivityTimeoutSeconds || DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS\n };\n\n trace.info('connectToSession', sessionParams);\n\n const allowedValues = values(MediaServiceType);\n\n switch (this._serviceOptions!.mediaServiceType) {\n case MediaServiceType.TURNSERVER:\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n break;\n case MediaServiceType.OPENTOK:\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n case MediaServiceType.MEDIASERVER:\n this._session!.instance = new MediaServerSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n default:\n trace.error(`mediaServiceType '${this._serviceOptions!.mediaServiceType}' is not supported.`, {allowedValues});\n doAsyncReject(new Error('mediaServiceType is not supported'));\n }\n\n if (this._session && this._session.instance) {\n this._session!.instance.connect()\n .then(() => {\n trace.info('Session is connected');\n resolve();\n })\n .catch((error: any) => {\n trace.error('Failed to connect to session', error);\n doAsyncReject(error);\n });\n }\n\n });\n } else {\n trace.warn('Session connected already. Disconnect before connect to new session again');\n }\n\n //TODO - Alex: Change a structure of this function to be more safe\n //(when we return, there should not be case when session or promise is null)\n //To fix, need to return promise immediately, then execute process in setTimeout(fn, 0);\n return this._session!.connectPromise!;\n }\n\n //#endregion Media Streams Management\n\n //#region Session Management\n\n /*\n Creates instance of WebRTC session which connects to (signaling server), and begins to listen to WebRTC events.\n */\n connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n return this._connectToSession(sessionParams);\n }\n\n /*\n Disconnects from WebRTC session.\n */\n disconnectFromSession(): Promise<void> {\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer);\n }\n\n /*\n Updates a credentials for turn server\n */\n updateSessionCredentials(credentials: MediaSessionIceCredentials): Promise<void> {\n trace.info('updateSessionCredentials');\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject('There no session to update credentials');\n }\n\n this._session!.params.credentials = cloneDeep(credentials);\n\n return Promise.resolve();\n }\n\n enableVoipDuringSession(): void {\n if (!this._isVoipEnabled && this.isLocalStreamInitialized) {\n this._initLocalStreamsPromise = null;\n }\n\n this._isVoipEnabled = true;\n }\n\n protected disconnectFromSessionInternal(reason: MediaSessionDisconnectReason): Promise<void> {\n if (!this._session) {\n return Promise.resolve();\n }\n\n trace.info('Disconnecting from session', reason);\n\n const lastSession = this._session;\n\n this._session = null;\n\n return Promise.all([\n lastSession.instance!.disconnect(),\n this.clearPublishers()\n ]).then(() => undefined);\n }\n\n protected getStatsForRemoteTrack(streamType: KnownMediaStream): Promise<RemoteTrackStats> {\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject(new Error('Media session not started'));\n }\n\n const streamForStats = this.getRegisteredStreamByType(streamType);\n\n if (!streamForStats) {\n return Promise.reject(new Error('Stream for requested type was not found'));\n }\n\n return this._session!.instance!.getRemoteTrackStats(streamForStats.mediaTrack)\n .then((trackStats: RemoteTrackStats) => {\n traceStatsInfo(`MediaTrackStats for ${streamType}: ${JSON.stringify(trackStats)}`);\n\n return {\n trackId: streamForStats.mediaTrack.id,\n trackStats: trackStats\n };\n });\n }\n\n private onSessionDisconnectHandler(reason: MediaSessionDisconnectReason): void {\n trace.info('onSessionDisconnectHandler', reason);\n if (reason !== MediaSessionDisconnectReason.ForcedByConsumer) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n this.disconnectFromSessionInternal(reason).then(() => {\n const reconnectReasons = [\n MediaSessionDisconnectReason.InitiatorPeerReconnected,\n MediaSessionDisconnectReason.SignalingChannelDisconnect,\n MediaSessionDisconnectReason.PeerConnectionInterrupted,\n MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout\n ];\n\n if (lastParams && includes(reconnectReasons, reason) && this._autoReconnectEnabled) {\n this.reconnectToSession(lastParams);\n } else if (!this._autoReconnectEnabled) {\n trace.info('No reconnection- auto reconnect disabled');\n } else {\n trace.info('No params for reconnection to media session');\n }\n });\n } else {\n trace.info('Ignore Session disconnect event');\n }\n }\n\n setAutoReconnect(state: boolean) {\n this._autoReconnectEnabled = state;\n trace.info('_autoReconnectEnabled:', state);\n\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n if (lastParams) {\n trace.info('setAutoReconnect - reconnect to session');\n this.reconnectToSession(lastParams);\n }\n }\n }\n\n reconnectToSession(lastParams: MediaSessionParams) {\n trace.info('Reconnecting to media session, sessionParams:', lastParams);\n\n this._connectToSession(lastParams)\n .then(() => {\n trace.info('Media session reconnected');\n })\n .catch((error: any) => {\n trace.error('Error while reconnecting to media session', error);\n });\n }\n\n //#endregion Session Management\n\n //#region Utils\n\n getSnapshotFromKnownStream(sourceStream: KnownMediaStream, snapshotOptions?: SnapshotOptions): Promise<SnapshotResult> {\n return new Promise((resolve, reject) => {\n if (sourceStream !== KnownMediaStream.USER_VIDEO_STREAM &&\n sourceStream !== KnownMediaStream.USER_SCREEN_SHARE_STREAM) {\n trace.error('The requested stream is not video stream, and cannot be used for snapshot');\n reject(new Error('INCOMPATIBLE_STREAM_FOR_SNAPSHOT'));\n\n return;\n }\n\n const snapshotStream = this.getRegisteredStreamByType(sourceStream);\n\n if (!snapshotStream) {\n trace.error('Cannot make snapshot: The requested stream not exists yet.');\n reject(new Error('NO_REQUESTED_STREAM'));\n\n return;\n }\n\n getSnapshotFromMediaStream(snapshotStream.mediaStream, snapshotOptions)\n .then((imageData: any) => {\n const urlComponents = imageData.split(';base64,');\n const mimeType = urlComponents[0].split(':')[1];\n const bytes = atob(urlComponents[1]);\n const buffer = new ArrayBuffer(bytes.length);\n const rawData = new Uint8Array(buffer);\n\n for (let i = 0; i < bytes.length; i++) {\n rawData[i] = bytes.charCodeAt(i);\n }\n\n const blob = new Blob([rawData], {type: mimeType});\n const objectUrl = window.URL.createObjectURL(blob);\n\n trace.info('Snapshot created successfully');\n\n const result: SnapshotResult = {\n base64img: imageData,\n objectUrl: objectUrl,\n imageBlob: blob,\n mimeType: mimeType\n };\n\n resolve(result);\n })\n .catch((error: any) => {\n trace.error('Error creating snapshot', error);\n reject(error);\n });\n });\n }\n\n //Will clean streams, publishers and subscribers, but will not remove event listeners\n clearService(): Promise<void> {\n trace.info('MediaService clearing all resources');\n\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer)\n .then(this.clearSubscribers)\n .then(this.clearRegisteredStreams)\n .then(this._localStreamsManager.clearAllStreams)\n .then(() => {\n this._initLocalStreamsPromise = null;\n this._isVoipEnabled = false;\n }).then(() => undefined);\n }\n\n //Will clear the service and remove all event listeners\n dispose(): Promise<void> {\n return this.clearService().catch(() => undefined).then(() => {\n this._emitter.removeAllListeners();\n });\n }\n\n //#endregion\n\n //#region Protected Methods\n\n protected get subscribers(): Map<HTMLDivElement, TechseeMediaSubscriber> {\n return this._subscribers;\n }\n\n protected registerLocalMediaStream(tsMediaStream: TechseeMediaStream): Promise<void> {\n trace.info('Registering local stream', tsMediaStream.streamType);\n if (this._registeredStreams.has(tsMediaStream.streamType)) {\n return Promise.reject(new Error(`Stream ${tsMediaStream.streamType} already registered`));\n }\n\n return this.registerStream(tsMediaStream);\n }\n\n protected getRegisteredStreamByType(streamType: KnownMediaStream): Nullable<TechseeMediaStream> {\n return this._registeredStreams.get(streamType) || null;\n }\n\n protected changeEnableForKnownStream(streamType: KnownMediaStream, isPaused: boolean): void {\n const streamToChangeState = this.getRegisteredStreamByType(streamType);\n\n if (!streamToChangeState) {\n trace.warn('There no stream found to change enable state', streamType);\n } else {\n streamToChangeState.mediaTrack.enabled = !isPaused;\n\n if (streamToChangeState.isRemote && streamToChangeState.streamKind === KnownMediaStreamKind.Audio) {\n this.subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamType) {\n subscriber.muteSound(isPaused);\n }\n });\n }\n\n trace.info('Local stream enable state is changed', {streamType, isPaused});\n }\n }\n\n protected registerEventCallback(event: string, callback: (eventArgs?: any) => void): void {\n this._emitter.on(event, callback);\n }\n\n protected unregisterEventCallback(event: string, callback: (eventArgs?: any) => void) {\n this._emitter.off(event, callback);\n }\n\n protected emitEvent(event: string, eventArgs?: any): void {\n setTimeout(() => {\n this._emitter.emit(event, eventArgs);\n });\n }\n\n //#endregion Protected Methods\n\n //#region Private Methods\n\n private registerRemoteMediaTrack(remoteMediaTrack: RemoteMediaTrack): Promise<void> {\n trace.info(`Registering remote ${remoteMediaTrack.trackType} MediaStreamTrack`, remoteMediaTrack.mediaTrack);\n const currentStream = this._registeredStreams.get(remoteMediaTrack.trackType);\n\n if (currentStream && !currentStream.isRemote) {\n return Promise.reject(new Error('Cannot register remote stream with the same type as local stream'));\n }\n\n const newDedicatedStream = new TechseeMediaStream(remoteMediaTrack.mediaTrack, remoteMediaTrack.trackType, true);\n\n return this.registerStream(newDedicatedStream);\n }\n\n private registerTrack(mediaStream: TechseeMediaStream): Promise<void> {\n mediaStream.mediaTrack.onended = () =>\n this.unregisterTechseeMediaStream(mediaStream, MediaStreamUnregisterReason.NativeEvent);\n\n this._registeredStreams.set(mediaStream.streamType, mediaStream);\n\n trace.info(`TechseeMediaStream registered - ${mediaStream.streamType}`, TechseeMediaStream);\n\n return this.updateSubscribersWithNewStream(mediaStream).then(() => {\n const eventArgs: StreamCreatedEventArgs = {streamType: mediaStream.streamType};\n\n this.emitEvent(privateEvents.STREAM_CREATED, eventArgs);\n });\n }\n\n protected registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(() => this.registerTrack(mediaStream));\n }\n\n return this.registerTrack(mediaStream);\n }\n\n private unregisterTechseeMediaStream(streamToUnregister: TechseeMediaStream, reason: MediaStreamUnregisterReason): Promise<void> {\n const traceError = (error: any): void => {\n trace.warn('Unregister TechseeMediaStream error:', error);\n };\n\n const promises: any = [];\n\n trace.info('Unregister TechseeMediaStream: ', streamToUnregister.streamType);\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamToUnregister.streamType) {\n promises.push(subscriber.stopRendering().catch(traceError));\n }\n });\n\n if (!streamToUnregister.isRemote) {\n this._publishers.forEach((publisher: TechseeMediaPublisher) => {\n if (publisher.streamTypes.find((streamType) => streamType === streamToUnregister.streamType)) {\n if (this._session && this._session!.instance) {\n promises.push(this._session.instance.onMediaStreamDestroyed(publisher.destinationRole).catch(traceError));\n }\n }\n });\n }\n\n return Promise.all(promises)\n .then(() => {\n this._registeredStreams.delete(streamToUnregister.streamType);\n trace.info('Stream deleted', streamToUnregister.streamType);\n\n const eventArgs: StreamDestroyedEventArgs = {streamType: streamToUnregister.streamType, reason: reason};\n\n this.emitEvent(privateEvents.STREAM_DESTROYED, eventArgs);\n });\n }\n\n private unregisterRemoteMediaTrack(mediaTrack: MediaStreamTrack): Promise<void> {\n const promises: any = [];\n\n this._registeredStreams.forEach((registeredStream: TechseeMediaStream) => {\n if (registeredStream.mediaTrack.id === mediaTrack.id) {\n promises.push(this.unregisterTechseeMediaStream(registeredStream, MediaStreamUnregisterReason.ClosedRemotely));\n }\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private removePublisher(publisher: TechseeMediaPublisher): Promise<void> {\n this._publishers.delete(publisher.destinationRole);\n if (this._session && this._session.instance) {\n trace.info('Removing publisher', publisher.destinationRole);\n\n return this._session.instance.onMediaStreamDestroyed(publisher.destinationRole)\n .catch((error: any) => {\n trace.warn('Error while removing publisher', error);\n });\n }\n\n return Promise.resolve();\n }\n\n private getStreamForDestinationRole(destinationRole: SessionClientRole): Promise<Nullable<MediaStream>> {\n this.serviceInitGuard();\n\n if (this._publishers.has(destinationRole)) {\n trace.info(`Publisher for ${destinationRole} already exists`);\n\n return Promise.resolve(this._publishers.get(destinationRole)!.mediaStream);\n }\n\n if (!this._publisherPromises.get(destinationRole)) {\n trace.info(`Creating publisher for ${destinationRole}`);\n const publisherPromise = this.initLocalMediaStreams()\n .then(() => this.createMediaPublisher(destinationRole))\n .then((mediaPublisher: Nullable<TechseeMediaPublisher>) => {\n if (mediaPublisher) {\n this._publishers.set(destinationRole, mediaPublisher);\n }\n\n this._publisherPromises.delete(destinationRole);\n\n return mediaPublisher ? mediaPublisher.mediaStream : null;\n })\n .catch((ex: any) => {\n if (ex && ex.message === 'audioStreamFailed') {\n this._isVoipEnabled = false;\n\n return null;\n }\n\n this._publisherPromises.delete(destinationRole);\n throw ex;\n });\n\n this._publisherPromises.set(destinationRole, publisherPromise);\n } else {\n trace.info(`Create publisher promise for ${destinationRole} already exists`);\n }\n\n return this._publisherPromises.get(destinationRole)!;\n }\n\n private updateSubscribersWithNewStream(registeredStream: TechseeMediaStream): Promise<void> {\n trace.info(`Updating subscribers of ${registeredStream.streamType} with new stream`);\n\n if (this._subscribers.size === 0) {\n trace.warn(`No subscribers exists for the ${registeredStream.streamType}.`);\n } else {\n trace.info(`Total ${this._subscribers.size} subscribers exists, will check if rerender needed.`);\n }\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === registeredStream.streamType) {\n trace.info(`${registeredStream.streamType} rendering on subscriber`);\n subscriber.renderStream(registeredStream);\n }\n });\n\n return Promise.resolve();\n }\n\n private bindClassMethods(): void {\n this.updateSessionCredentials = this.updateSessionCredentials.bind(this);\n this.getStreamForDestinationRole = this.getStreamForDestinationRole.bind(this);\n this.registerRemoteMediaTrack = this.registerRemoteMediaTrack.bind(this);\n this.unregisterRemoteMediaTrack = this.unregisterRemoteMediaTrack.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.updateSubscribersWithNewStream = this.updateSubscribersWithNewStream.bind(this);\n this.initLocalMediaStreams = this.initLocalMediaStreams.bind(this);\n this.onSessionDisconnectHandler = this.onSessionDisconnectHandler.bind(this);\n this.clearService = this.clearService.bind(this);\n this.clearRegisteredStreams = this.clearRegisteredStreams.bind(this);\n this.clearPublishers = this.clearPublishers.bind(this);\n this.clearSubscribers = this.clearSubscribers.bind(this);\n this._connectToSession = this._connectToSession.bind(this);\n this.disconnectFromMediaSession = this.disconnectFromMediaSession.bind(this);\n }\n\n //region Cleanup\n\n private clearRegisteredStreams(): Promise<void> {\n trace.info('Clearing registered streams');\n const promises: any = [];\n\n this._registeredStreams.forEach((streamToUnregister: TechseeMediaStream) => {\n promises.push(this.unregisterTechseeMediaStream(streamToUnregister, MediaStreamUnregisterReason.ServiceCleanUp));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearSubscribers(): Promise<void> {\n trace.info('Clearing subscribers');\n const promises: any = [];\n\n this._subscribers.forEach((subscriber) => {\n promises.push(this.destroySubscriber(subscriber.container));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearPublishers(): Promise<void> {\n trace.info('Clearing publishers');\n const promises: any = [];\n\n this._publishers.forEach((publisher) => {\n promises.push(this.removePublisher(publisher));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n //#endregion Cleanup\n\n //#region Simple Validation Methods\n\n protected serviceInitGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._serviceOptions, 'Media service is not initialized', shouldThrow);\n }\n\n protected sessionExistsGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._session, 'There no active session', shouldThrow);\n }\n\n //#endregion\n\n //#endregion Private Methods\n}\n"]}
1
+ {"version":3,"sources":["../src/MediaServiceBase.ts"],"names":[],"mappings":"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","file":"MediaServiceBase.d.ts","sourcesContent":["import filter from 'lodash/filter';\nimport cloneDeep from 'lodash/cloneDeep';\nimport values from 'lodash/values';\nimport debounce from 'lodash/debounce';\nimport includes from 'lodash/includes';\nimport {EventEmitter} from 'events';\nimport {Nullable} from '@techsee/techsee-common';\nimport {TechseeMediaSubscriber} from './MediaSubscriber';\nimport {TechseeMediaPublisher} from './MediaPublisher';\nimport {LocalStreamManager} from './LocalStreamManager';\nimport {TechseeMediaStream} from './TechseeMediaStream';\nimport {TurnWebRtcSession} from './MediaSession/SessionTurn';\nimport {getSnapshotFromMediaStream} from './MediaUtils/MediaDomUtils';\nimport {\n ClientWebRtcInfo,\n DeviceSupportInfo,\n IMediaEnvironment,\n IMediaSession,\n IMediaSubscriber,\n ISessionStreamsManager,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n MediaServiceOptions,\n MediaSessionConfiguration,\n MediaSessionHandlers,\n MediaSessionIceCredentials,\n MediaSessionParams,\n MediaSubscriberParams,\n RemoteMediaTrack,\n RemoteTrackStats,\n SnapshotOptions,\n SnapshotResult,\n StreamCreatedEventArgs,\n StreamDestroyedEventArgs\n} from './MediaContracts';\nimport {enumerateMediaDevices, isVideoPlaySupportedOnDevice} from './MediaUtils/Compatibility';\nimport {OpentokSession} from './MediaSession/SessionOpentok';\nimport {\n DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n LocalVideoSourceType,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {MediaServerSession} from './MediaSession/MediaServer';\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\nimport {recordingEvents} from './MediaSession/MediaSessionBase';\n\nconst trace = getMediaTracer('MediaServiceBase');\nconst traceStatsInfo = debounce(trace.info, 1000 * 5, {leading: true, maxWait: 1000 * 30});\n\nenum privateEvents {\n STREAM_CREATED = 'STREAM_CREATED',\n STREAM_DESTROYED = 'STREAM_DESTROYED'\n}\n\nexport abstract class TechseeMediaServiceBase {\n //This method should not be called. The only place that allowed to use it, is initLocalMediaStreams method.\n //Everyone who need to initialize MediaStream should use initLocalMediaStreams method.\n protected abstract getLocalMediaImplementation(): Promise<void>;\n\n protected abstract createMediaPublisher(destinationRole: SessionClientRole): Promise<Nullable<TechseeMediaPublisher>>;\n\n protected readonly _environment: IMediaEnvironment;\n protected readonly _sessionStreamsManager: ISessionStreamsManager;\n protected readonly _localStreamsManager: LocalStreamManager;\n\n private readonly _deviceSupportFlags: DeviceSupportInfo;\n private readonly _subscribers: Map<HTMLDivElement, TechseeMediaSubscriber> = new Map();\n private readonly _publishers: Map<SessionClientRole, TechseeMediaPublisher> = new Map();\n private readonly _publisherPromises: Map<SessionClientRole, Promise<Nullable<MediaStream>>> = new Map();\n protected _serviceOptions: Nullable<MediaServiceOptions> = null;\n private _isIOS_13_orLater = false;\n private _isIOS_14_orLater = false;\n private _autoReconnectEnabled = true;\n private readonly _emitter: EventEmitter;\n private _initLocalStreamsPromise: Nullable<Promise<void>> = null;\n private _initServicePromise: Nullable<Promise<void>> = null;\n private _registeredStreams: Map<KnownMediaStream, TechseeMediaStream> = new Map();\n private _isVoipEnabled = false;\n private _session: Nullable<{\n params: MediaSessionParams;\n connectPromise?: Promise<void>;\n instance?: IMediaSession;\n }> = null;\n\n protected constructor(environment: IMediaEnvironment, webRtcSupportInfo: ClientWebRtcInfo) {\n trace.info('TechseeMediaServiceBase created');\n\n this.bindClassMethods();\n\n this._emitter = new EventEmitter();\n this._environment = environment;\n this._deviceSupportFlags = {\n videoPlayback: false,\n hasCamera: false,\n hasMicrophone: false,\n webRtcSupportInfo: webRtcSupportInfo\n };\n\n this._localStreamsManager = new LocalStreamManager(this._environment);\n\n // TODO: Hack as IOS 13.3 and above sometimes do not return the video/audio device. This\n // is a hack to work around the issue until Apple resolve it\n this._isIOS_13_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 13));\n\n this._isIOS_14_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 14));\n\n trace.info(`TechseeMediaServiceBase: isIOS_13_orLater: ${this._isIOS_13_orLater}`);\n trace.info(`TechseeMediaServiceBase: isIOS_14_orLater: ${this._isIOS_14_orLater}`);\n\n this._sessionStreamsManager = {\n getMediaStreamForRole: this.getStreamForDestinationRole,\n addRemoteMediaTrack: this.registerRemoteMediaTrack,\n removeMediaTrack: this.unregisterRemoteMediaTrack\n };\n }\n\n get deviceSupportInfo(): DeviceSupportInfo {\n this.serviceInitGuard(false);\n\n return cloneDeep(this._deviceSupportFlags);\n }\n\n get isVoipEnabled(): boolean {\n return this._isVoipEnabled;\n }\n\n get isSessionActive(): boolean {\n return this._session !== null;\n }\n\n get isLocalStreamInitialized(): boolean {\n return this._initLocalStreamsPromise !== null;\n }\n\n /*\n Initializes media service. Before using anything from this service it should be initialized first.\n All of serviceOptions should be retrieved from account settings and by evaluating application state.\n For example, when initializing service, accountSettings is already loaded, and we know if FS session is audio enabled.\n */\n initMediaService(serviceOptions: MediaServiceOptions): Promise<void> {\n trace.info('TechseeMediaServiceBase.initMediaService', serviceOptions);\n const warnMessage = 'Multiple initialization does not supposed to happen. Not rejecting, but check the flow for correctness.';\n\n if (this._initServicePromise || this._serviceOptions) {\n console.warn(warnMessage);\n\n return this._initServicePromise ? this._initServicePromise : Promise.resolve();\n }\n\n this._serviceOptions = cloneDeep(serviceOptions);\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(this._serviceOptions.mediaServiceType)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n (this._isIOS_14_orLater ? Promise.resolve([]) : enumerateMediaDevices())\n .catch((error) => {\n trace.warn(error);\n\n return [];\n })\n .then((localDevicesList: MediaDeviceInfo[]) => {\n const groupedDevices = {\n video: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'videoinput'),\n audio: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'audioinput')\n };\n\n this._localStreamsManager.setGroupedDevices(groupedDevices);\n this._deviceSupportFlags.hasCamera = this._isIOS_13_orLater || (groupedDevices.video.length > 0);\n this._deviceSupportFlags.hasMicrophone = this._isIOS_13_orLater || (groupedDevices.audio.length > 0);\n\n })\n ]).then(() => undefined);\n\n\n return this._initServicePromise;\n }\n\n //#region Media Streams Management\n\n /*\n The main method to start the initialization of local media streams.\n This method can be executed many times and it will insure all calls are synced and only relevant stream will be created.\n DO NOT USE ANY OTHER METHOD THAT FORCES \"navigator.getUserMedia\" OTHER THAN THIS ONE.\n */\n initLocalMediaStreams(): Promise<void> {\n this.serviceInitGuard();\n const isDashboardOpentok = this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK &&\n (this._serviceOptions!.clientRole === SessionClientRole.AGENT || this._serviceOptions!.clientRole === SessionClientRole.OBSERVER);\n\n // This hack for IOS 14 force a recreation of stream on seemingly unnecessary occasions (like agent page refresh).\n // we're not sure why but the stream breaks\n if (!this._initLocalStreamsPromise || (this._isIOS_14_orLater && !isDashboardOpentok)) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error(`initLocalMediaStreams failure: ${mediaRequestFailResult}`);\n\n throw mediaRequestFailResult;\n });\n } else {\n trace.info('initLocalMediaStreams already done or in progress');\n }\n\n return this._initLocalStreamsPromise;\n }\n\n /*\n Creates an instance of subscriber that will \"listen\" to the requested stream.\n Whenever requested stream is available, subscriber will render it.\n */\n createSubscriber(subscriberParams: MediaSubscriberParams): Promise<IMediaSubscriber> {\n if (this._subscribers.has(subscriberParams.container)) {\n return Promise.reject('Subscriber for provided DIV element already exists');\n }\n\n const subscriber = new TechseeMediaSubscriber(subscriberParams);\n\n this._subscribers.set(subscriberParams.container, subscriber);\n const streamForSubscriber = this.getRegisteredStreamByType(subscriber.streamType);\n\n if (streamForSubscriber) {\n trace.info(`Stream for ${subscriberParams.streamType} is ready.`);\n subscriber.renderStream(streamForSubscriber);\n } else {\n trace.info(`Stream for ${subscriberParams.streamType} not created yet`);\n }\n\n return Promise.resolve(subscriber);\n }\n\n /*\n Destroys the subscriber for provided HTML DIV container\n */\n destroySubscriber(container: HTMLDivElement): Promise<void> {\n if (this._subscribers.has(container)) {\n const subscriber = this._subscribers.get(container);\n\n subscriber!.dispose();\n }\n\n this._subscribers.delete(container);\n\n return Promise.resolve();\n }\n\n onStreamDestroyed(callback: (eventArgs: StreamDestroyedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_DESTROYED, callback);\n }\n\n onStreamCreated(callback: (eventArgs: StreamCreatedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_CREATED, callback);\n }\n\n onRecordStarted(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STARTED, callback);\n }\n }\n\n onRecordStopped(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STOPPED, callback);\n }\n }\n\n onReconnecting(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECONNECTING, callback);\n }\n }\n\n protected getSwitchCameraConstraints() {\n const constraints = cloneDeep(window.latestLocalMediaConstraints);\n\n if (!constraints) {\n throw new Error('getSwitchCameraConstraints: unexpected use case constraints is null.');\n }\n\n trace.info('getSwitchCameraConstraints - Start switch camera with constraints:', constraints);\n\n const videoSourceType = (constraints.video as LocalVideoStreamConstraints).videoSourceType === LocalVideoSourceType.CAMERA\n ? LocalVideoSourceType.CAMERA_FRONT\n : LocalVideoSourceType.CAMERA;\n\n trace.info(`getSwitchCameraConstraints - switch to videoSourceType: ${videoSourceType}`);\n\n (constraints.video as LocalVideoStreamConstraints).videoSourceType = videoSourceType;\n\n return constraints;\n }\n\n switchCamera(revertCameraWhenFailed?: boolean): Promise<any> {\n trace.info('switchCamera: start');\n\n const latestLocalMediaConstraints = cloneDeep(window.latestLocalMediaConstraints);\n const constraints = !revertCameraWhenFailed ? this.getSwitchCameraConstraints() : latestLocalMediaConstraints;\n\n if (!constraints) {\n throw new Error('switchCamera - unexpected use case constraints is null.');\n }\n\n return this._localStreamsManager.destroyUserMediaStream()\n .then(() => this._localStreamsManager.getUserMediaStream(constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n const stream = streamResult.mediaStream;\n\n trace.info('switchCamera: new stream: ', stream);\n\n return this.replaceStreamTracks(stream)\n .then(() => this.registerStreamResult(constraints, streamResult, true)\n .then(() => ({\n revertCameraWhenFailed,\n constraints,\n streamResult\n })));\n }))\n .catch((err) => {\n trace.error('switchCamera: Failed to switch camera: ', err);\n\n if (!revertCameraWhenFailed) {\n window.latestLocalMediaConstraints = latestLocalMediaConstraints;\n\n return this.switchCamera(true);\n }\n\n return Promise.reject('Failed to switch camera.');\n });\n }\n\n private replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void> {\n if (this._session!.instance) {\n return this._session!.instance.replaceStreamTracks(mediaStream);\n }\n\n return Promise.reject('replaceStreamTracks - session instance is not exists.');\n }\n\n protected registerStreamResult(constraints: Nullable<LocalMediaConstraints>, streamResult: MediaRequestSuccessResult, switchCamera?: boolean, addStreamType?: KnownMediaStream) {\n const regPromises: any[] = [];\n const isOpentok = this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK;\n\n if (streamResult.isNew) {\n trace.info('registerStreamResult: stream result from getUserMediaStream:', streamResult.mediaStream);\n streamResult.mediaStream.getTracks().forEach((mediaTrack) => {\n trace.info('registerStreamResult: stream result from mediaTrack:', mediaTrack);\n const streamType = addStreamType || (mediaTrack.kind === 'video'\n ? KnownMediaStream.USER_VIDEO_STREAM : KnownMediaStream.USER_AUDIO_STREAM);\n const newDedicatedStream = new TechseeMediaStream(mediaTrack, streamType, false);\n\n if (isOpentok) {\n regPromises.push(this.registerStream(newDedicatedStream));\n } else {\n regPromises.push(switchCamera ? this.registerTrack(newDedicatedStream) : this.registerLocalMediaStream(newDedicatedStream));\n }\n });\n }\n\n return Promise.all(regPromises);\n }\n\n disconnectFromMediaSession() {\n if (this._session && this._session.instance) {\n return this._session.instance.sessionDisconnect();\n }\n }\n\n get supportSwitchCameras() {\n return this._isIOS_13_orLater || this._localStreamsManager.groupedDevices.camerasCount > 1;\n }\n\n private _connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n this.serviceInitGuard();\n\n if (!this._session) {\n this._session = {params: cloneDeep(sessionParams)};\n this._session.connectPromise = new Promise((resolve, reject) => {\n const doAsyncReject = (error: any): void => {\n\n /*\n Reject with timeout, to change JS 'execution flow'.\n If timeout removed, session is assigned to null, before promise returns, and \"catch\" will not\n work on promise that is returned.\n */\n setTimeout(() => {\n this._session = null;\n reject(error);\n });\n };\n\n const sessionHandlers: MediaSessionHandlers = {\n onDisconnectedHandler: this.onSessionDisconnectHandler\n };\n const mediaSessionParams: MediaSessionParams & MediaSessionConfiguration = {\n ...sessionParams,\n peerConnectivityTimeoutSeconds: this._serviceOptions!.peerConnectivityTimeoutSeconds || DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS\n };\n\n trace.info('connectToSession', sessionParams);\n\n const allowedValues = values(MediaServiceType);\n\n switch (this._serviceOptions!.mediaServiceType) {\n case MediaServiceType.TURNSERVER:\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n break;\n case MediaServiceType.OPENTOK:\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n case MediaServiceType.MEDIASERVER:\n this._session!.instance = new MediaServerSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n default:\n trace.error(`mediaServiceType '${this._serviceOptions!.mediaServiceType}' is not supported.`, {allowedValues});\n doAsyncReject(new Error('mediaServiceType is not supported'));\n }\n\n if (this._session && this._session.instance) {\n this._session!.instance.connect()\n .then(() => {\n trace.info('Session is connected');\n resolve();\n })\n .catch((error: any) => {\n trace.error('Failed to connect to session', error);\n doAsyncReject(error);\n });\n }\n\n });\n } else {\n trace.warn('Session connected already. Disconnect before connect to new session again');\n }\n\n //TODO - Alex: Change a structure of this function to be more safe\n //(when we return, there should not be case when session or promise is null)\n //To fix, need to return promise immediately, then execute process in setTimeout(fn, 0);\n return this._session!.connectPromise!;\n }\n\n //#endregion Media Streams Management\n\n //#region Session Management\n\n /*\n Creates instance of WebRTC session which connects to (signaling server), and begins to listen to WebRTC events.\n */\n connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n return this._connectToSession(sessionParams);\n }\n\n /*\n Disconnects from WebRTC session.\n */\n disconnectFromSession(): Promise<void> {\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer);\n }\n\n /*\n Updates a credentials for turn server\n */\n updateSessionCredentials(credentials: MediaSessionIceCredentials): Promise<void> {\n trace.info('updateSessionCredentials');\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject('There no session to update credentials');\n }\n\n this._session!.params.credentials = cloneDeep(credentials);\n\n return Promise.resolve();\n }\n\n enableVoipDuringSession(): void {\n if (!this._isVoipEnabled && this.isLocalStreamInitialized) {\n this._initLocalStreamsPromise = null;\n }\n\n this._isVoipEnabled = true;\n }\n\n protected disconnectFromSessionInternal(reason: MediaSessionDisconnectReason): Promise<void> {\n if (!this._session) {\n return Promise.resolve();\n }\n\n trace.info('Disconnecting from session', reason);\n\n const lastSession = this._session;\n\n this._session = null;\n\n return Promise.all([\n lastSession.instance!.disconnect(),\n this.clearPublishers()\n ]).then(() => undefined);\n }\n\n protected getStatsForRemoteTrack(streamType: KnownMediaStream): Promise<RemoteTrackStats> {\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject(new Error('Media session not started'));\n }\n\n const streamForStats = this.getRegisteredStreamByType(streamType);\n\n if (!streamForStats) {\n return Promise.reject(new Error('Stream for requested type was not found'));\n }\n\n return this._session!.instance!.getRemoteTrackStats(streamForStats.mediaTrack)\n .then((trackStats: RemoteTrackStats) => {\n traceStatsInfo(`MediaTrackStats for ${streamType}: ${JSON.stringify(trackStats)}`);\n\n return {\n trackId: streamForStats.mediaTrack.id,\n trackStats: trackStats\n };\n });\n }\n\n private onSessionDisconnectHandler(reason: MediaSessionDisconnectReason): void {\n trace.info('onSessionDisconnectHandler', reason);\n if (reason !== MediaSessionDisconnectReason.ForcedByConsumer) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n this.disconnectFromSessionInternal(reason).then(() => {\n const reconnectReasons = [\n MediaSessionDisconnectReason.InitiatorPeerReconnected,\n MediaSessionDisconnectReason.SignalingChannelDisconnect,\n MediaSessionDisconnectReason.PeerConnectionInterrupted,\n MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout\n ];\n\n if (lastParams && includes(reconnectReasons, reason) && this._autoReconnectEnabled) {\n this.reconnectToSession(lastParams);\n } else if (!this._autoReconnectEnabled) {\n trace.info('No reconnection- auto reconnect disabled');\n } else {\n trace.info('No params for reconnection to media session');\n }\n });\n } else {\n trace.info('Ignore Session disconnect event');\n }\n }\n\n setAutoReconnect(state: boolean) {\n this._autoReconnectEnabled = state;\n trace.info('_autoReconnectEnabled:', state);\n\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n if (lastParams) {\n trace.info('setAutoReconnect - reconnect to session');\n this.reconnectToSession(lastParams);\n }\n }\n }\n\n reconnectToSession(lastParams: MediaSessionParams) {\n trace.info('Reconnecting to media session, sessionParams:', lastParams);\n\n this._connectToSession(lastParams)\n .then(() => {\n trace.info('Media session reconnected');\n })\n .catch((error: any) => {\n trace.error('Error while reconnecting to media session', error);\n });\n }\n\n //#endregion Session Management\n\n //#region Utils\n\n getSnapshotFromKnownStream(sourceStream: KnownMediaStream, snapshotOptions?: SnapshotOptions): Promise<SnapshotResult> {\n return new Promise((resolve, reject) => {\n if (sourceStream !== KnownMediaStream.USER_VIDEO_STREAM &&\n sourceStream !== KnownMediaStream.USER_SCREEN_SHARE_STREAM) {\n trace.error('The requested stream is not video stream, and cannot be used for snapshot');\n reject(new Error('INCOMPATIBLE_STREAM_FOR_SNAPSHOT'));\n\n return;\n }\n\n const snapshotStream = this.getRegisteredStreamByType(sourceStream);\n\n if (!snapshotStream) {\n trace.error('Cannot make snapshot: The requested stream not exists yet.');\n reject(new Error('NO_REQUESTED_STREAM'));\n\n return;\n }\n\n getSnapshotFromMediaStream(snapshotStream.mediaStream, snapshotOptions)\n .then((imageData: any) => {\n const urlComponents = imageData.split(';base64,');\n const mimeType = urlComponents[0].split(':')[1];\n const bytes = atob(urlComponents[1]);\n const buffer = new ArrayBuffer(bytes.length);\n const rawData = new Uint8Array(buffer);\n\n for (let i = 0; i < bytes.length; i++) {\n rawData[i] = bytes.charCodeAt(i);\n }\n\n const blob = new Blob([rawData], {type: mimeType});\n const objectUrl = window.URL.createObjectURL(blob);\n\n trace.info('Snapshot created successfully');\n\n const result: SnapshotResult = {\n base64img: imageData,\n objectUrl: objectUrl,\n imageBlob: blob,\n mimeType: mimeType\n };\n\n resolve(result);\n })\n .catch((error: any) => {\n trace.error('Error creating snapshot', error);\n reject(error);\n });\n });\n }\n\n //Will clean streams, publishers and subscribers, but will not remove event listeners\n clearService(): Promise<void> {\n trace.info('MediaService clearing all resources');\n\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer)\n .then(this.clearSubscribers)\n .then(this.clearRegisteredStreams)\n .then(this._localStreamsManager.clearAllStreams)\n .then(() => {\n this._initLocalStreamsPromise = null;\n this._isVoipEnabled = false;\n }).then(() => undefined);\n }\n\n //Will clear the service and remove all event listeners\n dispose(): Promise<void> {\n return this.clearService().catch(() => undefined).then(() => {\n this._emitter.removeAllListeners();\n });\n }\n\n //#endregion\n\n //#region Protected Methods\n\n protected get subscribers(): Map<HTMLDivElement, TechseeMediaSubscriber> {\n return this._subscribers;\n }\n\n protected registerLocalMediaStream(tsMediaStream: TechseeMediaStream): Promise<void> {\n trace.info('Registering local stream', tsMediaStream.streamType);\n if (this._registeredStreams.has(tsMediaStream.streamType)) {\n return Promise.reject(new Error(`Stream ${tsMediaStream.streamType} already registered`));\n }\n\n return this.registerStream(tsMediaStream);\n }\n\n protected getRegisteredStreamByType(streamType: KnownMediaStream): Nullable<TechseeMediaStream> {\n return this._registeredStreams.get(streamType) || null;\n }\n\n protected changeEnableForKnownStream(streamType: KnownMediaStream, isPaused: boolean): void {\n const streamToChangeState = this.getRegisteredStreamByType(streamType);\n\n if (!streamToChangeState) {\n trace.warn('There no stream found to change enable state', streamType);\n } else {\n streamToChangeState.mediaTrack.enabled = !isPaused;\n\n if (streamToChangeState.isRemote && streamToChangeState.streamKind === KnownMediaStreamKind.Audio) {\n this.subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamType) {\n subscriber.muteSound(isPaused);\n }\n });\n }\n\n trace.info('Local stream enable state is changed', {streamType, isPaused});\n }\n }\n\n protected registerEventCallback(event: string, callback: (eventArgs?: any) => void): void {\n this._emitter.on(event, callback);\n }\n\n protected unregisterEventCallback(event: string, callback: (eventArgs?: any) => void) {\n this._emitter.off(event, callback);\n }\n\n protected emitEvent(event: string, eventArgs?: any): void {\n setTimeout(() => {\n this._emitter.emit(event, eventArgs);\n });\n }\n\n //#endregion Protected Methods\n\n //#region Private Methods\n\n private registerRemoteMediaTrack(remoteMediaTrack: RemoteMediaTrack): Promise<void> {\n trace.info(`Registering remote ${remoteMediaTrack.trackType} MediaStreamTrack`, remoteMediaTrack.mediaTrack);\n const currentStream = this._registeredStreams.get(remoteMediaTrack.trackType);\n\n if (currentStream && !currentStream.isRemote) {\n return Promise.reject(new Error('Cannot register remote stream with the same type as local stream'));\n }\n\n const newDedicatedStream = new TechseeMediaStream(remoteMediaTrack.mediaTrack, remoteMediaTrack.trackType, true);\n\n return this.registerStream(newDedicatedStream);\n }\n\n private registerTrack(mediaStream: TechseeMediaStream): Promise<void> {\n mediaStream.mediaTrack.onended = () =>\n this.unregisterTechseeMediaStream(mediaStream, MediaStreamUnregisterReason.NativeEvent);\n\n this._registeredStreams.set(mediaStream.streamType, mediaStream);\n\n trace.info(`TechseeMediaStream registered - ${mediaStream.streamType}`, TechseeMediaStream);\n\n return this.updateSubscribersWithNewStream(mediaStream).then(() => {\n const eventArgs: StreamCreatedEventArgs = {streamType: mediaStream.streamType};\n\n this.emitEvent(privateEvents.STREAM_CREATED, eventArgs);\n });\n }\n\n protected registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(() => this.registerTrack(mediaStream));\n }\n\n return this.registerTrack(mediaStream);\n }\n\n private unregisterTechseeMediaStream(streamToUnregister: TechseeMediaStream, reason: MediaStreamUnregisterReason): Promise<void> {\n const traceError = (error: any): void => {\n trace.warn('Unregister TechseeMediaStream error:', error);\n };\n\n const promises: any = [];\n\n trace.info('Unregister TechseeMediaStream: ', streamToUnregister.streamType);\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamToUnregister.streamType) {\n promises.push(subscriber.stopRendering().catch(traceError));\n }\n });\n\n if (!streamToUnregister.isRemote) {\n this._publishers.forEach((publisher: TechseeMediaPublisher) => {\n if (publisher.streamTypes.find((streamType) => streamType === streamToUnregister.streamType)) {\n if (this._session && this._session!.instance) {\n promises.push(this._session.instance.onMediaStreamDestroyed(publisher.destinationRole).catch(traceError));\n }\n }\n });\n }\n\n return Promise.all(promises)\n .then(() => {\n this._registeredStreams.delete(streamToUnregister.streamType);\n trace.info('Stream deleted', streamToUnregister.streamType);\n\n const eventArgs: StreamDestroyedEventArgs = {streamType: streamToUnregister.streamType, reason: reason};\n\n this.emitEvent(privateEvents.STREAM_DESTROYED, eventArgs);\n });\n }\n\n private unregisterRemoteMediaTrack(mediaTrack: MediaStreamTrack): Promise<void> {\n const promises: any = [];\n\n this._registeredStreams.forEach((registeredStream: TechseeMediaStream) => {\n if (registeredStream.mediaTrack.id === mediaTrack.id) {\n promises.push(this.unregisterTechseeMediaStream(registeredStream, MediaStreamUnregisterReason.ClosedRemotely));\n }\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private removePublisher(publisher: TechseeMediaPublisher): Promise<void> {\n this._publishers.delete(publisher.destinationRole);\n if (this._session && this._session.instance) {\n trace.info('Removing publisher', publisher.destinationRole);\n\n return this._session.instance.onMediaStreamDestroyed(publisher.destinationRole)\n .catch((error: any) => {\n trace.warn('Error while removing publisher', error);\n });\n }\n\n return Promise.resolve();\n }\n\n private getStreamForDestinationRole(destinationRole: SessionClientRole): Promise<Nullable<MediaStream>> {\n this.serviceInitGuard();\n\n if (this._publishers.has(destinationRole)) {\n trace.info(`Publisher for ${destinationRole} already exists`);\n\n return Promise.resolve(this._publishers.get(destinationRole)!.mediaStream);\n }\n\n if (!this._publisherPromises.get(destinationRole)) {\n trace.info(`Creating publisher for ${destinationRole}`);\n const publisherPromise = this.initLocalMediaStreams()\n .then(() => this.createMediaPublisher(destinationRole))\n .then((mediaPublisher: Nullable<TechseeMediaPublisher>) => {\n if (mediaPublisher) {\n this._publishers.set(destinationRole, mediaPublisher);\n }\n\n this._publisherPromises.delete(destinationRole);\n\n return mediaPublisher ? mediaPublisher.mediaStream : null;\n })\n .catch((ex: any) => {\n if (ex && ex.message === 'audioStreamFailed') {\n this._isVoipEnabled = false;\n\n return null;\n }\n\n this._publisherPromises.delete(destinationRole);\n throw ex;\n });\n\n this._publisherPromises.set(destinationRole, publisherPromise);\n } else {\n trace.info(`Create publisher promise for ${destinationRole} already exists`);\n }\n\n return this._publisherPromises.get(destinationRole)!;\n }\n\n private updateSubscribersWithNewStream(registeredStream: TechseeMediaStream): Promise<void> {\n trace.info(`Updating subscribers of ${registeredStream.streamType} with new stream`);\n\n if (this._subscribers.size === 0) {\n trace.warn(`No subscribers exists for the ${registeredStream.streamType}.`);\n } else {\n trace.info(`Total ${this._subscribers.size} subscribers exists, will check if rerender needed.`);\n }\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === registeredStream.streamType) {\n trace.info(`${registeredStream.streamType} rendering on subscriber`);\n subscriber.renderStream(registeredStream);\n }\n });\n\n return Promise.resolve();\n }\n\n private bindClassMethods(): void {\n this.updateSessionCredentials = this.updateSessionCredentials.bind(this);\n this.getStreamForDestinationRole = this.getStreamForDestinationRole.bind(this);\n this.registerRemoteMediaTrack = this.registerRemoteMediaTrack.bind(this);\n this.unregisterRemoteMediaTrack = this.unregisterRemoteMediaTrack.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.updateSubscribersWithNewStream = this.updateSubscribersWithNewStream.bind(this);\n this.initLocalMediaStreams = this.initLocalMediaStreams.bind(this);\n this.onSessionDisconnectHandler = this.onSessionDisconnectHandler.bind(this);\n this.clearService = this.clearService.bind(this);\n this.clearRegisteredStreams = this.clearRegisteredStreams.bind(this);\n this.clearPublishers = this.clearPublishers.bind(this);\n this.clearSubscribers = this.clearSubscribers.bind(this);\n this._connectToSession = this._connectToSession.bind(this);\n this.disconnectFromMediaSession = this.disconnectFromMediaSession.bind(this);\n }\n\n //region Cleanup\n\n private clearRegisteredStreams(): Promise<void> {\n trace.info('Clearing registered streams');\n const promises: any = [];\n\n this._registeredStreams.forEach((streamToUnregister: TechseeMediaStream) => {\n promises.push(this.unregisterTechseeMediaStream(streamToUnregister, MediaStreamUnregisterReason.ServiceCleanUp));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearSubscribers(): Promise<void> {\n trace.info('Clearing subscribers');\n const promises: any = [];\n\n this._subscribers.forEach((subscriber) => {\n promises.push(this.destroySubscriber(subscriber.container));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearPublishers(): Promise<void> {\n trace.info('Clearing publishers');\n const promises: any = [];\n\n this._publishers.forEach((publisher) => {\n promises.push(this.removePublisher(publisher));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n //#endregion Cleanup\n\n //#region Simple Validation Methods\n\n protected serviceInitGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._serviceOptions, 'Media service is not initialized', shouldThrow);\n }\n\n protected sessionExistsGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._session, 'There no active session', shouldThrow);\n }\n\n //#endregion\n\n //#endregion Private Methods\n}\n"]}
@@ -152,9 +152,10 @@ var TechseeMediaServiceBase = /** @class */function () {
152
152
  */
153
153
  TechseeMediaServiceBase.prototype.initLocalMediaStreams = function () {
154
154
  this.serviceInitGuard();
155
+ var isDashboardOpentok = this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK && (this._serviceOptions.clientRole === MediaConstants_1.SessionClientRole.AGENT || this._serviceOptions.clientRole === MediaConstants_1.SessionClientRole.OBSERVER);
155
156
  // This hack for IOS 14 force a recreation of stream on seemingly unnecessary occasions (like agent page refresh).
156
157
  // we're not sure why but the stream breaks
157
- if (!this._initLocalStreamsPromise || this._isIOS_14_orLater) {
158
+ if (!this._initLocalStreamsPromise || this._isIOS_14_orLater && !isDashboardOpentok) {
158
159
  trace.info('initLocalMediaStreams creating media streams');
159
160
  this._initLocalStreamsPromise = this.getLocalMediaImplementation().catch(function (mediaRequestFailResult) {
160
161
  trace.error("initLocalMediaStreams failure: " + mediaRequestFailResult);
@@ -267,13 +268,18 @@ var TechseeMediaServiceBase = /** @class */function () {
267
268
  TechseeMediaServiceBase.prototype.registerStreamResult = function (constraints, streamResult, switchCamera, addStreamType) {
268
269
  var _this = this;
269
270
  var regPromises = [];
271
+ var isOpentok = this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK;
270
272
  if (streamResult.isNew) {
271
273
  trace.info('registerStreamResult: stream result from getUserMediaStream:', streamResult.mediaStream);
272
274
  streamResult.mediaStream.getTracks().forEach(function (mediaTrack) {
273
275
  trace.info('registerStreamResult: stream result from mediaTrack:', mediaTrack);
274
276
  var streamType = addStreamType || (mediaTrack.kind === 'video' ? MediaConstants_1.KnownMediaStream.USER_VIDEO_STREAM : MediaConstants_1.KnownMediaStream.USER_AUDIO_STREAM);
275
277
  var newDedicatedStream = new TechseeMediaStream_1.TechseeMediaStream(mediaTrack, streamType, false);
276
- regPromises.push(switchCamera ? _this.registerTrack(newDedicatedStream) : _this.registerLocalMediaStream(newDedicatedStream));
278
+ if (isOpentok) {
279
+ regPromises.push(_this.registerStream(newDedicatedStream));
280
+ } else {
281
+ regPromises.push(switchCamera ? _this.registerTrack(newDedicatedStream) : _this.registerLocalMediaStream(newDedicatedStream));
282
+ }
277
283
  });
278
284
  }
279
285
  return Promise.all(regPromises);
@@ -1 +1 @@
1
- {"version":3,"sources":["MediaServiceBase.js","../src/MediaServiceBase.ts"],"names":["__assign","Object","assign","t","s","i","n","arguments","length","p","prototype","hasOwnProperty","call","apply","__importDefault","mod","__esModule","defineProperty","exports","value","TechseeMediaServiceBase","filter_1","require","cloneDeep_1","values_1","debounce_1","includes_1","events_1","MediaSubscriber_1","LocalStreamManager_1","TechseeMediaStream_1","SessionTurn_1","MediaDomUtils_1","Compatibility_1","SessionOpentok_1","MediaConstants_1","guards_1","MediaServer_1","MediaTracer_1","MediaSessionBase_1","trace","getMediaTracer","traceStatsInfo","default","info","leading","maxWait","privateEvents","environment","webRtcSupportInfo","_subscribers","Map","_publishers","_publisherPromises","_serviceOptions","_isIOS_13_orLater","_isIOS_14_orLater","_autoReconnectEnabled","_initLocalStreamsPromise","_initServicePromise","_registeredStreams","_isVoipEnabled","_session","bindClassMethods","_emitter","EventEmitter","_environment","_deviceSupportFlags","videoPlayback","hasCamera","hasMicrophone","_localStreamsManager","LocalStreamManager","isIOS","majorVersion","_sessionStreamsManager","getMediaStreamForRole","getStreamForDestinationRole","addRemoteMediaTrack","registerRemoteMediaTrack","removeMediaTrack","unregisterRemoteMediaTrack","get","serviceInitGuard","enumerable","configurable","initMediaService","serviceOptions","_this","warnMessage","console","warn","Promise","resolve","all","isVideoPlaySupportedOnDevice","mediaServiceType","then","isSupported","enumerateMediaDevices","catch","error","localDevicesList","groupedDevices","video","device","kind","toLowerCase","audio","setGroupedDevices","undefined","initLocalMediaStreams","getLocalMediaImplementation","mediaRequestFailResult","createSubscriber","subscriberParams","has","container","reject","subscriber","TechseeMediaSubscriber","set","streamForSubscriber","getRegisteredStreamByType","streamType","renderStream","destroySubscriber","dispose","delete","onStreamDestroyed","callback","registerEventCallback","STREAM_DESTROYED","onStreamCreated","STREAM_CREATED","onRecordStarted","instance","recordingEvents","RECORD_STARTED","onRecordStopped","RECORD_STOPPED","onReconnecting","RECONNECTING","getSwitchCameraConstraints","constraints","window","latestLocalMediaConstraints","Error","videoSourceType","LocalVideoSourceType","CAMERA","CAMERA_FRONT","switchCamera","revertCameraWhenFailed","destroyUserMediaStream","getUserMediaStream","streamResult","stream","mediaStream","replaceStreamTracks","registerStreamResult","err","addStreamType","regPromises","isNew","getTracks","forEach","mediaTrack","KnownMediaStream","USER_VIDEO_STREAM","USER_AUDIO_STREAM","newDedicatedStream","TechseeMediaStream","push","registerTrack","registerLocalMediaStream","disconnectFromMediaSession","sessionDisconnect","camerasCount","_connectToSession","sessionParams","params","connectPromise","doAsyncReject","setTimeout","sessionHandlers","onDisconnectedHandler","onSessionDisconnectHandler","mediaSessionParams","peerConnectivityTimeoutSeconds","DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS","allowedValues","MediaServiceType","TURNSERVER","TurnWebRtcSession","OPENTOK","OpentokSession","MEDIASERVER","MediaServerSession","connect","connectToSession","disconnectFromSession","disconnectFromSessionInternal","MediaSessionDisconnectReason","ForcedByConsumer","updateSessionCredentials","credentials","sessionExistsGuard","enableVoipDuringSession","isLocalStreamInitialized","reason","lastSession","disconnect","clearPublishers","getStatsForRemoteTrack","streamForStats","getRemoteTrackStats","trackStats","JSON","stringify","trackId","id","lastParams_1","reconnectReasons","InitiatorPeerReconnected","SignalingChannelDisconnect","PeerConnectionInterrupted","PeerConnectionStateChangeTimeout","reconnectToSession","setAutoReconnect","state","isSessionActive","lastParams","getSnapshotFromKnownStream","sourceStream","snapshotOptions","USER_SCREEN_SHARE_STREAM","snapshotStream","getSnapshotFromMediaStream","imageData","urlComponents","split","mimeType","bytes","atob","buffer","ArrayBuffer","rawData","Uint8Array","charCodeAt","blob","Blob","type","objectUrl","URL","createObjectURL","result","base64img","imageBlob","clearService","clearSubscribers","clearRegisteredStreams","clearAllStreams","removeAllListeners","tsMediaStream","registerStream","changeEnableForKnownStream","isPaused","streamToChangeState","enabled","isRemote","streamKind","KnownMediaStreamKind","Audio","subscribers","muteSound","event","on","unregisterEventCallback","off","emitEvent","eventArgs","emit","remoteMediaTrack","trackType","currentStream","onended","unregisterTechseeMediaStream","MediaStreamUnregisterReason","NativeEvent","updateSubscribersWithNewStream","ReplacingStream","streamToUnregister","traceError","promises","stopRendering","publisher","streamTypes","find","onMediaStreamDestroyed","destinationRole","registeredStream","ClosedRemotely","removePublisher","publisherPromise","createMediaPublisher","mediaPublisher","ex","message","size","bind","ServiceCleanUp","shouldThrow","throwableGuard"],"mappings":"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","file":"MediaServiceBase.js","sourcesContent":["\"use strict\";\nvar __assign = (this && this.__assign) || function () {\n __assign = Object.assign || function(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\n t[p] = s[p];\n }\n return t;\n };\n return __assign.apply(this, arguments);\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TechseeMediaServiceBase = void 0;\nvar filter_1 = __importDefault(require(\"lodash/filter\"));\nvar cloneDeep_1 = __importDefault(require(\"lodash/cloneDeep\"));\nvar values_1 = __importDefault(require(\"lodash/values\"));\nvar debounce_1 = __importDefault(require(\"lodash/debounce\"));\nvar includes_1 = __importDefault(require(\"lodash/includes\"));\nvar events_1 = require(\"events\");\nvar MediaSubscriber_1 = require(\"./MediaSubscriber\");\nvar LocalStreamManager_1 = require(\"./LocalStreamManager\");\nvar TechseeMediaStream_1 = require(\"./TechseeMediaStream\");\nvar SessionTurn_1 = require(\"./MediaSession/SessionTurn\");\nvar MediaDomUtils_1 = require(\"./MediaUtils/MediaDomUtils\");\nvar Compatibility_1 = require(\"./MediaUtils/Compatibility\");\nvar SessionOpentok_1 = require(\"./MediaSession/SessionOpentok\");\nvar MediaConstants_1 = require(\"./MediaConstants\");\nvar guards_1 = require(\"@techsee/techsee-common/lib/core/guards\");\nvar MediaServer_1 = require(\"./MediaSession/MediaServer\");\nvar MediaTracer_1 = require(\"./MediaUtils/MediaTracer\");\nvar MediaSessionBase_1 = require(\"./MediaSession/MediaSessionBase\");\nvar trace = MediaTracer_1.getMediaTracer('MediaServiceBase');\nvar traceStatsInfo = debounce_1.default(trace.info, 1000 * 5, { leading: true, maxWait: 1000 * 30 });\nvar privateEvents;\n(function (privateEvents) {\n privateEvents[\"STREAM_CREATED\"] = \"STREAM_CREATED\";\n privateEvents[\"STREAM_DESTROYED\"] = \"STREAM_DESTROYED\";\n})(privateEvents || (privateEvents = {}));\nvar TechseeMediaServiceBase = /** @class */ (function () {\n function TechseeMediaServiceBase(environment, webRtcSupportInfo) {\n this._subscribers = new Map();\n this._publishers = new Map();\n this._publisherPromises = new Map();\n this._serviceOptions = null;\n this._isIOS_13_orLater = false;\n this._isIOS_14_orLater = false;\n this._autoReconnectEnabled = true;\n this._initLocalStreamsPromise = null;\n this._initServicePromise = null;\n this._registeredStreams = new Map();\n this._isVoipEnabled = false;\n this._session = null;\n trace.info('TechseeMediaServiceBase created');\n this.bindClassMethods();\n this._emitter = new events_1.EventEmitter();\n this._environment = environment;\n this._deviceSupportFlags = {\n videoPlayback: false,\n hasCamera: false,\n hasMicrophone: false,\n webRtcSupportInfo: webRtcSupportInfo\n };\n this._localStreamsManager = new LocalStreamManager_1.LocalStreamManager(this._environment);\n // TODO: Hack as IOS 13.3 and above sometimes do not return the video/audio device. This\n // is a hack to work around the issue until Apple resolve it\n this._isIOS_13_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 13));\n this._isIOS_14_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 14));\n trace.info(\"TechseeMediaServiceBase: isIOS_13_orLater: \" + this._isIOS_13_orLater);\n trace.info(\"TechseeMediaServiceBase: isIOS_14_orLater: \" + this._isIOS_14_orLater);\n this._sessionStreamsManager = {\n getMediaStreamForRole: this.getStreamForDestinationRole,\n addRemoteMediaTrack: this.registerRemoteMediaTrack,\n removeMediaTrack: this.unregisterRemoteMediaTrack\n };\n }\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"deviceSupportInfo\", {\n get: function () {\n this.serviceInitGuard(false);\n return cloneDeep_1.default(this._deviceSupportFlags);\n },\n enumerable: false,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isVoipEnabled\", {\n get: function () {\n return this._isVoipEnabled;\n },\n enumerable: false,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isSessionActive\", {\n get: function () {\n return this._session !== null;\n },\n enumerable: false,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isLocalStreamInitialized\", {\n get: function () {\n return this._initLocalStreamsPromise !== null;\n },\n enumerable: false,\n configurable: true\n });\n /*\n Initializes media service. Before using anything from this service it should be initialized first.\n All of serviceOptions should be retrieved from account settings and by evaluating application state.\n For example, when initializing service, accountSettings is already loaded, and we know if FS session is audio enabled.\n */\n TechseeMediaServiceBase.prototype.initMediaService = function (serviceOptions) {\n var _this = this;\n trace.info('TechseeMediaServiceBase.initMediaService', serviceOptions);\n var warnMessage = 'Multiple initialization does not supposed to happen. Not rejecting, but check the flow for correctness.';\n if (this._initServicePromise || this._serviceOptions) {\n console.warn(warnMessage);\n return this._initServicePromise ? this._initServicePromise : Promise.resolve();\n }\n this._serviceOptions = cloneDeep_1.default(serviceOptions);\n this._initServicePromise = Promise.all([\n Compatibility_1.isVideoPlaySupportedOnDevice(this._serviceOptions.mediaServiceType)\n .then(function (isSupported) { return _this._deviceSupportFlags.videoPlayback = isSupported; }),\n (this._isIOS_14_orLater ? Promise.resolve([]) : Compatibility_1.enumerateMediaDevices())\n .catch(function (error) {\n trace.warn(error);\n return [];\n })\n .then(function (localDevicesList) {\n var groupedDevices = {\n video: filter_1.default(localDevicesList, function (device) { return device.kind.toLowerCase() === 'videoinput'; }),\n audio: filter_1.default(localDevicesList, function (device) { return device.kind.toLowerCase() === 'audioinput'; })\n };\n _this._localStreamsManager.setGroupedDevices(groupedDevices);\n _this._deviceSupportFlags.hasCamera = _this._isIOS_13_orLater || (groupedDevices.video.length > 0);\n _this._deviceSupportFlags.hasMicrophone = _this._isIOS_13_orLater || (groupedDevices.audio.length > 0);\n })\n ]).then(function () { return undefined; });\n return this._initServicePromise;\n };\n //#region Media Streams Management\n /*\n The main method to start the initialization of local media streams.\n This method can be executed many times and it will insure all calls are synced and only relevant stream will be created.\n DO NOT USE ANY OTHER METHOD THAT FORCES \"navigator.getUserMedia\" OTHER THAN THIS ONE.\n */\n TechseeMediaServiceBase.prototype.initLocalMediaStreams = function () {\n this.serviceInitGuard();\n // This hack for IOS 14 force a recreation of stream on seemingly unnecessary occasions (like agent page refresh).\n // we're not sure why but the stream breaks\n if (!this._initLocalStreamsPromise || this._isIOS_14_orLater) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch(function (mediaRequestFailResult) {\n trace.error(\"initLocalMediaStreams failure: \" + mediaRequestFailResult);\n throw mediaRequestFailResult;\n });\n }\n else {\n trace.info('initLocalMediaStreams already done or in progress');\n }\n return this._initLocalStreamsPromise;\n };\n /*\n Creates an instance of subscriber that will \"listen\" to the requested stream.\n Whenever requested stream is available, subscriber will render it.\n */\n TechseeMediaServiceBase.prototype.createSubscriber = function (subscriberParams) {\n if (this._subscribers.has(subscriberParams.container)) {\n return Promise.reject('Subscriber for provided DIV element already exists');\n }\n var subscriber = new MediaSubscriber_1.TechseeMediaSubscriber(subscriberParams);\n this._subscribers.set(subscriberParams.container, subscriber);\n var streamForSubscriber = this.getRegisteredStreamByType(subscriber.streamType);\n if (streamForSubscriber) {\n trace.info(\"Stream for \" + subscriberParams.streamType + \" is ready.\");\n subscriber.renderStream(streamForSubscriber);\n }\n else {\n trace.info(\"Stream for \" + subscriberParams.streamType + \" not created yet\");\n }\n return Promise.resolve(subscriber);\n };\n /*\n Destroys the subscriber for provided HTML DIV container\n */\n TechseeMediaServiceBase.prototype.destroySubscriber = function (container) {\n if (this._subscribers.has(container)) {\n var subscriber = this._subscribers.get(container);\n subscriber.dispose();\n }\n this._subscribers.delete(container);\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.onStreamDestroyed = function (callback) {\n this.registerEventCallback(privateEvents.STREAM_DESTROYED, callback);\n };\n TechseeMediaServiceBase.prototype.onStreamCreated = function (callback) {\n this.registerEventCallback(privateEvents.STREAM_CREATED, callback);\n };\n TechseeMediaServiceBase.prototype.onRecordStarted = function (callback) {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(MediaSessionBase_1.recordingEvents.RECORD_STARTED, callback);\n }\n };\n TechseeMediaServiceBase.prototype.onRecordStopped = function (callback) {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(MediaSessionBase_1.recordingEvents.RECORD_STOPPED, callback);\n }\n };\n TechseeMediaServiceBase.prototype.onReconnecting = function (callback) {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(MediaSessionBase_1.recordingEvents.RECONNECTING, callback);\n }\n };\n TechseeMediaServiceBase.prototype.getSwitchCameraConstraints = function () {\n var constraints = cloneDeep_1.default(window.latestLocalMediaConstraints);\n if (!constraints) {\n throw new Error('getSwitchCameraConstraints: unexpected use case constraints is null.');\n }\n trace.info('getSwitchCameraConstraints - Start switch camera with constraints:', constraints);\n var videoSourceType = constraints.video.videoSourceType === MediaConstants_1.LocalVideoSourceType.CAMERA\n ? MediaConstants_1.LocalVideoSourceType.CAMERA_FRONT\n : MediaConstants_1.LocalVideoSourceType.CAMERA;\n trace.info(\"getSwitchCameraConstraints - switch to videoSourceType: \" + videoSourceType);\n constraints.video.videoSourceType = videoSourceType;\n return constraints;\n };\n TechseeMediaServiceBase.prototype.switchCamera = function (revertCameraWhenFailed) {\n var _this = this;\n trace.info('switchCamera: start');\n var latestLocalMediaConstraints = cloneDeep_1.default(window.latestLocalMediaConstraints);\n var constraints = !revertCameraWhenFailed ? this.getSwitchCameraConstraints() : latestLocalMediaConstraints;\n if (!constraints) {\n throw new Error('switchCamera - unexpected use case constraints is null.');\n }\n return this._localStreamsManager.destroyUserMediaStream()\n .then(function () { return _this._localStreamsManager.getUserMediaStream(constraints)\n .then(function (streamResult) {\n var stream = streamResult.mediaStream;\n trace.info('switchCamera: new stream: ', stream);\n return _this.replaceStreamTracks(stream)\n .then(function () { return _this.registerStreamResult(constraints, streamResult, true)\n .then(function () { return ({\n revertCameraWhenFailed: revertCameraWhenFailed,\n constraints: constraints,\n streamResult: streamResult\n }); }); });\n }); })\n .catch(function (err) {\n trace.error('switchCamera: Failed to switch camera: ', err);\n if (!revertCameraWhenFailed) {\n window.latestLocalMediaConstraints = latestLocalMediaConstraints;\n return _this.switchCamera(true);\n }\n return Promise.reject('Failed to switch camera.');\n });\n };\n TechseeMediaServiceBase.prototype.replaceStreamTracks = function (mediaStream) {\n if (this._session.instance) {\n return this._session.instance.replaceStreamTracks(mediaStream);\n }\n return Promise.reject('replaceStreamTracks - session instance is not exists.');\n };\n TechseeMediaServiceBase.prototype.registerStreamResult = function (constraints, streamResult, switchCamera, addStreamType) {\n var _this = this;\n var regPromises = [];\n if (streamResult.isNew) {\n trace.info('registerStreamResult: stream result from getUserMediaStream:', streamResult.mediaStream);\n streamResult.mediaStream.getTracks().forEach(function (mediaTrack) {\n trace.info('registerStreamResult: stream result from mediaTrack:', mediaTrack);\n var streamType = addStreamType || (mediaTrack.kind === 'video'\n ? MediaConstants_1.KnownMediaStream.USER_VIDEO_STREAM : MediaConstants_1.KnownMediaStream.USER_AUDIO_STREAM);\n var newDedicatedStream = new TechseeMediaStream_1.TechseeMediaStream(mediaTrack, streamType, false);\n regPromises.push(switchCamera ? _this.registerTrack(newDedicatedStream) : _this.registerLocalMediaStream(newDedicatedStream));\n });\n }\n return Promise.all(regPromises);\n };\n TechseeMediaServiceBase.prototype.disconnectFromMediaSession = function () {\n if (this._session && this._session.instance) {\n return this._session.instance.sessionDisconnect();\n }\n };\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"supportSwitchCameras\", {\n get: function () {\n return this._isIOS_13_orLater || this._localStreamsManager.groupedDevices.camerasCount > 1;\n },\n enumerable: false,\n configurable: true\n });\n TechseeMediaServiceBase.prototype._connectToSession = function (sessionParams) {\n var _this = this;\n this.serviceInitGuard();\n if (!this._session) {\n this._session = { params: cloneDeep_1.default(sessionParams) };\n this._session.connectPromise = new Promise(function (resolve, reject) {\n var doAsyncReject = function (error) {\n /*\n Reject with timeout, to change JS 'execution flow'.\n If timeout removed, session is assigned to null, before promise returns, and \"catch\" will not\n work on promise that is returned.\n */\n setTimeout(function () {\n _this._session = null;\n reject(error);\n });\n };\n var sessionHandlers = {\n onDisconnectedHandler: _this.onSessionDisconnectHandler\n };\n var mediaSessionParams = __assign(__assign({}, sessionParams), { peerConnectivityTimeoutSeconds: _this._serviceOptions.peerConnectivityTimeoutSeconds || MediaConstants_1.DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS });\n trace.info('connectToSession', sessionParams);\n var allowedValues = values_1.default(MediaConstants_1.MediaServiceType);\n switch (_this._serviceOptions.mediaServiceType) {\n case MediaConstants_1.MediaServiceType.TURNSERVER:\n _this._session.instance = new SessionTurn_1.TurnWebRtcSession(mediaSessionParams, sessionHandlers, _this._sessionStreamsManager);\n break;\n case MediaConstants_1.MediaServiceType.OPENTOK:\n _this._session.instance = new SessionOpentok_1.OpentokSession(mediaSessionParams, _this._sessionStreamsManager);\n break;\n case MediaConstants_1.MediaServiceType.MEDIASERVER:\n _this._session.instance = new MediaServer_1.MediaServerSession(mediaSessionParams, _this._sessionStreamsManager);\n break;\n default:\n trace.error(\"mediaServiceType '\" + _this._serviceOptions.mediaServiceType + \"' is not supported.\", { allowedValues: allowedValues });\n doAsyncReject(new Error('mediaServiceType is not supported'));\n }\n if (_this._session && _this._session.instance) {\n _this._session.instance.connect()\n .then(function () {\n trace.info('Session is connected');\n resolve();\n })\n .catch(function (error) {\n trace.error('Failed to connect to session', error);\n doAsyncReject(error);\n });\n }\n });\n }\n else {\n trace.warn('Session connected already. Disconnect before connect to new session again');\n }\n //TODO - Alex: Change a structure of this function to be more safe\n //(when we return, there should not be case when session or promise is null)\n //To fix, need to return promise immediately, then execute process in setTimeout(fn, 0);\n return this._session.connectPromise;\n };\n //#endregion Media Streams Management\n //#region Session Management\n /*\n Creates instance of WebRTC session which connects to (signaling server), and begins to listen to WebRTC events.\n */\n TechseeMediaServiceBase.prototype.connectToSession = function (sessionParams) {\n return this._connectToSession(sessionParams);\n };\n /*\n Disconnects from WebRTC session.\n */\n TechseeMediaServiceBase.prototype.disconnectFromSession = function () {\n return this.disconnectFromSessionInternal(MediaConstants_1.MediaSessionDisconnectReason.ForcedByConsumer);\n };\n /*\n Updates a credentials for turn server\n */\n TechseeMediaServiceBase.prototype.updateSessionCredentials = function (credentials) {\n trace.info('updateSessionCredentials');\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject('There no session to update credentials');\n }\n this._session.params.credentials = cloneDeep_1.default(credentials);\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.enableVoipDuringSession = function () {\n if (!this._isVoipEnabled && this.isLocalStreamInitialized) {\n this._initLocalStreamsPromise = null;\n }\n this._isVoipEnabled = true;\n };\n TechseeMediaServiceBase.prototype.disconnectFromSessionInternal = function (reason) {\n if (!this._session) {\n return Promise.resolve();\n }\n trace.info('Disconnecting from session', reason);\n var lastSession = this._session;\n this._session = null;\n return Promise.all([\n lastSession.instance.disconnect(),\n this.clearPublishers()\n ]).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.getStatsForRemoteTrack = function (streamType) {\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject(new Error('Media session not started'));\n }\n var streamForStats = this.getRegisteredStreamByType(streamType);\n if (!streamForStats) {\n return Promise.reject(new Error('Stream for requested type was not found'));\n }\n return this._session.instance.getRemoteTrackStats(streamForStats.mediaTrack)\n .then(function (trackStats) {\n traceStatsInfo(\"MediaTrackStats for \" + streamType + \": \" + JSON.stringify(trackStats));\n return {\n trackId: streamForStats.mediaTrack.id,\n trackStats: trackStats\n };\n });\n };\n TechseeMediaServiceBase.prototype.onSessionDisconnectHandler = function (reason) {\n var _this = this;\n trace.info('onSessionDisconnectHandler', reason);\n if (reason !== MediaConstants_1.MediaSessionDisconnectReason.ForcedByConsumer) {\n var lastParams_1 = this._session && this._session.params ? this._session.params : null;\n this.disconnectFromSessionInternal(reason).then(function () {\n var reconnectReasons = [\n MediaConstants_1.MediaSessionDisconnectReason.InitiatorPeerReconnected,\n MediaConstants_1.MediaSessionDisconnectReason.SignalingChannelDisconnect,\n MediaConstants_1.MediaSessionDisconnectReason.PeerConnectionInterrupted,\n MediaConstants_1.MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout\n ];\n if (lastParams_1 && includes_1.default(reconnectReasons, reason) && _this._autoReconnectEnabled) {\n _this.reconnectToSession(lastParams_1);\n }\n else if (!_this._autoReconnectEnabled) {\n trace.info('No reconnection- auto reconnect disabled');\n }\n else {\n trace.info('No params for reconnection to media session');\n }\n });\n }\n else {\n trace.info('Ignore Session disconnect event');\n }\n };\n TechseeMediaServiceBase.prototype.setAutoReconnect = function (state) {\n this._autoReconnectEnabled = state;\n trace.info('_autoReconnectEnabled:', state);\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n var lastParams = this._session && this._session.params ? this._session.params : null;\n if (lastParams) {\n trace.info('setAutoReconnect - reconnect to session');\n this.reconnectToSession(lastParams);\n }\n }\n };\n TechseeMediaServiceBase.prototype.reconnectToSession = function (lastParams) {\n trace.info('Reconnecting to media session, sessionParams:', lastParams);\n this._connectToSession(lastParams)\n .then(function () {\n trace.info('Media session reconnected');\n })\n .catch(function (error) {\n trace.error('Error while reconnecting to media session', error);\n });\n };\n //#endregion Session Management\n //#region Utils\n TechseeMediaServiceBase.prototype.getSnapshotFromKnownStream = function (sourceStream, snapshotOptions) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n if (sourceStream !== MediaConstants_1.KnownMediaStream.USER_VIDEO_STREAM &&\n sourceStream !== MediaConstants_1.KnownMediaStream.USER_SCREEN_SHARE_STREAM) {\n trace.error('The requested stream is not video stream, and cannot be used for snapshot');\n reject(new Error('INCOMPATIBLE_STREAM_FOR_SNAPSHOT'));\n return;\n }\n var snapshotStream = _this.getRegisteredStreamByType(sourceStream);\n if (!snapshotStream) {\n trace.error('Cannot make snapshot: The requested stream not exists yet.');\n reject(new Error('NO_REQUESTED_STREAM'));\n return;\n }\n MediaDomUtils_1.getSnapshotFromMediaStream(snapshotStream.mediaStream, snapshotOptions)\n .then(function (imageData) {\n var urlComponents = imageData.split(';base64,');\n var mimeType = urlComponents[0].split(':')[1];\n var bytes = atob(urlComponents[1]);\n var buffer = new ArrayBuffer(bytes.length);\n var rawData = new Uint8Array(buffer);\n for (var i = 0; i < bytes.length; i++) {\n rawData[i] = bytes.charCodeAt(i);\n }\n var blob = new Blob([rawData], { type: mimeType });\n var objectUrl = window.URL.createObjectURL(blob);\n trace.info('Snapshot created successfully');\n var result = {\n base64img: imageData,\n objectUrl: objectUrl,\n imageBlob: blob,\n mimeType: mimeType\n };\n resolve(result);\n })\n .catch(function (error) {\n trace.error('Error creating snapshot', error);\n reject(error);\n });\n });\n };\n //Will clean streams, publishers and subscribers, but will not remove event listeners\n TechseeMediaServiceBase.prototype.clearService = function () {\n var _this = this;\n trace.info('MediaService clearing all resources');\n return this.disconnectFromSessionInternal(MediaConstants_1.MediaSessionDisconnectReason.ForcedByConsumer)\n .then(this.clearSubscribers)\n .then(this.clearRegisteredStreams)\n .then(this._localStreamsManager.clearAllStreams)\n .then(function () {\n _this._initLocalStreamsPromise = null;\n _this._isVoipEnabled = false;\n }).then(function () { return undefined; });\n };\n //Will clear the service and remove all event listeners\n TechseeMediaServiceBase.prototype.dispose = function () {\n var _this = this;\n return this.clearService().catch(function () { return undefined; }).then(function () {\n _this._emitter.removeAllListeners();\n });\n };\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"subscribers\", {\n //#endregion\n //#region Protected Methods\n get: function () {\n return this._subscribers;\n },\n enumerable: false,\n configurable: true\n });\n TechseeMediaServiceBase.prototype.registerLocalMediaStream = function (tsMediaStream) {\n trace.info('Registering local stream', tsMediaStream.streamType);\n if (this._registeredStreams.has(tsMediaStream.streamType)) {\n return Promise.reject(new Error(\"Stream \" + tsMediaStream.streamType + \" already registered\"));\n }\n return this.registerStream(tsMediaStream);\n };\n TechseeMediaServiceBase.prototype.getRegisteredStreamByType = function (streamType) {\n return this._registeredStreams.get(streamType) || null;\n };\n TechseeMediaServiceBase.prototype.changeEnableForKnownStream = function (streamType, isPaused) {\n var streamToChangeState = this.getRegisteredStreamByType(streamType);\n if (!streamToChangeState) {\n trace.warn('There no stream found to change enable state', streamType);\n }\n else {\n streamToChangeState.mediaTrack.enabled = !isPaused;\n if (streamToChangeState.isRemote && streamToChangeState.streamKind === MediaConstants_1.KnownMediaStreamKind.Audio) {\n this.subscribers.forEach(function (subscriber) {\n if (subscriber.streamType === streamType) {\n subscriber.muteSound(isPaused);\n }\n });\n }\n trace.info('Local stream enable state is changed', { streamType: streamType, isPaused: isPaused });\n }\n };\n TechseeMediaServiceBase.prototype.registerEventCallback = function (event, callback) {\n this._emitter.on(event, callback);\n };\n TechseeMediaServiceBase.prototype.unregisterEventCallback = function (event, callback) {\n this._emitter.off(event, callback);\n };\n TechseeMediaServiceBase.prototype.emitEvent = function (event, eventArgs) {\n var _this = this;\n setTimeout(function () {\n _this._emitter.emit(event, eventArgs);\n });\n };\n //#endregion Protected Methods\n //#region Private Methods\n TechseeMediaServiceBase.prototype.registerRemoteMediaTrack = function (remoteMediaTrack) {\n trace.info(\"Registering remote \" + remoteMediaTrack.trackType + \" MediaStreamTrack\", remoteMediaTrack.mediaTrack);\n var currentStream = this._registeredStreams.get(remoteMediaTrack.trackType);\n if (currentStream && !currentStream.isRemote) {\n return Promise.reject(new Error('Cannot register remote stream with the same type as local stream'));\n }\n var newDedicatedStream = new TechseeMediaStream_1.TechseeMediaStream(remoteMediaTrack.mediaTrack, remoteMediaTrack.trackType, true);\n return this.registerStream(newDedicatedStream);\n };\n TechseeMediaServiceBase.prototype.registerTrack = function (mediaStream) {\n var _this = this;\n mediaStream.mediaTrack.onended = function () {\n return _this.unregisterTechseeMediaStream(mediaStream, MediaConstants_1.MediaStreamUnregisterReason.NativeEvent);\n };\n this._registeredStreams.set(mediaStream.streamType, mediaStream);\n trace.info(\"TechseeMediaStream registered - \" + mediaStream.streamType, TechseeMediaStream_1.TechseeMediaStream);\n return this.updateSubscribersWithNewStream(mediaStream).then(function () {\n var eventArgs = { streamType: mediaStream.streamType };\n _this.emitEvent(privateEvents.STREAM_CREATED, eventArgs);\n });\n };\n TechseeMediaServiceBase.prototype.registerStream = function (mediaStream) {\n var _this = this;\n var currentStream = this._registeredStreams.get(mediaStream.streamType);\n if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaConstants_1.MediaStreamUnregisterReason.ReplacingStream).then(function () { return _this.registerTrack(mediaStream); });\n }\n return this.registerTrack(mediaStream);\n };\n TechseeMediaServiceBase.prototype.unregisterTechseeMediaStream = function (streamToUnregister, reason) {\n var _this = this;\n var traceError = function (error) {\n trace.warn('Unregister TechseeMediaStream error:', error);\n };\n var promises = [];\n trace.info('Unregister TechseeMediaStream: ', streamToUnregister.streamType);\n this._subscribers.forEach(function (subscriber) {\n if (subscriber.streamType === streamToUnregister.streamType) {\n promises.push(subscriber.stopRendering().catch(traceError));\n }\n });\n if (!streamToUnregister.isRemote) {\n this._publishers.forEach(function (publisher) {\n if (publisher.streamTypes.find(function (streamType) { return streamType === streamToUnregister.streamType; })) {\n if (_this._session && _this._session.instance) {\n promises.push(_this._session.instance.onMediaStreamDestroyed(publisher.destinationRole).catch(traceError));\n }\n }\n });\n }\n return Promise.all(promises)\n .then(function () {\n _this._registeredStreams.delete(streamToUnregister.streamType);\n trace.info('Stream deleted', streamToUnregister.streamType);\n var eventArgs = { streamType: streamToUnregister.streamType, reason: reason };\n _this.emitEvent(privateEvents.STREAM_DESTROYED, eventArgs);\n });\n };\n TechseeMediaServiceBase.prototype.unregisterRemoteMediaTrack = function (mediaTrack) {\n var _this = this;\n var promises = [];\n this._registeredStreams.forEach(function (registeredStream) {\n if (registeredStream.mediaTrack.id === mediaTrack.id) {\n promises.push(_this.unregisterTechseeMediaStream(registeredStream, MediaConstants_1.MediaStreamUnregisterReason.ClosedRemotely));\n }\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.removePublisher = function (publisher) {\n this._publishers.delete(publisher.destinationRole);\n if (this._session && this._session.instance) {\n trace.info('Removing publisher', publisher.destinationRole);\n return this._session.instance.onMediaStreamDestroyed(publisher.destinationRole)\n .catch(function (error) {\n trace.warn('Error while removing publisher', error);\n });\n }\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.getStreamForDestinationRole = function (destinationRole) {\n var _this = this;\n this.serviceInitGuard();\n if (this._publishers.has(destinationRole)) {\n trace.info(\"Publisher for \" + destinationRole + \" already exists\");\n return Promise.resolve(this._publishers.get(destinationRole).mediaStream);\n }\n if (!this._publisherPromises.get(destinationRole)) {\n trace.info(\"Creating publisher for \" + destinationRole);\n var publisherPromise = this.initLocalMediaStreams()\n .then(function () { return _this.createMediaPublisher(destinationRole); })\n .then(function (mediaPublisher) {\n if (mediaPublisher) {\n _this._publishers.set(destinationRole, mediaPublisher);\n }\n _this._publisherPromises.delete(destinationRole);\n return mediaPublisher ? mediaPublisher.mediaStream : null;\n })\n .catch(function (ex) {\n if (ex && ex.message === 'audioStreamFailed') {\n _this._isVoipEnabled = false;\n return null;\n }\n _this._publisherPromises.delete(destinationRole);\n throw ex;\n });\n this._publisherPromises.set(destinationRole, publisherPromise);\n }\n else {\n trace.info(\"Create publisher promise for \" + destinationRole + \" already exists\");\n }\n return this._publisherPromises.get(destinationRole);\n };\n TechseeMediaServiceBase.prototype.updateSubscribersWithNewStream = function (registeredStream) {\n trace.info(\"Updating subscribers of \" + registeredStream.streamType + \" with new stream\");\n if (this._subscribers.size === 0) {\n trace.warn(\"No subscribers exists for the \" + registeredStream.streamType + \".\");\n }\n else {\n trace.info(\"Total \" + this._subscribers.size + \" subscribers exists, will check if rerender needed.\");\n }\n this._subscribers.forEach(function (subscriber) {\n if (subscriber.streamType === registeredStream.streamType) {\n trace.info(registeredStream.streamType + \" rendering on subscriber\");\n subscriber.renderStream(registeredStream);\n }\n });\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.bindClassMethods = function () {\n this.updateSessionCredentials = this.updateSessionCredentials.bind(this);\n this.getStreamForDestinationRole = this.getStreamForDestinationRole.bind(this);\n this.registerRemoteMediaTrack = this.registerRemoteMediaTrack.bind(this);\n this.unregisterRemoteMediaTrack = this.unregisterRemoteMediaTrack.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.updateSubscribersWithNewStream = this.updateSubscribersWithNewStream.bind(this);\n this.initLocalMediaStreams = this.initLocalMediaStreams.bind(this);\n this.onSessionDisconnectHandler = this.onSessionDisconnectHandler.bind(this);\n this.clearService = this.clearService.bind(this);\n this.clearRegisteredStreams = this.clearRegisteredStreams.bind(this);\n this.clearPublishers = this.clearPublishers.bind(this);\n this.clearSubscribers = this.clearSubscribers.bind(this);\n this._connectToSession = this._connectToSession.bind(this);\n this.disconnectFromMediaSession = this.disconnectFromMediaSession.bind(this);\n };\n //region Cleanup\n TechseeMediaServiceBase.prototype.clearRegisteredStreams = function () {\n var _this = this;\n trace.info('Clearing registered streams');\n var promises = [];\n this._registeredStreams.forEach(function (streamToUnregister) {\n promises.push(_this.unregisterTechseeMediaStream(streamToUnregister, MediaConstants_1.MediaStreamUnregisterReason.ServiceCleanUp));\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.clearSubscribers = function () {\n var _this = this;\n trace.info('Clearing subscribers');\n var promises = [];\n this._subscribers.forEach(function (subscriber) {\n promises.push(_this.destroySubscriber(subscriber.container));\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.clearPublishers = function () {\n var _this = this;\n trace.info('Clearing publishers');\n var promises = [];\n this._publishers.forEach(function (publisher) {\n promises.push(_this.removePublisher(publisher));\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n //#endregion Cleanup\n //#region Simple Validation Methods\n TechseeMediaServiceBase.prototype.serviceInitGuard = function (shouldThrow) {\n if (shouldThrow === void 0) { shouldThrow = true; }\n return guards_1.throwableGuard(!!this._serviceOptions, 'Media service is not initialized', shouldThrow);\n };\n TechseeMediaServiceBase.prototype.sessionExistsGuard = function (shouldThrow) {\n if (shouldThrow === void 0) { shouldThrow = true; }\n return guards_1.throwableGuard(!!this._session, 'There no active session', shouldThrow);\n };\n return TechseeMediaServiceBase;\n}());\nexports.TechseeMediaServiceBase = TechseeMediaServiceBase;\n\n//# sourceMappingURL=MediaServiceBase.js.map\n","import filter from 'lodash/filter';\nimport cloneDeep from 'lodash/cloneDeep';\nimport values from 'lodash/values';\nimport debounce from 'lodash/debounce';\nimport includes from 'lodash/includes';\nimport {EventEmitter} from 'events';\nimport {Nullable} from '@techsee/techsee-common';\nimport {TechseeMediaSubscriber} from './MediaSubscriber';\nimport {TechseeMediaPublisher} from './MediaPublisher';\nimport {LocalStreamManager} from './LocalStreamManager';\nimport {TechseeMediaStream} from './TechseeMediaStream';\nimport {TurnWebRtcSession} from './MediaSession/SessionTurn';\nimport {getSnapshotFromMediaStream} from './MediaUtils/MediaDomUtils';\nimport {\n ClientWebRtcInfo,\n DeviceSupportInfo,\n IMediaEnvironment,\n IMediaSession,\n IMediaSubscriber,\n ISessionStreamsManager,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n MediaServiceOptions,\n MediaSessionConfiguration,\n MediaSessionHandlers,\n MediaSessionIceCredentials,\n MediaSessionParams,\n MediaSubscriberParams,\n RemoteMediaTrack,\n RemoteTrackStats,\n SnapshotOptions,\n SnapshotResult,\n StreamCreatedEventArgs,\n StreamDestroyedEventArgs\n} from './MediaContracts';\nimport {enumerateMediaDevices, isVideoPlaySupportedOnDevice} from './MediaUtils/Compatibility';\nimport {OpentokSession} from './MediaSession/SessionOpentok';\nimport {\n DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n LocalVideoSourceType,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {MediaServerSession} from './MediaSession/MediaServer';\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\nimport {recordingEvents} from './MediaSession/MediaSessionBase';\n\nconst trace = getMediaTracer('MediaServiceBase');\nconst traceStatsInfo = debounce(trace.info, 1000 * 5, {leading: true, maxWait: 1000 * 30});\n\nenum privateEvents {\n STREAM_CREATED = 'STREAM_CREATED',\n STREAM_DESTROYED = 'STREAM_DESTROYED'\n}\n\nexport abstract class TechseeMediaServiceBase {\n //This method should not be called. The only place that allowed to use it, is initLocalMediaStreams method.\n //Everyone who need to initialize MediaStream should use initLocalMediaStreams method.\n protected abstract getLocalMediaImplementation(): Promise<void>;\n\n protected abstract createMediaPublisher(destinationRole: SessionClientRole): Promise<Nullable<TechseeMediaPublisher>>;\n\n protected readonly _environment: IMediaEnvironment;\n protected readonly _sessionStreamsManager: ISessionStreamsManager;\n protected readonly _localStreamsManager: LocalStreamManager;\n\n private readonly _deviceSupportFlags: DeviceSupportInfo;\n private readonly _subscribers: Map<HTMLDivElement, TechseeMediaSubscriber> = new Map();\n private readonly _publishers: Map<SessionClientRole, TechseeMediaPublisher> = new Map();\n private readonly _publisherPromises: Map<SessionClientRole, Promise<Nullable<MediaStream>>> = new Map();\n protected _serviceOptions: Nullable<MediaServiceOptions> = null;\n private _isIOS_13_orLater = false;\n private _isIOS_14_orLater = false;\n private _autoReconnectEnabled = true;\n private readonly _emitter: EventEmitter;\n private _initLocalStreamsPromise: Nullable<Promise<void>> = null;\n private _initServicePromise: Nullable<Promise<void>> = null;\n private _registeredStreams: Map<KnownMediaStream, TechseeMediaStream> = new Map();\n private _isVoipEnabled = false;\n private _session: Nullable<{\n params: MediaSessionParams;\n connectPromise?: Promise<void>;\n instance?: IMediaSession;\n }> = null;\n\n protected constructor(environment: IMediaEnvironment, webRtcSupportInfo: ClientWebRtcInfo) {\n trace.info('TechseeMediaServiceBase created');\n\n this.bindClassMethods();\n\n this._emitter = new EventEmitter();\n this._environment = environment;\n this._deviceSupportFlags = {\n videoPlayback: false,\n hasCamera: false,\n hasMicrophone: false,\n webRtcSupportInfo: webRtcSupportInfo\n };\n\n this._localStreamsManager = new LocalStreamManager(this._environment);\n\n // TODO: Hack as IOS 13.3 and above sometimes do not return the video/audio device. This\n // is a hack to work around the issue until Apple resolve it\n this._isIOS_13_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 13));\n\n this._isIOS_14_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 14));\n\n trace.info(`TechseeMediaServiceBase: isIOS_13_orLater: ${this._isIOS_13_orLater}`);\n trace.info(`TechseeMediaServiceBase: isIOS_14_orLater: ${this._isIOS_14_orLater}`);\n\n this._sessionStreamsManager = {\n getMediaStreamForRole: this.getStreamForDestinationRole,\n addRemoteMediaTrack: this.registerRemoteMediaTrack,\n removeMediaTrack: this.unregisterRemoteMediaTrack\n };\n }\n\n get deviceSupportInfo(): DeviceSupportInfo {\n this.serviceInitGuard(false);\n\n return cloneDeep(this._deviceSupportFlags);\n }\n\n get isVoipEnabled(): boolean {\n return this._isVoipEnabled;\n }\n\n get isSessionActive(): boolean {\n return this._session !== null;\n }\n\n get isLocalStreamInitialized(): boolean {\n return this._initLocalStreamsPromise !== null;\n }\n\n /*\n Initializes media service. Before using anything from this service it should be initialized first.\n All of serviceOptions should be retrieved from account settings and by evaluating application state.\n For example, when initializing service, accountSettings is already loaded, and we know if FS session is audio enabled.\n */\n initMediaService(serviceOptions: MediaServiceOptions): Promise<void> {\n trace.info('TechseeMediaServiceBase.initMediaService', serviceOptions);\n const warnMessage = 'Multiple initialization does not supposed to happen. Not rejecting, but check the flow for correctness.';\n\n if (this._initServicePromise || this._serviceOptions) {\n console.warn(warnMessage);\n\n return this._initServicePromise ? this._initServicePromise : Promise.resolve();\n }\n\n this._serviceOptions = cloneDeep(serviceOptions);\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(this._serviceOptions.mediaServiceType)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n (this._isIOS_14_orLater ? Promise.resolve([]) : enumerateMediaDevices())\n .catch((error) => {\n trace.warn(error);\n\n return [];\n })\n .then((localDevicesList: MediaDeviceInfo[]) => {\n const groupedDevices = {\n video: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'videoinput'),\n audio: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'audioinput')\n };\n\n this._localStreamsManager.setGroupedDevices(groupedDevices);\n this._deviceSupportFlags.hasCamera = this._isIOS_13_orLater || (groupedDevices.video.length > 0);\n this._deviceSupportFlags.hasMicrophone = this._isIOS_13_orLater || (groupedDevices.audio.length > 0);\n\n })\n ]).then(() => undefined);\n\n\n return this._initServicePromise;\n }\n\n //#region Media Streams Management\n\n /*\n The main method to start the initialization of local media streams.\n This method can be executed many times and it will insure all calls are synced and only relevant stream will be created.\n DO NOT USE ANY OTHER METHOD THAT FORCES \"navigator.getUserMedia\" OTHER THAN THIS ONE.\n */\n initLocalMediaStreams(): Promise<void> {\n this.serviceInitGuard();\n\n // This hack for IOS 14 force a recreation of stream on seemingly unnecessary occasions (like agent page refresh).\n // we're not sure why but the stream breaks\n if (!this._initLocalStreamsPromise || this._isIOS_14_orLater) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error(`initLocalMediaStreams failure: ${mediaRequestFailResult}`);\n\n throw mediaRequestFailResult;\n });\n } else {\n trace.info('initLocalMediaStreams already done or in progress');\n }\n\n return this._initLocalStreamsPromise;\n }\n\n /*\n Creates an instance of subscriber that will \"listen\" to the requested stream.\n Whenever requested stream is available, subscriber will render it.\n */\n createSubscriber(subscriberParams: MediaSubscriberParams): Promise<IMediaSubscriber> {\n if (this._subscribers.has(subscriberParams.container)) {\n return Promise.reject('Subscriber for provided DIV element already exists');\n }\n\n const subscriber = new TechseeMediaSubscriber(subscriberParams);\n\n this._subscribers.set(subscriberParams.container, subscriber);\n const streamForSubscriber = this.getRegisteredStreamByType(subscriber.streamType);\n\n if (streamForSubscriber) {\n trace.info(`Stream for ${subscriberParams.streamType} is ready.`);\n subscriber.renderStream(streamForSubscriber);\n } else {\n trace.info(`Stream for ${subscriberParams.streamType} not created yet`);\n }\n\n return Promise.resolve(subscriber);\n }\n\n /*\n Destroys the subscriber for provided HTML DIV container\n */\n destroySubscriber(container: HTMLDivElement): Promise<void> {\n if (this._subscribers.has(container)) {\n const subscriber = this._subscribers.get(container);\n\n subscriber!.dispose();\n }\n\n this._subscribers.delete(container);\n\n return Promise.resolve();\n }\n\n onStreamDestroyed(callback: (eventArgs: StreamDestroyedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_DESTROYED, callback);\n }\n\n onStreamCreated(callback: (eventArgs: StreamCreatedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_CREATED, callback);\n }\n\n onRecordStarted(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STARTED, callback);\n }\n }\n\n onRecordStopped(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STOPPED, callback);\n }\n }\n\n onReconnecting(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECONNECTING, callback);\n }\n }\n\n protected getSwitchCameraConstraints() {\n const constraints = cloneDeep(window.latestLocalMediaConstraints);\n\n if (!constraints) {\n throw new Error('getSwitchCameraConstraints: unexpected use case constraints is null.');\n }\n\n trace.info('getSwitchCameraConstraints - Start switch camera with constraints:', constraints);\n\n const videoSourceType = (constraints.video as LocalVideoStreamConstraints).videoSourceType === LocalVideoSourceType.CAMERA\n ? LocalVideoSourceType.CAMERA_FRONT\n : LocalVideoSourceType.CAMERA;\n\n trace.info(`getSwitchCameraConstraints - switch to videoSourceType: ${videoSourceType}`);\n\n (constraints.video as LocalVideoStreamConstraints).videoSourceType = videoSourceType;\n\n return constraints;\n }\n\n switchCamera(revertCameraWhenFailed?: boolean): Promise<any> {\n trace.info('switchCamera: start');\n\n const latestLocalMediaConstraints = cloneDeep(window.latestLocalMediaConstraints);\n const constraints = !revertCameraWhenFailed ? this.getSwitchCameraConstraints() : latestLocalMediaConstraints;\n\n if (!constraints) {\n throw new Error('switchCamera - unexpected use case constraints is null.');\n }\n\n return this._localStreamsManager.destroyUserMediaStream()\n .then(() => this._localStreamsManager.getUserMediaStream(constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n const stream = streamResult.mediaStream;\n\n trace.info('switchCamera: new stream: ', stream);\n\n return this.replaceStreamTracks(stream)\n .then(() => this.registerStreamResult(constraints, streamResult, true)\n .then(() => ({\n revertCameraWhenFailed,\n constraints,\n streamResult\n })));\n }))\n .catch((err) => {\n trace.error('switchCamera: Failed to switch camera: ', err);\n\n if (!revertCameraWhenFailed) {\n window.latestLocalMediaConstraints = latestLocalMediaConstraints;\n\n return this.switchCamera(true);\n }\n\n return Promise.reject('Failed to switch camera.');\n });\n }\n\n private replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void> {\n if (this._session!.instance) {\n return this._session!.instance.replaceStreamTracks(mediaStream);\n }\n\n return Promise.reject('replaceStreamTracks - session instance is not exists.');\n }\n\n protected registerStreamResult(constraints: Nullable<LocalMediaConstraints>, streamResult: MediaRequestSuccessResult, switchCamera?: boolean, addStreamType?: KnownMediaStream) {\n const regPromises: any[] = [];\n\n if (streamResult.isNew) {\n trace.info('registerStreamResult: stream result from getUserMediaStream:', streamResult.mediaStream);\n streamResult.mediaStream.getTracks().forEach((mediaTrack) => {\n trace.info('registerStreamResult: stream result from mediaTrack:', mediaTrack);\n const streamType = addStreamType || (mediaTrack.kind === 'video'\n ? KnownMediaStream.USER_VIDEO_STREAM : KnownMediaStream.USER_AUDIO_STREAM);\n const newDedicatedStream = new TechseeMediaStream(mediaTrack, streamType, false);\n\n regPromises.push(switchCamera ? this.registerTrack(newDedicatedStream) : this.registerLocalMediaStream(newDedicatedStream));\n });\n }\n\n return Promise.all(regPromises);\n }\n\n disconnectFromMediaSession() {\n if (this._session && this._session.instance) {\n return this._session.instance.sessionDisconnect();\n }\n }\n\n get supportSwitchCameras() {\n return this._isIOS_13_orLater || this._localStreamsManager.groupedDevices.camerasCount > 1;\n }\n\n private _connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n this.serviceInitGuard();\n\n if (!this._session) {\n this._session = {params: cloneDeep(sessionParams)};\n this._session.connectPromise = new Promise((resolve, reject) => {\n const doAsyncReject = (error: any): void => {\n\n /*\n Reject with timeout, to change JS 'execution flow'.\n If timeout removed, session is assigned to null, before promise returns, and \"catch\" will not\n work on promise that is returned.\n */\n setTimeout(() => {\n this._session = null;\n reject(error);\n });\n };\n\n const sessionHandlers: MediaSessionHandlers = {\n onDisconnectedHandler: this.onSessionDisconnectHandler\n };\n const mediaSessionParams: MediaSessionParams & MediaSessionConfiguration = {\n ...sessionParams,\n peerConnectivityTimeoutSeconds: this._serviceOptions!.peerConnectivityTimeoutSeconds || DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS\n };\n\n trace.info('connectToSession', sessionParams);\n\n const allowedValues = values(MediaServiceType);\n\n switch (this._serviceOptions!.mediaServiceType) {\n case MediaServiceType.TURNSERVER:\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n break;\n case MediaServiceType.OPENTOK:\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n case MediaServiceType.MEDIASERVER:\n this._session!.instance = new MediaServerSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n default:\n trace.error(`mediaServiceType '${this._serviceOptions!.mediaServiceType}' is not supported.`, {allowedValues});\n doAsyncReject(new Error('mediaServiceType is not supported'));\n }\n\n if (this._session && this._session.instance) {\n this._session!.instance.connect()\n .then(() => {\n trace.info('Session is connected');\n resolve();\n })\n .catch((error: any) => {\n trace.error('Failed to connect to session', error);\n doAsyncReject(error);\n });\n }\n\n });\n } else {\n trace.warn('Session connected already. Disconnect before connect to new session again');\n }\n\n //TODO - Alex: Change a structure of this function to be more safe\n //(when we return, there should not be case when session or promise is null)\n //To fix, need to return promise immediately, then execute process in setTimeout(fn, 0);\n return this._session!.connectPromise!;\n }\n\n //#endregion Media Streams Management\n\n //#region Session Management\n\n /*\n Creates instance of WebRTC session which connects to (signaling server), and begins to listen to WebRTC events.\n */\n connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n return this._connectToSession(sessionParams);\n }\n\n /*\n Disconnects from WebRTC session.\n */\n disconnectFromSession(): Promise<void> {\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer);\n }\n\n /*\n Updates a credentials for turn server\n */\n updateSessionCredentials(credentials: MediaSessionIceCredentials): Promise<void> {\n trace.info('updateSessionCredentials');\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject('There no session to update credentials');\n }\n\n this._session!.params.credentials = cloneDeep(credentials);\n\n return Promise.resolve();\n }\n\n enableVoipDuringSession(): void {\n if (!this._isVoipEnabled && this.isLocalStreamInitialized) {\n this._initLocalStreamsPromise = null;\n }\n\n this._isVoipEnabled = true;\n }\n\n protected disconnectFromSessionInternal(reason: MediaSessionDisconnectReason): Promise<void> {\n if (!this._session) {\n return Promise.resolve();\n }\n\n trace.info('Disconnecting from session', reason);\n\n const lastSession = this._session;\n\n this._session = null;\n\n return Promise.all([\n lastSession.instance!.disconnect(),\n this.clearPublishers()\n ]).then(() => undefined);\n }\n\n protected getStatsForRemoteTrack(streamType: KnownMediaStream): Promise<RemoteTrackStats> {\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject(new Error('Media session not started'));\n }\n\n const streamForStats = this.getRegisteredStreamByType(streamType);\n\n if (!streamForStats) {\n return Promise.reject(new Error('Stream for requested type was not found'));\n }\n\n return this._session!.instance!.getRemoteTrackStats(streamForStats.mediaTrack)\n .then((trackStats: RemoteTrackStats) => {\n traceStatsInfo(`MediaTrackStats for ${streamType}: ${JSON.stringify(trackStats)}`);\n\n return {\n trackId: streamForStats.mediaTrack.id,\n trackStats: trackStats\n };\n });\n }\n\n private onSessionDisconnectHandler(reason: MediaSessionDisconnectReason): void {\n trace.info('onSessionDisconnectHandler', reason);\n if (reason !== MediaSessionDisconnectReason.ForcedByConsumer) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n this.disconnectFromSessionInternal(reason).then(() => {\n const reconnectReasons = [\n MediaSessionDisconnectReason.InitiatorPeerReconnected,\n MediaSessionDisconnectReason.SignalingChannelDisconnect,\n MediaSessionDisconnectReason.PeerConnectionInterrupted,\n MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout\n ];\n\n if (lastParams && includes(reconnectReasons, reason) && this._autoReconnectEnabled) {\n this.reconnectToSession(lastParams);\n } else if (!this._autoReconnectEnabled) {\n trace.info('No reconnection- auto reconnect disabled');\n } else {\n trace.info('No params for reconnection to media session');\n }\n });\n } else {\n trace.info('Ignore Session disconnect event');\n }\n }\n\n setAutoReconnect(state: boolean) {\n this._autoReconnectEnabled = state;\n trace.info('_autoReconnectEnabled:', state);\n\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n if (lastParams) {\n trace.info('setAutoReconnect - reconnect to session');\n this.reconnectToSession(lastParams);\n }\n }\n }\n\n reconnectToSession(lastParams: MediaSessionParams) {\n trace.info('Reconnecting to media session, sessionParams:', lastParams);\n\n this._connectToSession(lastParams)\n .then(() => {\n trace.info('Media session reconnected');\n })\n .catch((error: any) => {\n trace.error('Error while reconnecting to media session', error);\n });\n }\n\n //#endregion Session Management\n\n //#region Utils\n\n getSnapshotFromKnownStream(sourceStream: KnownMediaStream, snapshotOptions?: SnapshotOptions): Promise<SnapshotResult> {\n return new Promise((resolve, reject) => {\n if (sourceStream !== KnownMediaStream.USER_VIDEO_STREAM &&\n sourceStream !== KnownMediaStream.USER_SCREEN_SHARE_STREAM) {\n trace.error('The requested stream is not video stream, and cannot be used for snapshot');\n reject(new Error('INCOMPATIBLE_STREAM_FOR_SNAPSHOT'));\n\n return;\n }\n\n const snapshotStream = this.getRegisteredStreamByType(sourceStream);\n\n if (!snapshotStream) {\n trace.error('Cannot make snapshot: The requested stream not exists yet.');\n reject(new Error('NO_REQUESTED_STREAM'));\n\n return;\n }\n\n getSnapshotFromMediaStream(snapshotStream.mediaStream, snapshotOptions)\n .then((imageData: any) => {\n const urlComponents = imageData.split(';base64,');\n const mimeType = urlComponents[0].split(':')[1];\n const bytes = atob(urlComponents[1]);\n const buffer = new ArrayBuffer(bytes.length);\n const rawData = new Uint8Array(buffer);\n\n for (let i = 0; i < bytes.length; i++) {\n rawData[i] = bytes.charCodeAt(i);\n }\n\n const blob = new Blob([rawData], {type: mimeType});\n const objectUrl = window.URL.createObjectURL(blob);\n\n trace.info('Snapshot created successfully');\n\n const result: SnapshotResult = {\n base64img: imageData,\n objectUrl: objectUrl,\n imageBlob: blob,\n mimeType: mimeType\n };\n\n resolve(result);\n })\n .catch((error: any) => {\n trace.error('Error creating snapshot', error);\n reject(error);\n });\n });\n }\n\n //Will clean streams, publishers and subscribers, but will not remove event listeners\n clearService(): Promise<void> {\n trace.info('MediaService clearing all resources');\n\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer)\n .then(this.clearSubscribers)\n .then(this.clearRegisteredStreams)\n .then(this._localStreamsManager.clearAllStreams)\n .then(() => {\n this._initLocalStreamsPromise = null;\n this._isVoipEnabled = false;\n }).then(() => undefined);\n }\n\n //Will clear the service and remove all event listeners\n dispose(): Promise<void> {\n return this.clearService().catch(() => undefined).then(() => {\n this._emitter.removeAllListeners();\n });\n }\n\n //#endregion\n\n //#region Protected Methods\n\n protected get subscribers(): Map<HTMLDivElement, TechseeMediaSubscriber> {\n return this._subscribers;\n }\n\n protected registerLocalMediaStream(tsMediaStream: TechseeMediaStream): Promise<void> {\n trace.info('Registering local stream', tsMediaStream.streamType);\n if (this._registeredStreams.has(tsMediaStream.streamType)) {\n return Promise.reject(new Error(`Stream ${tsMediaStream.streamType} already registered`));\n }\n\n return this.registerStream(tsMediaStream);\n }\n\n protected getRegisteredStreamByType(streamType: KnownMediaStream): Nullable<TechseeMediaStream> {\n return this._registeredStreams.get(streamType) || null;\n }\n\n protected changeEnableForKnownStream(streamType: KnownMediaStream, isPaused: boolean): void {\n const streamToChangeState = this.getRegisteredStreamByType(streamType);\n\n if (!streamToChangeState) {\n trace.warn('There no stream found to change enable state', streamType);\n } else {\n streamToChangeState.mediaTrack.enabled = !isPaused;\n\n if (streamToChangeState.isRemote && streamToChangeState.streamKind === KnownMediaStreamKind.Audio) {\n this.subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamType) {\n subscriber.muteSound(isPaused);\n }\n });\n }\n\n trace.info('Local stream enable state is changed', {streamType, isPaused});\n }\n }\n\n protected registerEventCallback(event: string, callback: (eventArgs?: any) => void): void {\n this._emitter.on(event, callback);\n }\n\n protected unregisterEventCallback(event: string, callback: (eventArgs?: any) => void) {\n this._emitter.off(event, callback);\n }\n\n protected emitEvent(event: string, eventArgs?: any): void {\n setTimeout(() => {\n this._emitter.emit(event, eventArgs);\n });\n }\n\n //#endregion Protected Methods\n\n //#region Private Methods\n\n private registerRemoteMediaTrack(remoteMediaTrack: RemoteMediaTrack): Promise<void> {\n trace.info(`Registering remote ${remoteMediaTrack.trackType} MediaStreamTrack`, remoteMediaTrack.mediaTrack);\n const currentStream = this._registeredStreams.get(remoteMediaTrack.trackType);\n\n if (currentStream && !currentStream.isRemote) {\n return Promise.reject(new Error('Cannot register remote stream with the same type as local stream'));\n }\n\n const newDedicatedStream = new TechseeMediaStream(remoteMediaTrack.mediaTrack, remoteMediaTrack.trackType, true);\n\n return this.registerStream(newDedicatedStream);\n }\n\n private registerTrack(mediaStream: TechseeMediaStream): Promise<void> {\n mediaStream.mediaTrack.onended = () =>\n this.unregisterTechseeMediaStream(mediaStream, MediaStreamUnregisterReason.NativeEvent);\n\n this._registeredStreams.set(mediaStream.streamType, mediaStream);\n\n trace.info(`TechseeMediaStream registered - ${mediaStream.streamType}`, TechseeMediaStream);\n\n return this.updateSubscribersWithNewStream(mediaStream).then(() => {\n const eventArgs: StreamCreatedEventArgs = {streamType: mediaStream.streamType};\n\n this.emitEvent(privateEvents.STREAM_CREATED, eventArgs);\n });\n }\n\n protected registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(() => this.registerTrack(mediaStream));\n }\n\n return this.registerTrack(mediaStream);\n }\n\n private unregisterTechseeMediaStream(streamToUnregister: TechseeMediaStream, reason: MediaStreamUnregisterReason): Promise<void> {\n const traceError = (error: any): void => {\n trace.warn('Unregister TechseeMediaStream error:', error);\n };\n\n const promises: any = [];\n\n trace.info('Unregister TechseeMediaStream: ', streamToUnregister.streamType);\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamToUnregister.streamType) {\n promises.push(subscriber.stopRendering().catch(traceError));\n }\n });\n\n if (!streamToUnregister.isRemote) {\n this._publishers.forEach((publisher: TechseeMediaPublisher) => {\n if (publisher.streamTypes.find((streamType) => streamType === streamToUnregister.streamType)) {\n if (this._session && this._session!.instance) {\n promises.push(this._session.instance.onMediaStreamDestroyed(publisher.destinationRole).catch(traceError));\n }\n }\n });\n }\n\n return Promise.all(promises)\n .then(() => {\n this._registeredStreams.delete(streamToUnregister.streamType);\n trace.info('Stream deleted', streamToUnregister.streamType);\n\n const eventArgs: StreamDestroyedEventArgs = {streamType: streamToUnregister.streamType, reason: reason};\n\n this.emitEvent(privateEvents.STREAM_DESTROYED, eventArgs);\n });\n }\n\n private unregisterRemoteMediaTrack(mediaTrack: MediaStreamTrack): Promise<void> {\n const promises: any = [];\n\n this._registeredStreams.forEach((registeredStream: TechseeMediaStream) => {\n if (registeredStream.mediaTrack.id === mediaTrack.id) {\n promises.push(this.unregisterTechseeMediaStream(registeredStream, MediaStreamUnregisterReason.ClosedRemotely));\n }\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private removePublisher(publisher: TechseeMediaPublisher): Promise<void> {\n this._publishers.delete(publisher.destinationRole);\n if (this._session && this._session.instance) {\n trace.info('Removing publisher', publisher.destinationRole);\n\n return this._session.instance.onMediaStreamDestroyed(publisher.destinationRole)\n .catch((error: any) => {\n trace.warn('Error while removing publisher', error);\n });\n }\n\n return Promise.resolve();\n }\n\n private getStreamForDestinationRole(destinationRole: SessionClientRole): Promise<Nullable<MediaStream>> {\n this.serviceInitGuard();\n\n if (this._publishers.has(destinationRole)) {\n trace.info(`Publisher for ${destinationRole} already exists`);\n\n return Promise.resolve(this._publishers.get(destinationRole)!.mediaStream);\n }\n\n if (!this._publisherPromises.get(destinationRole)) {\n trace.info(`Creating publisher for ${destinationRole}`);\n const publisherPromise = this.initLocalMediaStreams()\n .then(() => this.createMediaPublisher(destinationRole))\n .then((mediaPublisher: Nullable<TechseeMediaPublisher>) => {\n if (mediaPublisher) {\n this._publishers.set(destinationRole, mediaPublisher);\n }\n\n this._publisherPromises.delete(destinationRole);\n\n return mediaPublisher ? mediaPublisher.mediaStream : null;\n })\n .catch((ex: any) => {\n if (ex && ex.message === 'audioStreamFailed') {\n this._isVoipEnabled = false;\n\n return null;\n }\n\n this._publisherPromises.delete(destinationRole);\n throw ex;\n });\n\n this._publisherPromises.set(destinationRole, publisherPromise);\n } else {\n trace.info(`Create publisher promise for ${destinationRole} already exists`);\n }\n\n return this._publisherPromises.get(destinationRole)!;\n }\n\n private updateSubscribersWithNewStream(registeredStream: TechseeMediaStream): Promise<void> {\n trace.info(`Updating subscribers of ${registeredStream.streamType} with new stream`);\n\n if (this._subscribers.size === 0) {\n trace.warn(`No subscribers exists for the ${registeredStream.streamType}.`);\n } else {\n trace.info(`Total ${this._subscribers.size} subscribers exists, will check if rerender needed.`);\n }\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === registeredStream.streamType) {\n trace.info(`${registeredStream.streamType} rendering on subscriber`);\n subscriber.renderStream(registeredStream);\n }\n });\n\n return Promise.resolve();\n }\n\n private bindClassMethods(): void {\n this.updateSessionCredentials = this.updateSessionCredentials.bind(this);\n this.getStreamForDestinationRole = this.getStreamForDestinationRole.bind(this);\n this.registerRemoteMediaTrack = this.registerRemoteMediaTrack.bind(this);\n this.unregisterRemoteMediaTrack = this.unregisterRemoteMediaTrack.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.updateSubscribersWithNewStream = this.updateSubscribersWithNewStream.bind(this);\n this.initLocalMediaStreams = this.initLocalMediaStreams.bind(this);\n this.onSessionDisconnectHandler = this.onSessionDisconnectHandler.bind(this);\n this.clearService = this.clearService.bind(this);\n this.clearRegisteredStreams = this.clearRegisteredStreams.bind(this);\n this.clearPublishers = this.clearPublishers.bind(this);\n this.clearSubscribers = this.clearSubscribers.bind(this);\n this._connectToSession = this._connectToSession.bind(this);\n this.disconnectFromMediaSession = this.disconnectFromMediaSession.bind(this);\n }\n\n //region Cleanup\n\n private clearRegisteredStreams(): Promise<void> {\n trace.info('Clearing registered streams');\n const promises: any = [];\n\n this._registeredStreams.forEach((streamToUnregister: TechseeMediaStream) => {\n promises.push(this.unregisterTechseeMediaStream(streamToUnregister, MediaStreamUnregisterReason.ServiceCleanUp));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearSubscribers(): Promise<void> {\n trace.info('Clearing subscribers');\n const promises: any = [];\n\n this._subscribers.forEach((subscriber) => {\n promises.push(this.destroySubscriber(subscriber.container));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearPublishers(): Promise<void> {\n trace.info('Clearing publishers');\n const promises: any = [];\n\n this._publishers.forEach((publisher) => {\n promises.push(this.removePublisher(publisher));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n //#endregion Cleanup\n\n //#region Simple Validation Methods\n\n protected serviceInitGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._serviceOptions, 'Media service is not initialized', shouldThrow);\n }\n\n protected sessionExistsGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._session, 'There no active session', shouldThrow);\n }\n\n //#endregion\n\n //#endregion Private Methods\n}\n"]}
1
+ {"version":3,"sources":["MediaServiceBase.js","../src/MediaServiceBase.ts"],"names":["__assign","Object","assign","t","s","i","n","arguments","length","p","prototype","hasOwnProperty","call","apply","__importDefault","mod","__esModule","defineProperty","exports","value","TechseeMediaServiceBase","filter_1","require","cloneDeep_1","values_1","debounce_1","includes_1","events_1","MediaSubscriber_1","LocalStreamManager_1","TechseeMediaStream_1","SessionTurn_1","MediaDomUtils_1","Compatibility_1","SessionOpentok_1","MediaConstants_1","guards_1","MediaServer_1","MediaTracer_1","MediaSessionBase_1","trace","getMediaTracer","traceStatsInfo","default","info","leading","maxWait","privateEvents","environment","webRtcSupportInfo","_subscribers","Map","_publishers","_publisherPromises","_serviceOptions","_isIOS_13_orLater","_isIOS_14_orLater","_autoReconnectEnabled","_initLocalStreamsPromise","_initServicePromise","_registeredStreams","_isVoipEnabled","_session","bindClassMethods","_emitter","EventEmitter","_environment","_deviceSupportFlags","videoPlayback","hasCamera","hasMicrophone","_localStreamsManager","LocalStreamManager","isIOS","majorVersion","_sessionStreamsManager","getMediaStreamForRole","getStreamForDestinationRole","addRemoteMediaTrack","registerRemoteMediaTrack","removeMediaTrack","unregisterRemoteMediaTrack","get","serviceInitGuard","enumerable","configurable","initMediaService","serviceOptions","_this","warnMessage","console","warn","Promise","resolve","all","isVideoPlaySupportedOnDevice","mediaServiceType","then","isSupported","enumerateMediaDevices","catch","error","localDevicesList","groupedDevices","video","device","kind","toLowerCase","audio","setGroupedDevices","undefined","initLocalMediaStreams","isDashboardOpentok","MediaServiceType","OPENTOK","clientRole","SessionClientRole","AGENT","OBSERVER","getLocalMediaImplementation","mediaRequestFailResult","createSubscriber","subscriberParams","has","container","reject","subscriber","TechseeMediaSubscriber","set","streamForSubscriber","getRegisteredStreamByType","streamType","renderStream","destroySubscriber","dispose","delete","onStreamDestroyed","callback","registerEventCallback","STREAM_DESTROYED","onStreamCreated","STREAM_CREATED","onRecordStarted","instance","recordingEvents","RECORD_STARTED","onRecordStopped","RECORD_STOPPED","onReconnecting","RECONNECTING","getSwitchCameraConstraints","constraints","window","latestLocalMediaConstraints","Error","videoSourceType","LocalVideoSourceType","CAMERA","CAMERA_FRONT","switchCamera","revertCameraWhenFailed","destroyUserMediaStream","getUserMediaStream","streamResult","stream","mediaStream","replaceStreamTracks","registerStreamResult","err","addStreamType","regPromises","isOpentok","isNew","getTracks","forEach","mediaTrack","KnownMediaStream","USER_VIDEO_STREAM","USER_AUDIO_STREAM","newDedicatedStream","TechseeMediaStream","push","registerStream","registerTrack","registerLocalMediaStream","disconnectFromMediaSession","sessionDisconnect","camerasCount","_connectToSession","sessionParams","params","connectPromise","doAsyncReject","setTimeout","sessionHandlers","onDisconnectedHandler","onSessionDisconnectHandler","mediaSessionParams","peerConnectivityTimeoutSeconds","DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS","allowedValues","TURNSERVER","TurnWebRtcSession","OpentokSession","MEDIASERVER","MediaServerSession","connect","connectToSession","disconnectFromSession","disconnectFromSessionInternal","MediaSessionDisconnectReason","ForcedByConsumer","updateSessionCredentials","credentials","sessionExistsGuard","enableVoipDuringSession","isLocalStreamInitialized","reason","lastSession","disconnect","clearPublishers","getStatsForRemoteTrack","streamForStats","getRemoteTrackStats","trackStats","JSON","stringify","trackId","id","lastParams_1","reconnectReasons","InitiatorPeerReconnected","SignalingChannelDisconnect","PeerConnectionInterrupted","PeerConnectionStateChangeTimeout","reconnectToSession","setAutoReconnect","state","isSessionActive","lastParams","getSnapshotFromKnownStream","sourceStream","snapshotOptions","USER_SCREEN_SHARE_STREAM","snapshotStream","getSnapshotFromMediaStream","imageData","urlComponents","split","mimeType","bytes","atob","buffer","ArrayBuffer","rawData","Uint8Array","charCodeAt","blob","Blob","type","objectUrl","URL","createObjectURL","result","base64img","imageBlob","clearService","clearSubscribers","clearRegisteredStreams","clearAllStreams","removeAllListeners","tsMediaStream","changeEnableForKnownStream","isPaused","streamToChangeState","enabled","isRemote","streamKind","KnownMediaStreamKind","Audio","subscribers","muteSound","event","on","unregisterEventCallback","off","emitEvent","eventArgs","emit","remoteMediaTrack","trackType","currentStream","onended","unregisterTechseeMediaStream","MediaStreamUnregisterReason","NativeEvent","updateSubscribersWithNewStream","ReplacingStream","streamToUnregister","traceError","promises","stopRendering","publisher","streamTypes","find","onMediaStreamDestroyed","destinationRole","registeredStream","ClosedRemotely","removePublisher","publisherPromise","createMediaPublisher","mediaPublisher","ex","message","size","bind","ServiceCleanUp","shouldThrow","throwableGuard"],"mappings":"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","file":"MediaServiceBase.js","sourcesContent":["\"use strict\";\nvar __assign = (this && this.__assign) || function () {\n __assign = Object.assign || function(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\n t[p] = s[p];\n }\n return t;\n };\n return __assign.apply(this, arguments);\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TechseeMediaServiceBase = void 0;\nvar filter_1 = __importDefault(require(\"lodash/filter\"));\nvar cloneDeep_1 = __importDefault(require(\"lodash/cloneDeep\"));\nvar values_1 = __importDefault(require(\"lodash/values\"));\nvar debounce_1 = __importDefault(require(\"lodash/debounce\"));\nvar includes_1 = __importDefault(require(\"lodash/includes\"));\nvar events_1 = require(\"events\");\nvar MediaSubscriber_1 = require(\"./MediaSubscriber\");\nvar LocalStreamManager_1 = require(\"./LocalStreamManager\");\nvar TechseeMediaStream_1 = require(\"./TechseeMediaStream\");\nvar SessionTurn_1 = require(\"./MediaSession/SessionTurn\");\nvar MediaDomUtils_1 = require(\"./MediaUtils/MediaDomUtils\");\nvar Compatibility_1 = require(\"./MediaUtils/Compatibility\");\nvar SessionOpentok_1 = require(\"./MediaSession/SessionOpentok\");\nvar MediaConstants_1 = require(\"./MediaConstants\");\nvar guards_1 = require(\"@techsee/techsee-common/lib/core/guards\");\nvar MediaServer_1 = require(\"./MediaSession/MediaServer\");\nvar MediaTracer_1 = require(\"./MediaUtils/MediaTracer\");\nvar MediaSessionBase_1 = require(\"./MediaSession/MediaSessionBase\");\nvar trace = MediaTracer_1.getMediaTracer('MediaServiceBase');\nvar traceStatsInfo = debounce_1.default(trace.info, 1000 * 5, { leading: true, maxWait: 1000 * 30 });\nvar privateEvents;\n(function (privateEvents) {\n privateEvents[\"STREAM_CREATED\"] = \"STREAM_CREATED\";\n privateEvents[\"STREAM_DESTROYED\"] = \"STREAM_DESTROYED\";\n})(privateEvents || (privateEvents = {}));\nvar TechseeMediaServiceBase = /** @class */ (function () {\n function TechseeMediaServiceBase(environment, webRtcSupportInfo) {\n this._subscribers = new Map();\n this._publishers = new Map();\n this._publisherPromises = new Map();\n this._serviceOptions = null;\n this._isIOS_13_orLater = false;\n this._isIOS_14_orLater = false;\n this._autoReconnectEnabled = true;\n this._initLocalStreamsPromise = null;\n this._initServicePromise = null;\n this._registeredStreams = new Map();\n this._isVoipEnabled = false;\n this._session = null;\n trace.info('TechseeMediaServiceBase created');\n this.bindClassMethods();\n this._emitter = new events_1.EventEmitter();\n this._environment = environment;\n this._deviceSupportFlags = {\n videoPlayback: false,\n hasCamera: false,\n hasMicrophone: false,\n webRtcSupportInfo: webRtcSupportInfo\n };\n this._localStreamsManager = new LocalStreamManager_1.LocalStreamManager(this._environment);\n // TODO: Hack as IOS 13.3 and above sometimes do not return the video/audio device. This\n // is a hack to work around the issue until Apple resolve it\n this._isIOS_13_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 13));\n this._isIOS_14_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 14));\n trace.info(\"TechseeMediaServiceBase: isIOS_13_orLater: \" + this._isIOS_13_orLater);\n trace.info(\"TechseeMediaServiceBase: isIOS_14_orLater: \" + this._isIOS_14_orLater);\n this._sessionStreamsManager = {\n getMediaStreamForRole: this.getStreamForDestinationRole,\n addRemoteMediaTrack: this.registerRemoteMediaTrack,\n removeMediaTrack: this.unregisterRemoteMediaTrack\n };\n }\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"deviceSupportInfo\", {\n get: function () {\n this.serviceInitGuard(false);\n return cloneDeep_1.default(this._deviceSupportFlags);\n },\n enumerable: false,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isVoipEnabled\", {\n get: function () {\n return this._isVoipEnabled;\n },\n enumerable: false,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isSessionActive\", {\n get: function () {\n return this._session !== null;\n },\n enumerable: false,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isLocalStreamInitialized\", {\n get: function () {\n return this._initLocalStreamsPromise !== null;\n },\n enumerable: false,\n configurable: true\n });\n /*\n Initializes media service. Before using anything from this service it should be initialized first.\n All of serviceOptions should be retrieved from account settings and by evaluating application state.\n For example, when initializing service, accountSettings is already loaded, and we know if FS session is audio enabled.\n */\n TechseeMediaServiceBase.prototype.initMediaService = function (serviceOptions) {\n var _this = this;\n trace.info('TechseeMediaServiceBase.initMediaService', serviceOptions);\n var warnMessage = 'Multiple initialization does not supposed to happen. Not rejecting, but check the flow for correctness.';\n if (this._initServicePromise || this._serviceOptions) {\n console.warn(warnMessage);\n return this._initServicePromise ? this._initServicePromise : Promise.resolve();\n }\n this._serviceOptions = cloneDeep_1.default(serviceOptions);\n this._initServicePromise = Promise.all([\n Compatibility_1.isVideoPlaySupportedOnDevice(this._serviceOptions.mediaServiceType)\n .then(function (isSupported) { return _this._deviceSupportFlags.videoPlayback = isSupported; }),\n (this._isIOS_14_orLater ? Promise.resolve([]) : Compatibility_1.enumerateMediaDevices())\n .catch(function (error) {\n trace.warn(error);\n return [];\n })\n .then(function (localDevicesList) {\n var groupedDevices = {\n video: filter_1.default(localDevicesList, function (device) { return device.kind.toLowerCase() === 'videoinput'; }),\n audio: filter_1.default(localDevicesList, function (device) { return device.kind.toLowerCase() === 'audioinput'; })\n };\n _this._localStreamsManager.setGroupedDevices(groupedDevices);\n _this._deviceSupportFlags.hasCamera = _this._isIOS_13_orLater || (groupedDevices.video.length > 0);\n _this._deviceSupportFlags.hasMicrophone = _this._isIOS_13_orLater || (groupedDevices.audio.length > 0);\n })\n ]).then(function () { return undefined; });\n return this._initServicePromise;\n };\n //#region Media Streams Management\n /*\n The main method to start the initialization of local media streams.\n This method can be executed many times and it will insure all calls are synced and only relevant stream will be created.\n DO NOT USE ANY OTHER METHOD THAT FORCES \"navigator.getUserMedia\" OTHER THAN THIS ONE.\n */\n TechseeMediaServiceBase.prototype.initLocalMediaStreams = function () {\n this.serviceInitGuard();\n var isDashboardOpentok = this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK &&\n (this._serviceOptions.clientRole === MediaConstants_1.SessionClientRole.AGENT || this._serviceOptions.clientRole === MediaConstants_1.SessionClientRole.OBSERVER);\n // This hack for IOS 14 force a recreation of stream on seemingly unnecessary occasions (like agent page refresh).\n // we're not sure why but the stream breaks\n if (!this._initLocalStreamsPromise || (this._isIOS_14_orLater && !isDashboardOpentok)) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch(function (mediaRequestFailResult) {\n trace.error(\"initLocalMediaStreams failure: \" + mediaRequestFailResult);\n throw mediaRequestFailResult;\n });\n }\n else {\n trace.info('initLocalMediaStreams already done or in progress');\n }\n return this._initLocalStreamsPromise;\n };\n /*\n Creates an instance of subscriber that will \"listen\" to the requested stream.\n Whenever requested stream is available, subscriber will render it.\n */\n TechseeMediaServiceBase.prototype.createSubscriber = function (subscriberParams) {\n if (this._subscribers.has(subscriberParams.container)) {\n return Promise.reject('Subscriber for provided DIV element already exists');\n }\n var subscriber = new MediaSubscriber_1.TechseeMediaSubscriber(subscriberParams);\n this._subscribers.set(subscriberParams.container, subscriber);\n var streamForSubscriber = this.getRegisteredStreamByType(subscriber.streamType);\n if (streamForSubscriber) {\n trace.info(\"Stream for \" + subscriberParams.streamType + \" is ready.\");\n subscriber.renderStream(streamForSubscriber);\n }\n else {\n trace.info(\"Stream for \" + subscriberParams.streamType + \" not created yet\");\n }\n return Promise.resolve(subscriber);\n };\n /*\n Destroys the subscriber for provided HTML DIV container\n */\n TechseeMediaServiceBase.prototype.destroySubscriber = function (container) {\n if (this._subscribers.has(container)) {\n var subscriber = this._subscribers.get(container);\n subscriber.dispose();\n }\n this._subscribers.delete(container);\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.onStreamDestroyed = function (callback) {\n this.registerEventCallback(privateEvents.STREAM_DESTROYED, callback);\n };\n TechseeMediaServiceBase.prototype.onStreamCreated = function (callback) {\n this.registerEventCallback(privateEvents.STREAM_CREATED, callback);\n };\n TechseeMediaServiceBase.prototype.onRecordStarted = function (callback) {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(MediaSessionBase_1.recordingEvents.RECORD_STARTED, callback);\n }\n };\n TechseeMediaServiceBase.prototype.onRecordStopped = function (callback) {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(MediaSessionBase_1.recordingEvents.RECORD_STOPPED, callback);\n }\n };\n TechseeMediaServiceBase.prototype.onReconnecting = function (callback) {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(MediaSessionBase_1.recordingEvents.RECONNECTING, callback);\n }\n };\n TechseeMediaServiceBase.prototype.getSwitchCameraConstraints = function () {\n var constraints = cloneDeep_1.default(window.latestLocalMediaConstraints);\n if (!constraints) {\n throw new Error('getSwitchCameraConstraints: unexpected use case constraints is null.');\n }\n trace.info('getSwitchCameraConstraints - Start switch camera with constraints:', constraints);\n var videoSourceType = constraints.video.videoSourceType === MediaConstants_1.LocalVideoSourceType.CAMERA\n ? MediaConstants_1.LocalVideoSourceType.CAMERA_FRONT\n : MediaConstants_1.LocalVideoSourceType.CAMERA;\n trace.info(\"getSwitchCameraConstraints - switch to videoSourceType: \" + videoSourceType);\n constraints.video.videoSourceType = videoSourceType;\n return constraints;\n };\n TechseeMediaServiceBase.prototype.switchCamera = function (revertCameraWhenFailed) {\n var _this = this;\n trace.info('switchCamera: start');\n var latestLocalMediaConstraints = cloneDeep_1.default(window.latestLocalMediaConstraints);\n var constraints = !revertCameraWhenFailed ? this.getSwitchCameraConstraints() : latestLocalMediaConstraints;\n if (!constraints) {\n throw new Error('switchCamera - unexpected use case constraints is null.');\n }\n return this._localStreamsManager.destroyUserMediaStream()\n .then(function () { return _this._localStreamsManager.getUserMediaStream(constraints)\n .then(function (streamResult) {\n var stream = streamResult.mediaStream;\n trace.info('switchCamera: new stream: ', stream);\n return _this.replaceStreamTracks(stream)\n .then(function () { return _this.registerStreamResult(constraints, streamResult, true)\n .then(function () { return ({\n revertCameraWhenFailed: revertCameraWhenFailed,\n constraints: constraints,\n streamResult: streamResult\n }); }); });\n }); })\n .catch(function (err) {\n trace.error('switchCamera: Failed to switch camera: ', err);\n if (!revertCameraWhenFailed) {\n window.latestLocalMediaConstraints = latestLocalMediaConstraints;\n return _this.switchCamera(true);\n }\n return Promise.reject('Failed to switch camera.');\n });\n };\n TechseeMediaServiceBase.prototype.replaceStreamTracks = function (mediaStream) {\n if (this._session.instance) {\n return this._session.instance.replaceStreamTracks(mediaStream);\n }\n return Promise.reject('replaceStreamTracks - session instance is not exists.');\n };\n TechseeMediaServiceBase.prototype.registerStreamResult = function (constraints, streamResult, switchCamera, addStreamType) {\n var _this = this;\n var regPromises = [];\n var isOpentok = this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK;\n if (streamResult.isNew) {\n trace.info('registerStreamResult: stream result from getUserMediaStream:', streamResult.mediaStream);\n streamResult.mediaStream.getTracks().forEach(function (mediaTrack) {\n trace.info('registerStreamResult: stream result from mediaTrack:', mediaTrack);\n var streamType = addStreamType || (mediaTrack.kind === 'video'\n ? MediaConstants_1.KnownMediaStream.USER_VIDEO_STREAM : MediaConstants_1.KnownMediaStream.USER_AUDIO_STREAM);\n var newDedicatedStream = new TechseeMediaStream_1.TechseeMediaStream(mediaTrack, streamType, false);\n if (isOpentok) {\n regPromises.push(_this.registerStream(newDedicatedStream));\n }\n else {\n regPromises.push(switchCamera ? _this.registerTrack(newDedicatedStream) : _this.registerLocalMediaStream(newDedicatedStream));\n }\n });\n }\n return Promise.all(regPromises);\n };\n TechseeMediaServiceBase.prototype.disconnectFromMediaSession = function () {\n if (this._session && this._session.instance) {\n return this._session.instance.sessionDisconnect();\n }\n };\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"supportSwitchCameras\", {\n get: function () {\n return this._isIOS_13_orLater || this._localStreamsManager.groupedDevices.camerasCount > 1;\n },\n enumerable: false,\n configurable: true\n });\n TechseeMediaServiceBase.prototype._connectToSession = function (sessionParams) {\n var _this = this;\n this.serviceInitGuard();\n if (!this._session) {\n this._session = { params: cloneDeep_1.default(sessionParams) };\n this._session.connectPromise = new Promise(function (resolve, reject) {\n var doAsyncReject = function (error) {\n /*\n Reject with timeout, to change JS 'execution flow'.\n If timeout removed, session is assigned to null, before promise returns, and \"catch\" will not\n work on promise that is returned.\n */\n setTimeout(function () {\n _this._session = null;\n reject(error);\n });\n };\n var sessionHandlers = {\n onDisconnectedHandler: _this.onSessionDisconnectHandler\n };\n var mediaSessionParams = __assign(__assign({}, sessionParams), { peerConnectivityTimeoutSeconds: _this._serviceOptions.peerConnectivityTimeoutSeconds || MediaConstants_1.DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS });\n trace.info('connectToSession', sessionParams);\n var allowedValues = values_1.default(MediaConstants_1.MediaServiceType);\n switch (_this._serviceOptions.mediaServiceType) {\n case MediaConstants_1.MediaServiceType.TURNSERVER:\n _this._session.instance = new SessionTurn_1.TurnWebRtcSession(mediaSessionParams, sessionHandlers, _this._sessionStreamsManager);\n break;\n case MediaConstants_1.MediaServiceType.OPENTOK:\n _this._session.instance = new SessionOpentok_1.OpentokSession(mediaSessionParams, _this._sessionStreamsManager);\n break;\n case MediaConstants_1.MediaServiceType.MEDIASERVER:\n _this._session.instance = new MediaServer_1.MediaServerSession(mediaSessionParams, _this._sessionStreamsManager);\n break;\n default:\n trace.error(\"mediaServiceType '\" + _this._serviceOptions.mediaServiceType + \"' is not supported.\", { allowedValues: allowedValues });\n doAsyncReject(new Error('mediaServiceType is not supported'));\n }\n if (_this._session && _this._session.instance) {\n _this._session.instance.connect()\n .then(function () {\n trace.info('Session is connected');\n resolve();\n })\n .catch(function (error) {\n trace.error('Failed to connect to session', error);\n doAsyncReject(error);\n });\n }\n });\n }\n else {\n trace.warn('Session connected already. Disconnect before connect to new session again');\n }\n //TODO - Alex: Change a structure of this function to be more safe\n //(when we return, there should not be case when session or promise is null)\n //To fix, need to return promise immediately, then execute process in setTimeout(fn, 0);\n return this._session.connectPromise;\n };\n //#endregion Media Streams Management\n //#region Session Management\n /*\n Creates instance of WebRTC session which connects to (signaling server), and begins to listen to WebRTC events.\n */\n TechseeMediaServiceBase.prototype.connectToSession = function (sessionParams) {\n return this._connectToSession(sessionParams);\n };\n /*\n Disconnects from WebRTC session.\n */\n TechseeMediaServiceBase.prototype.disconnectFromSession = function () {\n return this.disconnectFromSessionInternal(MediaConstants_1.MediaSessionDisconnectReason.ForcedByConsumer);\n };\n /*\n Updates a credentials for turn server\n */\n TechseeMediaServiceBase.prototype.updateSessionCredentials = function (credentials) {\n trace.info('updateSessionCredentials');\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject('There no session to update credentials');\n }\n this._session.params.credentials = cloneDeep_1.default(credentials);\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.enableVoipDuringSession = function () {\n if (!this._isVoipEnabled && this.isLocalStreamInitialized) {\n this._initLocalStreamsPromise = null;\n }\n this._isVoipEnabled = true;\n };\n TechseeMediaServiceBase.prototype.disconnectFromSessionInternal = function (reason) {\n if (!this._session) {\n return Promise.resolve();\n }\n trace.info('Disconnecting from session', reason);\n var lastSession = this._session;\n this._session = null;\n return Promise.all([\n lastSession.instance.disconnect(),\n this.clearPublishers()\n ]).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.getStatsForRemoteTrack = function (streamType) {\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject(new Error('Media session not started'));\n }\n var streamForStats = this.getRegisteredStreamByType(streamType);\n if (!streamForStats) {\n return Promise.reject(new Error('Stream for requested type was not found'));\n }\n return this._session.instance.getRemoteTrackStats(streamForStats.mediaTrack)\n .then(function (trackStats) {\n traceStatsInfo(\"MediaTrackStats for \" + streamType + \": \" + JSON.stringify(trackStats));\n return {\n trackId: streamForStats.mediaTrack.id,\n trackStats: trackStats\n };\n });\n };\n TechseeMediaServiceBase.prototype.onSessionDisconnectHandler = function (reason) {\n var _this = this;\n trace.info('onSessionDisconnectHandler', reason);\n if (reason !== MediaConstants_1.MediaSessionDisconnectReason.ForcedByConsumer) {\n var lastParams_1 = this._session && this._session.params ? this._session.params : null;\n this.disconnectFromSessionInternal(reason).then(function () {\n var reconnectReasons = [\n MediaConstants_1.MediaSessionDisconnectReason.InitiatorPeerReconnected,\n MediaConstants_1.MediaSessionDisconnectReason.SignalingChannelDisconnect,\n MediaConstants_1.MediaSessionDisconnectReason.PeerConnectionInterrupted,\n MediaConstants_1.MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout\n ];\n if (lastParams_1 && includes_1.default(reconnectReasons, reason) && _this._autoReconnectEnabled) {\n _this.reconnectToSession(lastParams_1);\n }\n else if (!_this._autoReconnectEnabled) {\n trace.info('No reconnection- auto reconnect disabled');\n }\n else {\n trace.info('No params for reconnection to media session');\n }\n });\n }\n else {\n trace.info('Ignore Session disconnect event');\n }\n };\n TechseeMediaServiceBase.prototype.setAutoReconnect = function (state) {\n this._autoReconnectEnabled = state;\n trace.info('_autoReconnectEnabled:', state);\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n var lastParams = this._session && this._session.params ? this._session.params : null;\n if (lastParams) {\n trace.info('setAutoReconnect - reconnect to session');\n this.reconnectToSession(lastParams);\n }\n }\n };\n TechseeMediaServiceBase.prototype.reconnectToSession = function (lastParams) {\n trace.info('Reconnecting to media session, sessionParams:', lastParams);\n this._connectToSession(lastParams)\n .then(function () {\n trace.info('Media session reconnected');\n })\n .catch(function (error) {\n trace.error('Error while reconnecting to media session', error);\n });\n };\n //#endregion Session Management\n //#region Utils\n TechseeMediaServiceBase.prototype.getSnapshotFromKnownStream = function (sourceStream, snapshotOptions) {\n var _this = this;\n return new Promise(function (resolve, reject) {\n if (sourceStream !== MediaConstants_1.KnownMediaStream.USER_VIDEO_STREAM &&\n sourceStream !== MediaConstants_1.KnownMediaStream.USER_SCREEN_SHARE_STREAM) {\n trace.error('The requested stream is not video stream, and cannot be used for snapshot');\n reject(new Error('INCOMPATIBLE_STREAM_FOR_SNAPSHOT'));\n return;\n }\n var snapshotStream = _this.getRegisteredStreamByType(sourceStream);\n if (!snapshotStream) {\n trace.error('Cannot make snapshot: The requested stream not exists yet.');\n reject(new Error('NO_REQUESTED_STREAM'));\n return;\n }\n MediaDomUtils_1.getSnapshotFromMediaStream(snapshotStream.mediaStream, snapshotOptions)\n .then(function (imageData) {\n var urlComponents = imageData.split(';base64,');\n var mimeType = urlComponents[0].split(':')[1];\n var bytes = atob(urlComponents[1]);\n var buffer = new ArrayBuffer(bytes.length);\n var rawData = new Uint8Array(buffer);\n for (var i = 0; i < bytes.length; i++) {\n rawData[i] = bytes.charCodeAt(i);\n }\n var blob = new Blob([rawData], { type: mimeType });\n var objectUrl = window.URL.createObjectURL(blob);\n trace.info('Snapshot created successfully');\n var result = {\n base64img: imageData,\n objectUrl: objectUrl,\n imageBlob: blob,\n mimeType: mimeType\n };\n resolve(result);\n })\n .catch(function (error) {\n trace.error('Error creating snapshot', error);\n reject(error);\n });\n });\n };\n //Will clean streams, publishers and subscribers, but will not remove event listeners\n TechseeMediaServiceBase.prototype.clearService = function () {\n var _this = this;\n trace.info('MediaService clearing all resources');\n return this.disconnectFromSessionInternal(MediaConstants_1.MediaSessionDisconnectReason.ForcedByConsumer)\n .then(this.clearSubscribers)\n .then(this.clearRegisteredStreams)\n .then(this._localStreamsManager.clearAllStreams)\n .then(function () {\n _this._initLocalStreamsPromise = null;\n _this._isVoipEnabled = false;\n }).then(function () { return undefined; });\n };\n //Will clear the service and remove all event listeners\n TechseeMediaServiceBase.prototype.dispose = function () {\n var _this = this;\n return this.clearService().catch(function () { return undefined; }).then(function () {\n _this._emitter.removeAllListeners();\n });\n };\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"subscribers\", {\n //#endregion\n //#region Protected Methods\n get: function () {\n return this._subscribers;\n },\n enumerable: false,\n configurable: true\n });\n TechseeMediaServiceBase.prototype.registerLocalMediaStream = function (tsMediaStream) {\n trace.info('Registering local stream', tsMediaStream.streamType);\n if (this._registeredStreams.has(tsMediaStream.streamType)) {\n return Promise.reject(new Error(\"Stream \" + tsMediaStream.streamType + \" already registered\"));\n }\n return this.registerStream(tsMediaStream);\n };\n TechseeMediaServiceBase.prototype.getRegisteredStreamByType = function (streamType) {\n return this._registeredStreams.get(streamType) || null;\n };\n TechseeMediaServiceBase.prototype.changeEnableForKnownStream = function (streamType, isPaused) {\n var streamToChangeState = this.getRegisteredStreamByType(streamType);\n if (!streamToChangeState) {\n trace.warn('There no stream found to change enable state', streamType);\n }\n else {\n streamToChangeState.mediaTrack.enabled = !isPaused;\n if (streamToChangeState.isRemote && streamToChangeState.streamKind === MediaConstants_1.KnownMediaStreamKind.Audio) {\n this.subscribers.forEach(function (subscriber) {\n if (subscriber.streamType === streamType) {\n subscriber.muteSound(isPaused);\n }\n });\n }\n trace.info('Local stream enable state is changed', { streamType: streamType, isPaused: isPaused });\n }\n };\n TechseeMediaServiceBase.prototype.registerEventCallback = function (event, callback) {\n this._emitter.on(event, callback);\n };\n TechseeMediaServiceBase.prototype.unregisterEventCallback = function (event, callback) {\n this._emitter.off(event, callback);\n };\n TechseeMediaServiceBase.prototype.emitEvent = function (event, eventArgs) {\n var _this = this;\n setTimeout(function () {\n _this._emitter.emit(event, eventArgs);\n });\n };\n //#endregion Protected Methods\n //#region Private Methods\n TechseeMediaServiceBase.prototype.registerRemoteMediaTrack = function (remoteMediaTrack) {\n trace.info(\"Registering remote \" + remoteMediaTrack.trackType + \" MediaStreamTrack\", remoteMediaTrack.mediaTrack);\n var currentStream = this._registeredStreams.get(remoteMediaTrack.trackType);\n if (currentStream && !currentStream.isRemote) {\n return Promise.reject(new Error('Cannot register remote stream with the same type as local stream'));\n }\n var newDedicatedStream = new TechseeMediaStream_1.TechseeMediaStream(remoteMediaTrack.mediaTrack, remoteMediaTrack.trackType, true);\n return this.registerStream(newDedicatedStream);\n };\n TechseeMediaServiceBase.prototype.registerTrack = function (mediaStream) {\n var _this = this;\n mediaStream.mediaTrack.onended = function () {\n return _this.unregisterTechseeMediaStream(mediaStream, MediaConstants_1.MediaStreamUnregisterReason.NativeEvent);\n };\n this._registeredStreams.set(mediaStream.streamType, mediaStream);\n trace.info(\"TechseeMediaStream registered - \" + mediaStream.streamType, TechseeMediaStream_1.TechseeMediaStream);\n return this.updateSubscribersWithNewStream(mediaStream).then(function () {\n var eventArgs = { streamType: mediaStream.streamType };\n _this.emitEvent(privateEvents.STREAM_CREATED, eventArgs);\n });\n };\n TechseeMediaServiceBase.prototype.registerStream = function (mediaStream) {\n var _this = this;\n var currentStream = this._registeredStreams.get(mediaStream.streamType);\n if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaConstants_1.MediaStreamUnregisterReason.ReplacingStream).then(function () { return _this.registerTrack(mediaStream); });\n }\n return this.registerTrack(mediaStream);\n };\n TechseeMediaServiceBase.prototype.unregisterTechseeMediaStream = function (streamToUnregister, reason) {\n var _this = this;\n var traceError = function (error) {\n trace.warn('Unregister TechseeMediaStream error:', error);\n };\n var promises = [];\n trace.info('Unregister TechseeMediaStream: ', streamToUnregister.streamType);\n this._subscribers.forEach(function (subscriber) {\n if (subscriber.streamType === streamToUnregister.streamType) {\n promises.push(subscriber.stopRendering().catch(traceError));\n }\n });\n if (!streamToUnregister.isRemote) {\n this._publishers.forEach(function (publisher) {\n if (publisher.streamTypes.find(function (streamType) { return streamType === streamToUnregister.streamType; })) {\n if (_this._session && _this._session.instance) {\n promises.push(_this._session.instance.onMediaStreamDestroyed(publisher.destinationRole).catch(traceError));\n }\n }\n });\n }\n return Promise.all(promises)\n .then(function () {\n _this._registeredStreams.delete(streamToUnregister.streamType);\n trace.info('Stream deleted', streamToUnregister.streamType);\n var eventArgs = { streamType: streamToUnregister.streamType, reason: reason };\n _this.emitEvent(privateEvents.STREAM_DESTROYED, eventArgs);\n });\n };\n TechseeMediaServiceBase.prototype.unregisterRemoteMediaTrack = function (mediaTrack) {\n var _this = this;\n var promises = [];\n this._registeredStreams.forEach(function (registeredStream) {\n if (registeredStream.mediaTrack.id === mediaTrack.id) {\n promises.push(_this.unregisterTechseeMediaStream(registeredStream, MediaConstants_1.MediaStreamUnregisterReason.ClosedRemotely));\n }\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.removePublisher = function (publisher) {\n this._publishers.delete(publisher.destinationRole);\n if (this._session && this._session.instance) {\n trace.info('Removing publisher', publisher.destinationRole);\n return this._session.instance.onMediaStreamDestroyed(publisher.destinationRole)\n .catch(function (error) {\n trace.warn('Error while removing publisher', error);\n });\n }\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.getStreamForDestinationRole = function (destinationRole) {\n var _this = this;\n this.serviceInitGuard();\n if (this._publishers.has(destinationRole)) {\n trace.info(\"Publisher for \" + destinationRole + \" already exists\");\n return Promise.resolve(this._publishers.get(destinationRole).mediaStream);\n }\n if (!this._publisherPromises.get(destinationRole)) {\n trace.info(\"Creating publisher for \" + destinationRole);\n var publisherPromise = this.initLocalMediaStreams()\n .then(function () { return _this.createMediaPublisher(destinationRole); })\n .then(function (mediaPublisher) {\n if (mediaPublisher) {\n _this._publishers.set(destinationRole, mediaPublisher);\n }\n _this._publisherPromises.delete(destinationRole);\n return mediaPublisher ? mediaPublisher.mediaStream : null;\n })\n .catch(function (ex) {\n if (ex && ex.message === 'audioStreamFailed') {\n _this._isVoipEnabled = false;\n return null;\n }\n _this._publisherPromises.delete(destinationRole);\n throw ex;\n });\n this._publisherPromises.set(destinationRole, publisherPromise);\n }\n else {\n trace.info(\"Create publisher promise for \" + destinationRole + \" already exists\");\n }\n return this._publisherPromises.get(destinationRole);\n };\n TechseeMediaServiceBase.prototype.updateSubscribersWithNewStream = function (registeredStream) {\n trace.info(\"Updating subscribers of \" + registeredStream.streamType + \" with new stream\");\n if (this._subscribers.size === 0) {\n trace.warn(\"No subscribers exists for the \" + registeredStream.streamType + \".\");\n }\n else {\n trace.info(\"Total \" + this._subscribers.size + \" subscribers exists, will check if rerender needed.\");\n }\n this._subscribers.forEach(function (subscriber) {\n if (subscriber.streamType === registeredStream.streamType) {\n trace.info(registeredStream.streamType + \" rendering on subscriber\");\n subscriber.renderStream(registeredStream);\n }\n });\n return Promise.resolve();\n };\n TechseeMediaServiceBase.prototype.bindClassMethods = function () {\n this.updateSessionCredentials = this.updateSessionCredentials.bind(this);\n this.getStreamForDestinationRole = this.getStreamForDestinationRole.bind(this);\n this.registerRemoteMediaTrack = this.registerRemoteMediaTrack.bind(this);\n this.unregisterRemoteMediaTrack = this.unregisterRemoteMediaTrack.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.updateSubscribersWithNewStream = this.updateSubscribersWithNewStream.bind(this);\n this.initLocalMediaStreams = this.initLocalMediaStreams.bind(this);\n this.onSessionDisconnectHandler = this.onSessionDisconnectHandler.bind(this);\n this.clearService = this.clearService.bind(this);\n this.clearRegisteredStreams = this.clearRegisteredStreams.bind(this);\n this.clearPublishers = this.clearPublishers.bind(this);\n this.clearSubscribers = this.clearSubscribers.bind(this);\n this._connectToSession = this._connectToSession.bind(this);\n this.disconnectFromMediaSession = this.disconnectFromMediaSession.bind(this);\n };\n //region Cleanup\n TechseeMediaServiceBase.prototype.clearRegisteredStreams = function () {\n var _this = this;\n trace.info('Clearing registered streams');\n var promises = [];\n this._registeredStreams.forEach(function (streamToUnregister) {\n promises.push(_this.unregisterTechseeMediaStream(streamToUnregister, MediaConstants_1.MediaStreamUnregisterReason.ServiceCleanUp));\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.clearSubscribers = function () {\n var _this = this;\n trace.info('Clearing subscribers');\n var promises = [];\n this._subscribers.forEach(function (subscriber) {\n promises.push(_this.destroySubscriber(subscriber.container));\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n TechseeMediaServiceBase.prototype.clearPublishers = function () {\n var _this = this;\n trace.info('Clearing publishers');\n var promises = [];\n this._publishers.forEach(function (publisher) {\n promises.push(_this.removePublisher(publisher));\n });\n return Promise.all(promises).then(function () { return undefined; });\n };\n //#endregion Cleanup\n //#region Simple Validation Methods\n TechseeMediaServiceBase.prototype.serviceInitGuard = function (shouldThrow) {\n if (shouldThrow === void 0) { shouldThrow = true; }\n return guards_1.throwableGuard(!!this._serviceOptions, 'Media service is not initialized', shouldThrow);\n };\n TechseeMediaServiceBase.prototype.sessionExistsGuard = function (shouldThrow) {\n if (shouldThrow === void 0) { shouldThrow = true; }\n return guards_1.throwableGuard(!!this._session, 'There no active session', shouldThrow);\n };\n return TechseeMediaServiceBase;\n}());\nexports.TechseeMediaServiceBase = TechseeMediaServiceBase;\n\n//# sourceMappingURL=MediaServiceBase.js.map\n","import filter from 'lodash/filter';\nimport cloneDeep from 'lodash/cloneDeep';\nimport values from 'lodash/values';\nimport debounce from 'lodash/debounce';\nimport includes from 'lodash/includes';\nimport {EventEmitter} from 'events';\nimport {Nullable} from '@techsee/techsee-common';\nimport {TechseeMediaSubscriber} from './MediaSubscriber';\nimport {TechseeMediaPublisher} from './MediaPublisher';\nimport {LocalStreamManager} from './LocalStreamManager';\nimport {TechseeMediaStream} from './TechseeMediaStream';\nimport {TurnWebRtcSession} from './MediaSession/SessionTurn';\nimport {getSnapshotFromMediaStream} from './MediaUtils/MediaDomUtils';\nimport {\n ClientWebRtcInfo,\n DeviceSupportInfo,\n IMediaEnvironment,\n IMediaSession,\n IMediaSubscriber,\n ISessionStreamsManager,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n MediaServiceOptions,\n MediaSessionConfiguration,\n MediaSessionHandlers,\n MediaSessionIceCredentials,\n MediaSessionParams,\n MediaSubscriberParams,\n RemoteMediaTrack,\n RemoteTrackStats,\n SnapshotOptions,\n SnapshotResult,\n StreamCreatedEventArgs,\n StreamDestroyedEventArgs\n} from './MediaContracts';\nimport {enumerateMediaDevices, isVideoPlaySupportedOnDevice} from './MediaUtils/Compatibility';\nimport {OpentokSession} from './MediaSession/SessionOpentok';\nimport {\n DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n LocalVideoSourceType,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {MediaServerSession} from './MediaSession/MediaServer';\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\nimport {recordingEvents} from './MediaSession/MediaSessionBase';\n\nconst trace = getMediaTracer('MediaServiceBase');\nconst traceStatsInfo = debounce(trace.info, 1000 * 5, {leading: true, maxWait: 1000 * 30});\n\nenum privateEvents {\n STREAM_CREATED = 'STREAM_CREATED',\n STREAM_DESTROYED = 'STREAM_DESTROYED'\n}\n\nexport abstract class TechseeMediaServiceBase {\n //This method should not be called. The only place that allowed to use it, is initLocalMediaStreams method.\n //Everyone who need to initialize MediaStream should use initLocalMediaStreams method.\n protected abstract getLocalMediaImplementation(): Promise<void>;\n\n protected abstract createMediaPublisher(destinationRole: SessionClientRole): Promise<Nullable<TechseeMediaPublisher>>;\n\n protected readonly _environment: IMediaEnvironment;\n protected readonly _sessionStreamsManager: ISessionStreamsManager;\n protected readonly _localStreamsManager: LocalStreamManager;\n\n private readonly _deviceSupportFlags: DeviceSupportInfo;\n private readonly _subscribers: Map<HTMLDivElement, TechseeMediaSubscriber> = new Map();\n private readonly _publishers: Map<SessionClientRole, TechseeMediaPublisher> = new Map();\n private readonly _publisherPromises: Map<SessionClientRole, Promise<Nullable<MediaStream>>> = new Map();\n protected _serviceOptions: Nullable<MediaServiceOptions> = null;\n private _isIOS_13_orLater = false;\n private _isIOS_14_orLater = false;\n private _autoReconnectEnabled = true;\n private readonly _emitter: EventEmitter;\n private _initLocalStreamsPromise: Nullable<Promise<void>> = null;\n private _initServicePromise: Nullable<Promise<void>> = null;\n private _registeredStreams: Map<KnownMediaStream, TechseeMediaStream> = new Map();\n private _isVoipEnabled = false;\n private _session: Nullable<{\n params: MediaSessionParams;\n connectPromise?: Promise<void>;\n instance?: IMediaSession;\n }> = null;\n\n protected constructor(environment: IMediaEnvironment, webRtcSupportInfo: ClientWebRtcInfo) {\n trace.info('TechseeMediaServiceBase created');\n\n this.bindClassMethods();\n\n this._emitter = new EventEmitter();\n this._environment = environment;\n this._deviceSupportFlags = {\n videoPlayback: false,\n hasCamera: false,\n hasMicrophone: false,\n webRtcSupportInfo: webRtcSupportInfo\n };\n\n this._localStreamsManager = new LocalStreamManager(this._environment);\n\n // TODO: Hack as IOS 13.3 and above sometimes do not return the video/audio device. This\n // is a hack to work around the issue until Apple resolve it\n this._isIOS_13_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 13));\n\n this._isIOS_14_orLater = this._environment.isIOS() &&\n (!this._environment.majorVersion() || (this._environment.majorVersion() >= 14));\n\n trace.info(`TechseeMediaServiceBase: isIOS_13_orLater: ${this._isIOS_13_orLater}`);\n trace.info(`TechseeMediaServiceBase: isIOS_14_orLater: ${this._isIOS_14_orLater}`);\n\n this._sessionStreamsManager = {\n getMediaStreamForRole: this.getStreamForDestinationRole,\n addRemoteMediaTrack: this.registerRemoteMediaTrack,\n removeMediaTrack: this.unregisterRemoteMediaTrack\n };\n }\n\n get deviceSupportInfo(): DeviceSupportInfo {\n this.serviceInitGuard(false);\n\n return cloneDeep(this._deviceSupportFlags);\n }\n\n get isVoipEnabled(): boolean {\n return this._isVoipEnabled;\n }\n\n get isSessionActive(): boolean {\n return this._session !== null;\n }\n\n get isLocalStreamInitialized(): boolean {\n return this._initLocalStreamsPromise !== null;\n }\n\n /*\n Initializes media service. Before using anything from this service it should be initialized first.\n All of serviceOptions should be retrieved from account settings and by evaluating application state.\n For example, when initializing service, accountSettings is already loaded, and we know if FS session is audio enabled.\n */\n initMediaService(serviceOptions: MediaServiceOptions): Promise<void> {\n trace.info('TechseeMediaServiceBase.initMediaService', serviceOptions);\n const warnMessage = 'Multiple initialization does not supposed to happen. Not rejecting, but check the flow for correctness.';\n\n if (this._initServicePromise || this._serviceOptions) {\n console.warn(warnMessage);\n\n return this._initServicePromise ? this._initServicePromise : Promise.resolve();\n }\n\n this._serviceOptions = cloneDeep(serviceOptions);\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(this._serviceOptions.mediaServiceType)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n (this._isIOS_14_orLater ? Promise.resolve([]) : enumerateMediaDevices())\n .catch((error) => {\n trace.warn(error);\n\n return [];\n })\n .then((localDevicesList: MediaDeviceInfo[]) => {\n const groupedDevices = {\n video: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'videoinput'),\n audio: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'audioinput')\n };\n\n this._localStreamsManager.setGroupedDevices(groupedDevices);\n this._deviceSupportFlags.hasCamera = this._isIOS_13_orLater || (groupedDevices.video.length > 0);\n this._deviceSupportFlags.hasMicrophone = this._isIOS_13_orLater || (groupedDevices.audio.length > 0);\n\n })\n ]).then(() => undefined);\n\n\n return this._initServicePromise;\n }\n\n //#region Media Streams Management\n\n /*\n The main method to start the initialization of local media streams.\n This method can be executed many times and it will insure all calls are synced and only relevant stream will be created.\n DO NOT USE ANY OTHER METHOD THAT FORCES \"navigator.getUserMedia\" OTHER THAN THIS ONE.\n */\n initLocalMediaStreams(): Promise<void> {\n this.serviceInitGuard();\n const isDashboardOpentok = this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK &&\n (this._serviceOptions!.clientRole === SessionClientRole.AGENT || this._serviceOptions!.clientRole === SessionClientRole.OBSERVER);\n\n // This hack for IOS 14 force a recreation of stream on seemingly unnecessary occasions (like agent page refresh).\n // we're not sure why but the stream breaks\n if (!this._initLocalStreamsPromise || (this._isIOS_14_orLater && !isDashboardOpentok)) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error(`initLocalMediaStreams failure: ${mediaRequestFailResult}`);\n\n throw mediaRequestFailResult;\n });\n } else {\n trace.info('initLocalMediaStreams already done or in progress');\n }\n\n return this._initLocalStreamsPromise;\n }\n\n /*\n Creates an instance of subscriber that will \"listen\" to the requested stream.\n Whenever requested stream is available, subscriber will render it.\n */\n createSubscriber(subscriberParams: MediaSubscriberParams): Promise<IMediaSubscriber> {\n if (this._subscribers.has(subscriberParams.container)) {\n return Promise.reject('Subscriber for provided DIV element already exists');\n }\n\n const subscriber = new TechseeMediaSubscriber(subscriberParams);\n\n this._subscribers.set(subscriberParams.container, subscriber);\n const streamForSubscriber = this.getRegisteredStreamByType(subscriber.streamType);\n\n if (streamForSubscriber) {\n trace.info(`Stream for ${subscriberParams.streamType} is ready.`);\n subscriber.renderStream(streamForSubscriber);\n } else {\n trace.info(`Stream for ${subscriberParams.streamType} not created yet`);\n }\n\n return Promise.resolve(subscriber);\n }\n\n /*\n Destroys the subscriber for provided HTML DIV container\n */\n destroySubscriber(container: HTMLDivElement): Promise<void> {\n if (this._subscribers.has(container)) {\n const subscriber = this._subscribers.get(container);\n\n subscriber!.dispose();\n }\n\n this._subscribers.delete(container);\n\n return Promise.resolve();\n }\n\n onStreamDestroyed(callback: (eventArgs: StreamDestroyedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_DESTROYED, callback);\n }\n\n onStreamCreated(callback: (eventArgs: StreamCreatedEventArgs) => void): void {\n this.registerEventCallback(privateEvents.STREAM_CREATED, callback);\n }\n\n onRecordStarted(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STARTED, callback);\n }\n }\n\n onRecordStopped(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECORD_STOPPED, callback);\n }\n }\n\n onReconnecting(callback: (eventArgs: any) => void): void {\n if (this._session && this._session.instance) {\n this._session.instance.registerEventCallback(recordingEvents.RECONNECTING, callback);\n }\n }\n\n protected getSwitchCameraConstraints() {\n const constraints = cloneDeep(window.latestLocalMediaConstraints);\n\n if (!constraints) {\n throw new Error('getSwitchCameraConstraints: unexpected use case constraints is null.');\n }\n\n trace.info('getSwitchCameraConstraints - Start switch camera with constraints:', constraints);\n\n const videoSourceType = (constraints.video as LocalVideoStreamConstraints).videoSourceType === LocalVideoSourceType.CAMERA\n ? LocalVideoSourceType.CAMERA_FRONT\n : LocalVideoSourceType.CAMERA;\n\n trace.info(`getSwitchCameraConstraints - switch to videoSourceType: ${videoSourceType}`);\n\n (constraints.video as LocalVideoStreamConstraints).videoSourceType = videoSourceType;\n\n return constraints;\n }\n\n switchCamera(revertCameraWhenFailed?: boolean): Promise<any> {\n trace.info('switchCamera: start');\n\n const latestLocalMediaConstraints = cloneDeep(window.latestLocalMediaConstraints);\n const constraints = !revertCameraWhenFailed ? this.getSwitchCameraConstraints() : latestLocalMediaConstraints;\n\n if (!constraints) {\n throw new Error('switchCamera - unexpected use case constraints is null.');\n }\n\n return this._localStreamsManager.destroyUserMediaStream()\n .then(() => this._localStreamsManager.getUserMediaStream(constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n const stream = streamResult.mediaStream;\n\n trace.info('switchCamera: new stream: ', stream);\n\n return this.replaceStreamTracks(stream)\n .then(() => this.registerStreamResult(constraints, streamResult, true)\n .then(() => ({\n revertCameraWhenFailed,\n constraints,\n streamResult\n })));\n }))\n .catch((err) => {\n trace.error('switchCamera: Failed to switch camera: ', err);\n\n if (!revertCameraWhenFailed) {\n window.latestLocalMediaConstraints = latestLocalMediaConstraints;\n\n return this.switchCamera(true);\n }\n\n return Promise.reject('Failed to switch camera.');\n });\n }\n\n private replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void> {\n if (this._session!.instance) {\n return this._session!.instance.replaceStreamTracks(mediaStream);\n }\n\n return Promise.reject('replaceStreamTracks - session instance is not exists.');\n }\n\n protected registerStreamResult(constraints: Nullable<LocalMediaConstraints>, streamResult: MediaRequestSuccessResult, switchCamera?: boolean, addStreamType?: KnownMediaStream) {\n const regPromises: any[] = [];\n const isOpentok = this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK;\n\n if (streamResult.isNew) {\n trace.info('registerStreamResult: stream result from getUserMediaStream:', streamResult.mediaStream);\n streamResult.mediaStream.getTracks().forEach((mediaTrack) => {\n trace.info('registerStreamResult: stream result from mediaTrack:', mediaTrack);\n const streamType = addStreamType || (mediaTrack.kind === 'video'\n ? KnownMediaStream.USER_VIDEO_STREAM : KnownMediaStream.USER_AUDIO_STREAM);\n const newDedicatedStream = new TechseeMediaStream(mediaTrack, streamType, false);\n\n if (isOpentok) {\n regPromises.push(this.registerStream(newDedicatedStream));\n } else {\n regPromises.push(switchCamera ? this.registerTrack(newDedicatedStream) : this.registerLocalMediaStream(newDedicatedStream));\n }\n });\n }\n\n return Promise.all(regPromises);\n }\n\n disconnectFromMediaSession() {\n if (this._session && this._session.instance) {\n return this._session.instance.sessionDisconnect();\n }\n }\n\n get supportSwitchCameras() {\n return this._isIOS_13_orLater || this._localStreamsManager.groupedDevices.camerasCount > 1;\n }\n\n private _connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n this.serviceInitGuard();\n\n if (!this._session) {\n this._session = {params: cloneDeep(sessionParams)};\n this._session.connectPromise = new Promise((resolve, reject) => {\n const doAsyncReject = (error: any): void => {\n\n /*\n Reject with timeout, to change JS 'execution flow'.\n If timeout removed, session is assigned to null, before promise returns, and \"catch\" will not\n work on promise that is returned.\n */\n setTimeout(() => {\n this._session = null;\n reject(error);\n });\n };\n\n const sessionHandlers: MediaSessionHandlers = {\n onDisconnectedHandler: this.onSessionDisconnectHandler\n };\n const mediaSessionParams: MediaSessionParams & MediaSessionConfiguration = {\n ...sessionParams,\n peerConnectivityTimeoutSeconds: this._serviceOptions!.peerConnectivityTimeoutSeconds || DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS\n };\n\n trace.info('connectToSession', sessionParams);\n\n const allowedValues = values(MediaServiceType);\n\n switch (this._serviceOptions!.mediaServiceType) {\n case MediaServiceType.TURNSERVER:\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n break;\n case MediaServiceType.OPENTOK:\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n case MediaServiceType.MEDIASERVER:\n this._session!.instance = new MediaServerSession(mediaSessionParams, this._sessionStreamsManager);\n break;\n default:\n trace.error(`mediaServiceType '${this._serviceOptions!.mediaServiceType}' is not supported.`, {allowedValues});\n doAsyncReject(new Error('mediaServiceType is not supported'));\n }\n\n if (this._session && this._session.instance) {\n this._session!.instance.connect()\n .then(() => {\n trace.info('Session is connected');\n resolve();\n })\n .catch((error: any) => {\n trace.error('Failed to connect to session', error);\n doAsyncReject(error);\n });\n }\n\n });\n } else {\n trace.warn('Session connected already. Disconnect before connect to new session again');\n }\n\n //TODO - Alex: Change a structure of this function to be more safe\n //(when we return, there should not be case when session or promise is null)\n //To fix, need to return promise immediately, then execute process in setTimeout(fn, 0);\n return this._session!.connectPromise!;\n }\n\n //#endregion Media Streams Management\n\n //#region Session Management\n\n /*\n Creates instance of WebRTC session which connects to (signaling server), and begins to listen to WebRTC events.\n */\n connectToSession(sessionParams: MediaSessionParams): Promise<void> {\n return this._connectToSession(sessionParams);\n }\n\n /*\n Disconnects from WebRTC session.\n */\n disconnectFromSession(): Promise<void> {\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer);\n }\n\n /*\n Updates a credentials for turn server\n */\n updateSessionCredentials(credentials: MediaSessionIceCredentials): Promise<void> {\n trace.info('updateSessionCredentials');\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject('There no session to update credentials');\n }\n\n this._session!.params.credentials = cloneDeep(credentials);\n\n return Promise.resolve();\n }\n\n enableVoipDuringSession(): void {\n if (!this._isVoipEnabled && this.isLocalStreamInitialized) {\n this._initLocalStreamsPromise = null;\n }\n\n this._isVoipEnabled = true;\n }\n\n protected disconnectFromSessionInternal(reason: MediaSessionDisconnectReason): Promise<void> {\n if (!this._session) {\n return Promise.resolve();\n }\n\n trace.info('Disconnecting from session', reason);\n\n const lastSession = this._session;\n\n this._session = null;\n\n return Promise.all([\n lastSession.instance!.disconnect(),\n this.clearPublishers()\n ]).then(() => undefined);\n }\n\n protected getStatsForRemoteTrack(streamType: KnownMediaStream): Promise<RemoteTrackStats> {\n if (!this.sessionExistsGuard(false)) {\n return Promise.reject(new Error('Media session not started'));\n }\n\n const streamForStats = this.getRegisteredStreamByType(streamType);\n\n if (!streamForStats) {\n return Promise.reject(new Error('Stream for requested type was not found'));\n }\n\n return this._session!.instance!.getRemoteTrackStats(streamForStats.mediaTrack)\n .then((trackStats: RemoteTrackStats) => {\n traceStatsInfo(`MediaTrackStats for ${streamType}: ${JSON.stringify(trackStats)}`);\n\n return {\n trackId: streamForStats.mediaTrack.id,\n trackStats: trackStats\n };\n });\n }\n\n private onSessionDisconnectHandler(reason: MediaSessionDisconnectReason): void {\n trace.info('onSessionDisconnectHandler', reason);\n if (reason !== MediaSessionDisconnectReason.ForcedByConsumer) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n this.disconnectFromSessionInternal(reason).then(() => {\n const reconnectReasons = [\n MediaSessionDisconnectReason.InitiatorPeerReconnected,\n MediaSessionDisconnectReason.SignalingChannelDisconnect,\n MediaSessionDisconnectReason.PeerConnectionInterrupted,\n MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout\n ];\n\n if (lastParams && includes(reconnectReasons, reason) && this._autoReconnectEnabled) {\n this.reconnectToSession(lastParams);\n } else if (!this._autoReconnectEnabled) {\n trace.info('No reconnection- auto reconnect disabled');\n } else {\n trace.info('No params for reconnection to media session');\n }\n });\n } else {\n trace.info('Ignore Session disconnect event');\n }\n }\n\n setAutoReconnect(state: boolean) {\n this._autoReconnectEnabled = state;\n trace.info('_autoReconnectEnabled:', state);\n\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n if (lastParams) {\n trace.info('setAutoReconnect - reconnect to session');\n this.reconnectToSession(lastParams);\n }\n }\n }\n\n reconnectToSession(lastParams: MediaSessionParams) {\n trace.info('Reconnecting to media session, sessionParams:', lastParams);\n\n this._connectToSession(lastParams)\n .then(() => {\n trace.info('Media session reconnected');\n })\n .catch((error: any) => {\n trace.error('Error while reconnecting to media session', error);\n });\n }\n\n //#endregion Session Management\n\n //#region Utils\n\n getSnapshotFromKnownStream(sourceStream: KnownMediaStream, snapshotOptions?: SnapshotOptions): Promise<SnapshotResult> {\n return new Promise((resolve, reject) => {\n if (sourceStream !== KnownMediaStream.USER_VIDEO_STREAM &&\n sourceStream !== KnownMediaStream.USER_SCREEN_SHARE_STREAM) {\n trace.error('The requested stream is not video stream, and cannot be used for snapshot');\n reject(new Error('INCOMPATIBLE_STREAM_FOR_SNAPSHOT'));\n\n return;\n }\n\n const snapshotStream = this.getRegisteredStreamByType(sourceStream);\n\n if (!snapshotStream) {\n trace.error('Cannot make snapshot: The requested stream not exists yet.');\n reject(new Error('NO_REQUESTED_STREAM'));\n\n return;\n }\n\n getSnapshotFromMediaStream(snapshotStream.mediaStream, snapshotOptions)\n .then((imageData: any) => {\n const urlComponents = imageData.split(';base64,');\n const mimeType = urlComponents[0].split(':')[1];\n const bytes = atob(urlComponents[1]);\n const buffer = new ArrayBuffer(bytes.length);\n const rawData = new Uint8Array(buffer);\n\n for (let i = 0; i < bytes.length; i++) {\n rawData[i] = bytes.charCodeAt(i);\n }\n\n const blob = new Blob([rawData], {type: mimeType});\n const objectUrl = window.URL.createObjectURL(blob);\n\n trace.info('Snapshot created successfully');\n\n const result: SnapshotResult = {\n base64img: imageData,\n objectUrl: objectUrl,\n imageBlob: blob,\n mimeType: mimeType\n };\n\n resolve(result);\n })\n .catch((error: any) => {\n trace.error('Error creating snapshot', error);\n reject(error);\n });\n });\n }\n\n //Will clean streams, publishers and subscribers, but will not remove event listeners\n clearService(): Promise<void> {\n trace.info('MediaService clearing all resources');\n\n return this.disconnectFromSessionInternal(MediaSessionDisconnectReason.ForcedByConsumer)\n .then(this.clearSubscribers)\n .then(this.clearRegisteredStreams)\n .then(this._localStreamsManager.clearAllStreams)\n .then(() => {\n this._initLocalStreamsPromise = null;\n this._isVoipEnabled = false;\n }).then(() => undefined);\n }\n\n //Will clear the service and remove all event listeners\n dispose(): Promise<void> {\n return this.clearService().catch(() => undefined).then(() => {\n this._emitter.removeAllListeners();\n });\n }\n\n //#endregion\n\n //#region Protected Methods\n\n protected get subscribers(): Map<HTMLDivElement, TechseeMediaSubscriber> {\n return this._subscribers;\n }\n\n protected registerLocalMediaStream(tsMediaStream: TechseeMediaStream): Promise<void> {\n trace.info('Registering local stream', tsMediaStream.streamType);\n if (this._registeredStreams.has(tsMediaStream.streamType)) {\n return Promise.reject(new Error(`Stream ${tsMediaStream.streamType} already registered`));\n }\n\n return this.registerStream(tsMediaStream);\n }\n\n protected getRegisteredStreamByType(streamType: KnownMediaStream): Nullable<TechseeMediaStream> {\n return this._registeredStreams.get(streamType) || null;\n }\n\n protected changeEnableForKnownStream(streamType: KnownMediaStream, isPaused: boolean): void {\n const streamToChangeState = this.getRegisteredStreamByType(streamType);\n\n if (!streamToChangeState) {\n trace.warn('There no stream found to change enable state', streamType);\n } else {\n streamToChangeState.mediaTrack.enabled = !isPaused;\n\n if (streamToChangeState.isRemote && streamToChangeState.streamKind === KnownMediaStreamKind.Audio) {\n this.subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamType) {\n subscriber.muteSound(isPaused);\n }\n });\n }\n\n trace.info('Local stream enable state is changed', {streamType, isPaused});\n }\n }\n\n protected registerEventCallback(event: string, callback: (eventArgs?: any) => void): void {\n this._emitter.on(event, callback);\n }\n\n protected unregisterEventCallback(event: string, callback: (eventArgs?: any) => void) {\n this._emitter.off(event, callback);\n }\n\n protected emitEvent(event: string, eventArgs?: any): void {\n setTimeout(() => {\n this._emitter.emit(event, eventArgs);\n });\n }\n\n //#endregion Protected Methods\n\n //#region Private Methods\n\n private registerRemoteMediaTrack(remoteMediaTrack: RemoteMediaTrack): Promise<void> {\n trace.info(`Registering remote ${remoteMediaTrack.trackType} MediaStreamTrack`, remoteMediaTrack.mediaTrack);\n const currentStream = this._registeredStreams.get(remoteMediaTrack.trackType);\n\n if (currentStream && !currentStream.isRemote) {\n return Promise.reject(new Error('Cannot register remote stream with the same type as local stream'));\n }\n\n const newDedicatedStream = new TechseeMediaStream(remoteMediaTrack.mediaTrack, remoteMediaTrack.trackType, true);\n\n return this.registerStream(newDedicatedStream);\n }\n\n private registerTrack(mediaStream: TechseeMediaStream): Promise<void> {\n mediaStream.mediaTrack.onended = () =>\n this.unregisterTechseeMediaStream(mediaStream, MediaStreamUnregisterReason.NativeEvent);\n\n this._registeredStreams.set(mediaStream.streamType, mediaStream);\n\n trace.info(`TechseeMediaStream registered - ${mediaStream.streamType}`, TechseeMediaStream);\n\n return this.updateSubscribersWithNewStream(mediaStream).then(() => {\n const eventArgs: StreamCreatedEventArgs = {streamType: mediaStream.streamType};\n\n this.emitEvent(privateEvents.STREAM_CREATED, eventArgs);\n });\n }\n\n protected registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(() => this.registerTrack(mediaStream));\n }\n\n return this.registerTrack(mediaStream);\n }\n\n private unregisterTechseeMediaStream(streamToUnregister: TechseeMediaStream, reason: MediaStreamUnregisterReason): Promise<void> {\n const traceError = (error: any): void => {\n trace.warn('Unregister TechseeMediaStream error:', error);\n };\n\n const promises: any = [];\n\n trace.info('Unregister TechseeMediaStream: ', streamToUnregister.streamType);\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === streamToUnregister.streamType) {\n promises.push(subscriber.stopRendering().catch(traceError));\n }\n });\n\n if (!streamToUnregister.isRemote) {\n this._publishers.forEach((publisher: TechseeMediaPublisher) => {\n if (publisher.streamTypes.find((streamType) => streamType === streamToUnregister.streamType)) {\n if (this._session && this._session!.instance) {\n promises.push(this._session.instance.onMediaStreamDestroyed(publisher.destinationRole).catch(traceError));\n }\n }\n });\n }\n\n return Promise.all(promises)\n .then(() => {\n this._registeredStreams.delete(streamToUnregister.streamType);\n trace.info('Stream deleted', streamToUnregister.streamType);\n\n const eventArgs: StreamDestroyedEventArgs = {streamType: streamToUnregister.streamType, reason: reason};\n\n this.emitEvent(privateEvents.STREAM_DESTROYED, eventArgs);\n });\n }\n\n private unregisterRemoteMediaTrack(mediaTrack: MediaStreamTrack): Promise<void> {\n const promises: any = [];\n\n this._registeredStreams.forEach((registeredStream: TechseeMediaStream) => {\n if (registeredStream.mediaTrack.id === mediaTrack.id) {\n promises.push(this.unregisterTechseeMediaStream(registeredStream, MediaStreamUnregisterReason.ClosedRemotely));\n }\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private removePublisher(publisher: TechseeMediaPublisher): Promise<void> {\n this._publishers.delete(publisher.destinationRole);\n if (this._session && this._session.instance) {\n trace.info('Removing publisher', publisher.destinationRole);\n\n return this._session.instance.onMediaStreamDestroyed(publisher.destinationRole)\n .catch((error: any) => {\n trace.warn('Error while removing publisher', error);\n });\n }\n\n return Promise.resolve();\n }\n\n private getStreamForDestinationRole(destinationRole: SessionClientRole): Promise<Nullable<MediaStream>> {\n this.serviceInitGuard();\n\n if (this._publishers.has(destinationRole)) {\n trace.info(`Publisher for ${destinationRole} already exists`);\n\n return Promise.resolve(this._publishers.get(destinationRole)!.mediaStream);\n }\n\n if (!this._publisherPromises.get(destinationRole)) {\n trace.info(`Creating publisher for ${destinationRole}`);\n const publisherPromise = this.initLocalMediaStreams()\n .then(() => this.createMediaPublisher(destinationRole))\n .then((mediaPublisher: Nullable<TechseeMediaPublisher>) => {\n if (mediaPublisher) {\n this._publishers.set(destinationRole, mediaPublisher);\n }\n\n this._publisherPromises.delete(destinationRole);\n\n return mediaPublisher ? mediaPublisher.mediaStream : null;\n })\n .catch((ex: any) => {\n if (ex && ex.message === 'audioStreamFailed') {\n this._isVoipEnabled = false;\n\n return null;\n }\n\n this._publisherPromises.delete(destinationRole);\n throw ex;\n });\n\n this._publisherPromises.set(destinationRole, publisherPromise);\n } else {\n trace.info(`Create publisher promise for ${destinationRole} already exists`);\n }\n\n return this._publisherPromises.get(destinationRole)!;\n }\n\n private updateSubscribersWithNewStream(registeredStream: TechseeMediaStream): Promise<void> {\n trace.info(`Updating subscribers of ${registeredStream.streamType} with new stream`);\n\n if (this._subscribers.size === 0) {\n trace.warn(`No subscribers exists for the ${registeredStream.streamType}.`);\n } else {\n trace.info(`Total ${this._subscribers.size} subscribers exists, will check if rerender needed.`);\n }\n\n this._subscribers.forEach((subscriber) => {\n if (subscriber.streamType === registeredStream.streamType) {\n trace.info(`${registeredStream.streamType} rendering on subscriber`);\n subscriber.renderStream(registeredStream);\n }\n });\n\n return Promise.resolve();\n }\n\n private bindClassMethods(): void {\n this.updateSessionCredentials = this.updateSessionCredentials.bind(this);\n this.getStreamForDestinationRole = this.getStreamForDestinationRole.bind(this);\n this.registerRemoteMediaTrack = this.registerRemoteMediaTrack.bind(this);\n this.unregisterRemoteMediaTrack = this.unregisterRemoteMediaTrack.bind(this);\n this.createMediaPublisher = this.createMediaPublisher.bind(this);\n this.updateSubscribersWithNewStream = this.updateSubscribersWithNewStream.bind(this);\n this.initLocalMediaStreams = this.initLocalMediaStreams.bind(this);\n this.onSessionDisconnectHandler = this.onSessionDisconnectHandler.bind(this);\n this.clearService = this.clearService.bind(this);\n this.clearRegisteredStreams = this.clearRegisteredStreams.bind(this);\n this.clearPublishers = this.clearPublishers.bind(this);\n this.clearSubscribers = this.clearSubscribers.bind(this);\n this._connectToSession = this._connectToSession.bind(this);\n this.disconnectFromMediaSession = this.disconnectFromMediaSession.bind(this);\n }\n\n //region Cleanup\n\n private clearRegisteredStreams(): Promise<void> {\n trace.info('Clearing registered streams');\n const promises: any = [];\n\n this._registeredStreams.forEach((streamToUnregister: TechseeMediaStream) => {\n promises.push(this.unregisterTechseeMediaStream(streamToUnregister, MediaStreamUnregisterReason.ServiceCleanUp));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearSubscribers(): Promise<void> {\n trace.info('Clearing subscribers');\n const promises: any = [];\n\n this._subscribers.forEach((subscriber) => {\n promises.push(this.destroySubscriber(subscriber.container));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n private clearPublishers(): Promise<void> {\n trace.info('Clearing publishers');\n const promises: any = [];\n\n this._publishers.forEach((publisher) => {\n promises.push(this.removePublisher(publisher));\n });\n\n return Promise.all(promises).then(() => undefined);\n }\n\n //#endregion Cleanup\n\n //#region Simple Validation Methods\n\n protected serviceInitGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._serviceOptions, 'Media service is not initialized', shouldThrow);\n }\n\n protected sessionExistsGuard(shouldThrow = true): boolean {\n return throwableGuard(!!this._session, 'There no active session', shouldThrow);\n }\n\n //#endregion\n\n //#endregion Private Methods\n}\n"]}
@@ -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,EAAE,yBAAyB,EACjD,kBAAkB,EAClB,gBAAgB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAC,iBAAiB,EAAoB,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAAC,iBAAiB,EAAE,gBAAgB,EAAkB,MAAM,oBAAoB,CAAC;AAKxF,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,EAAE,EAAE,GAAG,CAAC;KACX;CACJ;AAED,UAAU,YAAY;IAClB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,aAAK,WAAW,GAAG,GAAG,CAAA;AAEtB,aAAK,YAAY,GAAG,GAAG,CAAA;AA+BvB,UAAU,cAAc;IACpB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC;CACpB;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;AAGD,qBAAa,cAAe,SAAQ,gBAAgB,CAAC,gBAAgB,CAAE,YAAW,aAAa;IAC3F,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAM;IACnC,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,QAAQ,CAA8B;IAE9C,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,OAAO,KAAK,SAAS,GAEpB;gBAEW,aAAa,EAAE,kBAAkB,GAAG,yBAAyB,EAAE,aAAa,EAAE,sBAAsB;IAiBhH,iBAAiB,IAAI,IAAI;IAMzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAkDxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB3B,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,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuB5E,OAAO,CAAC,wBAAwB;IA8EhC,OAAO,CAAC,0BAA0B;IAalC,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,gCAAgC;IAexC,OAAO,CAAC,qBAAqB;IA+C7B,OAAO,CAAC,iCAAiC;IAsBzC,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 {SessionClientRole, SessionClientType} from '../MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {IMediaSessionPeer, MediaSessionBase, recordingEvents} 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 OTConnection {\n connectionId: string;\n}\n\ntype OTPublisher = any\n\ntype OTSubscriber = any\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\ninterface OTStreamSource {\n videoSource: any;\n audioSource: any;\n}\n\ninterface OTPeerConnection extends IMediaSessionPeer {\n localStream: Nullable<OTStreamSource>;\n connection: OTConnection;\n publisher: Nullable<OTPublisher>;\n subscriber: Nullable<OTSubscriber>;\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 private _lastStreamEvents: any[] = [];\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 sessionDisconnect(): void {\n if (this._session) {\n return this._session.disconnect();\n }\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 this._lastStreamEvents = [];\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 this._session!.on('archiveStarted', (event: any) => {\n trace.info('archiveStarted event', event);\n this.emitEvent(recordingEvents.RECORD_STARTED, event);\n });\n\n this._session!.on('archiveStopped', (event: any) => {\n trace.info('archiveStopped event', event);\n this.emitEvent(recordingEvents.RECORD_STOPPED, event);\n });\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 this._lastStreamEvents = [];\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 replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void> {\n return Promise.reject(new Error('Not implemented'));\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 const destinationRole = JSON.parse(connection.data).clientRole;\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 (connection.connectionId !== this.ownPeerId || (!streams.videoSource && !streams.audioSource)) {\n trace.info('Peer connection without publisher was created', peerConnection);\n\n return peerConnection;\n }\n\n return this.createMediaPublisher(streams)\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 (connection.connectionId === this.ownPeerId) {\n trace.info('Before subscribe the event streams');\n\n /*\n When publisher connect after others participants,\n make sure the publisher will not loose their stream to subscribe\n */\n return this._lastStreamEvents.reduce((promiseChain: any, streamEvent: any) => promiseChain\n .then(() => this.streamCreatedHandler(streamEvent)), Promise.resolve())\n .then(() => {\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 }\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 || connection.connectionId === this.ownPeerId) {\n return;\n }\n\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n this._lastStreamEvents.push(event);\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((subscriber: OTSubscriber) => {\n peerConnection.subscriber = subscriber;\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): 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 };\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\n const {element} = event;\n\n element.muted = true;\n\n const peerConnection = this._peerConnections.get(remoteStream.connection.connectionId)!;\n const otMediaStream: MediaStream = element.srcObject;\n\n peerConnection.subscriber = subscriber;\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 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 peerConnection.subscriber = null;\n\n return Promise.resolve();\n\n /**\n REMARK- opentok provide the same media track id for all participates.\n when we unregister the media track we check by the id which is the same for all.\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 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,EAAC,iBAAiB,EAAoB,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAAC,iBAAiB,EAAE,gBAAgB,EAAkB,MAAM,oBAAoB,CAAC;AAKxF,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,EAAE,EAAE,GAAG,CAAC;KACX;CACJ;AAED,UAAU,YAAY;IAClB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,aAAK,WAAW,GAAG,GAAG,CAAA;AAEtB,aAAK,YAAY,GAAG,GAAG,CAAA;AA+BvB,UAAU,cAAc;IACpB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC;CACpB;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;AAGD,qBAAa,cAAe,SAAQ,gBAAgB,CAAC,gBAAgB,CAAE,YAAW,aAAa;IAC3F,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAM;IACnC,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,QAAQ,CAA8B;IAE9C,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,OAAO,KAAK,SAAS,GAEpB;gBAEW,aAAa,EAAE,kBAAkB,GAAG,yBAAyB,EAAE,aAAa,EAAE,sBAAsB;IAiBhH,iBAAiB,IAAI,IAAI;IAMzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAkDxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB3B,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,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuB5E,OAAO,CAAC,wBAAwB;IA8EhC,OAAO,CAAC,0BAA0B;IAalC,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,oBAAoB;IAmC5B,OAAO,CAAC,gCAAgC;IAexC,OAAO,CAAC,qBAAqB;IA+C7B,OAAO,CAAC,iCAAiC;IAsBzC,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 {SessionClientRole, SessionClientType} from '../MediaConstants';\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\nimport {IMediaSessionPeer, MediaSessionBase, recordingEvents} 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 OTConnection {\n connectionId: string;\n}\n\ntype OTPublisher = any\n\ntype OTSubscriber = any\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\ninterface OTStreamSource {\n videoSource: any;\n audioSource: any;\n}\n\ninterface OTPeerConnection extends IMediaSessionPeer {\n localStream: Nullable<OTStreamSource>;\n connection: OTConnection;\n publisher: Nullable<OTPublisher>;\n subscriber: Nullable<OTSubscriber>;\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 private _lastStreamEvents: any[] = [];\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 sessionDisconnect(): void {\n if (this._session) {\n return this._session.disconnect();\n }\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 this._lastStreamEvents = [];\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 this._session!.on('archiveStarted', (event: any) => {\n trace.info('archiveStarted event', event);\n this.emitEvent(recordingEvents.RECORD_STARTED, event);\n });\n\n this._session!.on('archiveStopped', (event: any) => {\n trace.info('archiveStopped event', event);\n this.emitEvent(recordingEvents.RECORD_STOPPED, event);\n });\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 this._lastStreamEvents = [];\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 replaceStreamTracks(mediaStream: Nullable<MediaStream>): Promise<void> {\n return Promise.reject(new Error('Not implemented'));\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 const destinationRole = JSON.parse(connection.data).clientRole;\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 (connection.connectionId !== this.ownPeerId || (!streams.videoSource && !streams.audioSource)) {\n trace.info('Peer connection without publisher was created', peerConnection);\n\n return peerConnection;\n }\n\n return this.createMediaPublisher(streams)\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 (connection.connectionId === this.ownPeerId) {\n trace.info('Before subscribe the event streams');\n\n /*\n When publisher connect after others participants,\n make sure the publisher will not loose their stream to subscribe\n */\n return this._lastStreamEvents.reduce((promiseChain: any, streamEvent: any) => promiseChain\n .then(() => this.streamCreatedHandler(streamEvent)), Promise.resolve())\n .then(() => {\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 }\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 || connection.connectionId === this.ownPeerId) {\n return;\n }\n\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n this._lastStreamEvents.push(event);\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((subscriber: OTSubscriber) => {\n peerConnection.subscriber = subscriber;\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): 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 };\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 otPublisher.on('streamCreated', (event: any) => {\n trace.info('publisher stream created @@@@@@@@@@@@@@@', event);\n trace.info('publisher stream created', otPublisher.stream.hasAudio);\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\n const {element} = event;\n\n element.muted = true;\n\n const peerConnection = this._peerConnections.get(remoteStream.connection.connectionId)!;\n const otMediaStream: MediaStream = element.srcObject;\n\n peerConnection.subscriber = subscriber;\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 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 peerConnection.subscriber = null;\n\n return Promise.resolve();\n\n /**\n REMARK- opentok provide the same media track id for all participates.\n when we unregister the media track we check by the id which is the same for all.\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 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"]}
@@ -327,6 +327,10 @@ var OpentokSession = /** @class */function (_super) {
327
327
  document.getElementById(otPublisher.id).style.display = 'none';
328
328
  event.element.muted = true;
329
329
  });
330
+ otPublisher.on('streamCreated', function (event) {
331
+ trace.info('publisher stream created @@@@@@@@@@@@@@@', event);
332
+ trace.info('publisher stream created', otPublisher.stream.hasAudio);
333
+ });
330
334
  resolve(otPublisher);
331
335
  });
332
336
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["MediaSession/SessionOpentok.js"],"names":["__extends","extendStatics","d","b","Object","setPrototypeOf","__proto__","Array","p","prototype","hasOwnProperty","call","__","constructor","create","defineProperty","exports","value","OpentokSession","MediaConstants_1","require","guards_1","MediaSessionBase_1","MediaTracer_1","trace","getMediaTracer","_super","sessionParams","streamFactory","_this","_lastStreamEvents","_session","_credentials","credentials","connect","bind","disconnect","onMediaStreamDestroyed","onMediaStreamRenewed","createStreamForPublishing","createMediaPublisher","connectionCreatedHandler","connectionDestroyedHandler","streamCreatedHandler","streamDestroyedHandler","get","sessionId","enumerable","configurable","connection","connectionId","sessionDisconnect","info","error","Promise","reject","resolve","handleError","off","_a","_sessionParams","apiKey","window","OTSESSION","OT","initSession","on","event","emitEvent","recordingEvents","RECORD_STARTED","RECORD_STOPPED","connectHandler","token","currentSession","_peerConnections","forEach","peerConnection","destroyPeerConnection","clientRole","promises","peerRole","push","destroyPublisherOnPeerConnection","all","then","undefined","mediaStream","replaceStreamTracks","Error","getRemoteTrackStats","mediaTrack","peerConnectionOfTrack","getPeerConnectionByMediaTrack","subscriber","getStats","stats","warn","trackId","id","trackStats","sessionExistsGuard","destinationRole","JSON","parse","data","streams","peerId","peerType","clientType","SessionClientType","INITIATOR","GUEST","publisher","remoteMediaTracks","localStream","connectionTimoutPrt","ownPeerId","videoSource","audioSource","set","reduce","promiseChain","streamEvent","publish","catch","has","stream","createMediaSubscriber","destroySubscriberOnPeerConnection","delete","_sessionStreamsManager","getMediaStreamForRole","getTracks","track","kind","SessionClientRole","USER","otParams","publishAudio","publishVideo","showControls","otPublisher","initPublisher","preventDefault","document","getElementById","style","display","element","muted","unpublish","remoteStream","subscribe","err","otMediaStream","srcObject","addRemoteTrackToPeer","registerPeersRemoteTracks","unsubscribe","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 (Object.prototype.hasOwnProperty.call(b, 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 });\nexports.OpentokSession = void 0;\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._lastStreamEvents = [];\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: false,\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: false,\n configurable: true\n });\n OpentokSession.prototype.sessionDisconnect = function () {\n if (this._session) {\n return this._session.disconnect();\n }\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._lastStreamEvents = [];\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 _this._session.on('archiveStarted', function (event) {\n trace.info('archiveStarted event', event);\n _this.emitEvent(MediaSessionBase_1.recordingEvents.RECORD_STARTED, event);\n });\n _this._session.on('archiveStopped', function (event) {\n trace.info('archiveStopped event', event);\n _this.emitEvent(MediaSessionBase_1.recordingEvents.RECORD_STOPPED, event);\n });\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 _this._lastStreamEvents = [];\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.replaceStreamTracks = function (mediaStream) {\n return Promise.reject(new Error('Not implemented'));\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 var destinationRole = JSON.parse(connection.data).clientRole;\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 (connection.connectionId !== _this.ownPeerId || (!streams.videoSource && !streams.audioSource)) {\n trace.info('Peer connection without publisher was created', peerConnection);\n return peerConnection;\n }\n return _this.createMediaPublisher(streams)\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 (connection.connectionId === _this.ownPeerId) {\n trace.info('Before subscribe the event streams');\n /*\n When publisher connect after others participants,\n make sure the publisher will not loose their stream to subscribe\n */\n return _this._lastStreamEvents.reduce(function (promiseChain, streamEvent) { return promiseChain\n .then(function () { return _this.streamCreatedHandler(streamEvent); }); }, Promise.resolve())\n .then(function () {\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 }\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 trace.info('streamCreatedHandler', event.stream);\n var stream = event.stream;\n var connection = (stream || { connection: null }).connection;\n if (!this.sessionExistsGuard(false) || !stream || connection.connectionId === this.ownPeerId) {\n return;\n }\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n this._lastStreamEvents.push(event);\n return;\n }\n var peerConnection = this._peerConnections.get(connection.connectionId);\n trace.info('Subscribing the stream');\n this.createMediaSubscriber(stream).then(function (subscriber) {\n peerConnection.subscriber = subscriber;\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) {\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 };\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 var element = event.element;\n element.muted = true;\n var peerConnection = _this._peerConnections.get(remoteStream.connection.connectionId);\n var otMediaStream = element.srcObject;\n peerConnection.subscriber = subscriber;\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 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 peerConnection.subscriber = null;\n return Promise.resolve();\n /**\n REMARK- opentok provide the same media track id for all participates.\n when we unregister the media track we check by the id which is the same for all.\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 //#endregion\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","prototype","hasOwnProperty","call","__","constructor","create","defineProperty","exports","value","OpentokSession","MediaConstants_1","require","guards_1","MediaSessionBase_1","MediaTracer_1","trace","getMediaTracer","_super","sessionParams","streamFactory","_this","_lastStreamEvents","_session","_credentials","credentials","connect","bind","disconnect","onMediaStreamDestroyed","onMediaStreamRenewed","createStreamForPublishing","createMediaPublisher","connectionCreatedHandler","connectionDestroyedHandler","streamCreatedHandler","streamDestroyedHandler","get","sessionId","enumerable","configurable","connection","connectionId","sessionDisconnect","info","error","Promise","reject","resolve","handleError","off","_a","_sessionParams","apiKey","window","OTSESSION","OT","initSession","on","event","emitEvent","recordingEvents","RECORD_STARTED","RECORD_STOPPED","connectHandler","token","currentSession","_peerConnections","forEach","peerConnection","destroyPeerConnection","clientRole","promises","peerRole","push","destroyPublisherOnPeerConnection","all","then","undefined","mediaStream","replaceStreamTracks","Error","getRemoteTrackStats","mediaTrack","peerConnectionOfTrack","getPeerConnectionByMediaTrack","subscriber","getStats","stats","warn","trackId","id","trackStats","sessionExistsGuard","destinationRole","JSON","parse","data","streams","peerId","peerType","clientType","SessionClientType","INITIATOR","GUEST","publisher","remoteMediaTracks","localStream","connectionTimoutPrt","ownPeerId","videoSource","audioSource","set","reduce","promiseChain","streamEvent","publish","catch","has","stream","createMediaSubscriber","destroySubscriberOnPeerConnection","delete","_sessionStreamsManager","getMediaStreamForRole","getTracks","track","kind","SessionClientRole","USER","otParams","publishAudio","publishVideo","showControls","otPublisher","initPublisher","preventDefault","document","getElementById","style","display","element","muted","hasAudio","unpublish","remoteStream","subscribe","err","otMediaStream","srcObject","addRemoteTrackToPeer","registerPeersRemoteTracks","unsubscribe","shouldThrow","connected","throwableGuard","MediaSessionBase"],"mappings":"AAAA;;AACA,IAAIA,YAAa,aAAQ,UAAKA,SAAd,IAA6B,YAAY;AACrD,QAAIC,iBAAgB,uBAAUC,CAAV,EAAaC,CAAb,EAAgB;AAChCF,yBAAgBG,OAAOC,cAAP,IACX,EAAEC,WAAW,EAAb,cAA6BC,KAA7B,IAAsC,UAAUL,CAAV,EAAaC,CAAb,EAAgB;AAAED,cAAEI,SAAF,GAAcH,CAAd;AAAkB,SAD/D,IAEZ,UAAUD,CAAV,EAAaC,CAAb,EAAgB;AAAE,iBAAK,IAAIK,CAAT,IAAcL,CAAd;AAAiB,oBAAIC,OAAOK,SAAP,CAAiBC,cAAjB,CAAgCC,IAAhC,CAAqCR,CAArC,EAAwCK,CAAxC,CAAJ,EAAgDN,EAAEM,CAAF,IAAOL,EAAEK,CAAF,CAAP;AAAjE;AAA+E,SAFrG;AAGA,eAAOP,eAAcC,CAAd,EAAiBC,CAAjB,CAAP;AACH,KALD;AAMA,WAAO,UAAUD,CAAV,EAAaC,CAAb,EAAgB;AACnBF,uBAAcC,CAAd,EAAiBC,CAAjB;AACA,iBAASS,EAAT,GAAc;AAAE,iBAAKC,WAAL,GAAmBX,CAAnB;AAAuB;AACvCA,UAAEO,SAAF,GAAcN,MAAM,IAAN,GAAaC,OAAOU,MAAP,CAAcX,CAAd,CAAb,IAAiCS,GAAGH,SAAH,GAAeN,EAAEM,SAAjB,EAA4B,IAAIG,EAAJ,EAA7D,CAAd;AACH,KAJD;AAKH,CAZ2C,EAA5C;AAaAR,OAAOW,cAAP,CAAsBC,OAAtB,EAA+B,YAA/B,EAA6C,EAAEC,OAAO,IAAT,EAA7C;AACAD,QAAQE,cAAR,GAAyB,KAAK,CAA9B;AACA,IAAIC,mBAAmBC,QAAQ,mBAAR,CAAvB;AACA,IAAIC,WAAWD,QAAQ,yCAAR,CAAf;AACA,IAAIE,qBAAqBF,QAAQ,oBAAR,CAAzB;AACA,IAAIG,gBAAgBH,QAAQ,2BAAR,CAApB;AACA,IAAII,QAAQD,cAAcE,cAAd,CAA6B,gBAA7B,CAAZ;AACA;AACA,IAAIP,iBAAiB,aAAe,UAAUQ,MAAV,EAAkB;AAClD1B,cAAUkB,cAAV,EAA0BQ,MAA1B;AACA,aAASR,cAAT,CAAwBS,aAAxB,EAAuCC,aAAvC,EAAsD;AAClD,YAAIC,QAAQH,OAAOf,IAAP,CAAY,IAAZ,EAAkBgB,aAAlB,EAAiCC,aAAjC,KAAmD,IAA/D;AACAC,cAAMC,iBAAN,GAA0B,EAA1B;AACAD,cAAME,QAAN,GAAiB,IAAjB;AACAF,cAAMG,YAAN,GAAqBL,cAAcM,WAAnC;AACAJ,cAAMK,OAAN,GAAgBL,MAAMK,OAAN,CAAcC,IAAd,CAAmBN,KAAnB,CAAhB;AACAA,cAAMO,UAAN,GAAmBP,MAAMO,UAAN,CAAiBD,IAAjB,CAAsBN,KAAtB,CAAnB;AACAA,cAAMQ,sBAAN,GAA+BR,MAAMQ,sBAAN,CAA6BF,IAA7B,CAAkCN,KAAlC,CAA/B;AACAA,cAAMS,oBAAN,GAA6BT,MAAMS,oBAAN,CAA2BH,IAA3B,CAAgCN,KAAhC,CAA7B;AACAA,cAAMU,yBAAN,GAAkCV,MAAMU,yBAAN,CAAgCJ,IAAhC,CAAqCN,KAArC,CAAlC;AACAA,cAAMW,oBAAN,GAA6BX,MAAMW,oBAAN,CAA2BL,IAA3B,CAAgCN,KAAhC,CAA7B;AACAA,cAAMY,wBAAN,GAAiCZ,MAAMY,wBAAN,CAA+BN,IAA/B,CAAoCN,KAApC,CAAjC;AACAA,cAAMa,0BAAN,GAAmCb,MAAMa,0BAAN,CAAiCP,IAAjC,CAAsCN,KAAtC,CAAnC;AACAA,cAAMc,oBAAN,GAA6Bd,MAAMc,oBAAN,CAA2BR,IAA3B,CAAgCN,KAAhC,CAA7B;AACAA,cAAMe,sBAAN,GAA+Bf,MAAMe,sBAAN,CAA6BT,IAA7B,CAAkCN,KAAlC,CAA/B;AACA,eAAOA,KAAP;AACH;AACDzB,WAAOW,cAAP,CAAsBG,eAAeT,SAArC,EAAgD,IAAhD,EAAsD;AAClDoC,aAAK,eAAY;AACb,mBAAO,KAAKd,QAAL,IAAiB,KAAKA,QAAL,CAAce,SAA/B,GAA2C,KAAKf,QAAL,CAAce,SAAzD,GAAqE,EAA5E;AACH,SAHiD;AAIlDC,oBAAY,KAJsC;AAKlDC,sBAAc;AALoC,KAAtD;AAOA5C,WAAOW,cAAP,CAAsBG,eAAeT,SAArC,EAAgD,WAAhD,EAA6D;AACzDoC,aAAK,eAAY;AACb,mBAAO,KAAKd,QAAL,IAAiB,KAAKA,QAAL,CAAckB,UAA/B,GAA4C,KAAKlB,QAAL,CAAckB,UAAd,CAAyBC,YAArE,GAAoF,EAA3F;AACH,SAHwD;AAIzDH,oBAAY,KAJ6C;AAKzDC,sBAAc;AAL2C,KAA7D;AAOA9B,mBAAeT,SAAf,CAAyB0C,iBAAzB,GAA6C,YAAY;AACrD,YAAI,KAAKpB,QAAT,EAAmB;AACf,mBAAO,KAAKA,QAAL,CAAcK,UAAd,EAAP;AACH;AACJ,KAJD;AAKAlB,mBAAeT,SAAf,CAAyByB,OAAzB,GAAmC,YAAY;AAC3C,YAAIL,QAAQ,IAAZ;AACAL,cAAM4B,IAAN,CAAW,oBAAX,EAAiC,KAAKpB,YAAtC;AACA,YAAI,KAAKD,QAAT,EAAmB;AACfP,kBAAM6B,KAAN,CAAY,wBAAZ;AACA,mBAAOC,QAAQC,MAAR,CAAe,2DAAf,CAAP;AACH;AACD,eAAO,IAAID,OAAJ,CAAY,UAAUE,OAAV,EAAmBD,MAAnB,EAA2B;AAC1C,gBAAIE,cAAc,SAAdA,WAAc,CAAUJ,KAAV,EAAiB;AAC/BxB,sBAAME,QAAN,IAAkBF,MAAME,QAAN,CAAe2B,GAAf,EAAlB;AACA7B,sBAAME,QAAN,GAAiB,IAAjB;AACAP,sBAAM6B,KAAN,CAAY,sBAAZ,EAAoCA,KAApC;AACAE,uBAAOF,KAAP;AACH,aALD;AAMA,gBAAIM,KAAK9B,MAAM+B,cAAN,CAAqB3B,WAA9B;AAAA,gBAA2C4B,SAASF,GAAGE,MAAvD;AAAA,gBAA+Df,YAAYa,GAAGb,SAA9E;AACAjB,kBAAME,QAAN,GAAiB+B,OAAOC,SAAP,GAAmBD,OAAOE,EAAP,CAAUC,WAAV,CAAsBJ,MAAtB,EAA8Bf,SAA9B,CAApC;AACAjB,kBAAMC,iBAAN,GAA0B,EAA1B;AACAD,kBAAME,QAAN,CAAemC,EAAf,CAAkB,mBAAlB,EAAuCrC,MAAMY,wBAA7C;AACAZ,kBAAME,QAAN,CAAemC,EAAf,CAAkB,qBAAlB,EAAyCrC,MAAMa,0BAA/C;AACAb,kBAAME,QAAN,CAAemC,EAAf,CAAkB,eAAlB,EAAmCrC,MAAMc,oBAAzC;AACAd,kBAAME,QAAN,CAAemC,EAAf,CAAkB,iBAAlB,EAAqCrC,MAAMe,sBAA3C;AACAf,kBAAME,QAAN,CAAemC,EAAf,CAAkB,gBAAlB,EAAoC,UAAUC,KAAV,EAAiB;AACjD3C,sBAAM4B,IAAN,CAAW,sBAAX,EAAmCe,KAAnC;AACAtC,sBAAMuC,SAAN,CAAgB9C,mBAAmB+C,eAAnB,CAAmCC,cAAnD,EAAmEH,KAAnE;AACH,aAHD;AAIAtC,kBAAME,QAAN,CAAemC,EAAf,CAAkB,gBAAlB,EAAoC,UAAUC,KAAV,EAAiB;AACjD3C,sBAAM4B,IAAN,CAAW,sBAAX,EAAmCe,KAAnC;AACAtC,sBAAMuC,SAAN,CAAgB9C,mBAAmB+C,eAAnB,CAAmCE,cAAnD,EAAmEJ,KAAnE;AACH,aAHD;AAIA,gBAAIK,iBAAiB,SAAjBA,cAAiB,CAAUnB,KAAV,EAAiB;AAClC,oBAAIA,KAAJ,EAAW;AACPI,gCAAYJ,KAAZ;AACA;AACH;AACDG;AACH,aAND;AAOA3B,kBAAME,QAAN,CAAeG,OAAf,CAAuBL,MAAMG,YAAN,CAAmByC,KAA1C,EAAiDD,cAAjD;AACH,SA9BM,CAAP;AA+BH,KAtCD;AAuCAtD,mBAAeT,SAAf,CAAyB2B,UAAzB,GAAsC,YAAY;AAC9C,YAAIP,QAAQ,IAAZ;AACA,eAAO,IAAIyB,OAAJ,CAAY,UAAUE,OAAV,EAAmB;AAClChC,kBAAM4B,IAAN,CAAW,4BAAX;AACA,gBAAIsB,iBAAiB7C,MAAME,QAA3B;AACAF,kBAAME,QAAN,GAAiB,IAAjB;AACAF,kBAAM8C,gBAAN,CAAuBC,OAAvB,CAA+B,UAAUC,cAAV,EAA0B;AACrDhD,sBAAMiD,qBAAN,CAA4BD,cAA5B;AACH,aAFD;AAGA,gBAAI,CAACH,cAAL,EAAqB;AACjBlD,sBAAM4B,IAAN,CAAW,+BAAX;AACAI;AACA;AACH;AACDkB,2BAAehB,GAAf;AACAgB,2BAAetC,UAAf;AACAP,kBAAMC,iBAAN,GAA0B,EAA1B;AACA0B;AACH,SAhBM,CAAP;AAiBH,KAnBD;AAoBAtC,mBAAeT,SAAf,CAAyB4B,sBAAzB,GAAkD,UAAU0C,UAAV,EAAsB;AACpE,YAAIlD,QAAQ,IAAZ;AACA,YAAImD,WAAW,EAAf;AACA,aAAKL,gBAAL,CAAsBC,OAAtB,CAA8B,UAAUC,cAAV,EAA0B;AACpD,gBAAIA,eAAeI,QAAf,KAA4BF,UAAhC,EAA4C;AACxCC,yBAASE,IAAT,CAAcrD,MAAMsD,gCAAN,CAAuCN,cAAvC,CAAd;AACH;AACJ,SAJD;AAKA,eAAOvB,QAAQ8B,GAAR,CAAYJ,QAAZ,EAAsBK,IAAtB,CAA2B,YAAY;AAAE,mBAAOC,SAAP;AAAmB,SAA5D,CAAP;AACH,KATD;AAUApE,mBAAeT,SAAf,CAAyB6B,oBAAzB,GAAgD,UAAUyC,UAAV,EAAsBQ,WAAtB,EAAmC;AAC/E;AACA,eAAOjC,QAAQE,OAAR,EAAP;AACH,KAHD;AAIAtC,mBAAeT,SAAf,CAAyB+E,mBAAzB,GAA+C,UAAUD,WAAV,EAAuB;AAClE,eAAOjC,QAAQC,MAAR,CAAe,IAAIkC,KAAJ,CAAU,iBAAV,CAAf,CAAP;AACH,KAFD;AAGAvE,mBAAeT,SAAf,CAAyBiF,mBAAzB,GAA+C,UAAUC,UAAV,EAAsB;AACjE,YAAI9D,QAAQ,IAAZ;AACA,eAAO,IAAIyB,OAAJ,CAAY,UAAUE,OAAV,EAAmBD,MAAnB,EAA2B;AAC1C,gBAAIqC,wBAAwB/D,MAAMgE,6BAAN,CAAoCF,UAApC,CAA5B;AACA,gBAAI,CAACC,qBAAD,IAA0B,CAACA,sBAAsBE,UAArD,EAAiE;AAC7DvC,uBAAO,IAAIkC,KAAJ,CAAU,kDAAV,CAAP;AACH;AACD;AACAG,kCAAsBE,UAAtB,CAAiCC,QAAjC,CAA0C,UAAU1C,KAAV,EAAiB2C,KAAjB,EAAwB;AAC9D,oBAAI3C,KAAJ,EAAW;AACP7B,0BAAMyE,IAAN,CAAW,qBAAX,EAAkC5C,KAAlC;AACAG,4BAAQ,EAAE0C,SAASP,WAAWQ,EAAtB,EAA0BC,YAAY,EAAtC,EAAR;AACA;AACH;AACD5C,wBAAQ,EAAE0C,SAASP,WAAWQ,EAAtB,EAA0BC,YAAYJ,KAAtC,EAAR;AACH,aAPD;AAQH,SAdM,CAAP;AAeH,KAjBD;AAkBA;AACA9E,mBAAeT,SAAf,CAAyBgC,wBAAzB,GAAoD,UAAU0B,KAAV,EAAiB;AACjE,YAAItC,QAAQ,IAAZ;AACAL,cAAM4B,IAAN,CAAW,0BAAX,EAAuCe,KAAvC;AACA,YAAIlB,aAAakB,MAAMlB,UAAvB;AACA,YAAI,CAAC,KAAKoD,kBAAL,CAAwB,KAAxB,CAAD,IAAmC,CAACpD,UAAxC,EAAoD;AAChD;AACH;AACD,YAAIqD,kBAAkBC,KAAKC,KAAL,CAAWvD,WAAWwD,IAAtB,EAA4B1B,UAAlD;AACA,aAAKxC,yBAAL,CAA+B+D,eAA/B,EACKjB,IADL,CACU,UAAUqB,OAAV,EAAmB;AACzB,gBAAI7B,iBAAiB;AACjB8B,wBAAQ1D,WAAWC,YADF;AAEjB+B,0BAAUqB,eAFO;AAGjBM,0BAAU/E,MAAM+B,cAAN,CAAqBiD,UAArB,KAAoC1F,iBAAiB2F,iBAAjB,CAAmCC,SAAvE,GACJ5F,iBAAiB2F,iBAAjB,CAAmCE,KAD/B,GACuC7F,iBAAiB2F,iBAAjB,CAAmCC,SAJnE;AAKjB9D,4BAAYA,UALK;AAMjBgE,2BAAW,IANM;AAOjBnB,4BAAY,IAPK;AAQjBoB,mCAAmB,EARF;AASjBC,6BAAaT,OATI;AAUjBU,qCAAqB;AAVJ,aAArB;AAYA,gBAAInE,WAAWC,YAAX,KAA4BrB,MAAMwF,SAAlC,IAAgD,CAACX,QAAQY,WAAT,IAAwB,CAACZ,QAAQa,WAArF,EAAmG;AAC/F/F,sBAAM4B,IAAN,CAAW,+CAAX,EAA4DyB,cAA5D;AACA,uBAAOA,cAAP;AACH;AACD,mBAAOhD,MAAMW,oBAAN,CAA2BkE,OAA3B,EACFrB,IADE,CACG,UAAU4B,SAAV,EAAqB;AAC3BpC,+BAAeoC,SAAf,GAA2BA,SAA3B;AACAzF,sBAAM4B,IAAN,CAAW,4CAAX,EAAyDyB,cAAzD;AACA,uBAAOA,cAAP;AACH,aALM,CAAP;AAMH,SAxBD,EAyBKQ,IAzBL,CAyBU,UAAUR,cAAV,EAA0B;AAChChD,kBAAM8C,gBAAN,CAAuB6C,GAAvB,CAA2B3C,eAAe8B,MAA1C,EAAkD9B,cAAlD;AACA,gBAAI5B,WAAWC,YAAX,KAA4BrB,MAAMwF,SAAtC,EAAiD;AAC7C7F,sBAAM4B,IAAN,CAAW,oCAAX;AACA;;;;AAIA,uBAAOvB,MAAMC,iBAAN,CAAwB2F,MAAxB,CAA+B,UAAUC,YAAV,EAAwBC,WAAxB,EAAqC;AAAE,2BAAOD,aAC/ErC,IAD+E,CAC1E,YAAY;AAAE,+BAAOxD,MAAMc,oBAAN,CAA2BgF,WAA3B,CAAP;AAAiD,qBADW,CAAP;AACA,iBADtE,EACwErE,QAAQE,OAAR,EADxE,EAEF6B,IAFE,CAEG,YAAY;AAClB,wBAAIR,eAAeoC,SAAnB,EAA8B;AAC1B,+BAAO,IAAI3D,OAAJ,CAAY,UAAUE,OAAV,EAAmBD,MAAnB,EAA2B;AAC1C/B,kCAAM4B,IAAN,CAAW,8CAAX;AACAvB,kCAAME,QAAN,CAAe6F,OAAf,CAAuB/C,eAAeoC,SAAtC,EAAiD,UAAU5D,KAAV,EAAiB;AAC9D,oCAAIA,KAAJ,EAAW;AACP7B,0CAAM6B,KAAN,CAAY,qCAAqCA,KAAjD;AACA,2CAAOE,OAAOF,KAAP,CAAP;AACH;AACD7B,sCAAM4B,IAAN,CAAW,mCAAX;AACA,uCAAOI,SAAP;AACH,6BAPD;AAQH,yBAVM,CAAP;AAWH;AACD,2BAAOF,QAAQE,OAAR,EAAP;AACH,iBAjBM,CAAP;AAkBH;AACJ,SApDD,EAqDKqE,KArDL,CAqDW,UAAUxE,KAAV,EAAiB;AACxB;AACA7B,kBAAM6B,KAAN,CAAY,yCAAyCA,KAArD;AACH,SAxDD;AAyDH,KAjED;AAkEAnC,mBAAeT,SAAf,CAAyBiC,0BAAzB,GAAsD,UAAUyB,KAAV,EAAiB;AACnE3C,cAAM4B,IAAN,CAAW,4BAAX,EAAyCe,KAAzC;AACA,YAAIlB,aAAakB,MAAMlB,UAAvB;AACA,YAAI,CAAC,KAAKoD,kBAAL,CAAwB,KAAxB,CAAD,IAAmC,CAACpD,UAAxC,EAAoD;AAChD;AACH;AACD,YAAI,KAAK0B,gBAAL,CAAsBmD,GAAtB,CAA0B7E,WAAWC,YAArC,CAAJ,EAAwD;AACpD,iBAAK4B,qBAAL,CAA2B,KAAKH,gBAAL,CAAsB9B,GAAtB,CAA0BI,WAAWC,YAArC,CAA3B;AACH;AACJ,KATD;AAUAhC,mBAAeT,SAAf,CAAyBkC,oBAAzB,GAAgD,UAAUwB,KAAV,EAAiB;AAC7D3C,cAAM4B,IAAN,CAAW,sBAAX,EAAmCe,MAAM4D,MAAzC;AACA,YAAIA,SAAS5D,MAAM4D,MAAnB;AACA,YAAI9E,aAAa,CAAC8E,UAAU,EAAE9E,YAAY,IAAd,EAAX,EAAiCA,UAAlD;AACA,YAAI,CAAC,KAAKoD,kBAAL,CAAwB,KAAxB,CAAD,IAAmC,CAAC0B,MAApC,IAA8C9E,WAAWC,YAAX,KAA4B,KAAKmE,SAAnF,EAA8F;AAC1F;AACH;AACD,YAAI,CAAC,KAAK1C,gBAAL,CAAsBmD,GAAtB,CAA0B7E,WAAWC,YAArC,CAAL,EAAyD;AACrD1B,kBAAMyE,IAAN,CAAW,wDAAX;AACA,iBAAKnE,iBAAL,CAAuBoD,IAAvB,CAA4Bf,KAA5B;AACA;AACH;AACD,YAAIU,iBAAiB,KAAKF,gBAAL,CAAsB9B,GAAtB,CAA0BI,WAAWC,YAArC,CAArB;AACA1B,cAAM4B,IAAN,CAAW,wBAAX;AACA,aAAK4E,qBAAL,CAA2BD,MAA3B,EAAmC1C,IAAnC,CAAwC,UAAUS,UAAV,EAAsB;AAC1DjB,2BAAeiB,UAAf,GAA4BA,UAA5B;AACH,SAFD;AAGH,KAjBD;AAkBA5E,mBAAeT,SAAf,CAAyBmC,sBAAzB,GAAkD,UAAUuB,KAAV,EAAiB;AAC/D3C,cAAM4B,IAAN,CAAW,wBAAX,EAAqCe,KAArC;AACA,YAAI4D,SAAS5D,MAAM4D,MAAnB;AACA,YAAI,CAAC,KAAK1B,kBAAL,CAAwB,KAAxB,CAAD,IAAmC,CAAC0B,MAAxC,EAAgD;AAC5C;AACH;AACD,YAAIlD,iBAAiB,KAAKF,gBAAL,CAAsB9B,GAAtB,CAA0BkF,OAAO9E,UAAP,CAAkBC,YAA5C,CAArB;AACA,YAAI,CAAC2B,cAAL,EAAqB;AACjBrD,kBAAMyE,IAAN,CAAW,6CAAX;AACA;AACH;AACD,aAAKgC,iCAAL,CAAuCpD,cAAvC,EAAuDgD,KAAvD,CAA6D,YAAY;AAAE,mBAAOvC,SAAP;AAAmB,SAA9F;AACH,KAZD;AAaA;AACA;AACApE,mBAAeT,SAAf,CAAyBqE,qBAAzB,GAAiD,UAAUD,cAAV,EAA0B;AACvE,aAAKF,gBAAL,CAAsBuD,MAAtB,CAA6BrD,eAAe8B,MAA5C;AACA,aAAKxB,gCAAL,CAAsCN,cAAtC,EAAsDgD,KAAtD,CAA4D,YAAY;AAAE,mBAAOvC,SAAP;AAAmB,SAA7F;AACA,aAAK2C,iCAAL,CAAuCpD,cAAvC,EAAuDgD,KAAvD,CAA6D,YAAY;AAAE,mBAAOvC,SAAP;AAAmB,SAA9F;AACH,KAJD;AAKA;AACA;AACApE,mBAAeT,SAAf,CAAyB8B,yBAAzB,GAAqD,UAAU+D,eAAV,EAA2B;AAC5E,YAAIzE,QAAQ,IAAZ;AACAL,cAAM4B,IAAN,CAAW,2BAAX;AACA,eAAO,KAAK+E,sBAAL,CAA4BC,qBAA5B,CAAkD9B,eAAlD,EACFjB,IADE,CACG,UAAUE,WAAV,EAAuB;AAC7B,gBAAIwC,SAAS,EAAET,aAAa,IAAf,EAAqBC,aAAa,IAAlC,EAAb;AACA,gBAAIhC,WAAJ,EAAiB;AACb/D,sBAAM4B,IAAN,CAAW,mCAAX,EAAgDmC,WAAhD;AACAA,4BAAY8C,SAAZ,GAAwBzD,OAAxB,CAAgC,UAAU0D,KAAV,EAAiB;AAC7C,wBAAIA,MAAMC,IAAN,KAAe,OAAnB,EAA4B;AACxBR,+BAAOT,WAAP,GAAqBgB,KAArB;AACH,qBAFD,MAGK,IAAIA,MAAMC,IAAN,KAAe,OAAnB,EAA4B;AAC7BR,+BAAOR,WAAP,GAAqBe,KAArB;AACH;AACJ,iBAPD;AAQH,aAVD,MAWK,IAAIzG,MAAM+B,cAAN,CAAqBmB,UAArB,KAAoC5D,iBAAiBqH,iBAAjB,CAAmCC,IAA3E,EAAiF;AAClFjH,sBAAMyE,IAAN,CAAW,6DAAX,EAA0EK,eAA1E;AACH;AACD,mBAAOyB,MAAP;AACH,SAlBM,CAAP;AAmBH,KAtBD;AAuBA7G,mBAAeT,SAAf,CAAyB+B,oBAAzB,GAAgD,UAAUuF,MAAV,EAAkB;AAC9D,YAAIW,WAAW;AACXpB,yBAAaS,OAAOT,WAAP,GAAqBS,OAAOT,WAA5B,GAA0C,KAD5C;AAEXC,yBAAaQ,OAAOR,WAAP,GAAqBQ,OAAOR,WAA5B,GAA0C,KAF5C;AAGXoB,0BAAc,CAAC,CAACZ,OAAOR,WAHZ;AAIXqB,0BAAc,CAAC,CAACb,OAAOT,WAJZ;AAKXuB,0BAAc;AALH,SAAf;AAOA,eAAO,IAAIvF,OAAJ,CAAY,UAAUE,OAAV,EAAmBD,MAAnB,EAA2B;AAC1C,gBAAIuF,cAAchF,OAAOE,EAAP,CAAU+E,aAAV,CAAwBzD,SAAxB,EAAmCoD,QAAnC,EAA6C,UAAUrF,KAAV,EAAiB;AAC5E,oBAAIA,KAAJ,EAAW;AACPE,2BAAOF,KAAP;AACA;AACH;AACDyF,4BAAY5E,EAAZ,CAAe,iBAAf,EAAkC,UAAUC,KAAV,EAAiB;AAC/C3C,0BAAM4B,IAAN,CAAW,4BAAX,EAAyCe,KAAzC;AACA;AACA;AACAA,0BAAM6E,cAAN;AACH,iBALD;AAMAF,4BAAY5E,EAAZ,CAAe,qBAAf,EAAsC,UAAUC,KAAV,EAAiB;AACnD8E,6BAASC,cAAT,CAAwBJ,YAAY3C,EAApC,EAAwCgD,KAAxC,CAA8CC,OAA9C,GAAwD,MAAxD;AACAjF,0BAAMkF,OAAN,CAAcC,KAAd,GAAsB,IAAtB;AACH,iBAHD;AAIAR,4BAAY5E,EAAZ,CAAe,eAAf,EAAgC,UAAUC,KAAV,EAAiB;AAC7C3C,0BAAM4B,IAAN,CAAW,0CAAX,EAAuDe,KAAvD;AACA3C,0BAAM4B,IAAN,CAAW,0BAAX,EAAuC0F,YAAYf,MAAZ,CAAmBwB,QAA1D;AACH,iBAHD;AAIA/F,wBAAQsF,WAAR;AACH,aApBiB,CAAlB;AAqBH,SAtBM,CAAP;AAuBH,KA/BD;AAgCA5H,mBAAeT,SAAf,CAAyB0E,gCAAzB,GAA4D,UAAUN,cAAV,EAA0B;AAClF,YAAI,KAAKwB,kBAAL,CAAwB,KAAxB,KAAkCxB,eAAeoC,SAArD,EAAgE;AAC5D,iBAAKlF,QAAL,CAAcyH,SAAd,CAAwB3E,eAAeoC,SAAvC;AACH;AACDpC,uBAAeoC,SAAf,GAA2B,IAA3B;AACApC,uBAAesC,WAAf,GAA6B,IAA7B;AACA,eAAO7D,QAAQE,OAAR,EAAP;AACH,KAPD;AAQA;AACA;AACAtC,mBAAeT,SAAf,CAAyBuH,qBAAzB,GAAiD,UAAUyB,YAAV,EAAwB;AACrE,YAAI5H,QAAQ,IAAZ;AACA,eAAO,IAAIyB,OAAJ,CAAY,UAAUE,OAAV,EAAmBD,MAAnB,EAA2B;AAC1C,gBAAI,CAAC1B,MAAMwE,kBAAN,CAAyB,KAAzB,CAAL,EAAsC;AAClC9C,uBAAO,oBAAP;AACA;AACH;AACD,gBAAIuC,aAAajE,MAAME,QAAN,CAAe2H,SAAf,CAAyBD,YAAzB,EAAuC,IAAvC,EAA6C,IAA7C,EAAmD,UAAUE,GAAV,EAAe;AAC/E,oBAAIA,GAAJ,EAAS;AACLnI,0BAAMyE,IAAN,CAAW,2BAAX,EAAwC0D,GAAxC;AACApG,2BAAOoG,GAAP;AACA;AACH;AACD7D,2BAAW5B,EAAX,CAAc,qBAAd,EAAqC,UAAUC,KAAV,EAAiB;AAClD8E,6BAASC,cAAT,CAAwBpD,WAAWK,EAAnC,EAAuCgD,KAAvC,CAA6CC,OAA7C,GAAuD,MAAvD;AACA,wBAAIC,UAAUlF,MAAMkF,OAApB;AACAA,4BAAQC,KAAR,GAAgB,IAAhB;AACA,wBAAIzE,iBAAiBhD,MAAM8C,gBAAN,CAAuB9B,GAAvB,CAA2B4G,aAAaxG,UAAb,CAAwBC,YAAnD,CAArB;AACA,wBAAI0G,gBAAgBP,QAAQQ,SAA5B;AACAhF,mCAAeiB,UAAf,GAA4BA,UAA5B;AACA,wBAAI,CAAC8D,aAAL,EAAoB;AAChBpI,8BAAMyE,IAAN,CAAW,2CAAX;AACA;AACH;AACDzE,0BAAM4B,IAAN,CAAW,iCAAX,EAA8CyB,eAAeiB,UAA7D,EAAyE8D,aAAzE;AACAA,kCAAcvB,SAAd,GAA0BzD,OAA1B,CAAkC,UAAUe,UAAV,EAAsB;AACpD9D,8BAAMiI,oBAAN,CAA2BjF,cAA3B,EAA2Cc,UAA3C;AACH,qBAFD;AAGA9D,0BAAMkI,yBAAN,CAAgClF,cAAhC;AACH,iBAhBD;AAiBArB,wBAAQsC,UAAR;AACH,aAxBgB,CAAjB;AAyBH,SA9BM,CAAP;AA+BH,KAjCD;AAkCA5E,mBAAeT,SAAf,CAAyBwH,iCAAzB,GAA6D,UAAUpD,cAAV,EAA0B;AACnF,YAAI,KAAKwB,kBAAL,CAAwB,KAAxB,KAAkCxB,eAAeiB,UAArD,EAAiE;AAC7D,iBAAK/D,QAAL,CAAciI,WAAd,CAA0BnF,eAAeiB,UAAzC;AACH;AACDjB,uBAAeiB,UAAf,GAA4B,IAA5B;AACA,eAAOxC,QAAQE,OAAR,EAAP;AACA;;;;AAIA;AACA;AACA;AACA;AACA;AACH,KAfD;AAgBA;AACAtC,mBAAeT,SAAf,CAAyB4F,kBAAzB,GAA8C,UAAU4D,WAAV,EAAuB;AACjE,YAAIA,gBAAgB,KAAK,CAAzB,EAA4B;AAAEA,0BAAc,IAAd;AAAqB;AACnD,YAAIC,YAAY,CAAC,EAAE,KAAKnI,QAAL,IAAiB,KAAKA,QAAL,CAAckB,UAA/B,IAA6C,KAAKlB,QAAL,CAAckB,UAAd,CAAyBC,YAAxE,CAAjB;AACA,eAAO7B,SAAS8I,cAAT,CAAwBD,SAAxB,EAAmC,iCAAnC,EAAsED,WAAtE,CAAP;AACH,KAJD;AAKA,WAAO/I,cAAP;AACH,CAnXmC,CAmXlCI,mBAAmB8I,gBAnXe,CAApC;AAoXApJ,QAAQE,cAAR,GAAyBA,cAAzB;;AAEA","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 (Object.prototype.hasOwnProperty.call(b, 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 });\nexports.OpentokSession = void 0;\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._lastStreamEvents = [];\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: false,\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: false,\n configurable: true\n });\n OpentokSession.prototype.sessionDisconnect = function () {\n if (this._session) {\n return this._session.disconnect();\n }\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._lastStreamEvents = [];\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 _this._session.on('archiveStarted', function (event) {\n trace.info('archiveStarted event', event);\n _this.emitEvent(MediaSessionBase_1.recordingEvents.RECORD_STARTED, event);\n });\n _this._session.on('archiveStopped', function (event) {\n trace.info('archiveStopped event', event);\n _this.emitEvent(MediaSessionBase_1.recordingEvents.RECORD_STOPPED, event);\n });\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 _this._lastStreamEvents = [];\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.replaceStreamTracks = function (mediaStream) {\n return Promise.reject(new Error('Not implemented'));\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 var destinationRole = JSON.parse(connection.data).clientRole;\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 (connection.connectionId !== _this.ownPeerId || (!streams.videoSource && !streams.audioSource)) {\n trace.info('Peer connection without publisher was created', peerConnection);\n return peerConnection;\n }\n return _this.createMediaPublisher(streams)\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 (connection.connectionId === _this.ownPeerId) {\n trace.info('Before subscribe the event streams');\n /*\n When publisher connect after others participants,\n make sure the publisher will not loose their stream to subscribe\n */\n return _this._lastStreamEvents.reduce(function (promiseChain, streamEvent) { return promiseChain\n .then(function () { return _this.streamCreatedHandler(streamEvent); }); }, Promise.resolve())\n .then(function () {\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 }\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 trace.info('streamCreatedHandler', event.stream);\n var stream = event.stream;\n var connection = (stream || { connection: null }).connection;\n if (!this.sessionExistsGuard(false) || !stream || connection.connectionId === this.ownPeerId) {\n return;\n }\n if (!this._peerConnections.has(connection.connectionId)) {\n trace.warn('Unexpected case, connection should exist on this stage');\n this._lastStreamEvents.push(event);\n return;\n }\n var peerConnection = this._peerConnections.get(connection.connectionId);\n trace.info('Subscribing the stream');\n this.createMediaSubscriber(stream).then(function (subscriber) {\n peerConnection.subscriber = subscriber;\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) {\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 };\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 otPublisher.on('streamCreated', function (event) {\n trace.info('publisher stream created @@@@@@@@@@@@@@@', event);\n trace.info('publisher stream created', otPublisher.stream.hasAudio);\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 var element = event.element;\n element.muted = true;\n var peerConnection = _this._peerConnections.get(remoteStream.connection.connectionId);\n var otMediaStream = element.srcObject;\n peerConnection.subscriber = subscriber;\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 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 peerConnection.subscriber = null;\n return Promise.resolve();\n /**\n REMARK- opentok provide the same media track id for all participates.\n when we unregister the media track we check by the id which is the same for all.\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 //#endregion\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techsee/techsee-media-service",
3
- "version": "999.15.10-alpha",
3
+ "version": "999.15.11-alpha",
4
4
  "description": "Techsee Media Service Client",
5
5
  "author": "TechSee",
6
6
  "main": "lib/index.js",