@webex/plugin-meetings 3.3.0 → 3.3.1-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 +1 -1
- package/dist/constants.js +4 -2
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/mediaQualityMetrics/config.js +20 -16
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/index.js +30 -13
- package/dist/meeting/index.js.map +1 -1
- package/dist/meetings/index.js +6 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/reachability/index.js +82 -9
- package/dist/reachability/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +77 -27
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +46 -7
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/constants.d.ts +2 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +14 -2
- package/dist/types/meeting/index.d.ts +8 -0
- package/dist/types/reachability/index.d.ts +11 -0
- package/dist/types/statsAnalyzer/index.d.ts +14 -6
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +17 -4
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/constants.ts +2 -1
- package/src/mediaQualityMetrics/config.ts +22 -10
- package/src/meeting/index.ts +29 -14
- package/src/meetings/index.ts +7 -2
- package/src/reachability/index.ts +57 -0
- package/src/statsAnalyzer/index.ts +82 -22
- package/src/statsAnalyzer/mqaUtil.ts +68 -4
- package/test/unit/spec/meeting/index.js +28 -8
- package/test/unit/spec/meetings/index.js +38 -15
- package/test/unit/spec/reachability/index.ts +266 -0
- package/test/unit/spec/stats-analyzer/index.js +630 -314
package/package.json
CHANGED
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
"@webex/eslint-config-legacy": "0.0.0",
|
|
44
44
|
"@webex/jest-config-legacy": "0.0.0",
|
|
45
45
|
"@webex/legacy-tools": "0.0.0",
|
|
46
|
-
"@webex/plugin-meetings": "3.3.
|
|
47
|
-
"@webex/plugin-rooms": "3.3.
|
|
48
|
-
"@webex/test-helper-chai": "3.3.
|
|
49
|
-
"@webex/test-helper-mocha": "3.3.
|
|
50
|
-
"@webex/test-helper-mock-webex": "3.3.
|
|
51
|
-
"@webex/test-helper-retry": "3.3.
|
|
52
|
-
"@webex/test-helper-test-users": "3.3.
|
|
46
|
+
"@webex/plugin-meetings": "3.3.1-next.10",
|
|
47
|
+
"@webex/plugin-rooms": "3.3.1-next.3",
|
|
48
|
+
"@webex/test-helper-chai": "3.3.1-next.2",
|
|
49
|
+
"@webex/test-helper-mocha": "3.3.1-next.2",
|
|
50
|
+
"@webex/test-helper-mock-webex": "3.3.1-next.2",
|
|
51
|
+
"@webex/test-helper-retry": "3.3.1-next.2",
|
|
52
|
+
"@webex/test-helper-test-users": "3.3.1-next.2",
|
|
53
53
|
"chai": "^4.3.4",
|
|
54
54
|
"chai-as-promised": "^7.1.1",
|
|
55
55
|
"eslint": "^8.24.0",
|
|
@@ -61,20 +61,20 @@
|
|
|
61
61
|
"typescript": "^4.7.4"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@webex/common": "3.3.
|
|
65
|
-
"@webex/internal-media-core": "2.5.
|
|
66
|
-
"@webex/internal-plugin-conversation": "3.3.
|
|
67
|
-
"@webex/internal-plugin-device": "3.3.
|
|
68
|
-
"@webex/internal-plugin-llm": "3.3.
|
|
69
|
-
"@webex/internal-plugin-mercury": "3.3.
|
|
70
|
-
"@webex/internal-plugin-metrics": "3.3.
|
|
71
|
-
"@webex/internal-plugin-support": "3.3.
|
|
72
|
-
"@webex/internal-plugin-user": "3.3.
|
|
73
|
-
"@webex/internal-plugin-voicea": "3.3.
|
|
74
|
-
"@webex/media-helpers": "3.3.
|
|
75
|
-
"@webex/plugin-people": "3.3.
|
|
76
|
-
"@webex/plugin-rooms": "3.3.
|
|
77
|
-
"@webex/webex-core": "3.3.
|
|
64
|
+
"@webex/common": "3.3.1-next.2",
|
|
65
|
+
"@webex/internal-media-core": "2.5.3",
|
|
66
|
+
"@webex/internal-plugin-conversation": "3.3.1-next.3",
|
|
67
|
+
"@webex/internal-plugin-device": "3.3.1-next.2",
|
|
68
|
+
"@webex/internal-plugin-llm": "3.3.1-next.3",
|
|
69
|
+
"@webex/internal-plugin-mercury": "3.3.1-next.3",
|
|
70
|
+
"@webex/internal-plugin-metrics": "3.3.1-next.2",
|
|
71
|
+
"@webex/internal-plugin-support": "3.3.1-next.3",
|
|
72
|
+
"@webex/internal-plugin-user": "3.3.1-next.2",
|
|
73
|
+
"@webex/internal-plugin-voicea": "3.3.1-next.10",
|
|
74
|
+
"@webex/media-helpers": "3.3.1-next.4",
|
|
75
|
+
"@webex/plugin-people": "3.3.1-next.3",
|
|
76
|
+
"@webex/plugin-rooms": "3.3.1-next.3",
|
|
77
|
+
"@webex/webex-core": "3.3.1-next.2",
|
|
78
78
|
"ampersand-collection": "^2.0.2",
|
|
79
79
|
"bowser": "^2.11.0",
|
|
80
80
|
"btoa": "^1.2.1",
|
|
@@ -91,5 +91,5 @@
|
|
|
91
91
|
"//": [
|
|
92
92
|
"TODO: upgrade jwt-decode when moving to node 18"
|
|
93
93
|
],
|
|
94
|
-
"version": "3.3.
|
|
94
|
+
"version": "3.3.1-next.10"
|
|
95
95
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1122,6 +1122,8 @@ export const MQA_STATS = {
|
|
|
1122
1122
|
direction: 'sendrecv', // TODO: parse from SDP and save globally
|
|
1123
1123
|
isMain: false, // always true for share sender
|
|
1124
1124
|
mariFecEnabled: false, // unavailable
|
|
1125
|
+
mariRtxEnabled: false, // unavailable
|
|
1126
|
+
mariLiteEnabled: false, // unavailable
|
|
1125
1127
|
mariQosEnabled: false, // unavailable
|
|
1126
1128
|
multistreamEnabled: false, // unavailable
|
|
1127
1129
|
},
|
|
@@ -1134,7 +1136,6 @@ export const MQA_STATS = {
|
|
|
1134
1136
|
queueDelay: 0, // unavailable
|
|
1135
1137
|
remoteJitter: 0, // unavailable
|
|
1136
1138
|
remoteLossRate: 0,
|
|
1137
|
-
remoteReceiveRate: 0, // unavailable
|
|
1138
1139
|
roundTripTime: 0,
|
|
1139
1140
|
rtcpBitrate: 0, // unavailable
|
|
1140
1141
|
rtcpPackets: 0, // unavailable
|
|
@@ -8,6 +8,12 @@ export const emptyMqaInterval = {
|
|
|
8
8
|
processMaximumCPU: 0,
|
|
9
9
|
systemAverageCPU: 0,
|
|
10
10
|
systemMaximumCPU: 0,
|
|
11
|
+
screenWidth: 0,
|
|
12
|
+
screenHeight: 0,
|
|
13
|
+
screenResolution: 0,
|
|
14
|
+
appWindowWidth: 0,
|
|
15
|
+
appWindowHeight: 0,
|
|
16
|
+
appWindowSize: 0,
|
|
11
17
|
},
|
|
12
18
|
networkType: '',
|
|
13
19
|
intervalNumber: 0,
|
|
@@ -21,7 +27,9 @@ export const emptyAudioReceive = {
|
|
|
21
27
|
direction: 'inactive',
|
|
22
28
|
isMain: true,
|
|
23
29
|
mariFecEnabled: false,
|
|
30
|
+
mariRtxEnabled: false,
|
|
24
31
|
mariQosEnabled: false,
|
|
32
|
+
mariLiteEnabled: false,
|
|
25
33
|
multistreamEnabled: false,
|
|
26
34
|
},
|
|
27
35
|
dtlsBitrate: 0,
|
|
@@ -75,7 +83,9 @@ export const emptyAudioTransmit = {
|
|
|
75
83
|
direction: 'inactive',
|
|
76
84
|
isMain: true,
|
|
77
85
|
mariFecEnabled: false,
|
|
86
|
+
mariRtxEnabled: false,
|
|
78
87
|
mariQosEnabled: false,
|
|
88
|
+
mariLiteEnabled: false,
|
|
79
89
|
multistreamEnabled: false,
|
|
80
90
|
},
|
|
81
91
|
dtlsBitrate: 0,
|
|
@@ -86,7 +96,6 @@ export const emptyAudioTransmit = {
|
|
|
86
96
|
queueDelay: 0,
|
|
87
97
|
remoteJitter: 0,
|
|
88
98
|
remoteLossRate: 0,
|
|
89
|
-
remoteReceiveRate: 0,
|
|
90
99
|
roundTripTime: 0,
|
|
91
100
|
rtcpBitrate: 0,
|
|
92
101
|
rtcpPackets: 0,
|
|
@@ -119,9 +128,11 @@ export const emptyVideoReceive = {
|
|
|
119
128
|
common: {
|
|
120
129
|
direction: 'inactive',
|
|
121
130
|
isMain: true, // Not avaliable
|
|
122
|
-
mariFecEnabled:
|
|
123
|
-
|
|
124
|
-
|
|
131
|
+
mariFecEnabled: false,
|
|
132
|
+
mariRtxEnabled: false,
|
|
133
|
+
mariQosEnabled: false,
|
|
134
|
+
mariLiteEnabled: false,
|
|
135
|
+
multistreamEnabled: false,
|
|
125
136
|
},
|
|
126
137
|
dtlsBitrate: 0, // Not avaliable
|
|
127
138
|
dtlsPackets: 0, // Not avaliable
|
|
@@ -165,7 +176,7 @@ export const emptyVideoReceiveStream = {
|
|
|
165
176
|
ssci: 0, // Not avaliable
|
|
166
177
|
},
|
|
167
178
|
h264CodecProfile: 'BP',
|
|
168
|
-
isActiveSpeaker:
|
|
179
|
+
isActiveSpeaker: false,
|
|
169
180
|
optimalFrameSize: 0, // Not avaliable
|
|
170
181
|
receivedFrameSize: 0,
|
|
171
182
|
receivedHeight: 0,
|
|
@@ -184,19 +195,20 @@ export const emptyVideoTransmit = {
|
|
|
184
195
|
common: {
|
|
185
196
|
direction: 'inactive',
|
|
186
197
|
isMain: true,
|
|
187
|
-
mariFecEnabled: false,
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
mariFecEnabled: false,
|
|
199
|
+
mariRtxEnabled: false,
|
|
200
|
+
mariQosEnabled: false,
|
|
201
|
+
mariLiteEnabled: false,
|
|
202
|
+
multistreamEnabled: false,
|
|
190
203
|
},
|
|
191
204
|
dtlsBitrate: 0, // Not avaliable
|
|
192
205
|
dtlsPackets: 0, // Not avaliable
|
|
193
206
|
fecBitrate: 0, // Not avaliable
|
|
194
207
|
fecPackets: 0, // TODO: check inbound-rtp// Not avaliable
|
|
195
208
|
maxBitrate: 0, // Currently hardcoded
|
|
196
|
-
queueDelay: 0,
|
|
209
|
+
queueDelay: 0,
|
|
197
210
|
remoteJitter: 0, // remoteInboundRtp.jitter
|
|
198
211
|
remoteLossRate: 0, // comparedResults.lossRate
|
|
199
|
-
remoteReceiveRate: 0, // compareResults.packetsLost
|
|
200
212
|
roundTripTime: 0, // compareResults.roundTripTime
|
|
201
213
|
rtcpBitrate: 0, // Dont have access to it
|
|
202
214
|
rtcpPackets: 0, // Dont have access to rtcp
|
package/src/meeting/index.ts
CHANGED
|
@@ -4953,6 +4953,27 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4953
4953
|
);
|
|
4954
4954
|
}
|
|
4955
4955
|
|
|
4956
|
+
/**
|
|
4957
|
+
* This is a callback for the LLM event that is triggered when it comes online
|
|
4958
|
+
* This method in turn will trigger an event to the developers that the LLM is connected
|
|
4959
|
+
* @private
|
|
4960
|
+
* @memberof Meeting
|
|
4961
|
+
* @returns {null}
|
|
4962
|
+
*/
|
|
4963
|
+
private handleLLMOnline = (): void => {
|
|
4964
|
+
// @ts-ignore
|
|
4965
|
+
this.webex.internal.llm.off('online', this.handleLLMOnline);
|
|
4966
|
+
Trigger.trigger(
|
|
4967
|
+
this,
|
|
4968
|
+
{
|
|
4969
|
+
file: 'meeting/index',
|
|
4970
|
+
function: 'handleLLMOnline',
|
|
4971
|
+
},
|
|
4972
|
+
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED,
|
|
4973
|
+
undefined
|
|
4974
|
+
);
|
|
4975
|
+
};
|
|
4976
|
+
|
|
4956
4977
|
/**
|
|
4957
4978
|
* Specify joining via audio (option: pstn), video, screenshare
|
|
4958
4979
|
* @param {JoinOptions} options A configurable options object for joining a meeting
|
|
@@ -5172,6 +5193,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5172
5193
|
.then((join) => {
|
|
5173
5194
|
// @ts-ignore - config coming from registerPlugin
|
|
5174
5195
|
if (this.config.enableAutomaticLLM) {
|
|
5196
|
+
// @ts-ignore
|
|
5197
|
+
this.webex.internal.llm.on('online', this.handleLLMOnline);
|
|
5175
5198
|
this.updateLLMConnection()
|
|
5176
5199
|
.catch((error) => {
|
|
5177
5200
|
LoggerProxy.logger.error(
|
|
@@ -5189,15 +5212,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5189
5212
|
LoggerProxy.logger.info(
|
|
5190
5213
|
'Meeting:index#join --> Transcription Socket Connection Success'
|
|
5191
5214
|
);
|
|
5192
|
-
Trigger.trigger(
|
|
5193
|
-
this,
|
|
5194
|
-
{
|
|
5195
|
-
file: 'meeting/index',
|
|
5196
|
-
function: 'join',
|
|
5197
|
-
},
|
|
5198
|
-
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED,
|
|
5199
|
-
undefined
|
|
5200
|
-
);
|
|
5201
5215
|
});
|
|
5202
5216
|
}
|
|
5203
5217
|
|
|
@@ -6303,12 +6317,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6303
6317
|
if (this.config.stats.enableStatsAnalyzer) {
|
|
6304
6318
|
// @ts-ignore - config coming from registerPlugin
|
|
6305
6319
|
this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
|
|
6306
|
-
this.statsAnalyzer = new StatsAnalyzer(
|
|
6320
|
+
this.statsAnalyzer = new StatsAnalyzer({
|
|
6307
6321
|
// @ts-ignore - config coming from registerPlugin
|
|
6308
|
-
this.config.stats,
|
|
6309
|
-
(ssrc: number) => this.receiveSlotManager.findReceiveSlotBySsrc(ssrc),
|
|
6310
|
-
this.networkQualityMonitor
|
|
6311
|
-
|
|
6322
|
+
config: this.config.stats,
|
|
6323
|
+
receiveSlotCallback: (ssrc: number) => this.receiveSlotManager.findReceiveSlotBySsrc(ssrc),
|
|
6324
|
+
networkQualityMonitor: this.networkQualityMonitor,
|
|
6325
|
+
isMultistream: this.isMultistream,
|
|
6326
|
+
});
|
|
6312
6327
|
this.setupStatsAnalyzerEventHandlers();
|
|
6313
6328
|
this.networkQualityMonitor.on(
|
|
6314
6329
|
EVENT_TRIGGERS.NETWORK_QUALITY,
|
package/src/meetings/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
|
|
2
|
-
|
|
2
|
+
import {union} from 'lodash';
|
|
3
3
|
import '@webex/internal-plugin-mercury';
|
|
4
4
|
import '@webex/internal-plugin-conversation';
|
|
5
5
|
import '@webex/internal-plugin-metrics';
|
|
@@ -1001,7 +1001,10 @@ export default class Meetings extends WebexPlugin {
|
|
|
1001
1001
|
fetchUserPreferredWebexSite() {
|
|
1002
1002
|
return this.request.getMeetingPreferences().then((res) => {
|
|
1003
1003
|
if (res) {
|
|
1004
|
-
|
|
1004
|
+
const preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
|
|
1005
|
+
this.preferredWebexSite = preferredWebexSite;
|
|
1006
|
+
// @ts-ignore
|
|
1007
|
+
this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]);
|
|
1005
1008
|
}
|
|
1006
1009
|
|
|
1007
1010
|
// fall back to getting the preferred site from the user information
|
|
@@ -1014,6 +1017,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1014
1017
|
user?.userPreferences?.userPreferencesItems?.preferredWebExSite;
|
|
1015
1018
|
if (preferredWebexSite) {
|
|
1016
1019
|
this.preferredWebexSite = preferredWebexSite;
|
|
1020
|
+
// @ts-ignore
|
|
1021
|
+
this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]);
|
|
1017
1022
|
} else {
|
|
1018
1023
|
throw new Error('site not found');
|
|
1019
1024
|
}
|
|
@@ -296,6 +296,63 @@ export default class Reachability {
|
|
|
296
296
|
return reachable;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Returns true only if ALL protocols (UDP, TCP and TLS) have been tested and none
|
|
301
|
+
* of the media clusters where reachable with any of the protocols. This is done
|
|
302
|
+
* irrespective of the config, so for example:
|
|
303
|
+
* if config.meetings.experimental.enableTlsReachability === false,
|
|
304
|
+
* it will return false, because TLS reachability won't be tested,
|
|
305
|
+
* so we can't say for sure that media backend is unreachable over TLS.
|
|
306
|
+
*
|
|
307
|
+
* @returns {boolean}
|
|
308
|
+
*/
|
|
309
|
+
async isWebexMediaBackendUnreachable() {
|
|
310
|
+
let unreachable = false;
|
|
311
|
+
|
|
312
|
+
// @ts-ignore
|
|
313
|
+
const reachabilityData = await this.webex.boundedStorage
|
|
314
|
+
.get(this.namespace, REACHABILITY.localStorageResult)
|
|
315
|
+
.catch(() => {});
|
|
316
|
+
|
|
317
|
+
if (reachabilityData) {
|
|
318
|
+
try {
|
|
319
|
+
const reachabilityResults: ReachabilityResults = JSON.parse(reachabilityData);
|
|
320
|
+
|
|
321
|
+
const protocols = {
|
|
322
|
+
udp: {tested: false, reachable: undefined},
|
|
323
|
+
tcp: {tested: false, reachable: undefined},
|
|
324
|
+
xtls: {tested: false, reachable: undefined},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
Object.values(reachabilityResults).forEach((result) => {
|
|
328
|
+
Object.keys(protocols).forEach((protocol) => {
|
|
329
|
+
if (
|
|
330
|
+
result[protocol]?.result === 'reachable' ||
|
|
331
|
+
result[protocol]?.result === 'unreachable'
|
|
332
|
+
) {
|
|
333
|
+
protocols[protocol].tested = true;
|
|
334
|
+
|
|
335
|
+
// we need at least 1 'reachable' result to mark the whole protocol as reachable
|
|
336
|
+
if (result[protocol].result === 'reachable') {
|
|
337
|
+
protocols[protocol].reachable = true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
unreachable = Object.values(protocols).every(
|
|
344
|
+
(protocol) => protocol.tested && !protocol.reachable
|
|
345
|
+
);
|
|
346
|
+
} catch (e) {
|
|
347
|
+
LoggerProxy.logger.error(
|
|
348
|
+
`Roap:request#attachReachabilityData --> Error in parsing reachability data: ${e}`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return unreachable;
|
|
354
|
+
}
|
|
355
|
+
|
|
299
356
|
/**
|
|
300
357
|
* Get list of all unreachable clusters
|
|
301
358
|
* @returns {array} Unreachable clusters
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
getAudioReceiverStreamMqa,
|
|
36
36
|
getVideoSenderStreamMqa,
|
|
37
37
|
getVideoReceiverStreamMqa,
|
|
38
|
+
isStreamRequested,
|
|
38
39
|
} from './mqaUtil';
|
|
39
40
|
import {ReceiveSlot} from '../multistream/receiveSlot';
|
|
40
41
|
|
|
@@ -91,22 +92,31 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
91
92
|
successfulCandidatePair: any;
|
|
92
93
|
localIpAddress: string; // Returns the local IP address for diagnostics. this is the local IP of the interface used for the current media connection a host can have many local Ip Addresses
|
|
93
94
|
receiveSlotCallback: ReceiveSlotCallback;
|
|
95
|
+
isMultistream: boolean;
|
|
94
96
|
|
|
95
97
|
/**
|
|
96
98
|
* Creates a new instance of StatsAnalyzer
|
|
97
99
|
* @constructor
|
|
98
100
|
* @public
|
|
99
|
-
* @param {Object} config SDK Configuration Object
|
|
100
|
-
* @param {Function} receiveSlotCallback Callback used to access receive slots.
|
|
101
|
-
* @param {Object} networkQualityMonitor
|
|
102
|
-
* @param {Object} statsResults Default properties for stats
|
|
101
|
+
* @param {Object} config - SDK Configuration Object
|
|
102
|
+
* @param {Function} receiveSlotCallback - Callback used to access receive slots.
|
|
103
|
+
* @param {Object} networkQualityMonitor - Class for assessing network characteristics (jitter, packetLoss, latency)
|
|
104
|
+
* @param {Object} statsResults - Default properties for stats
|
|
105
|
+
* @param {boolean | undefined} isMultistream - Param indicating if the media connection is multistream or not
|
|
103
106
|
*/
|
|
104
|
-
constructor(
|
|
105
|
-
config
|
|
106
|
-
receiveSlotCallback
|
|
107
|
-
networkQualityMonitor
|
|
108
|
-
statsResults
|
|
109
|
-
|
|
107
|
+
constructor({
|
|
108
|
+
config,
|
|
109
|
+
receiveSlotCallback = () => undefined,
|
|
110
|
+
networkQualityMonitor = {},
|
|
111
|
+
statsResults = defaultStats,
|
|
112
|
+
isMultistream = false,
|
|
113
|
+
}: {
|
|
114
|
+
config: any;
|
|
115
|
+
receiveSlotCallback: ReceiveSlotCallback;
|
|
116
|
+
networkQualityMonitor: any;
|
|
117
|
+
statsResults?: any;
|
|
118
|
+
isMultistream?: boolean;
|
|
119
|
+
}) {
|
|
110
120
|
super();
|
|
111
121
|
this.statsStarted = false;
|
|
112
122
|
this.statsResults = statsResults;
|
|
@@ -120,6 +130,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
120
130
|
this.receiveSlotCallback = receiveSlotCallback;
|
|
121
131
|
this.successfulCandidatePair = {};
|
|
122
132
|
this.localIpAddress = '';
|
|
133
|
+
this.isMultistream = isMultistream;
|
|
123
134
|
}
|
|
124
135
|
|
|
125
136
|
/**
|
|
@@ -203,6 +214,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
203
214
|
statsResults: this.statsResults,
|
|
204
215
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
205
216
|
baseMediaType: 'audio-send',
|
|
217
|
+
isMultistream: this.isMultistream,
|
|
206
218
|
});
|
|
207
219
|
newMqa.audioTransmit.push(audioSender);
|
|
208
220
|
|
|
@@ -211,6 +223,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
211
223
|
statsResults: this.statsResults,
|
|
212
224
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
213
225
|
baseMediaType: 'audio-share-send',
|
|
226
|
+
isMultistream: this.isMultistream,
|
|
214
227
|
});
|
|
215
228
|
newMqa.audioTransmit.push(audioShareSender);
|
|
216
229
|
|
|
@@ -219,6 +232,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
219
232
|
statsResults: this.statsResults,
|
|
220
233
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
221
234
|
baseMediaType: 'audio-recv',
|
|
235
|
+
isMultistream: this.isMultistream,
|
|
222
236
|
});
|
|
223
237
|
newMqa.audioReceive.push(audioReceiver);
|
|
224
238
|
|
|
@@ -227,6 +241,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
227
241
|
statsResults: this.statsResults,
|
|
228
242
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
229
243
|
baseMediaType: 'audio-share-recv',
|
|
244
|
+
isMultistream: this.isMultistream,
|
|
230
245
|
});
|
|
231
246
|
newMqa.audioReceive.push(audioShareReceiver);
|
|
232
247
|
|
|
@@ -235,6 +250,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
235
250
|
statsResults: this.statsResults,
|
|
236
251
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
237
252
|
baseMediaType: 'video-send',
|
|
253
|
+
isMultistream: this.isMultistream,
|
|
238
254
|
});
|
|
239
255
|
newMqa.videoTransmit.push(videoSender);
|
|
240
256
|
|
|
@@ -243,6 +259,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
243
259
|
statsResults: this.statsResults,
|
|
244
260
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
245
261
|
baseMediaType: 'video-share-send',
|
|
262
|
+
isMultistream: this.isMultistream,
|
|
246
263
|
});
|
|
247
264
|
newMqa.videoTransmit.push(videoShareSender);
|
|
248
265
|
|
|
@@ -251,6 +268,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
251
268
|
statsResults: this.statsResults,
|
|
252
269
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
253
270
|
baseMediaType: 'video-recv',
|
|
271
|
+
isMultistream: this.isMultistream,
|
|
254
272
|
});
|
|
255
273
|
newMqa.videoReceive.push(videoReceiver);
|
|
256
274
|
|
|
@@ -259,6 +277,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
259
277
|
statsResults: this.statsResults,
|
|
260
278
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
261
279
|
baseMediaType: 'video-share-recv',
|
|
280
|
+
isMultistream: this.isMultistream,
|
|
262
281
|
});
|
|
263
282
|
newMqa.videoReceive.push(videoShareReceiver);
|
|
264
283
|
|
|
@@ -273,7 +292,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
273
292
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
274
293
|
mediaType,
|
|
275
294
|
});
|
|
276
|
-
|
|
295
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.SEND_DIRECTION)) {
|
|
296
|
+
newMqa.audioTransmit[0].streams.push(audioSenderStream);
|
|
297
|
+
}
|
|
277
298
|
|
|
278
299
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
279
300
|
} else if (mediaType.startsWith('audio-share-send')) {
|
|
@@ -285,7 +306,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
285
306
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
286
307
|
mediaType,
|
|
287
308
|
});
|
|
288
|
-
|
|
309
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.SEND_DIRECTION)) {
|
|
310
|
+
newMqa.audioTransmit[1].streams.push(audioSenderStream);
|
|
311
|
+
}
|
|
289
312
|
|
|
290
313
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
291
314
|
} else if (mediaType.startsWith('audio-recv')) {
|
|
@@ -297,7 +320,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
297
320
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
298
321
|
mediaType,
|
|
299
322
|
});
|
|
300
|
-
|
|
323
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.RECEIVE_DIRECTION)) {
|
|
324
|
+
newMqa.audioReceive[0].streams.push(audioReceiverStream);
|
|
325
|
+
}
|
|
301
326
|
|
|
302
327
|
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
303
328
|
} else if (mediaType.startsWith('audio-share-recv')) {
|
|
@@ -309,8 +334,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
309
334
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
310
335
|
mediaType,
|
|
311
336
|
});
|
|
312
|
-
|
|
313
|
-
|
|
337
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.RECEIVE_DIRECTION)) {
|
|
338
|
+
newMqa.audioReceive[1].streams.push(audioReceiverStream);
|
|
339
|
+
}
|
|
314
340
|
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
315
341
|
} else if (mediaType.startsWith('video-send-layer')) {
|
|
316
342
|
// We only want the stream-specific stats we get with video-send-layer-0, video-send-layer-1, etc.
|
|
@@ -322,8 +348,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
322
348
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
323
349
|
mediaType,
|
|
324
350
|
});
|
|
325
|
-
|
|
326
|
-
|
|
351
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.SEND_DIRECTION)) {
|
|
352
|
+
newMqa.videoTransmit[0].streams.push(videoSenderStream);
|
|
353
|
+
}
|
|
327
354
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
328
355
|
} else if (mediaType.startsWith('video-share-send')) {
|
|
329
356
|
const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
|
|
@@ -334,7 +361,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
334
361
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
335
362
|
mediaType,
|
|
336
363
|
});
|
|
337
|
-
|
|
364
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.SEND_DIRECTION)) {
|
|
365
|
+
newMqa.videoTransmit[1].streams.push(videoSenderStream);
|
|
366
|
+
}
|
|
338
367
|
|
|
339
368
|
this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
|
|
340
369
|
} else if (mediaType.startsWith('video-recv')) {
|
|
@@ -346,7 +375,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
346
375
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
347
376
|
mediaType,
|
|
348
377
|
});
|
|
349
|
-
|
|
378
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.RECEIVE_DIRECTION)) {
|
|
379
|
+
newMqa.videoReceive[0].streams.push(videoReceiverStream);
|
|
380
|
+
}
|
|
350
381
|
|
|
351
382
|
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
352
383
|
} else if (mediaType.startsWith('video-share-recv')) {
|
|
@@ -358,8 +389,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
358
389
|
lastMqaDataSent: this.lastMqaDataSent,
|
|
359
390
|
mediaType,
|
|
360
391
|
});
|
|
361
|
-
|
|
362
|
-
|
|
392
|
+
if (isStreamRequested(this.statsResults, mediaType, STATS.RECEIVE_DIRECTION)) {
|
|
393
|
+
newMqa.videoReceive[1].streams.push(videoReceiverStream);
|
|
394
|
+
}
|
|
363
395
|
this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
|
|
364
396
|
}
|
|
365
397
|
});
|
|
@@ -388,6 +420,17 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
388
420
|
|
|
389
421
|
newMqa.networkType = this.statsResults.connectionType.local.networkType;
|
|
390
422
|
|
|
423
|
+
newMqa.intervalMetadata.screenWidth = window.screen.width;
|
|
424
|
+
newMqa.intervalMetadata.screenHeight = window.screen.height;
|
|
425
|
+
newMqa.intervalMetadata.screenResolution = Math.round(
|
|
426
|
+
(window.screen.width * window.screen.height) / 256
|
|
427
|
+
);
|
|
428
|
+
newMqa.intervalMetadata.appWindowWidth = window.innerWidth;
|
|
429
|
+
newMqa.intervalMetadata.appWindowHeight = window.innerHeight;
|
|
430
|
+
newMqa.intervalMetadata.appWindowSize = Math.round(
|
|
431
|
+
(window.innerWidth * window.innerHeight) / 256
|
|
432
|
+
);
|
|
433
|
+
|
|
391
434
|
this.mqaSentCount += 1;
|
|
392
435
|
|
|
393
436
|
newMqa.intervalNumber = this.mqaSentCount;
|
|
@@ -998,12 +1041,16 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
998
1041
|
result.qualityLimitationReason;
|
|
999
1042
|
this.statsResults[mediaType][sendrecvType].qualityLimitationResolutionChanges =
|
|
1000
1043
|
result.qualityLimitationResolutionChanges;
|
|
1001
|
-
this.statsResults[mediaType][sendrecvType].
|
|
1044
|
+
this.statsResults[mediaType][sendrecvType].totalRtxPacketsSent =
|
|
1002
1045
|
result.retransmittedPacketsSent;
|
|
1046
|
+
this.statsResults[mediaType][sendrecvType].totalRtxBytesSent = result.retransmittedBytesSent;
|
|
1003
1047
|
this.statsResults[mediaType][sendrecvType].totalBytesSent = result.bytesSent;
|
|
1004
1048
|
this.statsResults[mediaType][sendrecvType].headerBytesSent = result.headerBytesSent;
|
|
1005
1049
|
this.statsResults[mediaType][sendrecvType].retransmittedBytesSent =
|
|
1006
1050
|
result.retransmittedBytesSent;
|
|
1051
|
+
this.statsResults[mediaType][sendrecvType].isRequested = result.isRequested;
|
|
1052
|
+
this.statsResults[mediaType][sendrecvType].lastRequestedUpdateTimestamp =
|
|
1053
|
+
result.lastRequestedUpdateTimestamp;
|
|
1007
1054
|
this.statsResults[mediaType][sendrecvType].requestedBitrate = result.requestedBitrate;
|
|
1008
1055
|
this.statsResults[mediaType][sendrecvType].requestedFrameSize = result.requestedFrameSize;
|
|
1009
1056
|
}
|
|
@@ -1089,6 +1136,12 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1089
1136
|
}
|
|
1090
1137
|
}
|
|
1091
1138
|
|
|
1139
|
+
if (mediaType.startsWith('video-recv')) {
|
|
1140
|
+
this.statsResults[mediaType][sendrecvType].isActiveSpeaker = result.isActiveSpeaker;
|
|
1141
|
+
this.statsResults[mediaType][sendrecvType].lastActiveSpeakerTimestamp =
|
|
1142
|
+
result.lastActiveSpeakerUpdateTimestamp;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1092
1145
|
// Check the over all packet Lost ratio
|
|
1093
1146
|
this.statsResults[mediaType][sendrecvType].currentPacketLossRatio =
|
|
1094
1147
|
currentPacketsLost > 0
|
|
@@ -1136,6 +1189,10 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1136
1189
|
this.statsResults[mediaType][sendrecvType].fecPacketsReceived = result.fecPacketsReceived;
|
|
1137
1190
|
this.statsResults[mediaType][sendrecvType].totalBytesReceived = result.bytesReceived;
|
|
1138
1191
|
this.statsResults[mediaType][sendrecvType].headerBytesReceived = result.headerBytesReceived;
|
|
1192
|
+
this.statsResults[mediaType][sendrecvType].totalRtxPacketsReceived =
|
|
1193
|
+
result.retransmittedPacketsReceived;
|
|
1194
|
+
this.statsResults[mediaType][sendrecvType].totalRtxBytesReceived =
|
|
1195
|
+
result.retransmittedBytesReceived;
|
|
1139
1196
|
|
|
1140
1197
|
this.statsResults[mediaType][sendrecvType].meanRtpJitter.push(result.jitter);
|
|
1141
1198
|
|
|
@@ -1148,6 +1205,9 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1148
1205
|
this.statsResults[mediaType][sendrecvType].totalSamplesDecoded =
|
|
1149
1206
|
result.totalSamplesDecoded || 0;
|
|
1150
1207
|
this.statsResults[mediaType][sendrecvType].concealedSamples = result.concealedSamples || 0;
|
|
1208
|
+
this.statsResults[mediaType][sendrecvType].isRequested = result.isRequested;
|
|
1209
|
+
this.statsResults[mediaType][sendrecvType].lastRequestedUpdateTimestamp =
|
|
1210
|
+
result.lastRequestedUpdateTimestamp;
|
|
1151
1211
|
}
|
|
1152
1212
|
}
|
|
1153
1213
|
|