@ohos-ports/genesys-cloud-streaming-client 20.1.0-beta.1

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.
Files changed (176) hide show
  1. package/README.md +47 -0
  2. package/dist/cjs/alerting-leader.d.ts +29 -0
  3. package/dist/cjs/alerting-leader.js +163 -0
  4. package/dist/cjs/client.d.ts +84 -0
  5. package/dist/cjs/client.js +680 -0
  6. package/dist/cjs/connection-manager.d.ts +18 -0
  7. package/dist/cjs/connection-manager.js +120 -0
  8. package/dist/cjs/connection-transfer.d.ts +7 -0
  9. package/dist/cjs/connection-transfer.js +20 -0
  10. package/dist/cjs/http-client.d.ts +18 -0
  11. package/dist/cjs/http-client.js +230 -0
  12. package/dist/cjs/index.d.ts +10 -0
  13. package/dist/cjs/index.js +31 -0
  14. package/dist/cjs/messenger.d.ts +37 -0
  15. package/dist/cjs/messenger.js +54 -0
  16. package/dist/cjs/notifications.d.ts +94 -0
  17. package/dist/cjs/notifications.js +495 -0
  18. package/dist/cjs/ping.d.ts +21 -0
  19. package/dist/cjs/ping.js +60 -0
  20. package/dist/cjs/polyfills.d.ts +0 -0
  21. package/dist/cjs/polyfills.js +6 -0
  22. package/dist/cjs/server-monitor.d.ts +16 -0
  23. package/dist/cjs/server-monitor.js +44 -0
  24. package/dist/cjs/stanza-definitions/webrtc-signaling.d.ts +29 -0
  25. package/dist/cjs/stanza-definitions/webrtc-signaling.js +113 -0
  26. package/dist/cjs/stanza-definitions/xep0051.d.ts +19 -0
  27. package/dist/cjs/stanza-definitions/xep0051.js +28 -0
  28. package/dist/cjs/stats-formatter.d.ts +6 -0
  29. package/dist/cjs/stats-formatter.js +63 -0
  30. package/dist/cjs/types/genesys-cloud-media-session.d.ts +64 -0
  31. package/dist/cjs/types/genesys-cloud-media-session.js +353 -0
  32. package/dist/cjs/types/interfaces.d.ts +310 -0
  33. package/dist/cjs/types/interfaces.js +22 -0
  34. package/dist/cjs/types/libs.d.ts +32 -0
  35. package/dist/cjs/types/libs.js +1 -0
  36. package/dist/cjs/types/media-session.d.ts +76 -0
  37. package/dist/cjs/types/media-session.js +2 -0
  38. package/dist/cjs/types/named-agent.d.ts +13 -0
  39. package/dist/cjs/types/named-agent.js +2 -0
  40. package/dist/cjs/types/offline-error.d.ts +2 -0
  41. package/dist/cjs/types/offline-error.js +5 -0
  42. package/dist/cjs/types/sasl-error.d.ts +8 -0
  43. package/dist/cjs/types/sasl-error.js +12 -0
  44. package/dist/cjs/types/stanza-media-session.d.ts +56 -0
  45. package/dist/cjs/types/stanza-media-session.js +255 -0
  46. package/dist/cjs/types/streaming-client-extension.d.ts +14 -0
  47. package/dist/cjs/types/streaming-client-extension.js +2 -0
  48. package/dist/cjs/types/timeout-error.d.ts +3 -0
  49. package/dist/cjs/types/timeout-error.js +9 -0
  50. package/dist/cjs/types/user-cancelled-error.d.ts +2 -0
  51. package/dist/cjs/types/user-cancelled-error.js +5 -0
  52. package/dist/cjs/utils.d.ts +34 -0
  53. package/dist/cjs/utils.js +202 -0
  54. package/dist/cjs/webrtc.d.ts +126 -0
  55. package/dist/cjs/webrtc.js +891 -0
  56. package/dist/deploy-info.json +21 -0
  57. package/dist/es/alerting-leader.d.ts +29 -0
  58. package/dist/es/alerting-leader.js +177 -0
  59. package/dist/es/client.d.ts +84 -0
  60. package/dist/es/client.js +699 -0
  61. package/dist/es/connection-manager.d.ts +18 -0
  62. package/dist/es/connection-manager.js +124 -0
  63. package/dist/es/connection-transfer.d.ts +7 -0
  64. package/dist/es/connection-transfer.js +16 -0
  65. package/dist/es/http-client.d.ts +18 -0
  66. package/dist/es/http-client.js +194 -0
  67. package/dist/es/index.bundle.js +39629 -0
  68. package/dist/es/index.d.ts +10 -0
  69. package/dist/es/index.js +10 -0
  70. package/dist/es/messenger.d.ts +37 -0
  71. package/dist/es/messenger.js +62 -0
  72. package/dist/es/notifications.d.ts +94 -0
  73. package/dist/es/notifications.js +507 -0
  74. package/dist/es/ping.d.ts +21 -0
  75. package/dist/es/ping.js +68 -0
  76. package/dist/es/polyfills.d.ts +0 -0
  77. package/dist/es/polyfills.js +6 -0
  78. package/dist/es/server-monitor.d.ts +16 -0
  79. package/dist/es/server-monitor.js +41 -0
  80. package/dist/es/stanza-definitions/webrtc-signaling.d.ts +29 -0
  81. package/dist/es/stanza-definitions/webrtc-signaling.js +110 -0
  82. package/dist/es/stanza-definitions/xep0051.d.ts +19 -0
  83. package/dist/es/stanza-definitions/xep0051.js +25 -0
  84. package/dist/es/stats-formatter.d.ts +6 -0
  85. package/dist/es/stats-formatter.js +54 -0
  86. package/dist/es/types/genesys-cloud-media-session.d.ts +64 -0
  87. package/dist/es/types/genesys-cloud-media-session.js +379 -0
  88. package/dist/es/types/interfaces.d.ts +310 -0
  89. package/dist/es/types/interfaces.js +19 -0
  90. package/dist/es/types/libs.d.ts +32 -0
  91. package/dist/es/types/libs.js +1 -0
  92. package/dist/es/types/media-session.d.ts +76 -0
  93. package/dist/es/types/media-session.js +1 -0
  94. package/dist/es/types/named-agent.d.ts +13 -0
  95. package/dist/es/types/named-agent.js +1 -0
  96. package/dist/es/types/offline-error.d.ts +2 -0
  97. package/dist/es/types/offline-error.js +2 -0
  98. package/dist/es/types/sasl-error.d.ts +8 -0
  99. package/dist/es/types/sasl-error.js +9 -0
  100. package/dist/es/types/stanza-media-session.d.ts +56 -0
  101. package/dist/es/types/stanza-media-session.js +264 -0
  102. package/dist/es/types/streaming-client-extension.d.ts +14 -0
  103. package/dist/es/types/streaming-client-extension.js +1 -0
  104. package/dist/es/types/timeout-error.d.ts +3 -0
  105. package/dist/es/types/timeout-error.js +5 -0
  106. package/dist/es/types/user-cancelled-error.d.ts +2 -0
  107. package/dist/es/types/user-cancelled-error.js +2 -0
  108. package/dist/es/utils.d.ts +34 -0
  109. package/dist/es/utils.js +192 -0
  110. package/dist/es/webrtc.d.ts +126 -0
  111. package/dist/es/webrtc.js +917 -0
  112. package/dist/manifest.json +19 -0
  113. package/dist/npm/CHANGELOG.md +637 -0
  114. package/dist/npm/README.md +47 -0
  115. package/dist/npm/alerting-leader.d.ts +29 -0
  116. package/dist/npm/alerting-leader.js +163 -0
  117. package/dist/npm/client.d.ts +84 -0
  118. package/dist/npm/client.js +680 -0
  119. package/dist/npm/connection-manager.d.ts +18 -0
  120. package/dist/npm/connection-manager.js +120 -0
  121. package/dist/npm/connection-transfer.d.ts +7 -0
  122. package/dist/npm/connection-transfer.js +20 -0
  123. package/dist/npm/http-client.d.ts +18 -0
  124. package/dist/npm/http-client.js +230 -0
  125. package/dist/npm/index.d.ts +10 -0
  126. package/dist/npm/index.js +31 -0
  127. package/dist/npm/messenger.d.ts +37 -0
  128. package/dist/npm/messenger.js +54 -0
  129. package/dist/npm/module.js +10 -0
  130. package/dist/npm/notifications.d.ts +94 -0
  131. package/dist/npm/notifications.js +495 -0
  132. package/dist/npm/ping.d.ts +21 -0
  133. package/dist/npm/ping.js +60 -0
  134. package/dist/npm/polyfills.d.ts +0 -0
  135. package/dist/npm/polyfills.js +6 -0
  136. package/dist/npm/server-monitor.d.ts +16 -0
  137. package/dist/npm/server-monitor.js +44 -0
  138. package/dist/npm/stanza-definitions/webrtc-signaling.d.ts +29 -0
  139. package/dist/npm/stanza-definitions/webrtc-signaling.js +113 -0
  140. package/dist/npm/stanza-definitions/xep0051.d.ts +19 -0
  141. package/dist/npm/stanza-definitions/xep0051.js +28 -0
  142. package/dist/npm/stats-formatter.d.ts +6 -0
  143. package/dist/npm/stats-formatter.js +63 -0
  144. package/dist/npm/types/genesys-cloud-media-session.d.ts +64 -0
  145. package/dist/npm/types/genesys-cloud-media-session.js +353 -0
  146. package/dist/npm/types/interfaces.d.ts +310 -0
  147. package/dist/npm/types/interfaces.js +22 -0
  148. package/dist/npm/types/libs.d.ts +32 -0
  149. package/dist/npm/types/libs.js +1 -0
  150. package/dist/npm/types/media-session.d.ts +76 -0
  151. package/dist/npm/types/media-session.js +2 -0
  152. package/dist/npm/types/named-agent.d.ts +13 -0
  153. package/dist/npm/types/named-agent.js +2 -0
  154. package/dist/npm/types/offline-error.d.ts +2 -0
  155. package/dist/npm/types/offline-error.js +5 -0
  156. package/dist/npm/types/sasl-error.d.ts +8 -0
  157. package/dist/npm/types/sasl-error.js +12 -0
  158. package/dist/npm/types/stanza-media-session.d.ts +56 -0
  159. package/dist/npm/types/stanza-media-session.js +255 -0
  160. package/dist/npm/types/streaming-client-extension.d.ts +14 -0
  161. package/dist/npm/types/streaming-client-extension.js +2 -0
  162. package/dist/npm/types/timeout-error.d.ts +3 -0
  163. package/dist/npm/types/timeout-error.js +9 -0
  164. package/dist/npm/types/user-cancelled-error.d.ts +2 -0
  165. package/dist/npm/types/user-cancelled-error.js +5 -0
  166. package/dist/npm/utils.d.ts +34 -0
  167. package/dist/npm/utils.js +207 -0
  168. package/dist/npm/webrtc.d.ts +126 -0
  169. package/dist/npm/webrtc.js +893 -0
  170. package/dist/streaming-client.browser.js +2 -0
  171. package/dist/streaming-client.browser.js.LICENSE.txt +12 -0
  172. package/dist/v20/streaming-client.browser.js +2 -0
  173. package/dist/v20/streaming-client.browser.js.LICENSE.txt +12 -0
  174. package/dist/v20.1.0/streaming-client.browser.js +2 -0
  175. package/dist/v20.1.0/streaming-client.browser.js.LICENSE.txt +12 -0
  176. package/package.json +132 -0
@@ -0,0 +1,917 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { EventEmitter } from 'events';
11
+ import { toBare } from 'stanza/JID';
12
+ import { LRUCache } from 'lru-cache';
13
+ import { JingleAction } from 'stanza/Constants';
14
+ import throttle from 'lodash.throttle';
15
+ import { isFirefox } from 'browserama';
16
+ import { definitions } from './stanza-definitions/webrtc-signaling';
17
+ import { isAcdJid, isScreenRecordingJid, isLiveScreenMonitoringJid, isSoftphoneJid, isVideoJid, calculatePayloadSize, retryPromise, iceIsDifferent } from './utils';
18
+ import { Client } from './client';
19
+ import { deepFlatten, formatStatsEvent } from './stats-formatter';
20
+ import { SessionTypes } from './types/interfaces';
21
+ import { StanzaMediaSession } from './types/stanza-media-session';
22
+ import { GenesysCloudMediaSession } from './types/genesys-cloud-media-session';
23
+ import { HttpClient } from './http-client';
24
+ const events = {
25
+ REQUEST_WEBRTC_DUMP: 'requestWebrtcDump',
26
+ /* jingle messaging */
27
+ REQUEST_INCOMING_RTCSESSION: 'requestIncomingRtcSession',
28
+ CANCEL_INCOMING_RTCSESSION: 'cancelIncomingRtcSession',
29
+ HANDLED_INCOMING_RTCSESSION: 'handledIncomingRtcSession',
30
+ OUTGOING_RTCSESSION_PROCEED: 'outgoingRtcSessionProceed',
31
+ OUTGOING_RTCSESSION_REJECTED: 'outgoingRtcSessionRejected',
32
+ // /* jingle */
33
+ // RTC_ICESERVERS: 'rtcIceServers', // ice servers have been discovered
34
+ INCOMING_RTCSESSION: 'incomingRtcSession',
35
+ OUTGOING_RTCSESSION: 'outgoingRtcSession',
36
+ RTCSESSION_ERROR: 'rtcSessionError' // jingle error occurred
37
+ // TRACE_RTCSESSION: 'traceRtcSession', // trace messages for logging, etc
38
+ // UPGRADE_MEDIA_ERROR: 'upgradeMediaError', // error occurred joining conference
39
+ // /* other */
40
+ // UPDATE_MEDIA_PRESENCE: 'updateMediaPresence',
41
+ // LASTN_CHANGE: 'lastNChange'
42
+ };
43
+ const desiredMaxStatsSize = 15000;
44
+ const MAX_DISCO_RETRIES = 2;
45
+ const ICE_SERVER_TIMEOUT = 15000; // 15 seconds
46
+ const ICE_SERVER_REFRESH_PERIOD = 1000 * 60 * 60 * 6; // 6 hours
47
+ export class WebrtcExtension extends EventEmitter {
48
+ constructor(client, clientOptions) {
49
+ super();
50
+ // sessionId maps to boolean where `true` means the sessionId is ignored
51
+ this.ignoredSessions = new LRUCache({ max: 10, ttl: 10 * 60 * 60 * 6 });
52
+ // sessionId maps to a list of sdp ice candidates
53
+ // hold onto early candidates for 1 minute
54
+ this.earlyIceCandidates = new LRUCache({ max: 10, ttl: 1000 * 60, ttlAutopurge: true });
55
+ this.pendingSessions = {};
56
+ this.statsArr = [];
57
+ this.throttleSendStatsInterval = 25000;
58
+ this.currentMaxStatSize = desiredMaxStatsSize;
59
+ this.statsSizeDecreaseAmount = 3000;
60
+ this.statBuffer = 0;
61
+ this.discoRetries = 0;
62
+ this.iceServers = [];
63
+ this.stanzaSessions = [];
64
+ this.webrtcSessions = [];
65
+ // Store a maximum of 5 previous non-duplicate reinvites.
66
+ // These will automatically be purged after three minutes.
67
+ this.reinviteCache = new LRUCache({
68
+ max: 5,
69
+ ttl: 1000 * 60 * 3
70
+ });
71
+ this.sessionsMap = {};
72
+ this.client = client;
73
+ this.config = {
74
+ allowIPv6: clientOptions.allowIPv6 === true,
75
+ optOutOfWebrtcStatsTelemetry: clientOptions.optOutOfWebrtcStatsTelemetry
76
+ };
77
+ this.logger = client.logger;
78
+ this.addEventListeners();
79
+ this.throttledSendStats = throttle(this.sendStats.bind(this), this.throttleSendStatsInterval, { leading: false, trailing: true });
80
+ this.client.on('jingle:outgoing', (session) => {
81
+ this.logger.info('Emitting jingle:outgoing media-session (session-init)', session.sid);
82
+ return this.emit(events.OUTGOING_RTCSESSION, session);
83
+ });
84
+ this.client.on('jingle:incoming', (session) => {
85
+ this.logger.info('Emitting jingle:incoming media-session (session-init)', session.sid);
86
+ return this.emit(events.INCOMING_RTCSESSION, session);
87
+ });
88
+ this.client.on('jingle:created', (session) => {
89
+ // if this is not a JingleSession, this is a generic BaseSession from jingle, aka dummy session.
90
+ // in this case, we can just kill this session because we will be using
91
+ if (!(session instanceof StanzaMediaSession)) {
92
+ session.end('cancel', true);
93
+ }
94
+ });
95
+ window.addEventListener('offline', () => this.onOnlineStatusChange(false));
96
+ window.addEventListener('online', () => this.onOnlineStatusChange(true));
97
+ }
98
+ get jid() {
99
+ var _a;
100
+ return (_a = this.stanzaInstance) === null || _a === void 0 ? void 0 : _a.jid;
101
+ }
102
+ onOnlineStatusChange(online) {
103
+ this.addStatToQueue({
104
+ actionName: 'WebrtcStats',
105
+ details: {
106
+ _eventType: 'onlineStatus',
107
+ _eventTimestamp: new Date().getTime(),
108
+ online
109
+ }
110
+ });
111
+ }
112
+ handleStanzaInstanceChange(stanzaInstance) {
113
+ return __awaiter(this, void 0, void 0, function* () {
114
+ this.stanzaInstance = stanzaInstance;
115
+ // We create a new Stanza instance every time we connect. If we still have active StanzaMediaSessions,
116
+ // the new instance doesn't know about them and won't properly process actions for those sessions
117
+ // (e.g. it will reply to a `session-terminate`, but the session itself won't receive that action,
118
+ // because Stanza doesn't know about it). Since we track those sessions ourselves, we can tell
119
+ // the new Stanza instance about them so those actions will be processed properly.
120
+ const sessionsMap = this.stanzaSessions.reduce((currentMap, session) => {
121
+ session.parent = stanzaInstance.jingle;
122
+ return Object.assign(Object.assign({}, currentMap), { [session.id]: session });
123
+ }, {});
124
+ this.stanzaInstance.jingle.sessions = sessionsMap;
125
+ if (this.refreshIceServersTimer) {
126
+ clearInterval(this.refreshIceServersTimer);
127
+ this.refreshIceServersTimer = null;
128
+ }
129
+ stanzaInstance.on('iq:set:genesysWebrtc', this.handleGenesysWebrtcStanza.bind(this));
130
+ this.refreshIceServersTimer = setInterval(this.refreshIceServers.bind(this), ICE_SERVER_REFRESH_PERIOD);
131
+ this.client.emit('sessionManagerChange', stanzaInstance);
132
+ });
133
+ }
134
+ configureNewStanzaInstance(stanzaInstance) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ Object.assign(stanzaInstance.jingle.config.peerConnectionConfig, {
137
+ sdpSemantics: 'unified-plan'
138
+ });
139
+ yield this.configureStanzaIceServers(stanzaInstance);
140
+ stanzaInstance.stanzas.define(definitions);
141
+ stanzaInstance.jingle.prepareSession = this.prepareSession.bind(this);
142
+ stanzaInstance.jingle.on('log', (level, message, data) => {
143
+ this.logger[level](message, data);
144
+ });
145
+ const eventsToProxy = [
146
+ 'iceConnectionType',
147
+ 'peerTrackAdded',
148
+ 'peerTrackRemoved',
149
+ 'mute',
150
+ 'unmute',
151
+ 'sessionState',
152
+ 'connectionState',
153
+ 'terminated',
154
+ 'stats',
155
+ 'endOfCandidates'
156
+ ];
157
+ for (const e of eventsToProxy) {
158
+ stanzaInstance.jingle.on(e, (session, ...data) => {
159
+ if (!(session instanceof StanzaMediaSession)) {
160
+ return;
161
+ }
162
+ session.emit(e, ...data);
163
+ });
164
+ }
165
+ });
166
+ }
167
+ configureStanzaIceServers(stanzaInstance) {
168
+ /* clear out stanzas default use of google's stun server */
169
+ stanzaInstance.jingle.config.iceServers = [];
170
+ /**
171
+ * NOTE: calling this here should not interfere with the `webrtc.ts` extension
172
+ * refreshingIceServers since that is async and this constructor is sync
173
+ */
174
+ this.setIceServers([], stanzaInstance);
175
+ return this._refreshIceServers(stanzaInstance);
176
+ }
177
+ handleGenesysOffer(iq) {
178
+ return __awaiter(this, void 0, void 0, function* () {
179
+ const message = iq.genesysWebrtc;
180
+ const params = message.params;
181
+ // XMPP-SIP-Gateway will repeat reinvite offers until the client has responded.
182
+ // We don't want to process the duplicate reinvites and instead will ignore them.
183
+ if (params.reinvite && this.reinviteCache.get(message.id)) {
184
+ this.logger.info('Ignoring duplicate reinvite offer', message.id);
185
+ return;
186
+ }
187
+ // If the reinvite isn't a duplicate, we should cache it so we can check against new offers.
188
+ if (params.reinvite) {
189
+ this.reinviteCache.set(message.id, true);
190
+ }
191
+ const ignoreHostCandidatesForForceTurnFF = this.getIceTransportPolicy() === 'relay' && isFirefox;
192
+ const commonParams = {
193
+ id: params.sessionId,
194
+ logger: this.logger,
195
+ peerID: iq.from,
196
+ fromJid: iq.from,
197
+ sessionType: this.getSessionTypeByJid(iq.from),
198
+ conversationId: params.conversationId,
199
+ ignoreHostCandidatesFromRemote: ignoreHostCandidatesForForceTurnFF,
200
+ optOutOfWebrtcStatsTelemetry: !!this.config.optOutOfWebrtcStatsTelemetry,
201
+ allowIPv6: this.config.allowIPv6,
202
+ iceServers: this.iceServers,
203
+ reinvite: !!params.reinvite,
204
+ iceTransportPolicy: this.getIceTransportPolicy()
205
+ };
206
+ let mediaSessionParams;
207
+ // we should only do something if the pending session tells us to
208
+ const pendingSession = this.pendingSessions[params.sessionId];
209
+ if (pendingSession) {
210
+ mediaSessionParams = Object.assign(Object.assign({}, commonParams), { meetingId: pendingSession.meetingId, fromUserId: pendingSession.fromUserId, originalRoomJid: pendingSession.originalRoomJid, privAnswerMode: pendingSession.privAnswerMode });
211
+ delete this.pendingSessions[pendingSession.sessionId];
212
+ }
213
+ else {
214
+ mediaSessionParams = commonParams;
215
+ }
216
+ // if we receive an offer for an existing session and the ice info has not changed, this is
217
+ // a renogotiate. If the ice has changed, it's a re-invite and we need to create a new session.
218
+ const existingSession = this.webrtcSessions.find(s => s.id === mediaSessionParams.id);
219
+ this.logger.info('offer received', { existingSession: !!existingSession, mediaSessionParams });
220
+ if (existingSession) {
221
+ existingSession.conversationId = params.conversationId;
222
+ // renego
223
+ if (!iceIsDifferent(existingSession.peerConnection.remoteDescription.sdp, params.sdp)) {
224
+ return this.handleGenesysRenegotiate(existingSession, params.sdp);
225
+ }
226
+ }
227
+ // reinvite/new session handled the same way here
228
+ const session = new GenesysCloudMediaSession(this, mediaSessionParams);
229
+ yield session.setRemoteDescription(params.sdp);
230
+ this.proxyStatsForSession(session);
231
+ session.on('sendIq', (iq) => { var _a; return (_a = this.stanzaInstance) === null || _a === void 0 ? void 0 : _a.sendIQ(iq); });
232
+ session.on('terminated', () => {
233
+ delete this.sessionsMap[session.id];
234
+ this.webrtcSessions = this.webrtcSessions.filter(s => s.id !== session.id);
235
+ });
236
+ this.webrtcSessions.push(session);
237
+ this.logger.info('emitting sdp media-session (offer');
238
+ this.applyEarlyIceCandidates(session);
239
+ return this.emit(events.INCOMING_RTCSESSION, session);
240
+ });
241
+ }
242
+ applyEarlyIceCandidates(session) {
243
+ const earlyCandidates = this.earlyIceCandidates.get(session.id);
244
+ if (earlyCandidates) {
245
+ this.earlyIceCandidates.delete(session.id);
246
+ for (const candidate of earlyCandidates) {
247
+ void session.addRemoteIceCandidate(candidate);
248
+ }
249
+ }
250
+ }
251
+ handleGenesysRenegotiate(existingSession, newSdp) {
252
+ return __awaiter(this, void 0, void 0, function* () {
253
+ yield existingSession.peerConnection.setRemoteDescription({ sdp: newSdp, type: 'offer' });
254
+ yield existingSession.accept();
255
+ });
256
+ }
257
+ handleGenesysIceCandidate(iq) {
258
+ return __awaiter(this, void 0, void 0, function* () {
259
+ const message = iq.genesysWebrtc;
260
+ const params = message.params;
261
+ const session = this.getSessionById(params.sessionId, true);
262
+ if (session) {
263
+ yield session.addRemoteIceCandidate(params.sdp);
264
+ }
265
+ else {
266
+ const earlyCandidates = this.earlyIceCandidates.get(params.sessionId);
267
+ if (earlyCandidates) {
268
+ this.earlyIceCandidates.set(params.sessionId, [...earlyCandidates, params.sdp]);
269
+ }
270
+ else {
271
+ this.earlyIceCandidates.set(params.sessionId, [params.sdp]);
272
+ }
273
+ }
274
+ });
275
+ }
276
+ handleGenesysTerminate(iq) {
277
+ return __awaiter(this, void 0, void 0, function* () {
278
+ const message = iq.genesysWebrtc;
279
+ const params = message.params;
280
+ const session = this.getSessionById(params.sessionId, true);
281
+ if (!session) {
282
+ this.logger.warn('Received terminate for session that does not exist', { sessionId: params.sessionId });
283
+ return;
284
+ }
285
+ session.onSessionTerminate(params.reason);
286
+ });
287
+ }
288
+ getSessionById(id, nullIfNotFound = false) {
289
+ const session = this.getAllSessions().find(session => session.id === id);
290
+ if (!session && !nullIfNotFound) {
291
+ const error = new Error('Failed to find session by id');
292
+ this.logger.error(error, { sessionId: id });
293
+ throw error;
294
+ }
295
+ return session;
296
+ }
297
+ sendIq(iq) {
298
+ return __awaiter(this, void 0, void 0, function* () {
299
+ if (!this.stanzaInstance) {
300
+ throw new Error('Failed to send iq because there was no stanza instance');
301
+ }
302
+ return this.stanzaInstance.sendIQ(iq);
303
+ });
304
+ }
305
+ handleMessage(msg) {
306
+ return __awaiter(this, void 0, void 0, function* () {
307
+ if (msg.propose) {
308
+ yield this.handlePropose(msg);
309
+ }
310
+ else if (msg.retract) {
311
+ this.handleRetract(msg.retract.sessionId);
312
+ }
313
+ else if (msg.accept) {
314
+ this.handledIncomingRtcSession(msg.accept.sessionId, msg);
315
+ }
316
+ else if (msg.reject) {
317
+ this.handledIncomingRtcSession(msg.reject.sessionId, msg);
318
+ }
319
+ });
320
+ }
321
+ handleGenesysWebrtcStanza(iq) {
322
+ return __awaiter(this, void 0, void 0, function* () {
323
+ const webrtcInfo = iq.genesysWebrtc;
324
+ if (webrtcInfo.method === 'offer') {
325
+ return this.handleGenesysOffer(iq);
326
+ }
327
+ else if (webrtcInfo.method === 'iceCandidate') {
328
+ return this.handleGenesysIceCandidate(iq);
329
+ }
330
+ else if (webrtcInfo.method === 'terminate') {
331
+ return this.handleGenesysTerminate(iq);
332
+ }
333
+ });
334
+ }
335
+ prepareSession(options) {
336
+ if (options.sid && this.sessionsMap[options.sid]) {
337
+ this.logger.debug('skipping creation of jingle webrtc session due to sdpOverXmpp');
338
+ return;
339
+ }
340
+ const pendingSession = this.pendingSessions[options.sid];
341
+ if (pendingSession) {
342
+ delete this.pendingSessions[pendingSession.sessionId];
343
+ }
344
+ const ignoreHostCandidatesForForceTurnFF = this.getIceTransportPolicy() === 'relay' && isFirefox;
345
+ const gcSessionOpts = {
346
+ options,
347
+ logger: this.logger,
348
+ id: options.sid,
349
+ fromJid: options.peerID,
350
+ peerID: options.peerID,
351
+ optOutOfWebrtcStatsTelemetry: !!this.config.optOutOfWebrtcStatsTelemetry,
352
+ conversationId: pendingSession === null || pendingSession === void 0 ? void 0 : pendingSession.conversationId,
353
+ fromUserId: pendingSession === null || pendingSession === void 0 ? void 0 : pendingSession.fromJid,
354
+ originalRoomJid: pendingSession === null || pendingSession === void 0 ? void 0 : pendingSession.originalRoomJid,
355
+ sessionType: pendingSession === null || pendingSession === void 0 ? void 0 : pendingSession.sessionType,
356
+ allowIPv6: this.config.allowIPv6,
357
+ ignoreHostCandidatesFromRemote: ignoreHostCandidatesForForceTurnFF,
358
+ meetingId: pendingSession === null || pendingSession === void 0 ? void 0 : pendingSession.meetingId
359
+ };
360
+ const session = new StanzaMediaSession(gcSessionOpts);
361
+ this.proxyStatsForSession(session);
362
+ this.stanzaSessions.push(session);
363
+ session.on('terminated', () => {
364
+ delete this.sessionsMap[session.id];
365
+ this.stanzaSessions = this.stanzaSessions.filter(s => s.id !== session.id);
366
+ });
367
+ return session;
368
+ }
369
+ // This should probably go into the webrtc sdk, but for now I'm putting here so it's in a central location.
370
+ // This should be moved when the sdk is the primary consumer
371
+ proxyStatsForSession(session) {
372
+ session.on('stats', (statsEvent) => {
373
+ const statsCopy = JSON.parse(JSON.stringify(statsEvent));
374
+ const extraDetails = {
375
+ conversationId: session.conversationId,
376
+ sessionId: session.id,
377
+ sessionType: session.sessionType
378
+ };
379
+ // format the event to what the api expects
380
+ const event = formatStatsEvent(statsCopy, extraDetails);
381
+ this.addStatToQueue(event);
382
+ });
383
+ }
384
+ // this fn checks to see if the new stat fits inside the buffer. If not, send the queue;
385
+ addStatToQueue(stat) {
386
+ if (this.config.optOutOfWebrtcStatsTelemetry) {
387
+ return;
388
+ }
389
+ if (!stat.details._appId) {
390
+ stat.details._appId = this.logger.clientId;
391
+ stat.details._appName = 'streamingclient';
392
+ stat.details._appVersion = Client.version;
393
+ }
394
+ stat.details._originAppId = this.client.config.appId;
395
+ // nr only accepts single level objects so we must flatten everything just in case
396
+ const flattenedDetails = deepFlatten(stat.details);
397
+ // new relic doesn't accept booleans so we convert them to strings
398
+ Object.keys(flattenedDetails).forEach((key) => {
399
+ const val = flattenedDetails[key];
400
+ if (typeof val === 'boolean') {
401
+ flattenedDetails[key] = `${val}`;
402
+ }
403
+ });
404
+ const formattedStat = Object.assign(Object.assign({}, stat), { details: flattenedDetails });
405
+ const currentEventSize = calculatePayloadSize(formattedStat);
406
+ // Check if the size of the current event plus the size of the previous stats exceeds max size.
407
+ const exceedsMaxStatSize = this.statBuffer + currentEventSize > this.currentMaxStatSize;
408
+ this.statsArr.push(formattedStat);
409
+ this.statBuffer += currentEventSize;
410
+ // If it exceeds max size, don't append just send current payload.
411
+ if (exceedsMaxStatSize) {
412
+ this.sendStatsImmediately();
413
+ }
414
+ else {
415
+ this.throttledSendStats();
416
+ }
417
+ }
418
+ getLogDetailsForPendingSessionId(sessionId) {
419
+ const logDetails = {
420
+ sessionId
421
+ };
422
+ const pendingSession = this.pendingSessions[sessionId];
423
+ if (pendingSession) {
424
+ logDetails.sessionType = pendingSession.sessionType;
425
+ logDetails.conversationId = pendingSession.conversationId;
426
+ }
427
+ return logDetails;
428
+ }
429
+ sendStatsImmediately() {
430
+ // `throttledSendStats` needs to have a scheduled exeuction for `flush` to invoke the throttled function
431
+ this.throttledSendStats();
432
+ this.throttledSendStats.flush();
433
+ }
434
+ sendStats() {
435
+ var _a, _b;
436
+ return __awaiter(this, void 0, void 0, function* () {
437
+ if (!navigator.onLine) {
438
+ return;
439
+ }
440
+ const statsToSend = [];
441
+ let currentSize = 0;
442
+ for (const stats of this.statsArr) {
443
+ const statSize = calculatePayloadSize(stats);
444
+ if (currentSize + statSize < this.currentMaxStatSize) {
445
+ statsToSend.push(stats);
446
+ currentSize += statSize;
447
+ }
448
+ else {
449
+ break;
450
+ }
451
+ }
452
+ this.statsArr.splice(0, statsToSend.length);
453
+ this.statBuffer = this.statsArr.reduce((currentSize, stats) => currentSize + calculatePayloadSize(stats), 0);
454
+ if (!statsToSend.length || this.client.isGuest) {
455
+ return;
456
+ }
457
+ const data = {
458
+ appName: 'streamingclient',
459
+ appVersion: Client.version,
460
+ originAppName: this.client.config.appName,
461
+ originAppVersion: this.client.config.appVersion,
462
+ actions: statsToSend
463
+ };
464
+ // At least for now, we'll just fire and forget. Since this is non-critical, we'll not retry failures
465
+ try {
466
+ let authToken = this.client.config.authToken;
467
+ let url = 'diagnostics/newrelic/insights';
468
+ if (this.client.backgroundAssistantMode) {
469
+ authToken = this.client.config.jwt;
470
+ url += '/backgroundassistant';
471
+ }
472
+ yield this.client.http.requestApi(url, {
473
+ method: 'post',
474
+ responseType: 'text',
475
+ host: this.client.config.apiHost,
476
+ authToken,
477
+ logger: this.client.logger,
478
+ data
479
+ });
480
+ this.currentMaxStatSize = desiredMaxStatsSize;
481
+ }
482
+ catch (err) {
483
+ // re-add the stats to the buffer
484
+ if (HttpClient.retryStatusCodes.has((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) || !navigator.onLine) {
485
+ this.statsArr = [...statsToSend, ...this.statsArr];
486
+ this.statBuffer = this.statsArr.reduce((currentSize, stats) => currentSize + calculatePayloadSize(stats), 0);
487
+ }
488
+ if (((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 413) {
489
+ const attemptedPayloadSize = this.currentMaxStatSize;
490
+ this.currentMaxStatSize -= this.statsSizeDecreaseAmount;
491
+ this.logger.info('Failed to send stats due to 413, retrying with smaller set', { attemptedPayloadSize, newPayloadSize: this.currentMaxStatSize });
492
+ yield this.sendStats();
493
+ }
494
+ else {
495
+ this.logger.error('Failed to send stats', {
496
+ err,
497
+ numberOfFailedStats: statsToSend.length
498
+ }, { skipServer: !navigator.onLine });
499
+ }
500
+ }
501
+ });
502
+ }
503
+ addEventListeners() {
504
+ this.client.on('connected', () => {
505
+ if (this.refreshIceServersTimer) {
506
+ clearInterval(this.refreshIceServersTimer);
507
+ this.refreshIceServersTimer = null;
508
+ }
509
+ this.refreshIceServersTimer = setInterval(this.refreshIceServers.bind(this), ICE_SERVER_REFRESH_PERIOD);
510
+ return this.refreshIceServers()
511
+ .catch((error) => this.logger.error('Error fetching ice servers after streaming-client connected', {
512
+ error,
513
+ channelId: this.client.config.channelId
514
+ }));
515
+ });
516
+ this.client.on('disconnected', () => {
517
+ clearInterval(this.refreshIceServersTimer);
518
+ this.refreshIceServersTimer = null;
519
+ });
520
+ }
521
+ /**
522
+ * Stanza Handlers
523
+ */
524
+ handlePropose(msg) {
525
+ return __awaiter(this, void 0, void 0, function* () {
526
+ if (msg.from === this.jid) {
527
+ return;
528
+ }
529
+ const sessionId = msg.propose.sessionId;
530
+ let sessionInfo = this.pendingSessions[sessionId];
531
+ const isDuplicatePropose = !!sessionInfo;
532
+ const sessionType = this.getSessionTypeByJid(msg.from);
533
+ const loggingParams = { sessionId, conversationId: msg.propose.conversationId, sessionType, isDuplicatePropose };
534
+ this.logger.info('propose received', loggingParams);
535
+ if (!isDuplicatePropose) {
536
+ const { appId } = this.client.config;
537
+ const proposeStat = {
538
+ actionName: 'WebrtcStats',
539
+ details: {
540
+ _eventTimestamp: new Date().getTime(),
541
+ _eventType: 'firstPropose',
542
+ conversationId: loggingParams.conversationId,
543
+ sdpViaXmppRequested: !!msg.propose.sdpOverXmpp,
544
+ sessionId,
545
+ sessionType
546
+ }
547
+ };
548
+ this.addStatToQueue(proposeStat);
549
+ // TODO: is ofrom used?
550
+ // const roomJid = (msg.ofrom && msg.ofrom.full) || msg.from.full || msg.from;
551
+ const fromJid = msg.from;
552
+ const roomJid = fromJid;
553
+ msg.propose.originalRoomJid = msg.propose.originalRoomJid || roomJid;
554
+ sessionInfo = Object.assign(Object.assign({}, msg.propose), { toJid: msg.to, fromJid,
555
+ sessionType,
556
+ roomJid, id: sessionId, sdpOverXmpp: msg.propose.sdpOverXmpp, privAnswerMode: msg.propose.privAnswerMode });
557
+ if (!isDuplicatePropose) {
558
+ this.sessionsMap[sessionInfo.id] = !!sessionInfo.sdpOverXmpp;
559
+ this.pendingSessions[sessionId] = sessionInfo;
560
+ }
561
+ }
562
+ if (sessionInfo.accepted) {
563
+ this.logger.info('proceed already sent for this session, but sending another', loggingParams);
564
+ yield this.acceptRtcSession(sessionId);
565
+ return;
566
+ }
567
+ this.emit(events.REQUEST_INCOMING_RTCSESSION, Object.assign(sessionInfo));
568
+ });
569
+ }
570
+ handleRetract(sessionId) {
571
+ this.logger.info('retract received', this.getLogDetailsForPendingSessionId(sessionId));
572
+ delete this.sessionsMap[sessionId];
573
+ delete this.pendingSessions[sessionId];
574
+ return this.emit(events.CANCEL_INCOMING_RTCSESSION, sessionId);
575
+ }
576
+ /**
577
+ * Inform the client that another client has already taken care of the pendingSession
578
+ */
579
+ handledIncomingRtcSession(sessionId, msg) {
580
+ const acceptedOrRejected = msg.accept ? 'accept' : 'reject';
581
+ this.logger.info(`${acceptedOrRejected} received`, this.getLogDetailsForPendingSessionId(sessionId));
582
+ return this.emit(events.HANDLED_INCOMING_RTCSESSION, sessionId);
583
+ }
584
+ /**
585
+ * Exposed Api
586
+ */
587
+ initiateRtcSession(opts) {
588
+ return __awaiter(this, void 0, void 0, function* () {
589
+ // send media presence to join conference or screen screenRecording
590
+ // or send propose to single client for 1:1 video chat
591
+ const session = {
592
+ to: opts.jid,
593
+ propose: {
594
+ id: globalThis.crypto.randomUUID(),
595
+ descriptions: []
596
+ }
597
+ };
598
+ if (opts.stream) {
599
+ for (const track of Array.from(opts.stream.getTracks())) {
600
+ session.propose.descriptions.push({ media: track.kind });
601
+ }
602
+ }
603
+ if (opts.provideVideo) {
604
+ const videoDescriptionAlreadyExists = session.propose.descriptions.filter((desciption) => desciption.media === 'video').length > 0;
605
+ if (!videoDescriptionAlreadyExists) {
606
+ session.propose.descriptions.push({ media: 'video' });
607
+ }
608
+ }
609
+ if (opts.provideAudio) {
610
+ const audioDescriptionAlreadyExists = session.propose.descriptions.filter((desciption) => desciption.media === 'audio').length > 0;
611
+ if (!audioDescriptionAlreadyExists) {
612
+ session.propose.descriptions.push({ media: 'audio' });
613
+ }
614
+ }
615
+ if (opts.mediaPurpose) {
616
+ session.propose.descriptions.push({ media: opts.mediaPurpose });
617
+ }
618
+ if (opts.jid && opts.jid.match(/@conference/)) {
619
+ let mediaDescriptions = session.propose.descriptions;
620
+ if (mediaDescriptions.length === 0) {
621
+ mediaDescriptions = [{ media: 'listener' }];
622
+ }
623
+ const mediaPresence = {
624
+ type: 'upgradeMedia',
625
+ to: opts.jid,
626
+ id: globalThis.crypto.randomUUID(),
627
+ from: this.jid,
628
+ media: {
629
+ conversationId: opts.conversationId,
630
+ sourceCommunicationId: opts.sourceCommunicationId
631
+ }
632
+ };
633
+ if (opts.mediaPurpose === SessionTypes.collaborateVideo) {
634
+ Object.assign(mediaPresence.media, {
635
+ video: true,
636
+ audio: true,
637
+ videoGuest: true
638
+ });
639
+ }
640
+ // TODO? can't set last-n on parent element because it invalidates presence root schema
641
+ for (const mediaDescription of mediaDescriptions) {
642
+ mediaPresence.media[mediaDescription.media] = true;
643
+ }
644
+ yield this.stanzaInstance.send('presence', mediaPresence);
645
+ }
646
+ else {
647
+ yield this.stanzaInstance.send('message', session); // send as Message
648
+ this.pendingSessions[session.propose.id] = session;
649
+ }
650
+ return session.propose.id;
651
+ });
652
+ }
653
+ // jingle proceed
654
+ acceptRtcSession(sessionId) {
655
+ return __awaiter(this, void 0, void 0, function* () {
656
+ const session = this.pendingSessions[sessionId];
657
+ if (!session) {
658
+ this.emit(events.RTCSESSION_ERROR, 'Cannot accept session because it is not pending or does not exist');
659
+ return;
660
+ }
661
+ const proceed = {
662
+ to: session.fromJid,
663
+ proceed: {
664
+ sessionId
665
+ }
666
+ };
667
+ session.accepted = true;
668
+ const details = this.getLogDetailsForPendingSessionId(sessionId);
669
+ this.logger.info('sending jingle proceed', details);
670
+ yield this.stanzaInstance.send('message', proceed); // send as Message
671
+ this.logger.info('sent jingle proceed', details);
672
+ });
673
+ }
674
+ rejectRtcSession(sessionId, ignore = false) {
675
+ return __awaiter(this, void 0, void 0, function* () {
676
+ const logDetails = this.getLogDetailsForPendingSessionId(sessionId);
677
+ const session = this.pendingSessions[sessionId];
678
+ if (!session) {
679
+ this.emit(events.RTCSESSION_ERROR, 'Cannot reject session because it is not pending or does not exist');
680
+ return;
681
+ }
682
+ delete this.sessionsMap[sessionId];
683
+ delete this.pendingSessions[sessionId];
684
+ if (ignore) {
685
+ this.ignoredSessions.set(sessionId, true);
686
+ }
687
+ else {
688
+ const reject1 = {
689
+ to: toBare(this.jid),
690
+ reject: {
691
+ sessionId
692
+ }
693
+ };
694
+ const firstMessage = this.stanzaInstance.send('message', reject1); // send as Message
695
+ const reject2 = {
696
+ to: session.fromJid,
697
+ reject: {
698
+ sessionId
699
+ }
700
+ };
701
+ const secondMessage = this.stanzaInstance.send('message', reject2); // send as Message
702
+ this.logger.info('sending jingle reject', logDetails);
703
+ yield Promise.all([firstMessage, secondMessage]);
704
+ this.logger.info('sent jingle reject', logDetails);
705
+ }
706
+ });
707
+ }
708
+ rtcSessionAccepted(sessionId) {
709
+ return __awaiter(this, void 0, void 0, function* () {
710
+ const pendingSession = this.pendingSessions[sessionId];
711
+ const logDetails = this.getLogDetailsForPendingSessionId(sessionId);
712
+ const accept = {
713
+ to: toBare(this.jid),
714
+ accept: {
715
+ sessionId
716
+ }
717
+ };
718
+ this.logger.info('sending session-info:accept', logDetails);
719
+ yield this.stanzaInstance.send('message', accept); // send as Message
720
+ this.logger.info('sent session-info:accept', logDetails);
721
+ });
722
+ }
723
+ notifyScreenShareStart(session) {
724
+ return __awaiter(this, void 0, void 0, function* () {
725
+ return this.stanzaInstance.send('iq', {
726
+ to: `${session.peerID}`,
727
+ from: this.jid,
728
+ type: 'set',
729
+ jingle: {
730
+ action: JingleAction.SessionInfo,
731
+ sid: session.id,
732
+ screenstart: {}
733
+ }
734
+ });
735
+ });
736
+ }
737
+ notifyScreenShareStop(session) {
738
+ return __awaiter(this, void 0, void 0, function* () {
739
+ return this.stanzaInstance.send('iq', {
740
+ to: `${session.peerID}`,
741
+ from: this.jid,
742
+ type: 'set',
743
+ jingle: {
744
+ action: JingleAction.SessionInfo,
745
+ sid: session.id,
746
+ screenstop: {}
747
+ }
748
+ });
749
+ });
750
+ }
751
+ cancelRtcSession(sessionId) {
752
+ return __awaiter(this, void 0, void 0, function* () {
753
+ const session = this.pendingSessions[sessionId];
754
+ const logDetails = this.getLogDetailsForPendingSessionId(sessionId);
755
+ if (!session) {
756
+ this.emit(events.RTCSESSION_ERROR, 'Cannot cancel session because it is not pending or does not exist');
757
+ return;
758
+ }
759
+ const retract = {
760
+ to: session.toJid,
761
+ retract: {
762
+ sessionId
763
+ }
764
+ };
765
+ delete this.pendingSessions[sessionId];
766
+ delete this.sessionsMap[sessionId];
767
+ this.logger.info('sending jingle retract', logDetails);
768
+ yield this.stanzaInstance.send('message', retract); // send as Message
769
+ this.logger.info('sent jingle retract', logDetails);
770
+ });
771
+ }
772
+ refreshIceServers() {
773
+ return __awaiter(this, void 0, void 0, function* () {
774
+ if (!this.refreshIceServersRetryPromise) {
775
+ this.refreshIceServersRetryPromise = retryPromise(this._refreshIceServers.bind(this, this.stanzaInstance), (error) => {
776
+ if (++this.discoRetries > MAX_DISCO_RETRIES) {
777
+ this.logger.warn('fetching ice servers failed. max retries reached.', {
778
+ retryAttempt: this.discoRetries,
779
+ MAX_DISCO_RETRIES,
780
+ error,
781
+ channelId: this.client.config.channelId
782
+ });
783
+ return false;
784
+ }
785
+ this.logger.warn('fetching ice servers failed. retrying', {
786
+ retryAttempt: this.discoRetries,
787
+ error,
788
+ channelId: this.client.config.channelId
789
+ });
790
+ return true;
791
+ }, 0, this.client.logger);
792
+ }
793
+ return this.refreshIceServersRetryPromise.promise
794
+ .finally(() => {
795
+ this.discoRetries = 0;
796
+ this.refreshIceServersRetryPromise = undefined;
797
+ });
798
+ });
799
+ }
800
+ _refreshIceServers(stanzaInstance) {
801
+ return __awaiter(this, void 0, void 0, function* () {
802
+ if (!stanzaInstance) {
803
+ throw new Error('No stanza instance to refresh ice servers');
804
+ }
805
+ const server = stanzaInstance.config.server;
806
+ const turnServersPromise = stanzaInstance.getServices(server, 'turn', '1');
807
+ const stunServersPromise = stanzaInstance.getServices(server, 'stun', '1');
808
+ const servicesPromise = new Promise((resolve, reject) => {
809
+ setTimeout(() => {
810
+ reject(new Error('Timeout waiting for refresh ice servers to finish'));
811
+ }, ICE_SERVER_TIMEOUT);
812
+ Promise.all([
813
+ turnServersPromise,
814
+ stunServersPromise
815
+ ])
816
+ .then(([turn, stun]) => {
817
+ resolve([turn, stun]);
818
+ })
819
+ .catch(reject);
820
+ });
821
+ const [turnServers, stunServers] = yield servicesPromise;
822
+ const iceServers = [
823
+ ...turnServers.services,
824
+ ...stunServers.services
825
+ ].map((service) => {
826
+ const port = service.port ? `:${service.port}` : '';
827
+ const ice = {
828
+ type: service.type,
829
+ urls: `${service.type}:${service.host}${port}`
830
+ };
831
+ if (['turn', 'turns'].includes(service.type)) {
832
+ if (service.transport && service.transport !== 'udp') {
833
+ ice.urls += `?transport=${service.transport}`;
834
+ }
835
+ if (service.username) {
836
+ ice.username = service.username;
837
+ }
838
+ if (service.password) {
839
+ ice.credential = service.password;
840
+ }
841
+ }
842
+ return ice;
843
+ });
844
+ this.setIceServers(iceServers, stanzaInstance);
845
+ if (!stunServers.services.length) {
846
+ this.logger.info('No stun servers received, setting iceTransportPolicy to "relay"');
847
+ this.setIceTransportPolicy('relay', stanzaInstance);
848
+ }
849
+ else {
850
+ this.setIceTransportPolicy('all', stanzaInstance);
851
+ }
852
+ return iceServers;
853
+ });
854
+ }
855
+ setIceServers(iceServers, stanzaInstance) {
856
+ stanzaInstance.jingle.iceServers = iceServers;
857
+ this.iceServers = iceServers;
858
+ }
859
+ getIceTransportPolicy() {
860
+ var _a;
861
+ return (_a = this.stanzaInstance) === null || _a === void 0 ? void 0 : _a.jingle.config.peerConnectionConfig.iceTransportPolicy;
862
+ }
863
+ setIceTransportPolicy(policy, stanzaInstance) {
864
+ stanzaInstance.jingle.config.peerConnectionConfig.iceTransportPolicy = policy;
865
+ }
866
+ getSessionTypeByJid(jid) {
867
+ if (isAcdJid(jid)) {
868
+ return SessionTypes.acdScreenShare;
869
+ }
870
+ else if (isScreenRecordingJid(jid)) {
871
+ return SessionTypes.screenRecording;
872
+ }
873
+ else if (isLiveScreenMonitoringJid(jid)) {
874
+ return SessionTypes.liveScreenMonitoring;
875
+ }
876
+ else if (isSoftphoneJid(jid)) {
877
+ return SessionTypes.softphone;
878
+ }
879
+ else if (isVideoJid(jid)) {
880
+ return SessionTypes.collaborateVideo;
881
+ }
882
+ else {
883
+ return SessionTypes.unknown;
884
+ }
885
+ }
886
+ getSessionManager() {
887
+ var _a;
888
+ return (_a = this.stanzaInstance) === null || _a === void 0 ? void 0 : _a.jingle;
889
+ }
890
+ getAllSessions() {
891
+ return [...this.stanzaSessions, ...this.webrtcSessions];
892
+ }
893
+ proxyNRStat(stat) {
894
+ this.addStatToQueue(stat);
895
+ }
896
+ get expose() {
897
+ return {
898
+ on: this.on.bind(this),
899
+ once: this.once.bind(this),
900
+ off: this.off.bind(this),
901
+ removeAllListeners: this.removeAllListeners.bind(this),
902
+ removeListener: this.removeListener.bind(this),
903
+ refreshIceServers: this.refreshIceServers.bind(this),
904
+ acceptRtcSession: this.acceptRtcSession.bind(this),
905
+ rejectRtcSession: this.rejectRtcSession.bind(this),
906
+ cancelRtcSession: this.cancelRtcSession.bind(this),
907
+ notifyScreenShareStart: this.notifyScreenShareStart.bind(this),
908
+ notifyScreenShareStop: this.notifyScreenShareStop.bind(this),
909
+ rtcSessionAccepted: this.rtcSessionAccepted.bind(this),
910
+ initiateRtcSession: this.initiateRtcSession.bind(this),
911
+ getSessionTypeByJid: this.getSessionTypeByJid.bind(this),
912
+ getSessionManager: this.getSessionManager.bind(this),
913
+ getAllSessions: this.getAllSessions.bind(this),
914
+ proxyNRStat: this.proxyNRStat.bind(this)
915
+ };
916
+ }
917
+ }