@webex/plugin-meetings 3.3.1 → 3.4.0

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 (126) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +7 -2
  3. package/dist/breakouts/index.js.map +1 -1
  4. package/dist/constants.js +11 -4
  5. package/dist/constants.js.map +1 -1
  6. package/dist/interpretation/index.js +1 -1
  7. package/dist/interpretation/siLanguage.js +1 -1
  8. package/dist/locus-info/selfUtils.js +0 -5
  9. package/dist/locus-info/selfUtils.js.map +1 -1
  10. package/dist/media/MediaConnectionAwaiter.js +70 -15
  11. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  12. package/dist/media/index.js +12 -0
  13. package/dist/media/index.js.map +1 -1
  14. package/dist/meeting/connectionStateHandler.js +67 -0
  15. package/dist/meeting/connectionStateHandler.js.map +1 -0
  16. package/dist/meeting/index.js +552 -357
  17. package/dist/meeting/index.js.map +1 -1
  18. package/dist/meeting/locusMediaRequest.js +7 -0
  19. package/dist/meeting/locusMediaRequest.js.map +1 -1
  20. package/dist/meeting/muteState.js +6 -1
  21. package/dist/meeting/muteState.js.map +1 -1
  22. package/dist/meeting/util.js +1 -0
  23. package/dist/meeting/util.js.map +1 -1
  24. package/dist/meeting-info/index.js +4 -4
  25. package/dist/meeting-info/index.js.map +1 -1
  26. package/dist/meeting-info/meeting-info-v2.js +2 -2
  27. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  28. package/dist/meeting-info/util.js +17 -17
  29. package/dist/meeting-info/util.js.map +1 -1
  30. package/dist/meeting-info/utilv2.js +16 -16
  31. package/dist/meeting-info/utilv2.js.map +1 -1
  32. package/dist/meetings/collection.js +1 -1
  33. package/dist/meetings/collection.js.map +1 -1
  34. package/dist/meetings/index.js +37 -33
  35. package/dist/meetings/index.js.map +1 -1
  36. package/dist/meetings/meetings.types.js +8 -0
  37. package/dist/meetings/meetings.types.js.map +1 -1
  38. package/dist/meetings/util.js +3 -2
  39. package/dist/meetings/util.js.map +1 -1
  40. package/dist/metrics/constants.js +2 -1
  41. package/dist/metrics/constants.js.map +1 -1
  42. package/dist/metrics/index.js +57 -0
  43. package/dist/metrics/index.js.map +1 -1
  44. package/dist/personal-meeting-room/index.js +1 -1
  45. package/dist/personal-meeting-room/index.js.map +1 -1
  46. package/dist/reachability/clusterReachability.js +108 -53
  47. package/dist/reachability/clusterReachability.js.map +1 -1
  48. package/dist/reachability/index.js +415 -56
  49. package/dist/reachability/index.js.map +1 -1
  50. package/dist/types/constants.d.ts +11 -3
  51. package/dist/types/media/MediaConnectionAwaiter.d.ts +24 -4
  52. package/dist/types/meeting/connectionStateHandler.d.ts +30 -0
  53. package/dist/types/meeting/index.d.ts +27 -7
  54. package/dist/types/meeting/locusMediaRequest.d.ts +2 -0
  55. package/dist/types/meeting-info/index.d.ts +3 -2
  56. package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -2
  57. package/dist/types/meeting-info/util.d.ts +5 -4
  58. package/dist/types/meeting-info/utilv2.d.ts +3 -2
  59. package/dist/types/meetings/collection.d.ts +3 -2
  60. package/dist/types/meetings/index.d.ts +4 -3
  61. package/dist/types/meetings/meetings.types.d.ts +9 -0
  62. package/dist/types/metrics/constants.d.ts +1 -0
  63. package/dist/types/metrics/index.d.ts +15 -0
  64. package/dist/types/reachability/clusterReachability.d.ts +31 -3
  65. package/dist/types/reachability/index.d.ts +93 -2
  66. package/dist/webinar/index.js +1 -1
  67. package/package.json +23 -23
  68. package/src/breakouts/index.ts +7 -1
  69. package/src/constants.ts +13 -17
  70. package/src/locus-info/selfUtils.ts +0 -5
  71. package/src/media/MediaConnectionAwaiter.ts +89 -14
  72. package/src/media/index.ts +13 -0
  73. package/src/meeting/connectionStateHandler.ts +65 -0
  74. package/src/meeting/index.ts +526 -292
  75. package/src/meeting/locusMediaRequest.ts +5 -0
  76. package/src/meeting/muteState.ts +6 -1
  77. package/src/meeting/util.ts +1 -0
  78. package/src/meeting-info/index.ts +9 -6
  79. package/src/meeting-info/meeting-info-v2.ts +4 -4
  80. package/src/meeting-info/util.ts +23 -28
  81. package/src/meeting-info/utilv2.ts +18 -24
  82. package/src/meetings/collection.ts +3 -3
  83. package/src/meetings/index.ts +39 -40
  84. package/src/meetings/meetings.types.ts +11 -0
  85. package/src/meetings/util.ts +5 -4
  86. package/src/metrics/constants.ts +1 -0
  87. package/src/metrics/index.ts +44 -0
  88. package/src/personal-meeting-room/index.ts +2 -2
  89. package/src/reachability/clusterReachability.ts +86 -25
  90. package/src/reachability/index.ts +316 -27
  91. package/test/unit/spec/breakouts/index.ts +51 -32
  92. package/test/unit/spec/locus-info/selfUtils.js +25 -23
  93. package/test/unit/spec/media/MediaConnectionAwaiter.ts +131 -32
  94. package/test/unit/spec/media/index.ts +42 -27
  95. package/test/unit/spec/meeting/connectionStateHandler.ts +102 -0
  96. package/test/unit/spec/meeting/index.js +758 -179
  97. package/test/unit/spec/meeting/locusMediaRequest.ts +7 -0
  98. package/test/unit/spec/meeting/muteState.js +24 -0
  99. package/test/unit/spec/meeting-info/index.js +4 -4
  100. package/test/unit/spec/meeting-info/meetinginfov2.js +24 -28
  101. package/test/unit/spec/meeting-info/request.js +2 -2
  102. package/test/unit/spec/meeting-info/utilv2.js +41 -49
  103. package/test/unit/spec/meetings/index.js +14 -0
  104. package/test/unit/spec/metrics/index.js +126 -0
  105. package/test/unit/spec/multistream/mediaRequestManager.ts +2 -2
  106. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -2
  107. package/test/unit/spec/reachability/clusterReachability.ts +116 -22
  108. package/test/unit/spec/reachability/index.ts +1153 -84
  109. package/test/unit/spec/rtcMetrics/index.ts +1 -0
  110. package/dist/mediaQualityMetrics/config.js +0 -321
  111. package/dist/mediaQualityMetrics/config.js.map +0 -1
  112. package/dist/statsAnalyzer/global.js +0 -44
  113. package/dist/statsAnalyzer/global.js.map +0 -1
  114. package/dist/statsAnalyzer/index.js +0 -1072
  115. package/dist/statsAnalyzer/index.js.map +0 -1
  116. package/dist/statsAnalyzer/mqaUtil.js +0 -368
  117. package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
  118. package/dist/types/mediaQualityMetrics/config.d.ts +0 -247
  119. package/dist/types/statsAnalyzer/global.d.ts +0 -36
  120. package/dist/types/statsAnalyzer/index.d.ts +0 -217
  121. package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -48
  122. package/src/mediaQualityMetrics/config.ts +0 -255
  123. package/src/statsAnalyzer/global.ts +0 -37
  124. package/src/statsAnalyzer/index.ts +0 -1318
  125. package/src/statsAnalyzer/mqaUtil.ts +0 -463
  126. package/test/unit/spec/stats-analyzer/index.js +0 -1819
@@ -1,4 +1,6 @@
1
1
  import { ClusterNode } from './request';
2
+ import EventsScope from '../common/events/events-scope';
3
+ import { Enum } from '../constants';
2
4
  export type TransportResult = {
3
5
  result: 'reachable' | 'unreachable' | 'untested';
4
6
  latencyInMilliseconds?: number;
@@ -9,10 +11,26 @@ export type ClusterReachabilityResult = {
9
11
  tcp: TransportResult;
10
12
  xtls: TransportResult;
11
13
  };
14
+ export type ResultEventData = {
15
+ protocol: 'udp' | 'tcp' | 'xtls';
16
+ result: 'reachable' | 'unreachable' | 'untested';
17
+ latencyInMilliseconds: number;
18
+ clientMediaIPs?: string[];
19
+ };
20
+ export type ClientMediaIpsUpdatedEventData = {
21
+ protocol: 'udp' | 'tcp' | 'xtls';
22
+ clientMediaIPs: string[];
23
+ };
24
+ export declare const Events: {
25
+ readonly resultReady: "resultReady";
26
+ readonly clientMediaIpsUpdated: "clientMediaIpsUpdated";
27
+ };
28
+ export type Events = Enum<typeof Events>;
12
29
  /**
13
30
  * A class that handles reachability checks for a single cluster.
31
+ * It emits events from Events enum
14
32
  */
15
- export declare class ClusterReachability {
33
+ export declare class ClusterReachability extends EventsScope {
16
34
  private numUdpUrls;
17
35
  private numTcpUrls;
18
36
  private numXTlsUrls;
@@ -61,6 +79,12 @@ export declare class ClusterReachability {
61
79
  * @returns {void}
62
80
  */
63
81
  private finishReachabilityCheck;
82
+ /**
83
+ * Aborts the cluster reachability checks by closing the peer connection
84
+ *
85
+ * @returns {void}
86
+ */
87
+ abort(): void;
64
88
  /**
65
89
  * Adds public IP (client media IPs)
66
90
  * @param {string} protocol
@@ -81,13 +105,17 @@ export declare class ClusterReachability {
81
105
  */
82
106
  private haveWeGotAllResults;
83
107
  /**
84
- * Stores the latency in the result for the given protocol and marks it as reachable
108
+ * Saves the latency in the result for the given protocol and marks it as reachable,
109
+ * emits the "resultReady" event if this is the first result for that protocol,
110
+ * emits the "clientMediaIpsUpdated" event if we already had a result and only found
111
+ * a new client IP
85
112
  *
86
113
  * @param {string} protocol
87
114
  * @param {number} latency
115
+ * @param {string|null} [publicIp]
88
116
  * @returns {void}
89
117
  */
90
- private storeLatencyResult;
118
+ private saveResult;
91
119
  /**
92
120
  * Registers a listener for the icecandidate event
93
121
  *
@@ -1,8 +1,10 @@
1
1
  /*!
2
2
  * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
3
  */
4
+ import { Defer } from '@webex/common';
4
5
  import ReachabilityRequest from './request';
5
6
  import { ClusterReachability, ClusterReachabilityResult } from './clusterReachability';
7
+ import EventsScope from '../common/events/events-scope';
6
8
  export type ReachabilityMetrics = {
7
9
  reachability_public_udp_success: number;
8
10
  reachability_public_udp_failed: number;
@@ -40,13 +42,37 @@ export type ReachabilityResults = Record<string, ClusterReachabilityResult & {
40
42
  * @class Reachability
41
43
  * @export
42
44
  */
43
- export default class Reachability {
45
+ export default class Reachability extends EventsScope {
44
46
  namespace: string;
45
47
  webex: object;
46
48
  reachabilityRequest: ReachabilityRequest;
47
49
  clusterReachability: {
48
50
  [key: string]: ClusterReachability;
49
51
  };
52
+ reachabilityDefer?: Defer;
53
+ vmnTimer?: ReturnType<typeof setTimeout>;
54
+ publicCloudTimer?: ReturnType<typeof setTimeout>;
55
+ overallTimer?: ReturnType<typeof setTimeout>;
56
+ expectedResultsCount: {
57
+ videoMesh: {
58
+ udp: number;
59
+ };
60
+ public: {
61
+ udp: number;
62
+ tcp: number;
63
+ xtls: number;
64
+ };
65
+ };
66
+ resultsCount: {
67
+ videoMesh: {
68
+ udp: number;
69
+ };
70
+ public: {
71
+ udp: number;
72
+ tcp: number;
73
+ xtls: number;
74
+ };
75
+ };
50
76
  /**
51
77
  * Creates an instance of Reachability.
52
78
  * @param {object} webex
@@ -111,10 +137,75 @@ export default class Reachability {
111
137
  * @memberof Reachability
112
138
  */
113
139
  private logUnreachableClusters;
140
+ /**
141
+ * Returns true if we've obtained all the reachability results for all the public clusters
142
+ * In other words, it means that all public clusters are reachable over each protocol,
143
+ * because we only get a "result" if we managed to reach a cluster
144
+ *
145
+ * @returns {boolean}
146
+ */
147
+ private areAllPublicClusterResultsReady;
148
+ /**
149
+ * Returns true if we've obtained all the reachability results for all the clusters
150
+ *
151
+ * @returns {boolean}
152
+ */
153
+ private areAllResultsReady;
154
+ /**
155
+ * Resolves the promise returned by gatherReachability() method
156
+ * @returns {void}
157
+ */
158
+ private resolveReachabilityPromise;
159
+ /**
160
+ * Aborts all cluster reachability checks that are in progress
161
+ *
162
+ * @returns {void}
163
+ */
164
+ private abortClusterReachability;
165
+ /**
166
+ * Helper function for calculating min/max/average values of latency
167
+ *
168
+ * @param {Array<any>} results
169
+ * @param {string} protocol
170
+ * @param {boolean} isVideoMesh
171
+ * @returns {{min:number, max: number, average: number}}
172
+ */
173
+ protected getStatistics(results: Array<ClusterReachabilityResult & {
174
+ isVideoMesh: boolean;
175
+ }>, protocol: 'udp' | 'tcp' | 'xtls', isVideoMesh: boolean): {
176
+ min: number;
177
+ max: number;
178
+ average: number;
179
+ };
180
+ /**
181
+ * Sends a metric with all the statistics about how long reachability took
182
+ *
183
+ * @returns {void}
184
+ */
185
+ protected sendMetric(): Promise<void>;
186
+ /**
187
+ * Starts all the timers used for various timeouts
188
+ *
189
+ * @returns {void}
190
+ */
191
+ private startTimers;
192
+ /**
193
+ * Stores given reachability results in local storage
194
+ *
195
+ * @param {ReachabilityResults} results
196
+ * @returns {Promise<void>}
197
+ */
198
+ private storeResults;
199
+ /**
200
+ * Resets all the internal counters that keep track of the results
201
+ *
202
+ * @returns {void}
203
+ */
204
+ private resetResultCounters;
114
205
  /**
115
206
  * Performs reachability checks for all clusters
116
207
  * @param {ClusterList} clusterList
117
- * @returns {Promise<ReachabilityResults>} reachability check results
208
+ * @returns {Promise<void>} promise that's resolved as soon as the checks are started
118
209
  */
119
210
  private performReachabilityChecks;
120
211
  }
@@ -62,7 +62,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
62
62
  updateCanManageWebcast: function updateCanManageWebcast(canManageWebcast) {
63
63
  this.set('canManageWebcast', canManageWebcast);
64
64
  },
65
- version: "3.3.1"
65
+ version: "3.4.0"
66
66
  });
67
67
  var _default = exports.default = Webinar;
68
68
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,13 +43,13 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-meetings": "3.3.1",
47
- "@webex/plugin-rooms": "3.3.1",
48
- "@webex/test-helper-chai": "3.3.1",
49
- "@webex/test-helper-mocha": "3.3.1",
50
- "@webex/test-helper-mock-webex": "3.3.1",
51
- "@webex/test-helper-retry": "3.3.1",
52
- "@webex/test-helper-test-users": "3.3.1",
46
+ "@webex/plugin-meetings": "3.4.0",
47
+ "@webex/plugin-rooms": "3.4.0",
48
+ "@webex/test-helper-chai": "3.4.0",
49
+ "@webex/test-helper-mocha": "3.4.0",
50
+ "@webex/test-helper-mock-webex": "3.4.0",
51
+ "@webex/test-helper-retry": "3.4.0",
52
+ "@webex/test-helper-test-users": "3.4.0",
53
53
  "chai": "^4.3.4",
54
54
  "chai-as-promised": "^7.1.1",
55
55
  "eslint": "^8.24.0",
@@ -61,20 +61,21 @@
61
61
  "typescript": "^4.7.4"
62
62
  },
63
63
  "dependencies": {
64
- "@webex/common": "3.3.1",
65
- "@webex/internal-media-core": "2.5.1",
66
- "@webex/internal-plugin-conversation": "3.3.1",
67
- "@webex/internal-plugin-device": "3.3.1",
68
- "@webex/internal-plugin-llm": "3.3.1",
69
- "@webex/internal-plugin-mercury": "3.3.1",
70
- "@webex/internal-plugin-metrics": "3.3.1",
71
- "@webex/internal-plugin-support": "3.3.1",
72
- "@webex/internal-plugin-user": "3.3.1",
73
- "@webex/internal-plugin-voicea": "3.3.1",
74
- "@webex/media-helpers": "3.3.1",
75
- "@webex/plugin-people": "3.3.1",
76
- "@webex/plugin-rooms": "3.3.1",
77
- "@webex/webex-core": "3.3.1",
64
+ "@webex/common": "3.4.0",
65
+ "@webex/internal-media-core": "2.10.0",
66
+ "@webex/internal-plugin-conversation": "3.4.0",
67
+ "@webex/internal-plugin-device": "3.4.0",
68
+ "@webex/internal-plugin-llm": "3.4.0",
69
+ "@webex/internal-plugin-mercury": "3.4.0",
70
+ "@webex/internal-plugin-metrics": "3.4.0",
71
+ "@webex/internal-plugin-support": "3.4.0",
72
+ "@webex/internal-plugin-user": "3.4.0",
73
+ "@webex/internal-plugin-voicea": "3.4.0",
74
+ "@webex/media-helpers": "3.4.0",
75
+ "@webex/plugin-people": "3.4.0",
76
+ "@webex/plugin-rooms": "3.4.0",
77
+ "@webex/web-capabilities": "^1.4.0",
78
+ "@webex/webex-core": "3.4.0",
78
79
  "ampersand-collection": "^2.0.2",
79
80
  "bowser": "^2.11.0",
80
81
  "btoa": "^1.2.1",
@@ -84,12 +85,11 @@
84
85
  "javascript-state-machine": "^3.1.0",
85
86
  "jwt-decode": "3.1.2",
86
87
  "lodash": "^4.17.21",
87
- "sdp-transform": "^2.12.0",
88
88
  "uuid": "^3.3.2",
89
89
  "webrtc-adapter": "^8.1.2"
90
90
  },
91
91
  "//": [
92
92
  "TODO: upgrade jwt-decode when moving to node 18"
93
93
  ],
94
- "version": "3.3.1"
94
+ "version": "3.4.0"
95
95
  }
@@ -148,7 +148,6 @@ const Breakouts = WebexPlugin.extend({
148
148
  this.triggerReturnToMainEvent(breakout);
149
149
  });
150
150
  this.listenToCurrentSessionTypeChange();
151
- this.listenToBroadcastMessages();
152
151
  this.listenToBreakoutRosters();
153
152
  this.listenToBreakoutHelp();
154
153
  // @ts-ignore
@@ -161,6 +160,7 @@ const Breakouts = WebexPlugin.extend({
161
160
  */
162
161
  cleanUp() {
163
162
  this.stopListening();
163
+ this.hasSubscribedToMessage = undefined;
164
164
  },
165
165
 
166
166
  /**
@@ -170,6 +170,7 @@ const Breakouts = WebexPlugin.extend({
170
170
  */
171
171
  locusUrlUpdate(locusUrl) {
172
172
  this.set('locusUrl', locusUrl);
173
+ this.listenToBroadcastMessages();
173
174
  const {isInMainSession, mainLocusUrl} = this;
174
175
  if (isInMainSession || !mainLocusUrl) {
175
176
  this.set('mainLocusUrl', locusUrl);
@@ -255,6 +256,10 @@ const Breakouts = WebexPlugin.extend({
255
256
  * @returns {void}
256
257
  */
257
258
  listenToBroadcastMessages() {
259
+ if (!this.webex.internal.llm.isConnected() || this.hasSubscribedToMessage) {
260
+ return;
261
+ }
262
+
258
263
  this.listenTo(this.webex.internal.llm, 'event:breakout.message', (event) => {
259
264
  const {
260
265
  data: {senderUserId, sentTime, message},
@@ -270,6 +275,7 @@ const Breakouts = WebexPlugin.extend({
270
275
  sessionId: this.currentBreakoutSession.sessionId,
271
276
  });
272
277
  });
278
+ this.hasSubscribedToMessage = true;
273
279
  },
274
280
 
275
281
  /**
package/src/constants.ts CHANGED
@@ -250,23 +250,6 @@ export const ASSIGN_ROLES_ERROR_CODES = {
250
250
  ReclaimHostIsHostAlreadyErrorCode: 2409150,
251
251
  };
252
252
 
253
- export const DEFAULT_GET_STATS_FILTER = {
254
- types: [
255
- 'track',
256
- 'transport',
257
- 'candidate-pair',
258
- 'outbound-rtp',
259
- 'outboundrtp',
260
- 'inbound-rtp',
261
- 'inboundrtp',
262
- 'remote-inbound-rtp',
263
- 'remote-outbound-rtp',
264
- 'remote-candidate',
265
- 'local-candidate',
266
- 'media-source',
267
- ],
268
- };
269
-
270
253
  export const RECORDING_STATE = {
271
254
  RECORDING: 'recording',
272
255
  IDLE: 'idle',
@@ -1331,3 +1314,16 @@ export const MEETING_PERMISSION_TOKEN_REFRESH_REASON = 'ttl-join';
1331
1314
 
1332
1315
  // constant for named media group type
1333
1316
  export const NAMED_MEDIA_GROUP_TYPE_AUDIO = 1;
1317
+
1318
+ export const DESTINATION_TYPE = {
1319
+ CONVERSATION_URL: 'CONVERSATION_URL',
1320
+ MEETING_LINK: 'MEETING_LINK',
1321
+ SIP_URI: 'SIP_URI',
1322
+ PERSONAL_ROOM: 'PERSONAL_ROOM',
1323
+ ONE_ON_ONE_CALL: 'ONE_ON_ONE_CALL',
1324
+ LOCUS_ID: 'LOCUS_ID',
1325
+ MEETING_ID: 'MEETING_ID',
1326
+ MEETING_UUID: 'MEETING_UUID',
1327
+ } as const;
1328
+
1329
+ export type DESTINATION_TYPE = Enum<typeof DESTINATION_TYPE>;
@@ -428,11 +428,6 @@ SelfUtils.mutedByOthersChanged = (oldSelf, changedSelf) => {
428
428
  return false;
429
429
  }
430
430
 
431
- // there is no need to trigger user update if no one muted user
432
- if (changedSelf.selfIdentity === changedSelf.modifiedBy) {
433
- return false;
434
- }
435
-
436
431
  return (
437
432
  changedSelf.remoteMuted !== null &&
438
433
  (oldSelf.remoteMuted !== changedSelf.remoteMuted ||
@@ -1,5 +1,5 @@
1
1
  import {Defer} from '@webex/common';
2
- import {ConnectionState, Event} from '@webex/internal-media-core';
2
+ import {ConnectionState, MediaConnectionEventNames} from '@webex/internal-media-core';
3
3
  import LoggerProxy from '../common/logs/logger-proxy';
4
4
  import {ICE_AND_DTLS_CONNECTION_TIMEOUT} from '../constants';
5
5
 
@@ -15,8 +15,10 @@ export default class MediaConnectionAwaiter {
15
15
  private timer: any;
16
16
  private defer: Defer;
17
17
  private retried: boolean;
18
+ private iceConnected: boolean;
18
19
  private onTimeoutCallback: () => void;
19
- private connectionStateCallback: () => void;
20
+ private peerConnectionStateCallback: () => void;
21
+ private iceConnectionStateCallback: () => void;
20
22
  private iceGatheringStateCallback: () => void;
21
23
 
22
24
  /**
@@ -26,9 +28,11 @@ export default class MediaConnectionAwaiter {
26
28
  this.webrtcMediaConnection = webrtcMediaConnection;
27
29
  this.defer = new Defer();
28
30
  this.retried = false;
31
+ this.iceConnected = false;
29
32
  this.onTimeoutCallback = this.onTimeout.bind(this);
30
- this.connectionStateCallback = this.connectionStateListenerCallback.bind(this);
31
- this.iceGatheringStateCallback = this.iceGatheringStateListenerCallback.bind(this);
33
+ this.peerConnectionStateCallback = this.peerConnectionStateHandler.bind(this);
34
+ this.iceConnectionStateCallback = this.iceConnectionStateHandler.bind(this);
35
+ this.iceGatheringStateCallback = this.iceGatheringStateHandler.bind(this);
32
36
  }
33
37
 
34
38
  /**
@@ -40,6 +44,15 @@ export default class MediaConnectionAwaiter {
40
44
  return this.webrtcMediaConnection.getConnectionState() === ConnectionState.Connected;
41
45
  }
42
46
 
47
+ /**
48
+ * Returns true if the connection is in an unrecoverable "failed" state
49
+ *
50
+ * @returns {boolean}
51
+ */
52
+ private isFailed(): boolean {
53
+ return this.webrtcMediaConnection.getConnectionState() === ConnectionState.Failed;
54
+ }
55
+
43
56
  /**
44
57
  * Returns true if the ICE Gathering is completed, false otherwise.
45
58
  *
@@ -56,22 +69,40 @@ export default class MediaConnectionAwaiter {
56
69
  */
57
70
  private clearCallbacks(): void {
58
71
  this.webrtcMediaConnection.off(
59
- Event.ICE_GATHERING_STATE_CHANGED,
72
+ MediaConnectionEventNames.ICE_GATHERING_STATE_CHANGED,
60
73
  this.iceGatheringStateCallback
61
74
  );
62
- this.webrtcMediaConnection.off(Event.CONNECTION_STATE_CHANGED, this.connectionStateCallback);
75
+ this.webrtcMediaConnection.off(
76
+ MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED,
77
+ this.peerConnectionStateCallback
78
+ );
79
+ this.webrtcMediaConnection.off(
80
+ MediaConnectionEventNames.ICE_CONNECTION_STATE_CHANGED,
81
+ this.iceConnectionStateCallback
82
+ );
63
83
  }
64
84
 
65
85
  /**
66
- * Listener for connection state change.
86
+ * On connection state change.
67
87
  *
68
88
  * @returns {void}
69
89
  */
70
- connectionStateListenerCallback(): void {
90
+ connectionStateChange(): void {
71
91
  LoggerProxy.logger.log(
72
- `Media:MediaConnectionAwaiter#connectionStateListenerCallback --> connection state: ${this.webrtcMediaConnection.getConnectionState()}`
92
+ `Media:MediaConnectionAwaiter#connectionStateChange --> connection state: ${this.webrtcMediaConnection.getConnectionState()}`
73
93
  );
74
94
 
95
+ if (this.isFailed()) {
96
+ LoggerProxy.logger.warn(
97
+ 'Media:MediaConnectionAwaiter#connectionStateChange --> ICE failed, rejecting'
98
+ );
99
+ this.clearCallbacks();
100
+
101
+ this.defer.reject({
102
+ iceConnected: this.iceConnected,
103
+ });
104
+ }
105
+
75
106
  if (!this.isConnected()) {
76
107
  return;
77
108
  }
@@ -83,16 +114,50 @@ export default class MediaConnectionAwaiter {
83
114
  this.defer.resolve();
84
115
  }
85
116
 
117
+ /**
118
+ * Listener for peer connection state change.
119
+ *
120
+ * @returns {void}
121
+ */
122
+ peerConnectionStateHandler(): void {
123
+ const peerConnectionState = this.webrtcMediaConnection.getPeerConnectionState();
124
+
125
+ LoggerProxy.logger.log(
126
+ `Media:MediaConnectionAwaiter#peerConnectionStateHandler --> Peer connection state change -> ${peerConnectionState}`
127
+ );
128
+
129
+ this.connectionStateChange();
130
+ }
131
+
132
+ /**
133
+ * Listener for ICE connection state change.
134
+ *
135
+ * @returns {void}
136
+ */
137
+ iceConnectionStateHandler(): void {
138
+ const iceConnectionState = this.webrtcMediaConnection.getIceConnectionState();
139
+
140
+ LoggerProxy.logger.log(
141
+ `Media:MediaConnectionAwaiter#iceConnectionStateHandler --> ICE connection state change -> ${iceConnectionState}`
142
+ );
143
+
144
+ if (iceConnectionState === 'connected' && !this.iceConnected) {
145
+ this.iceConnected = true;
146
+ }
147
+
148
+ this.connectionStateChange();
149
+ }
150
+
86
151
  /**
87
152
  * Listener for ICE gathering state change.
88
153
  *
89
154
  * @returns {void}
90
155
  */
91
- iceGatheringStateListenerCallback(): void {
156
+ iceGatheringStateHandler(): void {
92
157
  const iceGatheringState = this.webrtcMediaConnection.getIceGatheringState();
93
158
 
94
159
  LoggerProxy.logger.log(
95
- `Media:MediaConnectionAwaiter#iceGatheringStateListenerCallback --> ICE gathering state change -> ${iceGatheringState}`
160
+ `Media:MediaConnectionAwaiter#iceGatheringStateHandler --> ICE gathering state change -> ${iceGatheringState}`
96
161
  );
97
162
 
98
163
  if (!this.isIceGatheringCompleted()) {
@@ -147,7 +212,9 @@ export default class MediaConnectionAwaiter {
147
212
 
148
213
  this.clearCallbacks();
149
214
 
150
- this.defer.reject();
215
+ this.defer.reject({
216
+ iceConnected: this.iceConnected,
217
+ });
151
218
  }
152
219
 
153
220
  /**
@@ -160,10 +227,18 @@ export default class MediaConnectionAwaiter {
160
227
  return Promise.resolve();
161
228
  }
162
229
 
163
- this.webrtcMediaConnection.on(Event.CONNECTION_STATE_CHANGED, this.connectionStateCallback);
230
+ this.webrtcMediaConnection.on(
231
+ MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED,
232
+ this.peerConnectionStateCallback
233
+ );
234
+
235
+ this.webrtcMediaConnection.on(
236
+ MediaConnectionEventNames.ICE_CONNECTION_STATE_CHANGED,
237
+ this.iceConnectionStateCallback
238
+ );
164
239
 
165
240
  this.webrtcMediaConnection.on(
166
- Event.ICE_GATHERING_STATE_CHANGED,
241
+ MediaConnectionEventNames.ICE_GATHERING_STATE_CHANGED,
167
242
  this.iceGatheringStateCallback
168
243
  );
169
244
 
@@ -163,6 +163,19 @@ Media.createMediaConnection = (
163
163
  // we might not have any TURN server if TURN discovery failed or wasn't done or
164
164
  // we might get an empty TURN url if we land on a video mesh node
165
165
  if (turnServerInfo?.url) {
166
+ if (!isBrowser('firefox')) {
167
+ let bareTurnServer = turnServerInfo.url;
168
+ bareTurnServer = bareTurnServer.replace('turns:', 'turn:');
169
+ bareTurnServer = bareTurnServer.replace('443', '5004');
170
+
171
+ iceServers.push({
172
+ urls: bareTurnServer,
173
+ username: turnServerInfo.username || '',
174
+ credential: turnServerInfo.password || '',
175
+ });
176
+ }
177
+
178
+ // TURN-TLS server
166
179
  iceServers.push({
167
180
  urls: turnServerInfo.url,
168
181
  username: turnServerInfo.username || '',
@@ -0,0 +1,65 @@
1
+ import {MediaConnectionEventNames, ConnectionState} from '@webex/internal-media-core';
2
+ import EventsScope from '../common/events/events-scope';
3
+ import {Enum} from '../constants';
4
+
5
+ export const ConnectionStateEvent = {
6
+ stateChanged: 'connectionState:changed',
7
+ } as const;
8
+
9
+ export type ConnectionStateEvent = Enum<typeof ConnectionStateEvent>;
10
+
11
+ export interface ConnectionStateChangedEvent {
12
+ /**
13
+ * Current overall connection state
14
+ */
15
+ state: ConnectionState;
16
+ }
17
+
18
+ /**
19
+ * @class ConnectionStateHandler
20
+ */
21
+ export class ConnectionStateHandler extends EventsScope {
22
+ private webrtcMediaConnection: any;
23
+
24
+ private mediaConnectionState: ConnectionState;
25
+
26
+ /**
27
+ * @param {WebRTCMeeting} webrtcMediaConnection
28
+ */
29
+ constructor(webrtcMediaConnection) {
30
+ super();
31
+
32
+ this.webrtcMediaConnection = webrtcMediaConnection;
33
+
34
+ this.webrtcMediaConnection.on(
35
+ MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED,
36
+ this.handleConnectionStateChange.bind(this)
37
+ );
38
+
39
+ this.webrtcMediaConnection.on(
40
+ MediaConnectionEventNames.ICE_CONNECTION_STATE_CHANGED,
41
+ this.handleConnectionStateChange.bind(this)
42
+ );
43
+ }
44
+
45
+ /**
46
+ * Handler for connection state change.
47
+ *
48
+ * @returns {void}
49
+ */
50
+ private handleConnectionStateChange(): void {
51
+ const newConnectionState = this.webrtcMediaConnection.getConnectionState();
52
+
53
+ if (newConnectionState !== this.mediaConnectionState) {
54
+ this.mediaConnectionState = newConnectionState;
55
+ this.emit(
56
+ {
57
+ file: 'connectionStateHandler',
58
+ function: 'handleConnectionStateChange',
59
+ },
60
+ ConnectionStateEvent.stateChanged,
61
+ {state: this.mediaConnectionState}
62
+ );
63
+ }
64
+ }
65
+ }