@webex/plugin-meetings 3.0.0-beta.291 → 3.0.0-beta.293

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 (38) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/webex-errors.js +21 -1
  4. package/dist/common/errors/webex-errors.js.map +1 -1
  5. package/dist/config.js +1 -2
  6. package/dist/config.js.map +1 -1
  7. package/dist/interpretation/index.js +1 -1
  8. package/dist/interpretation/siLanguage.js +1 -1
  9. package/dist/meeting/index.js +357 -173
  10. package/dist/meeting/index.js.map +1 -1
  11. package/dist/meetings/index.js +0 -17
  12. package/dist/meetings/index.js.map +1 -1
  13. package/dist/reconnection-manager/index.js +2 -2
  14. package/dist/reconnection-manager/index.js.map +1 -1
  15. package/dist/roap/index.js +3 -2
  16. package/dist/roap/index.js.map +1 -1
  17. package/dist/roap/turnDiscovery.js +15 -16
  18. package/dist/roap/turnDiscovery.js.map +1 -1
  19. package/dist/types/common/errors/webex-errors.d.ts +12 -0
  20. package/dist/types/config.d.ts +0 -1
  21. package/dist/types/meeting/index.d.ts +42 -1
  22. package/dist/types/meetings/index.d.ts +0 -8
  23. package/dist/types/roap/index.d.ts +2 -1
  24. package/dist/types/roap/turnDiscovery.d.ts +3 -2
  25. package/dist/webinar/index.js +1 -1
  26. package/package.json +19 -19
  27. package/src/common/errors/webex-errors.ts +17 -0
  28. package/src/config.ts +0 -1
  29. package/src/meeting/index.ts +150 -9
  30. package/src/meetings/index.ts +0 -15
  31. package/src/reconnection-manager/index.ts +2 -2
  32. package/src/roap/index.ts +3 -2
  33. package/src/roap/turnDiscovery.ts +8 -12
  34. package/test/unit/spec/meeting/index.js +310 -45
  35. package/test/unit/spec/meetings/index.js +0 -28
  36. package/test/unit/spec/reconnection-manager/index.js +1 -0
  37. package/test/unit/spec/roap/index.ts +16 -22
  38. package/test/unit/spec/roap/turnDiscovery.ts +42 -31
@@ -38,6 +38,7 @@ import {
38
38
  UserInLobbyError,
39
39
  NoMediaEstablishedYetError,
40
40
  UserNotJoinedError,
41
+ AddMediaFailed,
41
42
  } from '../common/errors/webex-errors';
42
43
  import {StatsAnalyzer, EVENTS as StatsAnalyzerEvents} from '../statsAnalyzer';
43
44
  import NetworkQualityMonitor from '../networkQualityMonitor';
@@ -569,6 +570,7 @@ export default class Meeting extends StatelessWebexPlugin {
569
570
  allowMediaInLobby: boolean;
570
571
  turnDiscoverySkippedReason: string;
571
572
  turnServerUsed: boolean;
573
+ private retriedWithTurnServer: boolean;
572
574
  private sendSlotManager: SendSlotManager = new SendSlotManager(LoggerProxy);
573
575
  private deferSDPAnswer?: Defer; // used for waiting for a response
574
576
  private sdpResponseTimer?: ReturnType<typeof setTimeout>;
@@ -1304,6 +1306,15 @@ export default class Meeting extends StatelessWebexPlugin {
1304
1306
  * @memberof Meeting
1305
1307
  */
1306
1308
  this.turnServerUsed = false;
1309
+
1310
+ /**
1311
+ * Whether retry was done using TURN Discovery.
1312
+ * @instance
1313
+ * @type {boolean}
1314
+ * @private
1315
+ * @memberof Meeting
1316
+ */
1317
+ this.retriedWithTurnServer = false;
1307
1318
  }
1308
1319
 
1309
1320
  /**
@@ -5708,8 +5719,8 @@ export default class Meeting extends StatelessWebexPlugin {
5708
5719
  this.webex.internal.newMetrics.submitClientEvent({
5709
5720
  name: 'client.ice.end',
5710
5721
  payload: {
5711
- canProceed: false,
5712
- icePhase: 'JOIN_MEETING_FINAL',
5722
+ canProceed: !this.turnServerUsed, // If we haven't done turn tls retry yet we will proceed with join attempt
5723
+ icePhase: this.turnServerUsed ? 'JOIN_MEETING_FINAL' : 'JOIN_MEETING_RETRY',
5713
5724
  errors: [
5714
5725
  // @ts-ignore
5715
5726
  this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode({
@@ -5817,20 +5828,109 @@ export default class Meeting extends StatelessWebexPlugin {
5817
5828
  }
5818
5829
 
5819
5830
  /**
5820
- * Does TURN discovery, SDP offer/answer exhange, establishes ICE connection and DTLS handshake
5831
+ * Calls establishMediaConnection with isForced = true to force turn discovery to happen
5821
5832
  *
5822
5833
  * @private
5823
5834
  * @param {RemoteMediaManagerConfiguration} [remoteMediaManagerConfig]
5824
5835
  * @param {BundlePolicy} [bundlePolicy]
5825
5836
  * @returns {Promise<void>}
5826
5837
  */
5827
- private async establishMediaConnection(
5838
+ private async retryEstablishMediaConnectionWithForcedTurnDiscovery(
5828
5839
  remoteMediaManagerConfig?: RemoteMediaManagerConfiguration,
5829
5840
  bundlePolicy?: BundlePolicy
5841
+ ): Promise<void> {
5842
+ const LOG_HEADER =
5843
+ 'Meeting:index#addMedia():retryEstablishMediaConnectionWithForcedTurnDiscovery -->';
5844
+
5845
+ try {
5846
+ await this.establishMediaConnection(remoteMediaManagerConfig, bundlePolicy, true);
5847
+ } catch (err) {
5848
+ LoggerProxy.logger.error(
5849
+ `${LOG_HEADER} retry with TURN-TLS failed, media connection unable to connect, `,
5850
+ err
5851
+ );
5852
+
5853
+ throw err;
5854
+ }
5855
+ }
5856
+
5857
+ /**
5858
+ * Does relevant clean up before retrying to establish media connection
5859
+ * and performs the retry with forced turn discovery
5860
+ *
5861
+ * @private
5862
+ * @param {RemoteMediaManagerConfiguration} [remoteMediaManagerConfig]
5863
+ * @param {BundlePolicy} [bundlePolicy]
5864
+ * @returns {Promise<void>}
5865
+ */
5866
+ private async retryWithForcedTurnDiscovery(
5867
+ remoteMediaManagerConfig?: RemoteMediaManagerConfiguration,
5868
+ bundlePolicy?: BundlePolicy
5869
+ ): Promise<void> {
5870
+ this.retriedWithTurnServer = true;
5871
+
5872
+ await this.cleanUpBeforeRetryWithTurnServer();
5873
+
5874
+ await this.retryEstablishMediaConnectionWithForcedTurnDiscovery(
5875
+ remoteMediaManagerConfig,
5876
+ bundlePolicy
5877
+ );
5878
+ }
5879
+
5880
+ /**
5881
+ * If waitForMediaConnectionConnected() fails when we haven't done turn discovery then we
5882
+ * attempt to establish a media connection again, but this time using turn discovery. If we
5883
+ * used turn discovery on the first pass we do not attempt connection again.
5884
+ *
5885
+ * @private
5886
+ * @param {Error} error
5887
+ * @param {RemoteMediaManagerConfiguration} [remoteMediaManagerConfig]
5888
+ * @param {BundlePolicy} [bundlePolicy]
5889
+ * @returns {Promise<void>}
5890
+ */
5891
+ private async handleWaitForMediaConnectionConnectedError(
5892
+ error: Error,
5893
+ remoteMediaManagerConfig?: RemoteMediaManagerConfiguration,
5894
+ bundlePolicy?: BundlePolicy
5895
+ ): Promise<void> {
5896
+ const LOG_HEADER = 'Meeting:index#addMedia():handleWaitForMediaConnectionConnectedError -->';
5897
+
5898
+ // @ts-ignore - config coming from registerPlugin
5899
+ if (!this.turnServerUsed) {
5900
+ LoggerProxy.logger.info(
5901
+ `${LOG_HEADER} error waiting for media to connect on UDP, TCP, retrying using TURN-TLS, `,
5902
+ error
5903
+ );
5904
+
5905
+ await this.retryWithForcedTurnDiscovery(remoteMediaManagerConfig, bundlePolicy);
5906
+ } else {
5907
+ LoggerProxy.logger.error(
5908
+ `${LOG_HEADER} error waiting for media to connect using UDP, TCP and TURN-TLS`,
5909
+ error
5910
+ );
5911
+
5912
+ throw new AddMediaFailed();
5913
+ }
5914
+ }
5915
+
5916
+ /**
5917
+ * Does TURN discovery, SDP offer/answer exhange, establishes ICE connection and DTLS handshake.
5918
+ *
5919
+ * @private
5920
+ * @param {RemoteMediaManagerConfiguration} [remoteMediaManagerConfig]
5921
+ * @param {BundlePolicy} [bundlePolicy]
5922
+ * @param {boolean} [isForced] - let isForced be true to do turn discovery regardless of reachability results
5923
+ * @returns {Promise<void>}
5924
+ */
5925
+ private async establishMediaConnection(
5926
+ remoteMediaManagerConfig?: RemoteMediaManagerConfiguration,
5927
+ bundlePolicy?: BundlePolicy,
5928
+ isForced?: boolean
5830
5929
  ): Promise<void> {
5831
5930
  const LOG_HEADER = 'Meeting:index#addMedia():establishMediaConnection -->';
5832
5931
  // @ts-ignore
5833
5932
  const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
5933
+ const isRetry = this.retriedWithTurnServer;
5834
5934
 
5835
5935
  try {
5836
5936
  // @ts-ignore
@@ -5838,7 +5938,7 @@ export default class Meeting extends StatelessWebexPlugin {
5838
5938
  name: 'internal.client.add-media.turn-discovery.start',
5839
5939
  });
5840
5940
 
5841
- const turnDiscoveryObject = await this.roap.doTurnDiscovery(this, false);
5941
+ const turnDiscoveryObject = await this.roap.doTurnDiscovery(this, isRetry, isForced);
5842
5942
 
5843
5943
  this.turnDiscoverySkippedReason = turnDiscoveryObject?.turnDiscoverySkippedReason;
5844
5944
  this.turnServerUsed = !this.turnDiscoverySkippedReason;
@@ -5853,6 +5953,7 @@ export default class Meeting extends StatelessWebexPlugin {
5853
5953
  correlation_id: this.correlationId,
5854
5954
  latency: cdl.getTurnDiscoveryTime(),
5855
5955
  turnServerUsed: this.turnServerUsed,
5956
+ retriedWithTurnServer: this.retriedWithTurnServer,
5856
5957
  });
5857
5958
  }
5858
5959
 
@@ -5898,7 +5999,15 @@ export default class Meeting extends StatelessWebexPlugin {
5898
5999
  throw error;
5899
6000
  }
5900
6001
 
5901
- await this.waitForMediaConnectionConnected();
6002
+ try {
6003
+ await this.waitForMediaConnectionConnected();
6004
+ } catch (error) {
6005
+ await this.handleWaitForMediaConnectionConnectedError(
6006
+ error,
6007
+ remoteMediaManagerConfig,
6008
+ bundlePolicy
6009
+ );
6010
+ }
5902
6011
  }
5903
6012
 
5904
6013
  /**
@@ -5907,7 +6016,7 @@ export default class Meeting extends StatelessWebexPlugin {
5907
6016
  * @private
5908
6017
  * @returns {Promise<void>}
5909
6018
  */
5910
- private async cleanUpOnAddMediaFailure() {
6019
+ private async cleanUpOnAddMediaFailure(): Promise<void> {
5911
6020
  if (this.statsAnalyzer) {
5912
6021
  await this.statsAnalyzer.stopAnalyzer();
5913
6022
  }
@@ -5924,6 +6033,36 @@ export default class Meeting extends StatelessWebexPlugin {
5924
6033
  }
5925
6034
  }
5926
6035
 
6036
+ /**
6037
+ * Sends stats report, closes peer connection and cleans up any media connection
6038
+ * related things before trying to establish media connection again with turn server
6039
+ *
6040
+ * @private
6041
+ * @returns {Promise<void>}
6042
+ */
6043
+ private async cleanUpBeforeRetryWithTurnServer(): Promise<void> {
6044
+ // when media fails, we want to upload a webrtc dump to see whats going on
6045
+ // this function is async, but returns once the stats have been gathered
6046
+ await this.forceSendStatsReport({callFrom: 'cleanUpBeforeRetryWithTurnServer'});
6047
+
6048
+ if (this.mediaProperties.webrtcMediaConnection) {
6049
+ if (this.remoteMediaManager) {
6050
+ this.remoteMediaManager.stop();
6051
+ this.remoteMediaManager = null;
6052
+ }
6053
+
6054
+ Object.values(this.mediaRequestManagers).forEach((mediaRequestManager) =>
6055
+ mediaRequestManager.reset()
6056
+ );
6057
+
6058
+ this.receiveSlotManager.reset();
6059
+ this.mediaProperties.webrtcMediaConnection.close();
6060
+ this.sendSlotManager.reset();
6061
+
6062
+ this.mediaProperties.unsetPeerConnection();
6063
+ }
6064
+ }
6065
+
5927
6066
  /**
5928
6067
  * Creates a media connection to the server. Media connection is required for sending or receiving any audio/video.
5929
6068
  *
@@ -5932,7 +6071,8 @@ export default class Meeting extends StatelessWebexPlugin {
5932
6071
  * @public
5933
6072
  * @memberof Meeting
5934
6073
  */
5935
- async addMedia(options: AddMediaOptions = {}) {
6074
+ async addMedia(options: AddMediaOptions = {}): Promise<void> {
6075
+ this.retriedWithTurnServer = false;
5936
6076
  const LOG_HEADER = 'Meeting:index#addMedia -->';
5937
6077
  LoggerProxy.logger.info(`${LOG_HEADER} called with: ${JSON.stringify(options)}`);
5938
6078
 
@@ -6028,7 +6168,7 @@ export default class Meeting extends StatelessWebexPlugin {
6028
6168
 
6029
6169
  this.createStatsAnalyzer();
6030
6170
 
6031
- await this.establishMediaConnection(remoteMediaManagerConfig, bundlePolicy);
6171
+ await this.establishMediaConnection(remoteMediaManagerConfig, bundlePolicy, false);
6032
6172
 
6033
6173
  await Meeting.handleDeviceLogging();
6034
6174
 
@@ -6074,6 +6214,7 @@ export default class Meeting extends StatelessWebexPlugin {
6074
6214
  code: error.code,
6075
6215
  turnDiscoverySkippedReason: this.turnDiscoverySkippedReason,
6076
6216
  turnServerUsed: this.turnServerUsed,
6217
+ retriedWithTurnServer: this.retriedWithTurnServer,
6077
6218
  isMultistream: this.isMultistream,
6078
6219
  signalingState:
6079
6220
  this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
@@ -681,21 +681,6 @@ export default class Meetings extends WebexPlugin {
681
681
  }
682
682
  }
683
683
 
684
- /**
685
- * API to enable or disable TURN discovery
686
- * @param {Boolean} enable
687
- * @private
688
- * @memberof Meetings
689
- * @returns {undefined}
690
- */
691
- private _toggleTurnDiscovery(enable: boolean) {
692
- if (typeof enable !== 'boolean') {
693
- return;
694
- }
695
- // @ts-ignore
696
- this.config.experimental.enableTurnDiscovery = enable;
697
- }
698
-
699
684
  /**
700
685
  * API to toggle starting adhoc meeting
701
686
  * @param {Boolean} changeState
@@ -563,8 +563,8 @@ export default class ReconnectionManager {
563
563
  'ReconnectionManager:index#reconnectMedia --> Begin reestablishment of media'
564
564
  );
565
565
 
566
- // do the TURN server discovery again since the TURN server might change
567
- const turnServerResult = await this.meeting.roap.doTurnDiscovery(this.meeting, true);
566
+ // do the TURN server discovery again and ignore reachability results since the TURN server might change
567
+ const turnServerResult = await this.meeting.roap.doTurnDiscovery(this.meeting, true, true);
568
568
 
569
569
  const iceServers = [];
570
570
 
package/src/roap/index.ts CHANGED
@@ -222,9 +222,10 @@ export default class Roap extends StatelessWebexPlugin {
222
222
  * @param {Meeting} meeting
223
223
  * @param {Boolean} isReconnecting should be set to true if this is a new
224
224
  * media connection just after a reconnection
225
+ * @param {Boolean} [isForced]
225
226
  * @returns {Promise}
226
227
  */
227
- doTurnDiscovery(meeting: Meeting, isReconnecting: boolean) {
228
- return this.turnDiscovery.doTurnDiscovery(meeting, isReconnecting);
228
+ doTurnDiscovery(meeting: Meeting, isReconnecting: boolean, isForced?: boolean) {
229
+ return this.turnDiscovery.doTurnDiscovery(meeting, isReconnecting, isForced);
229
230
  }
230
231
  }
@@ -237,15 +237,6 @@ export default class TurnDiscovery {
237
237
  return 'reachability';
238
238
  }
239
239
 
240
- // @ts-ignore - fix type
241
- if (!meeting.config.experimental.enableTurnDiscovery) {
242
- LoggerProxy.logger.info(
243
- 'Roap:turnDiscovery#getSkipReason --> TURN discovery disabled in config, skipping it'
244
- );
245
-
246
- return 'config';
247
- }
248
-
249
240
  return '';
250
241
  }
251
242
 
@@ -274,12 +265,17 @@ export default class TurnDiscovery {
274
265
  * so it works fine no matter if TURN discovery is done or not.
275
266
  *
276
267
  * @param {Meeting} meeting
277
- * @param {Boolean} isReconnecting should be set to true if this is a new
268
+ * @param {Boolean} [isReconnecting] should be set to true if this is a new
278
269
  * media connection just after a reconnection
270
+ * @param {Boolean} [isForced]
279
271
  * @returns {Promise}
280
272
  */
281
- async doTurnDiscovery(meeting: Meeting, isReconnecting?: boolean) {
282
- const turnDiscoverySkippedReason = await this.getSkipReason(meeting);
273
+ async doTurnDiscovery(meeting: Meeting, isReconnecting?: boolean, isForced?: boolean) {
274
+ let turnDiscoverySkippedReason: string;
275
+
276
+ if (!isForced) {
277
+ turnDiscoverySkippedReason = await this.getSkipReason(meeting);
278
+ }
283
279
 
284
280
  if (turnDiscoverySkippedReason) {
285
281
  return {