@webex/plugin-meetings 3.7.0 → 3.8.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/annotation/index.js +17 -0
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/common/errors/join-forbidden-error.js +52 -0
- package/dist/common/errors/join-forbidden-error.js.map +1 -0
- package/dist/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
- package/dist/common/errors/join-webinar-error.js.map +1 -0
- package/dist/common/errors/multistream-not-supported-error.js +53 -0
- package/dist/common/errors/multistream-not-supported-error.js.map +1 -0
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +69 -6
- package/dist/constants.js.map +1 -1
- package/dist/index.js +16 -11
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +4 -4
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +14 -3
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +35 -17
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +1 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +30 -16
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +167 -0
- package/dist/meeting/brbState.js.map +1 -0
- package/dist/meeting/in-meeting-actions.js +13 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1373 -1052
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +32 -11
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +1 -6
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +51 -29
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +103 -67
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +115 -45
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +6 -2
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +107 -55
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +2 -0
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/util.js +1 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +39 -28
- package/dist/member/util.js.map +1 -1
- package/dist/members/util.js +4 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +6 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/remoteMedia.js +30 -15
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +40 -8
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +24 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.js +12 -15
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +471 -140
- package/dist/reachability/index.js.map +1 -1
- package/dist/{rtcMetrics/constants.js → reachability/reachability.types.js} +1 -5
- package/dist/reachability/reachability.types.js.map +1 -0
- package/dist/reachability/request.js +21 -8
- package/dist/reachability/request.js.map +1 -1
- package/dist/recording-controller/enums.js +8 -4
- package/dist/recording-controller/enums.js.map +1 -1
- package/dist/recording-controller/index.js +18 -9
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +13 -9
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js +15 -15
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +45 -79
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +3 -6
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/types/annotation/index.d.ts +5 -0
- package/dist/types/common/errors/join-forbidden-error.d.ts +15 -0
- package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
- package/dist/types/common/errors/multistream-not-supported-error.d.ts +17 -0
- package/dist/types/config.d.ts +2 -0
- package/dist/types/constants.d.ts +54 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/meeting/brbState.d.ts +54 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +12 -0
- package/dist/types/meeting/index.d.ts +86 -14
- package/dist/types/meeting/locusMediaRequest.d.ts +6 -3
- package/dist/types/meeting/request.d.ts +14 -3
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +3 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +30 -5
- package/dist/types/meetings/index.d.ts +20 -2
- package/dist/types/meetings/meetings.types.d.ts +8 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +7 -0
- package/dist/types/members/util.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +6 -1
- package/dist/types/multistream/remoteMediaManager.d.ts +10 -1
- package/dist/types/multistream/sendSlotManager.d.ts +8 -1
- package/dist/types/reachability/clusterReachability.d.ts +1 -10
- package/dist/types/reachability/index.d.ts +83 -36
- package/dist/types/reachability/reachability.types.d.ts +64 -0
- package/dist/types/reachability/request.d.ts +5 -1
- package/dist/types/recording-controller/enums.d.ts +5 -2
- package/dist/types/recording-controller/index.d.ts +1 -0
- package/dist/types/recording-controller/util.d.ts +2 -1
- package/dist/types/roap/request.d.ts +1 -13
- package/dist/webinar/index.js +390 -7
- package/dist/webinar/index.js.map +1 -1
- package/package.json +23 -22
- package/src/annotation/index.ts +16 -0
- package/src/common/errors/join-forbidden-error.ts +26 -0
- package/src/common/errors/join-webinar-error.ts +24 -0
- package/src/common/errors/multistream-not-supported-error.ts +30 -0
- package/src/config.ts +2 -0
- package/src/constants.ts +62 -3
- package/src/index.ts +5 -3
- package/src/interpretation/index.ts +3 -3
- package/src/locus-info/index.ts +20 -3
- package/src/locus-info/selfUtils.ts +24 -6
- package/src/media/MediaConnectionAwaiter.ts +2 -0
- package/src/media/properties.ts +34 -13
- package/src/meeting/brbState.ts +169 -0
- package/src/meeting/in-meeting-actions.ts +25 -0
- package/src/meeting/index.ts +485 -88
- package/src/meeting/locusMediaRequest.ts +38 -12
- package/src/meeting/muteState.ts +1 -6
- package/src/meeting/request.ts +30 -12
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +32 -13
- package/src/meeting-info/meeting-info-v2.ts +83 -12
- package/src/meeting-info/utilv2.ts +17 -3
- package/src/meetings/index.ts +79 -20
- package/src/meetings/meetings.types.ts +10 -0
- package/src/meetings/util.ts +2 -1
- package/src/member/index.ts +9 -0
- package/src/member/types.ts +8 -0
- package/src/member/util.ts +34 -24
- package/src/members/util.ts +1 -0
- package/src/metrics/constants.ts +6 -1
- package/src/multistream/remoteMedia.ts +28 -15
- package/src/multistream/remoteMediaManager.ts +32 -10
- package/src/multistream/sendSlotManager.ts +31 -0
- package/src/reachability/clusterReachability.ts +5 -15
- package/src/reachability/index.ts +315 -75
- package/src/reachability/reachability.types.ts +85 -0
- package/src/reachability/request.ts +55 -31
- package/src/recording-controller/enums.ts +5 -2
- package/src/recording-controller/index.ts +17 -4
- package/src/recording-controller/util.ts +28 -9
- package/src/roap/index.ts +14 -13
- package/src/roap/request.ts +30 -44
- package/src/roap/turnDiscovery.ts +2 -4
- package/src/webinar/index.ts +235 -9
- package/test/unit/spec/annotation/index.ts +46 -1
- package/test/unit/spec/interpretation/index.ts +39 -1
- package/test/unit/spec/locus-info/index.js +292 -60
- package/test/unit/spec/locus-info/selfConstant.js +7 -0
- package/test/unit/spec/locus-info/selfUtils.js +101 -1
- package/test/unit/spec/media/properties.ts +15 -0
- package/test/unit/spec/meeting/brbState.ts +114 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +15 -1
- package/test/unit/spec/meeting/index.js +908 -124
- package/test/unit/spec/meeting/locusMediaRequest.ts +111 -66
- package/test/unit/spec/meeting/muteState.js +0 -24
- package/test/unit/spec/meeting/request.js +3 -26
- package/test/unit/spec/meeting/utils.js +73 -28
- package/test/unit/spec/meeting-info/meetinginfov2.js +46 -4
- package/test/unit/spec/meeting-info/utilv2.js +26 -0
- package/test/unit/spec/meetings/index.js +172 -18
- package/test/unit/spec/meetings/utils.js +10 -0
- package/test/unit/spec/member/util.js +52 -11
- package/test/unit/spec/members/utils.js +95 -0
- package/test/unit/spec/multistream/remoteMedia.ts +11 -7
- package/test/unit/spec/multistream/remoteMediaManager.ts +397 -118
- package/test/unit/spec/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/reachability/index.ts +391 -9
- package/test/unit/spec/reachability/request.js +48 -12
- package/test/unit/spec/recording-controller/index.js +61 -5
- package/test/unit/spec/recording-controller/util.js +39 -3
- package/test/unit/spec/roap/index.ts +48 -1
- package/test/unit/spec/roap/request.ts +51 -109
- package/test/unit/spec/roap/turnDiscovery.ts +202 -147
- package/test/unit/spec/webinar/index.ts +509 -0
- package/dist/common/errors/webinar-registration-error.js.map +0 -1
- package/dist/networkQualityMonitor/index.js +0 -227
- package/dist/networkQualityMonitor/index.js.map +0 -1
- package/dist/rtcMetrics/constants.js.map +0 -1
- package/dist/rtcMetrics/index.js +0 -197
- package/dist/rtcMetrics/index.js.map +0 -1
- package/dist/types/networkQualityMonitor/index.d.ts +0 -70
- package/dist/types/rtcMetrics/constants.d.ts +0 -4
- package/dist/types/rtcMetrics/index.d.ts +0 -71
- package/src/common/errors/webinar-registration-error.ts +0 -27
|
@@ -6,20 +6,7 @@ import {convertStunUrlToTurn, convertStunUrlToTurnTls} from './util';
|
|
|
6
6
|
import EventsScope from '../common/events/events-scope';
|
|
7
7
|
|
|
8
8
|
import {CONNECTION_STATE, Enum, ICE_GATHERING_STATE} from '../constants';
|
|
9
|
-
|
|
10
|
-
// result for a specific transport protocol (like udp or tcp)
|
|
11
|
-
export type TransportResult = {
|
|
12
|
-
result: 'reachable' | 'unreachable' | 'untested';
|
|
13
|
-
latencyInMilliseconds?: number; // amount of time it took to get the first ICE candidate
|
|
14
|
-
clientMediaIPs?: string[];
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// reachability result for a specific media cluster
|
|
18
|
-
export type ClusterReachabilityResult = {
|
|
19
|
-
udp: TransportResult;
|
|
20
|
-
tcp: TransportResult;
|
|
21
|
-
xtls: TransportResult;
|
|
22
|
-
};
|
|
9
|
+
import {ClusterReachabilityResult} from './reachability.types';
|
|
23
10
|
|
|
24
11
|
// data for the Events.resultReady event
|
|
25
12
|
export type ResultEventData = {
|
|
@@ -370,11 +357,14 @@ export class ClusterReachability extends EventsScope {
|
|
|
370
357
|
|
|
371
358
|
this.startTimestamp = performance.now();
|
|
372
359
|
|
|
360
|
+
// Set up the state change listeners before triggering the ICE gathering
|
|
361
|
+
const gatherIceCandidatePromise = this.gatherIceCandidates();
|
|
362
|
+
|
|
373
363
|
// not awaiting the next call on purpose, because we're not sending the offer anywhere and there won't be any answer
|
|
374
364
|
// we just need to make this call to trigger the ICE gathering process
|
|
375
365
|
this.pc.setLocalDescription(offer);
|
|
376
366
|
|
|
377
|
-
await
|
|
367
|
+
await gatherIceCandidatePromise;
|
|
378
368
|
} catch (error) {
|
|
379
369
|
LoggerProxy.logger.warn(`Reachability:ClusterReachability#start --> Error: `, error);
|
|
380
370
|
}
|
|
@@ -9,64 +9,31 @@ import {Defer} from '@webex/common';
|
|
|
9
9
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
10
10
|
import MeetingUtil from '../meeting/util';
|
|
11
11
|
|
|
12
|
-
import {REACHABILITY} from '../constants';
|
|
12
|
+
import {IP_VERSION, REACHABILITY} from '../constants';
|
|
13
13
|
|
|
14
14
|
import ReachabilityRequest, {ClusterList} from './request';
|
|
15
|
+
import {
|
|
16
|
+
ClusterReachabilityResult,
|
|
17
|
+
TransportResult,
|
|
18
|
+
ClientMediaPreferences,
|
|
19
|
+
ReachabilityMetrics,
|
|
20
|
+
ReachabilityReportV0,
|
|
21
|
+
ReachabilityReportV1,
|
|
22
|
+
ReachabilityResults,
|
|
23
|
+
ReachabilityResultsForBackend,
|
|
24
|
+
TransportResultForBackend,
|
|
25
|
+
GetClustersTrigger,
|
|
26
|
+
} from './reachability.types';
|
|
15
27
|
import {
|
|
16
28
|
ClientMediaIpsUpdatedEventData,
|
|
17
29
|
ClusterReachability,
|
|
18
|
-
ClusterReachabilityResult,
|
|
19
30
|
Events,
|
|
20
31
|
ResultEventData,
|
|
21
|
-
TransportResult,
|
|
22
32
|
} from './clusterReachability';
|
|
23
33
|
import EventsScope from '../common/events/events-scope';
|
|
24
34
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
25
35
|
import Metrics from '../metrics';
|
|
26
36
|
|
|
27
|
-
export type ReachabilityMetrics = {
|
|
28
|
-
reachability_public_udp_success: number;
|
|
29
|
-
reachability_public_udp_failed: number;
|
|
30
|
-
reachability_public_tcp_success: number;
|
|
31
|
-
reachability_public_tcp_failed: number;
|
|
32
|
-
reachability_public_xtls_success: number;
|
|
33
|
-
reachability_public_xtls_failed: number;
|
|
34
|
-
reachability_vmn_udp_success: number;
|
|
35
|
-
reachability_vmn_udp_failed: number;
|
|
36
|
-
reachability_vmn_tcp_success: number;
|
|
37
|
-
reachability_vmn_tcp_failed: number;
|
|
38
|
-
reachability_vmn_xtls_success: number;
|
|
39
|
-
reachability_vmn_xtls_failed: number;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* This is the type that matches what backend expects us to send to them. It is a bit weird, because
|
|
44
|
-
* it uses strings instead of booleans and numbers, but that's what they require.
|
|
45
|
-
*/
|
|
46
|
-
export type TransportResultForBackend = {
|
|
47
|
-
reachable?: 'true' | 'false';
|
|
48
|
-
latencyInMilliseconds?: string;
|
|
49
|
-
clientMediaIPs?: string[];
|
|
50
|
-
untested?: 'true';
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export type ReachabilityResultForBackend = {
|
|
54
|
-
udp: TransportResultForBackend;
|
|
55
|
-
tcp: TransportResultForBackend;
|
|
56
|
-
xtls: TransportResultForBackend;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// this is the type that is required by the backend when we send them reachability results
|
|
60
|
-
export type ReachabilityResultsForBackend = Record<string, ReachabilityResultForBackend>;
|
|
61
|
-
|
|
62
|
-
// this is the type used by Reachability class internally and stored in local storage
|
|
63
|
-
export type ReachabilityResults = Record<
|
|
64
|
-
string,
|
|
65
|
-
ClusterReachabilityResult & {
|
|
66
|
-
isVideoMesh?: boolean;
|
|
67
|
-
}
|
|
68
|
-
>;
|
|
69
|
-
|
|
70
37
|
// timeouts in seconds
|
|
71
38
|
const DEFAULT_TIMEOUT = 3;
|
|
72
39
|
const VIDEO_MESH_TIMEOUT = 1;
|
|
@@ -84,6 +51,9 @@ export default class Reachability extends EventsScope {
|
|
|
84
51
|
[key: string]: ClusterReachability;
|
|
85
52
|
};
|
|
86
53
|
|
|
54
|
+
minRequiredClusters?: number;
|
|
55
|
+
orpheusApiVersion?: number;
|
|
56
|
+
|
|
87
57
|
reachabilityDefer?: Defer;
|
|
88
58
|
|
|
89
59
|
vmnTimer?: ReturnType<typeof setTimeout>;
|
|
@@ -92,6 +62,8 @@ export default class Reachability extends EventsScope {
|
|
|
92
62
|
|
|
93
63
|
expectedResultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
|
94
64
|
resultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
|
65
|
+
startTime = undefined;
|
|
66
|
+
totalDuration = undefined;
|
|
95
67
|
|
|
96
68
|
protected lastTrigger?: string;
|
|
97
69
|
|
|
@@ -118,14 +90,35 @@ export default class Reachability extends EventsScope {
|
|
|
118
90
|
|
|
119
91
|
/**
|
|
120
92
|
* Fetches the list of media clusters from the backend
|
|
93
|
+
* @param {string} trigger - explains the reason for starting reachability, used by Orpheus
|
|
94
|
+
* @param {Object} previousReport - last reachability report
|
|
121
95
|
* @param {boolean} isRetry
|
|
122
96
|
* @private
|
|
123
97
|
* @returns {Promise<{clusters: ClusterList, joinCookie: any}>}
|
|
124
98
|
*/
|
|
125
|
-
async getClusters(
|
|
99
|
+
async getClusters(
|
|
100
|
+
trigger: GetClustersTrigger,
|
|
101
|
+
previousReport?: any,
|
|
102
|
+
isRetry = false
|
|
103
|
+
): Promise<{
|
|
104
|
+
clusters: ClusterList;
|
|
105
|
+
joinCookie: any;
|
|
106
|
+
}> {
|
|
126
107
|
try {
|
|
127
|
-
const {clusters, joinCookie} = await this.reachabilityRequest.getClusters(
|
|
128
|
-
|
|
108
|
+
const {clusters, joinCookie, discoveryOptions} = await this.reachabilityRequest.getClusters(
|
|
109
|
+
trigger,
|
|
110
|
+
MeetingUtil.getIpVersion(this.webex),
|
|
111
|
+
previousReport
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
this.minRequiredClusters = discoveryOptions?.['early-call-min-clusters'];
|
|
115
|
+
this.orpheusApiVersion = discoveryOptions?.['report-version'];
|
|
116
|
+
|
|
117
|
+
// @ts-ignore
|
|
118
|
+
await this.webex.boundedStorage.put(
|
|
119
|
+
this.namespace,
|
|
120
|
+
REACHABILITY.localStorageJoinCookie,
|
|
121
|
+
JSON.stringify(joinCookie)
|
|
129
122
|
);
|
|
130
123
|
|
|
131
124
|
return {clusters, joinCookie};
|
|
@@ -138,7 +131,7 @@ export default class Reachability extends EventsScope {
|
|
|
138
131
|
`Reachability:index#getClusters --> Failed with error: ${error}, retrying...`
|
|
139
132
|
);
|
|
140
133
|
|
|
141
|
-
return this.getClusters(true);
|
|
134
|
+
return this.getClusters(trigger, previousReport, true);
|
|
142
135
|
}
|
|
143
136
|
}
|
|
144
137
|
|
|
@@ -150,6 +143,10 @@ export default class Reachability extends EventsScope {
|
|
|
150
143
|
* @memberof Reachability
|
|
151
144
|
*/
|
|
152
145
|
public async gatherReachability(trigger: string): Promise<ReachabilityResults> {
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
if (!this.webex.config.meetings.enableReachabilityChecks) {
|
|
148
|
+
throw new Error('enableReachabilityChecks is disabled in config');
|
|
149
|
+
}
|
|
153
150
|
// Fetch clusters and measure latency
|
|
154
151
|
try {
|
|
155
152
|
this.lastTrigger = trigger;
|
|
@@ -159,14 +156,7 @@ export default class Reachability extends EventsScope {
|
|
|
159
156
|
// @ts-ignore
|
|
160
157
|
this.webex.internal.device.ipNetworkDetector.detect(true);
|
|
161
158
|
|
|
162
|
-
const {clusters
|
|
163
|
-
|
|
164
|
-
// @ts-ignore
|
|
165
|
-
await this.webex.boundedStorage.put(
|
|
166
|
-
this.namespace,
|
|
167
|
-
REACHABILITY.localStorageJoinCookie,
|
|
168
|
-
JSON.stringify(joinCookie)
|
|
169
|
-
);
|
|
159
|
+
const {clusters} = await this.getClusters('startup');
|
|
170
160
|
|
|
171
161
|
this.reachabilityDefer = new Defer();
|
|
172
162
|
|
|
@@ -181,6 +171,124 @@ export default class Reachability extends EventsScope {
|
|
|
181
171
|
}
|
|
182
172
|
}
|
|
183
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Gets the last join cookie we got from Orpheus
|
|
176
|
+
*
|
|
177
|
+
* @returns {Promise<Object>} join cookie
|
|
178
|
+
*/
|
|
179
|
+
async getJoinCookie() {
|
|
180
|
+
// @ts-ignore
|
|
181
|
+
const joinCookieRaw = await this.webex.boundedStorage
|
|
182
|
+
.get(REACHABILITY.namespace, REACHABILITY.localStorageJoinCookie)
|
|
183
|
+
.catch(() => {});
|
|
184
|
+
|
|
185
|
+
let joinCookie;
|
|
186
|
+
|
|
187
|
+
if (joinCookieRaw) {
|
|
188
|
+
try {
|
|
189
|
+
joinCookie = JSON.parse(joinCookieRaw);
|
|
190
|
+
} catch (e) {
|
|
191
|
+
LoggerProxy.logger.error(
|
|
192
|
+
`MeetingRequest#constructor --> Error in parsing join cookie data: ${e}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return joinCookie;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Returns the reachability report that needs to be attached to the ROAP messages
|
|
202
|
+
* that we send to the backend.
|
|
203
|
+
*
|
|
204
|
+
* @returns {Promise<Object>}
|
|
205
|
+
*/
|
|
206
|
+
async getReachabilityReport(): Promise<
|
|
207
|
+
| {
|
|
208
|
+
joinCookie: any;
|
|
209
|
+
reachability?: ReachabilityReportV1;
|
|
210
|
+
}
|
|
211
|
+
| {
|
|
212
|
+
reachability: ReachabilityReportV0;
|
|
213
|
+
}
|
|
214
|
+
> {
|
|
215
|
+
const reachabilityResult = await this.getReachabilityResults();
|
|
216
|
+
const joinCookie = await this.getJoinCookie();
|
|
217
|
+
|
|
218
|
+
// Orpheus API version 0
|
|
219
|
+
if (!this.orpheusApiVersion) {
|
|
220
|
+
return {
|
|
221
|
+
reachability: reachabilityResult,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Orpheus API version 1
|
|
226
|
+
return {
|
|
227
|
+
reachability: {
|
|
228
|
+
version: 1,
|
|
229
|
+
result: {
|
|
230
|
+
usedDiscoveryOptions: {
|
|
231
|
+
'early-call-min-clusters': this.minRequiredClusters,
|
|
232
|
+
},
|
|
233
|
+
metrics: {
|
|
234
|
+
'total-duration-ms': this.totalDuration,
|
|
235
|
+
},
|
|
236
|
+
tests: reachabilityResult,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
joinCookie,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* This method is called when we don't succeed in reaching the minimum number of clusters
|
|
245
|
+
* required by Orpheus. It sends the results to Orpheus and gets a new list that it tries to reach again.
|
|
246
|
+
* @returns {Promise<ReachabilityResults>} reachability results
|
|
247
|
+
* @public
|
|
248
|
+
* @memberof Reachability
|
|
249
|
+
*/
|
|
250
|
+
public async gatherReachabilityFallback(): Promise<void> {
|
|
251
|
+
try {
|
|
252
|
+
const reachabilityReport = await this.getReachabilityReport();
|
|
253
|
+
|
|
254
|
+
const {clusters} = await this.getClusters('early-call/no-min-reached', reachabilityReport);
|
|
255
|
+
|
|
256
|
+
// stop all previous reachability checks that might still be going on in the background
|
|
257
|
+
this.abortCurrentChecks();
|
|
258
|
+
|
|
259
|
+
// Perform Reachability Check
|
|
260
|
+
await this.performReachabilityChecks(clusters);
|
|
261
|
+
} catch (error) {
|
|
262
|
+
LoggerProxy.logger.error(`Reachability:index#gatherReachabilityFallback --> Error:`, error);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Stops all reachability checks that are in progress
|
|
268
|
+
* @public
|
|
269
|
+
* @memberof Reachability
|
|
270
|
+
* @returns {void}
|
|
271
|
+
*/
|
|
272
|
+
public stopReachability() {
|
|
273
|
+
// overallTimer is always there only if there is reachability in progress
|
|
274
|
+
if (this.overallTimer) {
|
|
275
|
+
LoggerProxy.logger.log(
|
|
276
|
+
'Reachability:index#stopReachability --> stopping reachability checks'
|
|
277
|
+
);
|
|
278
|
+
this.abortCurrentChecks();
|
|
279
|
+
this.emit(
|
|
280
|
+
{
|
|
281
|
+
file: 'reachability',
|
|
282
|
+
function: 'stopReachability',
|
|
283
|
+
},
|
|
284
|
+
'reachability:stopped',
|
|
285
|
+
{}
|
|
286
|
+
);
|
|
287
|
+
this.sendMetric(true);
|
|
288
|
+
this.resolveReachabilityPromise();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
184
292
|
/**
|
|
185
293
|
* Returns statistics about last reachability results. The returned value is an object
|
|
186
294
|
* with a flat list of properties so that it can be easily sent with metrics
|
|
@@ -304,7 +412,7 @@ export default class Reachability extends EventsScope {
|
|
|
304
412
|
} catch (e) {
|
|
305
413
|
// empty storage, that's ok
|
|
306
414
|
LoggerProxy.logger.warn(
|
|
307
|
-
'
|
|
415
|
+
'Reachability:index#getReachabilityResults --> Error parsing reachability data: ',
|
|
308
416
|
e
|
|
309
417
|
);
|
|
310
418
|
}
|
|
@@ -336,7 +444,7 @@ export default class Reachability extends EventsScope {
|
|
|
336
444
|
);
|
|
337
445
|
} catch (e) {
|
|
338
446
|
LoggerProxy.logger.error(
|
|
339
|
-
`
|
|
447
|
+
`Reachability:index#isAnyPublicClusterReachable --> Error in parsing reachability data: ${e}`
|
|
340
448
|
);
|
|
341
449
|
}
|
|
342
450
|
}
|
|
@@ -393,7 +501,7 @@ export default class Reachability extends EventsScope {
|
|
|
393
501
|
);
|
|
394
502
|
} catch (e) {
|
|
395
503
|
LoggerProxy.logger.error(
|
|
396
|
-
`
|
|
504
|
+
`Reachability:index#isWebexMediaBackendUnreachable --> Error in parsing reachability data: ${e}`
|
|
397
505
|
);
|
|
398
506
|
}
|
|
399
507
|
}
|
|
@@ -427,6 +535,30 @@ export default class Reachability extends EventsScope {
|
|
|
427
535
|
return unreachableList;
|
|
428
536
|
}
|
|
429
537
|
|
|
538
|
+
/**
|
|
539
|
+
* Gets the number of reachable clusters from last run reachability check
|
|
540
|
+
* @returns {number} reachable clusters count
|
|
541
|
+
* @private
|
|
542
|
+
* @memberof Reachability
|
|
543
|
+
*/
|
|
544
|
+
private getNumberOfReachableClusters(): number {
|
|
545
|
+
let count = 0;
|
|
546
|
+
|
|
547
|
+
Object.entries(this.clusterReachability).forEach(([key, clusterReachability]) => {
|
|
548
|
+
const result = clusterReachability.getResult();
|
|
549
|
+
|
|
550
|
+
if (
|
|
551
|
+
result.udp.result === 'reachable' ||
|
|
552
|
+
result.tcp.result === 'reachable' ||
|
|
553
|
+
result.xtls.result === 'reachable'
|
|
554
|
+
) {
|
|
555
|
+
count += 1;
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
return count;
|
|
560
|
+
}
|
|
561
|
+
|
|
430
562
|
/**
|
|
431
563
|
* Make a log of unreachable clusters.
|
|
432
564
|
* @returns {undefined}
|
|
@@ -465,18 +597,27 @@ export default class Reachability extends EventsScope {
|
|
|
465
597
|
|
|
466
598
|
/**
|
|
467
599
|
* Resolves the promise returned by gatherReachability() method
|
|
600
|
+
* @param {boolean} checkMinRequiredClusters - if true, it will check if we have reached the minimum required clusters and do a fallback if needed
|
|
468
601
|
* @returns {void}
|
|
469
602
|
*/
|
|
470
|
-
private resolveReachabilityPromise() {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
clearTimeout(this.publicCloudTimer);
|
|
476
|
-
}
|
|
603
|
+
private resolveReachabilityPromise(checkMinRequiredClusters = true) {
|
|
604
|
+
this.totalDuration = performance.now() - this.startTime;
|
|
605
|
+
|
|
606
|
+
this.clearTimer('vmnTimer');
|
|
607
|
+
this.clearTimer('publicCloudTimer');
|
|
477
608
|
|
|
478
609
|
this.logUnreachableClusters();
|
|
479
610
|
this.reachabilityDefer?.resolve();
|
|
611
|
+
|
|
612
|
+
if (checkMinRequiredClusters) {
|
|
613
|
+
const numReachableClusters = this.getNumberOfReachableClusters();
|
|
614
|
+
if (this.minRequiredClusters && numReachableClusters < this.minRequiredClusters) {
|
|
615
|
+
LoggerProxy.logger.log(
|
|
616
|
+
`Reachability:index#resolveReachabilityPromise --> minRequiredClusters not reached (${numReachableClusters} < ${this.minRequiredClusters}), doing reachability fallback`
|
|
617
|
+
);
|
|
618
|
+
this.gatherReachabilityFallback();
|
|
619
|
+
}
|
|
620
|
+
}
|
|
480
621
|
}
|
|
481
622
|
|
|
482
623
|
/**
|
|
@@ -526,9 +667,10 @@ export default class Reachability extends EventsScope {
|
|
|
526
667
|
/**
|
|
527
668
|
* Sends a metric with all the statistics about how long reachability took
|
|
528
669
|
*
|
|
670
|
+
* @param {boolean} aborted true if the reachability checks were aborted
|
|
529
671
|
* @returns {void}
|
|
530
672
|
*/
|
|
531
|
-
protected async sendMetric() {
|
|
673
|
+
protected async sendMetric(aborted = false) {
|
|
532
674
|
const results = [];
|
|
533
675
|
|
|
534
676
|
Object.values(this.clusterReachability).forEach((clusterReachability) => {
|
|
@@ -539,6 +681,7 @@ export default class Reachability extends EventsScope {
|
|
|
539
681
|
});
|
|
540
682
|
|
|
541
683
|
const stats = {
|
|
684
|
+
aborted,
|
|
542
685
|
vmn: {
|
|
543
686
|
udp: this.getStatistics(results, 'udp', true),
|
|
544
687
|
},
|
|
@@ -591,6 +734,8 @@ export default class Reachability extends EventsScope {
|
|
|
591
734
|
`Reachability:index#startTimers --> Reachability checks timed out (${DEFAULT_TIMEOUT}s)`
|
|
592
735
|
);
|
|
593
736
|
|
|
737
|
+
// check against minimum required clusters, do a new call if we don't have enough
|
|
738
|
+
|
|
594
739
|
// resolve the promise, so that the client won't be blocked waiting on meetings.register() for too long
|
|
595
740
|
this.resolveReachabilityPromise();
|
|
596
741
|
}, DEFAULT_TIMEOUT * 1000);
|
|
@@ -646,6 +791,32 @@ export default class Reachability extends EventsScope {
|
|
|
646
791
|
this.resultsCount.public.xtls = 0;
|
|
647
792
|
}
|
|
648
793
|
|
|
794
|
+
/**
|
|
795
|
+
* Clears the timer
|
|
796
|
+
*
|
|
797
|
+
* @param {string} timer name of the timer to clear
|
|
798
|
+
* @returns {void}
|
|
799
|
+
*/
|
|
800
|
+
private clearTimer(timer: string) {
|
|
801
|
+
if (this[timer]) {
|
|
802
|
+
clearTimeout(this[timer]);
|
|
803
|
+
this[timer] = undefined;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Aborts current checks that are in progress
|
|
809
|
+
*
|
|
810
|
+
* @returns {void}
|
|
811
|
+
*/
|
|
812
|
+
private abortCurrentChecks() {
|
|
813
|
+
this.clearTimer('vmnTimer');
|
|
814
|
+
this.clearTimer('publicCloudTimer');
|
|
815
|
+
this.clearTimer('overallTimer');
|
|
816
|
+
|
|
817
|
+
this.abortClusterReachability();
|
|
818
|
+
}
|
|
819
|
+
|
|
649
820
|
/**
|
|
650
821
|
* Performs reachability checks for all clusters
|
|
651
822
|
* @param {ClusterList} clusterList
|
|
@@ -656,9 +827,7 @@ export default class Reachability extends EventsScope {
|
|
|
656
827
|
|
|
657
828
|
this.clusterReachability = {};
|
|
658
829
|
|
|
659
|
-
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
830
|
+
this.startTime = performance.now();
|
|
662
831
|
|
|
663
832
|
LoggerProxy.logger.log(
|
|
664
833
|
`Reachability:index#performReachabilityChecks --> doing UDP${
|
|
@@ -671,7 +840,6 @@ export default class Reachability extends EventsScope {
|
|
|
671
840
|
);
|
|
672
841
|
|
|
673
842
|
this.resetResultCounters();
|
|
674
|
-
this.startTimers();
|
|
675
843
|
|
|
676
844
|
// sanitize the urls in the clusterList
|
|
677
845
|
Object.keys(clusterList).forEach((key) => {
|
|
@@ -721,6 +889,24 @@ export default class Reachability extends EventsScope {
|
|
|
721
889
|
// save the initialized results (in case we don't get any "resultReady" events at all)
|
|
722
890
|
await this.storeResults(results);
|
|
723
891
|
|
|
892
|
+
if (!clusterList || !Object.keys(clusterList).length) {
|
|
893
|
+
// nothing to do, finish immediately
|
|
894
|
+
this.resolveReachabilityPromise(false);
|
|
895
|
+
|
|
896
|
+
this.emit(
|
|
897
|
+
{
|
|
898
|
+
file: 'reachability',
|
|
899
|
+
function: 'performReachabilityChecks',
|
|
900
|
+
},
|
|
901
|
+
'reachability:done',
|
|
902
|
+
{}
|
|
903
|
+
);
|
|
904
|
+
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
this.startTimers();
|
|
909
|
+
|
|
724
910
|
// now start the reachability on all the clusters
|
|
725
911
|
Object.keys(clusterList).forEach((key) => {
|
|
726
912
|
const cluster = clusterList[key];
|
|
@@ -753,8 +939,7 @@ export default class Reachability extends EventsScope {
|
|
|
753
939
|
await this.storeResults(results);
|
|
754
940
|
|
|
755
941
|
if (areAllResultsReady) {
|
|
756
|
-
|
|
757
|
-
this.overallTimer = undefined;
|
|
942
|
+
this.clearTimer('overallTimer');
|
|
758
943
|
this.emit(
|
|
759
944
|
{
|
|
760
945
|
file: 'reachability',
|
|
@@ -785,4 +970,59 @@ export default class Reachability extends EventsScope {
|
|
|
785
970
|
this.clusterReachability[key].start(); // not awaiting on purpose
|
|
786
971
|
});
|
|
787
972
|
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Returns the clientMediaPreferences object that needs to be sent to the backend
|
|
976
|
+
* when joining a meeting
|
|
977
|
+
*
|
|
978
|
+
* @param {boolean} isMultistream
|
|
979
|
+
* @param {IP_VERSION} ipver
|
|
980
|
+
* @returns {Object}
|
|
981
|
+
*/
|
|
982
|
+
async getClientMediaPreferences(
|
|
983
|
+
isMultistream: boolean,
|
|
984
|
+
ipver?: IP_VERSION
|
|
985
|
+
): Promise<ClientMediaPreferences> {
|
|
986
|
+
// if 0 or undefined, we assume version 0 and don't send any reachability in clientMediaPreferences
|
|
987
|
+
if (!this.orpheusApiVersion) {
|
|
988
|
+
return {
|
|
989
|
+
ipver,
|
|
990
|
+
joinCookie: await this.getJoinCookie(),
|
|
991
|
+
preferTranscoding: !isMultistream,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// must be version 1
|
|
996
|
+
|
|
997
|
+
// for version 1, the reachability report goes into clientMediaPreferences (and it contains joinCookie)
|
|
998
|
+
const reachabilityReport = (await this.getReachabilityReport()) as {
|
|
999
|
+
joinCookie: any;
|
|
1000
|
+
reachability?: ReachabilityReportV1;
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
return {
|
|
1004
|
+
ipver,
|
|
1005
|
+
preferTranscoding: !isMultistream,
|
|
1006
|
+
...reachabilityReport,
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Returns the reachability report that needs to be attached to the ROAP messages
|
|
1012
|
+
* that we send to the backend.
|
|
1013
|
+
* It may return undefined, if reachability is not needed to be attached to ROAP messages (that's the case for v1 or Orpheus API)
|
|
1014
|
+
*
|
|
1015
|
+
* @returns {Promise<ReachabilityReportV0>} object that needs to be attached to Roap messages
|
|
1016
|
+
*/
|
|
1017
|
+
async getReachabilityReportToAttachToRoap(): Promise<ReachabilityReportV0 | undefined> {
|
|
1018
|
+
// version 0
|
|
1019
|
+
if (!this.orpheusApiVersion) {
|
|
1020
|
+
return this.getReachabilityResults();
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// version 1
|
|
1024
|
+
|
|
1025
|
+
// for version 1 we don't attach anything to Roap messages, reachability report is sent inside clientMediaPreferences
|
|
1026
|
+
return undefined;
|
|
1027
|
+
}
|
|
788
1028
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {IP_VERSION} from '../constants';
|
|
2
|
+
|
|
3
|
+
// result for a specific transport protocol (like udp or tcp)
|
|
4
|
+
export type TransportResult = {
|
|
5
|
+
result: 'reachable' | 'unreachable' | 'untested';
|
|
6
|
+
latencyInMilliseconds?: number; // amount of time it took to get the first ICE candidate
|
|
7
|
+
clientMediaIPs?: string[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// reachability result for a specific media cluster
|
|
11
|
+
export type ClusterReachabilityResult = {
|
|
12
|
+
udp: TransportResult;
|
|
13
|
+
tcp: TransportResult;
|
|
14
|
+
xtls: TransportResult;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ReachabilityMetrics = {
|
|
18
|
+
reachability_public_udp_success: number;
|
|
19
|
+
reachability_public_udp_failed: number;
|
|
20
|
+
reachability_public_tcp_success: number;
|
|
21
|
+
reachability_public_tcp_failed: number;
|
|
22
|
+
reachability_public_xtls_success: number;
|
|
23
|
+
reachability_public_xtls_failed: number;
|
|
24
|
+
reachability_vmn_udp_success: number;
|
|
25
|
+
reachability_vmn_udp_failed: number;
|
|
26
|
+
reachability_vmn_tcp_success: number;
|
|
27
|
+
reachability_vmn_tcp_failed: number;
|
|
28
|
+
reachability_vmn_xtls_success: number;
|
|
29
|
+
reachability_vmn_xtls_failed: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* This is the type that matches what backend expects us to send to them. It is a bit weird, because
|
|
34
|
+
* it uses strings instead of booleans and numbers, but that's what they require.
|
|
35
|
+
*/
|
|
36
|
+
export type TransportResultForBackend = {
|
|
37
|
+
reachable?: 'true' | 'false';
|
|
38
|
+
latencyInMilliseconds?: string;
|
|
39
|
+
clientMediaIPs?: string[];
|
|
40
|
+
untested?: 'true';
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type ReachabilityResultForBackend = {
|
|
44
|
+
udp: TransportResultForBackend;
|
|
45
|
+
tcp: TransportResultForBackend;
|
|
46
|
+
xtls: TransportResultForBackend;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// this is the type that is required by the backend when we send them reachability results
|
|
50
|
+
export type ReachabilityResultsForBackend = Record<string, ReachabilityResultForBackend>;
|
|
51
|
+
|
|
52
|
+
// this is the type used by Reachability class internally and stored in local storage
|
|
53
|
+
export type ReachabilityResults = Record<
|
|
54
|
+
string,
|
|
55
|
+
ClusterReachabilityResult & {
|
|
56
|
+
isVideoMesh?: boolean;
|
|
57
|
+
}
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
export type ReachabilityReportV0 = ReachabilityResultsForBackend;
|
|
61
|
+
|
|
62
|
+
export type ReachabilityReportV1 = {
|
|
63
|
+
version: 1;
|
|
64
|
+
result: {
|
|
65
|
+
usedDiscoveryOptions: {
|
|
66
|
+
'early-call-min-clusters': number;
|
|
67
|
+
// there are more options, but we don't support them yet
|
|
68
|
+
};
|
|
69
|
+
metrics: {
|
|
70
|
+
'total-duration-ms': number;
|
|
71
|
+
// there are more metrics, but we don't support them yet
|
|
72
|
+
};
|
|
73
|
+
tests: Record<string, ReachabilityResultForBackend>;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export interface ClientMediaPreferences {
|
|
78
|
+
ipver: IP_VERSION;
|
|
79
|
+
joinCookie: any;
|
|
80
|
+
preferTranscoding: boolean;
|
|
81
|
+
reachability?: ReachabilityReportV1; // only present when using Orpheus API version 1
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Orpheus API supports more triggers, but we don't use them yet */
|
|
85
|
+
export type GetClustersTrigger = 'startup' | 'early-call/no-min-reached';
|