@webex/plugin-meetings 3.0.0-beta.22 → 3.0.0-beta.24

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 (124) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +0 -3
  4. package/dist/constants.js.map +1 -1
  5. package/dist/media/properties.js.map +1 -1
  6. package/dist/mediaQualityMetrics/config.js +505 -493
  7. package/dist/mediaQualityMetrics/config.js.map +1 -1
  8. package/dist/meeting/index.js +1 -4
  9. package/dist/meeting/index.js.map +1 -1
  10. package/dist/networkQualityMonitor/index.js +4 -2
  11. package/dist/networkQualityMonitor/index.js.map +1 -1
  12. package/dist/statsAnalyzer/global.js +1 -93
  13. package/dist/statsAnalyzer/global.js.map +1 -1
  14. package/dist/statsAnalyzer/index.js +297 -299
  15. package/dist/statsAnalyzer/index.js.map +1 -1
  16. package/dist/statsAnalyzer/mqaUtil.js +84 -47
  17. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  18. package/dist/types/breakouts/breakout.d.ts +8 -0
  19. package/dist/types/breakouts/collection.d.ts +5 -0
  20. package/dist/types/breakouts/index.d.ts +5 -0
  21. package/dist/types/common/browser-detection.d.ts +9 -0
  22. package/dist/types/common/collection.d.ts +48 -0
  23. package/dist/types/common/config.d.ts +2 -0
  24. package/dist/types/common/errors/captcha-error.d.ts +15 -0
  25. package/dist/types/common/errors/intent-to-join.d.ts +16 -0
  26. package/dist/types/common/errors/join-meeting.d.ts +17 -0
  27. package/dist/types/common/errors/media.d.ts +15 -0
  28. package/dist/types/common/errors/parameter.d.ts +15 -0
  29. package/dist/types/common/errors/password-error.d.ts +15 -0
  30. package/dist/types/common/errors/permission.d.ts +14 -0
  31. package/dist/types/common/errors/reconnection-in-progress.d.ts +9 -0
  32. package/dist/types/common/errors/reconnection.d.ts +15 -0
  33. package/dist/types/common/errors/stats.d.ts +15 -0
  34. package/dist/types/common/errors/webex-errors.d.ts +69 -0
  35. package/dist/types/common/errors/webex-meetings-error.d.ts +20 -0
  36. package/dist/types/common/events/events-scope.d.ts +17 -0
  37. package/dist/types/common/events/events.d.ts +12 -0
  38. package/dist/types/common/events/trigger-proxy.d.ts +2 -0
  39. package/dist/types/common/events/util.d.ts +2 -0
  40. package/dist/types/common/logs/logger-config.d.ts +2 -0
  41. package/dist/types/common/logs/logger-proxy.d.ts +2 -0
  42. package/dist/types/common/logs/request.d.ts +34 -0
  43. package/dist/types/common/queue.d.ts +32 -0
  44. package/dist/types/config.d.ts +77 -0
  45. package/dist/types/constants.d.ts +895 -0
  46. package/dist/types/index.d.ts +5 -0
  47. package/dist/types/locus-info/controlsUtils.d.ts +2 -0
  48. package/dist/types/locus-info/embeddedAppsUtils.d.ts +2 -0
  49. package/dist/types/locus-info/fullState.d.ts +2 -0
  50. package/dist/types/locus-info/hostUtils.d.ts +2 -0
  51. package/dist/types/locus-info/index.d.ts +269 -0
  52. package/dist/types/locus-info/infoUtils.d.ts +2 -0
  53. package/dist/types/locus-info/mediaSharesUtils.d.ts +2 -0
  54. package/dist/types/locus-info/parser.d.ts +212 -0
  55. package/dist/types/locus-info/selfUtils.d.ts +2 -0
  56. package/dist/types/media/index.d.ts +32 -0
  57. package/dist/types/media/properties.d.ts +107 -0
  58. package/dist/types/media/util.d.ts +2 -0
  59. package/dist/types/mediaQualityMetrics/config.d.ts +365 -0
  60. package/dist/types/meeting/effectsState.d.ts +42 -0
  61. package/dist/types/meeting/in-meeting-actions.d.ts +75 -0
  62. package/dist/types/meeting/index.d.ts +1706 -0
  63. package/dist/types/meeting/muteState.d.ts +108 -0
  64. package/dist/types/meeting/request.d.ts +261 -0
  65. package/dist/types/meeting/request.type.d.ts +11 -0
  66. package/dist/types/meeting/state.d.ts +9 -0
  67. package/dist/types/meeting/util.d.ts +2 -0
  68. package/dist/types/meeting-info/collection.d.ts +20 -0
  69. package/dist/types/meeting-info/index.d.ts +57 -0
  70. package/dist/types/meeting-info/meeting-info-v2.d.ts +93 -0
  71. package/dist/types/meeting-info/request.d.ts +22 -0
  72. package/dist/types/meeting-info/util.d.ts +2 -0
  73. package/dist/types/meeting-info/utilv2.d.ts +2 -0
  74. package/dist/types/meetings/collection.d.ts +23 -0
  75. package/dist/types/meetings/index.d.ts +297 -0
  76. package/dist/types/meetings/request.d.ts +27 -0
  77. package/dist/types/meetings/util.d.ts +18 -0
  78. package/dist/types/member/index.d.ts +146 -0
  79. package/dist/types/member/util.d.ts +2 -0
  80. package/dist/types/members/collection.d.ts +24 -0
  81. package/dist/types/members/index.d.ts +320 -0
  82. package/dist/types/members/request.d.ts +50 -0
  83. package/dist/types/members/util.d.ts +2 -0
  84. package/dist/types/metrics/config.d.ts +178 -0
  85. package/dist/types/metrics/constants.d.ts +57 -0
  86. package/dist/types/metrics/index.d.ts +160 -0
  87. package/dist/types/multistream/mediaRequestManager.d.ts +50 -0
  88. package/dist/types/multistream/receiveSlot.d.ts +64 -0
  89. package/dist/types/multistream/receiveSlotManager.d.ts +41 -0
  90. package/dist/types/multistream/remoteMedia.d.ts +93 -0
  91. package/dist/types/multistream/remoteMediaGroup.d.ts +56 -0
  92. package/dist/types/multistream/remoteMediaManager.d.ts +241 -0
  93. package/dist/types/networkQualityMonitor/index.d.ts +70 -0
  94. package/dist/types/personal-meeting-room/index.d.ts +47 -0
  95. package/dist/types/personal-meeting-room/request.d.ts +14 -0
  96. package/dist/types/personal-meeting-room/util.d.ts +2 -0
  97. package/dist/types/reachability/index.d.ts +140 -0
  98. package/dist/types/reachability/request.d.ts +35 -0
  99. package/dist/types/reactions/constants.d.ts +3 -0
  100. package/dist/types/reactions/reactions.d.ts +4 -0
  101. package/dist/types/reactions/reactions.type.d.ts +52 -0
  102. package/dist/types/reconnection-manager/index.d.ts +117 -0
  103. package/dist/types/recording-controller/enums.d.ts +7 -0
  104. package/dist/types/recording-controller/index.d.ts +193 -0
  105. package/dist/types/recording-controller/util.d.ts +13 -0
  106. package/dist/types/roap/index.d.ts +77 -0
  107. package/dist/types/roap/request.d.ts +35 -0
  108. package/dist/types/roap/turnDiscovery.d.ts +74 -0
  109. package/dist/types/statsAnalyzer/global.d.ts +36 -0
  110. package/dist/types/statsAnalyzer/index.d.ts +195 -0
  111. package/dist/types/statsAnalyzer/mqaUtil.d.ts +24 -0
  112. package/dist/types/transcription/index.d.ts +64 -0
  113. package/package.json +19 -19
  114. package/src/constants.ts +0 -3
  115. package/src/media/properties.ts +1 -3
  116. package/src/mediaQualityMetrics/config.ts +379 -377
  117. package/src/meeting/index.ts +1 -4
  118. package/src/networkQualityMonitor/index.ts +6 -6
  119. package/src/statsAnalyzer/global.ts +1 -94
  120. package/src/statsAnalyzer/index.ts +325 -373
  121. package/src/statsAnalyzer/mqaUtil.ts +92 -89
  122. package/test/integration/spec/space-meeting.js +1 -1
  123. package/test/unit/spec/networkQualityMonitor/index.js +4 -4
  124. package/test/unit/spec/stats-analyzer/index.js +63 -39
@@ -12,7 +12,13 @@ import {
12
12
  MEDIA_DEVICES,
13
13
  _UNKNOWN_,
14
14
  } from '../constants';
15
- import mqaData from '../mediaQualityMetrics/config';
15
+ import {
16
+ emptyAudioReceive,
17
+ emptyAudioTransmit,
18
+ emptyMqaInterval,
19
+ emptyVideoReceive,
20
+ emptyVideoTransmit,
21
+ } from '../mediaQualityMetrics/config';
16
22
  import LoggerProxy from '../common/logs/logger-proxy';
17
23
 
18
24
  import defaultStats from './global';
@@ -31,6 +37,22 @@ export const EVENTS = {
31
37
  REMOTE_MEDIA_STOPPED: 'REMOTE_MEDIA_STOPPED',
32
38
  };
33
39
 
40
+ const emptySender = {
41
+ trackLabel: '',
42
+ maxPacketLossRatio: 0,
43
+ availableBandwidth: 0,
44
+ bytesSent: 0,
45
+ meanRemoteJitter: [],
46
+ meanRoundTripTime: [],
47
+ };
48
+
49
+ const emptyReceiver = {
50
+ availableBandwidth: 0,
51
+ bytesReceived: 0,
52
+ meanRtpJitter: [],
53
+ meanRoundTripTime: [],
54
+ };
55
+
34
56
  /**
35
57
  * Stats Analyzer class that will emit events based on detected quality
36
58
  *
@@ -44,7 +66,6 @@ export class StatsAnalyzer extends EventsScope {
44
66
  lastEmittedStartStopEvent: any;
45
67
  lastMqaDataSent: any;
46
68
  lastStatsResults: any;
47
- localMQEStats: any;
48
69
  meetingMediaStatus: any;
49
70
  mqaInterval: NodeJS.Timeout;
50
71
  mqaSentCount: any;
@@ -75,147 +96,28 @@ export class StatsAnalyzer extends EventsScope {
75
96
  this.networkQualityMonitor = networkQualityMonitor;
76
97
  this.correlationId = config.correlationId;
77
98
  this.mqaSentCount = -1;
78
- this.lastMqaDataSent = {
79
- resolutions: {
80
- video: {send: {}, recv: {}},
81
- audio: {send: {}, recv: {}},
82
- share: {send: {}, recv: {}},
83
- },
84
- video: {send: {}, recv: {}},
85
- audio: {send: {}, recv: {}},
86
- share: {send: {}, recv: {}},
87
- };
88
- this.localMQEStats = {
89
- audio: {
90
- RX: {
91
- packetsLost: [],
92
- jitter: [],
93
- latency: [],
94
- bitRate: [],
95
- },
96
- TX: {
97
- packetsLost: [],
98
- jitter: [],
99
- latency: [],
100
- bitRate: [],
101
- },
102
- },
103
- video: {
104
- RX: {
105
- packetsLost: [],
106
- jitter: [],
107
- latency: [],
108
- bitRate: [],
109
- frameRate: [],
110
- resolutionWidth: [],
111
- resolutionHeight: [],
112
- requestedKeyFrame: [],
113
- receivedKeyFrame: [],
114
- },
115
- TX: {
116
- packetsLost: [],
117
- jitter: [],
118
- latency: [],
119
- bitRate: [],
120
- frameRate: [],
121
- resolutionWidth: [],
122
- resolutionHeight: [],
123
- requestedKeyFrame: [],
124
- receivedKeyFrame: [],
125
- },
126
- },
127
- };
128
- this.lastEmittedStartStopEvent = {
129
- audio: {
130
- local: undefined,
131
- remote: undefined,
132
- },
133
- video: {
134
- local: undefined,
135
- remote: undefined,
136
- },
137
- share: {
138
- local: undefined,
139
- remote: undefined,
140
- },
141
- };
142
- }
143
-
144
- populateResults(lastMqa) {
145
- // Audio
146
-
147
- this.localMQEStats.audio.RX.packetsLost.push(lastMqa.audioReceive[0].common.mediaHopByHopLost);
148
- this.localMQEStats.audio.RX.jitter.push(lastMqa.audioReceive[0].streams[0].common.rtpJitter);
149
- this.localMQEStats.audio.RX.latency.push(lastMqa.audioReceive[0].common.roundTripTime);
150
- this.localMQEStats.audio.RX.bitRate.push(
151
- lastMqa.audioReceive[0].streams[0].common.receivedBitrate
152
- );
153
-
154
- this.localMQEStats.audio.TX.packetsLost.push(lastMqa.audioTransmit[0].common.remoteLossRate);
155
- this.localMQEStats.audio.TX.jitter.push(lastMqa.audioTransmit[0].common.remoteJitter);
156
- this.localMQEStats.audio.TX.latency.push(lastMqa.audioTransmit[0].common.roundTripTime);
157
- this.localMQEStats.audio.TX.bitRate.push(
158
- lastMqa.audioTransmit[0].streams[0].common.transmittedBitrate
159
- );
160
-
161
- // Video
162
-
163
- this.localMQEStats.video.RX.packetsLost.push(lastMqa.videoReceive[0].common.mediaHopByHopLost);
164
- this.localMQEStats.video.RX.jitter.push(lastMqa.videoReceive[0].streams[0].common.rtpJitter);
165
- this.localMQEStats.video.RX.latency.push(
166
- lastMqa.videoReceive[0].streams[0].common.roundTripTime
167
- );
168
- this.localMQEStats.video.RX.bitRate.push(
169
- lastMqa.videoReceive[0].streams[0].common.receivedBitrate
170
- );
171
- this.localMQEStats.video.RX.frameRate.push(
172
- lastMqa.videoReceive[0].streams[0].common.receivedFrameRate
173
- );
174
- this.localMQEStats.video.RX.resolutionWidth.push(
175
- lastMqa.videoReceive[0].streams[0].receivedWidth
176
- );
177
- this.localMQEStats.video.RX.resolutionHeight.push(
178
- lastMqa.videoReceive[0].streams[0].receivedHeight
179
- );
180
- this.localMQEStats.video.RX.requestedKeyFrame.push();
181
- this.localMQEStats.video.RX.receivedKeyFrame.push();
182
-
183
- this.localMQEStats.video.TX.packetsLost.push(lastMqa.videoTransmit[0].common.remoteLossRate);
184
- this.localMQEStats.video.TX.jitter.push(lastMqa.videoTransmit[0].common.remoteJitter);
185
- this.localMQEStats.video.TX.latency.push(lastMqa.videoTransmit[0].common.roundTripTime);
186
- this.localMQEStats.video.TX.bitRate.push(
187
- lastMqa.videoTransmit[0].streams[0].common.transmittedBitrate
188
- );
189
- this.localMQEStats.video.TX.frameRate.push(
190
- lastMqa.videoTransmit[0].streams[0].common.transmittedFrameRate
191
- );
192
- this.localMQEStats.video.TX.resolutionWidth.push(
193
- lastMqa.videoTransmit[0].streams[0].transmittedWidth
194
- );
195
- this.localMQEStats.video.TX.resolutionHeight.push(
196
- lastMqa.videoTransmit[0].streams[0].transmittedHeight
197
- );
198
- this.localMQEStats.video.TX.requestedKeyFrame.push(
199
- lastMqa.videoTransmit[0].streams[0].requestedKeyFrames
200
- );
201
- this.localMQEStats.video.TX.receivedKeyFrame.push();
99
+ this.lastMqaDataSent = {};
100
+ this.lastEmittedStartStopEvent = {};
202
101
  }
203
102
 
103
+ /**
104
+ * Resets cumulative stats arrays.
105
+ *
106
+ * @public
107
+ * @memberof StatsAnalyzer
108
+ * @returns {void}
109
+ */
204
110
  resetStatsResults() {
205
- this.statsResults.audio.send.meanRemoteJitter = [];
206
- this.statsResults.video.send.meanRemoteJitter = [];
207
- this.statsResults.share.send.meanRemoteJitter = [];
208
-
209
- this.statsResults.audio.recv.meanRtpJitter = [];
210
-
211
- // TODO: currently no values are present
212
- this.statsResults.video.recv.meanRtpJitter = [];
213
- this.statsResults.share.recv.meanRtpJitter = [];
111
+ Object.keys(this.statsResults).forEach((mediaType) => {
112
+ if (mediaType.includes('recv')) {
113
+ this.statsResults[mediaType].recv.meanRtpJitter = [];
114
+ }
214
115
 
215
- // Reset the roundTripTime
216
- this.statsResults.audio.send.meanRoundTripTime = [];
217
- this.statsResults.video.send.meanRoundTripTime = [];
218
- this.statsResults.share.send.meanRoundTripTime = [];
116
+ if (mediaType.includes('send')) {
117
+ this.statsResults[mediaType].send.meanRemoteJitter = [];
118
+ this.statsResults[mediaType].send.meanRoundTripTime = [];
119
+ }
120
+ });
219
121
  }
220
122
 
221
123
  /**
@@ -237,82 +139,77 @@ export class StatsAnalyzer extends EventsScope {
237
139
  * @memberof StatsAnalyzer
238
140
  * @returns {void}
239
141
  */
240
- public sendMqaData() {
241
- const audioReceiver = mqaData.intervals[0].audioReceive[0];
242
- const audioSender = mqaData.intervals[0].audioTransmit[0];
243
- const videoReceiver = mqaData.intervals[0].videoReceive[0];
244
- const videoSender = mqaData.intervals[0].videoTransmit[0];
245
- const shareSender = mqaData.intervals[0].videoTransmit[1];
246
- const shareReceiver = mqaData.intervals[0].videoReceive[1];
247
-
248
- getAudioSenderMqa({
249
- audioSender,
250
- statsResults: this.statsResults,
251
- lastMqaDataSent: this.lastMqaDataSent,
252
- });
253
- getAudioReceiverMqa({
254
- audioReceiver,
255
- statsResults: this.statsResults,
256
- lastMqaDataSent: this.lastMqaDataSent,
257
- });
258
-
259
- getVideoReceiverMqa({
260
- videoReceiver,
261
- statsResults: this.statsResults,
262
- lastMqaDataSent: this.lastMqaDataSent,
263
- });
264
- getVideoSenderMqa({
265
- videoSender,
266
- statsResults: this.statsResults,
267
- lastMqaDataSent: this.lastMqaDataSent,
268
- });
142
+ sendMqaData() {
143
+ const newMqa = cloneDeep(emptyMqaInterval);
269
144
 
270
- // Capture mqa for share scenario
145
+ Object.keys(this.statsResults).forEach((mediaType) => {
146
+ if (mediaType.includes('audio-send') || mediaType.includes('audio-share-send')) {
147
+ const audioSender = cloneDeep(emptyAudioTransmit);
271
148
 
272
- getVideoSenderMqa({
273
- videoSender: shareSender,
274
- statsResults: this.statsResults,
275
- lastMqaDataSent: this.lastMqaDataSent,
276
- isShareStream: true,
149
+ getAudioSenderMqa({
150
+ audioSender,
151
+ statsResults: this.statsResults,
152
+ lastMqaDataSent: this.lastMqaDataSent,
153
+ mediaType,
154
+ });
155
+ newMqa.audioTransmit.push(audioSender);
156
+ } else if (mediaType.includes('audio-recv') || mediaType.includes('audio-share-recv')) {
157
+ const audioReceiver = cloneDeep(emptyAudioReceive);
158
+
159
+ getAudioReceiverMqa({
160
+ audioReceiver,
161
+ statsResults: this.statsResults,
162
+ lastMqaDataSent: this.lastMqaDataSent,
163
+ mediaType,
164
+ });
165
+ newMqa.audioReceive.push(audioReceiver);
166
+ } else if (mediaType.includes('video-send') || mediaType.includes('video-share-send')) {
167
+ const videoSender = cloneDeep(emptyVideoTransmit);
168
+
169
+ getVideoSenderMqa({
170
+ videoSender,
171
+ statsResults: this.statsResults,
172
+ lastMqaDataSent: this.lastMqaDataSent,
173
+ mediaType,
174
+ });
175
+ newMqa.videoTransmit.push(videoSender);
176
+ } else if (mediaType.includes('video-recv') || mediaType.includes('video-share-recv')) {
177
+ const videoReceiver = cloneDeep(emptyVideoReceive);
178
+
179
+ getVideoReceiverMqa({
180
+ videoReceiver,
181
+ statsResults: this.statsResults,
182
+ lastMqaDataSent: this.lastMqaDataSent,
183
+ mediaType,
184
+ });
185
+ newMqa.videoReceive.push(videoReceiver);
186
+ }
277
187
  });
278
188
 
279
- getVideoReceiverMqa({
280
- videoReceiver: shareReceiver,
281
- statsResults: this.statsResults,
282
- lastMqaDataSent: this.lastMqaDataSent,
283
- isShareStream: true,
284
- });
285
- mqaData.intervals[0].intervalMetadata.peerReflexiveIP =
286
- this.statsResults.connectionType.local.ipAddress[0];
189
+ newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress[0];
287
190
 
288
191
  // Adding peripheral information
289
- mqaData.intervals[0].intervalMetadata.peripherals = [];
290
- mqaData.intervals[0].intervalMetadata.peripherals.push({
291
- information: _UNKNOWN_,
292
- name: MEDIA_DEVICES.SPEAKER,
293
- });
294
- mqaData.intervals[0].intervalMetadata.peripherals.push({
295
- information:
296
- this.statsResults[STATS.AUDIO_CORRELATE][STATS.SEND_DIRECTION].trackLabel || _UNKNOWN_,
297
- name: MEDIA_DEVICES.MICROPHONE,
298
- });
299
- mqaData.intervals[0].intervalMetadata.peripherals.push({
300
- information:
301
- this.statsResults[STATS.VIDEO_CORRELATE][STATS.SEND_DIRECTION].trackLabel || _UNKNOWN_,
302
- name: MEDIA_DEVICES.CAMERA,
303
- });
192
+ newMqa.intervalMetadata.peripherals = [];
304
193
 
305
- // @ts-ignore
306
- mqaData.networkType = this.statsResults.connectionType.local.networkType;
307
-
308
- this.mqaSentCount += 1;
194
+ newMqa.intervalMetadata.peripherals.push({information: _UNKNOWN_, name: MEDIA_DEVICES.SPEAKER});
195
+ if (this.statsResults['audio-send']) {
196
+ newMqa.intervalMetadata.peripherals.push({
197
+ information: this.statsResults['audio-send']?.trackLabel,
198
+ name: MEDIA_DEVICES.MICROPHONE,
199
+ });
200
+ }
201
+ if (this.statsResults['video-send']) {
202
+ newMqa.intervalMetadata.peripherals.push({
203
+ information: this.statsResults['video-send']?.trackLabel,
204
+ name: MEDIA_DEVICES.CAMERA,
205
+ });
206
+ }
309
207
 
310
- mqaData.intervals[0].intervalNumber = this.mqaSentCount;
208
+ newMqa.networkType = this.statsResults.connectionType.local.networkType;
311
209
 
312
- // DO Deep copy, for some reason it takes the reference all the time rather then old value set
313
- this.lastMqaDataSent = cloneDeep(this.statsResults);
210
+ this.mqaSentCount += 1;
314
211
 
315
- this.populateResults(mqaData.intervals[0]);
212
+ newMqa.intervalNumber = this.mqaSentCount;
316
213
 
317
214
  this.resetStatsResults();
318
215
 
@@ -323,9 +220,9 @@ export class StatsAnalyzer extends EventsScope {
323
220
  },
324
221
  EVENTS.MEDIA_QUALITY,
325
222
  {
326
- data: mqaData.intervals[0],
223
+ data: newMqa,
327
224
  // @ts-ignore
328
- networkType: mqaData.networkType,
225
+ networkType: newMqa.networkType,
329
226
  }
330
227
  );
331
228
  }
@@ -416,6 +313,37 @@ export class StatsAnalyzer extends EventsScope {
416
313
  return;
417
314
  }
418
315
 
316
+ // Generate empty stats results
317
+ if (!this.statsResults[type]) {
318
+ this.statsResults[type] = {};
319
+ }
320
+
321
+ if (isSender && !this.statsResults[type].send) {
322
+ this.statsResults[type].send = cloneDeep(emptySender);
323
+ } else if (!isSender && !this.statsResults[type].recv) {
324
+ this.statsResults[type].recv = cloneDeep(emptyReceiver);
325
+ }
326
+
327
+ if (!this.statsResults.resolutions[type]) {
328
+ this.statsResults.resolutions[type] = {};
329
+ }
330
+
331
+ if (isSender && !this.statsResults.resolutions[type].send) {
332
+ this.statsResults.resolutions[type].send = cloneDeep(emptySender);
333
+ } else if (!isSender && !this.statsResults.resolutions[type].recv) {
334
+ this.statsResults.resolutions[type].recv = cloneDeep(emptyReceiver);
335
+ }
336
+
337
+ if (!this.statsResults.internal[type]) {
338
+ this.statsResults.internal[type] = {};
339
+ }
340
+
341
+ if (isSender && !this.statsResults.internal[type].send) {
342
+ this.statsResults.internal[type].send = cloneDeep(emptySender);
343
+ } else if (!isSender && !this.statsResults.internal[type].recv) {
344
+ this.statsResults.internal[type].recv = cloneDeep(emptyReceiver);
345
+ }
346
+
419
347
  switch (getStatsResult.type) {
420
348
  case 'outbound-rtp':
421
349
  this.processOutboundRTPResult(getStatsResult, type);
@@ -450,23 +378,25 @@ export class StatsAnalyzer extends EventsScope {
450
378
  /**
451
379
  * Filters the get stats results for types
452
380
  * @private
453
- * @param {Array} getStatsResults
381
+ * @param {Array} statsItem
454
382
  * @param {String} type
455
383
  * @param {boolean} isSender
456
384
  * @returns {void}
457
385
  */
458
- private filterAndParseGetStatsResults(
459
- getStatsResults: Array<any>,
460
- type: string,
461
- isSender: boolean
462
- ) {
386
+ filterAndParseGetStatsResults(statsItem: any, type: string, isSender: boolean) {
463
387
  const {types} = DEFAULT_GET_STATS_FILTER;
464
388
 
465
- getStatsResults.forEach((result) => {
389
+ statsItem.report.forEach((result) => {
466
390
  if (types.includes(result.type)) {
467
391
  this.parseGetStatsResult(result, type, isSender);
468
392
  }
469
393
  });
394
+
395
+ if (this.statsResults[type]) {
396
+ this.statsResults[type].direction = statsItem.currentDirection;
397
+ this.statsResults[type].trackLabel = statsItem.localTrackLabel;
398
+ this.statsResults[type].csi = statsItem.csi;
399
+ }
470
400
  }
471
401
 
472
402
  /**
@@ -480,7 +410,7 @@ export class StatsAnalyzer extends EventsScope {
480
410
  return;
481
411
  }
482
412
 
483
- if (type === STATS.AUDIO_CORRELATE) {
413
+ if (type.includes('audio-send')) {
484
414
  this.statsResults[type].send.audioLevel = result.audioLevel;
485
415
  this.statsResults[type].send.totalAudioEnergy = result.totalAudioEnergy;
486
416
  }
@@ -515,6 +445,10 @@ export class StatsAnalyzer extends EventsScope {
515
445
  // eslint-disable-next-line no-param-reassign
516
446
  if (currentValue === undefined) currentValue = 0;
517
447
 
448
+ if (!this.lastEmittedStartStopEvent[mediaType]) {
449
+ this.lastEmittedStartStopEvent[mediaType] = {};
450
+ }
451
+
518
452
  const lastEmittedEvent = isLocal
519
453
  ? this.lastEmittedStartStopEvent[mediaType].local
520
454
  : this.lastEmittedStartStopEvent[mediaType].remote;
@@ -555,21 +489,41 @@ export class StatsAnalyzer extends EventsScope {
555
489
  */
556
490
  private compareLastStatsResult() {
557
491
  if (this.lastStatsResults !== null && this.meetingMediaStatus) {
558
- // compare audio stats sent
559
- let mediaType = STATS.AUDIO_CORRELATE;
560
- let currentStats = null;
561
- let previousStats = null;
492
+ const getCurrentStatsTotals = (keyPrefix: string, value: string): number =>
493
+ Object.keys(this.statsResults)
494
+ .filter((key) => key.startsWith(keyPrefix))
495
+ .reduce((prev, cur) => prev + (this.statsResults[cur].recv[value] || 0), 0);
496
+
497
+ const getPreviousStatsTotals = (keyPrefix: string, value: string): number =>
498
+ Object.keys(this.statsResults)
499
+ .filter((key) => key.startsWith(keyPrefix))
500
+ .reduce((prev, cur) => prev + (this.lastStatsResults[cur].recv[value] || 0), 0);
501
+
502
+ const getCurrentResolutionsStatsTotals = (keyPrefix: string, value: string): number =>
503
+ Object.keys(this.statsResults)
504
+ .filter((key) => key.startsWith(keyPrefix))
505
+ .reduce((prev, cur) => prev + (this.statsResults.resolutions[cur].recv[value] || 0), 0);
506
+
507
+ const getPreviousResolutionsStatsTotals = (keyPrefix: string, value: string): number =>
508
+ Object.keys(this.statsResults)
509
+ .filter((key) => key.startsWith(keyPrefix))
510
+ .reduce(
511
+ (prev, cur) => prev + (this.lastStatsResults.resolutions[cur].recv[value] || 0),
512
+ 0
513
+ );
562
514
 
563
- if (this.meetingMediaStatus.expected.sendAudio) {
564
- currentStats = this.statsResults[mediaType].send;
565
- previousStats = this.lastStatsResults[mediaType].send;
515
+ if (this.meetingMediaStatus.expected.sendAudio && this.lastStatsResults['audio-send']) {
516
+ // compare audio stats sent
517
+ // NOTE: relies on there being only one sender.
518
+ const currentStats = this.statsResults['audio-send'].send;
519
+ const previousStats = this.lastStatsResults['audio-send'].send;
566
520
 
567
521
  if (
568
522
  currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
569
523
  currentStats.totalPacketsSent === 0
570
524
  ) {
571
525
  LoggerProxy.logger.info(
572
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} RTP packets sent`
526
+ `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`
573
527
  );
574
528
  } else {
575
529
  if (
@@ -577,19 +531,19 @@ export class StatsAnalyzer extends EventsScope {
577
531
  currentStats.totalAudioEnergy === 0
578
532
  ) {
579
533
  LoggerProxy.logger.info(
580
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} Energy present`
534
+ `StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`
581
535
  );
582
536
  }
583
537
 
584
538
  if (currentStats.audioLevel === 0) {
585
539
  LoggerProxy.logger.info(
586
- `StatsAnalyzer:index#compareLastStatsResult --> ${mediaType} level is 0 for the user`
540
+ `StatsAnalyzer:index#compareLastStatsResult --> audio level is 0 for the user`
587
541
  );
588
542
  }
589
543
  }
590
544
 
591
545
  this.emitStartStopEvents(
592
- mediaType,
546
+ 'audio',
593
547
  previousStats.totalPacketsSent,
594
548
  currentStats.totalPacketsSent,
595
549
  true
@@ -598,45 +552,44 @@ export class StatsAnalyzer extends EventsScope {
598
552
 
599
553
  if (this.meetingMediaStatus.expected.receiveAudio) {
600
554
  // compare audio stats received
601
- currentStats = this.statsResults[mediaType].recv;
602
- previousStats = this.lastStatsResults[mediaType].recv;
555
+ const currentPacketsReceived = getCurrentStatsTotals('audio-recv', 'totalPacketsReceived');
556
+ const previousPacketsReceived = getPreviousStatsTotals(
557
+ 'audio-recv',
558
+ 'totalPacketsReceived'
559
+ );
560
+ const currentSamplesReceived = getCurrentStatsTotals('audio-recv', 'totalSamplesReceived');
561
+ const previousSamplesReceived = getPreviousStatsTotals(
562
+ 'audio-recv',
563
+ 'totalSamplesReceived'
564
+ );
603
565
 
604
- if (
605
- currentStats.totalPacketsReceived === previousStats.totalPacketsReceived ||
606
- currentStats.totalPacketsReceived === 0
607
- ) {
566
+ if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
608
567
  LoggerProxy.logger.info(
609
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} RTP packets received`
568
+ `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received`
610
569
  );
611
570
  } else if (
612
- currentStats.totalSamplesReceived === previousStats.totalSamplesReceived ||
613
- currentStats.totalSamplesReceived === 0
571
+ currentSamplesReceived === previousSamplesReceived ||
572
+ currentSamplesReceived === 0
614
573
  ) {
615
574
  LoggerProxy.logger.info(
616
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} samples received`
575
+ `StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`
617
576
  );
618
577
  }
619
578
 
620
- this.emitStartStopEvents(
621
- mediaType,
622
- previousStats.totalPacketsReceived,
623
- currentStats.totalPacketsReceived,
624
- false
625
- );
579
+ this.emitStartStopEvents('audio', previousPacketsReceived, currentPacketsReceived, false);
626
580
  }
627
581
 
628
- mediaType = STATS.VIDEO_CORRELATE;
629
- if (this.meetingMediaStatus.expected.sendVideo) {
582
+ if (this.meetingMediaStatus.expected.sendVideo && this.lastStatsResults['video-send']) {
630
583
  // compare video stats sent
631
- currentStats = this.statsResults[mediaType].send;
632
- previousStats = this.lastStatsResults[mediaType].send;
584
+ const currentStats = this.statsResults['video-send'].send;
585
+ const previousStats = this.lastStatsResults['video-send'].send;
633
586
 
634
587
  if (
635
588
  currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
636
589
  currentStats.totalPacketsSent === 0
637
590
  ) {
638
591
  LoggerProxy.logger.info(
639
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} RTP packets sent`
592
+ `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`
640
593
  );
641
594
  } else {
642
595
  if (
@@ -644,95 +597,89 @@ export class StatsAnalyzer extends EventsScope {
644
597
  currentStats.framesEncoded === 0
645
598
  ) {
646
599
  LoggerProxy.logger.info(
647
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} Frames Encoded`
600
+ `StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`
648
601
  );
649
602
  }
650
603
 
651
604
  if (
652
- this.statsResults.resolutions[mediaType].send.framesSent ===
653
- this.lastStatsResults.resolutions[mediaType].send.framesSent ||
654
- this.statsResults.resolutions[mediaType].send.framesSent === 0
605
+ this.statsResults.resolutions['video-send'].send.framesSent ===
606
+ this.lastStatsResults.resolutions['video-send'].send.framesSent ||
607
+ this.statsResults.resolutions['video-send'].send.framesSent === 0
655
608
  ) {
656
609
  LoggerProxy.logger.info(
657
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} Frames sent`
610
+ `StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`
658
611
  );
659
612
  }
660
613
  }
661
614
 
662
- this.emitStartStopEvents(
663
- mediaType,
664
- previousStats.framesSent,
665
- currentStats.framesSent,
666
- true
667
- );
615
+ this.emitStartStopEvents('video', previousStats.framesSent, currentStats.framesSent, true);
668
616
  }
669
617
 
670
618
  if (this.meetingMediaStatus.expected.receiveVideo) {
671
- // compare video stats reveived
672
-
673
- currentStats = this.statsResults[mediaType].recv;
674
- previousStats = this.lastStatsResults[mediaType].recv;
619
+ // compare video stats received
620
+ const currentPacketsReceived = getCurrentStatsTotals('video-recv', 'totalPacketsReceived');
621
+ const previousPacketsReceived = getPreviousStatsTotals(
622
+ 'video-recv',
623
+ 'totalPacketsReceived'
624
+ );
625
+ const currentFramesReceived = getCurrentResolutionsStatsTotals(
626
+ 'video-recv',
627
+ 'framesReceived'
628
+ );
629
+ const previousFramesReceived = getPreviousResolutionsStatsTotals(
630
+ 'video-recv',
631
+ 'framesReceived'
632
+ );
633
+ const currentFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
634
+ const previousFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
635
+ const currentFramesDropped = getCurrentResolutionsStatsTotals(
636
+ 'video-recv',
637
+ 'framesDropped'
638
+ );
639
+ const previousFramesDropped = getPreviousResolutionsStatsTotals(
640
+ 'video-recv',
641
+ 'framesDropped'
642
+ );
675
643
 
676
- if (
677
- currentStats.totalPacketsReceived === previousStats.totalPacketsReceived ||
678
- currentStats.totalPacketsReceived === 0
679
- ) {
644
+ if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
680
645
  LoggerProxy.logger.info(
681
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} RTP packets received`
646
+ `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received`
682
647
  );
683
648
  } else {
684
- if (
685
- this.statsResults.resolutions[mediaType].recv.framesReceived ===
686
- this.lastStatsResults.resolutions[mediaType].recv.framesReceived ||
687
- this.statsResults.resolutions[mediaType].recv.framesReceived === 0
688
- ) {
649
+ if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
689
650
  LoggerProxy.logger.info(
690
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames received`
651
+ `StatsAnalyzer:index#compareLastStatsResult --> No video frames received`
691
652
  );
692
653
  }
693
654
 
694
- if (
695
- this.statsResults[mediaType].recv.framesDecoded ===
696
- this.lastStatsResults[mediaType].recv.framesDecoded ||
697
- this.statsResults.resolutions[mediaType].send.framesDecoded === 0
698
- ) {
655
+ if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
699
656
  LoggerProxy.logger.info(
700
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames decoded`
657
+ `StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded`
701
658
  );
702
659
  }
703
660
 
704
- if (
705
- this.statsResults.resolutions[mediaType].recv.framesDropped -
706
- this.lastStatsResults.resolutions[mediaType].recv.framesDropped >
707
- 10
708
- ) {
661
+ if (currentFramesDropped - previousFramesDropped > 10) {
709
662
  LoggerProxy.logger.info(
710
- `StatsAnalyzer:index#compareLastStatsResult --> ${mediaType} frames are getting dropped`
663
+ `StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped`
711
664
  );
712
665
  }
713
666
  }
714
667
 
715
- this.emitStartStopEvents(
716
- mediaType,
717
- previousStats.framesDecoded,
718
- currentStats.framesDecoded,
719
- false
720
- );
668
+ this.emitStartStopEvents('video', previousFramesDecoded, currentFramesDecoded, false);
721
669
  }
722
670
 
723
- mediaType = STATS.SHARE_CORRELATE;
724
- if (this.meetingMediaStatus.expected.sendShare) {
671
+ if (this.meetingMediaStatus.expected.sendShare && this.lastStatsResults['video-share-send']) {
725
672
  // compare share stats sent
726
673
 
727
- currentStats = this.statsResults[mediaType].send;
728
- previousStats = this.lastStatsResults[mediaType].send;
674
+ const currentStats = this.statsResults['video-share-send'].send;
675
+ const previousStats = this.lastStatsResults['video-share-send'].send;
729
676
 
730
677
  if (
731
678
  currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
732
679
  currentStats.totalPacketsSent === 0
733
680
  ) {
734
681
  LoggerProxy.logger.info(
735
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} RTP packets sent`
682
+ `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`
736
683
  );
737
684
  } else {
738
685
  if (
@@ -740,61 +687,72 @@ export class StatsAnalyzer extends EventsScope {
740
687
  currentStats.framesEncoded === 0
741
688
  ) {
742
689
  LoggerProxy.logger.info(
743
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames getting encoded`
690
+ `StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`
744
691
  );
745
692
  }
746
693
 
747
694
  if (
748
- this.statsResults.resolutions[mediaType].send.framesSent ===
749
- this.lastStatsResults.resolutions[mediaType].send.framesSent ||
750
- this.statsResults.resolutions[mediaType].send.framesSent === 0
695
+ this.statsResults.resolutions['video-share-send'].send.framesSent ===
696
+ this.lastStatsResults.resolutions['video-share-send'].send.framesSent ||
697
+ this.statsResults.resolutions['video-share-send'].send.framesSent === 0
751
698
  ) {
752
699
  LoggerProxy.logger.info(
753
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames sent`
700
+ `StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`
754
701
  );
755
702
  }
756
703
  }
704
+ }
757
705
 
706
+ if (this.meetingMediaStatus.expected.sendShare) {
758
707
  // TODO:need to check receive share value
759
- // compare share stats reveived
760
- currentStats = this.statsResults[mediaType].recv;
761
- previousStats = this.lastStatsResults[mediaType].recv;
708
+ // compare share stats received
709
+ const currentPacketsReceived = getCurrentStatsTotals(
710
+ 'video-share-recv',
711
+ 'totalPacketsReceived'
712
+ );
713
+ const previousPacketsReceived = getPreviousStatsTotals(
714
+ 'video-share-recv',
715
+ 'totalPacketsReceived'
716
+ );
717
+ const currentFramesReceived = getCurrentResolutionsStatsTotals(
718
+ 'video-share-recv',
719
+ 'framesReceived'
720
+ );
721
+ const previousFramesReceived = getPreviousResolutionsStatsTotals(
722
+ 'video-share-recv',
723
+ 'framesReceived'
724
+ );
725
+ const currentFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
726
+ const previousFramesDecoded = getPreviousStatsTotals('video-share-recv', 'framesDecoded');
727
+ const currentFramesDropped = getCurrentResolutionsStatsTotals(
728
+ 'video-share-recv',
729
+ 'framesDropped'
730
+ );
731
+ const previousFramesDropped = getPreviousResolutionsStatsTotals(
732
+ 'video-share-recv',
733
+ 'framesDropped'
734
+ );
762
735
 
763
- if (
764
- currentStats.totalPacketsReceived === previousStats.totalPacketsReceived ||
765
- currentStats.totalPacketsSent === 0
766
- ) {
736
+ if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
767
737
  LoggerProxy.logger.info(
768
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} RTP packets received`
738
+ `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received`
769
739
  );
770
740
  } else {
771
- if (
772
- this.statsResults.resolutions[mediaType].recv.framesReceived ===
773
- this.lastStatsResults.resolutions[mediaType].recv.framesReceived ||
774
- this.statsResults.resolutions[mediaType].recv.framesReceived === 0
775
- ) {
741
+ if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
776
742
  LoggerProxy.logger.info(
777
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames received`
743
+ `StatsAnalyzer:index#compareLastStatsResult --> No share frames received`
778
744
  );
779
745
  }
780
746
 
781
- if (
782
- this.statsResults[mediaType].recv.framesDecoded ===
783
- this.lastStatsResults[mediaType].recv.framesDecoded ||
784
- this.statsResults.resolutions[mediaType].send.framesDecoded === 0
785
- ) {
747
+ if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
786
748
  LoggerProxy.logger.info(
787
- `StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames decoded`
749
+ `StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`
788
750
  );
789
751
  }
790
752
 
791
- if (
792
- this.statsResults.resolutions[mediaType].recv.framesDropped -
793
- this.lastStatsResults.resolutions[mediaType].recv.framesDropped >
794
- 10
795
- ) {
753
+ if (currentFramesDropped - previousFramesDropped > 10) {
796
754
  LoggerProxy.logger.info(
797
- `StatsAnalyzer:index#compareLastStatsResult --> ${mediaType} frames are getting dropped`
755
+ `StatsAnalyzer:index#compareLastStatsResult --> share frames are getting dropped`
798
756
  );
799
757
  }
800
758
  }
@@ -832,47 +790,43 @@ export class StatsAnalyzer extends EventsScope {
832
790
  LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Collecting Stats');
833
791
 
834
792
  return this.mediaConnection.getTransceiverStats().then((transceiverStats) => {
835
- this.filterAndParseGetStatsResults(
836
- transceiverStats.video.sender,
837
- STATS.VIDEO_CORRELATE,
838
- true
839
- );
840
- this.filterAndParseGetStatsResults(
841
- transceiverStats.video.receiver,
842
- STATS.VIDEO_CORRELATE,
843
- false
844
- );
845
- this.filterAndParseGetStatsResults(
846
- transceiverStats.audio.sender,
847
- STATS.AUDIO_CORRELATE,
848
- true
793
+ transceiverStats.video.receivers.forEach((receiver, i) =>
794
+ this.filterAndParseGetStatsResults(receiver, `video-recv-${i}`, false)
849
795
  );
850
- this.filterAndParseGetStatsResults(
851
- transceiverStats.audio.receiver,
852
- STATS.AUDIO_CORRELATE,
853
- false
796
+ transceiverStats.audio.receivers.forEach((receiver, i) =>
797
+ this.filterAndParseGetStatsResults(receiver, `audio-recv-${i}`, false)
854
798
  );
855
- this.filterAndParseGetStatsResults(
856
- transceiverStats.screenShareVideo.sender,
857
- STATS.SHARE_CORRELATE,
858
- true
799
+ transceiverStats.screenShareVideo.receivers.forEach((receiver, i) =>
800
+ this.filterAndParseGetStatsResults(receiver, `video-share-recv-${i}`, false)
859
801
  );
860
- this.filterAndParseGetStatsResults(
861
- transceiverStats.screenShareVideo.receiver,
862
- STATS.SHARE_CORRELATE,
863
- false
802
+ transceiverStats.screenShareAudio.receivers.forEach((receiver, i) =>
803
+ this.filterAndParseGetStatsResults(receiver, `audio-share-recv-${i}`, false)
864
804
  );
865
805
 
866
- // updates the current direction of media
867
- this.statsResults[STATS.AUDIO_CORRELATE].direction = transceiverStats.audio.currentDirection;
868
- this.statsResults[STATS.VIDEO_CORRELATE].direction = transceiverStats.video.currentDirection;
869
- this.statsResults[STATS.SHARE_CORRELATE].direction =
870
- transceiverStats.screenShareVideo.currentDirection;
871
-
872
- this.statsResults[STATS.AUDIO_CORRELATE][STATS.SEND_DIRECTION].trackLabel =
873
- transceiverStats.audio.localTrackLabel;
874
- this.statsResults[STATS.VIDEO_CORRELATE][STATS.SEND_DIRECTION].trackLabel =
875
- transceiverStats.video.localTrackLabel;
806
+ transceiverStats.video.senders.forEach((sender, i) => {
807
+ if (i > 0) {
808
+ throw new Error('Stats Analyzer does not support multiple senders.');
809
+ }
810
+ this.filterAndParseGetStatsResults(sender, 'video-send', true);
811
+ });
812
+ transceiverStats.audio.senders.forEach((sender, i) => {
813
+ if (i > 0) {
814
+ throw new Error('Stats Analyzer does not support multiple senders.');
815
+ }
816
+ this.filterAndParseGetStatsResults(sender, 'audio-send', true);
817
+ });
818
+ transceiverStats.screenShareVideo.senders.forEach((sender, i) => {
819
+ if (i > 0) {
820
+ throw new Error('Stats Analyzer does not support multiple senders.');
821
+ }
822
+ this.filterAndParseGetStatsResults(sender, 'video-share-send', true);
823
+ });
824
+ transceiverStats.screenShareAudio.senders.forEach((sender, i) => {
825
+ if (i > 0) {
826
+ throw new Error('Stats Analyzer does not support multiple senders.');
827
+ }
828
+ this.filterAndParseGetStatsResults(sender, 'audio-share-send', true);
829
+ });
876
830
 
877
831
  this.compareLastStatsResult();
878
832
 
@@ -890,11 +844,10 @@ export class StatsAnalyzer extends EventsScope {
890
844
  * Processes OutboundRTP stats result and stores
891
845
  * @private
892
846
  * @param {*} result
893
- * @param {*} type
847
+ * @param {*} mediaType
894
848
  * @returns {void}
895
849
  */
896
- private processOutboundRTPResult(result: any, type: any) {
897
- const mediaType = type || STATS.AUDIO_CORRELATE;
850
+ private processOutboundRTPResult(result: any, mediaType: any) {
898
851
  const sendrecvType = STATS.SEND_DIRECTION;
899
852
 
900
853
  if (result.bytesSent) {
@@ -964,11 +917,10 @@ export class StatsAnalyzer extends EventsScope {
964
917
  * Processes InboundRTP stats result and stores
965
918
  * @private
966
919
  * @param {*} result
967
- * @param {*} type
920
+ * @param {*} mediaType
968
921
  * @returns {void}
969
922
  */
970
- private processInboundRTPResult(result: any, type: any) {
971
- const mediaType = type || STATS.AUDIO_CORRELATE;
923
+ private processInboundRTPResult(result: any, mediaType: any) {
972
924
  const sendrecvType = STATS.RECEIVE_DIRECTION;
973
925
 
974
926
  if (result.bytesReceived) {
@@ -1181,7 +1133,6 @@ export class StatsAnalyzer extends EventsScope {
1181
1133
  if (!result || result.type !== 'track') {
1182
1134
  return;
1183
1135
  }
1184
- if (result.type !== 'track') return;
1185
1136
 
1186
1137
  const sendrecvType =
1187
1138
  result.remoteSource === true ? STATS.RECEIVE_DIRECTION : STATS.SEND_DIRECTION;
@@ -1199,7 +1150,7 @@ export class StatsAnalyzer extends EventsScope {
1199
1150
  this.statsResults.resolutions[mediaType][sendrecvType].framesDropped = result.framesDropped;
1200
1151
  }
1201
1152
 
1202
- if (result.trackIdentifier && mediaType !== STATS.AUDIO_CORRELATE) {
1153
+ if (result.trackIdentifier && !mediaType.includes('audio')) {
1203
1154
  this.statsResults.resolutions[mediaType][sendrecvType].trackIdentifier =
1204
1155
  result.trackIdentifier;
1205
1156
 
@@ -1225,8 +1176,9 @@ export class StatsAnalyzer extends EventsScope {
1225
1176
  * @returns {void}
1226
1177
  * @memberof StatsAnalyzer
1227
1178
  */
1228
- private compareSentAndReceived(result: any, type: any) {
1229
- if (!type) {
1179
+ compareSentAndReceived(result, type) {
1180
+ // Don't compare on transceivers without a sender.
1181
+ if (!type || !this.statsResults.internal[type].send) {
1230
1182
  return;
1231
1183
  }
1232
1184