@webex/plugin-meetings 3.1.0-next.5 → 3.1.0-next.7

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.
@@ -84,10 +84,34 @@ export default class MediaProperties {
84
84
  * @returns {Promise<void>}
85
85
  */
86
86
  waitForMediaConnectionConnected(): Promise<void>;
87
+ /**
88
+ * Returns ICE transport information:
89
+ * - selectedCandidatePairChanges - number of times the selected candidate pair was changed, it should be at least 1 for successful connections
90
+ * it will be -1 if browser doesn't supply this information
91
+ * - numTransports - number of transports (should be 1 if we're using bundle)
92
+ *
93
+ * @param {Array<any>} allStatsReports array of RTC stats reports
94
+ * @returns {Object}
95
+ */
96
+ private getTransportInfo;
87
97
  /**
88
98
  * Returns the type of a connection that has been established
99
+ * It should be 'UDP' | 'TCP' | 'TURN-TLS' | 'TURN-TCP' | 'TURN-UDP' | 'unknown'
100
+ *
101
+ * If connection was not established, it returns 'unknown'
102
+ *
103
+ * @param {Array<any>} allStatsReports array of RTC stats reports
104
+ * @returns {string}
105
+ */
106
+ private getConnectionType;
107
+ /**
108
+ * Returns information about current webrtc media connection
89
109
  *
90
- * @returns {Promise<'UDP' | 'TCP' | 'TURN-TLS' | 'TURN-TCP' | 'TURN-UDP' | 'unknown'>}
110
+ * @returns {Promise<Object>}
91
111
  */
92
- getCurrentConnectionType(): Promise<string>;
112
+ getCurrentConnectionInfo(): Promise<{
113
+ connectionType: string;
114
+ selectedCandidatePairChanges: number;
115
+ numTransports: number;
116
+ }>;
93
117
  }
@@ -454,6 +454,7 @@ export default class Meeting extends StatelessWebexPlugin {
454
454
  private deferSDPAnswer?;
455
455
  private sdpResponseTimer?;
456
456
  private hasMediaConnectionConnectedAtLeastOnce;
457
+ private joinWithMediaRetryInfo?;
457
458
  /**
458
459
  * @param {Object} attrs
459
460
  * @param {Object} options
@@ -1078,10 +1079,7 @@ export default class Meeting extends StatelessWebexPlugin {
1078
1079
  joinWithMedia(options?: {
1079
1080
  joinOptions?: any;
1080
1081
  mediaOptions?: AddMediaOptions;
1081
- }): Promise<{
1082
- join: any;
1083
- media: void;
1084
- }>;
1082
+ }): any;
1085
1083
  /**
1086
1084
  * Initiates the reconnection of the media in the meeting
1087
1085
  *
@@ -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.1.0-next.5"
65
+ version: "3.1.0-next.7"
66
66
  });
67
67
  var _default = exports.default = Webinar;
68
68
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,7 +43,7 @@
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.1.0-next.5",
46
+ "@webex/plugin-meetings": "3.1.0-next.7",
47
47
  "@webex/plugin-rooms": "3.0.0-next.16",
48
48
  "@webex/test-helper-chai": "3.0.0-next.14",
49
49
  "@webex/test-helper-mocha": "3.0.0-next.14",
@@ -70,7 +70,7 @@
70
70
  "@webex/internal-plugin-metrics": "3.0.0-next.14",
71
71
  "@webex/internal-plugin-support": "3.0.0-next.16",
72
72
  "@webex/internal-plugin-user": "3.0.0-next.14",
73
- "@webex/internal-plugin-voicea": "3.1.0-next.5",
73
+ "@webex/internal-plugin-voicea": "3.1.0-next.7",
74
74
  "@webex/media-helpers": "3.0.1-next.17",
75
75
  "@webex/plugin-people": "3.0.0-next.16",
76
76
  "@webex/plugin-rooms": "3.0.0-next.16",
@@ -91,5 +91,5 @@
91
91
  "//": [
92
92
  "TODO: upgrade jwt-decode when moving to node 18"
93
93
  ],
94
- "version": "3.1.0-next.5"
94
+ "version": "3.1.0-next.7"
95
95
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable class-methods-use-this */
1
2
  import {
2
3
  LocalCameraStream,
3
4
  LocalMicrophoneStream,
@@ -182,25 +183,45 @@ export default class MediaProperties {
182
183
  }
183
184
 
184
185
  /**
185
- * Returns the type of a connection that has been established
186
+ * Returns ICE transport information:
187
+ * - selectedCandidatePairChanges - number of times the selected candidate pair was changed, it should be at least 1 for successful connections
188
+ * it will be -1 if browser doesn't supply this information
189
+ * - numTransports - number of transports (should be 1 if we're using bundle)
186
190
  *
187
- * @returns {Promise<'UDP' | 'TCP' | 'TURN-TLS' | 'TURN-TCP' | 'TURN-UDP' | 'unknown'>}
191
+ * @param {Array<any>} allStatsReports array of RTC stats reports
192
+ * @returns {Object}
188
193
  */
189
- async getCurrentConnectionType() {
190
- // we can only get the connection type after ICE connection has been established
191
- await this.waitForMediaConnectionConnected();
192
-
193
- const allStatsReports = [];
194
+ private getTransportInfo(allStatsReports: any[]): {
195
+ selectedCandidatePairChanges: number;
196
+ numTransports: number;
197
+ } {
198
+ const transports = allStatsReports.filter((report) => report.type === 'transport');
194
199
 
195
- try {
196
- const statsResult = await this.webrtcMediaConnection.getStats();
197
- statsResult.forEach((report) => allStatsReports.push(report));
198
- } catch (error) {
200
+ if (transports.length > 1) {
199
201
  LoggerProxy.logger.warn(
200
- `Media:properties#getCurrentConnectionType --> getStats() failed: ${error}`
202
+ `Media:properties#getSelectedCandidatePairChanges --> found more than 1 transport: ${transports.length}`
201
203
  );
202
204
  }
203
205
 
206
+ return {
207
+ selectedCandidatePairChanges:
208
+ transports.length > 0 && transports[0].selectedCandidatePairChanges !== undefined
209
+ ? transports[0].selectedCandidatePairChanges
210
+ : -1,
211
+ numTransports: transports.length,
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Returns the type of a connection that has been established
217
+ * It should be 'UDP' | 'TCP' | 'TURN-TLS' | 'TURN-TCP' | 'TURN-UDP' | 'unknown'
218
+ *
219
+ * If connection was not established, it returns 'unknown'
220
+ *
221
+ * @param {Array<any>} allStatsReports array of RTC stats reports
222
+ * @returns {string}
223
+ */
224
+ private getConnectionType(allStatsReports: any[]) {
204
225
  const successfulCandidatePairs = allStatsReports.filter(
205
226
  (report) => report.type === 'candidate-pair' && report.state?.toLowerCase() === 'succeeded'
206
227
  );
@@ -215,7 +236,7 @@ export default class MediaProperties {
215
236
 
216
237
  if (localCandidate === undefined) {
217
238
  LoggerProxy.logger.warn(
218
- `Media:properties#getCurrentConnectionType --> failed to find local candidate "${pair.localCandidateId}" in getStats() results`
239
+ `Media:properties#getConnectionType --> failed to find local candidate "${pair.localCandidateId}" in getStats() results`
219
240
  );
220
241
 
221
242
  return false;
@@ -235,7 +256,7 @@ export default class MediaProperties {
235
256
  return true;
236
257
  }
237
258
  LoggerProxy.logger.warn(
238
- `Media:properties#getCurrentConnectionType --> missing localCandidate.protocol, candidateType=${localCandidate.candidateType}`
259
+ `Media:properties#getConnectionType --> missing localCandidate.protocol, candidateType=${localCandidate.candidateType}`
239
260
  );
240
261
 
241
262
  return false;
@@ -247,7 +268,7 @@ export default class MediaProperties {
247
268
  .map((report) => report.state);
248
269
 
249
270
  LoggerProxy.logger.warn(
250
- `Media:properties#getCurrentConnectionType --> all candidate pair states: ${JSON.stringify(
271
+ `Media:properties#getConnectionType --> all candidate pair states: ${JSON.stringify(
251
272
  candidatePairStates
252
273
  )}`
253
274
  );
@@ -255,4 +276,35 @@ export default class MediaProperties {
255
276
 
256
277
  return foundConnectionType;
257
278
  }
279
+
280
+ /**
281
+ * Returns information about current webrtc media connection
282
+ *
283
+ * @returns {Promise<Object>}
284
+ */
285
+ async getCurrentConnectionInfo(): Promise<{
286
+ connectionType: string;
287
+ selectedCandidatePairChanges: number;
288
+ numTransports: number;
289
+ }> {
290
+ const allStatsReports = [];
291
+
292
+ try {
293
+ const statsResult = await this.webrtcMediaConnection.getStats();
294
+ statsResult.forEach((report) => allStatsReports.push(report));
295
+ } catch (error) {
296
+ LoggerProxy.logger.warn(
297
+ `Media:properties#getCurrentConnectionInfo --> getStats() failed: ${error}`
298
+ );
299
+ }
300
+
301
+ const connectionType = this.getConnectionType(allStatsReports);
302
+ const {selectedCandidatePairChanges, numTransports} = this.getTransportInfo(allStatsReports);
303
+
304
+ return {
305
+ connectionType,
306
+ selectedCandidatePairChanges,
307
+ numTransports,
308
+ };
309
+ }
258
310
  }
@@ -678,6 +678,7 @@ export default class Meeting extends StatelessWebexPlugin {
678
678
  private deferSDPAnswer?: Defer; // used for waiting for a response
679
679
  private sdpResponseTimer?: ReturnType<typeof setTimeout>;
680
680
  private hasMediaConnectionConnectedAtLeastOnce: boolean;
681
+ private joinWithMediaRetryInfo?: {isRetry: boolean; prevJoinResponse?: any};
681
682
 
682
683
  /**
683
684
  * @param {Object} attrs
@@ -1459,6 +1460,15 @@ export default class Meeting extends StatelessWebexPlugin {
1459
1460
  * @memberof Meeting
1460
1461
  */
1461
1462
  this.hasMediaConnectionConnectedAtLeastOnce = false;
1463
+
1464
+ /**
1465
+ * Information needed for a retry of a call to joinWithMedia
1466
+ * @instance
1467
+ * @type {{isRetry: boolean; prevJoinResponse?: any}}
1468
+ * @private
1469
+ * @memberof Meeting
1470
+ */
1471
+ this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
1462
1472
  }
1463
1473
 
1464
1474
  /**
@@ -4527,6 +4537,7 @@ export default class Meeting extends StatelessWebexPlugin {
4527
4537
  } = {}
4528
4538
  ) {
4529
4539
  const {mediaOptions, joinOptions = {}} = options;
4540
+ const {isRetry, prevJoinResponse} = this.joinWithMediaRetryInfo;
4530
4541
 
4531
4542
  if (!mediaOptions?.allowMediaInLobby) {
4532
4543
  return Promise.reject(
@@ -4538,6 +4549,7 @@ export default class Meeting extends StatelessWebexPlugin {
4538
4549
  LoggerProxy.logger.info('Meeting:index#joinWithMedia called');
4539
4550
 
4540
4551
  let joined = false;
4552
+ let joinResponse = prevJoinResponse;
4541
4553
 
4542
4554
  try {
4543
4555
  let turnServerInfo;
@@ -4550,7 +4562,14 @@ export default class Meeting extends StatelessWebexPlugin {
4550
4562
  ({turnDiscoverySkippedReason} = turnDiscoveryRequest);
4551
4563
  joinOptions.roapMessage = turnDiscoveryRequest.roapMessage;
4552
4564
 
4553
- const joinResponse = await this.join(joinOptions);
4565
+ if (!joinResponse) {
4566
+ LoggerProxy.logger.info(
4567
+ 'Meeting:index#joinWithMedia ---> calling join with joinOptions, ',
4568
+ joinOptions
4569
+ );
4570
+
4571
+ joinResponse = await this.join(joinOptions);
4572
+ }
4554
4573
 
4555
4574
  joined = true;
4556
4575
 
@@ -4568,6 +4587,8 @@ export default class Meeting extends StatelessWebexPlugin {
4568
4587
 
4569
4588
  const mediaResponse = await this.addMedia(mediaOptions, turnServerInfo);
4570
4589
 
4590
+ this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
4591
+
4571
4592
  return {
4572
4593
  join: joinResponse,
4573
4594
  media: mediaResponse,
@@ -4579,7 +4600,7 @@ export default class Meeting extends StatelessWebexPlugin {
4579
4600
 
4580
4601
  this.roap.abortTurnDiscovery();
4581
4602
 
4582
- if (joined) {
4603
+ if (joined && isRetry) {
4583
4604
  try {
4584
4605
  await this.leave({resourceId: joinOptions?.resourceId, reason: 'joinWithMedia failure'});
4585
4606
  } catch (e) {
@@ -4596,12 +4617,23 @@ export default class Meeting extends StatelessWebexPlugin {
4596
4617
  reason: error.message,
4597
4618
  stack: error.stack,
4598
4619
  leaveErrorReason: leaveError?.message,
4620
+ isRetry,
4599
4621
  },
4600
4622
  {
4601
4623
  type: error.name,
4602
4624
  }
4603
4625
  );
4604
4626
 
4627
+ if (!isRetry) {
4628
+ LoggerProxy.logger.warn('Meeting:index#joinWithMedia --> retrying call to joinWithMedia');
4629
+ this.joinWithMediaRetryInfo.isRetry = true;
4630
+ this.joinWithMediaRetryInfo.prevJoinResponse = joinResponse;
4631
+
4632
+ return this.joinWithMedia(options);
4633
+ }
4634
+
4635
+ this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
4636
+
4605
4637
  throw error;
4606
4638
  }
4607
4639
  }
@@ -6780,7 +6812,8 @@ export default class Meeting extends StatelessWebexPlugin {
6780
6812
  await this.enqueueScreenShareFloorRequest();
6781
6813
  }
6782
6814
 
6783
- const connectionType = await this.mediaProperties.getCurrentConnectionType();
6815
+ const {connectionType, selectedCandidatePairChanges, numTransports} =
6816
+ await this.mediaProperties.getCurrentConnectionInfo();
6784
6817
  // @ts-ignore
6785
6818
  const reachabilityStats = await this.webex.meetings.reachability.getReachabilityMetrics();
6786
6819
 
@@ -6788,8 +6821,11 @@ export default class Meeting extends StatelessWebexPlugin {
6788
6821
  correlation_id: this.correlationId,
6789
6822
  locus_id: this.locusUrl.split('/').pop(),
6790
6823
  connectionType,
6824
+ selectedCandidatePairChanges,
6825
+ numTransports,
6791
6826
  isMultistream: this.isMultistream,
6792
6827
  retriedWithTurnServer: this.retriedWithTurnServer,
6828
+ isJoinWithMediaRetry: this.joinWithMediaRetryInfo.isRetry,
6793
6829
  ...reachabilityStats,
6794
6830
  });
6795
6831
  // @ts-ignore
@@ -6811,16 +6847,22 @@ export default class Meeting extends StatelessWebexPlugin {
6811
6847
  // @ts-ignore
6812
6848
  const reachabilityMetrics = await this.webex.meetings.reachability.getReachabilityMetrics();
6813
6849
 
6850
+ const {selectedCandidatePairChanges, numTransports} =
6851
+ await this.mediaProperties.getCurrentConnectionInfo();
6852
+
6814
6853
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
6815
6854
  correlation_id: this.correlationId,
6816
6855
  locus_id: this.locusUrl.split('/').pop(),
6817
6856
  reason: error.message,
6818
6857
  stack: error.stack,
6819
6858
  code: error.code,
6859
+ selectedCandidatePairChanges,
6860
+ numTransports,
6820
6861
  turnDiscoverySkippedReason: this.turnDiscoverySkippedReason,
6821
6862
  turnServerUsed: this.turnServerUsed,
6822
6863
  retriedWithTurnServer: this.retriedWithTurnServer,
6823
6864
  isMultistream: this.isMultistream,
6865
+ isJoinWithMediaRetry: this.joinWithMediaRetryInfo.isRetry,
6824
6866
  signalingState:
6825
6867
  this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
6826
6868
  ?.signalingState ||