@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.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +7 -2
- package/dist/breakouts/index.js.map +1 -1
- package/dist/constants.js +11 -4
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/selfUtils.js +0 -5
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +70 -15
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/index.js +12 -0
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/connectionStateHandler.js +67 -0
- package/dist/meeting/connectionStateHandler.js.map +1 -0
- package/dist/meeting/index.js +552 -357
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +7 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +6 -1
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/index.js +4 -4
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -2
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/util.js +17 -17
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +16 -16
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +1 -1
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +37 -33
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +8 -0
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/util.js +3 -2
- package/dist/meetings/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +57 -0
- package/dist/metrics/index.js.map +1 -1
- package/dist/personal-meeting-room/index.js +1 -1
- package/dist/personal-meeting-room/index.js.map +1 -1
- package/dist/reachability/clusterReachability.js +108 -53
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +415 -56
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +11 -3
- package/dist/types/media/MediaConnectionAwaiter.d.ts +24 -4
- package/dist/types/meeting/connectionStateHandler.d.ts +30 -0
- package/dist/types/meeting/index.d.ts +27 -7
- package/dist/types/meeting/locusMediaRequest.d.ts +2 -0
- package/dist/types/meeting-info/index.d.ts +3 -2
- package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -2
- package/dist/types/meeting-info/util.d.ts +5 -4
- package/dist/types/meeting-info/utilv2.d.ts +3 -2
- package/dist/types/meetings/collection.d.ts +3 -2
- package/dist/types/meetings/index.d.ts +4 -3
- package/dist/types/meetings/meetings.types.d.ts +9 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/metrics/index.d.ts +15 -0
- package/dist/types/reachability/clusterReachability.d.ts +31 -3
- package/dist/types/reachability/index.d.ts +93 -2
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/breakouts/index.ts +7 -1
- package/src/constants.ts +13 -17
- package/src/locus-info/selfUtils.ts +0 -5
- package/src/media/MediaConnectionAwaiter.ts +89 -14
- package/src/media/index.ts +13 -0
- package/src/meeting/connectionStateHandler.ts +65 -0
- package/src/meeting/index.ts +526 -292
- package/src/meeting/locusMediaRequest.ts +5 -0
- package/src/meeting/muteState.ts +6 -1
- package/src/meeting/util.ts +1 -0
- package/src/meeting-info/index.ts +9 -6
- package/src/meeting-info/meeting-info-v2.ts +4 -4
- package/src/meeting-info/util.ts +23 -28
- package/src/meeting-info/utilv2.ts +18 -24
- package/src/meetings/collection.ts +3 -3
- package/src/meetings/index.ts +39 -40
- package/src/meetings/meetings.types.ts +11 -0
- package/src/meetings/util.ts +5 -4
- package/src/metrics/constants.ts +1 -0
- package/src/metrics/index.ts +44 -0
- package/src/personal-meeting-room/index.ts +2 -2
- package/src/reachability/clusterReachability.ts +86 -25
- package/src/reachability/index.ts +316 -27
- package/test/unit/spec/breakouts/index.ts +51 -32
- package/test/unit/spec/locus-info/selfUtils.js +25 -23
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +131 -32
- package/test/unit/spec/media/index.ts +42 -27
- package/test/unit/spec/meeting/connectionStateHandler.ts +102 -0
- package/test/unit/spec/meeting/index.js +758 -179
- package/test/unit/spec/meeting/locusMediaRequest.ts +7 -0
- package/test/unit/spec/meeting/muteState.js +24 -0
- package/test/unit/spec/meeting-info/index.js +4 -4
- package/test/unit/spec/meeting-info/meetinginfov2.js +24 -28
- package/test/unit/spec/meeting-info/request.js +2 -2
- package/test/unit/spec/meeting-info/utilv2.js +41 -49
- package/test/unit/spec/meetings/index.js +14 -0
- package/test/unit/spec/metrics/index.js +126 -0
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -2
- package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -2
- package/test/unit/spec/reachability/clusterReachability.ts +116 -22
- package/test/unit/spec/reachability/index.ts +1153 -84
- package/test/unit/spec/rtcMetrics/index.ts +1 -0
- package/dist/mediaQualityMetrics/config.js +0 -321
- package/dist/mediaQualityMetrics/config.js.map +0 -1
- package/dist/statsAnalyzer/global.js +0 -44
- package/dist/statsAnalyzer/global.js.map +0 -1
- package/dist/statsAnalyzer/index.js +0 -1072
- package/dist/statsAnalyzer/index.js.map +0 -1
- package/dist/statsAnalyzer/mqaUtil.js +0 -368
- package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +0 -247
- package/dist/types/statsAnalyzer/global.d.ts +0 -36
- package/dist/types/statsAnalyzer/index.d.ts +0 -217
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -48
- package/src/mediaQualityMetrics/config.ts +0 -255
- package/src/statsAnalyzer/global.ts +0 -37
- package/src/statsAnalyzer/index.ts +0 -1318
- package/src/statsAnalyzer/mqaUtil.ts +0 -463
- package/test/unit/spec/stats-analyzer/index.js +0 -1819
package/src/meeting/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ClientEvent,
|
|
10
10
|
ClientEventLeaveReason,
|
|
11
11
|
CallDiagnosticUtils,
|
|
12
|
+
CALL_DIAGNOSTIC_CONFIG,
|
|
12
13
|
} from '@webex/internal-plugin-metrics';
|
|
13
14
|
import {ClientEvent as RawClientEvent} from '@webex/event-dictionary-ts';
|
|
14
15
|
|
|
@@ -16,11 +17,13 @@ import {
|
|
|
16
17
|
ConnectionState,
|
|
17
18
|
Errors,
|
|
18
19
|
ErrorType,
|
|
19
|
-
|
|
20
|
+
MediaConnectionEventNames,
|
|
20
21
|
MediaContent,
|
|
21
22
|
MediaType,
|
|
22
23
|
RemoteTrackType,
|
|
23
24
|
RoapMessage,
|
|
25
|
+
StatsAnalyzer,
|
|
26
|
+
StatsAnalyzerEventNames,
|
|
24
27
|
} from '@webex/internal-media-core';
|
|
25
28
|
|
|
26
29
|
import {
|
|
@@ -40,6 +43,7 @@ import {
|
|
|
40
43
|
TURN_ON_CAPTION_STATUS,
|
|
41
44
|
type MeetingTranscriptPayload,
|
|
42
45
|
} from '@webex/internal-plugin-voicea';
|
|
46
|
+
|
|
43
47
|
import {processNewCaptions} from './voicea-meeting';
|
|
44
48
|
|
|
45
49
|
import {
|
|
@@ -50,7 +54,6 @@ import {
|
|
|
50
54
|
AddMediaFailed,
|
|
51
55
|
} from '../common/errors/webex-errors';
|
|
52
56
|
|
|
53
|
-
import {StatsAnalyzer, EVENTS as StatsAnalyzerEvents} from '../statsAnalyzer';
|
|
54
57
|
import NetworkQualityMonitor from '../networkQualityMonitor';
|
|
55
58
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
56
59
|
import EventsUtil from '../common/events/util';
|
|
@@ -79,10 +82,9 @@ import {Reactions, SkinTones} from '../reactions/reactions';
|
|
|
79
82
|
import PasswordError from '../common/errors/password-error';
|
|
80
83
|
import CaptchaError from '../common/errors/captcha-error';
|
|
81
84
|
import {
|
|
82
|
-
|
|
85
|
+
DESTINATION_TYPE,
|
|
83
86
|
_INCOMING_,
|
|
84
87
|
_JOIN_,
|
|
85
|
-
_MEETING_LINK_,
|
|
86
88
|
AUDIO,
|
|
87
89
|
CONTENT,
|
|
88
90
|
DISPLAY_HINTS,
|
|
@@ -116,9 +118,7 @@ import {
|
|
|
116
118
|
MEETING_PERMISSION_TOKEN_REFRESH_THRESHOLD_IN_SEC,
|
|
117
119
|
MEETING_PERMISSION_TOKEN_REFRESH_REASON,
|
|
118
120
|
ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
|
|
119
|
-
RECONNECTION,
|
|
120
121
|
NAMED_MEDIA_GROUP_TYPE_AUDIO,
|
|
121
|
-
LANGUAGE_ENGLISH,
|
|
122
122
|
} from '../constants';
|
|
123
123
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
124
124
|
import ParameterError from '../common/errors/parameter';
|
|
@@ -153,6 +153,10 @@ import RecordingController from '../recording-controller';
|
|
|
153
153
|
import ControlsOptionsManager from '../controls-options-manager';
|
|
154
154
|
import PermissionError from '../common/errors/permission';
|
|
155
155
|
import {LocusMediaRequest} from './locusMediaRequest';
|
|
156
|
+
import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
|
|
157
|
+
|
|
158
|
+
// default callback so we don't call an undefined function, but in practice it should never be used
|
|
159
|
+
const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
|
|
156
160
|
|
|
157
161
|
const logRequest = (request: any, {logText = ''}) => {
|
|
158
162
|
LoggerProxy.logger.info(`${logText} - sending request`);
|
|
@@ -526,7 +530,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
526
530
|
conversationUrl: string;
|
|
527
531
|
callStateForMetrics: CallStateForMetrics;
|
|
528
532
|
destination: string;
|
|
529
|
-
destinationType:
|
|
533
|
+
destinationType: DESTINATION_TYPE;
|
|
530
534
|
deviceUrl: string;
|
|
531
535
|
hostId: string;
|
|
532
536
|
id: string;
|
|
@@ -644,6 +648,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
644
648
|
})}event#${EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION}`
|
|
645
649
|
);
|
|
646
650
|
|
|
651
|
+
if (this.getCurUserType() !== 'host') {
|
|
652
|
+
delete payload.spokenLanguages;
|
|
653
|
+
}
|
|
654
|
+
|
|
647
655
|
// @ts-ignore
|
|
648
656
|
this.trigger(EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION, payload);
|
|
649
657
|
},
|
|
@@ -674,12 +682,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
674
682
|
},
|
|
675
683
|
};
|
|
676
684
|
|
|
677
|
-
private
|
|
685
|
+
private addMediaData: {
|
|
686
|
+
retriedWithTurnServer: boolean;
|
|
687
|
+
icePhaseCallback: () => string;
|
|
688
|
+
};
|
|
689
|
+
|
|
678
690
|
private sendSlotManager: SendSlotManager = new SendSlotManager(LoggerProxy);
|
|
679
691
|
private deferSDPAnswer?: Defer; // used for waiting for a response
|
|
680
692
|
private sdpResponseTimer?: ReturnType<typeof setTimeout>;
|
|
681
693
|
private hasMediaConnectionConnectedAtLeastOnce: boolean;
|
|
682
694
|
private joinWithMediaRetryInfo?: {isRetry: boolean; prevJoinResponse?: any};
|
|
695
|
+
private connectionStateHandler?: ConnectionStateHandler;
|
|
696
|
+
private iceCandidateErrors: Map<string, number>;
|
|
697
|
+
private iceCandidatesCount: number;
|
|
683
698
|
|
|
684
699
|
/**
|
|
685
700
|
* @param {Object} attrs
|
|
@@ -1445,13 +1460,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1445
1460
|
this.turnServerUsed = false;
|
|
1446
1461
|
|
|
1447
1462
|
/**
|
|
1448
|
-
*
|
|
1463
|
+
* Contains information used during the addMedia() operation:
|
|
1464
|
+
* retriedWithTurnServer - whether retry was done using TURN Discovery
|
|
1465
|
+
* icePhaseCallback - callback for determining the value for icePhase when sending failure event to CA
|
|
1466
|
+
*
|
|
1449
1467
|
* @instance
|
|
1450
|
-
* @type {
|
|
1468
|
+
* @type {Object}
|
|
1451
1469
|
* @private
|
|
1452
1470
|
* @memberof Meeting
|
|
1453
1471
|
*/
|
|
1454
|
-
this.
|
|
1472
|
+
this.addMediaData = {
|
|
1473
|
+
retriedWithTurnServer: false,
|
|
1474
|
+
icePhaseCallback: DEFAULT_ICE_PHASE_CALLBACK,
|
|
1475
|
+
};
|
|
1455
1476
|
|
|
1456
1477
|
/**
|
|
1457
1478
|
* Whether or not the media connection has ever successfully connected.
|
|
@@ -1470,6 +1491,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1470
1491
|
* @memberof Meeting
|
|
1471
1492
|
*/
|
|
1472
1493
|
this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Connection state handler
|
|
1497
|
+
* @instance
|
|
1498
|
+
* @type {ConnectionStateHandler}
|
|
1499
|
+
* @private
|
|
1500
|
+
* @memberof Meeting
|
|
1501
|
+
*/
|
|
1502
|
+
this.connectionStateHandler = undefined;
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* ICE Candidates errors map
|
|
1506
|
+
* @instance
|
|
1507
|
+
* @type {Map<[number, string], number>}
|
|
1508
|
+
* @private
|
|
1509
|
+
* @memberof Meeting
|
|
1510
|
+
*/
|
|
1511
|
+
this.iceCandidateErrors = new Map();
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Gathered ICE Candidates count
|
|
1515
|
+
* @instance
|
|
1516
|
+
* @type {number}
|
|
1517
|
+
* @private
|
|
1518
|
+
* @memberof Meeting
|
|
1519
|
+
*/
|
|
1520
|
+
this.iceCandidatesCount = 0;
|
|
1473
1521
|
}
|
|
1474
1522
|
|
|
1475
1523
|
/**
|
|
@@ -1718,7 +1766,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1718
1766
|
}
|
|
1719
1767
|
|
|
1720
1768
|
const isStartingSpaceInstantV2Meeting =
|
|
1721
|
-
this.destinationType ===
|
|
1769
|
+
this.destinationType === DESTINATION_TYPE.CONVERSATION_URL &&
|
|
1722
1770
|
// @ts-ignore - config coming from registerPlugin
|
|
1723
1771
|
this.config.experimental.enableAdhocMeetings &&
|
|
1724
1772
|
// @ts-ignore
|
|
@@ -1727,7 +1775,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1727
1775
|
const destination = isStartingSpaceInstantV2Meeting
|
|
1728
1776
|
? this.meetingInfo.meetingJoinUrl
|
|
1729
1777
|
: this.destination;
|
|
1730
|
-
const destinationType = isStartingSpaceInstantV2Meeting
|
|
1778
|
+
const destinationType = isStartingSpaceInstantV2Meeting
|
|
1779
|
+
? DESTINATION_TYPE.MEETING_LINK
|
|
1780
|
+
: this.destinationType;
|
|
1731
1781
|
|
|
1732
1782
|
const permissionTokenExpiryInfo = this.getPermissionTokenExpiryInfo();
|
|
1733
1783
|
|
|
@@ -3114,6 +3164,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3114
3164
|
correlation_id: this.correlationId,
|
|
3115
3165
|
locus_id: this.locusId,
|
|
3116
3166
|
});
|
|
3167
|
+
LoggerProxy.logger.info(
|
|
3168
|
+
'Meeting:index#setUpLocusInfoSelfListener --> MEDIA_INACTIVITY received, reconnecting...'
|
|
3169
|
+
);
|
|
3117
3170
|
this.reconnect();
|
|
3118
3171
|
});
|
|
3119
3172
|
|
|
@@ -4242,8 +4295,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4242
4295
|
* @memberof Meeting
|
|
4243
4296
|
*/
|
|
4244
4297
|
public closePeerConnections() {
|
|
4245
|
-
this.locusMediaRequest = undefined;
|
|
4246
|
-
|
|
4247
4298
|
if (this.mediaProperties.webrtcMediaConnection) {
|
|
4248
4299
|
if (this.remoteMediaManager) {
|
|
4249
4300
|
this.remoteMediaManager.stop();
|
|
@@ -4556,38 +4607,57 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4556
4607
|
try {
|
|
4557
4608
|
let turnServerInfo;
|
|
4558
4609
|
let turnDiscoverySkippedReason;
|
|
4610
|
+
let forceTurnDiscovery = false;
|
|
4559
4611
|
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
const turnDiscoveryRequest = await this.roap.generateTurnDiscoveryRequestMessage(this, true);
|
|
4612
|
+
if (!joinResponse) {
|
|
4613
|
+
// This is the 1st attempt or a retry after join request failed -> we need to do a join with TURN discovery
|
|
4563
4614
|
|
|
4564
|
-
|
|
4565
|
-
|
|
4615
|
+
// @ts-ignore
|
|
4616
|
+
joinOptions.reachability = await this.webex.meetings.reachability.getReachabilityResults();
|
|
4617
|
+
const turnDiscoveryRequest = await this.roap.generateTurnDiscoveryRequestMessage(
|
|
4618
|
+
this,
|
|
4619
|
+
true
|
|
4620
|
+
);
|
|
4621
|
+
|
|
4622
|
+
({turnDiscoverySkippedReason} = turnDiscoveryRequest);
|
|
4623
|
+
joinOptions.roapMessage = turnDiscoveryRequest.roapMessage;
|
|
4566
4624
|
|
|
4567
|
-
if (!joinResponse) {
|
|
4568
4625
|
LoggerProxy.logger.info(
|
|
4569
4626
|
'Meeting:index#joinWithMedia ---> calling join with joinOptions, ',
|
|
4570
4627
|
joinOptions
|
|
4571
4628
|
);
|
|
4572
4629
|
|
|
4573
4630
|
joinResponse = await this.join(joinOptions);
|
|
4574
|
-
}
|
|
4575
4631
|
|
|
4576
|
-
|
|
4632
|
+
joined = true;
|
|
4577
4633
|
|
|
4578
|
-
|
|
4579
|
-
({
|
|
4580
|
-
|
|
4634
|
+
// if we sent out TURN discovery Roap message with join, process the TURN discovery response
|
|
4635
|
+
if (joinOptions.roapMessage) {
|
|
4636
|
+
({turnServerInfo, turnDiscoverySkippedReason} =
|
|
4637
|
+
await this.roap.handleTurnDiscoveryHttpResponse(this, joinResponse));
|
|
4581
4638
|
|
|
4582
|
-
|
|
4583
|
-
|
|
4639
|
+
this.turnDiscoverySkippedReason = turnDiscoverySkippedReason;
|
|
4640
|
+
this.turnServerUsed = !!turnServerInfo;
|
|
4584
4641
|
|
|
4585
|
-
|
|
4586
|
-
|
|
4642
|
+
if (turnServerInfo === undefined) {
|
|
4643
|
+
this.roap.abortTurnDiscovery();
|
|
4644
|
+
}
|
|
4587
4645
|
}
|
|
4646
|
+
} else {
|
|
4647
|
+
// This is a retry, when join succeeded but addMedia failed, so we'll just call addMedia() again,
|
|
4648
|
+
// but we need to ensure that it also does a new TURN discovery
|
|
4649
|
+
forceTurnDiscovery = true;
|
|
4650
|
+
joined = true;
|
|
4588
4651
|
}
|
|
4589
4652
|
|
|
4590
|
-
const mediaResponse = await this.
|
|
4653
|
+
const mediaResponse = await this.addMediaInternal(
|
|
4654
|
+
() => {
|
|
4655
|
+
return this.joinWithMediaRetryInfo.isRetry ? 'JOIN_MEETING_FINAL' : 'JOIN_MEETING_RETRY';
|
|
4656
|
+
},
|
|
4657
|
+
turnServerInfo,
|
|
4658
|
+
forceTurnDiscovery,
|
|
4659
|
+
mediaOptions
|
|
4660
|
+
);
|
|
4591
4661
|
|
|
4592
4662
|
this.joinWithMediaRetryInfo = {isRetry: false, prevJoinResponse: undefined};
|
|
4593
4663
|
|
|
@@ -4626,7 +4696,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4626
4696
|
}
|
|
4627
4697
|
);
|
|
4628
4698
|
|
|
4629
|
-
if
|
|
4699
|
+
// if this was the first attempt, let's do a retry
|
|
4700
|
+
let shouldRetry = !isRetry;
|
|
4701
|
+
|
|
4702
|
+
if (CallDiagnosticUtils.isSdpOfferCreationError(error)) {
|
|
4703
|
+
// errors related to offer creation (for example missing H264 codec) will happen again no matter how many times we try,
|
|
4704
|
+
// so there is no point doing a retry
|
|
4705
|
+
shouldRetry = false;
|
|
4706
|
+
}
|
|
4707
|
+
|
|
4708
|
+
if (shouldRetry) {
|
|
4630
4709
|
LoggerProxy.logger.warn('Meeting:index#joinWithMedia --> retrying call to joinWithMedia');
|
|
4631
4710
|
this.joinWithMediaRetryInfo.isRetry = true;
|
|
4632
4711
|
this.joinWithMediaRetryInfo.prevJoinResponse = joinResponse;
|
|
@@ -4786,6 +4865,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4786
4865
|
reject(new Error('Webex Assistant is not enabled/supported'));
|
|
4787
4866
|
}
|
|
4788
4867
|
|
|
4868
|
+
if (this.getCurUserType() !== 'host') {
|
|
4869
|
+
LoggerProxy.logger.error(
|
|
4870
|
+
'Meeting:index#setSpokenLanguage --> Only host can set spoken language'
|
|
4871
|
+
);
|
|
4872
|
+
|
|
4873
|
+
reject(new Error('Only host can set spoken language'));
|
|
4874
|
+
}
|
|
4875
|
+
|
|
4789
4876
|
try {
|
|
4790
4877
|
const voiceaListenerLanguageUpdate = (payload) => {
|
|
4791
4878
|
// @ts-ignore
|
|
@@ -4839,10 +4926,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4839
4926
|
this.setUpVoiceaListeners();
|
|
4840
4927
|
}
|
|
4841
4928
|
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
await this.webex.internal.voicea.turnOnCaptions(options?.spokenLanguage);
|
|
4845
|
-
}
|
|
4929
|
+
// @ts-ignore
|
|
4930
|
+
await this.webex.internal.voicea.turnOnCaptions(options?.spokenLanguage);
|
|
4846
4931
|
} catch (error) {
|
|
4847
4932
|
LoggerProxy.logger.error(`Meeting:index#startTranscription --> ${error}`);
|
|
4848
4933
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.RECEIVE_TRANSCRIPTION_FAILURE, {
|
|
@@ -5141,6 +5226,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5141
5226
|
return MeetingUtil.joinMeetingOptions(this, options)
|
|
5142
5227
|
.then((join) => {
|
|
5143
5228
|
this.meetingFiniteStateMachine.join();
|
|
5229
|
+
this.setupLocusMediaRequest();
|
|
5230
|
+
|
|
5144
5231
|
LoggerProxy.logger.log('Meeting:index#join --> Success');
|
|
5145
5232
|
|
|
5146
5233
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.JOIN_SUCCESS, {
|
|
@@ -5233,8 +5320,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5233
5320
|
|
|
5234
5321
|
// @ts-ignore - Fix type
|
|
5235
5322
|
if (this.webex.internal.llm.isConnected()) {
|
|
5236
|
-
|
|
5237
|
-
|
|
5323
|
+
if (
|
|
5324
|
+
// @ts-ignore - Fix type
|
|
5325
|
+
url === this.webex.internal.llm.getLocusUrl() &&
|
|
5326
|
+
// @ts-ignore - Fix type
|
|
5327
|
+
datachannelUrl === this.webex.internal.llm.getDatachannelUrl() &&
|
|
5328
|
+
isJoined
|
|
5329
|
+
) {
|
|
5238
5330
|
return undefined;
|
|
5239
5331
|
}
|
|
5240
5332
|
// @ts-ignore - Fix type
|
|
@@ -5634,219 +5726,258 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5634
5726
|
* @returns {undefined}
|
|
5635
5727
|
*/
|
|
5636
5728
|
setupSdpListeners = () => {
|
|
5637
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5638
|
-
|
|
5639
|
-
|
|
5729
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
5730
|
+
MediaConnectionEventNames.REMOTE_SDP_ANSWER_PROCESSED,
|
|
5731
|
+
() => {
|
|
5732
|
+
// @ts-ignore
|
|
5733
|
+
const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
|
|
5640
5734
|
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5735
|
+
// @ts-ignore
|
|
5736
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5737
|
+
name: 'client.media-engine.remote-sdp-received',
|
|
5738
|
+
options: {meetingId: this.id},
|
|
5739
|
+
});
|
|
5740
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_OFFER_TO_ANSWER_LATENCY, {
|
|
5741
|
+
correlation_id: this.correlationId,
|
|
5742
|
+
latency: cdl.getLocalSDPGenRemoteSDPRecv(),
|
|
5743
|
+
meetingId: this.id,
|
|
5744
|
+
});
|
|
5651
5745
|
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5746
|
+
if (this.deferSDPAnswer) {
|
|
5747
|
+
this.deferSDPAnswer.resolve();
|
|
5748
|
+
clearTimeout(this.sdpResponseTimer);
|
|
5749
|
+
this.sdpResponseTimer = undefined;
|
|
5750
|
+
}
|
|
5656
5751
|
}
|
|
5657
|
-
|
|
5752
|
+
);
|
|
5658
5753
|
|
|
5659
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5754
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
5755
|
+
MediaConnectionEventNames.LOCAL_SDP_OFFER_GENERATED,
|
|
5756
|
+
() => {
|
|
5757
|
+
// @ts-ignore
|
|
5758
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5759
|
+
name: 'client.media-engine.local-sdp-generated',
|
|
5760
|
+
options: {meetingId: this.id},
|
|
5761
|
+
});
|
|
5665
5762
|
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5763
|
+
// Instantiate Defer so that the SDP offer/answer exchange timeout can start, see waitForRemoteSDPAnswer()
|
|
5764
|
+
this.deferSDPAnswer = new Defer();
|
|
5765
|
+
}
|
|
5766
|
+
);
|
|
5669
5767
|
|
|
5670
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5768
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
5769
|
+
MediaConnectionEventNames.LOCAL_SDP_ANSWER_GENERATED,
|
|
5770
|
+
() => {
|
|
5771
|
+
// we are sending "remote-sdp-received" only after we've generated the answer - this indicates that we've fully processed that incoming offer
|
|
5772
|
+
// @ts-ignore
|
|
5773
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5774
|
+
name: 'client.media-engine.remote-sdp-received',
|
|
5775
|
+
options: {meetingId: this.id},
|
|
5776
|
+
});
|
|
5777
|
+
}
|
|
5778
|
+
);
|
|
5678
5779
|
};
|
|
5679
5780
|
|
|
5680
5781
|
setupMediaConnectionListeners = () => {
|
|
5681
5782
|
this.setupSdpListeners();
|
|
5682
5783
|
|
|
5683
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5784
|
+
this.mediaProperties.webrtcMediaConnection.on(MediaConnectionEventNames.ROAP_STARTED, () => {
|
|
5684
5785
|
this.isRoapInProgress = true;
|
|
5685
5786
|
});
|
|
5686
5787
|
|
|
5687
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5788
|
+
this.mediaProperties.webrtcMediaConnection.on(MediaConnectionEventNames.ROAP_DONE, () => {
|
|
5688
5789
|
this.mediaNegotiatedEvent();
|
|
5689
5790
|
this.isRoapInProgress = false;
|
|
5690
5791
|
this.processNextQueuedMediaUpdate();
|
|
5691
5792
|
});
|
|
5692
5793
|
|
|
5693
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5794
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
5795
|
+
MediaConnectionEventNames.ROAP_FAILURE,
|
|
5796
|
+
this.handleRoapFailure
|
|
5797
|
+
);
|
|
5694
5798
|
|
|
5695
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5696
|
-
|
|
5799
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
5800
|
+
MediaConnectionEventNames.ROAP_MESSAGE_TO_SEND,
|
|
5801
|
+
(event) => {
|
|
5802
|
+
const LOG_HEADER = `Meeting:index#setupMediaConnectionListeners.ROAP_MESSAGE_TO_SEND --> correlationId=${this.correlationId}`;
|
|
5803
|
+
|
|
5804
|
+
switch (event.roapMessage.messageType) {
|
|
5805
|
+
case 'OK':
|
|
5806
|
+
logRequest(
|
|
5807
|
+
this.roap.sendRoapOK({
|
|
5808
|
+
seq: event.roapMessage.seq,
|
|
5809
|
+
mediaId: this.mediaId,
|
|
5810
|
+
correlationId: this.correlationId,
|
|
5811
|
+
}),
|
|
5812
|
+
{
|
|
5813
|
+
logText: `${LOG_HEADER} Roap OK`,
|
|
5814
|
+
}
|
|
5815
|
+
);
|
|
5816
|
+
break;
|
|
5697
5817
|
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5818
|
+
case 'OFFER':
|
|
5819
|
+
logRequest(
|
|
5820
|
+
this.roap
|
|
5821
|
+
.sendRoapMediaRequest({
|
|
5822
|
+
sdp: event.roapMessage.sdp,
|
|
5823
|
+
seq: event.roapMessage.seq,
|
|
5824
|
+
tieBreaker: event.roapMessage.tieBreaker,
|
|
5825
|
+
meeting: this, // or can pass meeting ID
|
|
5826
|
+
})
|
|
5827
|
+
.then(({roapAnswer}) => {
|
|
5828
|
+
if (roapAnswer) {
|
|
5829
|
+
LoggerProxy.logger.log(`${LOG_HEADER} received Roap ANSWER in http response`);
|
|
5830
|
+
|
|
5831
|
+
this.roapMessageReceived(roapAnswer);
|
|
5832
|
+
}
|
|
5833
|
+
}),
|
|
5834
|
+
{
|
|
5835
|
+
logText: `${LOG_HEADER} Roap Offer`,
|
|
5836
|
+
}
|
|
5837
|
+
).catch((error) => {
|
|
5838
|
+
// @ts-ignore
|
|
5839
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5840
|
+
name: 'client.media-engine.remote-sdp-received',
|
|
5841
|
+
payload: {
|
|
5842
|
+
canProceed: false,
|
|
5843
|
+
errors: [
|
|
5844
|
+
// @ts-ignore
|
|
5845
|
+
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
5846
|
+
{
|
|
5847
|
+
clientErrorCode: CALL_DIAGNOSTIC_CONFIG.MISSING_ROAP_ANSWER_CLIENT_CODE,
|
|
5848
|
+
}
|
|
5849
|
+
),
|
|
5850
|
+
],
|
|
5851
|
+
},
|
|
5852
|
+
options: {meetingId: this.id, rawError: error},
|
|
5853
|
+
});
|
|
5854
|
+
|
|
5855
|
+
this.deferSDPAnswer.reject(new Error('failed to send ROAP SDP offer'));
|
|
5856
|
+
clearTimeout(this.sdpResponseTimer);
|
|
5857
|
+
this.sdpResponseTimer = undefined;
|
|
5858
|
+
});
|
|
5859
|
+
break;
|
|
5711
5860
|
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
.sendRoapMediaRequest({
|
|
5861
|
+
case 'ANSWER':
|
|
5862
|
+
logRequest(
|
|
5863
|
+
this.roap.sendRoapAnswer({
|
|
5716
5864
|
sdp: event.roapMessage.sdp,
|
|
5717
5865
|
seq: event.roapMessage.seq,
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
})
|
|
5721
|
-
.then(({roapAnswer}) => {
|
|
5722
|
-
if (roapAnswer) {
|
|
5723
|
-
LoggerProxy.logger.log(`${LOG_HEADER} received Roap ANSWER in http response`);
|
|
5724
|
-
|
|
5725
|
-
this.roapMessageReceived(roapAnswer);
|
|
5726
|
-
}
|
|
5866
|
+
mediaId: this.mediaId,
|
|
5867
|
+
correlationId: this.correlationId,
|
|
5727
5868
|
}),
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
seq: event.roapMessage.seq,
|
|
5743
|
-
mediaId: this.mediaId,
|
|
5744
|
-
correlationId: this.correlationId,
|
|
5745
|
-
}),
|
|
5746
|
-
{
|
|
5747
|
-
logText: `${LOG_HEADER} Roap Answer`,
|
|
5748
|
-
}
|
|
5749
|
-
).catch((error) => {
|
|
5750
|
-
const metricName = BEHAVIORAL_METRICS.ROAP_ANSWER_FAILURE;
|
|
5751
|
-
const data = {
|
|
5752
|
-
correlation_id: this.correlationId,
|
|
5753
|
-
locus_id: this.locusUrl.split('/').pop(),
|
|
5754
|
-
reason: error.message,
|
|
5755
|
-
stack: error.stack,
|
|
5756
|
-
};
|
|
5757
|
-
const metadata = {
|
|
5758
|
-
type: error.name,
|
|
5759
|
-
};
|
|
5760
|
-
|
|
5761
|
-
Metrics.sendBehavioralMetric(metricName, data, metadata);
|
|
5762
|
-
});
|
|
5763
|
-
break;
|
|
5869
|
+
{
|
|
5870
|
+
logText: `${LOG_HEADER} Roap Answer`,
|
|
5871
|
+
}
|
|
5872
|
+
).catch((error) => {
|
|
5873
|
+
const metricName = BEHAVIORAL_METRICS.ROAP_ANSWER_FAILURE;
|
|
5874
|
+
const data = {
|
|
5875
|
+
correlation_id: this.correlationId,
|
|
5876
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
5877
|
+
reason: error.message,
|
|
5878
|
+
stack: error.stack,
|
|
5879
|
+
};
|
|
5880
|
+
const metadata = {
|
|
5881
|
+
type: error.name,
|
|
5882
|
+
};
|
|
5764
5883
|
|
|
5765
|
-
|
|
5766
|
-
if (
|
|
5767
|
-
event.roapMessage.errorType === ErrorType.CONFLICT ||
|
|
5768
|
-
event.roapMessage.errorType === ErrorType.DOUBLECONFLICT
|
|
5769
|
-
) {
|
|
5770
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_GLARE_CONDITION, {
|
|
5771
|
-
correlation_id: this.correlationId,
|
|
5772
|
-
locus_id: this.locusUrl.split('/').pop(),
|
|
5773
|
-
sequence: event.roapMessage.seq,
|
|
5884
|
+
Metrics.sendBehavioralMetric(metricName, data, metadata);
|
|
5774
5885
|
});
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5886
|
+
break;
|
|
5887
|
+
|
|
5888
|
+
case 'ERROR':
|
|
5889
|
+
if (
|
|
5890
|
+
event.roapMessage.errorType === ErrorType.CONFLICT ||
|
|
5891
|
+
event.roapMessage.errorType === ErrorType.DOUBLECONFLICT
|
|
5892
|
+
) {
|
|
5893
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_GLARE_CONDITION, {
|
|
5894
|
+
correlation_id: this.correlationId,
|
|
5895
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
5896
|
+
sequence: event.roapMessage.seq,
|
|
5897
|
+
});
|
|
5785
5898
|
}
|
|
5786
|
-
|
|
5787
|
-
|
|
5899
|
+
logRequest(
|
|
5900
|
+
this.roap.sendRoapError({
|
|
5901
|
+
seq: event.roapMessage.seq,
|
|
5902
|
+
errorType: event.roapMessage.errorType,
|
|
5903
|
+
mediaId: this.mediaId,
|
|
5904
|
+
correlationId: this.correlationId,
|
|
5905
|
+
}),
|
|
5906
|
+
{
|
|
5907
|
+
logText: `${LOG_HEADER} Roap Error (${event.roapMessage.errorType})`,
|
|
5908
|
+
}
|
|
5909
|
+
);
|
|
5910
|
+
break;
|
|
5788
5911
|
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5912
|
+
default:
|
|
5913
|
+
LoggerProxy.logger.error(
|
|
5914
|
+
`${LOG_HEADER} Unsupported message type: ${event.roapMessage.messageType}`
|
|
5915
|
+
);
|
|
5916
|
+
break;
|
|
5917
|
+
}
|
|
5794
5918
|
}
|
|
5795
|
-
|
|
5919
|
+
);
|
|
5796
5920
|
|
|
5797
5921
|
// eslint-disable-next-line no-param-reassign
|
|
5798
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
const mediaTrack = event.track;
|
|
5807
|
-
const remoteStream = new RemoteStream(MediaUtil.createMediaStream([mediaTrack]));
|
|
5922
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
5923
|
+
MediaConnectionEventNames.REMOTE_TRACK_ADDED,
|
|
5924
|
+
(event) => {
|
|
5925
|
+
LoggerProxy.logger.log(
|
|
5926
|
+
`Meeting:index#setupMediaConnectionListeners --> REMOTE_TRACK_ADDED event received for webrtcMediaConnection: ${JSON.stringify(
|
|
5927
|
+
event
|
|
5928
|
+
)}`
|
|
5929
|
+
);
|
|
5808
5930
|
|
|
5809
|
-
|
|
5810
|
-
|
|
5931
|
+
if (event.track) {
|
|
5932
|
+
const mediaTrack = event.track;
|
|
5933
|
+
const remoteStream = new RemoteStream(MediaUtil.createMediaStream([mediaTrack]));
|
|
5934
|
+
|
|
5935
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5936
|
+
let eventType;
|
|
5937
|
+
|
|
5938
|
+
switch (event.type) {
|
|
5939
|
+
case RemoteTrackType.AUDIO:
|
|
5940
|
+
eventType = EVENT_TYPES.REMOTE_AUDIO;
|
|
5941
|
+
this.mediaProperties.setRemoteAudioStream(remoteStream);
|
|
5942
|
+
break;
|
|
5943
|
+
case RemoteTrackType.VIDEO:
|
|
5944
|
+
eventType = EVENT_TYPES.REMOTE_VIDEO;
|
|
5945
|
+
this.mediaProperties.setRemoteVideoStream(remoteStream);
|
|
5946
|
+
break;
|
|
5947
|
+
case RemoteTrackType.SCREENSHARE_VIDEO:
|
|
5948
|
+
eventType = EVENT_TYPES.REMOTE_SHARE;
|
|
5949
|
+
this.mediaProperties.setRemoteShareStream(remoteStream);
|
|
5950
|
+
break;
|
|
5951
|
+
default: {
|
|
5952
|
+
LoggerProxy.logger.log(
|
|
5953
|
+
'Meeting:index#setupMediaConnectionListeners --> unexpected track'
|
|
5954
|
+
);
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5811
5957
|
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
break;
|
|
5825
|
-
default: {
|
|
5826
|
-
LoggerProxy.logger.log(
|
|
5827
|
-
'Meeting:index#setupMediaConnectionListeners --> unexpected track'
|
|
5958
|
+
if (eventType && mediaTrack) {
|
|
5959
|
+
Trigger.trigger(
|
|
5960
|
+
this,
|
|
5961
|
+
{
|
|
5962
|
+
file: 'meeting/index',
|
|
5963
|
+
function: 'setupRemoteTrackListener:MediaConnectionEventNames.REMOTE_TRACK_ADDED',
|
|
5964
|
+
},
|
|
5965
|
+
EVENT_TRIGGERS.MEDIA_READY,
|
|
5966
|
+
{
|
|
5967
|
+
type: eventType,
|
|
5968
|
+
stream: remoteStream.outputStream,
|
|
5969
|
+
}
|
|
5828
5970
|
);
|
|
5829
5971
|
}
|
|
5830
5972
|
}
|
|
5831
|
-
|
|
5832
|
-
if (eventType && mediaTrack) {
|
|
5833
|
-
Trigger.trigger(
|
|
5834
|
-
this,
|
|
5835
|
-
{
|
|
5836
|
-
file: 'meeting/index',
|
|
5837
|
-
function: 'setupRemoteTrackListener:Event.REMOTE_TRACK_ADDED',
|
|
5838
|
-
},
|
|
5839
|
-
EVENT_TRIGGERS.MEDIA_READY,
|
|
5840
|
-
{
|
|
5841
|
-
type: eventType,
|
|
5842
|
-
stream: remoteStream.outputStream,
|
|
5843
|
-
}
|
|
5844
|
-
);
|
|
5845
|
-
}
|
|
5846
5973
|
}
|
|
5847
|
-
|
|
5974
|
+
);
|
|
5975
|
+
|
|
5976
|
+
this.connectionStateHandler = new ConnectionStateHandler(
|
|
5977
|
+
this.mediaProperties.webrtcMediaConnection
|
|
5978
|
+
);
|
|
5848
5979
|
|
|
5849
|
-
this.
|
|
5980
|
+
this.connectionStateHandler.on(ConnectionStateEvent.stateChanged, (event) => {
|
|
5850
5981
|
const connectionFailed = () => {
|
|
5851
5982
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.CONNECTION_FAILURE, {
|
|
5852
5983
|
correlation_id: this.correlationId,
|
|
@@ -5875,7 +6006,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5875
6006
|
|
|
5876
6007
|
// @ts-ignore
|
|
5877
6008
|
const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
|
|
5878
|
-
|
|
5879
6009
|
switch (event.state) {
|
|
5880
6010
|
case ConnectionState.Connecting:
|
|
5881
6011
|
if (!this.hasMediaConnectionConnectedAtLeastOnce) {
|
|
@@ -5932,25 +6062,28 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5932
6062
|
}
|
|
5933
6063
|
});
|
|
5934
6064
|
|
|
5935
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
6065
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
6066
|
+
MediaConnectionEventNames.ACTIVE_SPEAKERS_CHANGED,
|
|
6067
|
+
(csis) => {
|
|
6068
|
+
Trigger.trigger(
|
|
6069
|
+
this,
|
|
6070
|
+
{
|
|
6071
|
+
file: 'meeting/index',
|
|
6072
|
+
function: 'setupMediaConnectionListeners',
|
|
6073
|
+
},
|
|
6074
|
+
EVENT_TRIGGERS.ACTIVE_SPEAKER_CHANGED,
|
|
6075
|
+
{
|
|
6076
|
+
memberIds: csis
|
|
6077
|
+
// @ts-ignore
|
|
6078
|
+
.map((csi) => this.members.findMemberByCsi(csi)?.id)
|
|
6079
|
+
.filter((item) => item !== undefined),
|
|
6080
|
+
}
|
|
6081
|
+
);
|
|
6082
|
+
}
|
|
6083
|
+
);
|
|
5951
6084
|
|
|
5952
6085
|
this.mediaProperties.webrtcMediaConnection.on(
|
|
5953
|
-
|
|
6086
|
+
MediaConnectionEventNames.VIDEO_SOURCES_COUNT_CHANGED,
|
|
5954
6087
|
(numTotalSources, numLiveSources, mediaContent) => {
|
|
5955
6088
|
Trigger.trigger(
|
|
5956
6089
|
this,
|
|
@@ -5973,7 +6106,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5973
6106
|
);
|
|
5974
6107
|
|
|
5975
6108
|
this.mediaProperties.webrtcMediaConnection.on(
|
|
5976
|
-
|
|
6109
|
+
MediaConnectionEventNames.AUDIO_SOURCES_COUNT_CHANGED,
|
|
5977
6110
|
(numTotalSources, numLiveSources, mediaContent) => {
|
|
5978
6111
|
Trigger.trigger(
|
|
5979
6112
|
this,
|
|
@@ -5990,6 +6123,45 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5990
6123
|
);
|
|
5991
6124
|
}
|
|
5992
6125
|
);
|
|
6126
|
+
|
|
6127
|
+
this.iceCandidateErrors.clear();
|
|
6128
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
6129
|
+
MediaConnectionEventNames.ICE_CANDIDATE_ERROR,
|
|
6130
|
+
(event) => {
|
|
6131
|
+
const {errorCode} = event.error;
|
|
6132
|
+
let {errorText} = event.error;
|
|
6133
|
+
|
|
6134
|
+
if (
|
|
6135
|
+
errorCode === 600 &&
|
|
6136
|
+
errorText === 'Address not associated with the desired network interface.'
|
|
6137
|
+
) {
|
|
6138
|
+
return;
|
|
6139
|
+
}
|
|
6140
|
+
|
|
6141
|
+
if (errorText.endsWith('.')) {
|
|
6142
|
+
errorText = errorText.slice(0, -1);
|
|
6143
|
+
}
|
|
6144
|
+
|
|
6145
|
+
errorText = errorText.toLowerCase();
|
|
6146
|
+
errorText = errorText.replace(/ /g, '_');
|
|
6147
|
+
|
|
6148
|
+
const error = `${errorCode}_${errorText}`;
|
|
6149
|
+
|
|
6150
|
+
const count = this.iceCandidateErrors.get(error) || 0;
|
|
6151
|
+
|
|
6152
|
+
this.iceCandidateErrors.set(error, count + 1);
|
|
6153
|
+
}
|
|
6154
|
+
);
|
|
6155
|
+
|
|
6156
|
+
this.iceCandidatesCount = 0;
|
|
6157
|
+
this.mediaProperties.webrtcMediaConnection.on(
|
|
6158
|
+
MediaConnectionEventNames.ICE_CANDIDATE,
|
|
6159
|
+
(event) => {
|
|
6160
|
+
if (event.candidate) {
|
|
6161
|
+
this.iceCandidatesCount += 1;
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
);
|
|
5993
6165
|
};
|
|
5994
6166
|
|
|
5995
6167
|
/**
|
|
@@ -5999,7 +6171,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5999
6171
|
* @memberof Meetings
|
|
6000
6172
|
*/
|
|
6001
6173
|
setupStatsAnalyzerEventHandlers = () => {
|
|
6002
|
-
this.statsAnalyzer.on(
|
|
6174
|
+
this.statsAnalyzer.on(StatsAnalyzerEventNames.MEDIA_QUALITY, (options) => {
|
|
6003
6175
|
// TODO: might have to send the same event to the developer
|
|
6004
6176
|
// Add ip address info if geo hint is present
|
|
6005
6177
|
// @ts-ignore fix type
|
|
@@ -6013,14 +6185,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6013
6185
|
name: 'client.mediaquality.event',
|
|
6014
6186
|
options: {
|
|
6015
6187
|
meetingId: this.id,
|
|
6016
|
-
networkType: options.networkType,
|
|
6188
|
+
networkType: options.data.networkType,
|
|
6017
6189
|
},
|
|
6018
6190
|
payload: {
|
|
6019
6191
|
intervals: [options.data],
|
|
6020
6192
|
},
|
|
6021
6193
|
});
|
|
6022
6194
|
});
|
|
6023
|
-
|
|
6195
|
+
|
|
6196
|
+
this.statsAnalyzer.on(StatsAnalyzerEventNames.LOCAL_MEDIA_STARTED, (data) => {
|
|
6024
6197
|
Trigger.trigger(
|
|
6025
6198
|
this,
|
|
6026
6199
|
{
|
|
@@ -6034,28 +6207,28 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6034
6207
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6035
6208
|
name: 'client.media.tx.start',
|
|
6036
6209
|
payload: {
|
|
6037
|
-
mediaType: data.
|
|
6038
|
-
shareInstanceId: data.
|
|
6210
|
+
mediaType: data.mediaType,
|
|
6211
|
+
shareInstanceId: data.mediaType === 'share' ? this.localShareInstanceId : undefined,
|
|
6039
6212
|
},
|
|
6040
6213
|
options: {
|
|
6041
6214
|
meetingId: this.id,
|
|
6042
6215
|
},
|
|
6043
6216
|
});
|
|
6044
6217
|
});
|
|
6045
|
-
this.statsAnalyzer.on(
|
|
6218
|
+
this.statsAnalyzer.on(StatsAnalyzerEventNames.LOCAL_MEDIA_STOPPED, (data) => {
|
|
6046
6219
|
// @ts-ignore
|
|
6047
6220
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6048
6221
|
name: 'client.media.tx.stop',
|
|
6049
6222
|
payload: {
|
|
6050
|
-
mediaType: data.
|
|
6051
|
-
shareInstanceId: data.
|
|
6223
|
+
mediaType: data.mediaType,
|
|
6224
|
+
shareInstanceId: data.mediaType === 'share' ? this.localShareInstanceId : undefined,
|
|
6052
6225
|
},
|
|
6053
6226
|
options: {
|
|
6054
6227
|
meetingId: this.id,
|
|
6055
6228
|
},
|
|
6056
6229
|
});
|
|
6057
6230
|
});
|
|
6058
|
-
this.statsAnalyzer.on(
|
|
6231
|
+
this.statsAnalyzer.on(StatsAnalyzerEventNames.REMOTE_MEDIA_STARTED, (data) => {
|
|
6059
6232
|
Trigger.trigger(
|
|
6060
6233
|
this,
|
|
6061
6234
|
{
|
|
@@ -6069,15 +6242,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6069
6242
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6070
6243
|
name: 'client.media.rx.start',
|
|
6071
6244
|
payload: {
|
|
6072
|
-
mediaType: data.
|
|
6073
|
-
shareInstanceId: data.
|
|
6245
|
+
mediaType: data.mediaType,
|
|
6246
|
+
shareInstanceId: data.mediaType === 'share' ? this.remoteShareInstanceId : undefined,
|
|
6074
6247
|
},
|
|
6075
6248
|
options: {
|
|
6076
6249
|
meetingId: this.id,
|
|
6077
6250
|
},
|
|
6078
6251
|
});
|
|
6079
6252
|
|
|
6080
|
-
if (data.
|
|
6253
|
+
if (data.mediaType === 'share') {
|
|
6081
6254
|
// @ts-ignore
|
|
6082
6255
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6083
6256
|
name: 'client.media.render.start',
|
|
@@ -6091,20 +6264,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6091
6264
|
});
|
|
6092
6265
|
}
|
|
6093
6266
|
});
|
|
6094
|
-
this.statsAnalyzer.on(
|
|
6267
|
+
this.statsAnalyzer.on(StatsAnalyzerEventNames.REMOTE_MEDIA_STOPPED, (data) => {
|
|
6095
6268
|
// @ts-ignore
|
|
6096
6269
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6097
6270
|
name: 'client.media.rx.stop',
|
|
6098
6271
|
payload: {
|
|
6099
|
-
mediaType: data.
|
|
6100
|
-
shareInstanceId: data.
|
|
6272
|
+
mediaType: data.mediaType,
|
|
6273
|
+
shareInstanceId: data.mediaType === 'share' ? this.remoteShareInstanceId : undefined,
|
|
6101
6274
|
},
|
|
6102
6275
|
options: {
|
|
6103
6276
|
meetingId: this.id,
|
|
6104
6277
|
},
|
|
6105
6278
|
});
|
|
6106
6279
|
|
|
6107
|
-
if (data.
|
|
6280
|
+
if (data.mediaType === 'share') {
|
|
6108
6281
|
// @ts-ignore
|
|
6109
6282
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6110
6283
|
name: 'client.media.render.stop',
|
|
@@ -6264,6 +6437,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6264
6437
|
try {
|
|
6265
6438
|
await this.mediaProperties.waitForMediaConnectionConnected();
|
|
6266
6439
|
} catch (error) {
|
|
6440
|
+
const {iceConnected} = error;
|
|
6441
|
+
|
|
6267
6442
|
if (!this.hasMediaConnectionConnectedAtLeastOnce) {
|
|
6268
6443
|
// Only send CA event for join flow if we haven't successfully connected media yet
|
|
6269
6444
|
// @ts-ignore
|
|
@@ -6271,7 +6446,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6271
6446
|
name: 'client.ice.end',
|
|
6272
6447
|
payload: {
|
|
6273
6448
|
canProceed: !this.turnServerUsed, // If we haven't done turn tls retry yet we will proceed with join attempt
|
|
6274
|
-
icePhase: this.
|
|
6449
|
+
icePhase: this.addMediaData.icePhaseCallback(),
|
|
6275
6450
|
errors: [
|
|
6276
6451
|
// @ts-ignore
|
|
6277
6452
|
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode(
|
|
@@ -6283,13 +6458,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6283
6458
|
this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc
|
|
6284
6459
|
?.signalingState ||
|
|
6285
6460
|
'unknown',
|
|
6286
|
-
|
|
6287
|
-
this.mediaProperties.webrtcMediaConnection?.multistreamConnection?.pc?.pc
|
|
6288
|
-
?.iceConnectionState ||
|
|
6289
|
-
this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc
|
|
6290
|
-
?.iceConnectionState ||
|
|
6291
|
-
'unknown',
|
|
6461
|
+
iceConnected,
|
|
6292
6462
|
turnServerUsed: this.turnServerUsed,
|
|
6463
|
+
unreachable:
|
|
6464
|
+
// @ts-ignore
|
|
6465
|
+
await this.webex.meetings.reachability
|
|
6466
|
+
.isWebexMediaBackendUnreachable()
|
|
6467
|
+
.catch(() => false),
|
|
6293
6468
|
}),
|
|
6294
6469
|
}
|
|
6295
6470
|
),
|
|
@@ -6317,12 +6492,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6317
6492
|
if (this.config.stats.enableStatsAnalyzer) {
|
|
6318
6493
|
// @ts-ignore - config coming from registerPlugin
|
|
6319
6494
|
this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
|
|
6320
|
-
this.statsAnalyzer = new StatsAnalyzer(
|
|
6495
|
+
this.statsAnalyzer = new StatsAnalyzer({
|
|
6321
6496
|
// @ts-ignore - config coming from registerPlugin
|
|
6322
|
-
this.config.stats,
|
|
6323
|
-
|
|
6324
|
-
this.
|
|
6325
|
-
);
|
|
6497
|
+
config: this.config.stats,
|
|
6498
|
+
networkQualityMonitor: this.networkQualityMonitor,
|
|
6499
|
+
isMultistream: this.isMultistream,
|
|
6500
|
+
});
|
|
6326
6501
|
this.setupStatsAnalyzerEventHandlers();
|
|
6327
6502
|
this.networkQualityMonitor.on(
|
|
6328
6503
|
EVENT_TRIGGERS.NETWORK_QUALITY,
|
|
@@ -6374,6 +6549,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6374
6549
|
ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT / 1000
|
|
6375
6550
|
} seconds`
|
|
6376
6551
|
);
|
|
6552
|
+
// @ts-ignore
|
|
6553
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
6554
|
+
name: 'client.media-engine.remote-sdp-received',
|
|
6555
|
+
payload: {
|
|
6556
|
+
canProceed: false,
|
|
6557
|
+
errors: [
|
|
6558
|
+
// @ts-ignore
|
|
6559
|
+
this.webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode({
|
|
6560
|
+
clientErrorCode: CALL_DIAGNOSTIC_CONFIG.MISSING_ROAP_ANSWER_CLIENT_CODE,
|
|
6561
|
+
}),
|
|
6562
|
+
],
|
|
6563
|
+
},
|
|
6564
|
+
options: {meetingId: this.id, rawError: new Error('Timeout waiting for SDP answer')},
|
|
6565
|
+
});
|
|
6566
|
+
|
|
6377
6567
|
deferSDPAnswer.reject(new Error('Timed out waiting for REMOTE SDP ANSWER'));
|
|
6378
6568
|
}, ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT);
|
|
6379
6569
|
|
|
@@ -6422,7 +6612,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6422
6612
|
remoteMediaManagerConfig?: RemoteMediaManagerConfiguration,
|
|
6423
6613
|
bundlePolicy?: BundlePolicy
|
|
6424
6614
|
): Promise<void> {
|
|
6425
|
-
this.retriedWithTurnServer = true;
|
|
6615
|
+
this.addMediaData.retriedWithTurnServer = true;
|
|
6426
6616
|
const LOG_HEADER = 'Meeting:index#addMedia():retryWithForcedTurnDiscovery -->';
|
|
6427
6617
|
|
|
6428
6618
|
await this.cleanUpBeforeRetryWithTurnServer();
|
|
@@ -6517,7 +6707,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6517
6707
|
correlation_id: this.correlationId,
|
|
6518
6708
|
latency: cdl.getTurnDiscoveryTime(),
|
|
6519
6709
|
turnServerUsed: this.turnServerUsed,
|
|
6520
|
-
retriedWithTurnServer: this.retriedWithTurnServer,
|
|
6710
|
+
retriedWithTurnServer: this.addMediaData.retriedWithTurnServer,
|
|
6521
6711
|
});
|
|
6522
6712
|
}
|
|
6523
6713
|
|
|
@@ -6541,7 +6731,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6541
6731
|
turnServerInfo?: TurnServerInfo
|
|
6542
6732
|
): Promise<void> {
|
|
6543
6733
|
const LOG_HEADER = 'Meeting:index#addMedia():establishMediaConnection -->';
|
|
6544
|
-
const isReconnecting =
|
|
6734
|
+
const isReconnecting =
|
|
6735
|
+
this.isMoveToInProgress || !!this.locusMediaRequest?.isConfluenceCreated();
|
|
6545
6736
|
|
|
6546
6737
|
// We are forcing turn discovery if the case is moveTo and a turn server was used already
|
|
6547
6738
|
if (this.isMoveToInProgress && this.turnServerUsed) {
|
|
@@ -6663,24 +6854,80 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6663
6854
|
}
|
|
6664
6855
|
}
|
|
6665
6856
|
|
|
6857
|
+
/**
|
|
6858
|
+
* Creates an instance of LocusMediaRequest for this meeting - it is needed for doing any calls
|
|
6859
|
+
* to Locus /media API (these are used for sending Roap messages and updating audio/video mute status).
|
|
6860
|
+
*
|
|
6861
|
+
* @returns {void}
|
|
6862
|
+
*/
|
|
6863
|
+
private setupLocusMediaRequest() {
|
|
6864
|
+
this.locusMediaRequest = new LocusMediaRequest(
|
|
6865
|
+
{
|
|
6866
|
+
correlationId: this.correlationId,
|
|
6867
|
+
meetingId: this.id,
|
|
6868
|
+
device: {
|
|
6869
|
+
url: this.deviceUrl,
|
|
6870
|
+
// @ts-ignore
|
|
6871
|
+
deviceType: this.config.deviceType,
|
|
6872
|
+
// @ts-ignore
|
|
6873
|
+
countryCode: this.webex.meetings.geoHintInfo?.countryCode,
|
|
6874
|
+
// @ts-ignore
|
|
6875
|
+
regionCode: this.webex.meetings.geoHintInfo?.regionCode,
|
|
6876
|
+
},
|
|
6877
|
+
preferTranscoding: !this.isMultistream,
|
|
6878
|
+
},
|
|
6879
|
+
{
|
|
6880
|
+
// @ts-ignore
|
|
6881
|
+
parent: this.webex,
|
|
6882
|
+
}
|
|
6883
|
+
);
|
|
6884
|
+
}
|
|
6885
|
+
|
|
6666
6886
|
/**
|
|
6667
6887
|
* Creates a media connection to the server. Media connection is required for sending or receiving any audio/video.
|
|
6668
6888
|
*
|
|
6669
6889
|
* @param {AddMediaOptions} options
|
|
6670
|
-
* @param {TurnServerInfo} turnServerInfo - TURN server information (used only internally by the SDK)
|
|
6671
6890
|
* @returns {Promise<void>}
|
|
6672
6891
|
* @public
|
|
6673
6892
|
* @memberof Meeting
|
|
6674
6893
|
*/
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6894
|
+
addMedia(options: AddMediaOptions = {}): Promise<void> {
|
|
6895
|
+
return this.addMediaInternal(
|
|
6896
|
+
() => (this.turnServerUsed ? 'JOIN_MEETING_FINAL' : 'JOIN_MEETING_RETRY'),
|
|
6897
|
+
undefined,
|
|
6898
|
+
false,
|
|
6899
|
+
options
|
|
6900
|
+
);
|
|
6901
|
+
}
|
|
6902
|
+
|
|
6903
|
+
/**
|
|
6904
|
+
* Internal version of addMedia() with some more arguments for finer control of its behavior
|
|
6905
|
+
*
|
|
6906
|
+
* @param {Function} icePhaseCallback - callback to determine the icePhase for CA "client.ice.end" failure events
|
|
6907
|
+
* @param {TurnServerInfo} turnServerInfo - TURN server information
|
|
6908
|
+
* @param {boolean} forceTurnDiscovery - if true, TURN discovery will be done
|
|
6909
|
+
* @param {AddMediaOptions} options - same as options of the public addMedia() method
|
|
6910
|
+
* @returns {Promise<void>}
|
|
6911
|
+
* @protected
|
|
6912
|
+
* @memberof Meeting
|
|
6913
|
+
*/
|
|
6914
|
+
protected async addMediaInternal(
|
|
6915
|
+
icePhaseCallback: () => string,
|
|
6916
|
+
turnServerInfo: TurnServerInfo,
|
|
6917
|
+
forceTurnDiscovery,
|
|
6918
|
+
options: AddMediaOptions = {}
|
|
6678
6919
|
): Promise<void> {
|
|
6679
|
-
this.retriedWithTurnServer = false;
|
|
6920
|
+
this.addMediaData.retriedWithTurnServer = false;
|
|
6921
|
+
this.addMediaData.icePhaseCallback = icePhaseCallback;
|
|
6922
|
+
|
|
6680
6923
|
this.hasMediaConnectionConnectedAtLeastOnce = false;
|
|
6681
6924
|
const LOG_HEADER = 'Meeting:index#addMedia -->';
|
|
6682
6925
|
LoggerProxy.logger.info(
|
|
6683
|
-
`${LOG_HEADER} called with:
|
|
6926
|
+
`${LOG_HEADER} called with: options=${JSON.stringify(
|
|
6927
|
+
options
|
|
6928
|
+
)}, turnServerInfo=${JSON.stringify(
|
|
6929
|
+
turnServerInfo
|
|
6930
|
+
)}, forceTurnDiscovery=${forceTurnDiscovery}`
|
|
6684
6931
|
);
|
|
6685
6932
|
|
|
6686
6933
|
if (options.allowMediaInLobby !== true && this.meetingState !== FULL_STATE.ACTIVE) {
|
|
@@ -6744,27 +6991,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6744
6991
|
receiveShare: shareAudioEnabled || shareVideoEnabled,
|
|
6745
6992
|
});
|
|
6746
6993
|
|
|
6747
|
-
this.locusMediaRequest = new LocusMediaRequest(
|
|
6748
|
-
{
|
|
6749
|
-
correlationId: this.correlationId,
|
|
6750
|
-
meetingId: this.id,
|
|
6751
|
-
device: {
|
|
6752
|
-
url: this.deviceUrl,
|
|
6753
|
-
// @ts-ignore
|
|
6754
|
-
deviceType: this.config.deviceType,
|
|
6755
|
-
// @ts-ignore
|
|
6756
|
-
countryCode: this.webex.meetings.geoHintInfo?.countryCode,
|
|
6757
|
-
// @ts-ignore
|
|
6758
|
-
regionCode: this.webex.meetings.geoHintInfo?.regionCode,
|
|
6759
|
-
},
|
|
6760
|
-
preferTranscoding: !this.isMultistream,
|
|
6761
|
-
},
|
|
6762
|
-
{
|
|
6763
|
-
// @ts-ignore
|
|
6764
|
-
parent: this.webex,
|
|
6765
|
-
}
|
|
6766
|
-
);
|
|
6767
|
-
|
|
6768
6994
|
this.audio = createMuteState(AUDIO, this, audioEnabled);
|
|
6769
6995
|
this.video = createMuteState(VIDEO, this, videoEnabled);
|
|
6770
6996
|
|
|
@@ -6778,7 +7004,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6778
7004
|
await this.establishMediaConnection(
|
|
6779
7005
|
remoteMediaManagerConfig,
|
|
6780
7006
|
bundlePolicy,
|
|
6781
|
-
|
|
7007
|
+
forceTurnDiscovery,
|
|
6782
7008
|
turnServerInfo
|
|
6783
7009
|
);
|
|
6784
7010
|
|
|
@@ -6804,9 +7030,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6804
7030
|
selectedCandidatePairChanges,
|
|
6805
7031
|
numTransports,
|
|
6806
7032
|
isMultistream: this.isMultistream,
|
|
6807
|
-
retriedWithTurnServer: this.retriedWithTurnServer,
|
|
7033
|
+
retriedWithTurnServer: this.addMediaData.retriedWithTurnServer,
|
|
6808
7034
|
isJoinWithMediaRetry: this.joinWithMediaRetryInfo.isRetry,
|
|
6809
7035
|
...reachabilityStats,
|
|
7036
|
+
iceCandidatesCount: this.iceCandidatesCount,
|
|
6810
7037
|
});
|
|
6811
7038
|
// @ts-ignore
|
|
6812
7039
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
@@ -6830,6 +7057,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6830
7057
|
const {selectedCandidatePairChanges, numTransports} =
|
|
6831
7058
|
await this.mediaProperties.getCurrentConnectionInfo();
|
|
6832
7059
|
|
|
7060
|
+
const iceCandidateErrors = Object.fromEntries(this.iceCandidateErrors);
|
|
7061
|
+
|
|
6833
7062
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
|
|
6834
7063
|
correlation_id: this.correlationId,
|
|
6835
7064
|
locus_id: this.locusUrl.split('/').pop(),
|
|
@@ -6840,7 +7069,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6840
7069
|
numTransports,
|
|
6841
7070
|
turnDiscoverySkippedReason: this.turnDiscoverySkippedReason,
|
|
6842
7071
|
turnServerUsed: this.turnServerUsed,
|
|
6843
|
-
retriedWithTurnServer: this.retriedWithTurnServer,
|
|
7072
|
+
retriedWithTurnServer: this.addMediaData.retriedWithTurnServer,
|
|
6844
7073
|
isMultistream: this.isMultistream,
|
|
6845
7074
|
isJoinWithMediaRetry: this.joinWithMediaRetryInfo.isRetry,
|
|
6846
7075
|
signalingState:
|
|
@@ -6859,6 +7088,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6859
7088
|
this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc?.iceConnectionState ||
|
|
6860
7089
|
'unknown',
|
|
6861
7090
|
...reachabilityMetrics,
|
|
7091
|
+
...iceCandidateErrors,
|
|
7092
|
+
iceCandidatesCount: this.iceCandidatesCount,
|
|
6862
7093
|
});
|
|
6863
7094
|
|
|
6864
7095
|
await this.cleanUpOnAddMediaFailure();
|
|
@@ -6879,6 +7110,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6879
7110
|
}
|
|
6880
7111
|
|
|
6881
7112
|
throw error;
|
|
7113
|
+
} finally {
|
|
7114
|
+
this.addMediaData.icePhaseCallback = DEFAULT_ICE_PHASE_CALLBACK;
|
|
6882
7115
|
}
|
|
6883
7116
|
}
|
|
6884
7117
|
|
|
@@ -7896,6 +8129,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7896
8129
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE, {
|
|
7897
8130
|
correlationId: this.correlationId,
|
|
7898
8131
|
muted,
|
|
8132
|
+
encoderImplementation: this.statsAnalyzer?.shareVideoEncoderImplementation,
|
|
7899
8133
|
});
|
|
7900
8134
|
};
|
|
7901
8135
|
|