@webex/plugin-meetings 3.3.1-next.2 → 3.3.1-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +7 -2
- package/dist/breakouts/index.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/media/MediaConnectionAwaiter.js +50 -13
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/mediaQualityMetrics/config.js +16 -6
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/connectionStateHandler.js +67 -0
- package/dist/meeting/connectionStateHandler.js.map +1 -0
- package/dist/meeting/index.js +98 -46
- package/dist/meeting/index.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +57 -0
- package/dist/metrics/index.js.map +1 -1
- package/dist/reachability/clusterReachability.js +108 -53
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +415 -56
- package/dist/reachability/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +81 -27
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +36 -10
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/media/MediaConnectionAwaiter.d.ts +18 -4
- package/dist/types/mediaQualityMetrics/config.d.ts +11 -0
- package/dist/types/meeting/connectionStateHandler.d.ts +30 -0
- package/dist/types/meeting/index.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/metrics/index.d.ts +15 -0
- package/dist/types/reachability/clusterReachability.d.ts +31 -3
- package/dist/types/reachability/index.d.ts +93 -2
- package/dist/types/statsAnalyzer/index.d.ts +15 -6
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +17 -4
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -22
- package/src/breakouts/index.ts +7 -1
- package/src/media/MediaConnectionAwaiter.ts +66 -11
- package/src/mediaQualityMetrics/config.ts +14 -3
- package/src/meeting/connectionStateHandler.ts +65 -0
- package/src/meeting/index.ts +72 -14
- package/src/metrics/constants.ts +1 -0
- package/src/metrics/index.ts +44 -0
- package/src/reachability/clusterReachability.ts +86 -25
- package/src/reachability/index.ts +316 -27
- package/src/statsAnalyzer/index.ts +85 -24
- package/src/statsAnalyzer/mqaUtil.ts +55 -7
- package/test/unit/spec/breakouts/index.ts +51 -32
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +90 -32
- package/test/unit/spec/meeting/connectionStateHandler.ts +102 -0
- package/test/unit/spec/meeting/index.js +158 -36
- package/test/unit/spec/metrics/index.js +126 -0
- package/test/unit/spec/reachability/clusterReachability.ts +116 -22
- package/test/unit/spec/reachability/index.ts +1153 -84
- package/test/unit/spec/stats-analyzer/index.js +647 -319
|
@@ -10,12 +10,21 @@ import testUtils from '../../../utils/testUtils';
|
|
|
10
10
|
import {MEDIA_DEVICES, MQA_INTERVAL, _UNKNOWN_} from '@webex/plugin-meetings/src/constants';
|
|
11
11
|
import LoggerProxy from '../../../../src/common/logs/logger-proxy';
|
|
12
12
|
import LoggerConfig from '../../../../src/common/logs/logger-config';
|
|
13
|
+
import {CpuInfo} from '@webex/web-capabilities';
|
|
13
14
|
|
|
14
15
|
const {assert} = chai;
|
|
15
16
|
|
|
16
17
|
chai.use(chaiAsPromised);
|
|
17
18
|
sinon.assert.expose(chai.assert, {prefix: ''});
|
|
18
19
|
|
|
20
|
+
const startStatsAnalyzer = async ({statsAnalyzer, mediaStatus, lastEmittedEvents = {}, pc}) => {
|
|
21
|
+
statsAnalyzer.updateMediaStatus(mediaStatus);
|
|
22
|
+
statsAnalyzer.startAnalyzer(pc);
|
|
23
|
+
statsAnalyzer.lastEmittedStartStopEvent = lastEmittedEvents;
|
|
24
|
+
|
|
25
|
+
await testUtils.flushPromises();
|
|
26
|
+
};
|
|
27
|
+
|
|
19
28
|
describe('plugin-meetings', () => {
|
|
20
29
|
describe('StatsAnalyzer', () => {
|
|
21
30
|
describe('parseStatsResult', () => {
|
|
@@ -29,10 +38,12 @@ describe('plugin-meetings', () => {
|
|
|
29
38
|
const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
30
39
|
|
|
31
40
|
statsAnalyzer = new StatsAnalyzer(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
{
|
|
42
|
+
config: initialConfig,
|
|
43
|
+
receiveSlotCallback: () => ({}),
|
|
44
|
+
networkQualityMonitor,
|
|
45
|
+
statsResults: defaultStats,
|
|
46
|
+
},
|
|
36
47
|
);
|
|
37
48
|
});
|
|
38
49
|
|
|
@@ -89,7 +100,7 @@ describe('plugin-meetings', () => {
|
|
|
89
100
|
requestedBitrate: 10000,
|
|
90
101
|
},
|
|
91
102
|
'audio-send',
|
|
92
|
-
true
|
|
103
|
+
true,
|
|
93
104
|
);
|
|
94
105
|
|
|
95
106
|
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.headerBytesSent, 25000);
|
|
@@ -126,7 +137,7 @@ describe('plugin-meetings', () => {
|
|
|
126
137
|
requestedBitrate: 50000,
|
|
127
138
|
},
|
|
128
139
|
'video-send',
|
|
129
|
-
true
|
|
140
|
+
true,
|
|
130
141
|
);
|
|
131
142
|
|
|
132
143
|
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.headerBytesSent, 50000);
|
|
@@ -136,11 +147,11 @@ describe('plugin-meetings', () => {
|
|
|
136
147
|
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.requestedBitrate, 50000);
|
|
137
148
|
assert.strictEqual(
|
|
138
149
|
statsAnalyzer.statsResults['video-send'].send.totalRtxPacketsSent,
|
|
139
|
-
10
|
|
150
|
+
10,
|
|
140
151
|
);
|
|
141
152
|
assert.strictEqual(
|
|
142
153
|
statsAnalyzer.statsResults['video-send'].send.totalRtxBytesSent,
|
|
143
|
-
500
|
|
154
|
+
500,
|
|
144
155
|
);
|
|
145
156
|
});
|
|
146
157
|
|
|
@@ -183,12 +194,12 @@ describe('plugin-meetings', () => {
|
|
|
183
194
|
requestedBitrate: 10000,
|
|
184
195
|
},
|
|
185
196
|
'audio-recv-1',
|
|
186
|
-
false
|
|
197
|
+
false,
|
|
187
198
|
);
|
|
188
199
|
|
|
189
200
|
assert.strictEqual(
|
|
190
201
|
statsAnalyzer.statsResults['audio-recv-1'].recv.totalPacketsReceived,
|
|
191
|
-
12
|
|
202
|
+
12,
|
|
192
203
|
);
|
|
193
204
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsDiscarded, 1);
|
|
194
205
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsReceived, 1);
|
|
@@ -196,18 +207,18 @@ describe('plugin-meetings', () => {
|
|
|
196
207
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.requestedBitrate, 10000);
|
|
197
208
|
assert.strictEqual(
|
|
198
209
|
statsAnalyzer.statsResults['audio-recv-1'].recv.headerBytesReceived,
|
|
199
|
-
250
|
|
210
|
+
250,
|
|
200
211
|
);
|
|
201
212
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.audioLevel, 0);
|
|
202
213
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalAudioEnergy, 133);
|
|
203
214
|
assert.strictEqual(
|
|
204
215
|
statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesReceived,
|
|
205
|
-
300000
|
|
216
|
+
300000,
|
|
206
217
|
);
|
|
207
218
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesDecoded, 0);
|
|
208
219
|
assert.strictEqual(
|
|
209
220
|
statsAnalyzer.statsResults['audio-recv-1'].recv.concealedSamples,
|
|
210
|
-
200000
|
|
221
|
+
200000,
|
|
211
222
|
);
|
|
212
223
|
});
|
|
213
224
|
|
|
@@ -243,7 +254,7 @@ describe('plugin-meetings', () => {
|
|
|
243
254
|
retransmittedPacketsReceived: 10,
|
|
244
255
|
},
|
|
245
256
|
'video-recv',
|
|
246
|
-
false
|
|
257
|
+
false,
|
|
247
258
|
);
|
|
248
259
|
|
|
249
260
|
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalPacketsReceived, 1500);
|
|
@@ -275,7 +286,7 @@ describe('plugin-meetings', () => {
|
|
|
275
286
|
type: 'media-source',
|
|
276
287
|
},
|
|
277
288
|
'audio-send',
|
|
278
|
-
true
|
|
289
|
+
true,
|
|
279
290
|
);
|
|
280
291
|
|
|
281
292
|
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.audioLevel, 0.03);
|
|
@@ -322,15 +333,17 @@ describe('plugin-meetings', () => {
|
|
|
322
333
|
const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
323
334
|
|
|
324
335
|
statsAnalyzer = new StatsAnalyzer(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
336
|
+
{
|
|
337
|
+
config: initialConfig,
|
|
338
|
+
receiveSlotCallback: () => ({}),
|
|
339
|
+
networkQualityMonitor,
|
|
340
|
+
statsResults: defaultStats,
|
|
341
|
+
},
|
|
329
342
|
);
|
|
330
343
|
|
|
331
344
|
sandBoxSpy = sandbox.spy(
|
|
332
345
|
statsAnalyzer.networkQualityMonitor,
|
|
333
|
-
'determineUplinkNetworkQuality'
|
|
346
|
+
'determineUplinkNetworkQuality',
|
|
334
347
|
);
|
|
335
348
|
});
|
|
336
349
|
|
|
@@ -347,7 +360,7 @@ describe('plugin-meetings', () => {
|
|
|
347
360
|
mediaType: 'video-send-1',
|
|
348
361
|
remoteRtpResults: statusResult,
|
|
349
362
|
statsAnalyzerCurrentStats: statsAnalyzer.statsResults,
|
|
350
|
-
})
|
|
363
|
+
}),
|
|
351
364
|
);
|
|
352
365
|
});
|
|
353
366
|
});
|
|
@@ -381,6 +394,24 @@ describe('plugin-meetings', () => {
|
|
|
381
394
|
};
|
|
382
395
|
};
|
|
383
396
|
|
|
397
|
+
const registerStatsAnalyzerEvents = (statsAnalyzer) => {
|
|
398
|
+
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
|
|
399
|
+
receivedEventsData.local.started = data;
|
|
400
|
+
});
|
|
401
|
+
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STOPPED, (data) => {
|
|
402
|
+
receivedEventsData.local.stopped = data;
|
|
403
|
+
});
|
|
404
|
+
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STARTED, (data) => {
|
|
405
|
+
receivedEventsData.remote.started = data;
|
|
406
|
+
});
|
|
407
|
+
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STOPPED, (data) => {
|
|
408
|
+
receivedEventsData.remote.stopped = data;
|
|
409
|
+
});
|
|
410
|
+
statsAnalyzer.on(EVENTS.MEDIA_QUALITY, ({data}) => {
|
|
411
|
+
mqeData = data;
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
|
|
384
415
|
before(() => {
|
|
385
416
|
LoggerConfig.set({enable: false});
|
|
386
417
|
LoggerProxy.set();
|
|
@@ -404,6 +435,7 @@ describe('plugin-meetings', () => {
|
|
|
404
435
|
type: 'outbound-rtp',
|
|
405
436
|
bytesSent: 1,
|
|
406
437
|
packetsSent: 0,
|
|
438
|
+
isRequested: true,
|
|
407
439
|
},
|
|
408
440
|
{
|
|
409
441
|
type: 'remote-inbound-rtp',
|
|
@@ -437,6 +469,8 @@ describe('plugin-meetings', () => {
|
|
|
437
469
|
fecPacketsReceived: 0,
|
|
438
470
|
packetsLost: 0,
|
|
439
471
|
packetsReceived: 0,
|
|
472
|
+
isRequested: true,
|
|
473
|
+
lastRequestedUpdateTimestamp: 0,
|
|
440
474
|
},
|
|
441
475
|
{
|
|
442
476
|
type: 'remote-outbound-rtp',
|
|
@@ -470,6 +504,8 @@ describe('plugin-meetings', () => {
|
|
|
470
504
|
bytesSent: 1,
|
|
471
505
|
framesSent: 0,
|
|
472
506
|
packetsSent: 0,
|
|
507
|
+
isRequested: true,
|
|
508
|
+
lastRequestedUpdateTimestamp: 0,
|
|
473
509
|
},
|
|
474
510
|
{
|
|
475
511
|
type: 'remote-inbound-rtp',
|
|
@@ -505,6 +541,10 @@ describe('plugin-meetings', () => {
|
|
|
505
541
|
framesReceived: 0,
|
|
506
542
|
packetsLost: 0,
|
|
507
543
|
packetsReceived: 0,
|
|
544
|
+
isRequested: true,
|
|
545
|
+
lastRequestedUpdateTimestamp: 0,
|
|
546
|
+
isActiveSpeaker: false,
|
|
547
|
+
lastActiveSpeakerUpdateTimestamp: 0,
|
|
508
548
|
},
|
|
509
549
|
{
|
|
510
550
|
type: 'remote-outbound-rtp',
|
|
@@ -538,6 +578,9 @@ describe('plugin-meetings', () => {
|
|
|
538
578
|
bytesSent: 1,
|
|
539
579
|
framesSent: 0,
|
|
540
580
|
packetsSent: 0,
|
|
581
|
+
isRequested: true,
|
|
582
|
+
lastRequestedUpdateTimestamp: 0,
|
|
583
|
+
encoderImplementation: 'fake-encoder',
|
|
541
584
|
},
|
|
542
585
|
{
|
|
543
586
|
type: 'remote-inbound-rtp',
|
|
@@ -573,6 +616,8 @@ describe('plugin-meetings', () => {
|
|
|
573
616
|
framesReceived: 0,
|
|
574
617
|
packetsLost: 0,
|
|
575
618
|
packetsReceived: 0,
|
|
619
|
+
isRequested: true,
|
|
620
|
+
lastRequestedUpdateTimestamp: 0,
|
|
576
621
|
},
|
|
577
622
|
{
|
|
578
623
|
type: 'remote-outbound-rtp',
|
|
@@ -622,23 +667,11 @@ describe('plugin-meetings', () => {
|
|
|
622
667
|
|
|
623
668
|
networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
624
669
|
|
|
625
|
-
statsAnalyzer = new StatsAnalyzer(
|
|
626
|
-
|
|
627
|
-
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
|
|
628
|
-
receivedEventsData.local.started = data;
|
|
629
|
-
});
|
|
630
|
-
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STOPPED, (data) => {
|
|
631
|
-
receivedEventsData.local.stopped = data;
|
|
632
|
-
});
|
|
633
|
-
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STARTED, (data) => {
|
|
634
|
-
receivedEventsData.remote.started = data;
|
|
635
|
-
});
|
|
636
|
-
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STOPPED, (data) => {
|
|
637
|
-
receivedEventsData.remote.stopped = data;
|
|
638
|
-
});
|
|
639
|
-
statsAnalyzer.on(EVENTS.MEDIA_QUALITY, ({data}) => {
|
|
640
|
-
mqeData = data;
|
|
670
|
+
statsAnalyzer = new StatsAnalyzer({
|
|
671
|
+
config: initialConfig, receiveSlotCallback: () => receiveSlot, networkQualityMonitor,
|
|
641
672
|
});
|
|
673
|
+
|
|
674
|
+
registerStatsAnalyzerEvents(statsAnalyzer);
|
|
642
675
|
});
|
|
643
676
|
|
|
644
677
|
afterEach(() => {
|
|
@@ -646,20 +679,12 @@ describe('plugin-meetings', () => {
|
|
|
646
679
|
clock.restore();
|
|
647
680
|
});
|
|
648
681
|
|
|
649
|
-
const startStatsAnalyzer = async (mediaStatus, lastEmittedEvents) => {
|
|
650
|
-
statsAnalyzer.updateMediaStatus(mediaStatus);
|
|
651
|
-
statsAnalyzer.startAnalyzer(pc);
|
|
652
|
-
statsAnalyzer.lastEmittedStartStopEvent = lastEmittedEvents || {};
|
|
653
|
-
|
|
654
|
-
await testUtils.flushPromises();
|
|
655
|
-
};
|
|
656
|
-
|
|
657
682
|
const mergeProperties = (
|
|
658
683
|
target,
|
|
659
684
|
properties,
|
|
660
685
|
keyValue = 'fake-candidate-id',
|
|
661
686
|
matchKey = 'type',
|
|
662
|
-
matchValue = 'local-candidate'
|
|
687
|
+
matchValue = 'local-candidate',
|
|
663
688
|
) => {
|
|
664
689
|
for (let key in target) {
|
|
665
690
|
if (target.hasOwnProperty(key)) {
|
|
@@ -704,7 +729,15 @@ describe('plugin-meetings', () => {
|
|
|
704
729
|
};
|
|
705
730
|
|
|
706
731
|
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for audio', async () => {
|
|
707
|
-
await startStatsAnalyzer({
|
|
732
|
+
await startStatsAnalyzer({
|
|
733
|
+
statsAnalyzer,
|
|
734
|
+
pc,
|
|
735
|
+
mediaStatus: {
|
|
736
|
+
expected: {
|
|
737
|
+
sendAudio: true,
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
});
|
|
708
741
|
|
|
709
742
|
// check that we haven't received any events yet
|
|
710
743
|
checkReceivedEvent({expected: {}});
|
|
@@ -724,7 +757,7 @@ describe('plugin-meetings', () => {
|
|
|
724
757
|
});
|
|
725
758
|
|
|
726
759
|
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for video', async () => {
|
|
727
|
-
await startStatsAnalyzer({expected: {sendVideo: true}});
|
|
760
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {sendVideo: true}}});
|
|
728
761
|
|
|
729
762
|
// check that we haven't received any events yet
|
|
730
763
|
checkReceivedEvent({expected: {}});
|
|
@@ -744,7 +777,7 @@ describe('plugin-meetings', () => {
|
|
|
744
777
|
});
|
|
745
778
|
|
|
746
779
|
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for share', async () => {
|
|
747
|
-
await startStatsAnalyzer({expected: {sendShare: true}});
|
|
780
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {sendShare: true}}});
|
|
748
781
|
|
|
749
782
|
// check that we haven't received any events yet
|
|
750
783
|
checkReceivedEvent({expected: {}});
|
|
@@ -764,7 +797,7 @@ describe('plugin-meetings', () => {
|
|
|
764
797
|
});
|
|
765
798
|
|
|
766
799
|
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for audio', async () => {
|
|
767
|
-
await startStatsAnalyzer({expected: {receiveAudio: true}});
|
|
800
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveAudio: true}}});
|
|
768
801
|
|
|
769
802
|
// check that we haven't received any events yet
|
|
770
803
|
checkReceivedEvent({expected: {}});
|
|
@@ -784,7 +817,7 @@ describe('plugin-meetings', () => {
|
|
|
784
817
|
});
|
|
785
818
|
|
|
786
819
|
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for video', async () => {
|
|
787
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
820
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
788
821
|
|
|
789
822
|
// check that we haven't received any events yet
|
|
790
823
|
checkReceivedEvent({expected: {}});
|
|
@@ -804,7 +837,7 @@ describe('plugin-meetings', () => {
|
|
|
804
837
|
});
|
|
805
838
|
|
|
806
839
|
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for share', async () => {
|
|
807
|
-
await startStatsAnalyzer({expected: {receiveShare: true}});
|
|
840
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveShare: true}}});
|
|
808
841
|
|
|
809
842
|
// check that we haven't received any events yet
|
|
810
843
|
checkReceivedEvent({expected: {}});
|
|
@@ -824,7 +857,7 @@ describe('plugin-meetings', () => {
|
|
|
824
857
|
});
|
|
825
858
|
|
|
826
859
|
it('emits the correct MEDIA_QUALITY events', async () => {
|
|
827
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
860
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
828
861
|
|
|
829
862
|
await progressTime();
|
|
830
863
|
|
|
@@ -833,7 +866,7 @@ describe('plugin-meetings', () => {
|
|
|
833
866
|
});
|
|
834
867
|
|
|
835
868
|
it('emits the correct transportType in MEDIA_QUALITY events', async () => {
|
|
836
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
869
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
837
870
|
|
|
838
871
|
await progressTime();
|
|
839
872
|
|
|
@@ -847,7 +880,7 @@ describe('plugin-meetings', () => {
|
|
|
847
880
|
fakeStats.audio.receivers[0].report[4].relayProtocol = 'tls';
|
|
848
881
|
fakeStats.video.receivers[0].report[4].relayProtocol = 'tls';
|
|
849
882
|
|
|
850
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
883
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
851
884
|
|
|
852
885
|
await progressTime();
|
|
853
886
|
|
|
@@ -856,19 +889,19 @@ describe('plugin-meetings', () => {
|
|
|
856
889
|
});
|
|
857
890
|
|
|
858
891
|
it('emits the correct peripherals in MEDIA_QUALITY events', async () => {
|
|
859
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
892
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
860
893
|
|
|
861
894
|
await progressTime();
|
|
862
895
|
|
|
863
896
|
assert.strictEqual(
|
|
864
897
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
|
|
865
898
|
.information,
|
|
866
|
-
'fake-microphone'
|
|
899
|
+
'fake-microphone',
|
|
867
900
|
);
|
|
868
901
|
assert.strictEqual(
|
|
869
902
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
|
|
870
903
|
.information,
|
|
871
|
-
'fake-camera'
|
|
904
|
+
'fake-camera',
|
|
872
905
|
);
|
|
873
906
|
});
|
|
874
907
|
|
|
@@ -876,30 +909,33 @@ describe('plugin-meetings', () => {
|
|
|
876
909
|
fakeStats.audio.senders[0].localTrackLabel = undefined;
|
|
877
910
|
fakeStats.video.senders[0].localTrackLabel = undefined;
|
|
878
911
|
|
|
879
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
912
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
880
913
|
|
|
881
914
|
await progressTime();
|
|
882
915
|
|
|
883
916
|
assert.strictEqual(
|
|
884
917
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
|
|
885
918
|
.information,
|
|
886
|
-
_UNKNOWN_
|
|
919
|
+
_UNKNOWN_,
|
|
887
920
|
);
|
|
888
921
|
assert.strictEqual(
|
|
889
922
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
|
|
890
923
|
.information,
|
|
891
|
-
_UNKNOWN_
|
|
924
|
+
_UNKNOWN_,
|
|
892
925
|
);
|
|
893
926
|
});
|
|
894
927
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
await startStatsAnalyzer();
|
|
928
|
+
describe('frame rate reporting in stats analyzer', () => {
|
|
929
|
+
beforeEach(async () => {
|
|
930
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
it('should report a zero frame rate for both transmitted and received video at the start', async () => {
|
|
898
934
|
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.transmittedFrameRate, 0);
|
|
899
935
|
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.receivedFrameRate, 0);
|
|
900
936
|
});
|
|
901
937
|
|
|
902
|
-
it('
|
|
938
|
+
it('should accurately report the transmitted and received frame rate after video frames are processed', async () => {
|
|
903
939
|
fakeStats.video.senders[0].report[0].framesSent += 300;
|
|
904
940
|
fakeStats.video.receivers[0].report[0].framesReceived += 300;
|
|
905
941
|
await progressTime(MQA_INTERVAL);
|
|
@@ -910,9 +946,12 @@ describe('plugin-meetings', () => {
|
|
|
910
946
|
});
|
|
911
947
|
});
|
|
912
948
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
await startStatsAnalyzer();
|
|
949
|
+
describe('RTP packets count in stats analyzer', () => {
|
|
950
|
+
beforeEach(async () => {
|
|
951
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it('should report zero RTP packets for all streams at the start of the stats analyzer', async () => {
|
|
916
955
|
assert.strictEqual(mqeData.audioTransmit[0].common.rtpPackets, 0);
|
|
917
956
|
assert.strictEqual(mqeData.audioTransmit[0].streams[0].common.rtpPackets, 0);
|
|
918
957
|
assert.strictEqual(mqeData.audioReceive[0].common.rtpPackets, 0);
|
|
@@ -923,7 +962,7 @@ describe('plugin-meetings', () => {
|
|
|
923
962
|
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.rtpPackets, 0);
|
|
924
963
|
});
|
|
925
964
|
|
|
926
|
-
it('after packets are sent', async () => {
|
|
965
|
+
it('should update the RTP packets count correctly after audio and video packets are sent', async () => {
|
|
927
966
|
fakeStats.audio.senders[0].report[0].packetsSent += 5;
|
|
928
967
|
fakeStats.video.senders[0].report[0].packetsSent += 5;
|
|
929
968
|
await progressTime(MQA_INTERVAL);
|
|
@@ -934,7 +973,7 @@ describe('plugin-meetings', () => {
|
|
|
934
973
|
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.rtpPackets, 5);
|
|
935
974
|
});
|
|
936
975
|
|
|
937
|
-
it('after packets are received', async () => {
|
|
976
|
+
it('should update the RTP packets count correctly after audio and video packets are received', async () => {
|
|
938
977
|
fakeStats.audio.senders[0].report[0].packetsSent += 10;
|
|
939
978
|
fakeStats.video.senders[0].report[0].packetsSent += 10;
|
|
940
979
|
fakeStats.audio.receivers[0].report[0].packetsReceived += 10;
|
|
@@ -948,38 +987,83 @@ describe('plugin-meetings', () => {
|
|
|
948
987
|
});
|
|
949
988
|
});
|
|
950
989
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
await startStatsAnalyzer();
|
|
990
|
+
describe('FEC packet reporting in stats analyzer', () => {
|
|
991
|
+
beforeEach(async () => {
|
|
992
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
it('should initially report zero FEC packets at the start of the stats analyzer', async () => {
|
|
954
996
|
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 0);
|
|
955
997
|
});
|
|
956
998
|
|
|
957
|
-
it('
|
|
999
|
+
it('should accurately report the count of FEC packets received', async () => {
|
|
958
1000
|
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 5;
|
|
959
1001
|
await progressTime(MQA_INTERVAL);
|
|
960
1002
|
|
|
961
1003
|
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 5);
|
|
962
1004
|
});
|
|
963
1005
|
|
|
964
|
-
it('
|
|
1006
|
+
it('should accurately update and reset the FEC packet count based on received packets over MQA intervals', async () => {
|
|
965
1007
|
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 15;
|
|
966
|
-
fakeStats.audio.receivers[0].report[0].fecPacketsDiscarded += 5;
|
|
967
1008
|
await progressTime(MQA_INTERVAL);
|
|
1009
|
+
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 15);
|
|
968
1010
|
|
|
969
|
-
|
|
1011
|
+
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 45;
|
|
1012
|
+
await progressTime(MQA_INTERVAL);
|
|
1013
|
+
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 45);
|
|
1014
|
+
|
|
1015
|
+
await progressTime(MQA_INTERVAL);
|
|
1016
|
+
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 0);
|
|
970
1017
|
});
|
|
971
1018
|
});
|
|
972
1019
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
await startStatsAnalyzer();
|
|
1020
|
+
describe('RTP recovered packets emission', async() => {
|
|
1021
|
+
beforeEach(async() => {
|
|
1022
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
it('should initially report zero RTP recovered packets', async() => {
|
|
1026
|
+
assert.strictEqual(mqeData.audioReceive[0].common.rtpRecovered, 0);
|
|
1027
|
+
})
|
|
1028
|
+
|
|
1029
|
+
it('should report RTP recovered packets equal to FEC packets received', async() => {
|
|
1030
|
+
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 10;
|
|
1031
|
+
|
|
1032
|
+
await progressTime(MQA_INTERVAL);
|
|
1033
|
+
assert.strictEqual(mqeData.audioReceive[0].common.rtpRecovered, 10);
|
|
1034
|
+
})
|
|
1035
|
+
|
|
1036
|
+
it('should reset RTP recovered packets count after each interval', async () => {
|
|
1037
|
+
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 100;
|
|
1038
|
+
await progressTime(MQA_INTERVAL);
|
|
1039
|
+
assert.strictEqual(mqeData.audioReceive[0].common.rtpRecovered, 100);
|
|
1040
|
+
|
|
1041
|
+
await progressTime(MQA_INTERVAL);
|
|
1042
|
+
assert.strictEqual(mqeData.audioReceive[0].common.rtpRecovered, 0);
|
|
1043
|
+
})
|
|
1044
|
+
|
|
1045
|
+
it('should correctly calculate RTP recovered packets after discarding FEC packets', async () => {
|
|
1046
|
+
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 100;
|
|
1047
|
+
fakeStats.audio.receivers[0].report[0].fecPacketsDiscarded += 20;
|
|
1048
|
+
|
|
1049
|
+
await progressTime(MQA_INTERVAL);
|
|
1050
|
+
assert.strictEqual(mqeData.audioReceive[0].common.rtpRecovered, 80);
|
|
1051
|
+
})
|
|
1052
|
+
})
|
|
1053
|
+
|
|
1054
|
+
describe('packet loss metrics reporting in stats analyzer', () => {
|
|
1055
|
+
beforeEach(async () => {
|
|
1056
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
it('should report zero packet loss for both audio and video at the start of the stats analyzer', async () => {
|
|
976
1060
|
assert.strictEqual(mqeData.audioReceive[0].common.mediaHopByHopLost, 0);
|
|
977
1061
|
assert.strictEqual(mqeData.audioReceive[0].common.rtpHopByHopLost, 0);
|
|
978
1062
|
assert.strictEqual(mqeData.videoReceive[0].common.mediaHopByHopLost, 0);
|
|
979
1063
|
assert.strictEqual(mqeData.videoReceive[0].common.rtpHopByHopLost, 0);
|
|
980
1064
|
});
|
|
981
1065
|
|
|
982
|
-
it('after
|
|
1066
|
+
it('should update packet loss metrics correctly for both audio and video after packet loss is detected', async () => {
|
|
983
1067
|
fakeStats.audio.receivers[0].report[0].packetsLost += 5;
|
|
984
1068
|
fakeStats.video.receivers[0].report[0].packetsLost += 5;
|
|
985
1069
|
await progressTime(MQA_INTERVAL);
|
|
@@ -991,36 +1075,56 @@ describe('plugin-meetings', () => {
|
|
|
991
1075
|
});
|
|
992
1076
|
});
|
|
993
1077
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
await startStatsAnalyzer();
|
|
997
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 0);
|
|
998
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 0);
|
|
1078
|
+
describe('maximum remote loss rate reporting in stats analyzer', () => {
|
|
1079
|
+
beforeEach(async () => {
|
|
1080
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
999
1081
|
});
|
|
1000
1082
|
|
|
1001
|
-
it('
|
|
1083
|
+
it('should report a zero maximum remote loss rate for both audio and video at the start', async () => {
|
|
1084
|
+
assert.strictEqual(mqeData.audioTransmit[0].common.maxRemoteLossRate, 0);
|
|
1085
|
+
assert.strictEqual(mqeData.videoTransmit[0].common.maxRemoteLossRate, 0);
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
it('should maintain a zero maximum remote loss rate for both audio and video after packets are sent without loss', async () => {
|
|
1002
1089
|
fakeStats.audio.senders[0].report[0].packetsSent += 100;
|
|
1003
1090
|
fakeStats.video.senders[0].report[0].packetsSent += 100;
|
|
1004
1091
|
await progressTime(MQA_INTERVAL);
|
|
1005
1092
|
|
|
1006
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.
|
|
1007
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.
|
|
1093
|
+
assert.strictEqual(mqeData.audioTransmit[0].common.maxRemoteLossRate, 0);
|
|
1094
|
+
assert.strictEqual(mqeData.videoTransmit[0].common.maxRemoteLossRate, 0);
|
|
1008
1095
|
});
|
|
1009
1096
|
|
|
1010
|
-
it('
|
|
1097
|
+
it('should accurately calculate the maximum remote loss rate for both audio and video after packet loss is detected', async () => {
|
|
1011
1098
|
fakeStats.audio.senders[0].report[0].packetsSent += 200;
|
|
1012
1099
|
fakeStats.audio.senders[0].report[1].packetsLost += 10;
|
|
1013
1100
|
fakeStats.video.senders[0].report[0].packetsSent += 200;
|
|
1014
1101
|
fakeStats.video.senders[0].report[1].packetsLost += 10;
|
|
1015
1102
|
await progressTime(MQA_INTERVAL);
|
|
1016
1103
|
|
|
1017
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.
|
|
1018
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.
|
|
1104
|
+
assert.strictEqual(mqeData.audioTransmit[0].common.maxRemoteLossRate, 5);
|
|
1105
|
+
assert.strictEqual(mqeData.videoTransmit[0].common.maxRemoteLossRate, 5);
|
|
1019
1106
|
});
|
|
1107
|
+
|
|
1108
|
+
it('should reset the maximum remote loss rate across MQA intervals', async() => {
|
|
1109
|
+
fakeStats.audio.senders[0].report[0].packetsSent += 100;
|
|
1110
|
+
fakeStats.audio.senders[0].report[1].packetsLost += 10;
|
|
1111
|
+
fakeStats.video.senders[0].report[0].packetsSent += 50;
|
|
1112
|
+
fakeStats.video.senders[0].report[1].packetsLost += 5;
|
|
1113
|
+
await progressTime(MQA_INTERVAL);
|
|
1114
|
+
|
|
1115
|
+
assert.strictEqual(mqeData.audioTransmit[0].common.maxRemoteLossRate, 10);
|
|
1116
|
+
assert.strictEqual(mqeData.videoTransmit[0].common.maxRemoteLossRate, 10);
|
|
1117
|
+
|
|
1118
|
+
await progressTime(MQA_INTERVAL);
|
|
1119
|
+
|
|
1120
|
+
assert.strictEqual(mqeData.audioTransmit[0].common.maxRemoteLossRate, 0);
|
|
1121
|
+
assert.strictEqual(mqeData.videoTransmit[0].common.maxRemoteLossRate, 0);
|
|
1122
|
+
|
|
1123
|
+
})
|
|
1020
1124
|
});
|
|
1021
1125
|
|
|
1022
1126
|
it('has the correct localIpAddress set when the candidateType is host', async () => {
|
|
1023
|
-
await startStatsAnalyzer();
|
|
1127
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1024
1128
|
|
|
1025
1129
|
await progressTime();
|
|
1026
1130
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -1030,7 +1134,7 @@ describe('plugin-meetings', () => {
|
|
|
1030
1134
|
});
|
|
1031
1135
|
|
|
1032
1136
|
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is set', async () => {
|
|
1033
|
-
await startStatsAnalyzer();
|
|
1137
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1034
1138
|
|
|
1035
1139
|
await progressTime();
|
|
1036
1140
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -1044,7 +1148,7 @@ describe('plugin-meetings', () => {
|
|
|
1044
1148
|
});
|
|
1045
1149
|
|
|
1046
1150
|
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is not set', async () => {
|
|
1047
|
-
await startStatsAnalyzer();
|
|
1151
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1048
1152
|
|
|
1049
1153
|
await progressTime();
|
|
1050
1154
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -1058,7 +1162,7 @@ describe('plugin-meetings', () => {
|
|
|
1058
1162
|
});
|
|
1059
1163
|
|
|
1060
1164
|
it('has no localIpAddress set when the candidateType is invalid', async () => {
|
|
1061
|
-
await startStatsAnalyzer();
|
|
1165
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1062
1166
|
|
|
1063
1167
|
await progressTime();
|
|
1064
1168
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -1067,10 +1171,19 @@ describe('plugin-meetings', () => {
|
|
|
1067
1171
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
1068
1172
|
});
|
|
1069
1173
|
|
|
1174
|
+
it('has the correct share video encoder implementation as provided by the stats', async () => {
|
|
1175
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1176
|
+
|
|
1177
|
+
await progressTime();
|
|
1178
|
+
assert.strictEqual(statsAnalyzer.shareVideoEncoderImplementation, 'fake-encoder');
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1070
1181
|
it('logs a message when audio send packets do not increase', async () => {
|
|
1071
1182
|
await startStatsAnalyzer(
|
|
1072
|
-
{
|
|
1073
|
-
|
|
1183
|
+
{
|
|
1184
|
+
statsAnalyzer, pc, mediaStatus: {expected: {sendAudio: true}},
|
|
1185
|
+
lastEmittedEvents: {audio: {local: EVENTS.LOCAL_MEDIA_STARTED}},
|
|
1186
|
+
},
|
|
1074
1187
|
);
|
|
1075
1188
|
|
|
1076
1189
|
// don't increase the packets when time progresses.
|
|
@@ -1078,15 +1191,17 @@ describe('plugin-meetings', () => {
|
|
|
1078
1191
|
|
|
1079
1192
|
assert(
|
|
1080
1193
|
loggerSpy.calledWith(
|
|
1081
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
|
|
1082
|
-
)
|
|
1194
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent',
|
|
1195
|
+
),
|
|
1083
1196
|
);
|
|
1084
1197
|
});
|
|
1085
1198
|
|
|
1086
1199
|
it('does not log a message when audio send packets increase', async () => {
|
|
1087
|
-
await startStatsAnalyzer(
|
|
1088
|
-
|
|
1089
|
-
|
|
1200
|
+
await startStatsAnalyzer({
|
|
1201
|
+
statsAnalyzer, pc,
|
|
1202
|
+
mediaStatus: {expected: {sendAudio: true}},
|
|
1203
|
+
lastEmittedEvents: {audio: {local: EVENTS.LOCAL_MEDIA_STOPPED}},
|
|
1204
|
+
},
|
|
1090
1205
|
);
|
|
1091
1206
|
|
|
1092
1207
|
fakeStats.audio.senders[0].report[0].packetsSent += 5;
|
|
@@ -1094,15 +1209,16 @@ describe('plugin-meetings', () => {
|
|
|
1094
1209
|
|
|
1095
1210
|
assert(
|
|
1096
1211
|
loggerSpy.neverCalledWith(
|
|
1097
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
|
|
1098
|
-
)
|
|
1212
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent',
|
|
1213
|
+
),
|
|
1099
1214
|
);
|
|
1100
1215
|
});
|
|
1101
1216
|
|
|
1102
1217
|
it('logs a message when video send packets do not increase', async () => {
|
|
1103
|
-
await startStatsAnalyzer(
|
|
1104
|
-
|
|
1105
|
-
|
|
1218
|
+
await startStatsAnalyzer({
|
|
1219
|
+
statsAnalyzer, pc, mediaStatus: {expected: {sendVideo: true}},
|
|
1220
|
+
lastEmittedEvents: {video: {local: EVENTS.LOCAL_MEDIA_STARTED}},
|
|
1221
|
+
},
|
|
1106
1222
|
);
|
|
1107
1223
|
|
|
1108
1224
|
// don't increase the packets when time progresses.
|
|
@@ -1110,31 +1226,42 @@ describe('plugin-meetings', () => {
|
|
|
1110
1226
|
|
|
1111
1227
|
assert(
|
|
1112
1228
|
loggerSpy.calledWith(
|
|
1113
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
|
|
1114
|
-
)
|
|
1229
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent',
|
|
1230
|
+
),
|
|
1115
1231
|
);
|
|
1116
1232
|
});
|
|
1117
1233
|
|
|
1118
1234
|
it('does not log a message when video send packets increase', async () => {
|
|
1119
1235
|
await startStatsAnalyzer(
|
|
1120
|
-
{
|
|
1121
|
-
|
|
1122
|
-
|
|
1236
|
+
{
|
|
1237
|
+
statsAnalyzer, pc,
|
|
1238
|
+
mediaStatus: {
|
|
1239
|
+
expected: {
|
|
1240
|
+
sendVideo: true,
|
|
1241
|
+
},
|
|
1242
|
+
},
|
|
1243
|
+
lastEmittedEvents: {
|
|
1244
|
+
video: {
|
|
1245
|
+
local: EVENTS.LOCAL_MEDIA_STOPPED,
|
|
1246
|
+
},
|
|
1247
|
+
},
|
|
1248
|
+
});
|
|
1123
1249
|
|
|
1124
1250
|
fakeStats.video.senders[0].report[0].packetsSent += 5;
|
|
1125
1251
|
await progressTime();
|
|
1126
1252
|
|
|
1127
1253
|
assert(
|
|
1128
1254
|
loggerSpy.neverCalledWith(
|
|
1129
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
|
|
1130
|
-
)
|
|
1255
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent',
|
|
1256
|
+
),
|
|
1131
1257
|
);
|
|
1132
1258
|
});
|
|
1133
1259
|
|
|
1134
1260
|
it('logs a message when share send packets do not increase', async () => {
|
|
1135
|
-
await startStatsAnalyzer(
|
|
1136
|
-
|
|
1137
|
-
|
|
1261
|
+
await startStatsAnalyzer({
|
|
1262
|
+
pc, mediaStatus: {expected: {sendShare: true}},
|
|
1263
|
+
lastEmittedEvents: {share: {local: EVENTS.LOCAL_MEDIA_STARTED}}, statsAnalyzer,
|
|
1264
|
+
},
|
|
1138
1265
|
);
|
|
1139
1266
|
|
|
1140
1267
|
// don't increase the packets when time progresses.
|
|
@@ -1142,15 +1269,16 @@ describe('plugin-meetings', () => {
|
|
|
1142
1269
|
|
|
1143
1270
|
assert(
|
|
1144
1271
|
loggerSpy.calledWith(
|
|
1145
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
|
|
1146
|
-
)
|
|
1272
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent',
|
|
1273
|
+
),
|
|
1147
1274
|
);
|
|
1148
1275
|
});
|
|
1149
1276
|
|
|
1150
1277
|
it('does not log a message when share send packets increase', async () => {
|
|
1151
|
-
await startStatsAnalyzer(
|
|
1152
|
-
|
|
1153
|
-
|
|
1278
|
+
await startStatsAnalyzer({
|
|
1279
|
+
pc, statsAnalyzer, mediaStatus: {expected: {sendShare: true}},
|
|
1280
|
+
lastEmittedEvents: {share: {local: EVENTS.LOCAL_MEDIA_STOPPED}},
|
|
1281
|
+
},
|
|
1154
1282
|
);
|
|
1155
1283
|
|
|
1156
1284
|
fakeStats.share.senders[0].report[0].packetsSent += 5;
|
|
@@ -1158,8 +1286,8 @@ describe('plugin-meetings', () => {
|
|
|
1158
1286
|
|
|
1159
1287
|
assert(
|
|
1160
1288
|
loggerSpy.neverCalledWith(
|
|
1161
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
|
|
1162
|
-
)
|
|
1289
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent',
|
|
1290
|
+
),
|
|
1163
1291
|
);
|
|
1164
1292
|
});
|
|
1165
1293
|
|
|
@@ -1172,7 +1300,7 @@ describe('plugin-meetings', () => {
|
|
|
1172
1300
|
id: '4',
|
|
1173
1301
|
};
|
|
1174
1302
|
|
|
1175
|
-
await startStatsAnalyzer();
|
|
1303
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1176
1304
|
|
|
1177
1305
|
// don't increase the packets when time progresses.
|
|
1178
1306
|
await progressTime();
|
|
@@ -1180,10 +1308,10 @@ describe('plugin-meetings', () => {
|
|
|
1180
1308
|
assert.neverCalledWith(
|
|
1181
1309
|
loggerSpy,
|
|
1182
1310
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1183
|
-
0
|
|
1311
|
+
0,
|
|
1184
1312
|
);
|
|
1185
1313
|
});
|
|
1186
|
-
}
|
|
1314
|
+
},
|
|
1187
1315
|
);
|
|
1188
1316
|
|
|
1189
1317
|
it(`logs a message if no packets are sent`, async () => {
|
|
@@ -1192,7 +1320,7 @@ describe('plugin-meetings', () => {
|
|
|
1192
1320
|
csi: 2,
|
|
1193
1321
|
id: '4',
|
|
1194
1322
|
};
|
|
1195
|
-
await startStatsAnalyzer();
|
|
1323
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1196
1324
|
|
|
1197
1325
|
// don't increase the packets when time progresses.
|
|
1198
1326
|
await progressTime();
|
|
@@ -1200,52 +1328,52 @@ describe('plugin-meetings', () => {
|
|
|
1200
1328
|
assert.calledWith(
|
|
1201
1329
|
loggerSpy,
|
|
1202
1330
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1203
|
-
0
|
|
1331
|
+
0,
|
|
1204
1332
|
);
|
|
1205
1333
|
|
|
1206
1334
|
assert.calledWith(
|
|
1207
1335
|
loggerSpy,
|
|
1208
1336
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
|
|
1209
|
-
0
|
|
1337
|
+
0,
|
|
1210
1338
|
);
|
|
1211
1339
|
|
|
1212
1340
|
assert.calledWith(
|
|
1213
1341
|
loggerSpy,
|
|
1214
1342
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
|
|
1215
|
-
0
|
|
1343
|
+
0,
|
|
1216
1344
|
);
|
|
1217
1345
|
|
|
1218
1346
|
assert.calledWith(
|
|
1219
1347
|
loggerSpy,
|
|
1220
1348
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1221
|
-
0
|
|
1349
|
+
0,
|
|
1222
1350
|
);
|
|
1223
1351
|
|
|
1224
1352
|
assert.calledWith(
|
|
1225
1353
|
loggerSpy,
|
|
1226
1354
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1227
|
-
0
|
|
1355
|
+
0,
|
|
1228
1356
|
);
|
|
1229
1357
|
|
|
1230
1358
|
assert.calledWith(
|
|
1231
1359
|
loggerSpy,
|
|
1232
1360
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
|
|
1233
|
-
0
|
|
1361
|
+
0,
|
|
1234
1362
|
);
|
|
1235
1363
|
assert.calledWith(
|
|
1236
1364
|
loggerSpy,
|
|
1237
1365
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
|
|
1238
|
-
0
|
|
1366
|
+
0,
|
|
1239
1367
|
);
|
|
1240
1368
|
assert.calledWith(
|
|
1241
1369
|
loggerSpy,
|
|
1242
1370
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1243
|
-
0
|
|
1371
|
+
0,
|
|
1244
1372
|
);
|
|
1245
1373
|
});
|
|
1246
1374
|
|
|
1247
1375
|
it(`does not log a message if receiveSlot is undefined`, async () => {
|
|
1248
|
-
await startStatsAnalyzer();
|
|
1376
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1249
1377
|
|
|
1250
1378
|
// don't increase the packets when time progresses.
|
|
1251
1379
|
await progressTime();
|
|
@@ -1253,12 +1381,12 @@ describe('plugin-meetings', () => {
|
|
|
1253
1381
|
assert.neverCalledWith(
|
|
1254
1382
|
loggerSpy,
|
|
1255
1383
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot "". Total packets received on slot: ',
|
|
1256
|
-
0
|
|
1384
|
+
0,
|
|
1257
1385
|
);
|
|
1258
1386
|
});
|
|
1259
1387
|
|
|
1260
1388
|
it('has the correct number of senders and receivers (2)', async () => {
|
|
1261
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1389
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
1262
1390
|
|
|
1263
1391
|
await progressTime();
|
|
1264
1392
|
|
|
@@ -1269,7 +1397,7 @@ describe('plugin-meetings', () => {
|
|
|
1269
1397
|
});
|
|
1270
1398
|
|
|
1271
1399
|
it('has one stream per sender/reciever', async () => {
|
|
1272
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1400
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
1273
1401
|
|
|
1274
1402
|
await progressTime();
|
|
1275
1403
|
|
|
@@ -1456,7 +1584,7 @@ describe('plugin-meetings', () => {
|
|
|
1456
1584
|
framesDropped: 0,
|
|
1457
1585
|
},
|
|
1458
1586
|
h264CodecProfile: 'BP',
|
|
1459
|
-
isActiveSpeaker:
|
|
1587
|
+
isActiveSpeaker: false,
|
|
1460
1588
|
optimalFrameSize: 0,
|
|
1461
1589
|
receivedFrameSize: 3600,
|
|
1462
1590
|
receivedHeight: 720,
|
|
@@ -1490,7 +1618,7 @@ describe('plugin-meetings', () => {
|
|
|
1490
1618
|
framesDropped: 0,
|
|
1491
1619
|
},
|
|
1492
1620
|
h264CodecProfile: 'BP',
|
|
1493
|
-
isActiveSpeaker:
|
|
1621
|
+
isActiveSpeaker: false,
|
|
1494
1622
|
optimalFrameSize: 0,
|
|
1495
1623
|
receivedFrameSize: 3600,
|
|
1496
1624
|
receivedHeight: 720,
|
|
@@ -1529,7 +1657,7 @@ describe('plugin-meetings', () => {
|
|
|
1529
1657
|
},
|
|
1530
1658
|
});
|
|
1531
1659
|
|
|
1532
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1660
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
1533
1661
|
|
|
1534
1662
|
await progressTime();
|
|
1535
1663
|
|
|
@@ -1554,7 +1682,7 @@ describe('plugin-meetings', () => {
|
|
|
1554
1682
|
framesDropped: 0,
|
|
1555
1683
|
},
|
|
1556
1684
|
h264CodecProfile: 'BP',
|
|
1557
|
-
isActiveSpeaker:
|
|
1685
|
+
isActiveSpeaker: false,
|
|
1558
1686
|
optimalFrameSize: 0,
|
|
1559
1687
|
receivedFrameSize: 3600,
|
|
1560
1688
|
receivedHeight: 720,
|
|
@@ -1586,7 +1714,7 @@ describe('plugin-meetings', () => {
|
|
|
1586
1714
|
framesDropped: 0,
|
|
1587
1715
|
},
|
|
1588
1716
|
h264CodecProfile: 'BP',
|
|
1589
|
-
isActiveSpeaker:
|
|
1717
|
+
isActiveSpeaker: false,
|
|
1590
1718
|
optimalFrameSize: 0,
|
|
1591
1719
|
receivedFrameSize: 3600,
|
|
1592
1720
|
receivedHeight: 720,
|
|
@@ -1618,7 +1746,7 @@ describe('plugin-meetings', () => {
|
|
|
1618
1746
|
framesDropped: 0,
|
|
1619
1747
|
},
|
|
1620
1748
|
h264CodecProfile: 'BP',
|
|
1621
|
-
isActiveSpeaker:
|
|
1749
|
+
isActiveSpeaker: false,
|
|
1622
1750
|
optimalFrameSize: 0,
|
|
1623
1751
|
receivedFrameSize: 3600,
|
|
1624
1752
|
receivedHeight: 720,
|
|
@@ -1633,187 +1761,387 @@ describe('plugin-meetings', () => {
|
|
|
1633
1761
|
]);
|
|
1634
1762
|
});
|
|
1635
1763
|
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1764
|
+
describe('stream count for simulcast', async () => {
|
|
1765
|
+
it('has three streams for video senders for simulcast', async () => {
|
|
1766
|
+
pc.getTransceiverStats = sinon.stub().resolves({
|
|
1767
|
+
audio: {
|
|
1768
|
+
senders: [fakeStats.audio.senders[0]],
|
|
1769
|
+
receivers: [fakeStats.audio.receivers[0]],
|
|
1770
|
+
},
|
|
1771
|
+
video: {
|
|
1772
|
+
senders: [
|
|
1773
|
+
{
|
|
1774
|
+
localTrackLabel: 'fake-camera',
|
|
1775
|
+
report: [
|
|
1776
|
+
{
|
|
1777
|
+
type: 'outbound-rtp',
|
|
1778
|
+
bytesSent: 1,
|
|
1779
|
+
framesSent: 0,
|
|
1780
|
+
packetsSent: 0,
|
|
1781
|
+
isRequested: true,
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
type: 'outbound-rtp',
|
|
1785
|
+
bytesSent: 1,
|
|
1786
|
+
framesSent: 0,
|
|
1787
|
+
packetsSent: 1,
|
|
1788
|
+
isRequested: true,
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
type: 'outbound-rtp',
|
|
1792
|
+
bytesSent: 1000,
|
|
1793
|
+
framesSent: 1,
|
|
1794
|
+
packetsSent: 0,
|
|
1795
|
+
isRequested: true,
|
|
1796
|
+
},
|
|
1797
|
+
{
|
|
1798
|
+
type: 'remote-inbound-rtp',
|
|
1799
|
+
packetsLost: 0,
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
type: 'candidate-pair',
|
|
1803
|
+
state: 'succeeded',
|
|
1804
|
+
localCandidateId: 'fake-candidate-id',
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
type: 'candidate-pair',
|
|
1808
|
+
state: 'failed',
|
|
1809
|
+
localCandidateId: 'bad-candidate-id',
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
type: 'local-candidate',
|
|
1813
|
+
id: 'fake-candidate-id',
|
|
1814
|
+
protocol: 'tcp',
|
|
1815
|
+
},
|
|
1816
|
+
],
|
|
1817
|
+
},
|
|
1818
|
+
],
|
|
1819
|
+
receivers: [fakeStats.video.receivers[0]],
|
|
1820
|
+
},
|
|
1821
|
+
screenShareAudio: {
|
|
1822
|
+
senders: [fakeStats.audio.senders[0]],
|
|
1823
|
+
receivers: [fakeStats.audio.receivers[0]],
|
|
1824
|
+
},
|
|
1825
|
+
screenShareVideo: {
|
|
1826
|
+
senders: [fakeStats.video.senders[0]],
|
|
1827
|
+
receivers: [fakeStats.video.receivers[0]],
|
|
1828
|
+
},
|
|
1829
|
+
});
|
|
1700
1830
|
|
|
1701
|
-
|
|
1831
|
+
await startStatsAnalyzer({
|
|
1832
|
+
pc,
|
|
1833
|
+
statsAnalyzer,
|
|
1834
|
+
mediaStatus: {
|
|
1835
|
+
expected: {
|
|
1836
|
+
receiveVideo: true,
|
|
1837
|
+
},
|
|
1838
|
+
},
|
|
1839
|
+
});
|
|
1702
1840
|
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1841
|
+
await progressTime();
|
|
1842
|
+
|
|
1843
|
+
assert.deepEqual(mqeData.videoTransmit[0].streams, [
|
|
1844
|
+
{
|
|
1845
|
+
common: {
|
|
1846
|
+
codec: 'H264',
|
|
1847
|
+
csi: [],
|
|
1848
|
+
duplicateSsci: 0,
|
|
1849
|
+
requestedBitrate: 0,
|
|
1850
|
+
requestedFrames: 0,
|
|
1851
|
+
rtpPackets: 0,
|
|
1852
|
+
ssci: 0,
|
|
1853
|
+
transmittedBitrate: 0.13333333333333333,
|
|
1854
|
+
transmittedFrameRate: 0,
|
|
1855
|
+
},
|
|
1856
|
+
h264CodecProfile: 'BP',
|
|
1857
|
+
isAvatar: false,
|
|
1858
|
+
isHardwareEncoded: false,
|
|
1859
|
+
localConfigurationChanges: 2,
|
|
1860
|
+
maxFrameQp: 0,
|
|
1861
|
+
maxNoiseLevel: 0,
|
|
1862
|
+
minRegionQp: 0,
|
|
1863
|
+
remoteConfigurationChanges: 0,
|
|
1864
|
+
requestedFrameSize: 0,
|
|
1865
|
+
requestedKeyFrames: 0,
|
|
1866
|
+
transmittedFrameSize: 0,
|
|
1867
|
+
transmittedHeight: 0,
|
|
1868
|
+
transmittedKeyFrames: 0,
|
|
1869
|
+
transmittedKeyFramesClient: 0,
|
|
1870
|
+
transmittedKeyFramesConfigurationChange: 0,
|
|
1871
|
+
transmittedKeyFramesFeedback: 0,
|
|
1872
|
+
transmittedKeyFramesLocalDrop: 0,
|
|
1873
|
+
transmittedKeyFramesOtherLayer: 0,
|
|
1874
|
+
transmittedKeyFramesPeriodic: 0,
|
|
1875
|
+
transmittedKeyFramesSceneChange: 0,
|
|
1876
|
+
transmittedKeyFramesStartup: 0,
|
|
1877
|
+
transmittedKeyFramesUnknown: 0,
|
|
1878
|
+
transmittedWidth: 0,
|
|
1709
1879
|
requestedBitrate: 0,
|
|
1710
|
-
requestedFrames: 0,
|
|
1711
|
-
rtpPackets: 0,
|
|
1712
|
-
ssci: 0,
|
|
1713
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1714
|
-
transmittedFrameRate: 0
|
|
1715
1880
|
},
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1881
|
+
{
|
|
1882
|
+
common: {
|
|
1883
|
+
codec: 'H264',
|
|
1884
|
+
csi: [],
|
|
1885
|
+
duplicateSsci: 0,
|
|
1886
|
+
requestedBitrate: 0,
|
|
1887
|
+
requestedFrames: 0,
|
|
1888
|
+
rtpPackets: 1,
|
|
1889
|
+
ssci: 0,
|
|
1890
|
+
transmittedBitrate: 0.13333333333333333,
|
|
1891
|
+
transmittedFrameRate: 0,
|
|
1892
|
+
},
|
|
1893
|
+
h264CodecProfile: 'BP',
|
|
1894
|
+
isAvatar: false,
|
|
1895
|
+
isHardwareEncoded: false,
|
|
1896
|
+
localConfigurationChanges: 2,
|
|
1897
|
+
maxFrameQp: 0,
|
|
1898
|
+
maxNoiseLevel: 0,
|
|
1899
|
+
minRegionQp: 0,
|
|
1900
|
+
remoteConfigurationChanges: 0,
|
|
1901
|
+
requestedFrameSize: 0,
|
|
1902
|
+
requestedKeyFrames: 0,
|
|
1903
|
+
transmittedFrameSize: 0,
|
|
1904
|
+
transmittedHeight: 0,
|
|
1905
|
+
transmittedKeyFrames: 0,
|
|
1906
|
+
transmittedKeyFramesClient: 0,
|
|
1907
|
+
transmittedKeyFramesConfigurationChange: 0,
|
|
1908
|
+
transmittedKeyFramesFeedback: 0,
|
|
1909
|
+
transmittedKeyFramesLocalDrop: 0,
|
|
1910
|
+
transmittedKeyFramesOtherLayer: 0,
|
|
1911
|
+
transmittedKeyFramesPeriodic: 0,
|
|
1912
|
+
transmittedKeyFramesSceneChange: 0,
|
|
1913
|
+
transmittedKeyFramesStartup: 0,
|
|
1914
|
+
transmittedKeyFramesUnknown: 0,
|
|
1915
|
+
transmittedWidth: 0,
|
|
1746
1916
|
requestedBitrate: 0,
|
|
1747
|
-
requestedFrames: 0,
|
|
1748
|
-
rtpPackets: 0,
|
|
1749
|
-
ssci: 0,
|
|
1750
|
-
transmittedBitrate: 0,
|
|
1751
|
-
transmittedFrameRate: 0,
|
|
1752
1917
|
},
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1918
|
+
{
|
|
1919
|
+
common: {
|
|
1920
|
+
codec: 'H264',
|
|
1921
|
+
csi: [],
|
|
1922
|
+
duplicateSsci: 0,
|
|
1923
|
+
requestedBitrate: 0,
|
|
1924
|
+
requestedFrames: 0,
|
|
1925
|
+
rtpPackets: 0,
|
|
1926
|
+
ssci: 0,
|
|
1927
|
+
transmittedBitrate: 133.33333333333334,
|
|
1928
|
+
transmittedFrameRate: 0,
|
|
1929
|
+
},
|
|
1930
|
+
h264CodecProfile: 'BP',
|
|
1931
|
+
isAvatar: false,
|
|
1932
|
+
isHardwareEncoded: false,
|
|
1933
|
+
localConfigurationChanges: 2,
|
|
1934
|
+
maxFrameQp: 0,
|
|
1935
|
+
maxNoiseLevel: 0,
|
|
1936
|
+
minRegionQp: 0,
|
|
1937
|
+
remoteConfigurationChanges: 0,
|
|
1938
|
+
requestedFrameSize: 0,
|
|
1939
|
+
requestedKeyFrames: 0,
|
|
1940
|
+
transmittedFrameSize: 0,
|
|
1941
|
+
transmittedHeight: 0,
|
|
1942
|
+
transmittedKeyFrames: 0,
|
|
1943
|
+
transmittedKeyFramesClient: 0,
|
|
1944
|
+
transmittedKeyFramesConfigurationChange: 0,
|
|
1945
|
+
transmittedKeyFramesFeedback: 0,
|
|
1946
|
+
transmittedKeyFramesLocalDrop: 0,
|
|
1947
|
+
transmittedKeyFramesOtherLayer: 0,
|
|
1948
|
+
transmittedKeyFramesPeriodic: 0,
|
|
1949
|
+
transmittedKeyFramesSceneChange: 0,
|
|
1950
|
+
transmittedKeyFramesStartup: 0,
|
|
1951
|
+
transmittedKeyFramesUnknown: 0,
|
|
1952
|
+
transmittedWidth: 0,
|
|
1783
1953
|
requestedBitrate: 0,
|
|
1784
|
-
requestedFrames: 0,
|
|
1785
|
-
rtpPackets: 1,
|
|
1786
|
-
ssci: 0,
|
|
1787
|
-
transmittedBitrate: 133.33333333333334,
|
|
1788
|
-
transmittedFrameRate: 0,
|
|
1789
1954
|
},
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1955
|
+
]);
|
|
1956
|
+
});
|
|
1957
|
+
});
|
|
1958
|
+
describe('active speaker status emission', async () => {
|
|
1959
|
+
beforeEach(async () => {
|
|
1960
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1961
|
+
performance.timeOrigin = 1;
|
|
1962
|
+
});
|
|
1963
|
+
|
|
1964
|
+
it('reports active speaker as true when the participant has been speaking', async () => {
|
|
1965
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = true;
|
|
1966
|
+
await progressTime(5 * MQA_INTERVAL);
|
|
1967
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, true);
|
|
1968
|
+
});
|
|
1969
|
+
|
|
1970
|
+
it('reports active speaker as false when the participant has not spoken', async () => {
|
|
1971
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = false;
|
|
1972
|
+
await progressTime(5 * MQA_INTERVAL);
|
|
1973
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1974
|
+
});
|
|
1975
|
+
|
|
1976
|
+
it('defaults to false when active speaker status is indeterminate', async () => {
|
|
1977
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = undefined;
|
|
1978
|
+
await progressTime(MQA_INTERVAL);
|
|
1979
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1980
|
+
});
|
|
1981
|
+
|
|
1982
|
+
it('updates active speaker to true following a recent status change to speaking', async () => {
|
|
1983
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = false;
|
|
1984
|
+
fakeStats.video.receivers[0].report[0].lastActiveSpeakerUpdateTimestamp = performance.timeOrigin + performance.now() + (30 * 1000);
|
|
1985
|
+
await progressTime(MQA_INTERVAL);
|
|
1986
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, true);
|
|
1987
|
+
await progressTime(MQA_INTERVAL);
|
|
1988
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1989
|
+
});
|
|
1990
|
+
});
|
|
1991
|
+
describe('sends streams according to their is requested flag', async () => {
|
|
1992
|
+
|
|
1993
|
+
beforeEach(async () => {
|
|
1994
|
+
performance.timeOrigin = 0;
|
|
1995
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1996
|
+
});
|
|
1997
|
+
|
|
1998
|
+
it('should send a stream if it is requested', async () => {
|
|
1999
|
+
fakeStats.audio.senders[0].report[0].isRequested = true;
|
|
2000
|
+
await progressTime(MQA_INTERVAL);
|
|
2001
|
+
assert.strictEqual(mqeData.audioTransmit[0].streams.length, 1);
|
|
2002
|
+
});
|
|
2003
|
+
|
|
2004
|
+
it('should not sent a stream if its is requested flag is undefined', async () => {
|
|
2005
|
+
fakeStats.audio.senders[0].report[0].isRequested = undefined;
|
|
2006
|
+
await progressTime(MQA_INTERVAL);
|
|
2007
|
+
assert.strictEqual(mqeData.audioTransmit[0].streams.length, 0);
|
|
2008
|
+
});
|
|
2009
|
+
|
|
2010
|
+
it('should not send a stream if it is not requested', async () => {
|
|
2011
|
+
fakeStats.audio.receivers[0].report[0].isRequested = false;
|
|
2012
|
+
await progressTime(MQA_INTERVAL);
|
|
2013
|
+
assert.strictEqual(mqeData.audioReceive[0].streams.length, 0);
|
|
2014
|
+
});
|
|
2015
|
+
|
|
2016
|
+
it('should send the stream if it was recently requested', async () => {
|
|
2017
|
+
fakeStats.audio.receivers[0].report[0].lastRequestedUpdateTimestamp = performance.timeOrigin + performance.now() + (30 * 1000);
|
|
2018
|
+
fakeStats.audio.receivers[0].report[0].isRequested = false;
|
|
2019
|
+
await progressTime(MQA_INTERVAL);
|
|
2020
|
+
assert.strictEqual(mqeData.audioReceive[0].streams.length, 1);
|
|
2021
|
+
await progressTime(MQA_INTERVAL);
|
|
2022
|
+
assert.strictEqual(mqeData.audioReceive[0].streams.length, 0);
|
|
2023
|
+
});
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
describe('window and screen size emission', async () => {
|
|
2027
|
+
beforeEach(async () => {
|
|
2028
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
2029
|
+
});
|
|
2030
|
+
|
|
2031
|
+
it('should record the screen size from window.screen properties', async () => {
|
|
2032
|
+
sinon.stub(window.screen, 'width').get(() => 1280);
|
|
2033
|
+
sinon.stub(window.screen, 'height').get(() => 720);
|
|
2034
|
+
await progressTime(MQA_INTERVAL);
|
|
2035
|
+
assert.strictEqual(mqeData.intervalMetadata.screenWidth, 1280);
|
|
2036
|
+
assert.strictEqual(mqeData.intervalMetadata.screenHeight, 720);
|
|
2037
|
+
assert.strictEqual(mqeData.intervalMetadata.screenResolution, 3600);
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
it('should record the initial app window size from window properties', async () => {
|
|
2041
|
+
sinon.stub(window, 'innerWidth').get(() => 720);
|
|
2042
|
+
sinon.stub(window, 'innerHeight').get(() => 360);
|
|
2043
|
+
await progressTime(MQA_INTERVAL);
|
|
2044
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowWidth, 720);
|
|
2045
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowHeight, 360);
|
|
2046
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowSize, 1013);
|
|
2047
|
+
|
|
2048
|
+
sinon.stub(window, 'innerWidth').get(() => 1080);
|
|
2049
|
+
sinon.stub(window, 'innerHeight').get(() => 720);
|
|
2050
|
+
await progressTime(MQA_INTERVAL);
|
|
2051
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowWidth, 1080);
|
|
2052
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowHeight, 720);
|
|
2053
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowSize, 3038);
|
|
2054
|
+
});
|
|
2055
|
+
});
|
|
2056
|
+
|
|
2057
|
+
describe('sends multistreamEnabled', async () => {
|
|
2058
|
+
it('false if StatsAnalyzer initialized with default value for isMultistream', async () => {
|
|
2059
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
2060
|
+
|
|
2061
|
+
await progressTime();
|
|
2062
|
+
|
|
2063
|
+
for (const data of [
|
|
2064
|
+
mqeData.audioTransmit,
|
|
2065
|
+
mqeData.audioReceive,
|
|
2066
|
+
mqeData.videoTransmit,
|
|
2067
|
+
mqeData.videoReceive,
|
|
2068
|
+
]) {
|
|
2069
|
+
assert.strictEqual(data[0].common.common.multistreamEnabled, false);
|
|
1814
2070
|
}
|
|
1815
|
-
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
it('false if StatsAnalyzer initialized with false', async () => {
|
|
2074
|
+
statsAnalyzer = new StatsAnalyzer({
|
|
2075
|
+
config: initialConfig,
|
|
2076
|
+
receiveSlotCallback: () => receiveSlot,
|
|
2077
|
+
networkQualityMonitor,
|
|
2078
|
+
isMultistream: false,
|
|
2079
|
+
});
|
|
2080
|
+
registerStatsAnalyzerEvents(statsAnalyzer);
|
|
2081
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: false}}});
|
|
2082
|
+
|
|
2083
|
+
await progressTime();
|
|
2084
|
+
|
|
2085
|
+
for (const data of [
|
|
2086
|
+
mqeData.audioTransmit,
|
|
2087
|
+
mqeData.audioReceive,
|
|
2088
|
+
mqeData.videoTransmit,
|
|
2089
|
+
mqeData.videoReceive,
|
|
2090
|
+
]) {
|
|
2091
|
+
assert.strictEqual(data[0].common.common.multistreamEnabled, false);
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
|
|
2095
|
+
it('true if StatsAnalyzer initialized with multistream', async () => {
|
|
2096
|
+
statsAnalyzer = new StatsAnalyzer({
|
|
2097
|
+
config: initialConfig,
|
|
2098
|
+
receiveSlotCallback: () => receiveSlot,
|
|
2099
|
+
networkQualityMonitor,
|
|
2100
|
+
isMultistream: true,
|
|
2101
|
+
});
|
|
2102
|
+
registerStatsAnalyzerEvents(statsAnalyzer);
|
|
2103
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
2104
|
+
|
|
2105
|
+
await progressTime();
|
|
2106
|
+
|
|
2107
|
+
for (const data of [
|
|
2108
|
+
mqeData.audioTransmit,
|
|
2109
|
+
mqeData.audioReceive,
|
|
2110
|
+
mqeData.videoTransmit,
|
|
2111
|
+
mqeData.videoReceive,
|
|
2112
|
+
]) {
|
|
2113
|
+
assert.strictEqual(data[0].common.common.multistreamEnabled, true);
|
|
2114
|
+
}
|
|
2115
|
+
});
|
|
1816
2116
|
});
|
|
1817
|
-
|
|
2117
|
+
|
|
2118
|
+
describe('CPU Information Reporting', async () => {
|
|
2119
|
+
let getNumLogicalCoresStub;
|
|
2120
|
+
|
|
2121
|
+
beforeEach(async () => {
|
|
2122
|
+
getNumLogicalCoresStub = sinon.stub(CpuInfo, 'getNumLogicalCores');
|
|
2123
|
+
});
|
|
2124
|
+
|
|
2125
|
+
afterEach(() => {
|
|
2126
|
+
getNumLogicalCoresStub.restore();
|
|
2127
|
+
});
|
|
2128
|
+
|
|
2129
|
+
it('reports 1 of logical CPU cores when not available', async () => {
|
|
2130
|
+
getNumLogicalCoresStub.returns(undefined);
|
|
2131
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
2132
|
+
|
|
2133
|
+
await progressTime();
|
|
2134
|
+
assert.equal(mqeData.intervalMetadata.cpuInfo.numberOfCores, 1);
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2137
|
+
it('reports the number of logical CPU cores', async () => {
|
|
2138
|
+
getNumLogicalCoresStub.returns(12);
|
|
2139
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
2140
|
+
|
|
2141
|
+
await progressTime();
|
|
2142
|
+
assert.equal(mqeData.intervalMetadata.cpuInfo.numberOfCores, 12);
|
|
2143
|
+
});
|
|
2144
|
+
});
|
|
2145
|
+
})
|
|
1818
2146
|
});
|
|
1819
2147
|
});
|