@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.
Files changed (37) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +4 -2
  4. package/dist/constants.js.map +1 -1
  5. package/dist/interpretation/index.js +1 -1
  6. package/dist/interpretation/siLanguage.js +1 -1
  7. package/dist/mediaQualityMetrics/config.js +20 -16
  8. package/dist/mediaQualityMetrics/config.js.map +1 -1
  9. package/dist/meeting/index.js +30 -13
  10. package/dist/meeting/index.js.map +1 -1
  11. package/dist/meetings/index.js +6 -1
  12. package/dist/meetings/index.js.map +1 -1
  13. package/dist/reachability/index.js +82 -9
  14. package/dist/reachability/index.js.map +1 -1
  15. package/dist/statsAnalyzer/index.js +77 -27
  16. package/dist/statsAnalyzer/index.js.map +1 -1
  17. package/dist/statsAnalyzer/mqaUtil.js +46 -7
  18. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  19. package/dist/types/constants.d.ts +2 -1
  20. package/dist/types/mediaQualityMetrics/config.d.ts +14 -2
  21. package/dist/types/meeting/index.d.ts +8 -0
  22. package/dist/types/reachability/index.d.ts +11 -0
  23. package/dist/types/statsAnalyzer/index.d.ts +14 -6
  24. package/dist/types/statsAnalyzer/mqaUtil.d.ts +17 -4
  25. package/dist/webinar/index.js +1 -1
  26. package/package.json +22 -22
  27. package/src/constants.ts +2 -1
  28. package/src/mediaQualityMetrics/config.ts +22 -10
  29. package/src/meeting/index.ts +29 -14
  30. package/src/meetings/index.ts +7 -2
  31. package/src/reachability/index.ts +57 -0
  32. package/src/statsAnalyzer/index.ts +82 -22
  33. package/src/statsAnalyzer/mqaUtil.ts +68 -4
  34. package/test/unit/spec/meeting/index.js +28 -8
  35. package/test/unit/spec/meetings/index.js +38 -15
  36. package/test/unit/spec/reachability/index.ts +266 -0
  37. 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.0",
47
- "@webex/plugin-rooms": "3.3.0",
48
- "@webex/test-helper-chai": "3.3.0",
49
- "@webex/test-helper-mocha": "3.3.0",
50
- "@webex/test-helper-mock-webex": "3.3.0",
51
- "@webex/test-helper-retry": "3.3.0",
52
- "@webex/test-helper-test-users": "3.3.0",
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.0",
65
- "@webex/internal-media-core": "2.5.0",
66
- "@webex/internal-plugin-conversation": "3.3.0",
67
- "@webex/internal-plugin-device": "3.3.0",
68
- "@webex/internal-plugin-llm": "3.3.0",
69
- "@webex/internal-plugin-mercury": "3.3.0",
70
- "@webex/internal-plugin-metrics": "3.3.0",
71
- "@webex/internal-plugin-support": "3.3.0",
72
- "@webex/internal-plugin-user": "3.3.0",
73
- "@webex/internal-plugin-voicea": "3.3.0",
74
- "@webex/media-helpers": "3.3.0",
75
- "@webex/plugin-people": "3.3.0",
76
- "@webex/plugin-rooms": "3.3.0",
77
- "@webex/webex-core": "3.3.0",
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.0"
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: true, // Not avaliable
123
- mariQosEnabled: true, // Not avaliable
124
- multistreamEnabled: true, // Not avaliable
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: true,
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, // Not avaliable
188
- mariQosEnabled: false, // Not avaliable
189
- multistreamEnabled: false, // Not avaliable
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, // outboundRtp.totalPacketSentDelay // TODO: check if totalInterFrameDelay/ packetSentDelay/ jitterBufferDalay
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
@@ -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,
@@ -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
- this.preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
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 class for assessing network characteristics (jitter, packetLoss, latency)
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: any,
106
- receiveSlotCallback: ReceiveSlotCallback = () => undefined,
107
- networkQualityMonitor: object = {},
108
- statsResults: object = defaultStats
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
- newMqa.audioTransmit[0].streams.push(audioSenderStream);
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
- newMqa.audioTransmit[1].streams.push(audioSenderStream);
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
- newMqa.audioReceive[0].streams.push(audioReceiverStream);
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
- newMqa.audioReceive[1].streams.push(audioReceiverStream);
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
- newMqa.videoTransmit[0].streams.push(videoSenderStream);
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
- newMqa.videoTransmit[1].streams.push(videoSenderStream);
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
- newMqa.videoReceive[0].streams.push(videoReceiverStream);
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
- newMqa.videoReceive[1].streams.push(videoReceiverStream);
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].retransmittedPacketsSent =
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