@techsee/techsee-media-service 999.7.6-sap → 999.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1 +1,89 @@
1
1
  # @techsee/techsee-media-service
2
+
3
+ **General Info**
4
+ This package is a starting point for creating Techsee Media SDK.
5
+ It contains basic framework for creating applications that utilizes media abilities.
6
+ It can be video and/or audio, with or without streaming over WebRTC protocol.
7
+ The framework currently supports: Video+Voip over Techsee Video (Turn) + IE Plugin and OpenTOK + IE Plugin.
8
+
9
+ You can see a very simple demo, which demonstrates how to create very basic MediaService implementation and use it for rendering video in react component.
10
+ Clone the repository, run `npm i`, then run `npm run demo`. The source code of the demo, can be found in ./demo folder.
11
+
12
+ ---
13
+
14
+ Some basic framework principles are described below, which will help you to work with media framework.
15
+
16
+ **Framework actors**
17
+ `MediaStreamTrack` - WebRTC object, the most basic media unit.
18
+ It can be video track (WebCam or ScreenShare), or audio track (Microphone stream).
19
+
20
+ `MediaStream` - WebRTC object that just organizes multiple `MediaStreamTracks` into single group.
21
+ The same `MediaStreamTrack` can belong to multiple `MediaStreams`.
22
+ In Techsee Media Service implementation, `MediaStream` can not contain more than one `MediaStreamTrack` and it used only because HTML5 video element works with `MediaStream` object.
23
+
24
+ `TechseeMediaStream` - This is a logical wrapper for WebRTC `MediaStreamTrack` and manages some additional information regarding the specific `MediaStreamTrack`.
25
+ For example this object has the type of the `MediaStreamTrack` (USER_VIDEO, AGENT_AUDIO...etc), has indication if track is local or remote.
26
+
27
+ `MediaSubscriber` - Techsee logical unit, that responsible to render `MediaStreams` into DOM.
28
+ In simple words this object creates video element in html, and renders `MediaStream` into it, when stream is ready.
29
+ `MediaSubscriber` is created for specific `MediaStream`, and whenever that stream created, the subscriber will start playing it.
30
+ The subscriber will notify it's consumers about the state changes through `onStateChanged` method.
31
+
32
+ `MediaSession` - Techsee logical unit, that used to stream video over WebRTC protocol. This object is responsible to handle all aspect of peers connectivity, including signaling, or third party service handling, like OpenTok.
33
+ When connection with peer established, this service registers `MediaStreamTracks` that received from peer and publishes local streams the connected peer.
34
+ Current implementation contains `MediaSessionBase` object, which have basic abstractions for all types of sessions. `SessionTurn` which handles the Techsee video sessions over TURN server.
35
+ `SessionOpentok` which handles the session connectivity over OpenTok third party service.
36
+
37
+ `RTCPeerConnection` - WebRTC object that represents connection with single peer, when SessionTurn is used for session. The same peer is used to send and/or receive media information.
38
+
39
+ `OTPeerConnection` - Design time only Typescript abstraction, which represents the connectivity with peer over OpenTok session.
40
+
41
+ `MediaPublisher` - Techsee logical unit which organizes local MediaStreamTracks into single MediaStream for publishing.
42
+
43
+ `MediaService` - Techsee logical unit. Represents the Facade of the framework and glues all of the above together.
44
+ Current implementation provides `MediaServiceBase` abstract object that contains all base logic and tools for displaying media locally or streaming it over WebRTC.
45
+ Each application should create its concrete `MediaService` and fill specific application logic gaps.
46
+ For example, currently exist `DashboardMediaService` in `techsee-app` and `MobileMediaService` in `techee-mobile`.
47
+
48
+ ---
49
+
50
+ **Consuming media service - High Level Flow**
51
+
52
+ In order to be able to use media service, first you need to create and instance of it and initialize it.
53
+ Depending on application, the constructor parameters can be different, but MediaServiceBase need to get the following in it's constructor:
54
+
55
+ `let mediaService = new SomeSpecificMediaService(environment, webRtcInfo);`
56
+
57
+ * **environment: IMediaEnvironment** - Service which will help understand in which environment the media service is currently running.
58
+ * **webRtcSupportInfo: ClientWebRtcInfo** - Information about WebRTC support on currently running device
59
+
60
+
61
+ When instance of specific MediaService was created, it should be initialized with `mediaService.initMediaService(serviceOptions)` method.
62
+ See `MediaServiceOptions` for options details. The method returns promise that resolved when initialization is done.
63
+ Initialization process, detects what media support currently running device have.
64
+ It checks if video can be played (natively or using IE plugins), if device have mic and so on...
65
+ After initialization promise resolved, the device information get be accessed through `mediaService.deviceSupportInfo`.
66
+
67
+ ***Note:*** Before promise returned by `mediaService.initMediaService(serviceOptions)` was not resolved, you not able to call `mediaService.initLocalMediaStreams(serviceOptions)`,
68
+ but you can create media subscribers if needed by calling `mediaService.createSubscriber(subscriberParams)`.
69
+
70
+ If audio stream is needed during the session, `mediaService.enableVoipDuringSession()` method should be called before initializing local streams.
71
+ After initialization was done and promise was resolved `mediaService.initLocalMediaStreams(serviceOptions)` can be called when video/audio streams should be created.
72
+ This method will start the local MediaStream creation process, and Camera/ScreenShare approval dialog will popup if there permission currently exists.
73
+ `initLocalMediaStreams` method is called by controller which displays local media stream on mobile, and in MediaSession, when `peerConnection ` is created.
74
+ `initLocalMediaStreams` synchronizes the calls and insures there only one instance of stream exists, and both sides receives the same one.
75
+
76
+ As mentioned before, `mediaService.createSubscriber(subscriberParams)` can be called even before initialization, so whenever you have the `div` container that should hold video element,
77
+ you can create the subscriber, by providing `KnownMediaStream` and `div` as container. Whenever the stream is ready, it will be played automatically by `MediaSubscriber`.
78
+ `MediaSubscriber` will create `video` element, add it to `div` container and will insure it plays.
79
+ `MediaSubscriber` has 'onStateChanged' method, which can be used for being notified on state changes of the subscriber.
80
+ It will notify when it starts or stops playing the video, or when it has been muted or unmuted. In event listener you can inspect properties of the subscriber to see in which state it currently exists.
81
+ In addition, `MediaSubscriber` has `onDispose` event handler, which will be called after `MediaSubscriber` was disposed and will never play video again, so references to it can be removed in order to avoid memory leaks.
82
+
83
+ **Connecting to WebRTC Media Session - High Level Flow**
84
+
85
+ After initialization promise was resolved, `mediaService.connectToSession(sessionParams)` can be called for connecting to Signaling server.
86
+ Connection to session can be done before media streams were initialized, if your use case requires it.
87
+ In case of of Techsee Live implementation, connection to session is done only when Terms and camera access approved by the user.
88
+ Depending on configuration passed in initialization method, `SessionTurn` or `SessionOpentok` will be created to manage the media session peers.
89
+ Whenever WebRTC peer will be connected, MediaSession instance will ask from `mediaService` for MediaStream that should be published to connected peer.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/LocalStreamManager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EAEH,qBAAqB,EACrB,2BAA2B,EAE3B,yBAAyB,EACzB,iBAAiB,EACpB,MAAM,kBAAkB,CAAC;AAS1B,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,qBAAqB,EAAE,QAAQ,CAAC,yBAAyB,CAAC,CAAC;QAC3D,wBAAwB,EAAE,QAAQ,CAAC,yBAAyB,CAAC,CAAC;KACjE;CACJ;AAcD,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,WAAW,CAAoB;gBAE3B,WAAW,EAAE,iBAAiB;IAO1C,kBAAkB,CAAC,WAAW,EAAE,qBAAqB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAyC1F,qBAAqB,CAAC,WAAW,EAAE,2BAA2B,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA0CnG,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAevC,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;IAe1C,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAShC,OAAO,CAAC,0BAA0B;IAqDlC,OAAO,CAAC,mCAAmC;IAsB3C,OAAO,CAAC,oBAAoB;IAsD5B,OAAO,CAAC,wBAAwB;CAoCnC","file":"LocalStreamManager.d.ts","sourcesContent":["import assign from 'lodash/assign';\nimport get from 'lodash/get';\n\nimport {Nullable} from '@techsee/techsee-common';\n\nimport {\n VideoStreamResolution,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n IMediaEnvironment\n} from './MediaContracts';\n\nimport {\n ConstraintType,\n DEFAULT_VIDEO_CONSTRAINT_TYPE,\n DEFAULT_VIDEO_RESOLUTION,\n MediaRequestErrorCode\n} from './MediaConstants';\n\ndeclare global {\n interface Window {\n latestUserMediaStream: Nullable<MediaRequestSuccessResult>;\n latestDesktopMediaStream: Nullable<MediaRequestSuccessResult>;\n }\n}\n\ndeclare const window: Window;\n\nwindow.latestUserMediaStream = null;\nlet userMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nwindow.latestDesktopMediaStream = null;\nlet desktopMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\n\nconst trace = getMediaTracer('LocalStreamManager');\n\nexport class LocalStreamManager {\n private environment: IMediaEnvironment;\n\n constructor(environment: IMediaEnvironment) {\n this.environment = environment;\n\n this.parseMediaErrorThenThrow = this.parseMediaErrorThenThrow.bind(this);\n this.clearAllStreams = this.clearAllStreams.bind(this);\n }\n\n getUserMediaStream(constraints: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n trace.info('getUserMediaStream', constraints);\n if (userMediaPromise) {\n return userMediaPromise;\n }\n\n if (window.latestUserMediaStream) {\n const videoTrack = window.latestUserMediaStream.mediaStream.getVideoTracks();\n\n if (videoTrack.length > 0 && videoTrack[0].readyState === 'live') {\n return Promise.resolve({...window.latestUserMediaStream, isNew: false});\n }\n this.destroyUserMediaStream();\n\n }\n\n\n userMediaPromise = new Promise((resolve, reject) => {\n const catchHandler = (err: any): void => {\n userMediaPromise = null;\n window.latestUserMediaStream = null;\n trace.error('getUserMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(false, constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n userMediaPromise = null;\n window.latestUserMediaStream = streamResult;\n resolve({...window.latestUserMediaStream, isNew: true});\n })\n .catch(catchHandler);\n } catch (err) {\n catchHandler(err);\n }\n });\n\n return userMediaPromise;\n }\n\n getDesktopMediaStream(constraints: LocalVideoStreamConstraints): Promise<MediaRequestSuccessResult> {\n if (desktopMediaPromise) {\n return desktopMediaPromise;\n }\n\n if (window.latestDesktopMediaStream) {\n const track = window.latestDesktopMediaStream.mediaStream.getVideoTracks();\n\n if (track.length > 0 && track[0].readyState === 'live') {\n //return Promise.resolve();\n return Promise.resolve({...window.latestDesktopMediaStream, isNew: false});\n }\n this.destroyDesktopMediaStream().catch((error: any) => {\n trace.error('Error destroying desktop media stream', error);\n });\n\n }\n\n desktopMediaPromise = new Promise((resolve, reject) => {\n const mediaCatchHandler = (err: any): void => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = null;\n trace.error('getDisplayMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(true, {video: constraints, audio: false})\n .then((streamResult: MediaRequestSuccessResult) => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = streamResult;\n\n resolve({...window.latestDesktopMediaStream, isNew: true});\n }).catch(mediaCatchHandler);\n } catch (err) {\n mediaCatchHandler(err);\n }\n });\n\n return desktopMediaPromise;\n }\n\n destroyUserMediaStream(): Promise<void> {\n const streamResult = window.latestUserMediaStream;\n\n window.latestUserMediaStream = null;\n userMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n destroyDesktopMediaStream(): Promise<void> {\n const streamResult = window.latestDesktopMediaStream;\n\n window.latestDesktopMediaStream = null;\n desktopMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n clearAllStreams(): Promise<void> {\n trace.info('LocalStreamManager Clearing all streams');\n\n return Promise.all([\n this.destroyDesktopMediaStream(),\n this.destroyUserMediaStream()\n ]).then(() => undefined);\n }\n\n private getHighestResolutionStream(isDesktopMedia: boolean, constraints?: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n const constraintsCandidates = this.getParsedConstraints(isDesktopMedia, constraints);\n\n if (constraintsCandidates.length === 0) {\n return Promise.reject(new Error('No constraints to retrieve the stream'));\n }\n\n return new Promise((resolve, reject) => {\n let lastSuccessfulResult: Nullable<MediaRequestSuccessResult> = null;\n let lastFailedResult: Nullable<MediaRequestFailResult> = null;\n let isStreamRequestFulfilled = false;\n\n (Promise as any).reduce(constraintsCandidates, (total: any, candidate: MediaStreamConstraints) => {\n if (isStreamRequestFulfilled) {\n return;\n }\n const constraintType = get(constraints, 'video.videoResolution.constraintType') || get(constraints, 'video.videoResolution[0].constraintType');\n\n return this.getStreamPromiseByParsedConstraints(candidate, isDesktopMedia)\n .then((stream: MediaStream) => {\n lastSuccessfulResult = {\n mediaStream: stream,\n constraint: candidate,\n constraintType,\n isNew: true\n };\n trace.info('getHighestResolutionStream Success', lastSuccessfulResult);\n isStreamRequestFulfilled = true;\n resolve(lastSuccessfulResult);\n })\n .catch((failResult: MediaRequestFailResult) => {\n lastFailedResult = failResult;\n if (lastFailedResult.errorCode === MediaRequestErrorCode.PermissionDenied) {\n isStreamRequestFulfilled = true;\n reject(lastFailedResult);\n }\n });\n }, 0)\n .then(() => {\n if (!isStreamRequestFulfilled) {\n if (!lastFailedResult) {\n lastFailedResult = {\n errorCode: MediaRequestErrorCode.GeneralError,\n message: 'Suitable stream cannot be created'\n };\n }\n reject(lastFailedResult);\n }\n });\n });\n }\n\n //@ts-ignore\n private getStreamPromiseByParsedConstraints(constraints: MediaStreamConstraints, isDesktopMedia: boolean): Promise<MediaStream> {\n let streamPromise = null;\n\n trace.info('getStreamPromiseByParsedConstraints', constraints);\n\n try {\n if (!isDesktopMedia) {\n streamPromise = navigator.mediaDevices.getUserMedia(constraints);\n } else {\n //@ts-ignore\n streamPromise = navigator.mediaDevices.getDisplayMedia\n //@ts-ignore\n ? navigator.mediaDevices.getDisplayMedia(constraints)\n : navigator.getDisplayMedia(constraints);\n }\n\n return streamPromise.catch(this.parseMediaErrorThenThrow);\n } catch (e) {\n this.parseMediaErrorThenThrow(e);\n }\n }\n\n private getParsedConstraints(isDesktopMedia: boolean, streamConstraints?: LocalMediaConstraints): MediaStreamConstraints[] {\n const constraintCandidates: MediaStreamConstraints[] = [];\n\n //TODO - Alex: need to understand which constraints not compatible with screen share\n if (isDesktopMedia) {\n return [{video: true}];\n }\n\n if (!streamConstraints) {\n return constraintCandidates;\n }\n\n const baseConstraints: MediaStreamConstraints = {};\n\n baseConstraints.audio = typeof streamConstraints.audio === 'boolean' ? streamConstraints.audio : undefined;\n\n if (!streamConstraints.video) {\n return [baseConstraints];\n }\n\n baseConstraints.video = {\n facingMode: 'environment',\n frameRate: {ideal: 15, max: 30}\n };\n\n const assignConstraint = (videoResolution: VideoStreamResolution): void => {\n const [width, height] = videoResolution.resolution.split('x');\n const constraintCandidate = assign({}, baseConstraints);\n\n constraintCandidate.video = assign({}, baseConstraints.video, {\n width: _constraintByType(width, videoResolution.constraintType),\n height: _constraintByType(height, videoResolution.constraintType)\n });\n constraintCandidates.push(constraintCandidate);\n };\n\n if ((baseConstraints.video && typeof streamConstraints.video === 'boolean') ||\n !((streamConstraints.video as LocalVideoStreamConstraints).videoResolution instanceof Array)) {\n\n const defaultResolution: VideoStreamResolution = {\n resolution: DEFAULT_VIDEO_RESOLUTION,\n constraintType: DEFAULT_VIDEO_CONSTRAINT_TYPE\n };\n\n assignConstraint(defaultResolution);\n\n return constraintCandidates;\n }\n\n (streamConstraints.video as any).videoResolution.forEach(assignConstraint);\n\n return constraintCandidates;\n }\n\n private parseMediaErrorThenThrow(error: any): MediaRequestFailResult {\n trace.error('Get media stream error.', error);\n const errorName = error && error.name ? error.name : '';\n\n const permissionErrorNames = [\n 'NotAllowedError',\n 'PermissionDismissedError',\n 'PermissionDeniedError'\n\n ];\n\n // const generalError = [\n // 'NotFoundError',\n // 'DevicesNotFoundError',\n // 'NotReadableError',\n // 'TrackStartError'\n // ];\n\n const constraintsErrors = [\n 'OverconstrainedError',\n 'ConstraintNotSatisfiedError'\n\n ];\n\n const isErrorOfType = (errorsArr: string[]) => errorsArr.filter((err: string) => err === errorName).length > 0;\n\n if (isErrorOfType(permissionErrorNames)) {\n throw {errorCode: MediaRequestErrorCode.PermissionDenied, message: errorName};\n }\n\n if (isErrorOfType(constraintsErrors)) {\n throw {errorCode: MediaRequestErrorCode.Overconstrained, message: errorName};\n }\n\n throw {errorCode: MediaRequestErrorCode.GeneralError, message: errorName};\n }\n}\n\nfunction _constraintByType(value: any, type: ConstraintType): any {\n switch (type) {\n case ConstraintType.MIN:\n return {min: value};\n default:\n return {ideal: value};\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/LocalStreamManager.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EAEH,qBAAqB,EACrB,2BAA2B,EAE3B,yBAAyB,EACzB,iBAAiB,EACpB,MAAM,kBAAkB,CAAC;AAS1B,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,qBAAqB,EAAE,QAAQ,CAAC,yBAAyB,CAAC,CAAC;QAC3D,wBAAwB,EAAE,QAAQ,CAAC,yBAAyB,CAAC,CAAC;KACjE;CACJ;AAcD,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,WAAW,CAAoB;gBAE3B,WAAW,EAAE,iBAAiB;IAO1C,kBAAkB,CAAC,WAAW,EAAE,qBAAqB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAyC1F,qBAAqB,CAAC,WAAW,EAAE,2BAA2B,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA0CnG,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAevC,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;IAe1C,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAShC,OAAO,CAAC,0BAA0B;IAqDlC,OAAO,CAAC,mCAAmC;IAsB3C,OAAO,CAAC,oBAAoB;IAsD5B,OAAO,CAAC,wBAAwB;CAoCnC","file":"LocalStreamManager.d.ts","sourcesContent":["import assign from 'lodash/assign';\nimport get from 'lodash/get';\nimport {Promise} from 'bluebird';\n\nimport {Nullable} from '@techsee/techsee-common';\n\nimport {\n VideoStreamResolution,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n IMediaEnvironment\n} from './MediaContracts';\n\nimport {\n ConstraintType,\n DEFAULT_VIDEO_CONSTRAINT_TYPE,\n DEFAULT_VIDEO_RESOLUTION,\n MediaRequestErrorCode\n} from './MediaConstants';\n\ndeclare global {\n interface Window {\n latestUserMediaStream: Nullable<MediaRequestSuccessResult>;\n latestDesktopMediaStream: Nullable<MediaRequestSuccessResult>;\n }\n}\n\ndeclare const window: Window;\n\nwindow.latestUserMediaStream = null;\nlet userMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nwindow.latestDesktopMediaStream = null;\nlet desktopMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\n\nconst trace = getMediaTracer('LocalStreamManager');\n\nexport class LocalStreamManager {\n private environment: IMediaEnvironment;\n\n constructor(environment: IMediaEnvironment) {\n this.environment = environment;\n\n this.parseMediaErrorThenThrow = this.parseMediaErrorThenThrow.bind(this);\n this.clearAllStreams = this.clearAllStreams.bind(this);\n }\n\n getUserMediaStream(constraints: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n trace.info('getUserMediaStream', constraints);\n if (userMediaPromise) {\n return userMediaPromise;\n }\n\n if (window.latestUserMediaStream) {\n const videoTrack = window.latestUserMediaStream.mediaStream.getVideoTracks();\n\n if (videoTrack.length > 0 && videoTrack[0].readyState === 'live') {\n return Promise.resolve({...window.latestUserMediaStream, isNew: false});\n }\n this.destroyUserMediaStream();\n\n }\n\n\n userMediaPromise = new Promise((resolve, reject) => {\n const catchHandler = (err: any): void => {\n userMediaPromise = null;\n window.latestUserMediaStream = null;\n trace.error('getUserMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(false, constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n userMediaPromise = null;\n window.latestUserMediaStream = streamResult;\n resolve({...window.latestUserMediaStream, isNew: true});\n })\n .catch(catchHandler);\n } catch (err) {\n catchHandler(err);\n }\n });\n\n return userMediaPromise;\n }\n\n getDesktopMediaStream(constraints: LocalVideoStreamConstraints): Promise<MediaRequestSuccessResult> {\n if (desktopMediaPromise) {\n return desktopMediaPromise;\n }\n\n if (window.latestDesktopMediaStream) {\n const track = window.latestDesktopMediaStream.mediaStream.getVideoTracks();\n\n if (track.length > 0 && track[0].readyState === 'live') {\n //return Promise.resolve();\n return Promise.resolve({...window.latestDesktopMediaStream, isNew: false});\n }\n this.destroyDesktopMediaStream().catch((error: any) => {\n trace.error('Error destroying desktop media stream', error);\n });\n\n }\n\n desktopMediaPromise = new Promise((resolve, reject) => {\n const mediaCatchHandler = (err: any): void => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = null;\n trace.error('getDisplayMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(true, {video: constraints, audio: false})\n .then((streamResult: MediaRequestSuccessResult) => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = streamResult;\n\n resolve({...window.latestDesktopMediaStream, isNew: true});\n }).catch(mediaCatchHandler);\n } catch (err) {\n mediaCatchHandler(err);\n }\n });\n\n return desktopMediaPromise;\n }\n\n destroyUserMediaStream(): Promise<void> {\n const streamResult = window.latestUserMediaStream;\n\n window.latestUserMediaStream = null;\n userMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n destroyDesktopMediaStream(): Promise<void> {\n const streamResult = window.latestDesktopMediaStream;\n\n window.latestDesktopMediaStream = null;\n desktopMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n clearAllStreams(): Promise<void> {\n trace.info('LocalStreamManager Clearing all streams');\n\n return Promise.all([\n this.destroyDesktopMediaStream(),\n this.destroyUserMediaStream()\n ]).then(() => undefined);\n }\n\n private getHighestResolutionStream(isDesktopMedia: boolean, constraints?: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n const constraintsCandidates = this.getParsedConstraints(isDesktopMedia, constraints);\n\n if (constraintsCandidates.length === 0) {\n return Promise.reject(new Error('No constraints to retrieve the stream'));\n }\n\n return new Promise((resolve, reject) => {\n let lastSuccessfulResult: Nullable<MediaRequestSuccessResult> = null;\n let lastFailedResult: Nullable<MediaRequestFailResult> = null;\n let isStreamRequestFulfilled = false;\n\n (Promise as any).reduce(constraintsCandidates, (total: any, candidate: MediaStreamConstraints) => {\n if (isStreamRequestFulfilled) {\n return;\n }\n const constraintType = get(constraints, 'video.videoResolution.constraintType') || get(constraints, 'video.videoResolution[0].constraintType');\n\n return this.getStreamPromiseByParsedConstraints(candidate, isDesktopMedia)\n .then((stream: MediaStream) => {\n lastSuccessfulResult = {\n mediaStream: stream,\n constraint: candidate,\n constraintType,\n isNew: true\n };\n trace.info('getHighestResolutionStream Success', lastSuccessfulResult);\n isStreamRequestFulfilled = true;\n resolve(lastSuccessfulResult);\n })\n .catch((failResult: MediaRequestFailResult) => {\n lastFailedResult = failResult;\n if (lastFailedResult.errorCode === MediaRequestErrorCode.PermissionDenied) {\n isStreamRequestFulfilled = true;\n reject(lastFailedResult);\n }\n });\n }, 0)\n .then(() => {\n if (!isStreamRequestFulfilled) {\n if (!lastFailedResult) {\n lastFailedResult = {\n errorCode: MediaRequestErrorCode.GeneralError,\n message: 'Suitable stream cannot be created'\n };\n }\n reject(lastFailedResult);\n }\n });\n });\n }\n\n //@ts-ignore\n private getStreamPromiseByParsedConstraints(constraints: MediaStreamConstraints, isDesktopMedia: boolean): Promise<MediaStream> {\n let streamPromise = null;\n\n trace.info('getStreamPromiseByParsedConstraints', constraints);\n\n try {\n if (!isDesktopMedia) {\n streamPromise = navigator.mediaDevices.getUserMedia(constraints);\n } else {\n //@ts-ignore\n streamPromise = navigator.mediaDevices.getDisplayMedia\n //@ts-ignore\n ? navigator.mediaDevices.getDisplayMedia(constraints)\n : navigator.getDisplayMedia(constraints);\n }\n\n return streamPromise.catch(this.parseMediaErrorThenThrow);\n } catch (e) {\n this.parseMediaErrorThenThrow(e);\n }\n }\n\n private getParsedConstraints(isDesktopMedia: boolean, streamConstraints?: LocalMediaConstraints): MediaStreamConstraints[] {\n const constraintCandidates: MediaStreamConstraints[] = [];\n\n //TODO - Alex: need to understand which constraints not compatible with screen share\n if (isDesktopMedia) {\n return [{video: true}];\n }\n\n if (!streamConstraints) {\n return constraintCandidates;\n }\n\n const baseConstraints: MediaStreamConstraints = {};\n\n baseConstraints.audio = typeof streamConstraints.audio === 'boolean' ? streamConstraints.audio : undefined;\n\n if (!streamConstraints.video) {\n return [baseConstraints];\n }\n\n baseConstraints.video = {\n facingMode: 'environment',\n frameRate: {ideal: 15, max: 30}\n };\n\n const assignConstraint = (videoResolution: VideoStreamResolution): void => {\n const [width, height] = videoResolution.resolution.split('x');\n const constraintCandidate = assign({}, baseConstraints);\n\n constraintCandidate.video = assign({}, baseConstraints.video, {\n width: _constraintByType(width, videoResolution.constraintType),\n height: _constraintByType(height, videoResolution.constraintType)\n });\n constraintCandidates.push(constraintCandidate);\n };\n\n if ((baseConstraints.video && typeof streamConstraints.video === 'boolean') ||\n !((streamConstraints.video as LocalVideoStreamConstraints).videoResolution instanceof Array)) {\n\n const defaultResolution: VideoStreamResolution = {\n resolution: DEFAULT_VIDEO_RESOLUTION,\n constraintType: DEFAULT_VIDEO_CONSTRAINT_TYPE\n };\n\n assignConstraint(defaultResolution);\n\n return constraintCandidates;\n }\n\n (streamConstraints.video as any).videoResolution.forEach(assignConstraint);\n\n return constraintCandidates;\n }\n\n private parseMediaErrorThenThrow(error: any): MediaRequestFailResult {\n trace.error('Get media stream error.', error);\n const errorName = error && error.name ? error.name : '';\n\n const permissionErrorNames = [\n 'NotAllowedError',\n 'PermissionDismissedError',\n 'PermissionDeniedError'\n\n ];\n\n // const generalError = [\n // 'NotFoundError',\n // 'DevicesNotFoundError',\n // 'NotReadableError',\n // 'TrackStartError'\n // ];\n\n const constraintsErrors = [\n 'OverconstrainedError',\n 'ConstraintNotSatisfiedError'\n\n ];\n\n const isErrorOfType = (errorsArr: string[]) => errorsArr.filter((err: string) => err === errorName).length > 0;\n\n if (isErrorOfType(permissionErrorNames)) {\n throw {errorCode: MediaRequestErrorCode.PermissionDenied, message: errorName};\n }\n\n if (isErrorOfType(constraintsErrors)) {\n throw {errorCode: MediaRequestErrorCode.Overconstrained, message: errorName};\n }\n\n throw {errorCode: MediaRequestErrorCode.GeneralError, message: errorName};\n }\n}\n\nfunction _constraintByType(value: any, type: ConstraintType): any {\n switch (type) {\n case ConstraintType.MIN:\n return {min: value};\n default:\n return {ideal: value};\n }\n}\n"]}
@@ -18,6 +18,7 @@ var __importDefault = undefined && undefined.__importDefault || function (mod) {
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  var assign_1 = __importDefault(require("lodash/assign"));
20
20
  var get_1 = __importDefault(require("lodash/get"));
21
+ var bluebird_1 = require("bluebird");
21
22
  var MediaConstants_1 = require("./MediaConstants");
22
23
  window.latestUserMediaStream = null;
23
24
  var userMediaPromise = null;
@@ -40,11 +41,11 @@ var LocalStreamManager = /** @class */function () {
40
41
  if (window.latestUserMediaStream) {
41
42
  var videoTrack = window.latestUserMediaStream.mediaStream.getVideoTracks();
42
43
  if (videoTrack.length > 0 && videoTrack[0].readyState === 'live') {
43
- return Promise.resolve(__assign({}, window.latestUserMediaStream, { isNew: false }));
44
+ return bluebird_1.Promise.resolve(__assign({}, window.latestUserMediaStream, { isNew: false }));
44
45
  }
45
46
  this.destroyUserMediaStream();
46
47
  }
47
- userMediaPromise = new Promise(function (resolve, reject) {
48
+ userMediaPromise = new bluebird_1.Promise(function (resolve, reject) {
48
49
  var catchHandler = function catchHandler(err) {
49
50
  userMediaPromise = null;
50
51
  window.latestUserMediaStream = null;
@@ -72,13 +73,13 @@ var LocalStreamManager = /** @class */function () {
72
73
  var track = window.latestDesktopMediaStream.mediaStream.getVideoTracks();
73
74
  if (track.length > 0 && track[0].readyState === 'live') {
74
75
  //return Promise.resolve();
75
- return Promise.resolve(__assign({}, window.latestDesktopMediaStream, { isNew: false }));
76
+ return bluebird_1.Promise.resolve(__assign({}, window.latestDesktopMediaStream, { isNew: false }));
76
77
  }
77
78
  this.destroyDesktopMediaStream().catch(function (error) {
78
79
  trace.error('Error destroying desktop media stream', error);
79
80
  });
80
81
  }
81
- desktopMediaPromise = new Promise(function (resolve, reject) {
82
+ desktopMediaPromise = new bluebird_1.Promise(function (resolve, reject) {
82
83
  var mediaCatchHandler = function mediaCatchHandler(err) {
83
84
  desktopMediaPromise = null;
84
85
  window.latestDesktopMediaStream = null;
@@ -106,7 +107,7 @@ var LocalStreamManager = /** @class */function () {
106
107
  track.stop();
107
108
  });
108
109
  }
109
- return Promise.resolve();
110
+ return bluebird_1.Promise.resolve();
110
111
  };
111
112
  LocalStreamManager.prototype.destroyDesktopMediaStream = function () {
112
113
  var streamResult = window.latestDesktopMediaStream;
@@ -117,11 +118,11 @@ var LocalStreamManager = /** @class */function () {
117
118
  track.stop();
118
119
  });
119
120
  }
120
- return Promise.resolve();
121
+ return bluebird_1.Promise.resolve();
121
122
  };
122
123
  LocalStreamManager.prototype.clearAllStreams = function () {
123
124
  trace.info('LocalStreamManager Clearing all streams');
124
- return Promise.all([this.destroyDesktopMediaStream(), this.destroyUserMediaStream()]).then(function () {
125
+ return bluebird_1.Promise.all([this.destroyDesktopMediaStream(), this.destroyUserMediaStream()]).then(function () {
125
126
  return undefined;
126
127
  });
127
128
  };
@@ -129,13 +130,13 @@ var LocalStreamManager = /** @class */function () {
129
130
  var _this = this;
130
131
  var constraintsCandidates = this.getParsedConstraints(isDesktopMedia, constraints);
131
132
  if (constraintsCandidates.length === 0) {
132
- return Promise.reject(new Error('No constraints to retrieve the stream'));
133
+ return bluebird_1.Promise.reject(new Error('No constraints to retrieve the stream'));
133
134
  }
134
- return new Promise(function (resolve, reject) {
135
+ return new bluebird_1.Promise(function (resolve, reject) {
135
136
  var lastSuccessfulResult = null;
136
137
  var lastFailedResult = null;
137
138
  var isStreamRequestFulfilled = false;
138
- Promise.reduce(constraintsCandidates, function (total, candidate) {
139
+ bluebird_1.Promise.reduce(constraintsCandidates, function (total, candidate) {
139
140
  if (isStreamRequestFulfilled) {
140
141
  return;
141
142
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["LocalStreamManager.js","../src/LocalStreamManager.ts"],"names":["__assign","Object","assign","t","s","i","n","arguments","length","p","prototype","hasOwnProperty","call","apply","__importDefault","mod","__esModule","defineProperty","exports","value","assign_1","require","get_1","MediaConstants_1","window","latestUserMediaStream","userMediaPromise","latestDesktopMediaStream","desktopMediaPromise","MediaTracer_1","trace","getMediaTracer","LocalStreamManager","environment","parseMediaErrorThenThrow","bind","clearAllStreams","getUserMediaStream","constraints","_this","info","videoTrack","mediaStream","getVideoTracks","readyState","Promise","resolve","isNew","destroyUserMediaStream","reject","catchHandler","err","error","getHighestResolutionStream","then","streamResult","catch","getDesktopMediaStream","track","destroyDesktopMediaStream","mediaCatchHandler","video","audio","getTracks","forEach","stop","all","undefined","isDesktopMedia","constraintsCandidates","getParsedConstraints","Error","lastSuccessfulResult","lastFailedResult","isStreamRequestFulfilled","reduce","total","candidate","constraintType","default","getStreamPromiseByParsedConstraints","stream","constraint","failResult","errorCode","MediaRequestErrorCode","PermissionDenied","GeneralError","message","streamPromise","navigator","mediaDevices","getUserMedia","getDisplayMedia","e","streamConstraints","constraintCandidates","baseConstraints","facingMode","frameRate","ideal","max","assignConstraint","videoResolution","_a","resolution","split","width","height","constraintCandidate","_constraintByType","push","Array","defaultResolution","DEFAULT_VIDEO_RESOLUTION","DEFAULT_VIDEO_CONSTRAINT_TYPE","errorName","name","permissionErrorNames","constraintsErrors","isErrorOfType","errorsArr","filter","Overconstrained","type","ConstraintType","MIN","min"],"mappings":"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","file":"LocalStreamManager.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 });\nvar assign_1 = __importDefault(require(\"lodash/assign\"));\nvar get_1 = __importDefault(require(\"lodash/get\"));\nvar MediaConstants_1 = require(\"./MediaConstants\");\nwindow.latestUserMediaStream = null;\nvar userMediaPromise = null;\nwindow.latestDesktopMediaStream = null;\nvar desktopMediaPromise = null;\nvar MediaTracer_1 = require(\"./MediaUtils/MediaTracer\");\nvar trace = MediaTracer_1.getMediaTracer('LocalStreamManager');\nvar LocalStreamManager = /** @class */ (function () {\n function LocalStreamManager(environment) {\n this.environment = environment;\n this.parseMediaErrorThenThrow = this.parseMediaErrorThenThrow.bind(this);\n this.clearAllStreams = this.clearAllStreams.bind(this);\n }\n LocalStreamManager.prototype.getUserMediaStream = function (constraints) {\n var _this = this;\n trace.info('getUserMediaStream', constraints);\n if (userMediaPromise) {\n return userMediaPromise;\n }\n if (window.latestUserMediaStream) {\n var videoTrack = window.latestUserMediaStream.mediaStream.getVideoTracks();\n if (videoTrack.length > 0 && videoTrack[0].readyState === 'live') {\n return Promise.resolve(__assign({}, window.latestUserMediaStream, { isNew: false }));\n }\n this.destroyUserMediaStream();\n }\n userMediaPromise = new Promise(function (resolve, reject) {\n var catchHandler = function (err) {\n userMediaPromise = null;\n window.latestUserMediaStream = null;\n trace.error('getUserMedia error', err);\n reject(err);\n };\n try {\n _this.getHighestResolutionStream(false, constraints)\n .then(function (streamResult) {\n userMediaPromise = null;\n window.latestUserMediaStream = streamResult;\n resolve(__assign({}, window.latestUserMediaStream, { isNew: true }));\n })\n .catch(catchHandler);\n }\n catch (err) {\n catchHandler(err);\n }\n });\n return userMediaPromise;\n };\n LocalStreamManager.prototype.getDesktopMediaStream = function (constraints) {\n var _this = this;\n if (desktopMediaPromise) {\n return desktopMediaPromise;\n }\n if (window.latestDesktopMediaStream) {\n var track = window.latestDesktopMediaStream.mediaStream.getVideoTracks();\n if (track.length > 0 && track[0].readyState === 'live') {\n //return Promise.resolve();\n return Promise.resolve(__assign({}, window.latestDesktopMediaStream, { isNew: false }));\n }\n this.destroyDesktopMediaStream().catch(function (error) {\n trace.error('Error destroying desktop media stream', error);\n });\n }\n desktopMediaPromise = new Promise(function (resolve, reject) {\n var mediaCatchHandler = function (err) {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = null;\n trace.error('getDisplayMedia error', err);\n reject(err);\n };\n try {\n _this.getHighestResolutionStream(true, { video: constraints, audio: false })\n .then(function (streamResult) {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = streamResult;\n resolve(__assign({}, window.latestDesktopMediaStream, { isNew: true }));\n }).catch(mediaCatchHandler);\n }\n catch (err) {\n mediaCatchHandler(err);\n }\n });\n return desktopMediaPromise;\n };\n LocalStreamManager.prototype.destroyUserMediaStream = function () {\n var streamResult = window.latestUserMediaStream;\n window.latestUserMediaStream = null;\n userMediaPromise = null;\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach(function (track) {\n track.stop();\n });\n }\n return Promise.resolve();\n };\n LocalStreamManager.prototype.destroyDesktopMediaStream = function () {\n var streamResult = window.latestDesktopMediaStream;\n window.latestDesktopMediaStream = null;\n desktopMediaPromise = null;\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach(function (track) {\n track.stop();\n });\n }\n return Promise.resolve();\n };\n LocalStreamManager.prototype.clearAllStreams = function () {\n trace.info('LocalStreamManager Clearing all streams');\n return Promise.all([\n this.destroyDesktopMediaStream(),\n this.destroyUserMediaStream()\n ]).then(function () { return undefined; });\n };\n LocalStreamManager.prototype.getHighestResolutionStream = function (isDesktopMedia, constraints) {\n var _this = this;\n var constraintsCandidates = this.getParsedConstraints(isDesktopMedia, constraints);\n if (constraintsCandidates.length === 0) {\n return Promise.reject(new Error('No constraints to retrieve the stream'));\n }\n return new Promise(function (resolve, reject) {\n var lastSuccessfulResult = null;\n var lastFailedResult = null;\n var isStreamRequestFulfilled = false;\n Promise.reduce(constraintsCandidates, function (total, candidate) {\n if (isStreamRequestFulfilled) {\n return;\n }\n var constraintType = get_1.default(constraints, 'video.videoResolution.constraintType') || get_1.default(constraints, 'video.videoResolution[0].constraintType');\n return _this.getStreamPromiseByParsedConstraints(candidate, isDesktopMedia)\n .then(function (stream) {\n lastSuccessfulResult = {\n mediaStream: stream,\n constraint: candidate,\n constraintType: constraintType,\n isNew: true\n };\n trace.info('getHighestResolutionStream Success', lastSuccessfulResult);\n isStreamRequestFulfilled = true;\n resolve(lastSuccessfulResult);\n })\n .catch(function (failResult) {\n lastFailedResult = failResult;\n if (lastFailedResult.errorCode === MediaConstants_1.MediaRequestErrorCode.PermissionDenied) {\n isStreamRequestFulfilled = true;\n reject(lastFailedResult);\n }\n });\n }, 0)\n .then(function () {\n if (!isStreamRequestFulfilled) {\n if (!lastFailedResult) {\n lastFailedResult = {\n errorCode: MediaConstants_1.MediaRequestErrorCode.GeneralError,\n message: 'Suitable stream cannot be created'\n };\n }\n reject(lastFailedResult);\n }\n });\n });\n };\n //@ts-ignore\n LocalStreamManager.prototype.getStreamPromiseByParsedConstraints = function (constraints, isDesktopMedia) {\n var streamPromise = null;\n trace.info('getStreamPromiseByParsedConstraints', constraints);\n try {\n if (!isDesktopMedia) {\n streamPromise = navigator.mediaDevices.getUserMedia(constraints);\n }\n else {\n //@ts-ignore\n streamPromise = navigator.mediaDevices.getDisplayMedia\n //@ts-ignore\n ? navigator.mediaDevices.getDisplayMedia(constraints)\n : navigator.getDisplayMedia(constraints);\n }\n return streamPromise.catch(this.parseMediaErrorThenThrow);\n }\n catch (e) {\n this.parseMediaErrorThenThrow(e);\n }\n };\n LocalStreamManager.prototype.getParsedConstraints = function (isDesktopMedia, streamConstraints) {\n var constraintCandidates = [];\n //TODO - Alex: need to understand which constraints not compatible with screen share\n if (isDesktopMedia) {\n return [{ video: true }];\n }\n if (!streamConstraints) {\n return constraintCandidates;\n }\n var baseConstraints = {};\n baseConstraints.audio = typeof streamConstraints.audio === 'boolean' ? streamConstraints.audio : undefined;\n if (!streamConstraints.video) {\n return [baseConstraints];\n }\n baseConstraints.video = {\n facingMode: 'environment',\n frameRate: { ideal: 15, max: 30 }\n };\n var assignConstraint = function (videoResolution) {\n var _a = videoResolution.resolution.split('x'), width = _a[0], height = _a[1];\n var constraintCandidate = assign_1.default({}, baseConstraints);\n constraintCandidate.video = assign_1.default({}, baseConstraints.video, {\n width: _constraintByType(width, videoResolution.constraintType),\n height: _constraintByType(height, videoResolution.constraintType)\n });\n constraintCandidates.push(constraintCandidate);\n };\n if ((baseConstraints.video && typeof streamConstraints.video === 'boolean') ||\n !(streamConstraints.video.videoResolution instanceof Array)) {\n var defaultResolution = {\n resolution: MediaConstants_1.DEFAULT_VIDEO_RESOLUTION,\n constraintType: MediaConstants_1.DEFAULT_VIDEO_CONSTRAINT_TYPE\n };\n assignConstraint(defaultResolution);\n return constraintCandidates;\n }\n streamConstraints.video.videoResolution.forEach(assignConstraint);\n return constraintCandidates;\n };\n LocalStreamManager.prototype.parseMediaErrorThenThrow = function (error) {\n trace.error('Get media stream error.', error);\n var errorName = error && error.name ? error.name : '';\n var permissionErrorNames = [\n 'NotAllowedError',\n 'PermissionDismissedError',\n 'PermissionDeniedError'\n ];\n // const generalError = [\n // 'NotFoundError',\n // 'DevicesNotFoundError',\n // 'NotReadableError',\n // 'TrackStartError'\n // ];\n var constraintsErrors = [\n 'OverconstrainedError',\n 'ConstraintNotSatisfiedError'\n ];\n var isErrorOfType = function (errorsArr) { return errorsArr.filter(function (err) { return err === errorName; }).length > 0; };\n if (isErrorOfType(permissionErrorNames)) {\n throw { errorCode: MediaConstants_1.MediaRequestErrorCode.PermissionDenied, message: errorName };\n }\n if (isErrorOfType(constraintsErrors)) {\n throw { errorCode: MediaConstants_1.MediaRequestErrorCode.Overconstrained, message: errorName };\n }\n throw { errorCode: MediaConstants_1.MediaRequestErrorCode.GeneralError, message: errorName };\n };\n return LocalStreamManager;\n}());\nexports.LocalStreamManager = LocalStreamManager;\nfunction _constraintByType(value, type) {\n switch (type) {\n case MediaConstants_1.ConstraintType.MIN:\n return { min: value };\n default:\n return { ideal: value };\n }\n}\n\n//# sourceMappingURL=LocalStreamManager.js.map\n","import assign from 'lodash/assign';\nimport get from 'lodash/get';\n\nimport {Nullable} from '@techsee/techsee-common';\n\nimport {\n VideoStreamResolution,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n IMediaEnvironment\n} from './MediaContracts';\n\nimport {\n ConstraintType,\n DEFAULT_VIDEO_CONSTRAINT_TYPE,\n DEFAULT_VIDEO_RESOLUTION,\n MediaRequestErrorCode\n} from './MediaConstants';\n\ndeclare global {\n interface Window {\n latestUserMediaStream: Nullable<MediaRequestSuccessResult>;\n latestDesktopMediaStream: Nullable<MediaRequestSuccessResult>;\n }\n}\n\ndeclare const window: Window;\n\nwindow.latestUserMediaStream = null;\nlet userMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nwindow.latestDesktopMediaStream = null;\nlet desktopMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\n\nconst trace = getMediaTracer('LocalStreamManager');\n\nexport class LocalStreamManager {\n private environment: IMediaEnvironment;\n\n constructor(environment: IMediaEnvironment) {\n this.environment = environment;\n\n this.parseMediaErrorThenThrow = this.parseMediaErrorThenThrow.bind(this);\n this.clearAllStreams = this.clearAllStreams.bind(this);\n }\n\n getUserMediaStream(constraints: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n trace.info('getUserMediaStream', constraints);\n if (userMediaPromise) {\n return userMediaPromise;\n }\n\n if (window.latestUserMediaStream) {\n const videoTrack = window.latestUserMediaStream.mediaStream.getVideoTracks();\n\n if (videoTrack.length > 0 && videoTrack[0].readyState === 'live') {\n return Promise.resolve({...window.latestUserMediaStream, isNew: false});\n }\n this.destroyUserMediaStream();\n\n }\n\n\n userMediaPromise = new Promise((resolve, reject) => {\n const catchHandler = (err: any): void => {\n userMediaPromise = null;\n window.latestUserMediaStream = null;\n trace.error('getUserMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(false, constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n userMediaPromise = null;\n window.latestUserMediaStream = streamResult;\n resolve({...window.latestUserMediaStream, isNew: true});\n })\n .catch(catchHandler);\n } catch (err) {\n catchHandler(err);\n }\n });\n\n return userMediaPromise;\n }\n\n getDesktopMediaStream(constraints: LocalVideoStreamConstraints): Promise<MediaRequestSuccessResult> {\n if (desktopMediaPromise) {\n return desktopMediaPromise;\n }\n\n if (window.latestDesktopMediaStream) {\n const track = window.latestDesktopMediaStream.mediaStream.getVideoTracks();\n\n if (track.length > 0 && track[0].readyState === 'live') {\n //return Promise.resolve();\n return Promise.resolve({...window.latestDesktopMediaStream, isNew: false});\n }\n this.destroyDesktopMediaStream().catch((error: any) => {\n trace.error('Error destroying desktop media stream', error);\n });\n\n }\n\n desktopMediaPromise = new Promise((resolve, reject) => {\n const mediaCatchHandler = (err: any): void => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = null;\n trace.error('getDisplayMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(true, {video: constraints, audio: false})\n .then((streamResult: MediaRequestSuccessResult) => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = streamResult;\n\n resolve({...window.latestDesktopMediaStream, isNew: true});\n }).catch(mediaCatchHandler);\n } catch (err) {\n mediaCatchHandler(err);\n }\n });\n\n return desktopMediaPromise;\n }\n\n destroyUserMediaStream(): Promise<void> {\n const streamResult = window.latestUserMediaStream;\n\n window.latestUserMediaStream = null;\n userMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n destroyDesktopMediaStream(): Promise<void> {\n const streamResult = window.latestDesktopMediaStream;\n\n window.latestDesktopMediaStream = null;\n desktopMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n clearAllStreams(): Promise<void> {\n trace.info('LocalStreamManager Clearing all streams');\n\n return Promise.all([\n this.destroyDesktopMediaStream(),\n this.destroyUserMediaStream()\n ]).then(() => undefined);\n }\n\n private getHighestResolutionStream(isDesktopMedia: boolean, constraints?: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n const constraintsCandidates = this.getParsedConstraints(isDesktopMedia, constraints);\n\n if (constraintsCandidates.length === 0) {\n return Promise.reject(new Error('No constraints to retrieve the stream'));\n }\n\n return new Promise((resolve, reject) => {\n let lastSuccessfulResult: Nullable<MediaRequestSuccessResult> = null;\n let lastFailedResult: Nullable<MediaRequestFailResult> = null;\n let isStreamRequestFulfilled = false;\n\n (Promise as any).reduce(constraintsCandidates, (total: any, candidate: MediaStreamConstraints) => {\n if (isStreamRequestFulfilled) {\n return;\n }\n const constraintType = get(constraints, 'video.videoResolution.constraintType') || get(constraints, 'video.videoResolution[0].constraintType');\n\n return this.getStreamPromiseByParsedConstraints(candidate, isDesktopMedia)\n .then((stream: MediaStream) => {\n lastSuccessfulResult = {\n mediaStream: stream,\n constraint: candidate,\n constraintType,\n isNew: true\n };\n trace.info('getHighestResolutionStream Success', lastSuccessfulResult);\n isStreamRequestFulfilled = true;\n resolve(lastSuccessfulResult);\n })\n .catch((failResult: MediaRequestFailResult) => {\n lastFailedResult = failResult;\n if (lastFailedResult.errorCode === MediaRequestErrorCode.PermissionDenied) {\n isStreamRequestFulfilled = true;\n reject(lastFailedResult);\n }\n });\n }, 0)\n .then(() => {\n if (!isStreamRequestFulfilled) {\n if (!lastFailedResult) {\n lastFailedResult = {\n errorCode: MediaRequestErrorCode.GeneralError,\n message: 'Suitable stream cannot be created'\n };\n }\n reject(lastFailedResult);\n }\n });\n });\n }\n\n //@ts-ignore\n private getStreamPromiseByParsedConstraints(constraints: MediaStreamConstraints, isDesktopMedia: boolean): Promise<MediaStream> {\n let streamPromise = null;\n\n trace.info('getStreamPromiseByParsedConstraints', constraints);\n\n try {\n if (!isDesktopMedia) {\n streamPromise = navigator.mediaDevices.getUserMedia(constraints);\n } else {\n //@ts-ignore\n streamPromise = navigator.mediaDevices.getDisplayMedia\n //@ts-ignore\n ? navigator.mediaDevices.getDisplayMedia(constraints)\n : navigator.getDisplayMedia(constraints);\n }\n\n return streamPromise.catch(this.parseMediaErrorThenThrow);\n } catch (e) {\n this.parseMediaErrorThenThrow(e);\n }\n }\n\n private getParsedConstraints(isDesktopMedia: boolean, streamConstraints?: LocalMediaConstraints): MediaStreamConstraints[] {\n const constraintCandidates: MediaStreamConstraints[] = [];\n\n //TODO - Alex: need to understand which constraints not compatible with screen share\n if (isDesktopMedia) {\n return [{video: true}];\n }\n\n if (!streamConstraints) {\n return constraintCandidates;\n }\n\n const baseConstraints: MediaStreamConstraints = {};\n\n baseConstraints.audio = typeof streamConstraints.audio === 'boolean' ? streamConstraints.audio : undefined;\n\n if (!streamConstraints.video) {\n return [baseConstraints];\n }\n\n baseConstraints.video = {\n facingMode: 'environment',\n frameRate: {ideal: 15, max: 30}\n };\n\n const assignConstraint = (videoResolution: VideoStreamResolution): void => {\n const [width, height] = videoResolution.resolution.split('x');\n const constraintCandidate = assign({}, baseConstraints);\n\n constraintCandidate.video = assign({}, baseConstraints.video, {\n width: _constraintByType(width, videoResolution.constraintType),\n height: _constraintByType(height, videoResolution.constraintType)\n });\n constraintCandidates.push(constraintCandidate);\n };\n\n if ((baseConstraints.video && typeof streamConstraints.video === 'boolean') ||\n !((streamConstraints.video as LocalVideoStreamConstraints).videoResolution instanceof Array)) {\n\n const defaultResolution: VideoStreamResolution = {\n resolution: DEFAULT_VIDEO_RESOLUTION,\n constraintType: DEFAULT_VIDEO_CONSTRAINT_TYPE\n };\n\n assignConstraint(defaultResolution);\n\n return constraintCandidates;\n }\n\n (streamConstraints.video as any).videoResolution.forEach(assignConstraint);\n\n return constraintCandidates;\n }\n\n private parseMediaErrorThenThrow(error: any): MediaRequestFailResult {\n trace.error('Get media stream error.', error);\n const errorName = error && error.name ? error.name : '';\n\n const permissionErrorNames = [\n 'NotAllowedError',\n 'PermissionDismissedError',\n 'PermissionDeniedError'\n\n ];\n\n // const generalError = [\n // 'NotFoundError',\n // 'DevicesNotFoundError',\n // 'NotReadableError',\n // 'TrackStartError'\n // ];\n\n const constraintsErrors = [\n 'OverconstrainedError',\n 'ConstraintNotSatisfiedError'\n\n ];\n\n const isErrorOfType = (errorsArr: string[]) => errorsArr.filter((err: string) => err === errorName).length > 0;\n\n if (isErrorOfType(permissionErrorNames)) {\n throw {errorCode: MediaRequestErrorCode.PermissionDenied, message: errorName};\n }\n\n if (isErrorOfType(constraintsErrors)) {\n throw {errorCode: MediaRequestErrorCode.Overconstrained, message: errorName};\n }\n\n throw {errorCode: MediaRequestErrorCode.GeneralError, message: errorName};\n }\n}\n\nfunction _constraintByType(value: any, type: ConstraintType): any {\n switch (type) {\n case ConstraintType.MIN:\n return {min: value};\n default:\n return {ideal: value};\n }\n}\n"]}
1
+ {"version":3,"sources":["LocalStreamManager.js","../src/LocalStreamManager.ts"],"names":["__assign","Object","assign","t","s","i","n","arguments","length","p","prototype","hasOwnProperty","call","apply","__importDefault","mod","__esModule","defineProperty","exports","value","assign_1","require","get_1","bluebird_1","MediaConstants_1","window","latestUserMediaStream","userMediaPromise","latestDesktopMediaStream","desktopMediaPromise","MediaTracer_1","trace","getMediaTracer","LocalStreamManager","environment","parseMediaErrorThenThrow","bind","clearAllStreams","getUserMediaStream","constraints","_this","info","videoTrack","mediaStream","getVideoTracks","readyState","Promise","resolve","isNew","destroyUserMediaStream","reject","catchHandler","err","error","getHighestResolutionStream","then","streamResult","catch","getDesktopMediaStream","track","destroyDesktopMediaStream","mediaCatchHandler","video","audio","getTracks","forEach","stop","all","undefined","isDesktopMedia","constraintsCandidates","getParsedConstraints","Error","lastSuccessfulResult","lastFailedResult","isStreamRequestFulfilled","reduce","total","candidate","constraintType","default","getStreamPromiseByParsedConstraints","stream","constraint","failResult","errorCode","MediaRequestErrorCode","PermissionDenied","GeneralError","message","streamPromise","navigator","mediaDevices","getUserMedia","getDisplayMedia","e","streamConstraints","constraintCandidates","baseConstraints","facingMode","frameRate","ideal","max","assignConstraint","videoResolution","_a","resolution","split","width","height","constraintCandidate","_constraintByType","push","Array","defaultResolution","DEFAULT_VIDEO_RESOLUTION","DEFAULT_VIDEO_CONSTRAINT_TYPE","errorName","name","permissionErrorNames","constraintsErrors","isErrorOfType","errorsArr","filter","Overconstrained","type","ConstraintType","MIN","min"],"mappings":"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","file":"LocalStreamManager.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 });\nvar assign_1 = __importDefault(require(\"lodash/assign\"));\nvar get_1 = __importDefault(require(\"lodash/get\"));\nvar bluebird_1 = require(\"bluebird\");\nvar MediaConstants_1 = require(\"./MediaConstants\");\nwindow.latestUserMediaStream = null;\nvar userMediaPromise = null;\nwindow.latestDesktopMediaStream = null;\nvar desktopMediaPromise = null;\nvar MediaTracer_1 = require(\"./MediaUtils/MediaTracer\");\nvar trace = MediaTracer_1.getMediaTracer('LocalStreamManager');\nvar LocalStreamManager = /** @class */ (function () {\n function LocalStreamManager(environment) {\n this.environment = environment;\n this.parseMediaErrorThenThrow = this.parseMediaErrorThenThrow.bind(this);\n this.clearAllStreams = this.clearAllStreams.bind(this);\n }\n LocalStreamManager.prototype.getUserMediaStream = function (constraints) {\n var _this = this;\n trace.info('getUserMediaStream', constraints);\n if (userMediaPromise) {\n return userMediaPromise;\n }\n if (window.latestUserMediaStream) {\n var videoTrack = window.latestUserMediaStream.mediaStream.getVideoTracks();\n if (videoTrack.length > 0 && videoTrack[0].readyState === 'live') {\n return bluebird_1.Promise.resolve(__assign({}, window.latestUserMediaStream, { isNew: false }));\n }\n this.destroyUserMediaStream();\n }\n userMediaPromise = new bluebird_1.Promise(function (resolve, reject) {\n var catchHandler = function (err) {\n userMediaPromise = null;\n window.latestUserMediaStream = null;\n trace.error('getUserMedia error', err);\n reject(err);\n };\n try {\n _this.getHighestResolutionStream(false, constraints)\n .then(function (streamResult) {\n userMediaPromise = null;\n window.latestUserMediaStream = streamResult;\n resolve(__assign({}, window.latestUserMediaStream, { isNew: true }));\n })\n .catch(catchHandler);\n }\n catch (err) {\n catchHandler(err);\n }\n });\n return userMediaPromise;\n };\n LocalStreamManager.prototype.getDesktopMediaStream = function (constraints) {\n var _this = this;\n if (desktopMediaPromise) {\n return desktopMediaPromise;\n }\n if (window.latestDesktopMediaStream) {\n var track = window.latestDesktopMediaStream.mediaStream.getVideoTracks();\n if (track.length > 0 && track[0].readyState === 'live') {\n //return Promise.resolve();\n return bluebird_1.Promise.resolve(__assign({}, window.latestDesktopMediaStream, { isNew: false }));\n }\n this.destroyDesktopMediaStream().catch(function (error) {\n trace.error('Error destroying desktop media stream', error);\n });\n }\n desktopMediaPromise = new bluebird_1.Promise(function (resolve, reject) {\n var mediaCatchHandler = function (err) {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = null;\n trace.error('getDisplayMedia error', err);\n reject(err);\n };\n try {\n _this.getHighestResolutionStream(true, { video: constraints, audio: false })\n .then(function (streamResult) {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = streamResult;\n resolve(__assign({}, window.latestDesktopMediaStream, { isNew: true }));\n }).catch(mediaCatchHandler);\n }\n catch (err) {\n mediaCatchHandler(err);\n }\n });\n return desktopMediaPromise;\n };\n LocalStreamManager.prototype.destroyUserMediaStream = function () {\n var streamResult = window.latestUserMediaStream;\n window.latestUserMediaStream = null;\n userMediaPromise = null;\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach(function (track) {\n track.stop();\n });\n }\n return bluebird_1.Promise.resolve();\n };\n LocalStreamManager.prototype.destroyDesktopMediaStream = function () {\n var streamResult = window.latestDesktopMediaStream;\n window.latestDesktopMediaStream = null;\n desktopMediaPromise = null;\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach(function (track) {\n track.stop();\n });\n }\n return bluebird_1.Promise.resolve();\n };\n LocalStreamManager.prototype.clearAllStreams = function () {\n trace.info('LocalStreamManager Clearing all streams');\n return bluebird_1.Promise.all([\n this.destroyDesktopMediaStream(),\n this.destroyUserMediaStream()\n ]).then(function () { return undefined; });\n };\n LocalStreamManager.prototype.getHighestResolutionStream = function (isDesktopMedia, constraints) {\n var _this = this;\n var constraintsCandidates = this.getParsedConstraints(isDesktopMedia, constraints);\n if (constraintsCandidates.length === 0) {\n return bluebird_1.Promise.reject(new Error('No constraints to retrieve the stream'));\n }\n return new bluebird_1.Promise(function (resolve, reject) {\n var lastSuccessfulResult = null;\n var lastFailedResult = null;\n var isStreamRequestFulfilled = false;\n bluebird_1.Promise.reduce(constraintsCandidates, function (total, candidate) {\n if (isStreamRequestFulfilled) {\n return;\n }\n var constraintType = get_1.default(constraints, 'video.videoResolution.constraintType') || get_1.default(constraints, 'video.videoResolution[0].constraintType');\n return _this.getStreamPromiseByParsedConstraints(candidate, isDesktopMedia)\n .then(function (stream) {\n lastSuccessfulResult = {\n mediaStream: stream,\n constraint: candidate,\n constraintType: constraintType,\n isNew: true\n };\n trace.info('getHighestResolutionStream Success', lastSuccessfulResult);\n isStreamRequestFulfilled = true;\n resolve(lastSuccessfulResult);\n })\n .catch(function (failResult) {\n lastFailedResult = failResult;\n if (lastFailedResult.errorCode === MediaConstants_1.MediaRequestErrorCode.PermissionDenied) {\n isStreamRequestFulfilled = true;\n reject(lastFailedResult);\n }\n });\n }, 0)\n .then(function () {\n if (!isStreamRequestFulfilled) {\n if (!lastFailedResult) {\n lastFailedResult = {\n errorCode: MediaConstants_1.MediaRequestErrorCode.GeneralError,\n message: 'Suitable stream cannot be created'\n };\n }\n reject(lastFailedResult);\n }\n });\n });\n };\n //@ts-ignore\n LocalStreamManager.prototype.getStreamPromiseByParsedConstraints = function (constraints, isDesktopMedia) {\n var streamPromise = null;\n trace.info('getStreamPromiseByParsedConstraints', constraints);\n try {\n if (!isDesktopMedia) {\n streamPromise = navigator.mediaDevices.getUserMedia(constraints);\n }\n else {\n //@ts-ignore\n streamPromise = navigator.mediaDevices.getDisplayMedia\n //@ts-ignore\n ? navigator.mediaDevices.getDisplayMedia(constraints)\n : navigator.getDisplayMedia(constraints);\n }\n return streamPromise.catch(this.parseMediaErrorThenThrow);\n }\n catch (e) {\n this.parseMediaErrorThenThrow(e);\n }\n };\n LocalStreamManager.prototype.getParsedConstraints = function (isDesktopMedia, streamConstraints) {\n var constraintCandidates = [];\n //TODO - Alex: need to understand which constraints not compatible with screen share\n if (isDesktopMedia) {\n return [{ video: true }];\n }\n if (!streamConstraints) {\n return constraintCandidates;\n }\n var baseConstraints = {};\n baseConstraints.audio = typeof streamConstraints.audio === 'boolean' ? streamConstraints.audio : undefined;\n if (!streamConstraints.video) {\n return [baseConstraints];\n }\n baseConstraints.video = {\n facingMode: 'environment',\n frameRate: { ideal: 15, max: 30 }\n };\n var assignConstraint = function (videoResolution) {\n var _a = videoResolution.resolution.split('x'), width = _a[0], height = _a[1];\n var constraintCandidate = assign_1.default({}, baseConstraints);\n constraintCandidate.video = assign_1.default({}, baseConstraints.video, {\n width: _constraintByType(width, videoResolution.constraintType),\n height: _constraintByType(height, videoResolution.constraintType)\n });\n constraintCandidates.push(constraintCandidate);\n };\n if ((baseConstraints.video && typeof streamConstraints.video === 'boolean') ||\n !(streamConstraints.video.videoResolution instanceof Array)) {\n var defaultResolution = {\n resolution: MediaConstants_1.DEFAULT_VIDEO_RESOLUTION,\n constraintType: MediaConstants_1.DEFAULT_VIDEO_CONSTRAINT_TYPE\n };\n assignConstraint(defaultResolution);\n return constraintCandidates;\n }\n streamConstraints.video.videoResolution.forEach(assignConstraint);\n return constraintCandidates;\n };\n LocalStreamManager.prototype.parseMediaErrorThenThrow = function (error) {\n trace.error('Get media stream error.', error);\n var errorName = error && error.name ? error.name : '';\n var permissionErrorNames = [\n 'NotAllowedError',\n 'PermissionDismissedError',\n 'PermissionDeniedError'\n ];\n // const generalError = [\n // 'NotFoundError',\n // 'DevicesNotFoundError',\n // 'NotReadableError',\n // 'TrackStartError'\n // ];\n var constraintsErrors = [\n 'OverconstrainedError',\n 'ConstraintNotSatisfiedError'\n ];\n var isErrorOfType = function (errorsArr) { return errorsArr.filter(function (err) { return err === errorName; }).length > 0; };\n if (isErrorOfType(permissionErrorNames)) {\n throw { errorCode: MediaConstants_1.MediaRequestErrorCode.PermissionDenied, message: errorName };\n }\n if (isErrorOfType(constraintsErrors)) {\n throw { errorCode: MediaConstants_1.MediaRequestErrorCode.Overconstrained, message: errorName };\n }\n throw { errorCode: MediaConstants_1.MediaRequestErrorCode.GeneralError, message: errorName };\n };\n return LocalStreamManager;\n}());\nexports.LocalStreamManager = LocalStreamManager;\nfunction _constraintByType(value, type) {\n switch (type) {\n case MediaConstants_1.ConstraintType.MIN:\n return { min: value };\n default:\n return { ideal: value };\n }\n}\n\n//# sourceMappingURL=LocalStreamManager.js.map\n","import assign from 'lodash/assign';\nimport get from 'lodash/get';\nimport {Promise} from 'bluebird';\n\nimport {Nullable} from '@techsee/techsee-common';\n\nimport {\n VideoStreamResolution,\n LocalMediaConstraints,\n LocalVideoStreamConstraints,\n MediaRequestFailResult,\n MediaRequestSuccessResult,\n IMediaEnvironment\n} from './MediaContracts';\n\nimport {\n ConstraintType,\n DEFAULT_VIDEO_CONSTRAINT_TYPE,\n DEFAULT_VIDEO_RESOLUTION,\n MediaRequestErrorCode\n} from './MediaConstants';\n\ndeclare global {\n interface Window {\n latestUserMediaStream: Nullable<MediaRequestSuccessResult>;\n latestDesktopMediaStream: Nullable<MediaRequestSuccessResult>;\n }\n}\n\ndeclare const window: Window;\n\nwindow.latestUserMediaStream = null;\nlet userMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nwindow.latestDesktopMediaStream = null;\nlet desktopMediaPromise: Nullable<Promise<MediaRequestSuccessResult>> = null;\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\n\nconst trace = getMediaTracer('LocalStreamManager');\n\nexport class LocalStreamManager {\n private environment: IMediaEnvironment;\n\n constructor(environment: IMediaEnvironment) {\n this.environment = environment;\n\n this.parseMediaErrorThenThrow = this.parseMediaErrorThenThrow.bind(this);\n this.clearAllStreams = this.clearAllStreams.bind(this);\n }\n\n getUserMediaStream(constraints: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n trace.info('getUserMediaStream', constraints);\n if (userMediaPromise) {\n return userMediaPromise;\n }\n\n if (window.latestUserMediaStream) {\n const videoTrack = window.latestUserMediaStream.mediaStream.getVideoTracks();\n\n if (videoTrack.length > 0 && videoTrack[0].readyState === 'live') {\n return Promise.resolve({...window.latestUserMediaStream, isNew: false});\n }\n this.destroyUserMediaStream();\n\n }\n\n\n userMediaPromise = new Promise((resolve, reject) => {\n const catchHandler = (err: any): void => {\n userMediaPromise = null;\n window.latestUserMediaStream = null;\n trace.error('getUserMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(false, constraints)\n .then((streamResult: MediaRequestSuccessResult) => {\n userMediaPromise = null;\n window.latestUserMediaStream = streamResult;\n resolve({...window.latestUserMediaStream, isNew: true});\n })\n .catch(catchHandler);\n } catch (err) {\n catchHandler(err);\n }\n });\n\n return userMediaPromise;\n }\n\n getDesktopMediaStream(constraints: LocalVideoStreamConstraints): Promise<MediaRequestSuccessResult> {\n if (desktopMediaPromise) {\n return desktopMediaPromise;\n }\n\n if (window.latestDesktopMediaStream) {\n const track = window.latestDesktopMediaStream.mediaStream.getVideoTracks();\n\n if (track.length > 0 && track[0].readyState === 'live') {\n //return Promise.resolve();\n return Promise.resolve({...window.latestDesktopMediaStream, isNew: false});\n }\n this.destroyDesktopMediaStream().catch((error: any) => {\n trace.error('Error destroying desktop media stream', error);\n });\n\n }\n\n desktopMediaPromise = new Promise((resolve, reject) => {\n const mediaCatchHandler = (err: any): void => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = null;\n trace.error('getDisplayMedia error', err);\n reject(err);\n };\n\n try {\n this.getHighestResolutionStream(true, {video: constraints, audio: false})\n .then((streamResult: MediaRequestSuccessResult) => {\n desktopMediaPromise = null;\n window.latestDesktopMediaStream = streamResult;\n\n resolve({...window.latestDesktopMediaStream, isNew: true});\n }).catch(mediaCatchHandler);\n } catch (err) {\n mediaCatchHandler(err);\n }\n });\n\n return desktopMediaPromise;\n }\n\n destroyUserMediaStream(): Promise<void> {\n const streamResult = window.latestUserMediaStream;\n\n window.latestUserMediaStream = null;\n userMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n destroyDesktopMediaStream(): Promise<void> {\n const streamResult = window.latestDesktopMediaStream;\n\n window.latestDesktopMediaStream = null;\n desktopMediaPromise = null;\n\n if (streamResult) {\n streamResult.mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.stop();\n });\n }\n\n return Promise.resolve();\n }\n\n clearAllStreams(): Promise<void> {\n trace.info('LocalStreamManager Clearing all streams');\n\n return Promise.all([\n this.destroyDesktopMediaStream(),\n this.destroyUserMediaStream()\n ]).then(() => undefined);\n }\n\n private getHighestResolutionStream(isDesktopMedia: boolean, constraints?: LocalMediaConstraints): Promise<MediaRequestSuccessResult> {\n const constraintsCandidates = this.getParsedConstraints(isDesktopMedia, constraints);\n\n if (constraintsCandidates.length === 0) {\n return Promise.reject(new Error('No constraints to retrieve the stream'));\n }\n\n return new Promise((resolve, reject) => {\n let lastSuccessfulResult: Nullable<MediaRequestSuccessResult> = null;\n let lastFailedResult: Nullable<MediaRequestFailResult> = null;\n let isStreamRequestFulfilled = false;\n\n (Promise as any).reduce(constraintsCandidates, (total: any, candidate: MediaStreamConstraints) => {\n if (isStreamRequestFulfilled) {\n return;\n }\n const constraintType = get(constraints, 'video.videoResolution.constraintType') || get(constraints, 'video.videoResolution[0].constraintType');\n\n return this.getStreamPromiseByParsedConstraints(candidate, isDesktopMedia)\n .then((stream: MediaStream) => {\n lastSuccessfulResult = {\n mediaStream: stream,\n constraint: candidate,\n constraintType,\n isNew: true\n };\n trace.info('getHighestResolutionStream Success', lastSuccessfulResult);\n isStreamRequestFulfilled = true;\n resolve(lastSuccessfulResult);\n })\n .catch((failResult: MediaRequestFailResult) => {\n lastFailedResult = failResult;\n if (lastFailedResult.errorCode === MediaRequestErrorCode.PermissionDenied) {\n isStreamRequestFulfilled = true;\n reject(lastFailedResult);\n }\n });\n }, 0)\n .then(() => {\n if (!isStreamRequestFulfilled) {\n if (!lastFailedResult) {\n lastFailedResult = {\n errorCode: MediaRequestErrorCode.GeneralError,\n message: 'Suitable stream cannot be created'\n };\n }\n reject(lastFailedResult);\n }\n });\n });\n }\n\n //@ts-ignore\n private getStreamPromiseByParsedConstraints(constraints: MediaStreamConstraints, isDesktopMedia: boolean): Promise<MediaStream> {\n let streamPromise = null;\n\n trace.info('getStreamPromiseByParsedConstraints', constraints);\n\n try {\n if (!isDesktopMedia) {\n streamPromise = navigator.mediaDevices.getUserMedia(constraints);\n } else {\n //@ts-ignore\n streamPromise = navigator.mediaDevices.getDisplayMedia\n //@ts-ignore\n ? navigator.mediaDevices.getDisplayMedia(constraints)\n : navigator.getDisplayMedia(constraints);\n }\n\n return streamPromise.catch(this.parseMediaErrorThenThrow);\n } catch (e) {\n this.parseMediaErrorThenThrow(e);\n }\n }\n\n private getParsedConstraints(isDesktopMedia: boolean, streamConstraints?: LocalMediaConstraints): MediaStreamConstraints[] {\n const constraintCandidates: MediaStreamConstraints[] = [];\n\n //TODO - Alex: need to understand which constraints not compatible with screen share\n if (isDesktopMedia) {\n return [{video: true}];\n }\n\n if (!streamConstraints) {\n return constraintCandidates;\n }\n\n const baseConstraints: MediaStreamConstraints = {};\n\n baseConstraints.audio = typeof streamConstraints.audio === 'boolean' ? streamConstraints.audio : undefined;\n\n if (!streamConstraints.video) {\n return [baseConstraints];\n }\n\n baseConstraints.video = {\n facingMode: 'environment',\n frameRate: {ideal: 15, max: 30}\n };\n\n const assignConstraint = (videoResolution: VideoStreamResolution): void => {\n const [width, height] = videoResolution.resolution.split('x');\n const constraintCandidate = assign({}, baseConstraints);\n\n constraintCandidate.video = assign({}, baseConstraints.video, {\n width: _constraintByType(width, videoResolution.constraintType),\n height: _constraintByType(height, videoResolution.constraintType)\n });\n constraintCandidates.push(constraintCandidate);\n };\n\n if ((baseConstraints.video && typeof streamConstraints.video === 'boolean') ||\n !((streamConstraints.video as LocalVideoStreamConstraints).videoResolution instanceof Array)) {\n\n const defaultResolution: VideoStreamResolution = {\n resolution: DEFAULT_VIDEO_RESOLUTION,\n constraintType: DEFAULT_VIDEO_CONSTRAINT_TYPE\n };\n\n assignConstraint(defaultResolution);\n\n return constraintCandidates;\n }\n\n (streamConstraints.video as any).videoResolution.forEach(assignConstraint);\n\n return constraintCandidates;\n }\n\n private parseMediaErrorThenThrow(error: any): MediaRequestFailResult {\n trace.error('Get media stream error.', error);\n const errorName = error && error.name ? error.name : '';\n\n const permissionErrorNames = [\n 'NotAllowedError',\n 'PermissionDismissedError',\n 'PermissionDeniedError'\n\n ];\n\n // const generalError = [\n // 'NotFoundError',\n // 'DevicesNotFoundError',\n // 'NotReadableError',\n // 'TrackStartError'\n // ];\n\n const constraintsErrors = [\n 'OverconstrainedError',\n 'ConstraintNotSatisfiedError'\n\n ];\n\n const isErrorOfType = (errorsArr: string[]) => errorsArr.filter((err: string) => err === errorName).length > 0;\n\n if (isErrorOfType(permissionErrorNames)) {\n throw {errorCode: MediaRequestErrorCode.PermissionDenied, message: errorName};\n }\n\n if (isErrorOfType(constraintsErrors)) {\n throw {errorCode: MediaRequestErrorCode.Overconstrained, message: errorName};\n }\n\n throw {errorCode: MediaRequestErrorCode.GeneralError, message: errorName};\n }\n}\n\nfunction _constraintByType(value: any, type: ConstraintType): any {\n switch (type) {\n case ConstraintType.MIN:\n return {min: value};\n default:\n return {ideal: value};\n }\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 isUndefined from 'lodash/isUndefined';\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 MediaRequestFailResult,\n MediaServiceOptions, 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, DEFAULT_VIDEO_WAIT_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\n\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\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 _autoReconnectEnabled: boolean | undefined;\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: boolean = 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 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 const isOpenTok = this._serviceOptions.mediaServiceType === MediaServiceType.OPENTOK;\n\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(isOpenTok)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n 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._deviceSupportFlags.hasCamera = groupedDevices.video.length > 0;\n this._deviceSupportFlags.hasMicrophone = groupedDevices.audio.length > 0;\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 if (!this._initLocalStreamsPromise) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error('initLocalMediaStreams failure: ', mediaRequestFailResult);\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 //#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 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 if (this._serviceOptions!.mediaServiceType === MediaServiceType.TURNSERVER) {\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n\n } else if (this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK) {\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n } else {\n const allowedValues = values(MediaServiceType);\n\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 /*\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 throw new Error('Voip support cannot be enabled after local stream where initialized');\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) && (isUndefined(this._autoReconnectEnabled) || 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\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n if (lastParams) {\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 }).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 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 registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n const registerTrack = (): 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 if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(registerTrack);\n }\n\n return registerTrack();\n\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 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 }\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 MediaRequestFailResult,\n MediaServiceOptions, 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, DEFAULT_VIDEO_WAIT_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\n\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\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 _autoReconnectEnabled: boolean = 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: boolean = 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 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 const isOpenTok = this._serviceOptions.mediaServiceType === MediaServiceType.OPENTOK;\n\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(isOpenTok)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n 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 speaker: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'audiooutput')\n };\n\n this._deviceSupportFlags.hasCamera = groupedDevices.video.length > 0;\n this._deviceSupportFlags.hasMicrophone = groupedDevices.audio.length > 0;\n trace.info('DashboardTrace-------------------', groupedDevices);\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 if (!this._initLocalStreamsPromise) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error('initLocalMediaStreams failure: ', mediaRequestFailResult);\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 //#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 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 if (this._serviceOptions!.mediaServiceType === MediaServiceType.TURNSERVER) {\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n\n } else if (this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK) {\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n } else {\n const allowedValues = values(MediaServiceType);\n\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 /*\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 throw new Error('Voip support cannot be enabled after local stream where initialized');\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 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 }).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 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 registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n const registerTrack = (): 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 if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(registerTrack);\n }\n\n return registerTrack();\n\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 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 }\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"]}
@@ -21,7 +21,6 @@ var cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
21
21
  var values_1 = __importDefault(require("lodash/values"));
22
22
  var debounce_1 = __importDefault(require("lodash/debounce"));
23
23
  var includes_1 = __importDefault(require("lodash/includes"));
24
- var isUndefined_1 = __importDefault(require("lodash/isUndefined"));
25
24
  var events_1 = require("events");
26
25
  var MediaSubscriber_1 = require("./MediaSubscriber");
27
26
  var LocalStreamManager_1 = require("./LocalStreamManager");
@@ -46,6 +45,7 @@ var TechseeMediaServiceBase = /** @class */function () {
46
45
  this._publishers = new Map();
47
46
  this._publisherPromises = new Map();
48
47
  this._serviceOptions = null;
48
+ this._autoReconnectEnabled = true;
49
49
  this._initLocalStreamsPromise = null;
50
50
  this._initServicePromise = null;
51
51
  this._registeredStreams = new Map();
@@ -124,10 +124,14 @@ var TechseeMediaServiceBase = /** @class */function () {
124
124
  }),
125
125
  audio: filter_1.default(localDevicesList, function (device) {
126
126
  return device.kind.toLowerCase() === 'audioinput';
127
+ }),
128
+ speaker: filter_1.default(localDevicesList, function (device) {
129
+ return device.kind.toLowerCase() === 'audiooutput';
127
130
  })
128
131
  };
129
132
  _this._deviceSupportFlags.hasCamera = groupedDevices.video.length > 0;
130
133
  _this._deviceSupportFlags.hasMicrophone = groupedDevices.audio.length > 0;
134
+ trace.info('DashboardTrace-------------------', groupedDevices);
131
135
  })]).then(function () {
132
136
  return undefined;
133
137
  });
@@ -299,7 +303,7 @@ var TechseeMediaServiceBase = /** @class */function () {
299
303
  var lastParams_1 = this._session && this._session.params ? this._session.params : null;
300
304
  this.disconnectFromSessionInternal(reason).then(function () {
301
305
  var reconnectReasons = [MediaConstants_1.MediaSessionDisconnectReason.InitiatorPeerReconnected, MediaConstants_1.MediaSessionDisconnectReason.SignalingChannelDisconnect, MediaConstants_1.MediaSessionDisconnectReason.PeerConnectionInterrupted, MediaConstants_1.MediaSessionDisconnectReason.PeerConnectionStateChangeTimeout];
302
- if (lastParams_1 && includes_1.default(reconnectReasons, reason) && (isUndefined_1.default(_this._autoReconnectEnabled) || _this._autoReconnectEnabled)) {
306
+ if (lastParams_1 && includes_1.default(reconnectReasons, reason) && _this._autoReconnectEnabled) {
303
307
  _this.reconnectToSession(lastParams_1);
304
308
  } else if (!_this._autoReconnectEnabled) {
305
309
  trace.info('No reconnection- auto reconnect disabled');
@@ -313,6 +317,7 @@ var TechseeMediaServiceBase = /** @class */function () {
313
317
  };
314
318
  TechseeMediaServiceBase.prototype.setAutoReconnect = function (state) {
315
319
  this._autoReconnectEnabled = state;
320
+ trace.info('_autoReconnectEnabled:', state);
316
321
  if (this._autoReconnectEnabled && !this.isSessionActive) {
317
322
  var lastParams = this._session && this._session.params ? this._session.params : null;
318
323
  if (lastParams) {
@@ -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","filter_1","require","cloneDeep_1","values_1","debounce_1","includes_1","isUndefined_1","events_1","MediaSubscriber_1","LocalStreamManager_1","TechseeMediaStream_1","SessionTurn_1","MediaDomUtils_1","Compatibility_1","SessionOpentok_1","MediaConstants_1","guards_1","MediaTracer_1","trace","getMediaTracer","traceStatsInfo","default","info","leading","maxWait","privateEvents","TechseeMediaServiceBase","environment","webRtcSupportInfo","_subscribers","Map","_publishers","_publisherPromises","_serviceOptions","_initLocalStreamsPromise","_initServicePromise","_registeredStreams","_isVoipEnabled","_session","bindClassMethods","_emitter","EventEmitter","_environment","_deviceSupportFlags","videoPlayback","hasCamera","hasMicrophone","_localStreamsManager","LocalStreamManager","_sessionStreamsManager","getMediaStreamForRole","getStreamForDestinationRole","addRemoteMediaTrack","registerRemoteMediaTrack","removeMediaTrack","unregisterRemoteMediaTrack","get","serviceInitGuard","enumerable","configurable","initMediaService","serviceOptions","_this","warnMessage","console","warn","Promise","resolve","isOpenTok","mediaServiceType","MediaServiceType","OPENTOK","all","isVideoPlaySupportedOnDevice","then","isSupported","enumerateMediaDevices","catch","error","localDevicesList","groupedDevices","video","device","kind","toLowerCase","audio","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","connectToSession","sessionParams","params","connectPromise","doAsyncReject","setTimeout","sessionHandlers","onDisconnectedHandler","onSessionDisconnectHandler","mediaSessionParams","peerConnectivityTimeoutSeconds","DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS","TURNSERVER","instance","TurnWebRtcSession","OpentokSession","allowedValues","Error","connect","disconnectFromSession","disconnectFromSessionInternal","MediaSessionDisconnectReason","ForcedByConsumer","updateSessionCredentials","credentials","sessionExistsGuard","enableVoipDuringSession","isLocalStreamInitialized","reason","lastSession","disconnect","clearPublishers","getStatsForRemoteTrack","streamForStats","getRemoteTrackStats","mediaTrack","trackStats","JSON","stringify","trackId","id","lastParams_1","reconnectReasons","InitiatorPeerReconnected","SignalingChannelDisconnect","PeerConnectionInterrupted","PeerConnectionStateChangeTimeout","_autoReconnectEnabled","reconnectToSession","setAutoReconnect","state","isSessionActive","lastParams","getSnapshotFromKnownStream","sourceStream","snapshotOptions","KnownMediaStream","USER_VIDEO_STREAM","USER_SCREEN_SHARE_STREAM","snapshotStream","getSnapshotFromMediaStream","mediaStream","imageData","urlComponents","split","mimeType","bytes","atob","buffer","ArrayBuffer","rawData","Uint8Array","charCodeAt","blob","Blob","type","objectUrl","window","URL","createObjectURL","result","base64img","imageBlob","clearService","clearSubscribers","clearRegisteredStreams","clearAllStreams","removeAllListeners","registerLocalMediaStream","tsMediaStream","registerStream","changeEnableForKnownStream","isPaused","streamToChangeState","enabled","isRemote","streamKind","KnownMediaStreamKind","Audio","subscribers","forEach","muteSound","event","on","emitEvent","eventArgs","emit","remoteMediaTrack","trackType","currentStream","newDedicatedStream","TechseeMediaStream","registerTrack","onended","unregisterTechseeMediaStream","MediaStreamUnregisterReason","NativeEvent","updateSubscribersWithNewStream","ReplacingStream","streamToUnregister","traceError","promises","push","stopRendering","publisher","streamTypes","find","onMediaStreamDestroyed","destinationRole","registeredStream","ClosedRemotely","removePublisher","publisherPromise","createMediaPublisher","mediaPublisher","ex","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 });\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 isUndefined_1 = __importDefault(require(\"lodash/isUndefined\"));\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 MediaTracer_1 = require(\"./MediaUtils/MediaTracer\");\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._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 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: true,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isVoipEnabled\", {\n get: function () {\n return this._isVoipEnabled;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isSessionActive\", {\n get: function () {\n return this._session !== null;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isLocalStreamInitialized\", {\n get: function () {\n return this._initLocalStreamsPromise !== null;\n },\n enumerable: true,\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 var isOpenTok = this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK;\n this._initServicePromise = Promise.all([\n Compatibility_1.isVideoPlaySupportedOnDevice(isOpenTok)\n .then(function (isSupported) { return _this._deviceSupportFlags.videoPlayback = isSupported; }),\n 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._deviceSupportFlags.hasCamera = groupedDevices.video.length > 0;\n _this._deviceSupportFlags.hasMicrophone = 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 if (!this._initLocalStreamsPromise) {\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 //#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 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({}, sessionParams, { peerConnectivityTimeoutSeconds: _this._serviceOptions.peerConnectivityTimeoutSeconds || MediaConstants_1.DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS });\n trace.info('connectToSession', sessionParams);\n if (_this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.TURNSERVER) {\n _this._session.instance = new SessionTurn_1.TurnWebRtcSession(mediaSessionParams, sessionHandlers, _this._sessionStreamsManager);\n }\n else if (_this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK) {\n _this._session.instance = new SessionOpentok_1.OpentokSession(mediaSessionParams, _this._sessionStreamsManager);\n }\n else {\n var allowedValues = values_1.default(MediaConstants_1.MediaServiceType);\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 /*\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 throw new Error('Voip support cannot be enabled after local stream where initialized');\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) && (isUndefined_1.default(_this._autoReconnectEnabled) || _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 if (this._autoReconnectEnabled && !this.isSessionActive) {\n var lastParams = this._session && this._session.params ? this._session.params : null;\n if (lastParams) {\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 }).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: true,\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.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.registerStream = function (mediaStream) {\n var _this = this;\n var currentStream = this._registeredStreams.get(mediaStream.streamType);\n var registerTrack = function () {\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 if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaConstants_1.MediaStreamUnregisterReason.ReplacingStream).then(registerTrack);\n }\n return registerTrack();\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 _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 };\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 isUndefined from 'lodash/isUndefined';\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 MediaRequestFailResult,\n MediaServiceOptions, 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, DEFAULT_VIDEO_WAIT_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\n\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\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 _autoReconnectEnabled: boolean | undefined;\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: boolean = 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 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 const isOpenTok = this._serviceOptions.mediaServiceType === MediaServiceType.OPENTOK;\n\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(isOpenTok)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n 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._deviceSupportFlags.hasCamera = groupedDevices.video.length > 0;\n this._deviceSupportFlags.hasMicrophone = groupedDevices.audio.length > 0;\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 if (!this._initLocalStreamsPromise) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error('initLocalMediaStreams failure: ', mediaRequestFailResult);\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 //#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 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 if (this._serviceOptions!.mediaServiceType === MediaServiceType.TURNSERVER) {\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n\n } else if (this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK) {\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n } else {\n const allowedValues = values(MediaServiceType);\n\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 /*\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 throw new Error('Voip support cannot be enabled after local stream where initialized');\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) && (isUndefined(this._autoReconnectEnabled) || 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\n if (this._autoReconnectEnabled && !this.isSessionActive) {\n const lastParams = this._session && this._session.params ? this._session.params : null;\n\n if (lastParams) {\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 }).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 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 registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n const registerTrack = (): 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 if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(registerTrack);\n }\n\n return registerTrack();\n\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 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 }\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","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","MediaTracer_1","trace","getMediaTracer","traceStatsInfo","default","info","leading","maxWait","privateEvents","TechseeMediaServiceBase","environment","webRtcSupportInfo","_subscribers","Map","_publishers","_publisherPromises","_serviceOptions","_autoReconnectEnabled","_initLocalStreamsPromise","_initServicePromise","_registeredStreams","_isVoipEnabled","_session","bindClassMethods","_emitter","EventEmitter","_environment","_deviceSupportFlags","videoPlayback","hasCamera","hasMicrophone","_localStreamsManager","LocalStreamManager","_sessionStreamsManager","getMediaStreamForRole","getStreamForDestinationRole","addRemoteMediaTrack","registerRemoteMediaTrack","removeMediaTrack","unregisterRemoteMediaTrack","get","serviceInitGuard","enumerable","configurable","initMediaService","serviceOptions","_this","warnMessage","console","warn","Promise","resolve","isOpenTok","mediaServiceType","MediaServiceType","OPENTOK","all","isVideoPlaySupportedOnDevice","then","isSupported","enumerateMediaDevices","catch","error","localDevicesList","groupedDevices","video","device","kind","toLowerCase","audio","speaker","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","connectToSession","sessionParams","params","connectPromise","doAsyncReject","setTimeout","sessionHandlers","onDisconnectedHandler","onSessionDisconnectHandler","mediaSessionParams","peerConnectivityTimeoutSeconds","DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS","TURNSERVER","instance","TurnWebRtcSession","OpentokSession","allowedValues","Error","connect","disconnectFromSession","disconnectFromSessionInternal","MediaSessionDisconnectReason","ForcedByConsumer","updateSessionCredentials","credentials","sessionExistsGuard","enableVoipDuringSession","isLocalStreamInitialized","reason","lastSession","disconnect","clearPublishers","getStatsForRemoteTrack","streamForStats","getRemoteTrackStats","mediaTrack","trackStats","JSON","stringify","trackId","id","lastParams_1","reconnectReasons","InitiatorPeerReconnected","SignalingChannelDisconnect","PeerConnectionInterrupted","PeerConnectionStateChangeTimeout","reconnectToSession","setAutoReconnect","state","isSessionActive","lastParams","getSnapshotFromKnownStream","sourceStream","snapshotOptions","KnownMediaStream","USER_VIDEO_STREAM","USER_SCREEN_SHARE_STREAM","snapshotStream","getSnapshotFromMediaStream","mediaStream","imageData","urlComponents","split","mimeType","bytes","atob","buffer","ArrayBuffer","rawData","Uint8Array","charCodeAt","blob","Blob","type","objectUrl","window","URL","createObjectURL","result","base64img","imageBlob","clearService","clearSubscribers","clearRegisteredStreams","clearAllStreams","removeAllListeners","registerLocalMediaStream","tsMediaStream","registerStream","changeEnableForKnownStream","isPaused","streamToChangeState","enabled","isRemote","streamKind","KnownMediaStreamKind","Audio","subscribers","forEach","muteSound","event","on","emitEvent","eventArgs","emit","remoteMediaTrack","trackType","currentStream","newDedicatedStream","TechseeMediaStream","registerTrack","onended","unregisterTechseeMediaStream","MediaStreamUnregisterReason","NativeEvent","updateSubscribersWithNewStream","ReplacingStream","streamToUnregister","traceError","promises","push","stopRendering","publisher","streamTypes","find","onMediaStreamDestroyed","destinationRole","registeredStream","ClosedRemotely","removePublisher","publisherPromise","createMediaPublisher","mediaPublisher","ex","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 });\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 MediaTracer_1 = require(\"./MediaUtils/MediaTracer\");\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._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 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: true,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isVoipEnabled\", {\n get: function () {\n return this._isVoipEnabled;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isSessionActive\", {\n get: function () {\n return this._session !== null;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(TechseeMediaServiceBase.prototype, \"isLocalStreamInitialized\", {\n get: function () {\n return this._initLocalStreamsPromise !== null;\n },\n enumerable: true,\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 var isOpenTok = this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK;\n this._initServicePromise = Promise.all([\n Compatibility_1.isVideoPlaySupportedOnDevice(isOpenTok)\n .then(function (isSupported) { return _this._deviceSupportFlags.videoPlayback = isSupported; }),\n 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 speaker: filter_1.default(localDevicesList, function (device) { return device.kind.toLowerCase() === 'audiooutput'; })\n };\n _this._deviceSupportFlags.hasCamera = groupedDevices.video.length > 0;\n _this._deviceSupportFlags.hasMicrophone = groupedDevices.audio.length > 0;\n trace.info('DashboardTrace-------------------', groupedDevices);\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 if (!this._initLocalStreamsPromise) {\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 //#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 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({}, sessionParams, { peerConnectivityTimeoutSeconds: _this._serviceOptions.peerConnectivityTimeoutSeconds || MediaConstants_1.DEFAULT_PEER_CONNECTIVITY_TIMEOUT_SECONDS });\n trace.info('connectToSession', sessionParams);\n if (_this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.TURNSERVER) {\n _this._session.instance = new SessionTurn_1.TurnWebRtcSession(mediaSessionParams, sessionHandlers, _this._sessionStreamsManager);\n }\n else if (_this._serviceOptions.mediaServiceType === MediaConstants_1.MediaServiceType.OPENTOK) {\n _this._session.instance = new SessionOpentok_1.OpentokSession(mediaSessionParams, _this._sessionStreamsManager);\n }\n else {\n var allowedValues = values_1.default(MediaConstants_1.MediaServiceType);\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 /*\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 throw new Error('Voip support cannot be enabled after local stream where initialized');\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 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 }).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: true,\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.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.registerStream = function (mediaStream) {\n var _this = this;\n var currentStream = this._registeredStreams.get(mediaStream.streamType);\n var registerTrack = function () {\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 if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaConstants_1.MediaStreamUnregisterReason.ReplacingStream).then(registerTrack);\n }\n return registerTrack();\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 _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 };\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 MediaRequestFailResult,\n MediaServiceOptions, 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, DEFAULT_VIDEO_WAIT_TIMEOUT_SECONDS,\n KnownMediaStream,\n KnownMediaStreamKind,\n MediaServiceType,\n MediaSessionDisconnectReason,\n MediaStreamUnregisterReason,\n SessionClientRole\n} from './MediaConstants';\n\nimport {throwableGuard} from '@techsee/techsee-common/lib/core/guards';\n\nimport {getMediaTracer} from './MediaUtils/MediaTracer';\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 _autoReconnectEnabled: boolean = 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: boolean = 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 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 const isOpenTok = this._serviceOptions.mediaServiceType === MediaServiceType.OPENTOK;\n\n this._initServicePromise = Promise.all([\n isVideoPlaySupportedOnDevice(isOpenTok)\n .then((isSupported: boolean) => this._deviceSupportFlags.videoPlayback = isSupported),\n 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 speaker: filter(localDevicesList, (device) => device.kind.toLowerCase() === 'audiooutput')\n };\n\n this._deviceSupportFlags.hasCamera = groupedDevices.video.length > 0;\n this._deviceSupportFlags.hasMicrophone = groupedDevices.audio.length > 0;\n trace.info('DashboardTrace-------------------', groupedDevices);\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 if (!this._initLocalStreamsPromise) {\n trace.info('initLocalMediaStreams creating media streams');\n this._initLocalStreamsPromise = this.getLocalMediaImplementation()\n .catch((mediaRequestFailResult: MediaRequestFailResult) => {\n trace.error('initLocalMediaStreams failure: ', mediaRequestFailResult);\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 //#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 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 if (this._serviceOptions!.mediaServiceType === MediaServiceType.TURNSERVER) {\n this._session!.instance = new TurnWebRtcSession(mediaSessionParams, sessionHandlers, this._sessionStreamsManager);\n\n } else if (this._serviceOptions!.mediaServiceType === MediaServiceType.OPENTOK) {\n this._session!.instance = new OpentokSession(mediaSessionParams, this._sessionStreamsManager);\n } else {\n const allowedValues = values(MediaServiceType);\n\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 /*\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 throw new Error('Voip support cannot be enabled after local stream where initialized');\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 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 }).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 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 registerStream(mediaStream: TechseeMediaStream): Promise<void> {\n const currentStream = this._registeredStreams.get(mediaStream.streamType);\n\n const registerTrack = (): 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 if (currentStream) {\n return this.unregisterTechseeMediaStream(currentStream, MediaStreamUnregisterReason.ReplacingStream).then(registerTrack);\n }\n\n return registerTrack();\n\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 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 }\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techsee/techsee-media-service",
3
- "version": "999.7.6-sap",
3
+ "version": "999.7.6",
4
4
  "description": "Techsee Media Service Client",
5
5
  "author": "TechSee - Alex Danilov",
6
6
  "main": "lib/index.js",
@@ -12,27 +12,44 @@
12
12
  "clean": "gulp --gulpfile ./.tools/build.js --clean",
13
13
  "lint": "./node_modules/.bin/eslint --ext .js --ext .jsx --ext .ts ./src ",
14
14
  "lint-fix": "./node_modules/.bin/eslint --ext .js --ext .jsx --ext .ts ./src --fix",
15
- "tag": "git tag v$npm_package_version -f | git push -f --tags"
15
+ "tag": "git tag v$npm_package_version -f | git push -f --tags",
16
+ "demo": "start-storybook -p 9003 -c .storybook -s ./ && open http://localhost:9003/"
16
17
  },
17
18
  "dependencies": {
18
19
  "@techsee/kurento-utils-temasys-mod": "6.6.3-dev-3",
19
- "@techsee/techsee-common": "^0.18.68",
20
+ "@techsee/techsee-common": "^0.18.90",
21
+ "bluebird": "~3.7.2",
20
22
  "kurento-utils": "6.6.2",
21
23
  "lodash": "4.13.1",
22
24
  "socket.io-client": "2.1.1",
23
25
  "webrtc-adapter": "6.3.0"
24
26
  },
25
27
  "devDependencies": {
28
+ "@storybook/addon-actions": "~5.2.8",
29
+ "@storybook/addon-info": "~3.4.12",
30
+ "@storybook/addon-knobs": "~3.4.12",
31
+ "@storybook/addon-options": "~3.4.12",
32
+ "@storybook/addon-storysource": "~5.2.8",
33
+ "@storybook/addon-viewport": "~5.2.8",
34
+ "@storybook/react": "~5.2.8",
35
+ "@types/bluebird": "~3.5.29",
36
+ "@types/jest": "~24.0.13",
26
37
  "@types/lodash": "~4.14.129",
27
38
  "@types/node": "~10.12.18",
28
- "@types/jest": "~24.0.13",
29
39
  "@types/socket.io-client": "~1.4.32",
40
+ "@types/storybook__addon-actions": "~3.4.3",
41
+ "@types/storybook__react": "~3.0.9",
30
42
  "@typescript-eslint/eslint-plugin": "~1.5.0",
31
43
  "babel-core": "~6.26.3",
44
+ "babel-loader": "~7.1.5",
45
+ "babel-plugin-transform-class-properties": "~6.24.1",
32
46
  "babel-plugin-transform-decorators-legacy": "~1.3.5",
47
+ "babel-plugin-transform-runtime": "~6.23.0",
33
48
  "babel-preset-env": "~1.7.0",
34
49
  "babel-preset-es2015": "~6.24.1",
50
+ "babel-preset-react": "~6.24.1",
35
51
  "babel-preset-stage-3": "~6.24.1",
52
+ "babel-runtime": "~6.26.0",
36
53
  "del": "~4.1.1",
37
54
  "eslint": "~5.16.0",
38
55
  "eslint-plugin-import": "~2.16.0",
@@ -45,8 +62,12 @@
45
62
  "gulp-typescript": "~5.0.1",
46
63
  "jest": "~24.8.0",
47
64
  "merge-stream": "~1.0.1",
65
+ "open-browser-webpack-plugin": "0.0.5",
66
+ "react": "~16.12.0",
67
+ "react-dom": "~16.12.0",
48
68
  "socket.io": "~2.2.0",
49
69
  "ts-jest": "~24.0.2",
70
+ "ts-loader": "~6.2.1",
50
71
  "typescript": "~3.3.4000"
51
72
  },
52
73
  "license": "ISC",