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