@webex/plugin-meetings 3.3.0 → 3.3.1-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +4 -2
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/mediaQualityMetrics/config.js +20 -16
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/index.js +30 -13
- package/dist/meeting/index.js.map +1 -1
- package/dist/meetings/index.js +6 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/reachability/index.js +82 -9
- package/dist/reachability/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +77 -27
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +46 -7
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/constants.d.ts +2 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +14 -2
- package/dist/types/meeting/index.d.ts +8 -0
- package/dist/types/reachability/index.d.ts +11 -0
- package/dist/types/statsAnalyzer/index.d.ts +14 -6
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +17 -4
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/constants.ts +2 -1
- package/src/mediaQualityMetrics/config.ts +22 -10
- package/src/meeting/index.ts +29 -14
- package/src/meetings/index.ts +7 -2
- package/src/reachability/index.ts +57 -0
- package/src/statsAnalyzer/index.ts +82 -22
- package/src/statsAnalyzer/mqaUtil.ts +68 -4
- package/test/unit/spec/meeting/index.js +28 -8
- package/test/unit/spec/meetings/index.js +38 -15
- package/test/unit/spec/reachability/index.ts +266 -0
- package/test/unit/spec/stats-analyzer/index.js +630 -314
|
@@ -16,6 +16,14 @@ const {assert} = chai;
|
|
|
16
16
|
chai.use(chaiAsPromised);
|
|
17
17
|
sinon.assert.expose(chai.assert, {prefix: ''});
|
|
18
18
|
|
|
19
|
+
const startStatsAnalyzer = async ({statsAnalyzer, mediaStatus, lastEmittedEvents = {}, pc}) => {
|
|
20
|
+
statsAnalyzer.updateMediaStatus(mediaStatus);
|
|
21
|
+
statsAnalyzer.startAnalyzer(pc);
|
|
22
|
+
statsAnalyzer.lastEmittedStartStopEvent = lastEmittedEvents;
|
|
23
|
+
|
|
24
|
+
await testUtils.flushPromises();
|
|
25
|
+
};
|
|
26
|
+
|
|
19
27
|
describe('plugin-meetings', () => {
|
|
20
28
|
describe('StatsAnalyzer', () => {
|
|
21
29
|
describe('parseStatsResult', () => {
|
|
@@ -29,10 +37,12 @@ describe('plugin-meetings', () => {
|
|
|
29
37
|
const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
30
38
|
|
|
31
39
|
statsAnalyzer = new StatsAnalyzer(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
{
|
|
41
|
+
config: initialConfig,
|
|
42
|
+
receiveSlotCallback: () => ({}),
|
|
43
|
+
networkQualityMonitor,
|
|
44
|
+
statsResults: defaultStats,
|
|
45
|
+
},
|
|
36
46
|
);
|
|
37
47
|
});
|
|
38
48
|
|
|
@@ -64,7 +74,7 @@ describe('plugin-meetings', () => {
|
|
|
64
74
|
assert(calledSpy.calledOnce);
|
|
65
75
|
});
|
|
66
76
|
|
|
67
|
-
it('processOutboundRTPResult should create the correct stats results', () => {
|
|
77
|
+
it('processOutboundRTPResult should create the correct stats results for audio', () => {
|
|
68
78
|
// establish the `statsResults` object.
|
|
69
79
|
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-send', true);
|
|
70
80
|
|
|
@@ -80,8 +90,6 @@ describe('plugin-meetings', () => {
|
|
|
80
90
|
nackCount: 1,
|
|
81
91
|
packetsSent: 3600,
|
|
82
92
|
remoteId: 'RTCRemoteInboundRtpAudioStream_123456789',
|
|
83
|
-
retransmittedBytesSent: 100,
|
|
84
|
-
retransmittedPacketsSent: 2,
|
|
85
93
|
ssrc: 123456789,
|
|
86
94
|
targetBitrate: 256000,
|
|
87
95
|
timestamp: 1707341489336,
|
|
@@ -91,7 +99,7 @@ describe('plugin-meetings', () => {
|
|
|
91
99
|
requestedBitrate: 10000,
|
|
92
100
|
},
|
|
93
101
|
'audio-send',
|
|
94
|
-
true
|
|
102
|
+
true,
|
|
95
103
|
);
|
|
96
104
|
|
|
97
105
|
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.headerBytesSent, 25000);
|
|
@@ -99,17 +107,54 @@ describe('plugin-meetings', () => {
|
|
|
99
107
|
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalNackCount, 1);
|
|
100
108
|
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalPacketsSent, 3600);
|
|
101
109
|
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.requestedBitrate, 10000);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('processOutboundRTPResult should create the correct stats results for video', () => {
|
|
113
|
+
// establish the `statsResults` object for video.
|
|
114
|
+
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'video-send', true);
|
|
115
|
+
|
|
116
|
+
statsAnalyzer.processOutboundRTPResult(
|
|
117
|
+
{
|
|
118
|
+
bytesSent: 250000,
|
|
119
|
+
codecId: 'RTCCodec_1_Outbound_107',
|
|
120
|
+
headerBytesSent: 50000,
|
|
121
|
+
id: 'RTCOutboundRTPVideoStream_987654321',
|
|
122
|
+
kind: 'video',
|
|
123
|
+
mediaSourceId: 'RTCVideoSource_3',
|
|
124
|
+
mediaType: 'video',
|
|
125
|
+
nackCount: 5,
|
|
126
|
+
packetsSent: 15000,
|
|
127
|
+
remoteId: 'RTCRemoteInboundRtpVideoStream_987654321',
|
|
128
|
+
retransmittedBytesSent: 500,
|
|
129
|
+
retransmittedPacketsSent: 10,
|
|
130
|
+
ssrc: 987654321,
|
|
131
|
+
targetBitrate: 1024000,
|
|
132
|
+
timestamp: 1707341489336,
|
|
133
|
+
trackId: 'RTCMediaStreamTrack_sender_3',
|
|
134
|
+
transportId: 'RTCTransport_0_2',
|
|
135
|
+
type: 'outbound-rtp',
|
|
136
|
+
requestedBitrate: 50000,
|
|
137
|
+
},
|
|
138
|
+
'video-send',
|
|
139
|
+
true,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.headerBytesSent, 50000);
|
|
143
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.totalBytesSent, 250000);
|
|
144
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.totalNackCount, 5);
|
|
145
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.totalPacketsSent, 15000);
|
|
146
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.requestedBitrate, 50000);
|
|
102
147
|
assert.strictEqual(
|
|
103
|
-
statsAnalyzer.statsResults['
|
|
104
|
-
|
|
148
|
+
statsAnalyzer.statsResults['video-send'].send.totalRtxPacketsSent,
|
|
149
|
+
10,
|
|
105
150
|
);
|
|
106
151
|
assert.strictEqual(
|
|
107
|
-
statsAnalyzer.statsResults['
|
|
108
|
-
|
|
152
|
+
statsAnalyzer.statsResults['video-send'].send.totalRtxBytesSent,
|
|
153
|
+
500,
|
|
109
154
|
);
|
|
110
155
|
});
|
|
111
156
|
|
|
112
|
-
it('processInboundRTPResult should create the correct stats results', () => {
|
|
157
|
+
it('processInboundRTPResult should create the correct stats results for audio', () => {
|
|
113
158
|
// establish the `statsResults` object.
|
|
114
159
|
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-recv-1', false);
|
|
115
160
|
|
|
@@ -148,12 +193,12 @@ describe('plugin-meetings', () => {
|
|
|
148
193
|
requestedBitrate: 10000,
|
|
149
194
|
},
|
|
150
195
|
'audio-recv-1',
|
|
151
|
-
false
|
|
196
|
+
false,
|
|
152
197
|
);
|
|
153
198
|
|
|
154
199
|
assert.strictEqual(
|
|
155
200
|
statsAnalyzer.statsResults['audio-recv-1'].recv.totalPacketsReceived,
|
|
156
|
-
12
|
|
201
|
+
12,
|
|
157
202
|
);
|
|
158
203
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsDiscarded, 1);
|
|
159
204
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsReceived, 1);
|
|
@@ -161,21 +206,67 @@ describe('plugin-meetings', () => {
|
|
|
161
206
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.requestedBitrate, 10000);
|
|
162
207
|
assert.strictEqual(
|
|
163
208
|
statsAnalyzer.statsResults['audio-recv-1'].recv.headerBytesReceived,
|
|
164
|
-
250
|
|
209
|
+
250,
|
|
165
210
|
);
|
|
166
211
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.audioLevel, 0);
|
|
167
212
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalAudioEnergy, 133);
|
|
168
213
|
assert.strictEqual(
|
|
169
214
|
statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesReceived,
|
|
170
|
-
300000
|
|
215
|
+
300000,
|
|
171
216
|
);
|
|
172
217
|
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesDecoded, 0);
|
|
173
218
|
assert.strictEqual(
|
|
174
219
|
statsAnalyzer.statsResults['audio-recv-1'].recv.concealedSamples,
|
|
175
|
-
200000
|
|
220
|
+
200000,
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('processInboundRTPResult should create the correct stats results for video', () => {
|
|
225
|
+
// establish the `statsResults` object for video.
|
|
226
|
+
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'video-recv', false);
|
|
227
|
+
|
|
228
|
+
statsAnalyzer.processInboundRTPResult(
|
|
229
|
+
{
|
|
230
|
+
bytesReceived: 100000,
|
|
231
|
+
codecId: 'RTCCodec_6_Inbound_107',
|
|
232
|
+
fecPacketsDiscarded: 2,
|
|
233
|
+
fecPacketsReceived: 2,
|
|
234
|
+
headerBytesReceived: 10000,
|
|
235
|
+
id: 'RTCInboundRTPVideoStream_987654321',
|
|
236
|
+
jitter: 0.05,
|
|
237
|
+
jitterBufferDelay: 5000,
|
|
238
|
+
jitterBufferEmittedCount: 50000,
|
|
239
|
+
kind: 'video',
|
|
240
|
+
lastPacketReceivedTimestamp: 1707341488529,
|
|
241
|
+
mediaType: 'video',
|
|
242
|
+
packetsDiscarded: 5,
|
|
243
|
+
packetsLost: 10,
|
|
244
|
+
packetsReceived: 1500,
|
|
245
|
+
remoteId: 'RTCRemoteOutboundRTPVideoStream_987654321',
|
|
246
|
+
ssrc: 987654321,
|
|
247
|
+
timestamp: 1707341489419,
|
|
248
|
+
trackId: 'RTCMediaStreamTrack_receiver_3',
|
|
249
|
+
transportId: 'RTCTransport_0_2',
|
|
250
|
+
type: 'inbound-rtp',
|
|
251
|
+
requestedBitrate: 50000,
|
|
252
|
+
retransmittedBytesReceived: 500,
|
|
253
|
+
retransmittedPacketsReceived: 10,
|
|
254
|
+
},
|
|
255
|
+
'video-recv',
|
|
256
|
+
false,
|
|
176
257
|
);
|
|
258
|
+
|
|
259
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalPacketsReceived, 1500);
|
|
260
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.fecPacketsDiscarded, 2);
|
|
261
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.fecPacketsReceived, 2);
|
|
262
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalBytesReceived, 100000);
|
|
263
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.requestedBitrate, 50000);
|
|
264
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.headerBytesReceived, 10000);
|
|
265
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalRtxBytesReceived, 500);
|
|
266
|
+
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalRtxPacketsReceived, 10);
|
|
177
267
|
});
|
|
178
268
|
|
|
269
|
+
|
|
179
270
|
it('parseAudioSource should create the correct stats results', () => {
|
|
180
271
|
// establish the `statsResults` object.
|
|
181
272
|
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-send', true);
|
|
@@ -194,7 +285,7 @@ describe('plugin-meetings', () => {
|
|
|
194
285
|
type: 'media-source',
|
|
195
286
|
},
|
|
196
287
|
'audio-send',
|
|
197
|
-
true
|
|
288
|
+
true,
|
|
198
289
|
);
|
|
199
290
|
|
|
200
291
|
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.audioLevel, 0.03);
|
|
@@ -241,15 +332,17 @@ describe('plugin-meetings', () => {
|
|
|
241
332
|
const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
242
333
|
|
|
243
334
|
statsAnalyzer = new StatsAnalyzer(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
335
|
+
{
|
|
336
|
+
config: initialConfig,
|
|
337
|
+
receiveSlotCallback: () => ({}),
|
|
338
|
+
networkQualityMonitor,
|
|
339
|
+
statsResults: defaultStats,
|
|
340
|
+
},
|
|
248
341
|
);
|
|
249
342
|
|
|
250
343
|
sandBoxSpy = sandbox.spy(
|
|
251
344
|
statsAnalyzer.networkQualityMonitor,
|
|
252
|
-
'determineUplinkNetworkQuality'
|
|
345
|
+
'determineUplinkNetworkQuality',
|
|
253
346
|
);
|
|
254
347
|
});
|
|
255
348
|
|
|
@@ -266,7 +359,7 @@ describe('plugin-meetings', () => {
|
|
|
266
359
|
mediaType: 'video-send-1',
|
|
267
360
|
remoteRtpResults: statusResult,
|
|
268
361
|
statsAnalyzerCurrentStats: statsAnalyzer.statsResults,
|
|
269
|
-
})
|
|
362
|
+
}),
|
|
270
363
|
);
|
|
271
364
|
});
|
|
272
365
|
});
|
|
@@ -300,6 +393,24 @@ describe('plugin-meetings', () => {
|
|
|
300
393
|
};
|
|
301
394
|
};
|
|
302
395
|
|
|
396
|
+
const registerStatsAnalyzerEvents = (statsAnalyzer) => {
|
|
397
|
+
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
|
|
398
|
+
receivedEventsData.local.started = data;
|
|
399
|
+
});
|
|
400
|
+
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STOPPED, (data) => {
|
|
401
|
+
receivedEventsData.local.stopped = data;
|
|
402
|
+
});
|
|
403
|
+
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STARTED, (data) => {
|
|
404
|
+
receivedEventsData.remote.started = data;
|
|
405
|
+
});
|
|
406
|
+
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STOPPED, (data) => {
|
|
407
|
+
receivedEventsData.remote.stopped = data;
|
|
408
|
+
});
|
|
409
|
+
statsAnalyzer.on(EVENTS.MEDIA_QUALITY, ({data}) => {
|
|
410
|
+
mqeData = data;
|
|
411
|
+
});
|
|
412
|
+
};
|
|
413
|
+
|
|
303
414
|
before(() => {
|
|
304
415
|
LoggerConfig.set({enable: false});
|
|
305
416
|
LoggerProxy.set();
|
|
@@ -323,6 +434,7 @@ describe('plugin-meetings', () => {
|
|
|
323
434
|
type: 'outbound-rtp',
|
|
324
435
|
bytesSent: 1,
|
|
325
436
|
packetsSent: 0,
|
|
437
|
+
isRequested: true,
|
|
326
438
|
},
|
|
327
439
|
{
|
|
328
440
|
type: 'remote-inbound-rtp',
|
|
@@ -356,6 +468,8 @@ describe('plugin-meetings', () => {
|
|
|
356
468
|
fecPacketsReceived: 0,
|
|
357
469
|
packetsLost: 0,
|
|
358
470
|
packetsReceived: 0,
|
|
471
|
+
isRequested: true,
|
|
472
|
+
lastRequestedUpdateTimestamp: 0,
|
|
359
473
|
},
|
|
360
474
|
{
|
|
361
475
|
type: 'remote-outbound-rtp',
|
|
@@ -389,6 +503,8 @@ describe('plugin-meetings', () => {
|
|
|
389
503
|
bytesSent: 1,
|
|
390
504
|
framesSent: 0,
|
|
391
505
|
packetsSent: 0,
|
|
506
|
+
isRequested: true,
|
|
507
|
+
lastRequestedUpdateTimestamp: 0,
|
|
392
508
|
},
|
|
393
509
|
{
|
|
394
510
|
type: 'remote-inbound-rtp',
|
|
@@ -424,6 +540,10 @@ describe('plugin-meetings', () => {
|
|
|
424
540
|
framesReceived: 0,
|
|
425
541
|
packetsLost: 0,
|
|
426
542
|
packetsReceived: 0,
|
|
543
|
+
isRequested: true,
|
|
544
|
+
lastRequestedUpdateTimestamp: 0,
|
|
545
|
+
isActiveSpeaker: false,
|
|
546
|
+
lastActiveSpeakerUpdateTimestamp: 0,
|
|
427
547
|
},
|
|
428
548
|
{
|
|
429
549
|
type: 'remote-outbound-rtp',
|
|
@@ -457,6 +577,8 @@ describe('plugin-meetings', () => {
|
|
|
457
577
|
bytesSent: 1,
|
|
458
578
|
framesSent: 0,
|
|
459
579
|
packetsSent: 0,
|
|
580
|
+
isRequested: true,
|
|
581
|
+
lastRequestedUpdateTimestamp: 0,
|
|
460
582
|
},
|
|
461
583
|
{
|
|
462
584
|
type: 'remote-inbound-rtp',
|
|
@@ -492,6 +614,8 @@ describe('plugin-meetings', () => {
|
|
|
492
614
|
framesReceived: 0,
|
|
493
615
|
packetsLost: 0,
|
|
494
616
|
packetsReceived: 0,
|
|
617
|
+
isRequested: true,
|
|
618
|
+
lastRequestedUpdateTimestamp: 0,
|
|
495
619
|
},
|
|
496
620
|
{
|
|
497
621
|
type: 'remote-outbound-rtp',
|
|
@@ -541,23 +665,11 @@ describe('plugin-meetings', () => {
|
|
|
541
665
|
|
|
542
666
|
networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
543
667
|
|
|
544
|
-
statsAnalyzer = new StatsAnalyzer(
|
|
545
|
-
|
|
546
|
-
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
|
|
547
|
-
receivedEventsData.local.started = data;
|
|
548
|
-
});
|
|
549
|
-
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STOPPED, (data) => {
|
|
550
|
-
receivedEventsData.local.stopped = data;
|
|
551
|
-
});
|
|
552
|
-
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STARTED, (data) => {
|
|
553
|
-
receivedEventsData.remote.started = data;
|
|
554
|
-
});
|
|
555
|
-
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STOPPED, (data) => {
|
|
556
|
-
receivedEventsData.remote.stopped = data;
|
|
557
|
-
});
|
|
558
|
-
statsAnalyzer.on(EVENTS.MEDIA_QUALITY, ({data}) => {
|
|
559
|
-
mqeData = data;
|
|
668
|
+
statsAnalyzer = new StatsAnalyzer({
|
|
669
|
+
config: initialConfig, receiveSlotCallback: () => receiveSlot, networkQualityMonitor,
|
|
560
670
|
});
|
|
671
|
+
|
|
672
|
+
registerStatsAnalyzerEvents(statsAnalyzer);
|
|
561
673
|
});
|
|
562
674
|
|
|
563
675
|
afterEach(() => {
|
|
@@ -565,20 +677,12 @@ describe('plugin-meetings', () => {
|
|
|
565
677
|
clock.restore();
|
|
566
678
|
});
|
|
567
679
|
|
|
568
|
-
const startStatsAnalyzer = async (mediaStatus, lastEmittedEvents) => {
|
|
569
|
-
statsAnalyzer.updateMediaStatus(mediaStatus);
|
|
570
|
-
statsAnalyzer.startAnalyzer(pc);
|
|
571
|
-
statsAnalyzer.lastEmittedStartStopEvent = lastEmittedEvents || {};
|
|
572
|
-
|
|
573
|
-
await testUtils.flushPromises();
|
|
574
|
-
};
|
|
575
|
-
|
|
576
680
|
const mergeProperties = (
|
|
577
681
|
target,
|
|
578
682
|
properties,
|
|
579
683
|
keyValue = 'fake-candidate-id',
|
|
580
684
|
matchKey = 'type',
|
|
581
|
-
matchValue = 'local-candidate'
|
|
685
|
+
matchValue = 'local-candidate',
|
|
582
686
|
) => {
|
|
583
687
|
for (let key in target) {
|
|
584
688
|
if (target.hasOwnProperty(key)) {
|
|
@@ -623,7 +727,15 @@ describe('plugin-meetings', () => {
|
|
|
623
727
|
};
|
|
624
728
|
|
|
625
729
|
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for audio', async () => {
|
|
626
|
-
await startStatsAnalyzer({
|
|
730
|
+
await startStatsAnalyzer({
|
|
731
|
+
statsAnalyzer,
|
|
732
|
+
pc,
|
|
733
|
+
mediaStatus: {
|
|
734
|
+
expected: {
|
|
735
|
+
sendAudio: true,
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
});
|
|
627
739
|
|
|
628
740
|
// check that we haven't received any events yet
|
|
629
741
|
checkReceivedEvent({expected: {}});
|
|
@@ -643,7 +755,7 @@ describe('plugin-meetings', () => {
|
|
|
643
755
|
});
|
|
644
756
|
|
|
645
757
|
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for video', async () => {
|
|
646
|
-
await startStatsAnalyzer({expected: {sendVideo: true}});
|
|
758
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {sendVideo: true}}});
|
|
647
759
|
|
|
648
760
|
// check that we haven't received any events yet
|
|
649
761
|
checkReceivedEvent({expected: {}});
|
|
@@ -663,7 +775,7 @@ describe('plugin-meetings', () => {
|
|
|
663
775
|
});
|
|
664
776
|
|
|
665
777
|
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for share', async () => {
|
|
666
|
-
await startStatsAnalyzer({expected: {sendShare: true}});
|
|
778
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {sendShare: true}}});
|
|
667
779
|
|
|
668
780
|
// check that we haven't received any events yet
|
|
669
781
|
checkReceivedEvent({expected: {}});
|
|
@@ -683,7 +795,7 @@ describe('plugin-meetings', () => {
|
|
|
683
795
|
});
|
|
684
796
|
|
|
685
797
|
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for audio', async () => {
|
|
686
|
-
await startStatsAnalyzer({expected: {receiveAudio: true}});
|
|
798
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveAudio: true}}});
|
|
687
799
|
|
|
688
800
|
// check that we haven't received any events yet
|
|
689
801
|
checkReceivedEvent({expected: {}});
|
|
@@ -703,7 +815,7 @@ describe('plugin-meetings', () => {
|
|
|
703
815
|
});
|
|
704
816
|
|
|
705
817
|
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for video', async () => {
|
|
706
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
818
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
707
819
|
|
|
708
820
|
// check that we haven't received any events yet
|
|
709
821
|
checkReceivedEvent({expected: {}});
|
|
@@ -723,7 +835,7 @@ describe('plugin-meetings', () => {
|
|
|
723
835
|
});
|
|
724
836
|
|
|
725
837
|
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for share', async () => {
|
|
726
|
-
await startStatsAnalyzer({expected: {receiveShare: true}});
|
|
838
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveShare: true}}});
|
|
727
839
|
|
|
728
840
|
// check that we haven't received any events yet
|
|
729
841
|
checkReceivedEvent({expected: {}});
|
|
@@ -743,7 +855,7 @@ describe('plugin-meetings', () => {
|
|
|
743
855
|
});
|
|
744
856
|
|
|
745
857
|
it('emits the correct MEDIA_QUALITY events', async () => {
|
|
746
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
858
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
747
859
|
|
|
748
860
|
await progressTime();
|
|
749
861
|
|
|
@@ -752,7 +864,7 @@ describe('plugin-meetings', () => {
|
|
|
752
864
|
});
|
|
753
865
|
|
|
754
866
|
it('emits the correct transportType in MEDIA_QUALITY events', async () => {
|
|
755
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
867
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
756
868
|
|
|
757
869
|
await progressTime();
|
|
758
870
|
|
|
@@ -766,7 +878,7 @@ describe('plugin-meetings', () => {
|
|
|
766
878
|
fakeStats.audio.receivers[0].report[4].relayProtocol = 'tls';
|
|
767
879
|
fakeStats.video.receivers[0].report[4].relayProtocol = 'tls';
|
|
768
880
|
|
|
769
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
881
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
770
882
|
|
|
771
883
|
await progressTime();
|
|
772
884
|
|
|
@@ -775,19 +887,19 @@ describe('plugin-meetings', () => {
|
|
|
775
887
|
});
|
|
776
888
|
|
|
777
889
|
it('emits the correct peripherals in MEDIA_QUALITY events', async () => {
|
|
778
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
890
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
779
891
|
|
|
780
892
|
await progressTime();
|
|
781
893
|
|
|
782
894
|
assert.strictEqual(
|
|
783
895
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
|
|
784
896
|
.information,
|
|
785
|
-
'fake-microphone'
|
|
897
|
+
'fake-microphone',
|
|
786
898
|
);
|
|
787
899
|
assert.strictEqual(
|
|
788
900
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
|
|
789
901
|
.information,
|
|
790
|
-
'fake-camera'
|
|
902
|
+
'fake-camera',
|
|
791
903
|
);
|
|
792
904
|
});
|
|
793
905
|
|
|
@@ -795,30 +907,33 @@ describe('plugin-meetings', () => {
|
|
|
795
907
|
fakeStats.audio.senders[0].localTrackLabel = undefined;
|
|
796
908
|
fakeStats.video.senders[0].localTrackLabel = undefined;
|
|
797
909
|
|
|
798
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
910
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
799
911
|
|
|
800
912
|
await progressTime();
|
|
801
913
|
|
|
802
914
|
assert.strictEqual(
|
|
803
915
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
|
|
804
916
|
.information,
|
|
805
|
-
_UNKNOWN_
|
|
917
|
+
_UNKNOWN_,
|
|
806
918
|
);
|
|
807
919
|
assert.strictEqual(
|
|
808
920
|
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
|
|
809
921
|
.information,
|
|
810
|
-
_UNKNOWN_
|
|
922
|
+
_UNKNOWN_,
|
|
811
923
|
);
|
|
812
924
|
});
|
|
813
925
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
await startStatsAnalyzer();
|
|
926
|
+
describe('frame rate reporting in stats analyzer', () => {
|
|
927
|
+
beforeEach(async () => {
|
|
928
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
it('should report a zero frame rate for both transmitted and received video at the start', async () => {
|
|
817
932
|
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.transmittedFrameRate, 0);
|
|
818
933
|
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.receivedFrameRate, 0);
|
|
819
934
|
});
|
|
820
935
|
|
|
821
|
-
it('
|
|
936
|
+
it('should accurately report the transmitted and received frame rate after video frames are processed', async () => {
|
|
822
937
|
fakeStats.video.senders[0].report[0].framesSent += 300;
|
|
823
938
|
fakeStats.video.receivers[0].report[0].framesReceived += 300;
|
|
824
939
|
await progressTime(MQA_INTERVAL);
|
|
@@ -829,9 +944,12 @@ describe('plugin-meetings', () => {
|
|
|
829
944
|
});
|
|
830
945
|
});
|
|
831
946
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
await startStatsAnalyzer();
|
|
947
|
+
describe('RTP packets count in stats analyzer', () => {
|
|
948
|
+
beforeEach(async () => {
|
|
949
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
it('should report zero RTP packets for all streams at the start of the stats analyzer', async () => {
|
|
835
953
|
assert.strictEqual(mqeData.audioTransmit[0].common.rtpPackets, 0);
|
|
836
954
|
assert.strictEqual(mqeData.audioTransmit[0].streams[0].common.rtpPackets, 0);
|
|
837
955
|
assert.strictEqual(mqeData.audioReceive[0].common.rtpPackets, 0);
|
|
@@ -842,7 +960,7 @@ describe('plugin-meetings', () => {
|
|
|
842
960
|
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.rtpPackets, 0);
|
|
843
961
|
});
|
|
844
962
|
|
|
845
|
-
it('after packets are sent', async () => {
|
|
963
|
+
it('should update the RTP packets count correctly after audio and video packets are sent', async () => {
|
|
846
964
|
fakeStats.audio.senders[0].report[0].packetsSent += 5;
|
|
847
965
|
fakeStats.video.senders[0].report[0].packetsSent += 5;
|
|
848
966
|
await progressTime(MQA_INTERVAL);
|
|
@@ -853,7 +971,7 @@ describe('plugin-meetings', () => {
|
|
|
853
971
|
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.rtpPackets, 5);
|
|
854
972
|
});
|
|
855
973
|
|
|
856
|
-
it('after packets are received', async () => {
|
|
974
|
+
it('should update the RTP packets count correctly after audio and video packets are received', async () => {
|
|
857
975
|
fakeStats.audio.senders[0].report[0].packetsSent += 10;
|
|
858
976
|
fakeStats.video.senders[0].report[0].packetsSent += 10;
|
|
859
977
|
fakeStats.audio.receivers[0].report[0].packetsReceived += 10;
|
|
@@ -867,20 +985,23 @@ describe('plugin-meetings', () => {
|
|
|
867
985
|
});
|
|
868
986
|
});
|
|
869
987
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
await startStatsAnalyzer();
|
|
988
|
+
describe('FEC packet reporting in stats analyzer', () => {
|
|
989
|
+
beforeEach(async () => {
|
|
990
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
it('should initially report zero FEC packets at the start of the stats analyzer', async () => {
|
|
873
994
|
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 0);
|
|
874
995
|
});
|
|
875
996
|
|
|
876
|
-
it('
|
|
997
|
+
it('should accurately report the count of FEC packets received', async () => {
|
|
877
998
|
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 5;
|
|
878
999
|
await progressTime(MQA_INTERVAL);
|
|
879
1000
|
|
|
880
1001
|
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 5);
|
|
881
1002
|
});
|
|
882
1003
|
|
|
883
|
-
it('
|
|
1004
|
+
it('should correctly adjust the FEC packet count when packets are discarded', async () => {
|
|
884
1005
|
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 15;
|
|
885
1006
|
fakeStats.audio.receivers[0].report[0].fecPacketsDiscarded += 5;
|
|
886
1007
|
await progressTime(MQA_INTERVAL);
|
|
@@ -889,16 +1010,19 @@ describe('plugin-meetings', () => {
|
|
|
889
1010
|
});
|
|
890
1011
|
});
|
|
891
1012
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
await startStatsAnalyzer();
|
|
1013
|
+
describe('packet loss metrics reporting in stats analyzer', () => {
|
|
1014
|
+
beforeEach(async () => {
|
|
1015
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it('should report zero packet loss for both audio and video at the start of the stats analyzer', async () => {
|
|
895
1019
|
assert.strictEqual(mqeData.audioReceive[0].common.mediaHopByHopLost, 0);
|
|
896
1020
|
assert.strictEqual(mqeData.audioReceive[0].common.rtpHopByHopLost, 0);
|
|
897
1021
|
assert.strictEqual(mqeData.videoReceive[0].common.mediaHopByHopLost, 0);
|
|
898
1022
|
assert.strictEqual(mqeData.videoReceive[0].common.rtpHopByHopLost, 0);
|
|
899
1023
|
});
|
|
900
1024
|
|
|
901
|
-
it('after
|
|
1025
|
+
it('should update packet loss metrics correctly for both audio and video after packet loss is detected', async () => {
|
|
902
1026
|
fakeStats.audio.receivers[0].report[0].packetsLost += 5;
|
|
903
1027
|
fakeStats.video.receivers[0].report[0].packetsLost += 5;
|
|
904
1028
|
await progressTime(MQA_INTERVAL);
|
|
@@ -910,14 +1034,17 @@ describe('plugin-meetings', () => {
|
|
|
910
1034
|
});
|
|
911
1035
|
});
|
|
912
1036
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
await startStatsAnalyzer();
|
|
1037
|
+
describe('remote loss rate reporting in stats analyzer', () => {
|
|
1038
|
+
beforeEach(async () => {
|
|
1039
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
it('should report a zero remote loss rate for both audio and video at the start', async () => {
|
|
916
1043
|
assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 0);
|
|
917
1044
|
assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 0);
|
|
918
1045
|
});
|
|
919
1046
|
|
|
920
|
-
it('after packets are sent', async () => {
|
|
1047
|
+
it('should maintain a zero remote loss rate for both audio and video after packets are sent without loss', async () => {
|
|
921
1048
|
fakeStats.audio.senders[0].report[0].packetsSent += 100;
|
|
922
1049
|
fakeStats.video.senders[0].report[0].packetsSent += 100;
|
|
923
1050
|
await progressTime(MQA_INTERVAL);
|
|
@@ -926,7 +1053,7 @@ describe('plugin-meetings', () => {
|
|
|
926
1053
|
assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 0);
|
|
927
1054
|
});
|
|
928
1055
|
|
|
929
|
-
it('
|
|
1056
|
+
it('should accurately calculate the remote loss rate for both audio and video after packet loss is detected', async () => {
|
|
930
1057
|
fakeStats.audio.senders[0].report[0].packetsSent += 200;
|
|
931
1058
|
fakeStats.audio.senders[0].report[1].packetsLost += 10;
|
|
932
1059
|
fakeStats.video.senders[0].report[0].packetsSent += 200;
|
|
@@ -939,7 +1066,7 @@ describe('plugin-meetings', () => {
|
|
|
939
1066
|
});
|
|
940
1067
|
|
|
941
1068
|
it('has the correct localIpAddress set when the candidateType is host', async () => {
|
|
942
|
-
await startStatsAnalyzer();
|
|
1069
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
943
1070
|
|
|
944
1071
|
await progressTime();
|
|
945
1072
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -949,7 +1076,7 @@ describe('plugin-meetings', () => {
|
|
|
949
1076
|
});
|
|
950
1077
|
|
|
951
1078
|
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is set', async () => {
|
|
952
|
-
await startStatsAnalyzer();
|
|
1079
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
953
1080
|
|
|
954
1081
|
await progressTime();
|
|
955
1082
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -963,7 +1090,7 @@ describe('plugin-meetings', () => {
|
|
|
963
1090
|
});
|
|
964
1091
|
|
|
965
1092
|
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is not set', async () => {
|
|
966
|
-
await startStatsAnalyzer();
|
|
1093
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
967
1094
|
|
|
968
1095
|
await progressTime();
|
|
969
1096
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -977,7 +1104,7 @@ describe('plugin-meetings', () => {
|
|
|
977
1104
|
});
|
|
978
1105
|
|
|
979
1106
|
it('has no localIpAddress set when the candidateType is invalid', async () => {
|
|
980
|
-
await startStatsAnalyzer();
|
|
1107
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
981
1108
|
|
|
982
1109
|
await progressTime();
|
|
983
1110
|
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
@@ -988,8 +1115,10 @@ describe('plugin-meetings', () => {
|
|
|
988
1115
|
|
|
989
1116
|
it('logs a message when audio send packets do not increase', async () => {
|
|
990
1117
|
await startStatsAnalyzer(
|
|
991
|
-
{
|
|
992
|
-
|
|
1118
|
+
{
|
|
1119
|
+
statsAnalyzer, pc, mediaStatus: {expected: {sendAudio: true}},
|
|
1120
|
+
lastEmittedEvents: {audio: {local: EVENTS.LOCAL_MEDIA_STARTED}},
|
|
1121
|
+
},
|
|
993
1122
|
);
|
|
994
1123
|
|
|
995
1124
|
// don't increase the packets when time progresses.
|
|
@@ -997,15 +1126,17 @@ describe('plugin-meetings', () => {
|
|
|
997
1126
|
|
|
998
1127
|
assert(
|
|
999
1128
|
loggerSpy.calledWith(
|
|
1000
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
|
|
1001
|
-
)
|
|
1129
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent',
|
|
1130
|
+
),
|
|
1002
1131
|
);
|
|
1003
1132
|
});
|
|
1004
1133
|
|
|
1005
1134
|
it('does not log a message when audio send packets increase', async () => {
|
|
1006
|
-
await startStatsAnalyzer(
|
|
1007
|
-
|
|
1008
|
-
|
|
1135
|
+
await startStatsAnalyzer({
|
|
1136
|
+
statsAnalyzer, pc,
|
|
1137
|
+
mediaStatus: {expected: {sendAudio: true}},
|
|
1138
|
+
lastEmittedEvents: {audio: {local: EVENTS.LOCAL_MEDIA_STOPPED}},
|
|
1139
|
+
},
|
|
1009
1140
|
);
|
|
1010
1141
|
|
|
1011
1142
|
fakeStats.audio.senders[0].report[0].packetsSent += 5;
|
|
@@ -1013,15 +1144,16 @@ describe('plugin-meetings', () => {
|
|
|
1013
1144
|
|
|
1014
1145
|
assert(
|
|
1015
1146
|
loggerSpy.neverCalledWith(
|
|
1016
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
|
|
1017
|
-
)
|
|
1147
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent',
|
|
1148
|
+
),
|
|
1018
1149
|
);
|
|
1019
1150
|
});
|
|
1020
1151
|
|
|
1021
1152
|
it('logs a message when video send packets do not increase', async () => {
|
|
1022
|
-
await startStatsAnalyzer(
|
|
1023
|
-
|
|
1024
|
-
|
|
1153
|
+
await startStatsAnalyzer({
|
|
1154
|
+
statsAnalyzer, pc, mediaStatus: {expected: {sendVideo: true}},
|
|
1155
|
+
lastEmittedEvents: {video: {local: EVENTS.LOCAL_MEDIA_STARTED}},
|
|
1156
|
+
},
|
|
1025
1157
|
);
|
|
1026
1158
|
|
|
1027
1159
|
// don't increase the packets when time progresses.
|
|
@@ -1029,31 +1161,42 @@ describe('plugin-meetings', () => {
|
|
|
1029
1161
|
|
|
1030
1162
|
assert(
|
|
1031
1163
|
loggerSpy.calledWith(
|
|
1032
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
|
|
1033
|
-
)
|
|
1164
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent',
|
|
1165
|
+
),
|
|
1034
1166
|
);
|
|
1035
1167
|
});
|
|
1036
1168
|
|
|
1037
1169
|
it('does not log a message when video send packets increase', async () => {
|
|
1038
1170
|
await startStatsAnalyzer(
|
|
1039
|
-
{
|
|
1040
|
-
|
|
1041
|
-
|
|
1171
|
+
{
|
|
1172
|
+
statsAnalyzer, pc,
|
|
1173
|
+
mediaStatus: {
|
|
1174
|
+
expected: {
|
|
1175
|
+
sendVideo: true,
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
lastEmittedEvents: {
|
|
1179
|
+
video: {
|
|
1180
|
+
local: EVENTS.LOCAL_MEDIA_STOPPED,
|
|
1181
|
+
},
|
|
1182
|
+
},
|
|
1183
|
+
});
|
|
1042
1184
|
|
|
1043
1185
|
fakeStats.video.senders[0].report[0].packetsSent += 5;
|
|
1044
1186
|
await progressTime();
|
|
1045
1187
|
|
|
1046
1188
|
assert(
|
|
1047
1189
|
loggerSpy.neverCalledWith(
|
|
1048
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
|
|
1049
|
-
)
|
|
1190
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent',
|
|
1191
|
+
),
|
|
1050
1192
|
);
|
|
1051
1193
|
});
|
|
1052
1194
|
|
|
1053
1195
|
it('logs a message when share send packets do not increase', async () => {
|
|
1054
|
-
await startStatsAnalyzer(
|
|
1055
|
-
|
|
1056
|
-
|
|
1196
|
+
await startStatsAnalyzer({
|
|
1197
|
+
pc, mediaStatus: {expected: {sendShare: true}},
|
|
1198
|
+
lastEmittedEvents: {share: {local: EVENTS.LOCAL_MEDIA_STARTED}}, statsAnalyzer,
|
|
1199
|
+
},
|
|
1057
1200
|
);
|
|
1058
1201
|
|
|
1059
1202
|
// don't increase the packets when time progresses.
|
|
@@ -1061,15 +1204,16 @@ describe('plugin-meetings', () => {
|
|
|
1061
1204
|
|
|
1062
1205
|
assert(
|
|
1063
1206
|
loggerSpy.calledWith(
|
|
1064
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
|
|
1065
|
-
)
|
|
1207
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent',
|
|
1208
|
+
),
|
|
1066
1209
|
);
|
|
1067
1210
|
});
|
|
1068
1211
|
|
|
1069
1212
|
it('does not log a message when share send packets increase', async () => {
|
|
1070
|
-
await startStatsAnalyzer(
|
|
1071
|
-
|
|
1072
|
-
|
|
1213
|
+
await startStatsAnalyzer({
|
|
1214
|
+
pc, statsAnalyzer, mediaStatus: {expected: {sendShare: true}},
|
|
1215
|
+
lastEmittedEvents: {share: {local: EVENTS.LOCAL_MEDIA_STOPPED}},
|
|
1216
|
+
},
|
|
1073
1217
|
);
|
|
1074
1218
|
|
|
1075
1219
|
fakeStats.share.senders[0].report[0].packetsSent += 5;
|
|
@@ -1077,8 +1221,8 @@ describe('plugin-meetings', () => {
|
|
|
1077
1221
|
|
|
1078
1222
|
assert(
|
|
1079
1223
|
loggerSpy.neverCalledWith(
|
|
1080
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
|
|
1081
|
-
)
|
|
1224
|
+
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent',
|
|
1225
|
+
),
|
|
1082
1226
|
);
|
|
1083
1227
|
});
|
|
1084
1228
|
|
|
@@ -1091,7 +1235,7 @@ describe('plugin-meetings', () => {
|
|
|
1091
1235
|
id: '4',
|
|
1092
1236
|
};
|
|
1093
1237
|
|
|
1094
|
-
await startStatsAnalyzer();
|
|
1238
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1095
1239
|
|
|
1096
1240
|
// don't increase the packets when time progresses.
|
|
1097
1241
|
await progressTime();
|
|
@@ -1099,10 +1243,10 @@ describe('plugin-meetings', () => {
|
|
|
1099
1243
|
assert.neverCalledWith(
|
|
1100
1244
|
loggerSpy,
|
|
1101
1245
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1102
|
-
0
|
|
1246
|
+
0,
|
|
1103
1247
|
);
|
|
1104
1248
|
});
|
|
1105
|
-
}
|
|
1249
|
+
},
|
|
1106
1250
|
);
|
|
1107
1251
|
|
|
1108
1252
|
it(`logs a message if no packets are sent`, async () => {
|
|
@@ -1111,7 +1255,7 @@ describe('plugin-meetings', () => {
|
|
|
1111
1255
|
csi: 2,
|
|
1112
1256
|
id: '4',
|
|
1113
1257
|
};
|
|
1114
|
-
await startStatsAnalyzer();
|
|
1258
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1115
1259
|
|
|
1116
1260
|
// don't increase the packets when time progresses.
|
|
1117
1261
|
await progressTime();
|
|
@@ -1119,52 +1263,52 @@ describe('plugin-meetings', () => {
|
|
|
1119
1263
|
assert.calledWith(
|
|
1120
1264
|
loggerSpy,
|
|
1121
1265
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1122
|
-
0
|
|
1266
|
+
0,
|
|
1123
1267
|
);
|
|
1124
1268
|
|
|
1125
1269
|
assert.calledWith(
|
|
1126
1270
|
loggerSpy,
|
|
1127
1271
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
|
|
1128
|
-
0
|
|
1272
|
+
0,
|
|
1129
1273
|
);
|
|
1130
1274
|
|
|
1131
1275
|
assert.calledWith(
|
|
1132
1276
|
loggerSpy,
|
|
1133
1277
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
|
|
1134
|
-
0
|
|
1278
|
+
0,
|
|
1135
1279
|
);
|
|
1136
1280
|
|
|
1137
1281
|
assert.calledWith(
|
|
1138
1282
|
loggerSpy,
|
|
1139
1283
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1140
|
-
0
|
|
1284
|
+
0,
|
|
1141
1285
|
);
|
|
1142
1286
|
|
|
1143
1287
|
assert.calledWith(
|
|
1144
1288
|
loggerSpy,
|
|
1145
1289
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1146
|
-
0
|
|
1290
|
+
0,
|
|
1147
1291
|
);
|
|
1148
1292
|
|
|
1149
1293
|
assert.calledWith(
|
|
1150
1294
|
loggerSpy,
|
|
1151
1295
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
|
|
1152
|
-
0
|
|
1296
|
+
0,
|
|
1153
1297
|
);
|
|
1154
1298
|
assert.calledWith(
|
|
1155
1299
|
loggerSpy,
|
|
1156
1300
|
'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
|
|
1157
|
-
0
|
|
1301
|
+
0,
|
|
1158
1302
|
);
|
|
1159
1303
|
assert.calledWith(
|
|
1160
1304
|
loggerSpy,
|
|
1161
1305
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1162
|
-
0
|
|
1306
|
+
0,
|
|
1163
1307
|
);
|
|
1164
1308
|
});
|
|
1165
1309
|
|
|
1166
1310
|
it(`does not log a message if receiveSlot is undefined`, async () => {
|
|
1167
|
-
await startStatsAnalyzer();
|
|
1311
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1168
1312
|
|
|
1169
1313
|
// don't increase the packets when time progresses.
|
|
1170
1314
|
await progressTime();
|
|
@@ -1172,12 +1316,12 @@ describe('plugin-meetings', () => {
|
|
|
1172
1316
|
assert.neverCalledWith(
|
|
1173
1317
|
loggerSpy,
|
|
1174
1318
|
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot "". Total packets received on slot: ',
|
|
1175
|
-
0
|
|
1319
|
+
0,
|
|
1176
1320
|
);
|
|
1177
1321
|
});
|
|
1178
1322
|
|
|
1179
1323
|
it('has the correct number of senders and receivers (2)', async () => {
|
|
1180
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1324
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
1181
1325
|
|
|
1182
1326
|
await progressTime();
|
|
1183
1327
|
|
|
@@ -1188,7 +1332,7 @@ describe('plugin-meetings', () => {
|
|
|
1188
1332
|
});
|
|
1189
1333
|
|
|
1190
1334
|
it('has one stream per sender/reciever', async () => {
|
|
1191
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1335
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
1192
1336
|
|
|
1193
1337
|
await progressTime();
|
|
1194
1338
|
|
|
@@ -1375,7 +1519,7 @@ describe('plugin-meetings', () => {
|
|
|
1375
1519
|
framesDropped: 0,
|
|
1376
1520
|
},
|
|
1377
1521
|
h264CodecProfile: 'BP',
|
|
1378
|
-
isActiveSpeaker:
|
|
1522
|
+
isActiveSpeaker: false,
|
|
1379
1523
|
optimalFrameSize: 0,
|
|
1380
1524
|
receivedFrameSize: 3600,
|
|
1381
1525
|
receivedHeight: 720,
|
|
@@ -1409,7 +1553,7 @@ describe('plugin-meetings', () => {
|
|
|
1409
1553
|
framesDropped: 0,
|
|
1410
1554
|
},
|
|
1411
1555
|
h264CodecProfile: 'BP',
|
|
1412
|
-
isActiveSpeaker:
|
|
1556
|
+
isActiveSpeaker: false,
|
|
1413
1557
|
optimalFrameSize: 0,
|
|
1414
1558
|
receivedFrameSize: 3600,
|
|
1415
1559
|
receivedHeight: 720,
|
|
@@ -1448,7 +1592,7 @@ describe('plugin-meetings', () => {
|
|
|
1448
1592
|
},
|
|
1449
1593
|
});
|
|
1450
1594
|
|
|
1451
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1595
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
1452
1596
|
|
|
1453
1597
|
await progressTime();
|
|
1454
1598
|
|
|
@@ -1473,7 +1617,7 @@ describe('plugin-meetings', () => {
|
|
|
1473
1617
|
framesDropped: 0,
|
|
1474
1618
|
},
|
|
1475
1619
|
h264CodecProfile: 'BP',
|
|
1476
|
-
isActiveSpeaker:
|
|
1620
|
+
isActiveSpeaker: false,
|
|
1477
1621
|
optimalFrameSize: 0,
|
|
1478
1622
|
receivedFrameSize: 3600,
|
|
1479
1623
|
receivedHeight: 720,
|
|
@@ -1505,7 +1649,7 @@ describe('plugin-meetings', () => {
|
|
|
1505
1649
|
framesDropped: 0,
|
|
1506
1650
|
},
|
|
1507
1651
|
h264CodecProfile: 'BP',
|
|
1508
|
-
isActiveSpeaker:
|
|
1652
|
+
isActiveSpeaker: false,
|
|
1509
1653
|
optimalFrameSize: 0,
|
|
1510
1654
|
receivedFrameSize: 3600,
|
|
1511
1655
|
receivedHeight: 720,
|
|
@@ -1537,7 +1681,7 @@ describe('plugin-meetings', () => {
|
|
|
1537
1681
|
framesDropped: 0,
|
|
1538
1682
|
},
|
|
1539
1683
|
h264CodecProfile: 'BP',
|
|
1540
|
-
isActiveSpeaker:
|
|
1684
|
+
isActiveSpeaker: false,
|
|
1541
1685
|
optimalFrameSize: 0,
|
|
1542
1686
|
receivedFrameSize: 3600,
|
|
1543
1687
|
receivedHeight: 720,
|
|
@@ -1552,186 +1696,358 @@ describe('plugin-meetings', () => {
|
|
|
1552
1696
|
]);
|
|
1553
1697
|
});
|
|
1554
1698
|
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1699
|
+
describe('stream count for simulcast', async () => {
|
|
1700
|
+
it('has three streams for video senders for simulcast', async () => {
|
|
1701
|
+
pc.getTransceiverStats = sinon.stub().resolves({
|
|
1702
|
+
audio: {
|
|
1703
|
+
senders: [fakeStats.audio.senders[0]],
|
|
1704
|
+
receivers: [fakeStats.audio.receivers[0]],
|
|
1705
|
+
},
|
|
1706
|
+
video: {
|
|
1707
|
+
senders: [
|
|
1708
|
+
{
|
|
1709
|
+
localTrackLabel: 'fake-camera',
|
|
1710
|
+
report: [
|
|
1711
|
+
{
|
|
1712
|
+
type: 'outbound-rtp',
|
|
1713
|
+
bytesSent: 1,
|
|
1714
|
+
framesSent: 0,
|
|
1715
|
+
packetsSent: 0,
|
|
1716
|
+
isRequested: true,
|
|
1717
|
+
},
|
|
1718
|
+
{
|
|
1719
|
+
type: 'outbound-rtp',
|
|
1720
|
+
bytesSent: 1,
|
|
1721
|
+
framesSent: 0,
|
|
1722
|
+
packetsSent: 1,
|
|
1723
|
+
isRequested: true,
|
|
1724
|
+
},
|
|
1725
|
+
{
|
|
1726
|
+
type: 'outbound-rtp',
|
|
1727
|
+
bytesSent: 1000,
|
|
1728
|
+
framesSent: 1,
|
|
1729
|
+
packetsSent: 0,
|
|
1730
|
+
isRequested: true,
|
|
1731
|
+
},
|
|
1732
|
+
{
|
|
1733
|
+
type: 'remote-inbound-rtp',
|
|
1734
|
+
packetsLost: 0,
|
|
1735
|
+
},
|
|
1736
|
+
{
|
|
1737
|
+
type: 'candidate-pair',
|
|
1738
|
+
state: 'succeeded',
|
|
1739
|
+
localCandidateId: 'fake-candidate-id',
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
type: 'candidate-pair',
|
|
1743
|
+
state: 'failed',
|
|
1744
|
+
localCandidateId: 'bad-candidate-id',
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
type: 'local-candidate',
|
|
1748
|
+
id: 'fake-candidate-id',
|
|
1749
|
+
protocol: 'tcp',
|
|
1750
|
+
},
|
|
1751
|
+
],
|
|
1752
|
+
},
|
|
1753
|
+
],
|
|
1754
|
+
receivers: [fakeStats.video.receivers[0]],
|
|
1755
|
+
},
|
|
1756
|
+
screenShareAudio: {
|
|
1757
|
+
senders: [fakeStats.audio.senders[0]],
|
|
1758
|
+
receivers: [fakeStats.audio.receivers[0]],
|
|
1759
|
+
},
|
|
1760
|
+
screenShareVideo: {
|
|
1761
|
+
senders: [fakeStats.video.senders[0]],
|
|
1762
|
+
receivers: [fakeStats.video.receivers[0]],
|
|
1763
|
+
},
|
|
1764
|
+
});
|
|
1619
1765
|
|
|
1620
|
-
|
|
1766
|
+
await startStatsAnalyzer({
|
|
1767
|
+
pc,
|
|
1768
|
+
statsAnalyzer,
|
|
1769
|
+
mediaStatus: {
|
|
1770
|
+
expected: {
|
|
1771
|
+
receiveVideo: true,
|
|
1772
|
+
},
|
|
1773
|
+
},
|
|
1774
|
+
});
|
|
1621
1775
|
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1776
|
+
await progressTime();
|
|
1777
|
+
|
|
1778
|
+
assert.deepEqual(mqeData.videoTransmit[0].streams, [
|
|
1779
|
+
{
|
|
1780
|
+
common: {
|
|
1781
|
+
codec: 'H264',
|
|
1782
|
+
csi: [],
|
|
1783
|
+
duplicateSsci: 0,
|
|
1784
|
+
requestedBitrate: 0,
|
|
1785
|
+
requestedFrames: 0,
|
|
1786
|
+
rtpPackets: 0,
|
|
1787
|
+
ssci: 0,
|
|
1788
|
+
transmittedBitrate: 0.13333333333333333,
|
|
1789
|
+
transmittedFrameRate: 0,
|
|
1790
|
+
},
|
|
1791
|
+
h264CodecProfile: 'BP',
|
|
1792
|
+
isAvatar: false,
|
|
1793
|
+
isHardwareEncoded: false,
|
|
1794
|
+
localConfigurationChanges: 2,
|
|
1795
|
+
maxFrameQp: 0,
|
|
1796
|
+
maxNoiseLevel: 0,
|
|
1797
|
+
minRegionQp: 0,
|
|
1798
|
+
remoteConfigurationChanges: 0,
|
|
1799
|
+
requestedFrameSize: 0,
|
|
1800
|
+
requestedKeyFrames: 0,
|
|
1801
|
+
transmittedFrameSize: 0,
|
|
1802
|
+
transmittedHeight: 0,
|
|
1803
|
+
transmittedKeyFrames: 0,
|
|
1804
|
+
transmittedKeyFramesClient: 0,
|
|
1805
|
+
transmittedKeyFramesConfigurationChange: 0,
|
|
1806
|
+
transmittedKeyFramesFeedback: 0,
|
|
1807
|
+
transmittedKeyFramesLocalDrop: 0,
|
|
1808
|
+
transmittedKeyFramesOtherLayer: 0,
|
|
1809
|
+
transmittedKeyFramesPeriodic: 0,
|
|
1810
|
+
transmittedKeyFramesSceneChange: 0,
|
|
1811
|
+
transmittedKeyFramesStartup: 0,
|
|
1812
|
+
transmittedKeyFramesUnknown: 0,
|
|
1813
|
+
transmittedWidth: 0,
|
|
1628
1814
|
requestedBitrate: 0,
|
|
1629
|
-
requestedFrames: 0,
|
|
1630
|
-
rtpPackets: 0,
|
|
1631
|
-
ssci: 0,
|
|
1632
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1633
|
-
transmittedFrameRate: 0
|
|
1634
1815
|
},
|
|
1635
|
-
|
|
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
|
-
|
|
1816
|
+
{
|
|
1817
|
+
common: {
|
|
1818
|
+
codec: 'H264',
|
|
1819
|
+
csi: [],
|
|
1820
|
+
duplicateSsci: 0,
|
|
1821
|
+
requestedBitrate: 0,
|
|
1822
|
+
requestedFrames: 0,
|
|
1823
|
+
rtpPackets: 1,
|
|
1824
|
+
ssci: 0,
|
|
1825
|
+
transmittedBitrate: 0.13333333333333333,
|
|
1826
|
+
transmittedFrameRate: 0,
|
|
1827
|
+
},
|
|
1828
|
+
h264CodecProfile: 'BP',
|
|
1829
|
+
isAvatar: false,
|
|
1830
|
+
isHardwareEncoded: false,
|
|
1831
|
+
localConfigurationChanges: 2,
|
|
1832
|
+
maxFrameQp: 0,
|
|
1833
|
+
maxNoiseLevel: 0,
|
|
1834
|
+
minRegionQp: 0,
|
|
1835
|
+
remoteConfigurationChanges: 0,
|
|
1836
|
+
requestedFrameSize: 0,
|
|
1837
|
+
requestedKeyFrames: 0,
|
|
1838
|
+
transmittedFrameSize: 0,
|
|
1839
|
+
transmittedHeight: 0,
|
|
1840
|
+
transmittedKeyFrames: 0,
|
|
1841
|
+
transmittedKeyFramesClient: 0,
|
|
1842
|
+
transmittedKeyFramesConfigurationChange: 0,
|
|
1843
|
+
transmittedKeyFramesFeedback: 0,
|
|
1844
|
+
transmittedKeyFramesLocalDrop: 0,
|
|
1845
|
+
transmittedKeyFramesOtherLayer: 0,
|
|
1846
|
+
transmittedKeyFramesPeriodic: 0,
|
|
1847
|
+
transmittedKeyFramesSceneChange: 0,
|
|
1848
|
+
transmittedKeyFramesStartup: 0,
|
|
1849
|
+
transmittedKeyFramesUnknown: 0,
|
|
1850
|
+
transmittedWidth: 0,
|
|
1665
1851
|
requestedBitrate: 0,
|
|
1666
|
-
requestedFrames: 0,
|
|
1667
|
-
rtpPackets: 0,
|
|
1668
|
-
ssci: 0,
|
|
1669
|
-
transmittedBitrate: 0,
|
|
1670
|
-
transmittedFrameRate: 0,
|
|
1671
1852
|
},
|
|
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
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1853
|
+
{
|
|
1854
|
+
common: {
|
|
1855
|
+
codec: 'H264',
|
|
1856
|
+
csi: [],
|
|
1857
|
+
duplicateSsci: 0,
|
|
1858
|
+
requestedBitrate: 0,
|
|
1859
|
+
requestedFrames: 0,
|
|
1860
|
+
rtpPackets: 0,
|
|
1861
|
+
ssci: 0,
|
|
1862
|
+
transmittedBitrate: 133.33333333333334,
|
|
1863
|
+
transmittedFrameRate: 0,
|
|
1864
|
+
},
|
|
1865
|
+
h264CodecProfile: 'BP',
|
|
1866
|
+
isAvatar: false,
|
|
1867
|
+
isHardwareEncoded: false,
|
|
1868
|
+
localConfigurationChanges: 2,
|
|
1869
|
+
maxFrameQp: 0,
|
|
1870
|
+
maxNoiseLevel: 0,
|
|
1871
|
+
minRegionQp: 0,
|
|
1872
|
+
remoteConfigurationChanges: 0,
|
|
1873
|
+
requestedFrameSize: 0,
|
|
1874
|
+
requestedKeyFrames: 0,
|
|
1875
|
+
transmittedFrameSize: 0,
|
|
1876
|
+
transmittedHeight: 0,
|
|
1877
|
+
transmittedKeyFrames: 0,
|
|
1878
|
+
transmittedKeyFramesClient: 0,
|
|
1879
|
+
transmittedKeyFramesConfigurationChange: 0,
|
|
1880
|
+
transmittedKeyFramesFeedback: 0,
|
|
1881
|
+
transmittedKeyFramesLocalDrop: 0,
|
|
1882
|
+
transmittedKeyFramesOtherLayer: 0,
|
|
1883
|
+
transmittedKeyFramesPeriodic: 0,
|
|
1884
|
+
transmittedKeyFramesSceneChange: 0,
|
|
1885
|
+
transmittedKeyFramesStartup: 0,
|
|
1886
|
+
transmittedKeyFramesUnknown: 0,
|
|
1887
|
+
transmittedWidth: 0,
|
|
1702
1888
|
requestedBitrate: 0,
|
|
1703
|
-
requestedFrames: 0,
|
|
1704
|
-
rtpPackets: 1,
|
|
1705
|
-
ssci: 0,
|
|
1706
|
-
transmittedBitrate: 133.33333333333334,
|
|
1707
|
-
transmittedFrameRate: 0,
|
|
1708
1889
|
},
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1890
|
+
]);
|
|
1891
|
+
});
|
|
1892
|
+
});
|
|
1893
|
+
describe('active speaker status emission', async () => {
|
|
1894
|
+
beforeEach(async () => {
|
|
1895
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1896
|
+
performance.timeOrigin = 1;
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1899
|
+
it('reports active speaker as true when the participant has been speaking', async () => {
|
|
1900
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = true;
|
|
1901
|
+
await progressTime(5 * MQA_INTERVAL);
|
|
1902
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, true);
|
|
1903
|
+
});
|
|
1904
|
+
|
|
1905
|
+
it('reports active speaker as false when the participant has not spoken', async () => {
|
|
1906
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = false;
|
|
1907
|
+
await progressTime(5 * MQA_INTERVAL);
|
|
1908
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1909
|
+
});
|
|
1910
|
+
|
|
1911
|
+
it('defaults to false when active speaker status is indeterminate', async () => {
|
|
1912
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = undefined;
|
|
1913
|
+
await progressTime(MQA_INTERVAL);
|
|
1914
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
it('updates active speaker to true following a recent status change to speaking', async () => {
|
|
1918
|
+
fakeStats.video.receivers[0].report[0].isActiveSpeaker = false;
|
|
1919
|
+
fakeStats.video.receivers[0].report[0].lastActiveSpeakerUpdateTimestamp = performance.timeOrigin + performance.now() + (30 * 1000);
|
|
1920
|
+
await progressTime(MQA_INTERVAL);
|
|
1921
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, true);
|
|
1922
|
+
await progressTime(MQA_INTERVAL);
|
|
1923
|
+
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1924
|
+
});
|
|
1925
|
+
});
|
|
1926
|
+
describe('sends streams according to their is requested flag', async () => {
|
|
1927
|
+
|
|
1928
|
+
beforeEach(async () => {
|
|
1929
|
+
performance.timeOrigin = 0;
|
|
1930
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
it('should send a stream if it is requested', async () => {
|
|
1934
|
+
fakeStats.audio.senders[0].report[0].isRequested = true;
|
|
1935
|
+
await progressTime(MQA_INTERVAL);
|
|
1936
|
+
assert.strictEqual(mqeData.audioTransmit[0].streams.length, 1);
|
|
1937
|
+
});
|
|
1938
|
+
|
|
1939
|
+
it('should not sent a stream if its is requested flag is undefined', async () => {
|
|
1940
|
+
fakeStats.audio.senders[0].report[0].isRequested = undefined;
|
|
1941
|
+
await progressTime(MQA_INTERVAL);
|
|
1942
|
+
assert.strictEqual(mqeData.audioTransmit[0].streams.length, 0);
|
|
1943
|
+
});
|
|
1944
|
+
|
|
1945
|
+
it('should not send a stream if it is not requested', async () => {
|
|
1946
|
+
fakeStats.audio.receivers[0].report[0].isRequested = false;
|
|
1947
|
+
await progressTime(MQA_INTERVAL);
|
|
1948
|
+
assert.strictEqual(mqeData.audioReceive[0].streams.length, 0);
|
|
1949
|
+
});
|
|
1950
|
+
|
|
1951
|
+
it('should send the stream if it was recently requested', async () => {
|
|
1952
|
+
fakeStats.audio.receivers[0].report[0].lastRequestedUpdateTimestamp = performance.timeOrigin + performance.now() + (30 * 1000);
|
|
1953
|
+
fakeStats.audio.receivers[0].report[0].isRequested = false;
|
|
1954
|
+
await progressTime(MQA_INTERVAL);
|
|
1955
|
+
assert.strictEqual(mqeData.audioReceive[0].streams.length, 1);
|
|
1956
|
+
await progressTime(MQA_INTERVAL);
|
|
1957
|
+
assert.strictEqual(mqeData.audioReceive[0].streams.length, 0);
|
|
1958
|
+
});
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
describe('window and screen size emission', async () => {
|
|
1962
|
+
beforeEach(async () => {
|
|
1963
|
+
await startStatsAnalyzer({pc, statsAnalyzer});
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
it('should record the screen size from window.screen properties', async () => {
|
|
1967
|
+
sinon.stub(window.screen, 'width').get(() => 1280);
|
|
1968
|
+
sinon.stub(window.screen, 'height').get(() => 720);
|
|
1969
|
+
await progressTime(MQA_INTERVAL);
|
|
1970
|
+
assert.strictEqual(mqeData.intervalMetadata.screenWidth, 1280);
|
|
1971
|
+
assert.strictEqual(mqeData.intervalMetadata.screenHeight, 720);
|
|
1972
|
+
assert.strictEqual(mqeData.intervalMetadata.screenResolution, 3600);
|
|
1973
|
+
});
|
|
1974
|
+
|
|
1975
|
+
it('should record the initial app window size from window properties', async () => {
|
|
1976
|
+
sinon.stub(window, 'innerWidth').get(() => 720);
|
|
1977
|
+
sinon.stub(window, 'innerHeight').get(() => 360);
|
|
1978
|
+
await progressTime(MQA_INTERVAL);
|
|
1979
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowWidth, 720);
|
|
1980
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowHeight, 360);
|
|
1981
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowSize, 1013);
|
|
1982
|
+
|
|
1983
|
+
sinon.stub(window, 'innerWidth').get(() => 1080);
|
|
1984
|
+
sinon.stub(window, 'innerHeight').get(() => 720);
|
|
1985
|
+
await progressTime(MQA_INTERVAL);
|
|
1986
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowWidth, 1080);
|
|
1987
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowHeight, 720);
|
|
1988
|
+
assert.strictEqual(mqeData.intervalMetadata.appWindowSize, 3038);
|
|
1989
|
+
});
|
|
1990
|
+
});
|
|
1991
|
+
|
|
1992
|
+
describe('sends multistreamEnabled', async () => {
|
|
1993
|
+
it('false if StatsAnalyzer initialized with default value for isMultistream', async () => {
|
|
1994
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
1995
|
+
|
|
1996
|
+
await progressTime();
|
|
1997
|
+
|
|
1998
|
+
for (const data of [
|
|
1999
|
+
mqeData.audioTransmit,
|
|
2000
|
+
mqeData.audioReceive,
|
|
2001
|
+
mqeData.videoTransmit,
|
|
2002
|
+
mqeData.videoReceive,
|
|
2003
|
+
]) {
|
|
2004
|
+
assert.strictEqual(data[0].common.common.multistreamEnabled, false);
|
|
1733
2005
|
}
|
|
1734
|
-
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
it('false if StatsAnalyzer initialized with false', async () => {
|
|
2009
|
+
statsAnalyzer = new StatsAnalyzer({
|
|
2010
|
+
config: initialConfig,
|
|
2011
|
+
receiveSlotCallback: () => receiveSlot,
|
|
2012
|
+
networkQualityMonitor,
|
|
2013
|
+
isMultistream: false,
|
|
2014
|
+
});
|
|
2015
|
+
registerStatsAnalyzerEvents(statsAnalyzer);
|
|
2016
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: false}}});
|
|
2017
|
+
|
|
2018
|
+
await progressTime();
|
|
2019
|
+
|
|
2020
|
+
for (const data of [
|
|
2021
|
+
mqeData.audioTransmit,
|
|
2022
|
+
mqeData.audioReceive,
|
|
2023
|
+
mqeData.videoTransmit,
|
|
2024
|
+
mqeData.videoReceive,
|
|
2025
|
+
]) {
|
|
2026
|
+
assert.strictEqual(data[0].common.common.multistreamEnabled, false);
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
it('true if StatsAnalyzer initialized with multistream', async () => {
|
|
2031
|
+
statsAnalyzer = new StatsAnalyzer({
|
|
2032
|
+
config: initialConfig,
|
|
2033
|
+
receiveSlotCallback: () => receiveSlot,
|
|
2034
|
+
networkQualityMonitor,
|
|
2035
|
+
isMultistream: true,
|
|
2036
|
+
});
|
|
2037
|
+
registerStatsAnalyzerEvents(statsAnalyzer);
|
|
2038
|
+
await startStatsAnalyzer({pc, statsAnalyzer, mediaStatus: {expected: {receiveVideo: true}}});
|
|
2039
|
+
|
|
2040
|
+
await progressTime();
|
|
2041
|
+
|
|
2042
|
+
for (const data of [
|
|
2043
|
+
mqeData.audioTransmit,
|
|
2044
|
+
mqeData.audioReceive,
|
|
2045
|
+
mqeData.videoTransmit,
|
|
2046
|
+
mqeData.videoReceive,
|
|
2047
|
+
]) {
|
|
2048
|
+
assert.strictEqual(data[0].common.common.multistreamEnabled, true);
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
1735
2051
|
});
|
|
1736
2052
|
});
|
|
1737
2053
|
});
|