@webex/plugin-meetings 2.60.0-next.1 → 2.60.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +2 -1
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/in-meeting-actions.js +2 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +155 -103
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +3 -0
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +14 -29
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +17 -0
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +13 -0
- package/dist/meetings/index.js.map +1 -1
- package/dist/metrics/constants.js +1 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reconnection-manager/index.js +26 -26
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/rtcMetrics/index.js +25 -0
- package/dist/rtcMetrics/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +21 -1
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +16 -16
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +21 -22
- package/src/constants.ts +10 -4
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +140 -92
- package/src/meeting-info/meeting-info-v2.ts +4 -0
- package/src/meeting-info/utilv2.ts +6 -19
- package/src/meetings/collection.ts +13 -0
- package/src/meetings/index.ts +11 -0
- package/src/metrics/constants.ts +1 -0
- package/src/reconnection-manager/index.ts +62 -66
- package/src/rtcMetrics/index.ts +24 -0
- package/src/statsAnalyzer/index.ts +30 -1
- package/src/statsAnalyzer/mqaUtil.ts +17 -14
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +1058 -158
- package/test/unit/spec/meeting/muteState.js +2 -1
- package/test/unit/spec/meeting-info/meetinginfov2.js +28 -0
- package/test/unit/spec/meetings/collection.js +12 -0
- package/test/unit/spec/meetings/index.js +306 -118
- package/test/unit/spec/member/util.js +0 -31
- package/test/unit/spec/reconnection-manager/index.js +25 -10
- package/test/unit/spec/rtcMetrics/index.ts +20 -0
- package/test/unit/spec/stats-analyzer/index.js +12 -2
package/src/meeting/index.ts
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
ClientEvent,
|
|
10
10
|
ClientEventLeaveReason,
|
|
11
11
|
CallDiagnosticUtils,
|
|
12
|
-
CALL_DIAGNOSTIC_CONFIG,
|
|
13
12
|
} from '@webex/internal-plugin-metrics';
|
|
14
13
|
import {
|
|
15
14
|
ConnectionState,
|
|
@@ -105,6 +104,7 @@ import {
|
|
|
105
104
|
MEETING_PERMISSION_TOKEN_REFRESH_THRESHOLD_IN_SEC,
|
|
106
105
|
MEETING_PERMISSION_TOKEN_REFRESH_REASON,
|
|
107
106
|
ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
|
|
107
|
+
RECONNECTION,
|
|
108
108
|
} from '../constants';
|
|
109
109
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
110
110
|
import ParameterError from '../common/errors/parameter';
|
|
@@ -528,7 +528,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
528
528
|
meetingInfoFailureCode?: number;
|
|
529
529
|
meetingInfoExtraParams?: Record<string, any>;
|
|
530
530
|
networkQualityMonitor: NetworkQualityMonitor;
|
|
531
|
-
networkStatus
|
|
531
|
+
networkStatus?: NETWORK_STATUS;
|
|
532
532
|
passwordStatus: string;
|
|
533
533
|
queuedMediaUpdates: any[];
|
|
534
534
|
recording: any;
|
|
@@ -577,6 +577,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
577
577
|
private sendSlotManager: SendSlotManager = new SendSlotManager(LoggerProxy);
|
|
578
578
|
private deferSDPAnswer?: Defer; // used for waiting for a response
|
|
579
579
|
private sdpResponseTimer?: ReturnType<typeof setTimeout>;
|
|
580
|
+
private hasMediaConnectionConnectedAtLeastOnce: boolean;
|
|
580
581
|
|
|
581
582
|
/**
|
|
582
583
|
* @param {Object} attrs
|
|
@@ -1096,13 +1097,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1096
1097
|
*/
|
|
1097
1098
|
this.networkQualityMonitor = null;
|
|
1098
1099
|
/**
|
|
1100
|
+
* Indicates network status of the webrtc media connection
|
|
1099
1101
|
* @instance
|
|
1100
1102
|
* @type {String}
|
|
1101
1103
|
* @readonly
|
|
1102
1104
|
* @public
|
|
1103
1105
|
* @memberof Meeting
|
|
1104
1106
|
*/
|
|
1105
|
-
this.networkStatus =
|
|
1107
|
+
this.networkStatus = undefined;
|
|
1106
1108
|
/**
|
|
1107
1109
|
* Passing only info as we send basic info for meeting added event
|
|
1108
1110
|
* @instance
|
|
@@ -1318,6 +1320,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1318
1320
|
* @memberof Meeting
|
|
1319
1321
|
*/
|
|
1320
1322
|
this.retriedWithTurnServer = false;
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Whether or not the media connection has ever successfully connected.
|
|
1326
|
+
* @instance
|
|
1327
|
+
* @type {boolean}
|
|
1328
|
+
* @private
|
|
1329
|
+
* @memberof Meeting
|
|
1330
|
+
*/
|
|
1331
|
+
this.hasMediaConnectionConnectedAtLeastOnce = false;
|
|
1321
1332
|
}
|
|
1322
1333
|
|
|
1323
1334
|
/**
|
|
@@ -1928,12 +1939,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1928
1939
|
|
|
1929
1940
|
/**
|
|
1930
1941
|
* sets the network status on meeting object
|
|
1931
|
-
* @param {
|
|
1942
|
+
* @param {NETWORK_STATUS} networkStatus
|
|
1932
1943
|
* @private
|
|
1933
1944
|
* @returns {undefined}
|
|
1934
1945
|
* @memberof Meeting
|
|
1935
1946
|
*/
|
|
1936
|
-
private setNetworkStatus(networkStatus
|
|
1947
|
+
private setNetworkStatus(networkStatus?: NETWORK_STATUS) {
|
|
1937
1948
|
if (networkStatus === NETWORK_STATUS.DISCONNECTED) {
|
|
1938
1949
|
Trigger.trigger(
|
|
1939
1950
|
this,
|
|
@@ -3428,6 +3439,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3428
3439
|
requiredPolicies: [SELF_POLICY.SUPPORT_FILE_TRANSFER],
|
|
3429
3440
|
policies: this.selfUserPolicies,
|
|
3430
3441
|
}),
|
|
3442
|
+
canChat: ControlsOptionsUtil.hasPolicies({
|
|
3443
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_CHAT],
|
|
3444
|
+
policies: this.selfUserPolicies,
|
|
3445
|
+
}),
|
|
3431
3446
|
canShareApplication:
|
|
3432
3447
|
(ControlsOptionsUtil.hasHints({
|
|
3433
3448
|
requiredHints: [DISPLAY_HINTS.SHARE_APPLICATION],
|
|
@@ -3916,6 +3931,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3916
3931
|
this.receiveSlotManager.reset();
|
|
3917
3932
|
this.mediaProperties.webrtcMediaConnection.close();
|
|
3918
3933
|
this.sendSlotManager.reset();
|
|
3934
|
+
this.setNetworkStatus(undefined);
|
|
3919
3935
|
}
|
|
3920
3936
|
|
|
3921
3937
|
this.audio = null;
|
|
@@ -4268,6 +4284,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4268
4284
|
|
|
4269
4285
|
return this.reconnectionManager
|
|
4270
4286
|
.reconnect(options)
|
|
4287
|
+
.then(() => this.waitForRemoteSDPAnswer())
|
|
4288
|
+
.then(() => this.waitForMediaConnectionConnected())
|
|
4271
4289
|
.then(() => {
|
|
4272
4290
|
Trigger.trigger(
|
|
4273
4291
|
this,
|
|
@@ -4278,6 +4296,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4278
4296
|
EVENT_TRIGGERS.MEETING_RECONNECTION_SUCCESS
|
|
4279
4297
|
);
|
|
4280
4298
|
LoggerProxy.logger.log('Meeting:index#reconnect --> Meeting reconnect success');
|
|
4299
|
+
|
|
4300
|
+
// @ts-ignore
|
|
4301
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
4302
|
+
name: 'client.media.recovered',
|
|
4303
|
+
payload: {
|
|
4304
|
+
recoveredBy: 'new',
|
|
4305
|
+
},
|
|
4306
|
+
options: {
|
|
4307
|
+
meetingId: this.id,
|
|
4308
|
+
},
|
|
4309
|
+
});
|
|
4310
|
+
this.reconnectionManager.setStatus(RECONNECTION.STATE.COMPLETE);
|
|
4281
4311
|
})
|
|
4282
4312
|
.catch((error) => {
|
|
4283
4313
|
Trigger.trigger(
|
|
@@ -4587,7 +4617,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4587
4617
|
// @ts-ignore
|
|
4588
4618
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
4589
4619
|
name: 'client.call.initiated',
|
|
4590
|
-
payload: {
|
|
4620
|
+
payload: {
|
|
4621
|
+
trigger: 'user-interaction',
|
|
4622
|
+
isRoapCallEnabled: true,
|
|
4623
|
+
pstnAudioType: options?.pstnAudioType,
|
|
4624
|
+
},
|
|
4591
4625
|
options: {meetingId: this.id},
|
|
4592
4626
|
});
|
|
4593
4627
|
|
|
@@ -4753,7 +4787,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4753
4787
|
.then((join) => {
|
|
4754
4788
|
if (isBrowser) {
|
|
4755
4789
|
// @ts-ignore - config coming from registerPlugin
|
|
4756
|
-
if (this.config.receiveTranscription ||
|
|
4790
|
+
if (this.config.receiveTranscription || options.receiveTranscription) {
|
|
4757
4791
|
if (this.isTranscriptionSupported()) {
|
|
4758
4792
|
LoggerProxy.logger.info(
|
|
4759
4793
|
'Meeting:index#join --> Attempting to enabled to receive transcription!'
|
|
@@ -5398,40 +5432,25 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5398
5432
|
|
|
5399
5433
|
this.mediaProperties.webrtcMediaConnection.on(Event.CONNECTION_STATE_CHANGED, (event) => {
|
|
5400
5434
|
const connectionFailed = () => {
|
|
5401
|
-
// we know the media connection failed and browser will not attempt to recover it any more
|
|
5402
|
-
// so reset the timer as it's not needed anymore, we want to reconnect immediately
|
|
5403
|
-
this.reconnectionManager.resetReconnectionTimer();
|
|
5404
|
-
|
|
5405
|
-
this.reconnect({networkDisconnect: true});
|
|
5406
|
-
// @ts-ignore
|
|
5407
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
5408
|
-
name: 'client.ice.end',
|
|
5409
|
-
payload: {
|
|
5410
|
-
canProceed: false,
|
|
5411
|
-
icePhase: 'IN_MEETING',
|
|
5412
|
-
errors: [
|
|
5413
|
-
// @ts-ignore
|
|
5414
|
-
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
5415
|
-
{
|
|
5416
|
-
clientErrorCode: CALL_DIAGNOSTIC_CONFIG.ICE_FAILURE_CLIENT_CODE,
|
|
5417
|
-
}
|
|
5418
|
-
),
|
|
5419
|
-
],
|
|
5420
|
-
},
|
|
5421
|
-
options: {
|
|
5422
|
-
meetingId: this.id,
|
|
5423
|
-
},
|
|
5424
|
-
});
|
|
5425
|
-
|
|
5426
|
-
this.uploadLogs({
|
|
5427
|
-
file: 'peer-connection-manager/index',
|
|
5428
|
-
function: 'connectionFailed',
|
|
5429
|
-
});
|
|
5430
|
-
|
|
5431
5435
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.CONNECTION_FAILURE, {
|
|
5432
5436
|
correlation_id: this.correlationId,
|
|
5433
5437
|
locus_id: this.locusId,
|
|
5438
|
+
networkStatus: this.networkStatus,
|
|
5439
|
+
hasMediaConnectionConnectedAtLeastOnce: this.hasMediaConnectionConnectedAtLeastOnce,
|
|
5434
5440
|
});
|
|
5441
|
+
|
|
5442
|
+
if (this.hasMediaConnectionConnectedAtLeastOnce) {
|
|
5443
|
+
// we know the media connection failed and browser will not attempt to recover it any more
|
|
5444
|
+
// so reset the timer as it's not needed anymore, we want to reconnect immediately
|
|
5445
|
+
this.reconnectionManager.resetReconnectionTimer();
|
|
5446
|
+
|
|
5447
|
+
this.reconnect({networkDisconnect: true});
|
|
5448
|
+
|
|
5449
|
+
this.uploadLogs({
|
|
5450
|
+
file: 'peer-connection-manager/index',
|
|
5451
|
+
function: 'connectionFailed',
|
|
5452
|
+
});
|
|
5453
|
+
}
|
|
5435
5454
|
};
|
|
5436
5455
|
|
|
5437
5456
|
LoggerProxy.logger.info(
|
|
@@ -5443,22 +5462,32 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5443
5462
|
|
|
5444
5463
|
switch (event.state) {
|
|
5445
5464
|
case ConnectionState.Connecting:
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5465
|
+
if (!this.hasMediaConnectionConnectedAtLeastOnce) {
|
|
5466
|
+
// Only send CA event for join flow if we haven't successfully connected media yet
|
|
5467
|
+
// @ts-ignore
|
|
5468
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5469
|
+
name: 'client.ice.start',
|
|
5470
|
+
options: {
|
|
5471
|
+
meetingId: this.id,
|
|
5472
|
+
},
|
|
5473
|
+
});
|
|
5474
|
+
}
|
|
5453
5475
|
break;
|
|
5454
5476
|
case ConnectionState.Connected:
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5477
|
+
if (!this.hasMediaConnectionConnectedAtLeastOnce) {
|
|
5478
|
+
// Only send CA event for join flow if we haven't successfully connected media yet
|
|
5479
|
+
// @ts-ignore
|
|
5480
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5481
|
+
name: 'client.ice.end',
|
|
5482
|
+
payload: {
|
|
5483
|
+
canProceed: true,
|
|
5484
|
+
icePhase: 'JOIN_MEETING_FINAL',
|
|
5485
|
+
},
|
|
5486
|
+
options: {
|
|
5487
|
+
meetingId: this.id,
|
|
5488
|
+
},
|
|
5489
|
+
});
|
|
5490
|
+
}
|
|
5462
5491
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.CONNECTION_SUCCESS, {
|
|
5463
5492
|
correlation_id: this.correlationId,
|
|
5464
5493
|
locus_id: this.locusId,
|
|
@@ -5467,6 +5496,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5467
5496
|
this.setNetworkStatus(NETWORK_STATUS.CONNECTED);
|
|
5468
5497
|
this.reconnectionManager.iceReconnected();
|
|
5469
5498
|
this.statsAnalyzer.startAnalyzer(this.mediaProperties.webrtcMediaConnection);
|
|
5499
|
+
this.hasMediaConnectionConnectedAtLeastOnce = true;
|
|
5470
5500
|
break;
|
|
5471
5501
|
case ConnectionState.Disconnected:
|
|
5472
5502
|
this.setNetworkStatus(NETWORK_STATUS.DISCONNECTED);
|
|
@@ -5771,36 +5801,42 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5771
5801
|
try {
|
|
5772
5802
|
await this.mediaProperties.waitForMediaConnectionConnected();
|
|
5773
5803
|
} catch (error) {
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
+
if (!this.hasMediaConnectionConnectedAtLeastOnce) {
|
|
5805
|
+
// Only send CA event for join flow if we haven't successfully connected media yet
|
|
5806
|
+
// @ts-ignore
|
|
5807
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5808
|
+
name: 'client.ice.end',
|
|
5809
|
+
payload: {
|
|
5810
|
+
canProceed: !this.turnServerUsed, // If we haven't done turn tls retry yet we will proceed with join attempt
|
|
5811
|
+
icePhase: this.turnServerUsed ? 'JOIN_MEETING_FINAL' : 'JOIN_MEETING_RETRY',
|
|
5812
|
+
errors: [
|
|
5813
|
+
// @ts-ignore
|
|
5814
|
+
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
5815
|
+
{
|
|
5816
|
+
clientErrorCode: CallDiagnosticUtils.generateClientErrorCodeForIceFailure({
|
|
5817
|
+
signalingState:
|
|
5818
|
+
this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
|
|
5819
|
+
?.signalingState ||
|
|
5820
|
+
this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc
|
|
5821
|
+
?.signalingState ||
|
|
5822
|
+
'unknown',
|
|
5823
|
+
iceConnectionState:
|
|
5824
|
+
this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
|
|
5825
|
+
?.iceConnectionState ||
|
|
5826
|
+
this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc
|
|
5827
|
+
?.iceConnectionState ||
|
|
5828
|
+
'unknown',
|
|
5829
|
+
turnServerUsed: this.turnServerUsed,
|
|
5830
|
+
}),
|
|
5831
|
+
}
|
|
5832
|
+
),
|
|
5833
|
+
],
|
|
5834
|
+
},
|
|
5835
|
+
options: {
|
|
5836
|
+
meetingId: this.id,
|
|
5837
|
+
},
|
|
5838
|
+
});
|
|
5839
|
+
}
|
|
5804
5840
|
throw new Error(
|
|
5805
5841
|
`Timed out waiting for media connection to be connected, correlationId=${this.correlationId}`
|
|
5806
5842
|
);
|
|
@@ -5924,9 +5960,24 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5924
5960
|
bundlePolicy?: BundlePolicy
|
|
5925
5961
|
): Promise<void> {
|
|
5926
5962
|
this.retriedWithTurnServer = true;
|
|
5963
|
+
const LOG_HEADER = 'Meeting:index#addMedia():retryWithForcedTurnDiscovery -->';
|
|
5927
5964
|
|
|
5928
5965
|
await this.cleanUpBeforeRetryWithTurnServer();
|
|
5929
5966
|
|
|
5967
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_RETRY, {
|
|
5968
|
+
correlation_id: this.correlationId,
|
|
5969
|
+
state: this.state,
|
|
5970
|
+
meetingState: this.meetingState,
|
|
5971
|
+
reason: 'forcingTurnTls',
|
|
5972
|
+
});
|
|
5973
|
+
|
|
5974
|
+
if (this.state === MEETING_STATE.STATES.LEFT) {
|
|
5975
|
+
LoggerProxy.logger.info(
|
|
5976
|
+
`${LOG_HEADER} meeting state was LEFT after first attempt to establish media connection. Attempting to rejoin. `
|
|
5977
|
+
);
|
|
5978
|
+
await this.join({rejoin: true});
|
|
5979
|
+
}
|
|
5980
|
+
|
|
5930
5981
|
await this.retryEstablishMediaConnectionWithForcedTurnDiscovery(
|
|
5931
5982
|
remoteMediaManagerConfig,
|
|
5932
5983
|
bundlePolicy
|
|
@@ -6130,6 +6181,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6130
6181
|
*/
|
|
6131
6182
|
async addMedia(options: AddMediaOptions = {}): Promise<void> {
|
|
6132
6183
|
this.retriedWithTurnServer = false;
|
|
6184
|
+
this.hasMediaConnectionConnectedAtLeastOnce = false;
|
|
6133
6185
|
const LOG_HEADER = 'Meeting:index#addMedia -->';
|
|
6134
6186
|
LoggerProxy.logger.info(`${LOG_HEADER} called with: ${JSON.stringify(options)}`);
|
|
6135
6187
|
|
|
@@ -6807,17 +6859,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6807
6859
|
.catch((error) => {
|
|
6808
6860
|
LoggerProxy.logger.error('Meeting:index#stopWhiteboardShare --> Error ', error);
|
|
6809
6861
|
|
|
6810
|
-
Metrics.sendBehavioralMetric(
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
stack: error.stack,
|
|
6818
|
-
board: {channelUrl},
|
|
6819
|
-
}
|
|
6820
|
-
);
|
|
6862
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_STOP_WHITEBOARD_SHARE_FAILURE, {
|
|
6863
|
+
correlation_id: this.correlationId,
|
|
6864
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
6865
|
+
reason: error.message,
|
|
6866
|
+
stack: error.stack,
|
|
6867
|
+
board: {channelUrl},
|
|
6868
|
+
});
|
|
6821
6869
|
|
|
6822
6870
|
return Promise.reject(error);
|
|
6823
6871
|
})
|
|
@@ -216,6 +216,10 @@ export default class MeetingInfoV2 {
|
|
|
216
216
|
installedOrgID,
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
+
if (installedOrgID) {
|
|
220
|
+
body.installedOrgID = installedOrgID;
|
|
221
|
+
}
|
|
222
|
+
|
|
219
223
|
const uri = this.webex.meetings.preferredWebexSite
|
|
220
224
|
? `https://${this.webex.meetings.preferredWebexSite}/wbxappapi/v2/meetings/spaceInstant`
|
|
221
225
|
: '';
|
|
@@ -203,25 +203,12 @@ MeetingInfoUtil.getDestinationType = async (from) => {
|
|
|
203
203
|
return Promise.resolve(options);
|
|
204
204
|
}
|
|
205
205
|
);
|
|
206
|
-
} else if (hydraId
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
{
|
|
213
|
-
cluster: hydraId.cluster,
|
|
214
|
-
},
|
|
215
|
-
webex
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
options.destination = hydraId.destination
|
|
219
|
-
? `${serviceUrl}/conversations/${hydraId.destination}`
|
|
220
|
-
: serviceUrl;
|
|
221
|
-
} catch (e) {
|
|
222
|
-
LoggerProxy.logger.error(`Meeting-info:util#getDestinationType --> ${e}`);
|
|
223
|
-
throw e;
|
|
224
|
-
}
|
|
206
|
+
} else if (hydraId.room) {
|
|
207
|
+
LoggerProxy.logger.error(
|
|
208
|
+
`Meeting-info:util#getDestinationType --> Using the space ID as a destination is no longer supported. Please refer to the [migration guide](https://github.com/webex/webex-js-sdk/wiki/Migration-to-Unified-Space-Meetings) to migrate to use the meeting ID or SIP address.`
|
|
209
|
+
);
|
|
210
|
+
// Error code 30105 added as Space ID deprecated as of beta, Please refer migration guide.
|
|
211
|
+
throw new SpaceIDDeprecatedError();
|
|
225
212
|
} else {
|
|
226
213
|
LoggerProxy.logger.warn(`Meeting-info:util#getDestinationType --> ${meetingInfoError}`);
|
|
227
214
|
throw new ParameterError(`${meetingInfoError}`);
|
|
@@ -60,4 +60,17 @@ export default class MeetingCollection extends Collection {
|
|
|
60
60
|
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Gets the meeting that has a webrtc media connection
|
|
66
|
+
* NOTE: this function assumes there is no more than 1 such meeting
|
|
67
|
+
*
|
|
68
|
+
* @returns {Meeting} first meeting found, else undefined
|
|
69
|
+
* @public
|
|
70
|
+
* @memberof MeetingCollection
|
|
71
|
+
*/
|
|
72
|
+
public getActiveWebrtcMeeting() {
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
return find(this.meetings, (meeting) => meeting.mediaProperties.webrtcMediaConnection);
|
|
75
|
+
}
|
|
63
76
|
}
|
package/src/meetings/index.ts
CHANGED
|
@@ -1467,4 +1467,15 @@ export default class Meetings extends WebexPlugin {
|
|
|
1467
1467
|
getLogger() {
|
|
1468
1468
|
return LoggerProxy.get();
|
|
1469
1469
|
}
|
|
1470
|
+
|
|
1471
|
+
/**
|
|
1472
|
+
* Returns the first meeting it finds that has the webrtc media connection created.
|
|
1473
|
+
* Useful for debugging in the console.
|
|
1474
|
+
*
|
|
1475
|
+
* @private
|
|
1476
|
+
* @returns {Meeting} Meeting object that has a webrtc media connection, else undefined
|
|
1477
|
+
*/
|
|
1478
|
+
getActiveWebrtcMeeting() {
|
|
1479
|
+
return this.meetingCollection.getActiveWebrtcMeeting();
|
|
1480
|
+
}
|
|
1470
1481
|
}
|
package/src/metrics/constants.ts
CHANGED
|
@@ -9,6 +9,7 @@ const BEHAVIORAL_METRICS = {
|
|
|
9
9
|
JOIN_FAILURE: 'js_sdk_join_failures',
|
|
10
10
|
ADD_MEDIA_SUCCESS: 'js_sdk_add_media_success',
|
|
11
11
|
ADD_MEDIA_FAILURE: 'js_sdk_add_media_failures',
|
|
12
|
+
ADD_MEDIA_RETRY: 'js_sdk_add_media_retry',
|
|
12
13
|
ROAP_MERCURY_EVENT_RECEIVED: 'js_sdk_roap_mercury_received',
|
|
13
14
|
CONNECTION_SUCCESS: 'js_sdk_connection_success',
|
|
14
15
|
CONNECTION_FAILURE: 'js_sdk_connection_failures',
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
_CALL_,
|
|
15
15
|
_LEFT_,
|
|
16
16
|
_ID_,
|
|
17
|
+
RECONNECTION_STATE,
|
|
17
18
|
} from '../constants';
|
|
18
19
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
19
20
|
import ReconnectionError from '../common/errors/reconnection';
|
|
@@ -96,7 +97,7 @@ export default class ReconnectionManager {
|
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
99
|
* @instance
|
|
99
|
-
* @type {
|
|
100
|
+
* @type {RECONNECTION_STATE}
|
|
100
101
|
* @private
|
|
101
102
|
* @memberof ReconnectionManager
|
|
102
103
|
*/
|
|
@@ -265,6 +266,18 @@ export default class ReconnectionManager {
|
|
|
265
266
|
return this.status === RECONNECTION.STATE.IN_PROGRESS;
|
|
266
267
|
}
|
|
267
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Sets the reconnection status
|
|
271
|
+
*
|
|
272
|
+
* @public
|
|
273
|
+
* @param {RECONNECTION_STATE} status
|
|
274
|
+
* @memberof ReconnectionManager
|
|
275
|
+
* @returns {undefined}
|
|
276
|
+
*/
|
|
277
|
+
public setStatus(status: RECONNECTION_STATE) {
|
|
278
|
+
this.status = status;
|
|
279
|
+
}
|
|
280
|
+
|
|
268
281
|
/**
|
|
269
282
|
* @returns {Boolean}
|
|
270
283
|
* @throws {ReconnectionError}
|
|
@@ -337,73 +350,55 @@ export default class ReconnectionManager {
|
|
|
337
350
|
});
|
|
338
351
|
}
|
|
339
352
|
|
|
340
|
-
return this.executeReconnection({networkDisconnect})
|
|
341
|
-
|
|
342
|
-
LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Reconnection successful.');
|
|
353
|
+
return this.executeReconnection({networkDisconnect}).catch((reconnectError) => {
|
|
354
|
+
if (reconnectError instanceof NeedsRetryError) {
|
|
343
355
|
LoggerProxy.logger.info(
|
|
344
|
-
'ReconnectionManager:index#reconnect -->
|
|
356
|
+
'ReconnectionManager:index#reconnect --> Reconnection not successful, retrying.'
|
|
345
357
|
);
|
|
358
|
+
// Reset our reconnect status since we are looping back to the beginning
|
|
359
|
+
this.status = RECONNECTION.STATE.DEFAULT_STATUS;
|
|
346
360
|
|
|
347
|
-
//
|
|
348
|
-
this.
|
|
349
|
-
|
|
350
|
-
payload: {
|
|
351
|
-
recoveredBy: 'new',
|
|
352
|
-
},
|
|
353
|
-
options: {
|
|
354
|
-
meetingId: this.meeting.id,
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
})
|
|
358
|
-
.catch((reconnectError) => {
|
|
359
|
-
if (reconnectError instanceof NeedsRetryError) {
|
|
360
|
-
LoggerProxy.logger.info(
|
|
361
|
-
'ReconnectionManager:index#reconnect --> Reconnection not successful, retrying.'
|
|
362
|
-
);
|
|
363
|
-
// Reset our reconnect status since we are looping back to the beginning
|
|
364
|
-
this.status = RECONNECTION.STATE.DEFAULT_STATUS;
|
|
365
|
-
|
|
366
|
-
// This is a network retry, so we should not log START metrics again
|
|
367
|
-
return this.reconnect({networkDisconnect: true, networkRetry: true});
|
|
368
|
-
}
|
|
361
|
+
// This is a network retry, so we should not log START metrics again
|
|
362
|
+
return this.reconnect({networkDisconnect: true, networkRetry: true});
|
|
363
|
+
}
|
|
369
364
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
365
|
+
// Reconnect has failed
|
|
366
|
+
LoggerProxy.logger.error(
|
|
367
|
+
'ReconnectionManager:index#reconnect --> Reconnection failed.',
|
|
368
|
+
reconnectError.message
|
|
369
|
+
);
|
|
370
|
+
LoggerProxy.logger.info(
|
|
371
|
+
'ReconnectionManager:index#reconnect --> Sending reconnect abort metric.'
|
|
372
|
+
);
|
|
378
373
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
374
|
+
// @ts-ignore
|
|
375
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
376
|
+
name: 'client.call.aborted',
|
|
377
|
+
payload: {
|
|
378
|
+
errors: [
|
|
379
|
+
{
|
|
380
|
+
category: 'expected',
|
|
381
|
+
errorCode: 2008,
|
|
382
|
+
fatal: true,
|
|
383
|
+
name: 'media-engine',
|
|
384
|
+
shownToUser: false,
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
options: {
|
|
389
|
+
meetingId: this.meeting.id,
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
if (reconnectError instanceof NeedsRejoinError) {
|
|
393
|
+
// send call aborded event with catogery as expected as we are trying to rejoin
|
|
399
394
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
395
|
+
if (this.autoRejoinEnabled) {
|
|
396
|
+
return this.rejoinMeeting(reconnectError.wasSharing);
|
|
403
397
|
}
|
|
398
|
+
}
|
|
404
399
|
|
|
405
|
-
|
|
406
|
-
|
|
400
|
+
throw reconnectError;
|
|
401
|
+
});
|
|
407
402
|
}
|
|
408
403
|
|
|
409
404
|
/**
|
|
@@ -485,14 +480,13 @@ export default class ReconnectionManager {
|
|
|
485
480
|
const media = await this.reconnectMedia();
|
|
486
481
|
|
|
487
482
|
LoggerProxy.logger.log(
|
|
488
|
-
'ReconnectionManager:index#executeReconnection -->
|
|
483
|
+
'ReconnectionManager:index#executeReconnection --> webRTC media connection renewed and local sdp offer sent'
|
|
489
484
|
);
|
|
490
|
-
this.status = RECONNECTION.STATE.COMPLETE;
|
|
491
485
|
|
|
492
486
|
return media;
|
|
493
487
|
} catch (error) {
|
|
494
488
|
LoggerProxy.logger.error(
|
|
495
|
-
'ReconnectionManager:index#executeReconnection -->
|
|
489
|
+
'ReconnectionManager:index#executeReconnection --> failed to renew webRTC media connection or initiate offer'
|
|
496
490
|
);
|
|
497
491
|
this.status = RECONNECTION.STATE.FAILURE;
|
|
498
492
|
|
|
@@ -559,9 +553,7 @@ export default class ReconnectionManager {
|
|
|
559
553
|
* @memberof ReconnectionManager
|
|
560
554
|
*/
|
|
561
555
|
async reconnectMedia() {
|
|
562
|
-
LoggerProxy.logger.log(
|
|
563
|
-
'ReconnectionManager:index#reconnectMedia --> Begin reestablishment of media'
|
|
564
|
-
);
|
|
556
|
+
LoggerProxy.logger.log('ReconnectionManager:index#reconnectMedia --> do turn discovery');
|
|
565
557
|
|
|
566
558
|
// do the TURN server discovery again and ignore reachability results since the TURN server might change
|
|
567
559
|
const turnServerResult = await this.meeting.roap.doTurnDiscovery(this.meeting, true, true);
|
|
@@ -576,6 +568,10 @@ export default class ReconnectionManager {
|
|
|
576
568
|
});
|
|
577
569
|
}
|
|
578
570
|
|
|
571
|
+
LoggerProxy.logger.log(
|
|
572
|
+
'ReconnectionManager:index#reconnectMedia --> renew webRTC media connection and send local sdp offer'
|
|
573
|
+
);
|
|
574
|
+
|
|
579
575
|
await this.meeting.mediaProperties.webrtcMediaConnection.reconnect(iceServers);
|
|
580
576
|
|
|
581
577
|
// resend media requests
|