@webex/plugin-meetings 2.18.0 → 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.
- package/README.md +0 -300
- package/dist/constants.js +3 -206
- package/dist/constants.js.map +1 -1
- package/dist/meeting/effectsState.js +1 -2
- package/dist/meeting/effectsState.js.map +1 -1
- package/dist/meeting/index.js +366 -496
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +4 -213
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +0 -28
- package/dist/meetings/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +145 -86
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/package.json +6 -8
- package/src/constants.ts +1 -214
- package/src/meeting/effectsState.js +1 -2
- package/src/meeting/index.js +120 -213
- package/src/meeting/util.js +4 -252
- package/src/meetings/index.js +0 -22
- package/src/statsAnalyzer/index.js +164 -99
- package/test/integration/spec/journey.js +2 -67
- package/test/unit/spec/meeting/effectsState.js +2 -1
- package/test/unit/spec/meeting/index.js +88 -29
- package/test/unit/spec/meeting/utils.js +0 -2
- package/test/unit/spec/stats-analyzer/index.js +209 -1
- package/dist/analyzer/analyzer.js +0 -113
- package/dist/analyzer/analyzer.js.map +0 -1
- package/dist/analyzer/calculator.js +0 -87
- package/dist/analyzer/calculator.js.map +0 -1
- package/dist/metrics/mqa-processor.js +0 -170
- package/dist/metrics/mqa-processor.js.map +0 -1
- package/dist/stats/data.js +0 -93
- package/dist/stats/data.js.map +0 -1
- package/dist/stats/events.js +0 -222
- package/dist/stats/events.js.map +0 -1
- package/dist/stats/filter.js +0 -84
- package/dist/stats/filter.js.map +0 -1
- package/dist/stats/history.js +0 -147
- package/dist/stats/history.js.map +0 -1
- package/dist/stats/index.js +0 -425
- package/dist/stats/index.js.map +0 -1
- package/dist/stats/metrics.js +0 -112
- package/dist/stats/metrics.js.map +0 -1
- package/dist/stats/stats.js +0 -592
- package/dist/stats/stats.js.map +0 -1
- package/dist/stats/stream.js +0 -156
- package/dist/stats/stream.js.map +0 -1
- package/dist/stats/transformer.js +0 -126
- package/dist/stats/transformer.js.map +0 -1
- package/dist/stats/util.js +0 -64
- package/dist/stats/util.js.map +0 -1
- package/src/analyzer/analyzer.js +0 -78
- package/src/analyzer/calculator.js +0 -77
- package/src/metrics/mqa-processor.js +0 -118
- package/src/stats/data.js +0 -56
- package/src/stats/events.js +0 -185
- package/src/stats/filter.js +0 -40
- package/src/stats/history.js +0 -107
- package/src/stats/index.js +0 -320
- package/src/stats/metrics.js +0 -95
- package/src/stats/stats.js +0 -477
- package/src/stats/stream.js +0 -108
- package/src/stats/transformer.js +0 -109
- 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 {
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
280
|
-
this.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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.
|
|
318
|
+
this.statsInterval = undefined;
|
|
303
319
|
}
|
|
304
320
|
|
|
305
321
|
if (this.mqaInterval) {
|
|
306
|
-
this.sendMqaData();
|
|
307
322
|
clearInterval(this.mqaInterval);
|
|
308
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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
|
-
|
|
591
|
-
this.
|
|
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
|
-
|
|
595
|
-
|
|
596
|
-
|
|
654
|
+
this.peerConnection.videoTransceiver.receiver.getStats().then((res) => {
|
|
655
|
+
this.filterAndParseGetStatsResults(res, STATS.VIDEO_CORRELATE, false);
|
|
656
|
+
}),
|
|
597
657
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
658
|
+
this.peerConnection.audioTransceiver.sender.getStats().then((res) => {
|
|
659
|
+
this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, true);
|
|
660
|
+
}),
|
|
601
661
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
});
|
|
662
|
+
this.peerConnection.audioTransceiver.receiver.getStats().then((res) => {
|
|
663
|
+
this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, false);
|
|
664
|
+
}),
|
|
606
665
|
|
|
607
|
-
|
|
608
|
-
this.
|
|
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
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|
-
|
|
617
|
-
|
|
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
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {assert} from '@webex/test-helper-chai';
|
|
3
3
|
import sinon from 'sinon';
|
|
4
4
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
5
|
+
import {BNR_STATUS} from '@webex/plugin-meetings/src/constants';
|
|
5
6
|
|
|
6
7
|
import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
7
|
-
import {BNR_STATUS} from '@webex/plugin-meetings/src/constants';
|
|
8
8
|
import Meeting from '@webex/plugin-meetings/src/meeting';
|
|
9
9
|
import Meetings from '@webex/plugin-meetings';
|
|
10
10
|
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
@@ -178,6 +178,7 @@ describe('plugin-meetings', () => {
|
|
|
178
178
|
|
|
179
179
|
meeting.addMedia = sinon.stub().returns(Promise.resolve());
|
|
180
180
|
meeting.getMediaStreams = sinon.stub().returns(Promise.resolve());
|
|
181
|
+
sinon.stub(meeting, 'effects').value(effects);
|
|
181
182
|
sinon.replace(meeting, 'addMedia', () => {
|
|
182
183
|
sinon.stub(meeting.mediaProperties, 'audioTrack').value(fakeMediaTrack());
|
|
183
184
|
sinon.stub(meeting.mediaProperties, 'mediaDirection').value({
|