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