@webex/plugin-meetings 2.19.1 → 2.19.2

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 (60) hide show
  1. package/README.md +0 -300
  2. package/dist/constants.js +3 -206
  3. package/dist/constants.js.map +1 -1
  4. package/dist/meeting/index.js +352 -489
  5. package/dist/meeting/index.js.map +1 -1
  6. package/dist/meeting/util.js +4 -213
  7. package/dist/meeting/util.js.map +1 -1
  8. package/dist/meetings/index.js +0 -28
  9. package/dist/meetings/index.js.map +1 -1
  10. package/dist/statsAnalyzer/index.js +145 -86
  11. package/dist/statsAnalyzer/index.js.map +1 -1
  12. package/package.json +5 -7
  13. package/src/constants.ts +1 -214
  14. package/src/meeting/index.js +110 -208
  15. package/src/meeting/util.js +4 -252
  16. package/src/meetings/index.js +0 -22
  17. package/src/statsAnalyzer/index.js +164 -99
  18. package/test/integration/spec/journey.js +2 -67
  19. package/test/unit/spec/meeting/index.js +88 -29
  20. package/test/unit/spec/meeting/utils.js +0 -2
  21. package/test/unit/spec/stats-analyzer/index.js +209 -1
  22. package/dist/analyzer/analyzer.js +0 -113
  23. package/dist/analyzer/analyzer.js.map +0 -1
  24. package/dist/analyzer/calculator.js +0 -87
  25. package/dist/analyzer/calculator.js.map +0 -1
  26. package/dist/metrics/mqa-processor.js +0 -170
  27. package/dist/metrics/mqa-processor.js.map +0 -1
  28. package/dist/stats/data.js +0 -93
  29. package/dist/stats/data.js.map +0 -1
  30. package/dist/stats/events.js +0 -222
  31. package/dist/stats/events.js.map +0 -1
  32. package/dist/stats/filter.js +0 -84
  33. package/dist/stats/filter.js.map +0 -1
  34. package/dist/stats/history.js +0 -147
  35. package/dist/stats/history.js.map +0 -1
  36. package/dist/stats/index.js +0 -425
  37. package/dist/stats/index.js.map +0 -1
  38. package/dist/stats/metrics.js +0 -112
  39. package/dist/stats/metrics.js.map +0 -1
  40. package/dist/stats/stats.js +0 -592
  41. package/dist/stats/stats.js.map +0 -1
  42. package/dist/stats/stream.js +0 -156
  43. package/dist/stats/stream.js.map +0 -1
  44. package/dist/stats/transformer.js +0 -126
  45. package/dist/stats/transformer.js.map +0 -1
  46. package/dist/stats/util.js +0 -64
  47. package/dist/stats/util.js.map +0 -1
  48. package/src/analyzer/analyzer.js +0 -78
  49. package/src/analyzer/calculator.js +0 -77
  50. package/src/metrics/mqa-processor.js +0 -118
  51. package/src/stats/data.js +0 -56
  52. package/src/stats/events.js +0 -185
  53. package/src/stats/filter.js +0 -40
  54. package/src/stats/history.js +0 -107
  55. package/src/stats/index.js +0 -320
  56. package/src/stats/metrics.js +0 -95
  57. package/src/stats/stats.js +0 -477
  58. package/src/stats/stream.js +0 -108
  59. package/src/stats/transformer.js +0 -109
  60. package/src/stats/util.js +0 -44
@@ -1,8 +1,7 @@
1
1
  import {cloneDeep} from 'lodash';
2
2
 
3
3
  import EventsScope from '../common/events/events-scope';
4
- import {EVENT_TRIGGERS,
5
- DEFAULT_GET_STATS_FILTER, CONNECTION_STATE, STATS, MQA_INTEVAL, NETWORK_TYPE, MEDIA_DEVICES, _UNKNOWN_} from '../constants';
4
+ import {DEFAULT_GET_STATS_FILTER, CONNECTION_STATE, STATS, MQA_INTEVAL, NETWORK_TYPE, MEDIA_DEVICES, _UNKNOWN_} from '../constants';
6
5
  import mqaData from '../mediaQualityMetrics/config';
7
6
  import LoggerProxy from '../common/logs/logger-proxy';
8
7
 
@@ -14,16 +13,13 @@ import {
14
13
  getVideoReceiverMqa
15
14
  } from './mqaUtil';
16
15
 
17
-
18
- /**
19
- * Meeting High Packet Loss Event
20
- * Emitted when ever there is high packet loss detected
21
- * @event meeting:highPacketLoss
22
- * @type {Object}
23
- * @property {String} mediaType Type of media experiencing packet loss
24
- * @property {Number} packetLossRatio current packet loss ratio
25
- * @memberof StatsAnalyzer
26
- */
16
+ export const EVENTS = {
17
+ MEDIA_QUALITY: 'MEDIA_QUALITY',
18
+ LOCAL_MEDIA_STARTED: 'LOCAL_MEDIA_STARTED',
19
+ LOCAL_MEDIA_STOPPED: 'LOCAL_MEDIA_STOPPED',
20
+ REMOTE_MEDIA_STARTED: 'REMOTE_MEDIA_STARTED',
21
+ REMOTE_MEDIA_STOPPED: 'REMOTE_MEDIA_STOPPED',
22
+ };
27
23
 
28
24
  /**
29
25
  * Stats Analyzer class that will emit events based on detected quality
@@ -32,7 +28,7 @@ import {
32
28
  * @class StatsAnalyzer
33
29
  * @extends {EventsScope}
34
30
  */
35
- export default class StatsAnalyzer extends EventsScope {
31
+ export class StatsAnalyzer extends EventsScope {
36
32
  /**
37
33
  * Creates a new instance of StatsAnalyzer
38
34
  * @constructor
@@ -96,6 +92,20 @@ export default class StatsAnalyzer extends EventsScope {
96
92
  }
97
93
  }
98
94
  };
95
+ this.lastEmittedStartStopEvent = {
96
+ audio: {
97
+ local: undefined,
98
+ remote: undefined,
99
+ },
100
+ video: {
101
+ local: undefined,
102
+ remote: undefined,
103
+ },
104
+ share: {
105
+ local: undefined,
106
+ remote: undefined,
107
+ },
108
+ };
99
109
  }
100
110
 
101
111
  populateResults(lastMqa) {
@@ -244,7 +254,7 @@ export default class StatsAnalyzer extends EventsScope {
244
254
  file: 'statsAnalyzer',
245
255
  function: 'sendMqaData'
246
256
  },
247
- EVENT_TRIGGERS.MEDIA_QUALITY,
257
+ EVENTS.MEDIA_QUALITY,
248
258
  {
249
259
  data: mqaData.intervals[0],
250
260
  networkType: mqaData.networkType
@@ -270,22 +280,27 @@ export default class StatsAnalyzer extends EventsScope {
270
280
  * @public
271
281
  * @memberof StatsAnalyzer
272
282
  * @param {PeerConnection} peerConnection
273
- * @returns {void}
283
+ * @returns {Promise}
274
284
  */
275
285
  startAnalyzer(peerConnection) {
276
286
  if (!this.statsStarted) {
277
287
  this.statsStarted = true;
278
288
  this.peerConnection = peerConnection;
279
- this.getStatsAndParse();
280
- this.statsInterval = setInterval(() => {
281
- this.getStatsAndParse();
282
- }, this.config.analyzerInterval);
283
- // Trigger initial fetch
284
- this.sendMqaData();
285
- this.mqaInterval = setInterval(() => {
286
- this.sendMqaData();
287
- }, MQA_INTEVAL);
289
+
290
+ return this.getStatsAndParse()
291
+ .then(() => {
292
+ this.statsInterval = setInterval(() => {
293
+ this.getStatsAndParse();
294
+ }, this.config.analyzerInterval);
295
+ // Trigger initial fetch
296
+ this.sendMqaData();
297
+ this.mqaInterval = setInterval(() => {
298
+ this.sendMqaData();
299
+ }, MQA_INTEVAL);
300
+ });
288
301
  }
302
+
303
+ return Promise.resolve();
289
304
  }
290
305
 
291
306
  /**
@@ -296,18 +311,27 @@ export default class StatsAnalyzer extends EventsScope {
296
311
  * @returns {void}
297
312
  */
298
313
  stopAnalyzer() {
314
+ const sendOneLastMqa = this.mqaInterval && this.statsInterval;
315
+
299
316
  if (this.statsInterval) {
300
- this.getStatsAndParse();
301
317
  clearInterval(this.statsInterval);
302
- this.statsInterva = undefined;
318
+ this.statsInterval = undefined;
303
319
  }
304
320
 
305
321
  if (this.mqaInterval) {
306
- this.sendMqaData();
307
322
  clearInterval(this.mqaInterval);
308
- this.statsInterva = undefined;
323
+ this.mqaInterval = undefined;
324
+ }
325
+
326
+ if (sendOneLastMqa) {
327
+ return this.getStatsAndParse().then(() => {
328
+ this.sendMqaData();
329
+ this.peerConnection = null;
330
+ });
309
331
  }
310
332
  this.peerConnection = null;
333
+
334
+ return Promise.resolve();
311
335
  }
312
336
 
313
337
  /**
@@ -389,6 +413,61 @@ export default class StatsAnalyzer extends EventsScope {
389
413
  }
390
414
  }
391
415
 
416
+ /**
417
+ * emits started/stopped events for local/remote media by checking
418
+ * if given values are increasing or not. The previousValue, currentValue
419
+ * params can be any numerical value like number of receive packets or
420
+ * decoded frames, etc.
421
+ *
422
+ * @private
423
+ * @param {string} mediaType
424
+ * @param {number} previousValue - value to compare
425
+ * @param {number} currentValue - value to compare (must be same type of value as previousValue)
426
+ * @param {boolean} isLocal - true if stats are for local media being sent out, false for remote media being received
427
+ * @memberof StatsAnalyzer
428
+ * @returns {void}
429
+ */
430
+ emitStartStopEvents = (mediaType, previousValue, currentValue, isLocal) => {
431
+ if (mediaType !== 'audio' && mediaType !== 'video' && mediaType !== 'share') {
432
+ throw new Error(`Unsupported mediaType: ${mediaType}`);
433
+ }
434
+
435
+ // eslint-disable-next-line no-param-reassign
436
+ if (previousValue === undefined) previousValue = 0;
437
+ // eslint-disable-next-line no-param-reassign
438
+ if (currentValue === undefined) currentValue = 0;
439
+
440
+ const lastEmittedEvent = isLocal ? this.lastEmittedStartStopEvent[mediaType].local : this.lastEmittedStartStopEvent[mediaType].remote;
441
+
442
+ let newEvent;
443
+
444
+ if ((currentValue - previousValue) > 0) {
445
+ newEvent = isLocal ? EVENTS.LOCAL_MEDIA_STARTED : EVENTS.REMOTE_MEDIA_STARTED;
446
+ }
447
+ else if ((currentValue === previousValue) && currentValue > 0) {
448
+ newEvent = isLocal ? EVENTS.LOCAL_MEDIA_STOPPED : EVENTS.REMOTE_MEDIA_STOPPED;
449
+ }
450
+
451
+ if (newEvent && lastEmittedEvent !== newEvent) {
452
+ if (isLocal) {
453
+ this.lastEmittedStartStopEvent[mediaType].local = newEvent;
454
+ }
455
+ else {
456
+ this.lastEmittedStartStopEvent[mediaType].remote = newEvent;
457
+ }
458
+ this.emit(
459
+ {
460
+ file: 'statsAnalyzer/index',
461
+ function: 'compareLastStatsResult'
462
+ },
463
+ newEvent,
464
+ {
465
+ type: mediaType
466
+ }
467
+ );
468
+ }
469
+ };
470
+
392
471
  /**
393
472
  * compares current and previous stats to check if packets are not sent
394
473
  *
@@ -420,18 +499,7 @@ export default class StatsAnalyzer extends EventsScope {
420
499
  }
421
500
  }
422
501
 
423
- if (previousStats.totalPacketsSent === 0 && currentStats.totalPacketsSent > 0) {
424
- this.emit(
425
- {
426
- file: 'statsAnalyzer/index',
427
- function: 'compareLastStatsResult'
428
- },
429
- EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
430
- {
431
- type: mediaType.AUDIO
432
- }
433
- );
434
- }
502
+ this.emitStartStopEvents(mediaType, previousStats.totalPacketsSent, currentStats.totalPacketsSent, true);
435
503
  }
436
504
 
437
505
  if (this.meetingMediaStatus.expected.receiveAudio) {
@@ -446,6 +514,8 @@ export default class StatsAnalyzer extends EventsScope {
446
514
  currentStats.totalSamplesReceived === 0) {
447
515
  LoggerProxy.logger.info(`StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} samples received`);
448
516
  }
517
+
518
+ this.emitStartStopEvents(mediaType, previousStats.totalPacketsReceived, currentStats.totalPacketsReceived, false);
449
519
  }
450
520
 
451
521
  mediaType = STATS.VIDEO_CORRELATE;
@@ -467,18 +537,12 @@ export default class StatsAnalyzer extends EventsScope {
467
537
  }
468
538
  }
469
539
 
470
- if (previousStats.framesSent === 0 && currentStats.framesSent > 0) {
471
- this.emit(
472
- {
473
- file: 'statsAnalyzer/index',
474
- function: 'compareLastStatsResult'
475
- },
476
- EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
477
- {
478
- type: mediaType.VIDEO
479
- }
480
- );
481
- }
540
+ this.emitStartStopEvents(
541
+ mediaType,
542
+ previousStats.framesSent,
543
+ currentStats.framesSent,
544
+ true
545
+ );
482
546
  }
483
547
 
484
548
 
@@ -496,7 +560,7 @@ export default class StatsAnalyzer extends EventsScope {
496
560
  LoggerProxy.logger.info(`StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames received`);
497
561
  }
498
562
 
499
- if (this.statsResults.resolutions[mediaType].recv.framesDecoded === this.lastStatsResults.resolutions[mediaType].recv.framesDecoded || this.statsResults.resolutions[mediaType].send.framesDecoded === 0) {
563
+ if (this.statsResults[mediaType].recv.framesDecoded === this.lastStatsResults[mediaType].recv.framesDecoded || this.statsResults.resolutions[mediaType].send.framesDecoded === 0) {
500
564
  LoggerProxy.logger.info(`StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames decoded`);
501
565
  }
502
566
 
@@ -504,6 +568,13 @@ export default class StatsAnalyzer extends EventsScope {
504
568
  LoggerProxy.logger.info(`StatsAnalyzer:index#compareLastStatsResult --> ${mediaType} frames are getting dropped`);
505
569
  }
506
570
  }
571
+
572
+ this.emitStartStopEvents(
573
+ mediaType,
574
+ previousStats.framesDecoded,
575
+ currentStats.framesDecoded,
576
+ false
577
+ );
507
578
  }
508
579
 
509
580
  mediaType = STATS.SHARE_CORRELATE;
@@ -526,19 +597,6 @@ export default class StatsAnalyzer extends EventsScope {
526
597
  }
527
598
  }
528
599
 
529
- if (previousStats.framesSent === 0 && currentStats.framesSent > 0) {
530
- this.emit(
531
- {
532
- file: 'statsAnalyzer/index',
533
- function: 'compareLastStatsResult'
534
- },
535
- EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
536
- {
537
- type: mediaType.VIDEO
538
- }
539
- );
540
- }
541
-
542
600
  // TODO:need to check receive share value
543
601
  // compare share stats reveived
544
602
  currentStats = this.statsResults[mediaType].recv;
@@ -552,7 +610,7 @@ export default class StatsAnalyzer extends EventsScope {
552
610
  LoggerProxy.logger.info(`StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames received`);
553
611
  }
554
612
 
555
- if (this.statsResults.resolutions[mediaType].recv.framesDecoded === this.lastStatsResults.resolutions[mediaType].recv.framesDecoded || this.statsResults.resolutions[mediaType].send.framesDecoded === 0) {
613
+ if (this.statsResults[mediaType].recv.framesDecoded === this.lastStatsResults[mediaType].recv.framesDecoded || this.statsResults.resolutions[mediaType].send.framesDecoded === 0) {
556
614
  LoggerProxy.logger.info(`StatsAnalyzer:index#compareLastStatsResult --> No ${mediaType} frames decoded`);
557
615
  }
558
616
 
@@ -560,6 +618,10 @@ export default class StatsAnalyzer extends EventsScope {
560
618
  LoggerProxy.logger.info(`StatsAnalyzer:index#compareLastStatsResult --> ${mediaType} frames are getting dropped`);
561
619
  }
562
620
  }
621
+
622
+ // we are not calling emitStartStopEvents() for sending or receiving share because sharing is often started and stopped
623
+ // in meetings and this.meetingMediaStatus.expected values can be out of sync with the actual packet flow
624
+ // so we would send "sharing stopped" events incorrectly
563
625
  }
564
626
  }
565
627
  }
@@ -569,58 +631,60 @@ export default class StatsAnalyzer extends EventsScope {
569
631
  *
570
632
  * @private
571
633
  * @memberof StatsAnalyzer
572
- * @returns {void}
634
+ * @returns {Promise}
573
635
  */
574
636
  getStatsAndParse() {
575
637
  if (!this.peerConnection) {
576
- return;
638
+ return Promise.resolve();
577
639
  }
578
640
 
579
641
  if (this.peerConnection && this.peerConnection.connectionState === CONNECTION_STATE.FAILED) {
580
642
  LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> PeerConnection is in failed state');
581
643
 
582
- return;
644
+ return Promise.resolve();
583
645
  }
584
646
 
585
647
  LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Collecting Stats');
586
- this.peerConnection.videoTransceiver.sender.getStats().then((res) => {
587
- this.filterAndParseGetStatsResults(res, STATS.VIDEO_CORRELATE, true);
588
- });
589
648
 
590
- this.peerConnection.videoTransceiver.receiver.getStats().then((res) => {
591
- this.filterAndParseGetStatsResults(res, STATS.VIDEO_CORRELATE, false);
592
- });
649
+ return Promise.all([
650
+ this.peerConnection.videoTransceiver.sender.getStats().then((res) => {
651
+ this.filterAndParseGetStatsResults(res, STATS.VIDEO_CORRELATE, true);
652
+ }),
593
653
 
594
- this.peerConnection.audioTransceiver.sender.getStats().then((res) => {
595
- this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, true);
596
- });
654
+ this.peerConnection.videoTransceiver.receiver.getStats().then((res) => {
655
+ this.filterAndParseGetStatsResults(res, STATS.VIDEO_CORRELATE, false);
656
+ }),
597
657
 
598
- this.peerConnection.audioTransceiver.receiver.getStats().then((res) => {
599
- this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, false);
600
- });
658
+ this.peerConnection.audioTransceiver.sender.getStats().then((res) => {
659
+ this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, true);
660
+ }),
601
661
 
602
- // TODO: add checks for screen share
603
- this.peerConnection.shareTransceiver.sender.getStats().then((res) => {
604
- this.filterAndParseGetStatsResults(res, STATS.SHARE_CORRELATE, true);
605
- });
662
+ this.peerConnection.audioTransceiver.receiver.getStats().then((res) => {
663
+ this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, false);
664
+ }),
606
665
 
607
- this.peerConnection.shareTransceiver.receiver.getStats().then((res) => {
608
- this.filterAndParseGetStatsResults(res, STATS.SHARE_CORRELATE, false);
609
- });
666
+ // TODO: add checks for screen share
667
+ this.peerConnection.shareTransceiver.sender.getStats().then((res) => {
668
+ this.filterAndParseGetStatsResults(res, STATS.SHARE_CORRELATE, true);
669
+ }),
610
670
 
611
- // updates the current direction of media
612
- this.statsResults[STATS.AUDIO_CORRELATE].direction = this.peerConnection.audioTransceiver.currentDirection;
613
- this.statsResults[STATS.VIDEO_CORRELATE].direction = this.peerConnection.videoTransceiver.currentDirection;
614
- this.statsResults[STATS.SHARE_CORRELATE].direction = this.peerConnection.shareTransceiver.currentDirection;
671
+ this.peerConnection.shareTransceiver.receiver.getStats().then((res) => {
672
+ this.filterAndParseGetStatsResults(res, STATS.SHARE_CORRELATE, false);
673
+ }),
615
674
 
616
- // Process Stats results every 5 seconds
617
- this.compareLastStatsResult();
675
+ ]).then(() => {
676
+ this.statsResults[STATS.AUDIO_CORRELATE].direction = this.peerConnection.audioTransceiver.currentDirection;
677
+ this.statsResults[STATS.VIDEO_CORRELATE].direction = this.peerConnection.videoTransceiver.currentDirection;
678
+ this.statsResults[STATS.SHARE_CORRELATE].direction = this.peerConnection.shareTransceiver.currentDirection;
618
679
 
619
- // Save the last results to compare with the current
620
- // DO Deep copy, for some reason it takes the reference all the time rather then old value set
621
- this.lastStatsResults = JSON.parse(JSON.stringify(this.statsResults));
680
+ // Process Stats results every 5 seconds
681
+ this.compareLastStatsResult();
682
+
683
+ // Save the last results to compare with the current
684
+ this.lastStatsResults = JSON.parse(JSON.stringify(this.statsResults));
622
685
 
623
- LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Finished Collecting Stats');
686
+ LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Finished Collecting Stats');
687
+ });
624
688
  }
625
689
 
626
690
  /**
@@ -674,6 +738,7 @@ export default class StatsAnalyzer extends EventsScope {
674
738
  this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;
675
739
  this.statsResults[mediaType][sendrecvType].totalPacketsSent = result.packetsSent;
676
740
  this.statsResults[mediaType][sendrecvType].totalFirCount = result.firCount;
741
+ this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;
677
742
  this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;
678
743
  this.statsResults[mediaType][sendrecvType].encoderImplementation = result.encoderImplementation;
679
744
  this.statsResults[mediaType][sendrecvType].qualityLimitationReason = result.qualityLimitationReason;
@@ -1,9 +1,10 @@
1
1
  /* globals navigator */
2
2
  import {assert} from '@webex/test-helper-chai';
3
3
  import {skipInNode} from '@webex/test-helper-mocha';
4
- import BrowserDetection from '@webex/plugin-meetings/src/common/browser-detection';
5
4
  import sinon from 'sinon';
6
5
 
6
+ import BrowserDetection from '@webex/plugin-meetings/src/common/browser-detection';
7
+
7
8
  import DEFAULT_RESOLUTIONS from '../../../src/config';
8
9
  import testUtils from '../../utils/testUtils';
9
10
 
@@ -297,65 +298,6 @@ skipInNode(describe)('plugin-meetings', () => {
297
298
  assert.exists(alice.meeting.members.selfId, 'selfId not present');
298
299
  });
299
300
 
300
- it('check for getStats', () => {
301
- const options = {
302
- useConfig: true,
303
- senders: [
304
- {
305
- id: 'audioSend',
306
- history: true,
307
- correlate: 'audio', // NECESSARY KEY
308
- onData: (k) => { console.log('audioSend stats data cb', k); }
309
- }
310
- ],
311
- receivers: [
312
- {
313
- id: 'audioRecv',
314
- history: true,
315
- correlate: 'audio', // NECESSARY KEY
316
- onData: (k) => { console.log('audioRecv stats data cb', k); }
317
- }
318
- ]
319
- };
320
-
321
- alice.meeting.getStats(options, true);
322
- bob.meeting.getStats(options, true);
323
-
324
- assert.exists(alice.meeting.getStats());
325
- assert.exists(bob.meeting.getStats());
326
-
327
- assert.exists(alice.meeting.getStats().getSender('audioSend'));
328
- assert.exists(alice.meeting.getStats().getReceiver('audioRecv'));
329
- assert.exists(bob.meeting.getStats().getSender('audioSend'));
330
- assert.exists(bob.meeting.getStats().getReceiver('audioRecv'));
331
-
332
- return testUtils.waitUntil(7000);
333
- });
334
-
335
- it('check populated get stats data', () => {
336
- assert.ok(alice.meeting.getStats().getSender('audioSend').getHistory().get().length);
337
- assert.ok(alice.meeting.getStats().getReceiver('audioRecv').getHistory().get().length);
338
- assert.ok(bob.meeting.getStats().getSender('audioSend').getHistory().get().length);
339
- assert.ok(bob.meeting.getStats().getReceiver('audioRecv').getHistory().get().length);
340
- });
341
-
342
- it('analyze getStats data', () => {
343
- const aliceThirtySecondsData1 = alice.meeting.getStats().getSender('audioSend').getHistory().getSlice(5);
344
- const analysis1 = alice.webex.meetings.getAnalyzer().analyze(aliceThirtySecondsData1, {analysisKeys: [{key: 'bytesSent', check: 'increasing', prop: 'rtpOutAudio'}]});
345
- const aliceThirtySecondsData2 = alice.meeting.getStats().getReceiver('audioRecv').getHistory().getSlice(5);
346
- const analysis2 = alice.webex.meetings.getAnalyzer().analyze(aliceThirtySecondsData2, {analysisKeys: [{key: 'bytesReceived', check: 'increasing', prop: 'rtpInAudio'}]});
347
-
348
- const bobThirtySecondsData3 = bob.meeting.getStats().getSender('audioSend').getHistory().getSlice(5);
349
- const analysis3 = alice.webex.meetings.getAnalyzer().analyze(bobThirtySecondsData3, {analysisKeys: [{key: 'bytesSent', check: 'increasing', prop: 'rtpOutAudio'}]});
350
- const bobThirtySecondsData4 = bob.meeting.getStats().getReceiver('audioRecv').getHistory().getSlice(5);
351
- const analysis4 = alice.webex.meetings.getAnalyzer().analyze(bobThirtySecondsData4, {analysisKeys: [{key: 'bytesReceived', check: 'increasing', prop: 'rtpInAudio'}]});
352
-
353
- assert.equal(analysis1.valid, true);
354
- assert.equal(analysis2.valid, true);
355
- assert.equal(analysis3.valid, true);
356
- assert.equal(analysis4.valid, true);
357
- });
358
-
359
301
  it('alice Audio Mute ', () => {
360
302
  const checkEvent = (event) => !!event.delta.updated.find((member) => alice.meeting.members.selfId === member.id && member.isAudioMuted === true);
361
303
 
@@ -774,13 +716,6 @@ skipInNode(describe)('plugin-meetings', () => {
774
716
  throw e;
775
717
  }));
776
718
 
777
- it('shut down get stats', () => {
778
- alice.meeting.stopStats();
779
- bob.meeting.stopStats();
780
- assert.ok(!alice.meeting.stats);
781
- assert.ok(!bob.meeting.stats);
782
- });
783
-
784
719
  it('leave on the meeting object', () => {
785
720
  const checkInactive = (result) => result.reason === 'CALL_INACTIVE';
786
721