@webex/plugin-meetings 3.3.1-next.9 → 3.4.0
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/constants.js +11 -4
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/selfUtils.js +0 -5
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +70 -15
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/index.js +12 -0
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/connectionStateHandler.js +67 -0
- package/dist/meeting/connectionStateHandler.js.map +1 -0
- package/dist/meeting/index.js +552 -357
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +7 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +6 -1
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/index.js +4 -4
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -2
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/util.js +17 -17
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +16 -16
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +1 -1
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +37 -33
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +8 -0
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/util.js +3 -2
- package/dist/meetings/util.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/personal-meeting-room/index.js +1 -1
- package/dist/personal-meeting-room/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/types/constants.d.ts +11 -3
- package/dist/types/media/MediaConnectionAwaiter.d.ts +24 -4
- package/dist/types/meeting/connectionStateHandler.d.ts +30 -0
- package/dist/types/meeting/index.d.ts +27 -7
- package/dist/types/meeting/locusMediaRequest.d.ts +2 -0
- package/dist/types/meeting-info/index.d.ts +3 -2
- package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -2
- package/dist/types/meeting-info/util.d.ts +5 -4
- package/dist/types/meeting-info/utilv2.d.ts +3 -2
- package/dist/types/meetings/collection.d.ts +3 -2
- package/dist/types/meetings/index.d.ts +4 -3
- package/dist/types/meetings/meetings.types.d.ts +9 -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/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/breakouts/index.ts +7 -1
- package/src/constants.ts +13 -17
- package/src/locus-info/selfUtils.ts +0 -5
- package/src/media/MediaConnectionAwaiter.ts +89 -14
- package/src/media/index.ts +13 -0
- package/src/meeting/connectionStateHandler.ts +65 -0
- package/src/meeting/index.ts +526 -292
- package/src/meeting/locusMediaRequest.ts +5 -0
- package/src/meeting/muteState.ts +6 -1
- package/src/meeting/util.ts +1 -0
- package/src/meeting-info/index.ts +9 -6
- package/src/meeting-info/meeting-info-v2.ts +4 -4
- package/src/meeting-info/util.ts +23 -28
- package/src/meeting-info/utilv2.ts +18 -24
- package/src/meetings/collection.ts +3 -3
- package/src/meetings/index.ts +39 -40
- package/src/meetings/meetings.types.ts +11 -0
- package/src/meetings/util.ts +5 -4
- package/src/metrics/constants.ts +1 -0
- package/src/metrics/index.ts +44 -0
- package/src/personal-meeting-room/index.ts +2 -2
- package/src/reachability/clusterReachability.ts +86 -25
- package/src/reachability/index.ts +316 -27
- package/test/unit/spec/breakouts/index.ts +51 -32
- package/test/unit/spec/locus-info/selfUtils.js +25 -23
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +131 -32
- package/test/unit/spec/media/index.ts +42 -27
- package/test/unit/spec/meeting/connectionStateHandler.ts +102 -0
- package/test/unit/spec/meeting/index.js +758 -179
- package/test/unit/spec/meeting/locusMediaRequest.ts +7 -0
- package/test/unit/spec/meeting/muteState.js +24 -0
- package/test/unit/spec/meeting-info/index.js +4 -4
- package/test/unit/spec/meeting-info/meetinginfov2.js +24 -28
- package/test/unit/spec/meeting-info/request.js +2 -2
- package/test/unit/spec/meeting-info/utilv2.js +41 -49
- package/test/unit/spec/meetings/index.js +14 -0
- package/test/unit/spec/metrics/index.js +126 -0
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -2
- package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -2
- package/test/unit/spec/reachability/clusterReachability.ts +116 -22
- package/test/unit/spec/reachability/index.ts +1153 -84
- package/test/unit/spec/rtcMetrics/index.ts +1 -0
- package/dist/mediaQualityMetrics/config.js +0 -327
- package/dist/mediaQualityMetrics/config.js.map +0 -1
- package/dist/statsAnalyzer/global.js +0 -44
- package/dist/statsAnalyzer/global.js.map +0 -1
- package/dist/statsAnalyzer/index.js +0 -1103
- package/dist/statsAnalyzer/index.js.map +0 -1
- package/dist/statsAnalyzer/mqaUtil.js +0 -385
- package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +0 -253
- package/dist/types/statsAnalyzer/global.d.ts +0 -36
- package/dist/types/statsAnalyzer/index.d.ts +0 -217
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -57
- package/src/mediaQualityMetrics/config.ts +0 -261
- package/src/statsAnalyzer/global.ts +0 -37
- package/src/statsAnalyzer/index.ts +0 -1357
- package/src/statsAnalyzer/mqaUtil.ts +0 -491
- package/test/unit/spec/stats-analyzer/index.js +0 -1951
|
@@ -1,1951 +0,0 @@
|
|
|
1
|
-
import 'jsdom-global/register';
|
|
2
|
-
import chai from 'chai';
|
|
3
|
-
import chaiAsPromised from 'chai-as-promised';
|
|
4
|
-
import sinon from 'sinon';
|
|
5
|
-
import {ConnectionState} from '@webex/internal-media-core';
|
|
6
|
-
|
|
7
|
-
import {StatsAnalyzer, EVENTS} from '../../../../src/statsAnalyzer';
|
|
8
|
-
import NetworkQualityMonitor from '../../../../src/networkQualityMonitor';
|
|
9
|
-
import testUtils from '../../../utils/testUtils';
|
|
10
|
-
import {MEDIA_DEVICES, MQA_INTERVAL, _UNKNOWN_} from '@webex/plugin-meetings/src/constants';
|
|
11
|
-
import LoggerProxy from '../../../../src/common/logs/logger-proxy';
|
|
12
|
-
import LoggerConfig from '../../../../src/common/logs/logger-config';
|
|
13
|
-
|
|
14
|
-
const {assert} = chai;
|
|
15
|
-
|
|
16
|
-
chai.use(chaiAsPromised);
|
|
17
|
-
sinon.assert.expose(chai.assert, {prefix: ''});
|
|
18
|
-
|
|
19
|
-
describe('plugin-meetings', () => {
|
|
20
|
-
describe('StatsAnalyzer', () => {
|
|
21
|
-
describe('parseStatsResult', () => {
|
|
22
|
-
const sandbox = sinon.createSandbox();
|
|
23
|
-
let statsAnalyzer;
|
|
24
|
-
|
|
25
|
-
const initialConfig = {};
|
|
26
|
-
const defaultStats = {};
|
|
27
|
-
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
30
|
-
|
|
31
|
-
statsAnalyzer = new StatsAnalyzer(
|
|
32
|
-
initialConfig,
|
|
33
|
-
() => ({}),
|
|
34
|
-
networkQualityMonitor,
|
|
35
|
-
defaultStats
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
sandbox.reset();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should call processOutboundRTPResult', () => {
|
|
44
|
-
const calledSpy = sandbox.spy(statsAnalyzer, 'processOutboundRTPResult');
|
|
45
|
-
statsAnalyzer.parseGetStatsResult({type: 'outbound-rtp'}, 'video-send');
|
|
46
|
-
assert(calledSpy.calledOnce);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should call processInboundRTPResult', () => {
|
|
50
|
-
const calledSpy = sandbox.spy(statsAnalyzer, 'processInboundRTPResult');
|
|
51
|
-
statsAnalyzer.parseGetStatsResult({type: 'inbound-rtp'}, 'video-recv');
|
|
52
|
-
assert(calledSpy.calledOnce);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should call compareSentAndReceived', () => {
|
|
56
|
-
const calledSpy = sandbox.spy(statsAnalyzer, 'compareSentAndReceived');
|
|
57
|
-
statsAnalyzer.parseGetStatsResult({type: 'remote-outbound-rtp'}, 'video-send');
|
|
58
|
-
assert(calledSpy.calledOnce);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should call parseCandidate', () => {
|
|
62
|
-
const calledSpy = sandbox.spy(statsAnalyzer, 'parseCandidate');
|
|
63
|
-
statsAnalyzer.parseGetStatsResult({type: 'local-candidate'}, 'video-send');
|
|
64
|
-
assert(calledSpy.calledOnce);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('processOutboundRTPResult should create the correct stats results for audio', () => {
|
|
68
|
-
// establish the `statsResults` object.
|
|
69
|
-
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-send', true);
|
|
70
|
-
|
|
71
|
-
statsAnalyzer.processOutboundRTPResult(
|
|
72
|
-
{
|
|
73
|
-
bytesSent: 50000,
|
|
74
|
-
codecId: 'RTCCodec_1_Outbound_111',
|
|
75
|
-
headerBytesSent: 25000,
|
|
76
|
-
id: 'RTCOutboundRTPAudioStream_123456789',
|
|
77
|
-
kind: 'audio',
|
|
78
|
-
mediaSourceId: 'RTCAudioSource_2',
|
|
79
|
-
mediaType: 'audio',
|
|
80
|
-
nackCount: 1,
|
|
81
|
-
packetsSent: 3600,
|
|
82
|
-
remoteId: 'RTCRemoteInboundRtpAudioStream_123456789',
|
|
83
|
-
ssrc: 123456789,
|
|
84
|
-
targetBitrate: 256000,
|
|
85
|
-
timestamp: 1707341489336,
|
|
86
|
-
trackId: 'RTCMediaStreamTrack_sender_2',
|
|
87
|
-
transportId: 'RTCTransport_0_1',
|
|
88
|
-
type: 'outbound-rtp',
|
|
89
|
-
requestedBitrate: 10000,
|
|
90
|
-
},
|
|
91
|
-
'audio-send',
|
|
92
|
-
true
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.headerBytesSent, 25000);
|
|
96
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalBytesSent, 50000);
|
|
97
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalNackCount, 1);
|
|
98
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalPacketsSent, 3600);
|
|
99
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.requestedBitrate, 10000);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('processOutboundRTPResult should create the correct stats results for video', () => {
|
|
103
|
-
// establish the `statsResults` object for video.
|
|
104
|
-
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'video-send', true);
|
|
105
|
-
|
|
106
|
-
statsAnalyzer.processOutboundRTPResult(
|
|
107
|
-
{
|
|
108
|
-
bytesSent: 250000,
|
|
109
|
-
codecId: 'RTCCodec_1_Outbound_107',
|
|
110
|
-
headerBytesSent: 50000,
|
|
111
|
-
id: 'RTCOutboundRTPVideoStream_987654321',
|
|
112
|
-
kind: 'video',
|
|
113
|
-
mediaSourceId: 'RTCVideoSource_3',
|
|
114
|
-
mediaType: 'video',
|
|
115
|
-
nackCount: 5,
|
|
116
|
-
packetsSent: 15000,
|
|
117
|
-
remoteId: 'RTCRemoteInboundRtpVideoStream_987654321',
|
|
118
|
-
retransmittedBytesSent: 500,
|
|
119
|
-
retransmittedPacketsSent: 10,
|
|
120
|
-
ssrc: 987654321,
|
|
121
|
-
targetBitrate: 1024000,
|
|
122
|
-
timestamp: 1707341489336,
|
|
123
|
-
trackId: 'RTCMediaStreamTrack_sender_3',
|
|
124
|
-
transportId: 'RTCTransport_0_2',
|
|
125
|
-
type: 'outbound-rtp',
|
|
126
|
-
requestedBitrate: 50000,
|
|
127
|
-
},
|
|
128
|
-
'video-send',
|
|
129
|
-
true
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.headerBytesSent, 50000);
|
|
133
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.totalBytesSent, 250000);
|
|
134
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.totalNackCount, 5);
|
|
135
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.totalPacketsSent, 15000);
|
|
136
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-send'].send.requestedBitrate, 50000);
|
|
137
|
-
assert.strictEqual(
|
|
138
|
-
statsAnalyzer.statsResults['video-send'].send.totalRtxPacketsSent,
|
|
139
|
-
10
|
|
140
|
-
);
|
|
141
|
-
assert.strictEqual(
|
|
142
|
-
statsAnalyzer.statsResults['video-send'].send.totalRtxBytesSent,
|
|
143
|
-
500
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('processInboundRTPResult should create the correct stats results for audio', () => {
|
|
148
|
-
// establish the `statsResults` object.
|
|
149
|
-
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-recv-1', false);
|
|
150
|
-
|
|
151
|
-
statsAnalyzer.processInboundRTPResult(
|
|
152
|
-
{
|
|
153
|
-
audioLevel: 0,
|
|
154
|
-
bytesReceived: 509,
|
|
155
|
-
codecId: 'RTCCodec_6_Inbound_111',
|
|
156
|
-
concealedSamples: 200000,
|
|
157
|
-
concealmentEvents: 13,
|
|
158
|
-
fecPacketsDiscarded: 1,
|
|
159
|
-
fecPacketsReceived: 1,
|
|
160
|
-
headerBytesReceived: 250,
|
|
161
|
-
id: 'RTCInboundRTPAudioStream_123456789',
|
|
162
|
-
insertedSamplesForDeceleration: 0,
|
|
163
|
-
jitter: 0.012,
|
|
164
|
-
jitterBufferDelay: 1000,
|
|
165
|
-
jitterBufferEmittedCount: 10000,
|
|
166
|
-
kind: 'audio',
|
|
167
|
-
lastPacketReceivedTimestamp: 1707341488529,
|
|
168
|
-
mediaType: 'audio',
|
|
169
|
-
packetsDiscarded: 0,
|
|
170
|
-
packetsLost: 0,
|
|
171
|
-
packetsReceived: 12,
|
|
172
|
-
remoteId: 'RTCRemoteOutboundRTPAudioStream_123456789',
|
|
173
|
-
removedSamplesForAcceleration: 0,
|
|
174
|
-
silentConcealedSamples: 200000,
|
|
175
|
-
ssrc: 123456789,
|
|
176
|
-
timestamp: 1707341489419,
|
|
177
|
-
totalAudioEnergy: 133,
|
|
178
|
-
totalSamplesDuration: 7,
|
|
179
|
-
totalSamplesReceived: 300000,
|
|
180
|
-
trackId: 'RTCMediaStreamTrack_receiver_76',
|
|
181
|
-
transportId: 'RTCTransport_0_1',
|
|
182
|
-
type: 'inbound-rtp',
|
|
183
|
-
requestedBitrate: 10000,
|
|
184
|
-
},
|
|
185
|
-
'audio-recv-1',
|
|
186
|
-
false
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
assert.strictEqual(
|
|
190
|
-
statsAnalyzer.statsResults['audio-recv-1'].recv.totalPacketsReceived,
|
|
191
|
-
12
|
|
192
|
-
);
|
|
193
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsDiscarded, 1);
|
|
194
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsReceived, 1);
|
|
195
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalBytesReceived, 509);
|
|
196
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.requestedBitrate, 10000);
|
|
197
|
-
assert.strictEqual(
|
|
198
|
-
statsAnalyzer.statsResults['audio-recv-1'].recv.headerBytesReceived,
|
|
199
|
-
250
|
|
200
|
-
);
|
|
201
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.audioLevel, 0);
|
|
202
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalAudioEnergy, 133);
|
|
203
|
-
assert.strictEqual(
|
|
204
|
-
statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesReceived,
|
|
205
|
-
300000
|
|
206
|
-
);
|
|
207
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesDecoded, 0);
|
|
208
|
-
assert.strictEqual(
|
|
209
|
-
statsAnalyzer.statsResults['audio-recv-1'].recv.concealedSamples,
|
|
210
|
-
200000
|
|
211
|
-
);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('processInboundRTPResult should create the correct stats results for video', () => {
|
|
215
|
-
// establish the `statsResults` object for video.
|
|
216
|
-
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'video-recv', false);
|
|
217
|
-
|
|
218
|
-
statsAnalyzer.processInboundRTPResult(
|
|
219
|
-
{
|
|
220
|
-
bytesReceived: 100000,
|
|
221
|
-
codecId: 'RTCCodec_6_Inbound_107',
|
|
222
|
-
fecPacketsDiscarded: 2,
|
|
223
|
-
fecPacketsReceived: 2,
|
|
224
|
-
headerBytesReceived: 10000,
|
|
225
|
-
id: 'RTCInboundRTPVideoStream_987654321',
|
|
226
|
-
jitter: 0.05,
|
|
227
|
-
jitterBufferDelay: 5000,
|
|
228
|
-
jitterBufferEmittedCount: 50000,
|
|
229
|
-
kind: 'video',
|
|
230
|
-
lastPacketReceivedTimestamp: 1707341488529,
|
|
231
|
-
mediaType: 'video',
|
|
232
|
-
packetsDiscarded: 5,
|
|
233
|
-
packetsLost: 10,
|
|
234
|
-
packetsReceived: 1500,
|
|
235
|
-
remoteId: 'RTCRemoteOutboundRTPVideoStream_987654321',
|
|
236
|
-
ssrc: 987654321,
|
|
237
|
-
timestamp: 1707341489419,
|
|
238
|
-
trackId: 'RTCMediaStreamTrack_receiver_3',
|
|
239
|
-
transportId: 'RTCTransport_0_2',
|
|
240
|
-
type: 'inbound-rtp',
|
|
241
|
-
requestedBitrate: 50000,
|
|
242
|
-
retransmittedBytesReceived: 500,
|
|
243
|
-
retransmittedPacketsReceived: 10,
|
|
244
|
-
},
|
|
245
|
-
'video-recv',
|
|
246
|
-
false
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalPacketsReceived, 1500);
|
|
250
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.fecPacketsDiscarded, 2);
|
|
251
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.fecPacketsReceived, 2);
|
|
252
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalBytesReceived, 100000);
|
|
253
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.requestedBitrate, 50000);
|
|
254
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.headerBytesReceived, 10000);
|
|
255
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalRtxBytesReceived, 500);
|
|
256
|
-
assert.strictEqual(statsAnalyzer.statsResults['video-recv'].recv.totalRtxPacketsReceived, 10);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
it('parseAudioSource should create the correct stats results', () => {
|
|
261
|
-
// establish the `statsResults` object.
|
|
262
|
-
statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-send', true);
|
|
263
|
-
|
|
264
|
-
statsAnalyzer.parseAudioSource(
|
|
265
|
-
{
|
|
266
|
-
audioLevel: 0.03,
|
|
267
|
-
echoReturnLoss: -30,
|
|
268
|
-
echoReturnLossEnhancement: 0.17,
|
|
269
|
-
id: 'RTCAudioSource_2',
|
|
270
|
-
kind: 'audio',
|
|
271
|
-
timestamp: 1707341488160.012,
|
|
272
|
-
totalAudioEnergy: 0.001,
|
|
273
|
-
totalSamplesDuration: 4.5,
|
|
274
|
-
trackIdentifier: '2207e5bf-c595-4301-93f7-283994d8143f',
|
|
275
|
-
type: 'media-source',
|
|
276
|
-
},
|
|
277
|
-
'audio-send',
|
|
278
|
-
true
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.audioLevel, 0.03);
|
|
282
|
-
assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalAudioEnergy, 0.001);
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
describe('compareSentAndReceived()', () => {
|
|
287
|
-
let statsAnalyzer;
|
|
288
|
-
let sandBoxSpy;
|
|
289
|
-
|
|
290
|
-
const initialConfig = {
|
|
291
|
-
videoPacketLossRatioThreshold: 9,
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const defaultStats = {
|
|
295
|
-
resolutions: {},
|
|
296
|
-
internal: {
|
|
297
|
-
'video-send-1': {
|
|
298
|
-
send: {
|
|
299
|
-
totalPacketsLostOnReceiver: 10,
|
|
300
|
-
},
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
'video-send-1': {
|
|
304
|
-
send: {
|
|
305
|
-
packetsSent: 2,
|
|
306
|
-
meanRemoteJitter: [],
|
|
307
|
-
meanRoundTripTime: [],
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const statusResult = {
|
|
313
|
-
type: 'remote-inbound-rtp',
|
|
314
|
-
packetsLost: 11,
|
|
315
|
-
rttThreshold: 501,
|
|
316
|
-
jitterThreshold: 501,
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
const sandbox = sinon.createSandbox();
|
|
320
|
-
|
|
321
|
-
beforeEach(() => {
|
|
322
|
-
const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
323
|
-
|
|
324
|
-
statsAnalyzer = new StatsAnalyzer(
|
|
325
|
-
initialConfig,
|
|
326
|
-
() => ({}),
|
|
327
|
-
networkQualityMonitor,
|
|
328
|
-
defaultStats
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
sandBoxSpy = sandbox.spy(
|
|
332
|
-
statsAnalyzer.networkQualityMonitor,
|
|
333
|
-
'determineUplinkNetworkQuality'
|
|
334
|
-
);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
afterEach(() => {
|
|
338
|
-
sandbox.restore();
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
it('should trigger determineUplinkNetworkQuality with specific arguments', async () => {
|
|
342
|
-
await statsAnalyzer.parseGetStatsResult(statusResult, 'video-send-1', true);
|
|
343
|
-
|
|
344
|
-
assert.calledOnce(statsAnalyzer.networkQualityMonitor.determineUplinkNetworkQuality);
|
|
345
|
-
assert(
|
|
346
|
-
sandBoxSpy.calledWith({
|
|
347
|
-
mediaType: 'video-send-1',
|
|
348
|
-
remoteRtpResults: statusResult,
|
|
349
|
-
statsAnalyzerCurrentStats: statsAnalyzer.statsResults,
|
|
350
|
-
})
|
|
351
|
-
);
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
describe('startAnalyzer', () => {
|
|
356
|
-
let clock;
|
|
357
|
-
let pc;
|
|
358
|
-
let networkQualityMonitor;
|
|
359
|
-
let statsAnalyzer;
|
|
360
|
-
let mqeData;
|
|
361
|
-
let loggerSpy;
|
|
362
|
-
let receiveSlot;
|
|
363
|
-
|
|
364
|
-
let receivedEventsData = {
|
|
365
|
-
local: {},
|
|
366
|
-
remote: {},
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
const initialConfig = {
|
|
370
|
-
analyzerInterval: 1000,
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
let fakeStats;
|
|
374
|
-
|
|
375
|
-
const sandbox = sinon.createSandbox();
|
|
376
|
-
|
|
377
|
-
const resetReceivedEvents = () => {
|
|
378
|
-
receivedEventsData = {
|
|
379
|
-
local: {},
|
|
380
|
-
remote: {},
|
|
381
|
-
};
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
before(() => {
|
|
385
|
-
LoggerConfig.set({enable: false});
|
|
386
|
-
LoggerProxy.set();
|
|
387
|
-
loggerSpy = sandbox.spy(LoggerProxy.logger, 'info');
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
beforeEach(() => {
|
|
391
|
-
clock = sinon.useFakeTimers();
|
|
392
|
-
receiveSlot = undefined;
|
|
393
|
-
|
|
394
|
-
resetReceivedEvents();
|
|
395
|
-
|
|
396
|
-
// bytesReceived and bytesSent need to be non-zero in order for StatsAnalyzer to parse any other values
|
|
397
|
-
fakeStats = {
|
|
398
|
-
audio: {
|
|
399
|
-
senders: [
|
|
400
|
-
{
|
|
401
|
-
localTrackLabel: 'fake-microphone',
|
|
402
|
-
report: [
|
|
403
|
-
{
|
|
404
|
-
type: 'outbound-rtp',
|
|
405
|
-
bytesSent: 1,
|
|
406
|
-
packetsSent: 0,
|
|
407
|
-
isRequested: true,
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
type: 'remote-inbound-rtp',
|
|
411
|
-
packetsLost: 0,
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
type: 'candidate-pair',
|
|
415
|
-
state: 'succeeded',
|
|
416
|
-
localCandidateId: 'fake-candidate-id',
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
type: 'candidate-pair',
|
|
420
|
-
state: 'failed',
|
|
421
|
-
localCandidateId: 'bad-candidate-id',
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
type: 'local-candidate',
|
|
425
|
-
id: 'fake-candidate-id',
|
|
426
|
-
protocol: 'tcp',
|
|
427
|
-
},
|
|
428
|
-
],
|
|
429
|
-
},
|
|
430
|
-
],
|
|
431
|
-
receivers: [
|
|
432
|
-
{
|
|
433
|
-
report: [
|
|
434
|
-
{
|
|
435
|
-
type: 'inbound-rtp',
|
|
436
|
-
bytesReceived: 1,
|
|
437
|
-
fecPacketsDiscarded: 0,
|
|
438
|
-
fecPacketsReceived: 0,
|
|
439
|
-
packetsLost: 0,
|
|
440
|
-
packetsReceived: 0,
|
|
441
|
-
isRequested: true,
|
|
442
|
-
lastRequestedUpdateTimestamp: 0,
|
|
443
|
-
},
|
|
444
|
-
{
|
|
445
|
-
type: 'remote-outbound-rtp',
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
type: 'candidate-pair',
|
|
449
|
-
state: 'succeeded',
|
|
450
|
-
localCandidateId: 'fake-candidate-id',
|
|
451
|
-
},
|
|
452
|
-
{
|
|
453
|
-
type: 'candidate-pair',
|
|
454
|
-
state: 'failed',
|
|
455
|
-
localCandidateId: 'bad-candidate-id',
|
|
456
|
-
},
|
|
457
|
-
{
|
|
458
|
-
type: 'local-candidate',
|
|
459
|
-
id: 'fake-candidate-id',
|
|
460
|
-
protocol: 'tcp',
|
|
461
|
-
},
|
|
462
|
-
],
|
|
463
|
-
},
|
|
464
|
-
],
|
|
465
|
-
},
|
|
466
|
-
video: {
|
|
467
|
-
senders: [
|
|
468
|
-
{
|
|
469
|
-
localTrackLabel: 'fake-camera',
|
|
470
|
-
report: [
|
|
471
|
-
{
|
|
472
|
-
type: 'outbound-rtp',
|
|
473
|
-
bytesSent: 1,
|
|
474
|
-
framesSent: 0,
|
|
475
|
-
packetsSent: 0,
|
|
476
|
-
isRequested: true,
|
|
477
|
-
lastRequestedUpdateTimestamp: 0,
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
type: 'remote-inbound-rtp',
|
|
481
|
-
packetsLost: 0,
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
type: 'candidate-pair',
|
|
485
|
-
state: 'succeeded',
|
|
486
|
-
localCandidateId: 'fake-candidate-id',
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
type: 'candidate-pair',
|
|
490
|
-
state: 'failed',
|
|
491
|
-
localCandidateId: 'bad-candidate-id',
|
|
492
|
-
},
|
|
493
|
-
{
|
|
494
|
-
type: 'local-candidate',
|
|
495
|
-
id: 'fake-candidate-id',
|
|
496
|
-
protocol: 'tcp',
|
|
497
|
-
},
|
|
498
|
-
],
|
|
499
|
-
},
|
|
500
|
-
],
|
|
501
|
-
receivers: [
|
|
502
|
-
{
|
|
503
|
-
report: [
|
|
504
|
-
{
|
|
505
|
-
type: 'inbound-rtp',
|
|
506
|
-
bytesReceived: 1,
|
|
507
|
-
frameHeight: 720,
|
|
508
|
-
frameWidth: 1280,
|
|
509
|
-
framesDecoded: 0,
|
|
510
|
-
framesReceived: 0,
|
|
511
|
-
packetsLost: 0,
|
|
512
|
-
packetsReceived: 0,
|
|
513
|
-
isRequested: true,
|
|
514
|
-
lastRequestedUpdateTimestamp: 0,
|
|
515
|
-
isActiveSpeaker: false,
|
|
516
|
-
lastActiveSpeakerUpdateTimestamp: 0,
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
type: 'remote-outbound-rtp',
|
|
520
|
-
},
|
|
521
|
-
{
|
|
522
|
-
type: 'candidate-pair',
|
|
523
|
-
state: 'succeeded',
|
|
524
|
-
localCandidateId: 'fake-candidate-id',
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
type: 'candidate-pair',
|
|
528
|
-
state: 'failed',
|
|
529
|
-
localCandidateId: 'bad-candidate-id',
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
type: 'local-candidate',
|
|
533
|
-
id: 'fake-candidate-id',
|
|
534
|
-
protocol: 'tcp',
|
|
535
|
-
},
|
|
536
|
-
],
|
|
537
|
-
},
|
|
538
|
-
],
|
|
539
|
-
},
|
|
540
|
-
share: {
|
|
541
|
-
senders: [
|
|
542
|
-
{
|
|
543
|
-
localTrackLabel: 'fake-share',
|
|
544
|
-
report: [
|
|
545
|
-
{
|
|
546
|
-
type: 'outbound-rtp',
|
|
547
|
-
bytesSent: 1,
|
|
548
|
-
framesSent: 0,
|
|
549
|
-
packetsSent: 0,
|
|
550
|
-
isRequested: true,
|
|
551
|
-
lastRequestedUpdateTimestamp: 0,
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
type: 'remote-inbound-rtp',
|
|
555
|
-
packetsLost: 0,
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
type: 'candidate-pair',
|
|
559
|
-
state: 'succeeded',
|
|
560
|
-
localCandidateId: 'fake-candidate-id',
|
|
561
|
-
},
|
|
562
|
-
{
|
|
563
|
-
type: 'candidate-pair',
|
|
564
|
-
state: 'failed',
|
|
565
|
-
localCandidateId: 'bad-candidate-id',
|
|
566
|
-
},
|
|
567
|
-
{
|
|
568
|
-
type: 'local-candidate',
|
|
569
|
-
id: 'fake-candidate-id',
|
|
570
|
-
protocol: 'tcp',
|
|
571
|
-
},
|
|
572
|
-
],
|
|
573
|
-
},
|
|
574
|
-
],
|
|
575
|
-
receivers: [
|
|
576
|
-
{
|
|
577
|
-
report: [
|
|
578
|
-
{
|
|
579
|
-
type: 'inbound-rtp',
|
|
580
|
-
bytesReceived: 1,
|
|
581
|
-
frameHeight: 720,
|
|
582
|
-
frameWidth: 1280,
|
|
583
|
-
framesDecoded: 0,
|
|
584
|
-
framesReceived: 0,
|
|
585
|
-
packetsLost: 0,
|
|
586
|
-
packetsReceived: 0,
|
|
587
|
-
isRequested: true,
|
|
588
|
-
lastRequestedUpdateTimestamp: 0,
|
|
589
|
-
},
|
|
590
|
-
{
|
|
591
|
-
type: 'remote-outbound-rtp',
|
|
592
|
-
},
|
|
593
|
-
{
|
|
594
|
-
type: 'candidate-pair',
|
|
595
|
-
state: 'succeeded',
|
|
596
|
-
localCandidateId: 'fake-candidate-id',
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
type: 'candidate-pair',
|
|
600
|
-
state: 'failed',
|
|
601
|
-
localCandidateId: 'bad-candidate-id',
|
|
602
|
-
},
|
|
603
|
-
{
|
|
604
|
-
type: 'local-candidate',
|
|
605
|
-
id: 'fake-candidate-id',
|
|
606
|
-
protocol: 'tcp',
|
|
607
|
-
},
|
|
608
|
-
],
|
|
609
|
-
},
|
|
610
|
-
],
|
|
611
|
-
},
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
pc = {
|
|
615
|
-
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
|
616
|
-
getTransceiverStats: sinon.stub().resolves({
|
|
617
|
-
audio: {
|
|
618
|
-
senders: [fakeStats.audio.senders[0]],
|
|
619
|
-
receivers: [fakeStats.audio.receivers[0]],
|
|
620
|
-
},
|
|
621
|
-
video: {
|
|
622
|
-
senders: [fakeStats.video.senders[0]],
|
|
623
|
-
receivers: [fakeStats.video.receivers[0]],
|
|
624
|
-
},
|
|
625
|
-
screenShareAudio: {
|
|
626
|
-
senders: [fakeStats.audio.senders[0]],
|
|
627
|
-
receivers: [fakeStats.audio.receivers[0]],
|
|
628
|
-
},
|
|
629
|
-
screenShareVideo: {
|
|
630
|
-
senders: [fakeStats.share.senders[0]],
|
|
631
|
-
receivers: [fakeStats.share.receivers[0]],
|
|
632
|
-
},
|
|
633
|
-
}),
|
|
634
|
-
};
|
|
635
|
-
|
|
636
|
-
networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
|
|
637
|
-
|
|
638
|
-
statsAnalyzer = new StatsAnalyzer(initialConfig, () => receiveSlot, networkQualityMonitor);
|
|
639
|
-
|
|
640
|
-
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
|
|
641
|
-
receivedEventsData.local.started = data;
|
|
642
|
-
});
|
|
643
|
-
statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STOPPED, (data) => {
|
|
644
|
-
receivedEventsData.local.stopped = data;
|
|
645
|
-
});
|
|
646
|
-
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STARTED, (data) => {
|
|
647
|
-
receivedEventsData.remote.started = data;
|
|
648
|
-
});
|
|
649
|
-
statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STOPPED, (data) => {
|
|
650
|
-
receivedEventsData.remote.stopped = data;
|
|
651
|
-
});
|
|
652
|
-
statsAnalyzer.on(EVENTS.MEDIA_QUALITY, ({data}) => {
|
|
653
|
-
mqeData = data;
|
|
654
|
-
});
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
afterEach(() => {
|
|
658
|
-
sandbox.reset();
|
|
659
|
-
clock.restore();
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
const startStatsAnalyzer = async (mediaStatus, lastEmittedEvents) => {
|
|
663
|
-
statsAnalyzer.updateMediaStatus(mediaStatus);
|
|
664
|
-
statsAnalyzer.startAnalyzer(pc);
|
|
665
|
-
statsAnalyzer.lastEmittedStartStopEvent = lastEmittedEvents || {};
|
|
666
|
-
|
|
667
|
-
await testUtils.flushPromises();
|
|
668
|
-
};
|
|
669
|
-
|
|
670
|
-
const mergeProperties = (
|
|
671
|
-
target,
|
|
672
|
-
properties,
|
|
673
|
-
keyValue = 'fake-candidate-id',
|
|
674
|
-
matchKey = 'type',
|
|
675
|
-
matchValue = 'local-candidate'
|
|
676
|
-
) => {
|
|
677
|
-
for (let key in target) {
|
|
678
|
-
if (target.hasOwnProperty(key)) {
|
|
679
|
-
if (typeof target[key] === 'object') {
|
|
680
|
-
mergeProperties(target[key], properties, keyValue, matchKey, matchValue);
|
|
681
|
-
}
|
|
682
|
-
if (key === 'id' && target[key] === keyValue && target[matchKey] === matchValue) {
|
|
683
|
-
Object.assign(target, properties);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
const progressTime = async (time = initialConfig.analyzerInterval) => {
|
|
690
|
-
await clock.tickAsync(time);
|
|
691
|
-
await testUtils.flushPromises();
|
|
692
|
-
};
|
|
693
|
-
|
|
694
|
-
const checkReceivedEvent = ({expected}) => {
|
|
695
|
-
// check that we got the REMOTE_MEDIA_STARTED event for audio
|
|
696
|
-
assert.deepEqual(receivedEventsData.local.started, expected.local?.started);
|
|
697
|
-
assert.deepEqual(receivedEventsData.local.stopped, expected.local?.stopped);
|
|
698
|
-
assert.deepEqual(receivedEventsData.remote.started, expected.remote?.started);
|
|
699
|
-
assert.deepEqual(receivedEventsData.remote.stopped, expected.remote?.stopped);
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
const checkMqeData = () => {
|
|
703
|
-
for (const data of [
|
|
704
|
-
mqeData.audioTransmit,
|
|
705
|
-
mqeData.audioReceive,
|
|
706
|
-
mqeData.videoTransmit,
|
|
707
|
-
mqeData.videoReceive,
|
|
708
|
-
]) {
|
|
709
|
-
assert.strictEqual(data.length, 2);
|
|
710
|
-
assert.strictEqual(data[0].common.common.isMain, true);
|
|
711
|
-
assert.strictEqual(data[1].common.common.isMain, false);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].receivedFrameSize, 3600);
|
|
715
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].receivedHeight, 720);
|
|
716
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].receivedWidth, 1280);
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for audio', async () => {
|
|
720
|
-
await startStatsAnalyzer({expected: {sendAudio: true}});
|
|
721
|
-
|
|
722
|
-
// check that we haven't received any events yet
|
|
723
|
-
checkReceivedEvent({expected: {}});
|
|
724
|
-
|
|
725
|
-
// setup a mock to return some values higher the previous ones
|
|
726
|
-
fakeStats.audio.senders[0].report[0].packetsSent += 10;
|
|
727
|
-
|
|
728
|
-
await progressTime();
|
|
729
|
-
|
|
730
|
-
// check that we got the LOCAL_MEDIA_STARTED event for audio
|
|
731
|
-
checkReceivedEvent({expected: {local: {started: {type: 'audio'}}}});
|
|
732
|
-
|
|
733
|
-
// now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
|
|
734
|
-
resetReceivedEvents();
|
|
735
|
-
await progressTime();
|
|
736
|
-
checkReceivedEvent({expected: {local: {stopped: {type: 'audio'}}}});
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for video', async () => {
|
|
740
|
-
await startStatsAnalyzer({expected: {sendVideo: true}});
|
|
741
|
-
|
|
742
|
-
// check that we haven't received any events yet
|
|
743
|
-
checkReceivedEvent({expected: {}});
|
|
744
|
-
|
|
745
|
-
// setup a mock to return some values higher the previous ones
|
|
746
|
-
fakeStats.video.senders[0].report[0].framesSent += 1;
|
|
747
|
-
|
|
748
|
-
await progressTime();
|
|
749
|
-
|
|
750
|
-
// check that we got the LOCAL_MEDIA_STARTED event for audio
|
|
751
|
-
checkReceivedEvent({expected: {local: {started: {type: 'video'}}}});
|
|
752
|
-
|
|
753
|
-
// now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
|
|
754
|
-
resetReceivedEvents();
|
|
755
|
-
await progressTime();
|
|
756
|
-
checkReceivedEvent({expected: {local: {stopped: {type: 'video'}}}});
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for share', async () => {
|
|
760
|
-
await startStatsAnalyzer({expected: {sendShare: true}});
|
|
761
|
-
|
|
762
|
-
// check that we haven't received any events yet
|
|
763
|
-
checkReceivedEvent({expected: {}});
|
|
764
|
-
|
|
765
|
-
// setup a mock to return some values higher the previous ones
|
|
766
|
-
fakeStats.share.senders[0].report[0].framesSent += 1;
|
|
767
|
-
|
|
768
|
-
await progressTime();
|
|
769
|
-
|
|
770
|
-
// check that we got the LOCAL_MEDIA_STARTED event for audio
|
|
771
|
-
checkReceivedEvent({expected: {local: {started: {type: 'share'}}}});
|
|
772
|
-
|
|
773
|
-
// now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
|
|
774
|
-
resetReceivedEvents();
|
|
775
|
-
await progressTime();
|
|
776
|
-
checkReceivedEvent({expected: {local: {stopped: {type: 'share'}}}});
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for audio', async () => {
|
|
780
|
-
await startStatsAnalyzer({expected: {receiveAudio: true}});
|
|
781
|
-
|
|
782
|
-
// check that we haven't received any events yet
|
|
783
|
-
checkReceivedEvent({expected: {}});
|
|
784
|
-
|
|
785
|
-
// setup a mock to return some values higher the previous ones
|
|
786
|
-
fakeStats.audio.receivers[0].report[0].packetsReceived += 5;
|
|
787
|
-
|
|
788
|
-
await progressTime();
|
|
789
|
-
// check that we got the REMOTE_MEDIA_STARTED event for audio
|
|
790
|
-
checkReceivedEvent({expected: {remote: {started: {type: 'audio'}}}});
|
|
791
|
-
|
|
792
|
-
// now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
|
|
793
|
-
resetReceivedEvents();
|
|
794
|
-
await progressTime();
|
|
795
|
-
|
|
796
|
-
checkReceivedEvent({expected: {remote: {stopped: {type: 'audio'}}}});
|
|
797
|
-
});
|
|
798
|
-
|
|
799
|
-
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for video', async () => {
|
|
800
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
801
|
-
|
|
802
|
-
// check that we haven't received any events yet
|
|
803
|
-
checkReceivedEvent({expected: {}});
|
|
804
|
-
|
|
805
|
-
// setup a mock to return some values higher the previous ones
|
|
806
|
-
fakeStats.video.receivers[0].report[0].framesDecoded += 1;
|
|
807
|
-
|
|
808
|
-
await progressTime();
|
|
809
|
-
// check that we got the REMOTE_MEDIA_STARTED event for video
|
|
810
|
-
checkReceivedEvent({expected: {remote: {started: {type: 'video'}}}});
|
|
811
|
-
|
|
812
|
-
// now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
|
|
813
|
-
resetReceivedEvents();
|
|
814
|
-
await progressTime();
|
|
815
|
-
|
|
816
|
-
checkReceivedEvent({expected: {remote: {stopped: {type: 'video'}}}});
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for share', async () => {
|
|
820
|
-
await startStatsAnalyzer({expected: {receiveShare: true}});
|
|
821
|
-
|
|
822
|
-
// check that we haven't received any events yet
|
|
823
|
-
checkReceivedEvent({expected: {}});
|
|
824
|
-
|
|
825
|
-
// setup a mock to return some values higher the previous ones
|
|
826
|
-
fakeStats.share.receivers[0].report[0].framesDecoded += 1;
|
|
827
|
-
|
|
828
|
-
await progressTime();
|
|
829
|
-
// check that we got the REMOTE_MEDIA_STARTED event for video
|
|
830
|
-
checkReceivedEvent({expected: {remote: {started: {type: 'share'}}}});
|
|
831
|
-
|
|
832
|
-
// now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
|
|
833
|
-
resetReceivedEvents();
|
|
834
|
-
await progressTime();
|
|
835
|
-
|
|
836
|
-
checkReceivedEvent({expected: {remote: {stopped: {type: 'share'}}}});
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
it('emits the correct MEDIA_QUALITY events', async () => {
|
|
840
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
841
|
-
|
|
842
|
-
await progressTime();
|
|
843
|
-
|
|
844
|
-
// Check that the mqe data has been emitted and is correctly computed.
|
|
845
|
-
checkMqeData();
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
it('emits the correct transportType in MEDIA_QUALITY events', async () => {
|
|
849
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
850
|
-
|
|
851
|
-
await progressTime();
|
|
852
|
-
|
|
853
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.transportType, 'TCP');
|
|
854
|
-
assert.strictEqual(mqeData.videoReceive[0].common.transportType, 'TCP');
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
it('emits the correct transportType in MEDIA_QUALITY events when using a TURN server', async () => {
|
|
858
|
-
fakeStats.audio.senders[0].report[4].relayProtocol = 'tls';
|
|
859
|
-
fakeStats.video.senders[0].report[4].relayProtocol = 'tls';
|
|
860
|
-
fakeStats.audio.receivers[0].report[4].relayProtocol = 'tls';
|
|
861
|
-
fakeStats.video.receivers[0].report[4].relayProtocol = 'tls';
|
|
862
|
-
|
|
863
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
864
|
-
|
|
865
|
-
await progressTime();
|
|
866
|
-
|
|
867
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.transportType, 'TLS');
|
|
868
|
-
assert.strictEqual(mqeData.videoReceive[0].common.transportType, 'TLS');
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
it('emits the correct peripherals in MEDIA_QUALITY events', async () => {
|
|
872
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
873
|
-
|
|
874
|
-
await progressTime();
|
|
875
|
-
|
|
876
|
-
assert.strictEqual(
|
|
877
|
-
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
|
|
878
|
-
.information,
|
|
879
|
-
'fake-microphone'
|
|
880
|
-
);
|
|
881
|
-
assert.strictEqual(
|
|
882
|
-
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
|
|
883
|
-
.information,
|
|
884
|
-
'fake-camera'
|
|
885
|
-
);
|
|
886
|
-
});
|
|
887
|
-
|
|
888
|
-
it('emits the correct peripherals in MEDIA_QUALITY events when localTrackLabel is undefined', async () => {
|
|
889
|
-
fakeStats.audio.senders[0].localTrackLabel = undefined;
|
|
890
|
-
fakeStats.video.senders[0].localTrackLabel = undefined;
|
|
891
|
-
|
|
892
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
893
|
-
|
|
894
|
-
await progressTime();
|
|
895
|
-
|
|
896
|
-
assert.strictEqual(
|
|
897
|
-
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
|
|
898
|
-
.information,
|
|
899
|
-
_UNKNOWN_
|
|
900
|
-
);
|
|
901
|
-
assert.strictEqual(
|
|
902
|
-
mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
|
|
903
|
-
.information,
|
|
904
|
-
_UNKNOWN_
|
|
905
|
-
);
|
|
906
|
-
});
|
|
907
|
-
|
|
908
|
-
describe('frame rate reporting in stats analyzer', () => {
|
|
909
|
-
beforeEach(async () => {
|
|
910
|
-
await startStatsAnalyzer();
|
|
911
|
-
});
|
|
912
|
-
|
|
913
|
-
it('should report a zero frame rate for both transmitted and received video at the start', async () => {
|
|
914
|
-
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.transmittedFrameRate, 0);
|
|
915
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.receivedFrameRate, 0);
|
|
916
|
-
});
|
|
917
|
-
|
|
918
|
-
it('should accurately report the transmitted and received frame rate after video frames are processed', async () => {
|
|
919
|
-
fakeStats.video.senders[0].report[0].framesSent += 300;
|
|
920
|
-
fakeStats.video.receivers[0].report[0].framesReceived += 300;
|
|
921
|
-
await progressTime(MQA_INTERVAL);
|
|
922
|
-
|
|
923
|
-
// 300 frames in 60 seconds = 5 frames per second
|
|
924
|
-
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.transmittedFrameRate, 5);
|
|
925
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.receivedFrameRate, 5);
|
|
926
|
-
});
|
|
927
|
-
});
|
|
928
|
-
|
|
929
|
-
describe('RTP packets count in stats analyzer', () => {
|
|
930
|
-
beforeEach(async () => {
|
|
931
|
-
await startStatsAnalyzer();
|
|
932
|
-
});
|
|
933
|
-
|
|
934
|
-
it('should report zero RTP packets for all streams at the start of the stats analyzer', async () => {
|
|
935
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.rtpPackets, 0);
|
|
936
|
-
assert.strictEqual(mqeData.audioTransmit[0].streams[0].common.rtpPackets, 0);
|
|
937
|
-
assert.strictEqual(mqeData.audioReceive[0].common.rtpPackets, 0);
|
|
938
|
-
assert.strictEqual(mqeData.audioReceive[0].streams[0].common.rtpPackets, 0);
|
|
939
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.rtpPackets, 0);
|
|
940
|
-
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.rtpPackets, 0);
|
|
941
|
-
assert.strictEqual(mqeData.videoReceive[0].common.rtpPackets, 0);
|
|
942
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.rtpPackets, 0);
|
|
943
|
-
});
|
|
944
|
-
|
|
945
|
-
it('should update the RTP packets count correctly after audio and video packets are sent', async () => {
|
|
946
|
-
fakeStats.audio.senders[0].report[0].packetsSent += 5;
|
|
947
|
-
fakeStats.video.senders[0].report[0].packetsSent += 5;
|
|
948
|
-
await progressTime(MQA_INTERVAL);
|
|
949
|
-
|
|
950
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.rtpPackets, 5);
|
|
951
|
-
assert.strictEqual(mqeData.audioTransmit[0].streams[0].common.rtpPackets, 5);
|
|
952
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.rtpPackets, 5);
|
|
953
|
-
assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.rtpPackets, 5);
|
|
954
|
-
});
|
|
955
|
-
|
|
956
|
-
it('should update the RTP packets count correctly after audio and video packets are received', async () => {
|
|
957
|
-
fakeStats.audio.senders[0].report[0].packetsSent += 10;
|
|
958
|
-
fakeStats.video.senders[0].report[0].packetsSent += 10;
|
|
959
|
-
fakeStats.audio.receivers[0].report[0].packetsReceived += 10;
|
|
960
|
-
fakeStats.video.receivers[0].report[0].packetsReceived += 10;
|
|
961
|
-
await progressTime(MQA_INTERVAL);
|
|
962
|
-
|
|
963
|
-
assert.strictEqual(mqeData.audioReceive[0].common.rtpPackets, 10);
|
|
964
|
-
assert.strictEqual(mqeData.audioReceive[0].streams[0].common.rtpPackets, 10);
|
|
965
|
-
assert.strictEqual(mqeData.videoReceive[0].common.rtpPackets, 10);
|
|
966
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.rtpPackets, 10);
|
|
967
|
-
});
|
|
968
|
-
});
|
|
969
|
-
|
|
970
|
-
describe('FEC packet reporting in stats analyzer', () => {
|
|
971
|
-
beforeEach(async () => {
|
|
972
|
-
await startStatsAnalyzer();
|
|
973
|
-
})
|
|
974
|
-
|
|
975
|
-
it('should initially report zero FEC packets at the start of the stats analyzer', async () => {
|
|
976
|
-
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 0);
|
|
977
|
-
});
|
|
978
|
-
|
|
979
|
-
it('should accurately report the count of FEC packets received', async () => {
|
|
980
|
-
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 5;
|
|
981
|
-
await progressTime(MQA_INTERVAL);
|
|
982
|
-
|
|
983
|
-
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 5);
|
|
984
|
-
});
|
|
985
|
-
|
|
986
|
-
it('should correctly adjust the FEC packet count when packets are discarded', async () => {
|
|
987
|
-
fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 15;
|
|
988
|
-
fakeStats.audio.receivers[0].report[0].fecPacketsDiscarded += 5;
|
|
989
|
-
await progressTime(MQA_INTERVAL);
|
|
990
|
-
|
|
991
|
-
assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 10);
|
|
992
|
-
});
|
|
993
|
-
});
|
|
994
|
-
|
|
995
|
-
describe('packet loss metrics reporting in stats analyzer', () => {
|
|
996
|
-
beforeEach(async () => {
|
|
997
|
-
await startStatsAnalyzer();
|
|
998
|
-
})
|
|
999
|
-
|
|
1000
|
-
it('should report zero packet loss for both audio and video at the start of the stats analyzer', async () => {
|
|
1001
|
-
assert.strictEqual(mqeData.audioReceive[0].common.mediaHopByHopLost, 0);
|
|
1002
|
-
assert.strictEqual(mqeData.audioReceive[0].common.rtpHopByHopLost, 0);
|
|
1003
|
-
assert.strictEqual(mqeData.videoReceive[0].common.mediaHopByHopLost, 0);
|
|
1004
|
-
assert.strictEqual(mqeData.videoReceive[0].common.rtpHopByHopLost, 0);
|
|
1005
|
-
});
|
|
1006
|
-
|
|
1007
|
-
it('should update packet loss metrics correctly for both audio and video after packet loss is detected', async () => {
|
|
1008
|
-
fakeStats.audio.receivers[0].report[0].packetsLost += 5;
|
|
1009
|
-
fakeStats.video.receivers[0].report[0].packetsLost += 5;
|
|
1010
|
-
await progressTime(MQA_INTERVAL);
|
|
1011
|
-
|
|
1012
|
-
assert.strictEqual(mqeData.audioReceive[0].common.mediaHopByHopLost, 5);
|
|
1013
|
-
assert.strictEqual(mqeData.audioReceive[0].common.rtpHopByHopLost, 5);
|
|
1014
|
-
assert.strictEqual(mqeData.videoReceive[0].common.mediaHopByHopLost, 5);
|
|
1015
|
-
assert.strictEqual(mqeData.videoReceive[0].common.rtpHopByHopLost, 5);
|
|
1016
|
-
});
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
describe('remote loss rate reporting in stats analyzer', () => {
|
|
1020
|
-
beforeEach(async () => {
|
|
1021
|
-
await startStatsAnalyzer();
|
|
1022
|
-
});
|
|
1023
|
-
|
|
1024
|
-
it('should report a zero remote loss rate for both audio and video at the start', async () => {
|
|
1025
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 0);
|
|
1026
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 0);
|
|
1027
|
-
});
|
|
1028
|
-
|
|
1029
|
-
it('should maintain a zero remote loss rate for both audio and video after packets are sent without loss', async () => {
|
|
1030
|
-
fakeStats.audio.senders[0].report[0].packetsSent += 100;
|
|
1031
|
-
fakeStats.video.senders[0].report[0].packetsSent += 100;
|
|
1032
|
-
await progressTime(MQA_INTERVAL);
|
|
1033
|
-
|
|
1034
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 0);
|
|
1035
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 0);
|
|
1036
|
-
});
|
|
1037
|
-
|
|
1038
|
-
it('should accurately calculate the remote loss rate for both audio and video after packet loss is detected', async () => {
|
|
1039
|
-
fakeStats.audio.senders[0].report[0].packetsSent += 200;
|
|
1040
|
-
fakeStats.audio.senders[0].report[1].packetsLost += 10;
|
|
1041
|
-
fakeStats.video.senders[0].report[0].packetsSent += 200;
|
|
1042
|
-
fakeStats.video.senders[0].report[1].packetsLost += 10;
|
|
1043
|
-
await progressTime(MQA_INTERVAL);
|
|
1044
|
-
|
|
1045
|
-
assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 5);
|
|
1046
|
-
assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 5);
|
|
1047
|
-
});
|
|
1048
|
-
});
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
it('has the correct localIpAddress set when the candidateType is host', async () => {
|
|
1052
|
-
await startStatsAnalyzer();
|
|
1053
|
-
|
|
1054
|
-
await progressTime();
|
|
1055
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
1056
|
-
mergeProperties(fakeStats, {address: 'test', candidateType: 'host'});
|
|
1057
|
-
await progressTime();
|
|
1058
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'test');
|
|
1059
|
-
});
|
|
1060
|
-
|
|
1061
|
-
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is set', async () => {
|
|
1062
|
-
await startStatsAnalyzer();
|
|
1063
|
-
|
|
1064
|
-
await progressTime();
|
|
1065
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
1066
|
-
mergeProperties(fakeStats, {
|
|
1067
|
-
relayProtocol: 'test',
|
|
1068
|
-
address: 'test2',
|
|
1069
|
-
candidateType: 'prflx',
|
|
1070
|
-
});
|
|
1071
|
-
await progressTime();
|
|
1072
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'test2');
|
|
1073
|
-
});
|
|
1074
|
-
|
|
1075
|
-
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is not set', async () => {
|
|
1076
|
-
await startStatsAnalyzer();
|
|
1077
|
-
|
|
1078
|
-
await progressTime();
|
|
1079
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
1080
|
-
mergeProperties(fakeStats, {
|
|
1081
|
-
relatedAddress: 'relatedAddress',
|
|
1082
|
-
address: 'test2',
|
|
1083
|
-
candidateType: 'prflx',
|
|
1084
|
-
});
|
|
1085
|
-
await progressTime();
|
|
1086
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'relatedAddress');
|
|
1087
|
-
});
|
|
1088
|
-
|
|
1089
|
-
it('has no localIpAddress set when the candidateType is invalid', async () => {
|
|
1090
|
-
await startStatsAnalyzer();
|
|
1091
|
-
|
|
1092
|
-
await progressTime();
|
|
1093
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
1094
|
-
mergeProperties(fakeStats, {candidateType: 'invalid'});
|
|
1095
|
-
await progressTime();
|
|
1096
|
-
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
1097
|
-
});
|
|
1098
|
-
|
|
1099
|
-
it('logs a message when audio send packets do not increase', async () => {
|
|
1100
|
-
await startStatsAnalyzer(
|
|
1101
|
-
{expected: {sendAudio: true}},
|
|
1102
|
-
{audio: {local: EVENTS.LOCAL_MEDIA_STARTED}}
|
|
1103
|
-
);
|
|
1104
|
-
|
|
1105
|
-
// don't increase the packets when time progresses.
|
|
1106
|
-
await progressTime();
|
|
1107
|
-
|
|
1108
|
-
assert(
|
|
1109
|
-
loggerSpy.calledWith(
|
|
1110
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
|
|
1111
|
-
)
|
|
1112
|
-
);
|
|
1113
|
-
});
|
|
1114
|
-
|
|
1115
|
-
it('does not log a message when audio send packets increase', async () => {
|
|
1116
|
-
await startStatsAnalyzer(
|
|
1117
|
-
{expected: {sendAudio: true}},
|
|
1118
|
-
{audio: {local: EVENTS.LOCAL_MEDIA_STOPPED}}
|
|
1119
|
-
);
|
|
1120
|
-
|
|
1121
|
-
fakeStats.audio.senders[0].report[0].packetsSent += 5;
|
|
1122
|
-
await progressTime();
|
|
1123
|
-
|
|
1124
|
-
assert(
|
|
1125
|
-
loggerSpy.neverCalledWith(
|
|
1126
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
|
|
1127
|
-
)
|
|
1128
|
-
);
|
|
1129
|
-
});
|
|
1130
|
-
|
|
1131
|
-
it('logs a message when video send packets do not increase', async () => {
|
|
1132
|
-
await startStatsAnalyzer(
|
|
1133
|
-
{expected: {sendVideo: true}},
|
|
1134
|
-
{video: {local: EVENTS.LOCAL_MEDIA_STARTED}}
|
|
1135
|
-
);
|
|
1136
|
-
|
|
1137
|
-
// don't increase the packets when time progresses.
|
|
1138
|
-
await progressTime();
|
|
1139
|
-
|
|
1140
|
-
assert(
|
|
1141
|
-
loggerSpy.calledWith(
|
|
1142
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
|
|
1143
|
-
)
|
|
1144
|
-
);
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
it('does not log a message when video send packets increase', async () => {
|
|
1148
|
-
await startStatsAnalyzer(
|
|
1149
|
-
{expected: {sendVideo: true}},
|
|
1150
|
-
{video: {local: EVENTS.LOCAL_MEDIA_STOPPED}}
|
|
1151
|
-
);
|
|
1152
|
-
|
|
1153
|
-
fakeStats.video.senders[0].report[0].packetsSent += 5;
|
|
1154
|
-
await progressTime();
|
|
1155
|
-
|
|
1156
|
-
assert(
|
|
1157
|
-
loggerSpy.neverCalledWith(
|
|
1158
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
|
|
1159
|
-
)
|
|
1160
|
-
);
|
|
1161
|
-
});
|
|
1162
|
-
|
|
1163
|
-
it('logs a message when share send packets do not increase', async () => {
|
|
1164
|
-
await startStatsAnalyzer(
|
|
1165
|
-
{expected: {sendShare: true}},
|
|
1166
|
-
{share: {local: EVENTS.LOCAL_MEDIA_STARTED}}
|
|
1167
|
-
);
|
|
1168
|
-
|
|
1169
|
-
// don't increase the packets when time progresses.
|
|
1170
|
-
await progressTime();
|
|
1171
|
-
|
|
1172
|
-
assert(
|
|
1173
|
-
loggerSpy.calledWith(
|
|
1174
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
|
|
1175
|
-
)
|
|
1176
|
-
);
|
|
1177
|
-
});
|
|
1178
|
-
|
|
1179
|
-
it('does not log a message when share send packets increase', async () => {
|
|
1180
|
-
await startStatsAnalyzer(
|
|
1181
|
-
{expected: {sendShare: true}},
|
|
1182
|
-
{share: {local: EVENTS.LOCAL_MEDIA_STOPPED}}
|
|
1183
|
-
);
|
|
1184
|
-
|
|
1185
|
-
fakeStats.share.senders[0].report[0].packetsSent += 5;
|
|
1186
|
-
await progressTime();
|
|
1187
|
-
|
|
1188
|
-
assert(
|
|
1189
|
-
loggerSpy.neverCalledWith(
|
|
1190
|
-
'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
|
|
1191
|
-
)
|
|
1192
|
-
);
|
|
1193
|
-
});
|
|
1194
|
-
|
|
1195
|
-
['avatar', 'invalid', 'no source', 'bandwidth limited', 'policy violation'].forEach(
|
|
1196
|
-
(sourceState) => {
|
|
1197
|
-
it(`does not log a message when no packets are recieved for a receive slot with sourceState "${sourceState}"`, async () => {
|
|
1198
|
-
receiveSlot = {
|
|
1199
|
-
sourceState,
|
|
1200
|
-
csi: 2,
|
|
1201
|
-
id: '4',
|
|
1202
|
-
};
|
|
1203
|
-
|
|
1204
|
-
await startStatsAnalyzer();
|
|
1205
|
-
|
|
1206
|
-
// don't increase the packets when time progresses.
|
|
1207
|
-
await progressTime();
|
|
1208
|
-
|
|
1209
|
-
assert.neverCalledWith(
|
|
1210
|
-
loggerSpy,
|
|
1211
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1212
|
-
0
|
|
1213
|
-
);
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
1216
|
-
);
|
|
1217
|
-
|
|
1218
|
-
it(`logs a message if no packets are sent`, async () => {
|
|
1219
|
-
receiveSlot = {
|
|
1220
|
-
sourceState: 'live',
|
|
1221
|
-
csi: 2,
|
|
1222
|
-
id: '4',
|
|
1223
|
-
};
|
|
1224
|
-
await startStatsAnalyzer();
|
|
1225
|
-
|
|
1226
|
-
// don't increase the packets when time progresses.
|
|
1227
|
-
await progressTime();
|
|
1228
|
-
|
|
1229
|
-
assert.calledWith(
|
|
1230
|
-
loggerSpy,
|
|
1231
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1232
|
-
0
|
|
1233
|
-
);
|
|
1234
|
-
|
|
1235
|
-
assert.calledWith(
|
|
1236
|
-
loggerSpy,
|
|
1237
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
|
|
1238
|
-
0
|
|
1239
|
-
);
|
|
1240
|
-
|
|
1241
|
-
assert.calledWith(
|
|
1242
|
-
loggerSpy,
|
|
1243
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
|
|
1244
|
-
0
|
|
1245
|
-
);
|
|
1246
|
-
|
|
1247
|
-
assert.calledWith(
|
|
1248
|
-
loggerSpy,
|
|
1249
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1250
|
-
0
|
|
1251
|
-
);
|
|
1252
|
-
|
|
1253
|
-
assert.calledWith(
|
|
1254
|
-
loggerSpy,
|
|
1255
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1256
|
-
0
|
|
1257
|
-
);
|
|
1258
|
-
|
|
1259
|
-
assert.calledWith(
|
|
1260
|
-
loggerSpy,
|
|
1261
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
|
|
1262
|
-
0
|
|
1263
|
-
);
|
|
1264
|
-
assert.calledWith(
|
|
1265
|
-
loggerSpy,
|
|
1266
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
|
|
1267
|
-
0
|
|
1268
|
-
);
|
|
1269
|
-
assert.calledWith(
|
|
1270
|
-
loggerSpy,
|
|
1271
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
|
|
1272
|
-
0
|
|
1273
|
-
);
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
it(`does not log a message if receiveSlot is undefined`, async () => {
|
|
1277
|
-
await startStatsAnalyzer();
|
|
1278
|
-
|
|
1279
|
-
// don't increase the packets when time progresses.
|
|
1280
|
-
await progressTime();
|
|
1281
|
-
|
|
1282
|
-
assert.neverCalledWith(
|
|
1283
|
-
loggerSpy,
|
|
1284
|
-
'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot "". Total packets received on slot: ',
|
|
1285
|
-
0
|
|
1286
|
-
);
|
|
1287
|
-
});
|
|
1288
|
-
|
|
1289
|
-
it('has the correct number of senders and receivers (2)', async () => {
|
|
1290
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1291
|
-
|
|
1292
|
-
await progressTime();
|
|
1293
|
-
|
|
1294
|
-
assert.lengthOf(mqeData.audioTransmit, 2);
|
|
1295
|
-
assert.lengthOf(mqeData.audioReceive, 2);
|
|
1296
|
-
assert.lengthOf(mqeData.videoTransmit, 2);
|
|
1297
|
-
assert.lengthOf(mqeData.videoReceive, 2);
|
|
1298
|
-
});
|
|
1299
|
-
|
|
1300
|
-
it('has one stream per sender/reciever', async () => {
|
|
1301
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1302
|
-
|
|
1303
|
-
await progressTime();
|
|
1304
|
-
|
|
1305
|
-
assert.deepEqual(mqeData.audioTransmit[0].streams, [
|
|
1306
|
-
{
|
|
1307
|
-
common: {
|
|
1308
|
-
codec: 'opus',
|
|
1309
|
-
csi: [],
|
|
1310
|
-
requestedBitrate: 0,
|
|
1311
|
-
requestedFrames: 0,
|
|
1312
|
-
rtpPackets: 0,
|
|
1313
|
-
ssci: 0,
|
|
1314
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1315
|
-
transmittedFrameRate: 0,
|
|
1316
|
-
},
|
|
1317
|
-
transmittedKeyFrames: 0,
|
|
1318
|
-
requestedKeyFrames: 0,
|
|
1319
|
-
requestedBitrate: 0,
|
|
1320
|
-
},
|
|
1321
|
-
]);
|
|
1322
|
-
assert.deepEqual(mqeData.audioTransmit[1].streams, [
|
|
1323
|
-
{
|
|
1324
|
-
common: {
|
|
1325
|
-
codec: 'opus',
|
|
1326
|
-
csi: [],
|
|
1327
|
-
requestedBitrate: 0,
|
|
1328
|
-
requestedFrames: 0,
|
|
1329
|
-
rtpPackets: 0,
|
|
1330
|
-
ssci: 0,
|
|
1331
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1332
|
-
transmittedFrameRate: 0,
|
|
1333
|
-
},
|
|
1334
|
-
transmittedKeyFrames: 0,
|
|
1335
|
-
requestedKeyFrames: 0,
|
|
1336
|
-
requestedBitrate: 0,
|
|
1337
|
-
},
|
|
1338
|
-
]);
|
|
1339
|
-
assert.deepEqual(mqeData.audioReceive[0].streams, [
|
|
1340
|
-
{
|
|
1341
|
-
common: {
|
|
1342
|
-
codec: 'opus',
|
|
1343
|
-
concealedFrames: 0,
|
|
1344
|
-
csi: [],
|
|
1345
|
-
maxConcealRunLength: 0,
|
|
1346
|
-
optimalBitrate: 0,
|
|
1347
|
-
optimalFrameRate: 0,
|
|
1348
|
-
receivedBitrate: 0.13333333333333333,
|
|
1349
|
-
receivedFrameRate: 0,
|
|
1350
|
-
renderedFrameRate: 0,
|
|
1351
|
-
requestedBitrate: 0,
|
|
1352
|
-
requestedFrameRate: 0,
|
|
1353
|
-
rtpEndToEndLost: 0,
|
|
1354
|
-
maxRtpJitter: 0,
|
|
1355
|
-
meanRtpJitter: 0,
|
|
1356
|
-
rtpPackets: 0,
|
|
1357
|
-
ssci: 0,
|
|
1358
|
-
rtpJitter: 0,
|
|
1359
|
-
framesDropped: 0,
|
|
1360
|
-
framesReceived: 0,
|
|
1361
|
-
},
|
|
1362
|
-
},
|
|
1363
|
-
]);
|
|
1364
|
-
assert.deepEqual(mqeData.audioReceive[1].streams, [
|
|
1365
|
-
{
|
|
1366
|
-
common: {
|
|
1367
|
-
codec: 'opus',
|
|
1368
|
-
concealedFrames: 0,
|
|
1369
|
-
csi: [],
|
|
1370
|
-
maxConcealRunLength: 0,
|
|
1371
|
-
optimalBitrate: 0,
|
|
1372
|
-
optimalFrameRate: 0,
|
|
1373
|
-
receivedBitrate: 0.13333333333333333,
|
|
1374
|
-
receivedFrameRate: 0,
|
|
1375
|
-
renderedFrameRate: 0,
|
|
1376
|
-
requestedBitrate: 0,
|
|
1377
|
-
requestedFrameRate: 0,
|
|
1378
|
-
rtpEndToEndLost: 0,
|
|
1379
|
-
maxRtpJitter: 0,
|
|
1380
|
-
meanRtpJitter: 0,
|
|
1381
|
-
rtpPackets: 0,
|
|
1382
|
-
ssci: 0,
|
|
1383
|
-
rtpJitter: 0,
|
|
1384
|
-
framesDropped: 0,
|
|
1385
|
-
framesReceived: 0,
|
|
1386
|
-
},
|
|
1387
|
-
},
|
|
1388
|
-
]);
|
|
1389
|
-
assert.deepEqual(mqeData.videoTransmit[0].streams, [
|
|
1390
|
-
{
|
|
1391
|
-
common: {
|
|
1392
|
-
codec: 'H264',
|
|
1393
|
-
csi: [],
|
|
1394
|
-
duplicateSsci: 0,
|
|
1395
|
-
requestedBitrate: 0,
|
|
1396
|
-
requestedFrames: 0,
|
|
1397
|
-
rtpPackets: 0,
|
|
1398
|
-
ssci: 0,
|
|
1399
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1400
|
-
transmittedFrameRate: 0,
|
|
1401
|
-
},
|
|
1402
|
-
h264CodecProfile: 'BP',
|
|
1403
|
-
isAvatar: false,
|
|
1404
|
-
isHardwareEncoded: false,
|
|
1405
|
-
localConfigurationChanges: 2,
|
|
1406
|
-
maxFrameQp: 0,
|
|
1407
|
-
maxNoiseLevel: 0,
|
|
1408
|
-
minRegionQp: 0,
|
|
1409
|
-
remoteConfigurationChanges: 0,
|
|
1410
|
-
requestedFrameSize: 0,
|
|
1411
|
-
requestedKeyFrames: 0,
|
|
1412
|
-
transmittedFrameSize: 0,
|
|
1413
|
-
transmittedHeight: 0,
|
|
1414
|
-
transmittedKeyFrames: 0,
|
|
1415
|
-
transmittedKeyFramesClient: 0,
|
|
1416
|
-
transmittedKeyFramesConfigurationChange: 0,
|
|
1417
|
-
transmittedKeyFramesFeedback: 0,
|
|
1418
|
-
transmittedKeyFramesLocalDrop: 0,
|
|
1419
|
-
transmittedKeyFramesOtherLayer: 0,
|
|
1420
|
-
transmittedKeyFramesPeriodic: 0,
|
|
1421
|
-
transmittedKeyFramesSceneChange: 0,
|
|
1422
|
-
transmittedKeyFramesStartup: 0,
|
|
1423
|
-
transmittedKeyFramesUnknown: 0,
|
|
1424
|
-
transmittedWidth: 0,
|
|
1425
|
-
requestedBitrate: 0,
|
|
1426
|
-
},
|
|
1427
|
-
]);
|
|
1428
|
-
assert.deepEqual(mqeData.videoTransmit[1].streams, [
|
|
1429
|
-
{
|
|
1430
|
-
common: {
|
|
1431
|
-
codec: 'H264',
|
|
1432
|
-
csi: [],
|
|
1433
|
-
duplicateSsci: 0,
|
|
1434
|
-
requestedBitrate: 0,
|
|
1435
|
-
requestedFrames: 0,
|
|
1436
|
-
rtpPackets: 0,
|
|
1437
|
-
ssci: 0,
|
|
1438
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1439
|
-
transmittedFrameRate: 0,
|
|
1440
|
-
},
|
|
1441
|
-
h264CodecProfile: 'BP',
|
|
1442
|
-
isAvatar: false,
|
|
1443
|
-
isHardwareEncoded: false,
|
|
1444
|
-
localConfigurationChanges: 2,
|
|
1445
|
-
maxFrameQp: 0,
|
|
1446
|
-
maxNoiseLevel: 0,
|
|
1447
|
-
minRegionQp: 0,
|
|
1448
|
-
remoteConfigurationChanges: 0,
|
|
1449
|
-
requestedBitrate: 0,
|
|
1450
|
-
requestedFrameSize: 0,
|
|
1451
|
-
requestedKeyFrames: 0,
|
|
1452
|
-
transmittedFrameSize: 0,
|
|
1453
|
-
transmittedHeight: 0,
|
|
1454
|
-
transmittedKeyFrames: 0,
|
|
1455
|
-
transmittedKeyFramesClient: 0,
|
|
1456
|
-
transmittedKeyFramesConfigurationChange: 0,
|
|
1457
|
-
transmittedKeyFramesFeedback: 0,
|
|
1458
|
-
transmittedKeyFramesLocalDrop: 0,
|
|
1459
|
-
transmittedKeyFramesOtherLayer: 0,
|
|
1460
|
-
transmittedKeyFramesPeriodic: 0,
|
|
1461
|
-
transmittedKeyFramesSceneChange: 0,
|
|
1462
|
-
transmittedKeyFramesStartup: 0,
|
|
1463
|
-
transmittedKeyFramesUnknown: 0,
|
|
1464
|
-
transmittedWidth: 0,
|
|
1465
|
-
},
|
|
1466
|
-
]);
|
|
1467
|
-
assert.deepEqual(mqeData.videoReceive[0].streams, [
|
|
1468
|
-
{
|
|
1469
|
-
common: {
|
|
1470
|
-
codec: 'H264',
|
|
1471
|
-
concealedFrames: 0,
|
|
1472
|
-
csi: [],
|
|
1473
|
-
maxConcealRunLength: 0,
|
|
1474
|
-
optimalBitrate: 0,
|
|
1475
|
-
optimalFrameRate: 0,
|
|
1476
|
-
receivedBitrate: 0.13333333333333333,
|
|
1477
|
-
receivedFrameRate: 0,
|
|
1478
|
-
renderedFrameRate: 0,
|
|
1479
|
-
requestedBitrate: 0,
|
|
1480
|
-
requestedFrameRate: 0,
|
|
1481
|
-
rtpEndToEndLost: 0,
|
|
1482
|
-
rtpJitter: 0,
|
|
1483
|
-
rtpPackets: 0,
|
|
1484
|
-
ssci: 0,
|
|
1485
|
-
framesDropped: 0,
|
|
1486
|
-
},
|
|
1487
|
-
h264CodecProfile: 'BP',
|
|
1488
|
-
isActiveSpeaker: false,
|
|
1489
|
-
optimalFrameSize: 0,
|
|
1490
|
-
receivedFrameSize: 3600,
|
|
1491
|
-
receivedHeight: 720,
|
|
1492
|
-
receivedKeyFrames: 0,
|
|
1493
|
-
receivedKeyFramesForRequest: 0,
|
|
1494
|
-
receivedKeyFramesSourceChange: 0,
|
|
1495
|
-
receivedKeyFramesUnknown: 0,
|
|
1496
|
-
receivedWidth: 1280,
|
|
1497
|
-
requestedFrameSize: 0,
|
|
1498
|
-
requestedKeyFrames: 0,
|
|
1499
|
-
},
|
|
1500
|
-
]);
|
|
1501
|
-
assert.deepEqual(mqeData.videoReceive[1].streams, [
|
|
1502
|
-
{
|
|
1503
|
-
common: {
|
|
1504
|
-
codec: 'H264',
|
|
1505
|
-
concealedFrames: 0,
|
|
1506
|
-
csi: [],
|
|
1507
|
-
maxConcealRunLength: 0,
|
|
1508
|
-
optimalBitrate: 0,
|
|
1509
|
-
optimalFrameRate: 0,
|
|
1510
|
-
receivedBitrate: 0.13333333333333333,
|
|
1511
|
-
receivedFrameRate: 0,
|
|
1512
|
-
renderedFrameRate: 0,
|
|
1513
|
-
requestedBitrate: 0,
|
|
1514
|
-
requestedFrameRate: 0,
|
|
1515
|
-
rtpEndToEndLost: 0,
|
|
1516
|
-
rtpJitter: 0,
|
|
1517
|
-
rtpPackets: 0,
|
|
1518
|
-
ssci: 0,
|
|
1519
|
-
framesDropped: 0,
|
|
1520
|
-
},
|
|
1521
|
-
h264CodecProfile: 'BP',
|
|
1522
|
-
isActiveSpeaker: false,
|
|
1523
|
-
optimalFrameSize: 0,
|
|
1524
|
-
receivedFrameSize: 3600,
|
|
1525
|
-
receivedHeight: 720,
|
|
1526
|
-
receivedKeyFrames: 0,
|
|
1527
|
-
receivedKeyFramesForRequest: 0,
|
|
1528
|
-
receivedKeyFramesSourceChange: 0,
|
|
1529
|
-
receivedKeyFramesUnknown: 0,
|
|
1530
|
-
receivedWidth: 1280,
|
|
1531
|
-
requestedFrameSize: 0,
|
|
1532
|
-
requestedKeyFrames: 0,
|
|
1533
|
-
},
|
|
1534
|
-
]);
|
|
1535
|
-
});
|
|
1536
|
-
|
|
1537
|
-
it('has three streams for video receivers when three exist', async () => {
|
|
1538
|
-
pc.getTransceiverStats = sinon.stub().resolves({
|
|
1539
|
-
audio: {
|
|
1540
|
-
senders: [fakeStats.audio.senders[0]],
|
|
1541
|
-
receivers: [fakeStats.audio.receivers[0]],
|
|
1542
|
-
},
|
|
1543
|
-
video: {
|
|
1544
|
-
senders: [fakeStats.video.senders[0]],
|
|
1545
|
-
receivers: [
|
|
1546
|
-
fakeStats.video.receivers[0],
|
|
1547
|
-
fakeStats.video.receivers[0],
|
|
1548
|
-
fakeStats.video.receivers[0],
|
|
1549
|
-
],
|
|
1550
|
-
},
|
|
1551
|
-
screenShareAudio: {
|
|
1552
|
-
senders: [fakeStats.audio.senders[0]],
|
|
1553
|
-
receivers: [fakeStats.audio.receivers[0]],
|
|
1554
|
-
},
|
|
1555
|
-
screenShareVideo: {
|
|
1556
|
-
senders: [fakeStats.video.senders[0]],
|
|
1557
|
-
receivers: [fakeStats.video.receivers[0]],
|
|
1558
|
-
},
|
|
1559
|
-
});
|
|
1560
|
-
|
|
1561
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1562
|
-
|
|
1563
|
-
await progressTime();
|
|
1564
|
-
|
|
1565
|
-
assert.deepEqual(mqeData.videoReceive[0].streams, [
|
|
1566
|
-
{
|
|
1567
|
-
common: {
|
|
1568
|
-
codec: 'H264',
|
|
1569
|
-
concealedFrames: 0,
|
|
1570
|
-
csi: [],
|
|
1571
|
-
maxConcealRunLength: 0,
|
|
1572
|
-
optimalBitrate: 0,
|
|
1573
|
-
optimalFrameRate: 0,
|
|
1574
|
-
receivedBitrate: 0.13333333333333333,
|
|
1575
|
-
receivedFrameRate: 0,
|
|
1576
|
-
renderedFrameRate: 0,
|
|
1577
|
-
requestedBitrate: 0,
|
|
1578
|
-
requestedFrameRate: 0,
|
|
1579
|
-
rtpEndToEndLost: 0,
|
|
1580
|
-
rtpJitter: 0,
|
|
1581
|
-
rtpPackets: 0,
|
|
1582
|
-
ssci: 0,
|
|
1583
|
-
framesDropped: 0,
|
|
1584
|
-
},
|
|
1585
|
-
h264CodecProfile: 'BP',
|
|
1586
|
-
isActiveSpeaker: false,
|
|
1587
|
-
optimalFrameSize: 0,
|
|
1588
|
-
receivedFrameSize: 3600,
|
|
1589
|
-
receivedHeight: 720,
|
|
1590
|
-
receivedKeyFrames: 0,
|
|
1591
|
-
receivedKeyFramesForRequest: 0,
|
|
1592
|
-
receivedKeyFramesSourceChange: 0,
|
|
1593
|
-
receivedKeyFramesUnknown: 0,
|
|
1594
|
-
receivedWidth: 1280,
|
|
1595
|
-
requestedFrameSize: 0,
|
|
1596
|
-
requestedKeyFrames: 0,
|
|
1597
|
-
},
|
|
1598
|
-
{
|
|
1599
|
-
common: {
|
|
1600
|
-
codec: 'H264',
|
|
1601
|
-
concealedFrames: 0,
|
|
1602
|
-
csi: [],
|
|
1603
|
-
maxConcealRunLength: 0,
|
|
1604
|
-
optimalBitrate: 0,
|
|
1605
|
-
optimalFrameRate: 0,
|
|
1606
|
-
receivedBitrate: 0.13333333333333333,
|
|
1607
|
-
receivedFrameRate: 0,
|
|
1608
|
-
renderedFrameRate: 0,
|
|
1609
|
-
requestedBitrate: 0,
|
|
1610
|
-
requestedFrameRate: 0,
|
|
1611
|
-
rtpEndToEndLost: 0,
|
|
1612
|
-
rtpJitter: 0,
|
|
1613
|
-
rtpPackets: 0,
|
|
1614
|
-
ssci: 0,
|
|
1615
|
-
framesDropped: 0,
|
|
1616
|
-
},
|
|
1617
|
-
h264CodecProfile: 'BP',
|
|
1618
|
-
isActiveSpeaker: false,
|
|
1619
|
-
optimalFrameSize: 0,
|
|
1620
|
-
receivedFrameSize: 3600,
|
|
1621
|
-
receivedHeight: 720,
|
|
1622
|
-
receivedKeyFrames: 0,
|
|
1623
|
-
receivedKeyFramesForRequest: 0,
|
|
1624
|
-
receivedKeyFramesSourceChange: 0,
|
|
1625
|
-
receivedKeyFramesUnknown: 0,
|
|
1626
|
-
receivedWidth: 1280,
|
|
1627
|
-
requestedFrameSize: 0,
|
|
1628
|
-
requestedKeyFrames: 0,
|
|
1629
|
-
},
|
|
1630
|
-
{
|
|
1631
|
-
common: {
|
|
1632
|
-
codec: 'H264',
|
|
1633
|
-
concealedFrames: 0,
|
|
1634
|
-
csi: [],
|
|
1635
|
-
maxConcealRunLength: 0,
|
|
1636
|
-
optimalBitrate: 0,
|
|
1637
|
-
optimalFrameRate: 0,
|
|
1638
|
-
receivedBitrate: 0.13333333333333333,
|
|
1639
|
-
receivedFrameRate: 0,
|
|
1640
|
-
renderedFrameRate: 0,
|
|
1641
|
-
requestedBitrate: 0,
|
|
1642
|
-
requestedFrameRate: 0,
|
|
1643
|
-
rtpEndToEndLost: 0,
|
|
1644
|
-
rtpJitter: 0,
|
|
1645
|
-
rtpPackets: 0,
|
|
1646
|
-
ssci: 0,
|
|
1647
|
-
framesDropped: 0,
|
|
1648
|
-
},
|
|
1649
|
-
h264CodecProfile: 'BP',
|
|
1650
|
-
isActiveSpeaker: false,
|
|
1651
|
-
optimalFrameSize: 0,
|
|
1652
|
-
receivedFrameSize: 3600,
|
|
1653
|
-
receivedHeight: 720,
|
|
1654
|
-
receivedKeyFrames: 0,
|
|
1655
|
-
receivedKeyFramesForRequest: 0,
|
|
1656
|
-
receivedKeyFramesSourceChange: 0,
|
|
1657
|
-
receivedKeyFramesUnknown: 0,
|
|
1658
|
-
receivedWidth: 1280,
|
|
1659
|
-
requestedFrameSize: 0,
|
|
1660
|
-
requestedKeyFrames: 0,
|
|
1661
|
-
},
|
|
1662
|
-
]);
|
|
1663
|
-
});
|
|
1664
|
-
|
|
1665
|
-
describe('stream count for simulcast', async() => {
|
|
1666
|
-
it('has three streams for video senders for simulcast', async () => {
|
|
1667
|
-
pc.getTransceiverStats = sinon.stub().resolves({
|
|
1668
|
-
audio: {
|
|
1669
|
-
senders: [fakeStats.audio.senders[0]],
|
|
1670
|
-
receivers: [fakeStats.audio.receivers[0]],
|
|
1671
|
-
},
|
|
1672
|
-
video: {
|
|
1673
|
-
senders: [
|
|
1674
|
-
{
|
|
1675
|
-
localTrackLabel: 'fake-camera',
|
|
1676
|
-
report: [
|
|
1677
|
-
{
|
|
1678
|
-
type: 'outbound-rtp',
|
|
1679
|
-
bytesSent: 1,
|
|
1680
|
-
framesSent: 0,
|
|
1681
|
-
packetsSent: 0,
|
|
1682
|
-
isRequested: true,
|
|
1683
|
-
},
|
|
1684
|
-
{
|
|
1685
|
-
type: 'outbound-rtp',
|
|
1686
|
-
bytesSent: 1,
|
|
1687
|
-
framesSent: 0,
|
|
1688
|
-
packetsSent: 1,
|
|
1689
|
-
isRequested: true,
|
|
1690
|
-
},
|
|
1691
|
-
{
|
|
1692
|
-
type: 'outbound-rtp',
|
|
1693
|
-
bytesSent: 1000,
|
|
1694
|
-
framesSent: 1,
|
|
1695
|
-
packetsSent: 0,
|
|
1696
|
-
isRequested: true,
|
|
1697
|
-
},
|
|
1698
|
-
{
|
|
1699
|
-
type: 'remote-inbound-rtp',
|
|
1700
|
-
packetsLost: 0,
|
|
1701
|
-
},
|
|
1702
|
-
{
|
|
1703
|
-
type: 'candidate-pair',
|
|
1704
|
-
state: 'succeeded',
|
|
1705
|
-
localCandidateId: 'fake-candidate-id',
|
|
1706
|
-
},
|
|
1707
|
-
{
|
|
1708
|
-
type: 'candidate-pair',
|
|
1709
|
-
state: 'failed',
|
|
1710
|
-
localCandidateId: 'bad-candidate-id',
|
|
1711
|
-
},
|
|
1712
|
-
{
|
|
1713
|
-
type: 'local-candidate',
|
|
1714
|
-
id: 'fake-candidate-id',
|
|
1715
|
-
protocol: 'tcp',
|
|
1716
|
-
},
|
|
1717
|
-
],
|
|
1718
|
-
},
|
|
1719
|
-
],
|
|
1720
|
-
receivers: [fakeStats.video.receivers[0]],
|
|
1721
|
-
},
|
|
1722
|
-
screenShareAudio: {
|
|
1723
|
-
senders: [fakeStats.audio.senders[0]],
|
|
1724
|
-
receivers: [fakeStats.audio.receivers[0]],
|
|
1725
|
-
},
|
|
1726
|
-
screenShareVideo: {
|
|
1727
|
-
senders: [fakeStats.video.senders[0]],
|
|
1728
|
-
receivers: [fakeStats.video.receivers[0]],
|
|
1729
|
-
},
|
|
1730
|
-
});
|
|
1731
|
-
|
|
1732
|
-
await startStatsAnalyzer({expected: {receiveVideo: true}});
|
|
1733
|
-
|
|
1734
|
-
await progressTime();
|
|
1735
|
-
|
|
1736
|
-
assert.deepEqual(mqeData.videoTransmit[0].streams, [
|
|
1737
|
-
{
|
|
1738
|
-
common: {
|
|
1739
|
-
codec: 'H264',
|
|
1740
|
-
csi: [],
|
|
1741
|
-
duplicateSsci: 0,
|
|
1742
|
-
requestedBitrate: 0,
|
|
1743
|
-
requestedFrames: 0,
|
|
1744
|
-
rtpPackets: 0,
|
|
1745
|
-
ssci: 0,
|
|
1746
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1747
|
-
transmittedFrameRate: 0
|
|
1748
|
-
},
|
|
1749
|
-
h264CodecProfile: 'BP',
|
|
1750
|
-
isAvatar: false,
|
|
1751
|
-
isHardwareEncoded: false,
|
|
1752
|
-
localConfigurationChanges: 2,
|
|
1753
|
-
maxFrameQp: 0,
|
|
1754
|
-
maxNoiseLevel: 0,
|
|
1755
|
-
minRegionQp: 0,
|
|
1756
|
-
remoteConfigurationChanges: 0,
|
|
1757
|
-
requestedFrameSize: 0,
|
|
1758
|
-
requestedKeyFrames: 0,
|
|
1759
|
-
transmittedFrameSize: 0,
|
|
1760
|
-
transmittedHeight: 0,
|
|
1761
|
-
transmittedKeyFrames: 0,
|
|
1762
|
-
transmittedKeyFramesClient: 0,
|
|
1763
|
-
transmittedKeyFramesConfigurationChange: 0,
|
|
1764
|
-
transmittedKeyFramesFeedback: 0,
|
|
1765
|
-
transmittedKeyFramesLocalDrop: 0,
|
|
1766
|
-
transmittedKeyFramesOtherLayer: 0,
|
|
1767
|
-
transmittedKeyFramesPeriodic: 0,
|
|
1768
|
-
transmittedKeyFramesSceneChange: 0,
|
|
1769
|
-
transmittedKeyFramesStartup: 0,
|
|
1770
|
-
transmittedKeyFramesUnknown: 0,
|
|
1771
|
-
transmittedWidth: 0,
|
|
1772
|
-
requestedBitrate: 0,
|
|
1773
|
-
},
|
|
1774
|
-
{
|
|
1775
|
-
common: {
|
|
1776
|
-
codec: 'H264',
|
|
1777
|
-
csi: [],
|
|
1778
|
-
duplicateSsci: 0,
|
|
1779
|
-
requestedBitrate: 0,
|
|
1780
|
-
requestedFrames: 0,
|
|
1781
|
-
rtpPackets: 1,
|
|
1782
|
-
ssci: 0,
|
|
1783
|
-
transmittedBitrate: 0.13333333333333333,
|
|
1784
|
-
transmittedFrameRate: 0,
|
|
1785
|
-
},
|
|
1786
|
-
h264CodecProfile: 'BP',
|
|
1787
|
-
isAvatar: false,
|
|
1788
|
-
isHardwareEncoded: false,
|
|
1789
|
-
localConfigurationChanges: 2,
|
|
1790
|
-
maxFrameQp: 0,
|
|
1791
|
-
maxNoiseLevel: 0,
|
|
1792
|
-
minRegionQp: 0,
|
|
1793
|
-
remoteConfigurationChanges: 0,
|
|
1794
|
-
requestedFrameSize: 0,
|
|
1795
|
-
requestedKeyFrames: 0,
|
|
1796
|
-
transmittedFrameSize: 0,
|
|
1797
|
-
transmittedHeight: 0,
|
|
1798
|
-
transmittedKeyFrames: 0,
|
|
1799
|
-
transmittedKeyFramesClient: 0,
|
|
1800
|
-
transmittedKeyFramesConfigurationChange: 0,
|
|
1801
|
-
transmittedKeyFramesFeedback: 0,
|
|
1802
|
-
transmittedKeyFramesLocalDrop: 0,
|
|
1803
|
-
transmittedKeyFramesOtherLayer: 0,
|
|
1804
|
-
transmittedKeyFramesPeriodic: 0,
|
|
1805
|
-
transmittedKeyFramesSceneChange: 0,
|
|
1806
|
-
transmittedKeyFramesStartup: 0,
|
|
1807
|
-
transmittedKeyFramesUnknown: 0,
|
|
1808
|
-
transmittedWidth: 0,
|
|
1809
|
-
requestedBitrate: 0,
|
|
1810
|
-
},
|
|
1811
|
-
{
|
|
1812
|
-
common: {
|
|
1813
|
-
codec: 'H264',
|
|
1814
|
-
csi: [],
|
|
1815
|
-
duplicateSsci: 0,
|
|
1816
|
-
requestedBitrate: 0,
|
|
1817
|
-
requestedFrames: 0,
|
|
1818
|
-
rtpPackets: 0,
|
|
1819
|
-
ssci: 0,
|
|
1820
|
-
transmittedBitrate: 133.33333333333334,
|
|
1821
|
-
transmittedFrameRate: 0,
|
|
1822
|
-
},
|
|
1823
|
-
h264CodecProfile: 'BP',
|
|
1824
|
-
isAvatar: false,
|
|
1825
|
-
isHardwareEncoded: false,
|
|
1826
|
-
localConfigurationChanges: 2,
|
|
1827
|
-
maxFrameQp: 0,
|
|
1828
|
-
maxNoiseLevel: 0,
|
|
1829
|
-
minRegionQp: 0,
|
|
1830
|
-
remoteConfigurationChanges: 0,
|
|
1831
|
-
requestedFrameSize: 0,
|
|
1832
|
-
requestedKeyFrames: 0,
|
|
1833
|
-
transmittedFrameSize: 0,
|
|
1834
|
-
transmittedHeight: 0,
|
|
1835
|
-
transmittedKeyFrames: 0,
|
|
1836
|
-
transmittedKeyFramesClient: 0,
|
|
1837
|
-
transmittedKeyFramesConfigurationChange: 0,
|
|
1838
|
-
transmittedKeyFramesFeedback: 0,
|
|
1839
|
-
transmittedKeyFramesLocalDrop: 0,
|
|
1840
|
-
transmittedKeyFramesOtherLayer: 0,
|
|
1841
|
-
transmittedKeyFramesPeriodic: 0,
|
|
1842
|
-
transmittedKeyFramesSceneChange: 0,
|
|
1843
|
-
transmittedKeyFramesStartup: 0,
|
|
1844
|
-
transmittedKeyFramesUnknown: 0,
|
|
1845
|
-
transmittedWidth: 0,
|
|
1846
|
-
requestedBitrate: 0,
|
|
1847
|
-
}
|
|
1848
|
-
]);
|
|
1849
|
-
});
|
|
1850
|
-
});
|
|
1851
|
-
describe('active speaker status emission', async () => {
|
|
1852
|
-
beforeEach(async () => {
|
|
1853
|
-
await startStatsAnalyzer();
|
|
1854
|
-
performance.timeOrigin = 1;
|
|
1855
|
-
});
|
|
1856
|
-
|
|
1857
|
-
it('reports active speaker as true when the participant has been speaking', async () => {
|
|
1858
|
-
fakeStats.video.receivers[0].report[0].isActiveSpeaker = true;
|
|
1859
|
-
await progressTime(5 * MQA_INTERVAL);
|
|
1860
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, true);
|
|
1861
|
-
});
|
|
1862
|
-
|
|
1863
|
-
it('reports active speaker as false when the participant has not spoken', async () => {
|
|
1864
|
-
fakeStats.video.receivers[0].report[0].isActiveSpeaker = false;
|
|
1865
|
-
await progressTime(5 * MQA_INTERVAL);
|
|
1866
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1867
|
-
});
|
|
1868
|
-
|
|
1869
|
-
it('defaults to false when active speaker status is indeterminate', async () => {
|
|
1870
|
-
fakeStats.video.receivers[0].report[0].isActiveSpeaker = undefined;
|
|
1871
|
-
await progressTime(MQA_INTERVAL);
|
|
1872
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1873
|
-
});
|
|
1874
|
-
|
|
1875
|
-
it('updates active speaker to true following a recent status change to speaking', async () => {
|
|
1876
|
-
fakeStats.video.receivers[0].report[0].isActiveSpeaker = false;
|
|
1877
|
-
fakeStats.video.receivers[0].report[0].lastActiveSpeakerUpdateTimestamp = performance.timeOrigin + performance.now() + (30 * 1000);
|
|
1878
|
-
await progressTime(MQA_INTERVAL);
|
|
1879
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, true);
|
|
1880
|
-
await progressTime(MQA_INTERVAL);
|
|
1881
|
-
assert.strictEqual(mqeData.videoReceive[0].streams[0].isActiveSpeaker, false);
|
|
1882
|
-
});
|
|
1883
|
-
});
|
|
1884
|
-
describe('sends streams according to their is requested flag', async () => {
|
|
1885
|
-
|
|
1886
|
-
beforeEach(async () => {
|
|
1887
|
-
performance.timeOrigin = 0;
|
|
1888
|
-
await startStatsAnalyzer();
|
|
1889
|
-
});
|
|
1890
|
-
|
|
1891
|
-
it('should send a stream if it is requested', async () => {
|
|
1892
|
-
fakeStats.audio.senders[0].report[0].isRequested = true;
|
|
1893
|
-
await progressTime(MQA_INTERVAL);
|
|
1894
|
-
assert.strictEqual(mqeData.audioTransmit[0].streams.length, 1);
|
|
1895
|
-
});
|
|
1896
|
-
|
|
1897
|
-
it('should not sent a stream if its is requested flag is undefined', async () => {
|
|
1898
|
-
fakeStats.audio.senders[0].report[0].isRequested = undefined;
|
|
1899
|
-
await progressTime(MQA_INTERVAL);
|
|
1900
|
-
assert.strictEqual(mqeData.audioTransmit[0].streams.length, 0);
|
|
1901
|
-
});
|
|
1902
|
-
|
|
1903
|
-
it('should not send a stream if it is not requested', async () => {
|
|
1904
|
-
fakeStats.audio.receivers[0].report[0].isRequested = false;
|
|
1905
|
-
await progressTime(MQA_INTERVAL);
|
|
1906
|
-
assert.strictEqual(mqeData.audioReceive[0].streams.length, 0);
|
|
1907
|
-
});
|
|
1908
|
-
|
|
1909
|
-
it('should send the stream if it was recently requested', async () => {
|
|
1910
|
-
fakeStats.audio.receivers[0].report[0].lastRequestedUpdateTimestamp = performance.timeOrigin + performance.now() + (30 * 1000);
|
|
1911
|
-
fakeStats.audio.receivers[0].report[0].isRequested = false;
|
|
1912
|
-
await progressTime(MQA_INTERVAL);
|
|
1913
|
-
assert.strictEqual(mqeData.audioReceive[0].streams.length, 1);
|
|
1914
|
-
await progressTime(MQA_INTERVAL);
|
|
1915
|
-
assert.strictEqual(mqeData.audioReceive[0].streams.length, 0);
|
|
1916
|
-
});
|
|
1917
|
-
});
|
|
1918
|
-
|
|
1919
|
-
describe('window and screen size emission', async () => {
|
|
1920
|
-
beforeEach(async() => {
|
|
1921
|
-
await startStatsAnalyzer();
|
|
1922
|
-
})
|
|
1923
|
-
|
|
1924
|
-
it('should record the screen size from window.screen properties', async () => {
|
|
1925
|
-
sinon.stub(window.screen, 'width').get(() => 1280);
|
|
1926
|
-
sinon.stub(window.screen, 'height').get(() => 720);
|
|
1927
|
-
await progressTime(MQA_INTERVAL);
|
|
1928
|
-
assert.strictEqual(mqeData.intervalMetadata.screenWidth, 1280);
|
|
1929
|
-
assert.strictEqual(mqeData.intervalMetadata.screenHeight, 720);
|
|
1930
|
-
assert.strictEqual(mqeData.intervalMetadata.screenResolution, 3600);
|
|
1931
|
-
})
|
|
1932
|
-
|
|
1933
|
-
it('should record the initial app window size from window properties', async () => {
|
|
1934
|
-
sinon.stub(window, 'innerWidth').get(() => 720);
|
|
1935
|
-
sinon.stub(window, 'innerHeight').get(() => 360);
|
|
1936
|
-
await progressTime(MQA_INTERVAL);
|
|
1937
|
-
assert.strictEqual(mqeData.intervalMetadata.appWindowWidth, 720);
|
|
1938
|
-
assert.strictEqual(mqeData.intervalMetadata.appWindowHeight, 360);
|
|
1939
|
-
assert.strictEqual(mqeData.intervalMetadata.appWindowSize, 1013);
|
|
1940
|
-
|
|
1941
|
-
sinon.stub(window, 'innerWidth').get(() => 1080);
|
|
1942
|
-
sinon.stub(window, 'innerHeight').get(() => 720);
|
|
1943
|
-
await progressTime(MQA_INTERVAL);
|
|
1944
|
-
assert.strictEqual(mqeData.intervalMetadata.appWindowWidth, 1080);
|
|
1945
|
-
assert.strictEqual(mqeData.intervalMetadata.appWindowHeight, 720);
|
|
1946
|
-
assert.strictEqual(mqeData.intervalMetadata.appWindowSize, 3038);
|
|
1947
|
-
})
|
|
1948
|
-
})
|
|
1949
|
-
})
|
|
1950
|
-
});
|
|
1951
|
-
});
|