mediasoup-client 3.6.59 → 3.6.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -3
- package/lib/Consumer.d.ts +3 -3
- package/lib/Consumer.d.ts.map +1 -1
- package/lib/DataConsumer.d.ts +3 -3
- package/lib/DataConsumer.d.ts.map +1 -1
- package/lib/DataProducer.d.ts +3 -3
- package/lib/DataProducer.d.ts.map +1 -1
- package/lib/Device.d.ts +3 -3
- package/lib/Device.d.ts.map +1 -1
- package/lib/EnhancedEventEmitter.d.ts +1 -1
- package/lib/EnhancedEventEmitter.d.ts.map +1 -1
- package/lib/Producer.d.ts +4 -4
- package/lib/Producer.d.ts.map +1 -1
- package/lib/RtpParameters.d.ts +11 -11
- package/lib/RtpParameters.d.ts.map +1 -1
- package/lib/SctpParameters.d.ts +4 -4
- package/lib/SctpParameters.d.ts.map +1 -1
- package/lib/Transport.d.ts +11 -11
- package/lib/Transport.d.ts.map +1 -1
- package/lib/handlers/Chrome55.js +6 -6
- package/lib/handlers/Chrome67.js +6 -6
- package/lib/handlers/Chrome70.d.ts.map +1 -1
- package/lib/handlers/Chrome70.js +10 -7
- package/lib/handlers/Chrome74.d.ts.map +1 -1
- package/lib/handlers/Chrome74.js +10 -7
- package/lib/handlers/Edge11.js +6 -6
- package/lib/handlers/FakeHandler.d.ts +1 -1
- package/lib/handlers/FakeHandler.d.ts.map +1 -1
- package/lib/handlers/FakeHandler.js +6 -6
- package/lib/handlers/Firefox60.d.ts.map +1 -1
- package/lib/handlers/Firefox60.js +10 -7
- package/lib/handlers/HandlerInterface.d.ts +11 -11
- package/lib/handlers/HandlerInterface.d.ts.map +1 -1
- package/lib/handlers/ReactNative.js +6 -6
- package/lib/handlers/Safari11.js +6 -6
- package/lib/handlers/Safari12.d.ts.map +1 -1
- package/lib/handlers/Safari12.js +10 -7
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/scalabilityModes.d.ts +1 -1
- package/lib/scalabilityModes.d.ts.map +1 -1
- package/lib/tests/fakeParameters.d.ts +20 -0
- package/lib/tests/fakeParameters.d.ts.map +1 -0
- package/lib/tests/fakeParameters.js +636 -0
- package/lib/tests/test.d.ts +6 -0
- package/lib/tests/test.d.ts.map +1 -0
- package/lib/tests/test.js +1270 -0
- package/npm-scripts.js +143 -28
- package/package.json +38 -11
- package/.eslintrc.js +0 -243
- package/.github/FUNDING.yml +0 -12
- package/.github/ISSUE_TEMPLATE/Bug_Report.md +0 -24
- package/.github/ISSUE_TEMPLATE/Feature_Request.md +0 -7
- package/.github/ISSUE_TEMPLATE/Support_Question.md +0 -12
- package/.travis.yml +0 -26
- package/src/Consumer.ts +0 -334
- package/src/DataConsumer.ts +0 -271
- package/src/DataProducer.ts +0 -291
- package/src/Device.ts +0 -584
- package/src/EnhancedEventEmitter.ts +0 -133
- package/src/Logger.ts +0 -47
- package/src/Producer.ts +0 -534
- package/src/RtpParameters.ts +0 -326
- package/src/SctpParameters.ts +0 -74
- package/src/Transport.ts +0 -1275
- package/src/errors.ts +0 -45
- package/src/handlers/Chrome55.ts +0 -866
- package/src/handlers/Chrome67.ts +0 -951
- package/src/handlers/Chrome70.ts +0 -964
- package/src/handlers/Chrome74.ts +0 -1058
- package/src/handlers/Edge11.ts +0 -667
- package/src/handlers/FakeHandler.ts +0 -448
- package/src/handlers/Firefox60.ts +0 -1040
- package/src/handlers/HandlerInterface.ts +0 -162
- package/src/handlers/ReactNative.ts +0 -886
- package/src/handlers/Safari11.ts +0 -944
- package/src/handlers/Safari12.ts +0 -1009
- package/src/handlers/ortc/edgeUtils.ts +0 -77
- package/src/handlers/sdp/MediaSection.ts +0 -836
- package/src/handlers/sdp/RemoteSdp.ts +0 -474
- package/src/handlers/sdp/commonUtils.ts +0 -259
- package/src/handlers/sdp/plainRtpUtils.ts +0 -58
- package/src/handlers/sdp/planBUtils.ts +0 -227
- package/src/handlers/sdp/unifiedPlanUtils.ts +0 -188
- package/src/index.ts +0 -28
- package/src/ortc.ts +0 -1169
- package/src/scalabilityModes.ts +0 -27
- package/src/types.ts +0 -11
- package/src/utils.ts +0 -18
- package/test/fakeParameters.js +0 -701
- package/test/test.js +0 -1672
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,1270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* This test runs in Node so no browser auto-detection is done. Instead, a
|
|
4
|
+
* FakeHandler device is used.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
23
|
+
if (mod && mod.__esModule) return mod;
|
|
24
|
+
var result = {};
|
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
26
|
+
__setModuleDefault(result, mod);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
const sdpTransform = __importStar(require("sdp-transform"));
|
|
31
|
+
const fake_mediastreamtrack_1 = require("fake-mediastreamtrack");
|
|
32
|
+
const mediasoupClient = __importStar(require("../"));
|
|
33
|
+
const errors_1 = require("../errors");
|
|
34
|
+
const utils = __importStar(require("../utils"));
|
|
35
|
+
const RemoteSdp_1 = require("../handlers/sdp/RemoteSdp");
|
|
36
|
+
const FakeHandler_1 = require("../handlers/FakeHandler");
|
|
37
|
+
const fakeParameters = __importStar(require("./fakeParameters"));
|
|
38
|
+
const { Device, detectDevice, parseScalabilityMode, debug } = mediasoupClient;
|
|
39
|
+
let device;
|
|
40
|
+
let sendTransport;
|
|
41
|
+
let recvTransport;
|
|
42
|
+
let audioProducer;
|
|
43
|
+
let videoProducer;
|
|
44
|
+
let audioConsumer;
|
|
45
|
+
let videoConsumer;
|
|
46
|
+
let dataProducer;
|
|
47
|
+
let dataConsumer;
|
|
48
|
+
test('mediasoup-client exposes debug dependency', () => {
|
|
49
|
+
expect(typeof debug).toBe('function');
|
|
50
|
+
}, 500);
|
|
51
|
+
test('detectDevice() returns nothing in Node', () => {
|
|
52
|
+
expect(detectDevice()).toBe(undefined);
|
|
53
|
+
}, 500);
|
|
54
|
+
test('create a Device in Node without custom handlerName/handlerFactory throws UnsupportedError', () => {
|
|
55
|
+
expect(() => new Device())
|
|
56
|
+
.toThrow(errors_1.UnsupportedError);
|
|
57
|
+
}, 500);
|
|
58
|
+
test('create a Device with an unknown handlerName string throws TypeError', () => {
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
expect(() => new Device({ handlerName: 'FooBrowser666' }))
|
|
61
|
+
.toThrow(TypeError);
|
|
62
|
+
}, 500);
|
|
63
|
+
test('create a Device in Node with a valid handlerFactory succeeds', () => {
|
|
64
|
+
device = new Device({ handlerFactory: FakeHandler_1.FakeHandler.createFactory(fakeParameters) });
|
|
65
|
+
expect(typeof device).toBe('object');
|
|
66
|
+
expect(device.handlerName).toBe('FakeHandler');
|
|
67
|
+
expect(device.loaded).toBe(false);
|
|
68
|
+
}, 500);
|
|
69
|
+
test('device.rtpCapabilities getter throws InvalidStateError if not loaded', () => {
|
|
70
|
+
expect(() => device.rtpCapabilities)
|
|
71
|
+
.toThrow(errors_1.InvalidStateError);
|
|
72
|
+
}, 500);
|
|
73
|
+
test('device.sctpCapabilities getter throws InvalidStateError if not loaded', () => {
|
|
74
|
+
expect(() => device.sctpCapabilities)
|
|
75
|
+
.toThrow(errors_1.InvalidStateError);
|
|
76
|
+
}, 500);
|
|
77
|
+
test('device.canProduce() throws InvalidStateError if not loaded', () => {
|
|
78
|
+
expect(() => device.canProduce('audio'))
|
|
79
|
+
.toThrow(errors_1.InvalidStateError);
|
|
80
|
+
}, 500);
|
|
81
|
+
test('device.createSendTransport() throws InvalidStateError if not loaded', () => {
|
|
82
|
+
const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
|
|
83
|
+
expect(() => device.createSendTransport({
|
|
84
|
+
id,
|
|
85
|
+
iceParameters,
|
|
86
|
+
iceCandidates,
|
|
87
|
+
dtlsParameters,
|
|
88
|
+
sctpParameters
|
|
89
|
+
}))
|
|
90
|
+
.toThrow(errors_1.InvalidStateError);
|
|
91
|
+
}, 500);
|
|
92
|
+
test('device.load() without routerRtpCapabilities rejects with TypeError', async () => {
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
await expect(device.load({}))
|
|
95
|
+
.rejects
|
|
96
|
+
.toThrow(TypeError);
|
|
97
|
+
expect(device.loaded).toBe(false);
|
|
98
|
+
}, 500);
|
|
99
|
+
test('device.load() with invalid routerRtpCapabilities rejects with TypeError', async () => {
|
|
100
|
+
// Clonse fake router RTP capabilities to make them invalid.
|
|
101
|
+
const routerRtpCapabilities = utils.clone(fakeParameters.generateRouterRtpCapabilities(), {});
|
|
102
|
+
for (const codec of routerRtpCapabilities.codecs) {
|
|
103
|
+
delete codec.mimeType;
|
|
104
|
+
}
|
|
105
|
+
await expect(device.load({ routerRtpCapabilities }))
|
|
106
|
+
.rejects
|
|
107
|
+
.toThrow(TypeError);
|
|
108
|
+
expect(device.loaded).toBe(false);
|
|
109
|
+
}, 500);
|
|
110
|
+
test('device.load() succeeds', async () => {
|
|
111
|
+
// Assume we get the router RTP capabilities.
|
|
112
|
+
const routerRtpCapabilities = fakeParameters.generateRouterRtpCapabilities();
|
|
113
|
+
await expect(device.load({ routerRtpCapabilities }))
|
|
114
|
+
.resolves
|
|
115
|
+
.toBe(undefined);
|
|
116
|
+
expect(device.loaded).toBe(true);
|
|
117
|
+
}, 500);
|
|
118
|
+
test('device.load() rejects with InvalidStateError if already loaded', async () => {
|
|
119
|
+
// @ts-ignore
|
|
120
|
+
await expect(device.load({}))
|
|
121
|
+
.rejects
|
|
122
|
+
.toThrow(errors_1.InvalidStateError);
|
|
123
|
+
expect(device.loaded).toBe(true);
|
|
124
|
+
}, 500);
|
|
125
|
+
test('device.rtpCapabilities getter succeeds', () => {
|
|
126
|
+
expect(typeof device.rtpCapabilities).toBe('object');
|
|
127
|
+
}, 500);
|
|
128
|
+
test('device.sctpCapabilities getter succeeds', () => {
|
|
129
|
+
expect(typeof device.sctpCapabilities).toBe('object');
|
|
130
|
+
}, 500);
|
|
131
|
+
test('device.canProduce() with "audio"/"video" kind returns true', () => {
|
|
132
|
+
expect(device.canProduce('audio')).toBe(true);
|
|
133
|
+
expect(device.canProduce('video')).toBe(true);
|
|
134
|
+
}, 500);
|
|
135
|
+
test('device.canProduce() with invalid kind throws TypeError', () => {
|
|
136
|
+
// @ts-ignore
|
|
137
|
+
expect(() => device.canProduce('chicken'))
|
|
138
|
+
.toThrow(TypeError);
|
|
139
|
+
}, 500);
|
|
140
|
+
test('device.createSendTransport() for sending media succeeds', () => {
|
|
141
|
+
// Assume we create a transport in the server and get its remote parameters.
|
|
142
|
+
const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
|
|
143
|
+
sendTransport = device.createSendTransport({
|
|
144
|
+
id,
|
|
145
|
+
iceParameters,
|
|
146
|
+
iceCandidates,
|
|
147
|
+
dtlsParameters,
|
|
148
|
+
sctpParameters,
|
|
149
|
+
appData: { baz: 'BAZ' }
|
|
150
|
+
});
|
|
151
|
+
expect(typeof sendTransport).toBe('object');
|
|
152
|
+
expect(sendTransport.id).toBe(id);
|
|
153
|
+
expect(sendTransport.closed).toBe(false);
|
|
154
|
+
expect(sendTransport.direction).toBe('send');
|
|
155
|
+
expect(typeof sendTransport.handler).toBe('object');
|
|
156
|
+
expect(sendTransport.handler instanceof FakeHandler_1.FakeHandler).toBe(true);
|
|
157
|
+
expect(sendTransport.connectionState).toBe('new');
|
|
158
|
+
expect(sendTransport.appData).toEqual({ baz: 'BAZ' });
|
|
159
|
+
}, 500);
|
|
160
|
+
test('device.createRecvTransport() for receiving media succeeds', () => {
|
|
161
|
+
// Assume we create a transport in the server and get its remote parameters.
|
|
162
|
+
const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
|
|
163
|
+
recvTransport = device.createRecvTransport({
|
|
164
|
+
id,
|
|
165
|
+
iceParameters,
|
|
166
|
+
iceCandidates,
|
|
167
|
+
dtlsParameters,
|
|
168
|
+
sctpParameters
|
|
169
|
+
});
|
|
170
|
+
expect(typeof recvTransport).toBe('object');
|
|
171
|
+
expect(recvTransport.id).toBe(id);
|
|
172
|
+
expect(recvTransport.closed).toBe(false);
|
|
173
|
+
expect(recvTransport.direction).toBe('recv');
|
|
174
|
+
expect(typeof recvTransport.handler).toBe('object');
|
|
175
|
+
expect(recvTransport.handler instanceof FakeHandler_1.FakeHandler).toBe(true);
|
|
176
|
+
expect(recvTransport.connectionState).toBe('new');
|
|
177
|
+
expect(recvTransport.appData).toEqual({});
|
|
178
|
+
}, 500);
|
|
179
|
+
test('device.createSendTransport() with missing remote Transport parameters throws TypeError', () => {
|
|
180
|
+
// @ts-ignore
|
|
181
|
+
expect(() => device.createSendTransport({ id: '1234' }))
|
|
182
|
+
.toThrow(TypeError);
|
|
183
|
+
// @ts-ignore
|
|
184
|
+
expect(() => device.createSendTransport({ id: '1234', iceParameters: {} }))
|
|
185
|
+
.toThrow(TypeError);
|
|
186
|
+
expect(() => device.createSendTransport({
|
|
187
|
+
id: '1234',
|
|
188
|
+
// @ts-ignore
|
|
189
|
+
iceParameters: {},
|
|
190
|
+
iceCandidates: []
|
|
191
|
+
}))
|
|
192
|
+
.toThrow(TypeError);
|
|
193
|
+
}, 500);
|
|
194
|
+
test('device.createRecvTransport() with a non object appData throws TypeError', () => {
|
|
195
|
+
const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
|
|
196
|
+
expect(() => device.createRecvTransport({
|
|
197
|
+
id,
|
|
198
|
+
iceParameters,
|
|
199
|
+
iceCandidates,
|
|
200
|
+
dtlsParameters,
|
|
201
|
+
sctpParameters,
|
|
202
|
+
// @ts-ignore
|
|
203
|
+
appData: 1234
|
|
204
|
+
}))
|
|
205
|
+
.toThrow(TypeError);
|
|
206
|
+
}, 500);
|
|
207
|
+
test('transport.produce() without "connect" listener rejects', async () => {
|
|
208
|
+
const audioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
209
|
+
await expect(sendTransport.produce({ track: audioTrack }))
|
|
210
|
+
.rejects
|
|
211
|
+
.toThrow(Error);
|
|
212
|
+
}, 500);
|
|
213
|
+
test('transport.produce() succeeds', async () => {
|
|
214
|
+
var _a, _b;
|
|
215
|
+
const audioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
216
|
+
const videoTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'video' });
|
|
217
|
+
let audioProducerId;
|
|
218
|
+
let videoProducerId;
|
|
219
|
+
let connectEventNumTimesCalled = 0;
|
|
220
|
+
let produceEventNumTimesCalled = 0;
|
|
221
|
+
// eslint-disable-next-line no-unused-vars
|
|
222
|
+
sendTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => {
|
|
223
|
+
connectEventNumTimesCalled++;
|
|
224
|
+
expect(typeof dtlsParameters).toBe('object');
|
|
225
|
+
// Emulate communication with the server and success response (no response
|
|
226
|
+
// data needed).
|
|
227
|
+
setTimeout(callback);
|
|
228
|
+
});
|
|
229
|
+
// eslint-disable-next-line no-unused-vars
|
|
230
|
+
sendTransport.on('produce', ({ kind, rtpParameters, appData }, callback /* errback */) => {
|
|
231
|
+
produceEventNumTimesCalled++;
|
|
232
|
+
expect(typeof kind).toBe('string');
|
|
233
|
+
expect(typeof rtpParameters).toBe('object');
|
|
234
|
+
let id;
|
|
235
|
+
switch (kind) {
|
|
236
|
+
case 'audio':
|
|
237
|
+
{
|
|
238
|
+
expect(appData).toEqual({ foo: 'FOO' });
|
|
239
|
+
id = fakeParameters.generateProducerRemoteParameters().id;
|
|
240
|
+
audioProducerId = id;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case 'video':
|
|
244
|
+
{
|
|
245
|
+
expect(appData).toEqual({});
|
|
246
|
+
id = fakeParameters.generateProducerRemoteParameters().id;
|
|
247
|
+
videoProducerId = id;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
default:
|
|
251
|
+
{
|
|
252
|
+
throw new Error('unknown kind');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Emulate communication with the server and success response with Producer
|
|
256
|
+
// remote parameters.
|
|
257
|
+
setTimeout(() => callback({ id }));
|
|
258
|
+
});
|
|
259
|
+
let codecs;
|
|
260
|
+
let headerExtensions;
|
|
261
|
+
let encodings;
|
|
262
|
+
let rtcp;
|
|
263
|
+
// Pause the audio track before creating its Producer.
|
|
264
|
+
audioTrack.enabled = false;
|
|
265
|
+
// Use stopTracks: false.
|
|
266
|
+
audioProducer = await sendTransport.produce({ track: audioTrack, stopTracks: false, appData: { foo: 'FOO' } });
|
|
267
|
+
expect(connectEventNumTimesCalled).toBe(1);
|
|
268
|
+
expect(produceEventNumTimesCalled).toBe(1);
|
|
269
|
+
expect(typeof audioProducer).toBe('object');
|
|
270
|
+
expect(audioProducer.id).toBe(audioProducerId);
|
|
271
|
+
expect(audioProducer.closed).toBe(false);
|
|
272
|
+
expect(audioProducer.kind).toBe('audio');
|
|
273
|
+
expect(audioProducer.track).toBe(audioTrack);
|
|
274
|
+
expect(typeof audioProducer.rtpParameters).toBe('object');
|
|
275
|
+
expect(typeof audioProducer.rtpParameters.mid).toBe('string');
|
|
276
|
+
expect(audioProducer.rtpParameters.codecs.length).toBe(1);
|
|
277
|
+
codecs = audioProducer.rtpParameters.codecs;
|
|
278
|
+
expect(codecs[0]).toEqual({
|
|
279
|
+
mimeType: 'audio/opus',
|
|
280
|
+
payloadType: 111,
|
|
281
|
+
clockRate: 48000,
|
|
282
|
+
channels: 2,
|
|
283
|
+
rtcpFeedback: [
|
|
284
|
+
{ type: 'transport-cc', parameter: '' }
|
|
285
|
+
],
|
|
286
|
+
parameters: {
|
|
287
|
+
minptime: 10,
|
|
288
|
+
useinbandfec: 1
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
headerExtensions = audioProducer.rtpParameters.headerExtensions;
|
|
292
|
+
expect(headerExtensions).toEqual([
|
|
293
|
+
{
|
|
294
|
+
uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
|
|
295
|
+
id: 1,
|
|
296
|
+
encrypt: false,
|
|
297
|
+
parameters: {}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
|
|
301
|
+
id: 10,
|
|
302
|
+
encrypt: false,
|
|
303
|
+
parameters: {}
|
|
304
|
+
}
|
|
305
|
+
]);
|
|
306
|
+
encodings = audioProducer.rtpParameters.encodings;
|
|
307
|
+
expect(Array.isArray(encodings)).toBe(true);
|
|
308
|
+
expect(encodings.length).toBe(1);
|
|
309
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
|
|
310
|
+
expect(Object.keys(encodings[0])).toEqual(['ssrc', 'dtx']);
|
|
311
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].ssrc)).toBe('number');
|
|
312
|
+
rtcp = audioProducer.rtpParameters.rtcp;
|
|
313
|
+
expect(typeof rtcp).toBe('object');
|
|
314
|
+
expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
|
|
315
|
+
expect(audioProducer.paused).toBe(true);
|
|
316
|
+
expect(audioProducer.maxSpatialLayer).toBe(undefined);
|
|
317
|
+
expect(audioProducer.appData).toEqual({ foo: 'FOO' });
|
|
318
|
+
// Reset the audio paused state.
|
|
319
|
+
audioProducer.resume();
|
|
320
|
+
const videoEncodings = [
|
|
321
|
+
{ maxBitrate: 100000 },
|
|
322
|
+
{ maxBitrate: 500000 }
|
|
323
|
+
];
|
|
324
|
+
// Note that stopTracks is not give so it's true by default.
|
|
325
|
+
// Use disableTrackOnPause: false and zeroRtpOnPause: true
|
|
326
|
+
videoProducer = await sendTransport.produce({
|
|
327
|
+
track: videoTrack,
|
|
328
|
+
encodings: videoEncodings,
|
|
329
|
+
disableTrackOnPause: false,
|
|
330
|
+
zeroRtpOnPause: true
|
|
331
|
+
});
|
|
332
|
+
expect(connectEventNumTimesCalled).toBe(1);
|
|
333
|
+
expect(produceEventNumTimesCalled).toBe(2);
|
|
334
|
+
expect(typeof videoProducer).toBe('object');
|
|
335
|
+
expect(videoProducer.id).toBe(videoProducerId);
|
|
336
|
+
expect(videoProducer.closed).toBe(false);
|
|
337
|
+
expect(videoProducer.kind).toBe('video');
|
|
338
|
+
expect(videoProducer.track).toBe(videoTrack);
|
|
339
|
+
expect(typeof videoProducer.rtpParameters).toBe('object');
|
|
340
|
+
expect(typeof videoProducer.rtpParameters.mid).toBe('string');
|
|
341
|
+
expect(videoProducer.rtpParameters.codecs.length).toBe(2);
|
|
342
|
+
codecs = videoProducer.rtpParameters.codecs;
|
|
343
|
+
expect(codecs[0]).toEqual({
|
|
344
|
+
mimeType: 'video/VP8',
|
|
345
|
+
payloadType: 96,
|
|
346
|
+
clockRate: 90000,
|
|
347
|
+
rtcpFeedback: [
|
|
348
|
+
{ type: 'goog-remb', parameter: '' },
|
|
349
|
+
{ type: 'transport-cc', parameter: '' },
|
|
350
|
+
{ type: 'ccm', parameter: 'fir' },
|
|
351
|
+
{ type: 'nack', parameter: '' },
|
|
352
|
+
{ type: 'nack', parameter: 'pli' }
|
|
353
|
+
],
|
|
354
|
+
parameters: {
|
|
355
|
+
baz: '1234abcd'
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
expect(codecs[1]).toEqual({
|
|
359
|
+
mimeType: 'video/rtx',
|
|
360
|
+
payloadType: 97,
|
|
361
|
+
clockRate: 90000,
|
|
362
|
+
rtcpFeedback: [],
|
|
363
|
+
parameters: {
|
|
364
|
+
apt: 96
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
headerExtensions = videoProducer.rtpParameters.headerExtensions;
|
|
368
|
+
expect(headerExtensions).toEqual([
|
|
369
|
+
{
|
|
370
|
+
uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
|
|
371
|
+
id: 1,
|
|
372
|
+
encrypt: false,
|
|
373
|
+
parameters: {}
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
|
|
377
|
+
id: 3,
|
|
378
|
+
encrypt: false,
|
|
379
|
+
parameters: {}
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
|
|
383
|
+
id: 5,
|
|
384
|
+
encrypt: false,
|
|
385
|
+
parameters: {}
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
uri: 'urn:3gpp:video-orientation',
|
|
389
|
+
id: 4,
|
|
390
|
+
encrypt: false,
|
|
391
|
+
parameters: {}
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
uri: 'urn:ietf:params:rtp-hdrext:toffset',
|
|
395
|
+
id: 2,
|
|
396
|
+
encrypt: false,
|
|
397
|
+
parameters: {}
|
|
398
|
+
}
|
|
399
|
+
]);
|
|
400
|
+
encodings = videoProducer.rtpParameters.encodings;
|
|
401
|
+
expect(Array.isArray(encodings)).toBe(true);
|
|
402
|
+
expect(encodings.length).toBe(2);
|
|
403
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
|
|
404
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].ssrc)).toBe('number');
|
|
405
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx)).toBe('object');
|
|
406
|
+
expect(Object.keys(encodings[0].rtx)).toEqual(['ssrc']);
|
|
407
|
+
expect(typeof ((_a = encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx) === null || _a === void 0 ? void 0 : _a.ssrc)).toBe('number');
|
|
408
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[1])).toBe('object');
|
|
409
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[1].ssrc)).toBe('number');
|
|
410
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[1].rtx)).toBe('object');
|
|
411
|
+
expect(Object.keys(encodings[1].rtx)).toEqual(['ssrc']);
|
|
412
|
+
expect(typeof ((_b = encodings === null || encodings === void 0 ? void 0 : encodings[1].rtx) === null || _b === void 0 ? void 0 : _b.ssrc)).toBe('number');
|
|
413
|
+
rtcp = videoProducer.rtpParameters.rtcp;
|
|
414
|
+
expect(typeof rtcp).toBe('object');
|
|
415
|
+
expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
|
|
416
|
+
expect(videoProducer.paused).toBe(false);
|
|
417
|
+
expect(videoProducer.maxSpatialLayer).toBe(undefined);
|
|
418
|
+
expect(videoProducer.appData).toEqual({});
|
|
419
|
+
sendTransport.removeAllListeners('connect');
|
|
420
|
+
sendTransport.removeAllListeners('produce');
|
|
421
|
+
}, 500);
|
|
422
|
+
test('transport.produce() without track rejects with TypeError', async () => {
|
|
423
|
+
await expect(sendTransport.produce({}))
|
|
424
|
+
.rejects
|
|
425
|
+
.toThrow(TypeError);
|
|
426
|
+
}, 500);
|
|
427
|
+
test('transport.produce() in a receiving Transport rejects with UnsupportedError', async () => {
|
|
428
|
+
const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
429
|
+
await expect(recvTransport.produce({ track }))
|
|
430
|
+
.rejects
|
|
431
|
+
.toThrow(errors_1.UnsupportedError);
|
|
432
|
+
}, 500);
|
|
433
|
+
test('transport.produce() with an ended track rejects with InvalidStateError', async () => {
|
|
434
|
+
const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
435
|
+
track.stop();
|
|
436
|
+
await expect(sendTransport.produce({ track }))
|
|
437
|
+
.rejects
|
|
438
|
+
.toThrow(errors_1.InvalidStateError);
|
|
439
|
+
}, 500);
|
|
440
|
+
test('transport.produce() with a non object appData rejects with TypeError', async () => {
|
|
441
|
+
const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
442
|
+
// @ts-ignore
|
|
443
|
+
await expect(sendTransport.produce({ track, appData: true }))
|
|
444
|
+
.rejects
|
|
445
|
+
.toThrow(TypeError);
|
|
446
|
+
}, 500);
|
|
447
|
+
test('transport.consume() succeeds', async () => {
|
|
448
|
+
var _a;
|
|
449
|
+
const audioConsumerRemoteParameters = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' });
|
|
450
|
+
const videoConsumerRemoteParameters = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' });
|
|
451
|
+
let connectEventNumTimesCalled = 0;
|
|
452
|
+
// eslint-disable-next-line no-unused-vars
|
|
453
|
+
recvTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => {
|
|
454
|
+
connectEventNumTimesCalled++;
|
|
455
|
+
expect(typeof dtlsParameters).toBe('object');
|
|
456
|
+
// Emulate communication with the server and success response (no response
|
|
457
|
+
// data needed).
|
|
458
|
+
setTimeout(callback);
|
|
459
|
+
});
|
|
460
|
+
let codecs;
|
|
461
|
+
let headerExtensions;
|
|
462
|
+
let encodings;
|
|
463
|
+
let rtcp;
|
|
464
|
+
audioConsumer = await recvTransport.consume({
|
|
465
|
+
id: audioConsumerRemoteParameters.id,
|
|
466
|
+
producerId: audioConsumerRemoteParameters.producerId,
|
|
467
|
+
kind: audioConsumerRemoteParameters.kind,
|
|
468
|
+
rtpParameters: audioConsumerRemoteParameters.rtpParameters,
|
|
469
|
+
appData: { bar: 'BAR' }
|
|
470
|
+
});
|
|
471
|
+
expect(connectEventNumTimesCalled).toBe(1);
|
|
472
|
+
expect(typeof audioConsumer).toBe('object');
|
|
473
|
+
expect(audioConsumer.id).toBe(audioConsumerRemoteParameters.id);
|
|
474
|
+
expect(audioConsumer.producerId).toBe(audioConsumerRemoteParameters.producerId);
|
|
475
|
+
expect(audioConsumer.closed).toBe(false);
|
|
476
|
+
expect(audioConsumer.kind).toBe('audio');
|
|
477
|
+
expect(typeof audioConsumer.track).toBe('object');
|
|
478
|
+
expect(typeof audioConsumer.rtpParameters).toBe('object');
|
|
479
|
+
expect(audioConsumer.rtpParameters.mid).toBe(undefined);
|
|
480
|
+
expect(audioConsumer.rtpParameters.codecs.length).toBe(1);
|
|
481
|
+
codecs = audioConsumer.rtpParameters.codecs;
|
|
482
|
+
expect(codecs[0]).toEqual({
|
|
483
|
+
mimeType: 'audio/opus',
|
|
484
|
+
payloadType: 100,
|
|
485
|
+
clockRate: 48000,
|
|
486
|
+
channels: 2,
|
|
487
|
+
rtcpFeedback: [
|
|
488
|
+
{ type: 'transport-cc', parameter: '' }
|
|
489
|
+
],
|
|
490
|
+
parameters: {
|
|
491
|
+
useinbandfec: 1,
|
|
492
|
+
foo: 'bar'
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
headerExtensions = audioConsumer.rtpParameters.headerExtensions;
|
|
496
|
+
expect(headerExtensions).toEqual([
|
|
497
|
+
{
|
|
498
|
+
uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
|
|
499
|
+
id: 1,
|
|
500
|
+
encrypt: false,
|
|
501
|
+
parameters: {}
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
|
|
505
|
+
id: 5,
|
|
506
|
+
encrypt: false,
|
|
507
|
+
parameters: {}
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
|
|
511
|
+
id: 10,
|
|
512
|
+
encrypt: false,
|
|
513
|
+
parameters: {}
|
|
514
|
+
}
|
|
515
|
+
]);
|
|
516
|
+
encodings = audioConsumer.rtpParameters.encodings;
|
|
517
|
+
expect(Array.isArray(encodings)).toBe(true);
|
|
518
|
+
expect(encodings.length).toBe(1);
|
|
519
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
|
|
520
|
+
expect(Object.keys(encodings[0])).toEqual(['ssrc', 'dtx']);
|
|
521
|
+
expect(typeof encodings[0].ssrc).toBe('number');
|
|
522
|
+
rtcp = audioProducer.rtpParameters.rtcp;
|
|
523
|
+
expect(typeof rtcp).toBe('object');
|
|
524
|
+
expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
|
|
525
|
+
expect(audioConsumer.paused).toBe(false);
|
|
526
|
+
expect(audioConsumer.appData).toEqual({ bar: 'BAR' });
|
|
527
|
+
videoConsumer = await recvTransport.consume({
|
|
528
|
+
id: videoConsumerRemoteParameters.id,
|
|
529
|
+
producerId: videoConsumerRemoteParameters.producerId,
|
|
530
|
+
kind: videoConsumerRemoteParameters.kind,
|
|
531
|
+
rtpParameters: videoConsumerRemoteParameters.rtpParameters
|
|
532
|
+
});
|
|
533
|
+
expect(connectEventNumTimesCalled).toBe(1);
|
|
534
|
+
expect(typeof videoConsumer).toBe('object');
|
|
535
|
+
expect(videoConsumer.id).toBe(videoConsumerRemoteParameters.id);
|
|
536
|
+
expect(videoConsumer.producerId).toBe(videoConsumerRemoteParameters.producerId);
|
|
537
|
+
expect(videoConsumer.closed).toBe(false);
|
|
538
|
+
expect(videoConsumer.kind).toBe('video');
|
|
539
|
+
expect(typeof videoConsumer.track).toBe('object');
|
|
540
|
+
expect(typeof videoConsumer.rtpParameters).toBe('object');
|
|
541
|
+
expect(videoConsumer.rtpParameters.mid).toBe(undefined);
|
|
542
|
+
expect(videoConsumer.rtpParameters.codecs.length).toBe(2);
|
|
543
|
+
codecs = videoConsumer.rtpParameters.codecs;
|
|
544
|
+
expect(codecs[0]).toEqual({
|
|
545
|
+
mimeType: 'video/VP8',
|
|
546
|
+
payloadType: 101,
|
|
547
|
+
clockRate: 90000,
|
|
548
|
+
rtcpFeedback: [
|
|
549
|
+
{ type: 'nack', parameter: '' },
|
|
550
|
+
{ type: 'nack', parameter: 'pli' },
|
|
551
|
+
{ type: 'ccm', parameter: 'fir' },
|
|
552
|
+
{ type: 'goog-remb', parameter: '' },
|
|
553
|
+
{ type: 'transport-cc', parameter: '' }
|
|
554
|
+
],
|
|
555
|
+
parameters: {
|
|
556
|
+
'x-google-start-bitrate': 1500
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
expect(codecs[1]).toEqual({
|
|
560
|
+
mimeType: 'video/rtx',
|
|
561
|
+
payloadType: 102,
|
|
562
|
+
clockRate: 90000,
|
|
563
|
+
rtcpFeedback: [],
|
|
564
|
+
parameters: {
|
|
565
|
+
apt: 101
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
headerExtensions = videoConsumer.rtpParameters.headerExtensions;
|
|
569
|
+
expect(headerExtensions).toEqual([
|
|
570
|
+
{
|
|
571
|
+
uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
|
|
572
|
+
id: 1,
|
|
573
|
+
encrypt: false,
|
|
574
|
+
parameters: {}
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
|
|
578
|
+
id: 4,
|
|
579
|
+
encrypt: false,
|
|
580
|
+
parameters: {}
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
|
|
584
|
+
id: 5,
|
|
585
|
+
encrypt: false,
|
|
586
|
+
parameters: {}
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
uri: 'urn:3gpp:video-orientation',
|
|
590
|
+
id: 11,
|
|
591
|
+
encrypt: false,
|
|
592
|
+
parameters: {}
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
uri: 'urn:ietf:params:rtp-hdrext:toffset',
|
|
596
|
+
id: 12,
|
|
597
|
+
encrypt: false,
|
|
598
|
+
parameters: {}
|
|
599
|
+
}
|
|
600
|
+
]);
|
|
601
|
+
encodings = videoConsumer.rtpParameters.encodings;
|
|
602
|
+
expect(Array.isArray(encodings)).toBe(true);
|
|
603
|
+
expect(encodings.length).toBe(1);
|
|
604
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
|
|
605
|
+
expect(Object.keys(encodings[0])).toEqual(['ssrc', 'rtx', 'dtx']);
|
|
606
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].ssrc)).toBe('number');
|
|
607
|
+
expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx)).toBe('object');
|
|
608
|
+
expect(Object.keys(encodings[0].rtx)).toEqual(['ssrc']);
|
|
609
|
+
expect(typeof ((_a = encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx) === null || _a === void 0 ? void 0 : _a.ssrc)).toBe('number');
|
|
610
|
+
rtcp = videoConsumer.rtpParameters.rtcp;
|
|
611
|
+
expect(typeof rtcp).toBe('object');
|
|
612
|
+
expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
|
|
613
|
+
expect(videoConsumer.paused).toBe(false);
|
|
614
|
+
expect(videoConsumer.appData).toEqual({});
|
|
615
|
+
recvTransport.removeAllListeners('connect');
|
|
616
|
+
}, 500);
|
|
617
|
+
test('transport.consume() without remote Consumer parameters rejects with TypeError', async () => {
|
|
618
|
+
// @ts-ignore
|
|
619
|
+
await expect(recvTransport.consume({}))
|
|
620
|
+
.rejects
|
|
621
|
+
.toThrow(TypeError);
|
|
622
|
+
}, 500);
|
|
623
|
+
test('transport.consume() with missing remote Consumer parameters rejects with TypeError', async () => {
|
|
624
|
+
// @ts-ignore
|
|
625
|
+
await expect(recvTransport.consume({ id: '1234' }))
|
|
626
|
+
.rejects
|
|
627
|
+
.toThrow(TypeError);
|
|
628
|
+
// @ts-ignore
|
|
629
|
+
await expect(recvTransport.consume({ id: '1234', producerId: '4444' }))
|
|
630
|
+
.rejects
|
|
631
|
+
.toThrow(TypeError);
|
|
632
|
+
await expect(recvTransport.consume(
|
|
633
|
+
// @ts-ignore
|
|
634
|
+
{
|
|
635
|
+
id: '1234',
|
|
636
|
+
producerId: '4444',
|
|
637
|
+
kind: 'audio'
|
|
638
|
+
}))
|
|
639
|
+
.rejects
|
|
640
|
+
.toThrow(TypeError);
|
|
641
|
+
await expect(recvTransport.consume(
|
|
642
|
+
// @ts-ignore
|
|
643
|
+
{
|
|
644
|
+
id: '1234',
|
|
645
|
+
producerId: '4444',
|
|
646
|
+
kind: 'audio'
|
|
647
|
+
}))
|
|
648
|
+
.rejects
|
|
649
|
+
.toThrow(TypeError);
|
|
650
|
+
}, 500);
|
|
651
|
+
test('transport.consume() in a sending Transport rejects with UnsupportedError', async () => {
|
|
652
|
+
const { id, producerId, kind, rtpParameters } = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' });
|
|
653
|
+
await expect(sendTransport.consume({
|
|
654
|
+
id,
|
|
655
|
+
producerId,
|
|
656
|
+
kind,
|
|
657
|
+
rtpParameters
|
|
658
|
+
}))
|
|
659
|
+
.rejects
|
|
660
|
+
.toThrow(errors_1.UnsupportedError);
|
|
661
|
+
}, 500);
|
|
662
|
+
test('transport.consume() with unsupported rtpParameters rejects with UnsupportedError', async () => {
|
|
663
|
+
const { id, producerId, kind, rtpParameters } = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/ISAC' });
|
|
664
|
+
await expect(sendTransport.consume({
|
|
665
|
+
id,
|
|
666
|
+
producerId,
|
|
667
|
+
kind,
|
|
668
|
+
rtpParameters
|
|
669
|
+
}))
|
|
670
|
+
.rejects
|
|
671
|
+
.toThrow(errors_1.UnsupportedError);
|
|
672
|
+
}, 500);
|
|
673
|
+
test('transport.consume() with a non object appData rejects with TypeError', async () => {
|
|
674
|
+
const consumerRemoteParameters = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' });
|
|
675
|
+
// @ts-ignore
|
|
676
|
+
await expect(recvTransport.consume({ consumerRemoteParameters, appData: true }))
|
|
677
|
+
.rejects
|
|
678
|
+
.toThrow(TypeError);
|
|
679
|
+
}, 500);
|
|
680
|
+
test('transport.produceData() succeeds', async () => {
|
|
681
|
+
let dataProducerId;
|
|
682
|
+
let produceDataEventNumTimesCalled = 0;
|
|
683
|
+
// eslint-disable-next-line no-unused-vars
|
|
684
|
+
sendTransport.on('producedata', ({ sctpStreamParameters, label, protocol, appData }, callback /* errback */) => {
|
|
685
|
+
produceDataEventNumTimesCalled++;
|
|
686
|
+
expect(typeof sctpStreamParameters).toBe('object');
|
|
687
|
+
expect(label).toBe('FOO');
|
|
688
|
+
expect(protocol).toBe('BAR');
|
|
689
|
+
expect(appData).toEqual({ foo: 'FOO' });
|
|
690
|
+
const id = fakeParameters.generateDataProducerRemoteParameters().id;
|
|
691
|
+
dataProducerId = id;
|
|
692
|
+
// Emulate communication with the server and success response with Producer
|
|
693
|
+
// remote parameters.
|
|
694
|
+
setTimeout(() => callback({ id }));
|
|
695
|
+
});
|
|
696
|
+
dataProducer = await sendTransport.produceData({
|
|
697
|
+
ordered: false,
|
|
698
|
+
maxPacketLifeTime: 5555,
|
|
699
|
+
label: 'FOO',
|
|
700
|
+
protocol: 'BAR',
|
|
701
|
+
appData: { foo: 'FOO' }
|
|
702
|
+
});
|
|
703
|
+
expect(produceDataEventNumTimesCalled).toBe(1);
|
|
704
|
+
expect(typeof dataProducer).toBe('object');
|
|
705
|
+
expect(dataProducer.id).toBe(dataProducerId);
|
|
706
|
+
expect(dataProducer.closed).toBe(false);
|
|
707
|
+
expect(typeof dataProducer.sctpStreamParameters).toBe('object');
|
|
708
|
+
expect(typeof dataProducer.sctpStreamParameters.streamId).toBe('number');
|
|
709
|
+
expect(dataProducer.sctpStreamParameters.ordered).toBe(false);
|
|
710
|
+
expect(dataProducer.sctpStreamParameters.maxPacketLifeTime).toBe(5555);
|
|
711
|
+
expect(dataProducer.sctpStreamParameters.maxRetransmits).toBe(undefined);
|
|
712
|
+
expect(dataProducer.label).toBe('FOO');
|
|
713
|
+
expect(dataProducer.protocol).toBe('BAR');
|
|
714
|
+
sendTransport.removeAllListeners('producedata');
|
|
715
|
+
}, 500);
|
|
716
|
+
test('transport.produceData() in a receiving Transport rejects with UnsupportedError', async () => {
|
|
717
|
+
await expect(recvTransport.produceData({}))
|
|
718
|
+
.rejects
|
|
719
|
+
.toThrow(errors_1.UnsupportedError);
|
|
720
|
+
}, 500);
|
|
721
|
+
test('transport.produceData() with a non object appData rejects with TypeError', async () => {
|
|
722
|
+
// @ts-ignore
|
|
723
|
+
await expect(sendTransport.produceData({ appData: true }))
|
|
724
|
+
.rejects
|
|
725
|
+
.toThrow(TypeError);
|
|
726
|
+
}, 500);
|
|
727
|
+
test('transport.consumeData() succeeds', async () => {
|
|
728
|
+
const dataConsumerRemoteParameters = fakeParameters.generateDataConsumerRemoteParameters();
|
|
729
|
+
dataConsumer = await recvTransport.consumeData({
|
|
730
|
+
id: dataConsumerRemoteParameters.id,
|
|
731
|
+
dataProducerId: dataConsumerRemoteParameters.dataProducerId,
|
|
732
|
+
sctpStreamParameters: dataConsumerRemoteParameters.sctpStreamParameters,
|
|
733
|
+
label: 'FOO',
|
|
734
|
+
protocol: 'BAR',
|
|
735
|
+
appData: { bar: 'BAR' }
|
|
736
|
+
});
|
|
737
|
+
expect(typeof dataConsumer).toBe('object');
|
|
738
|
+
expect(dataConsumer.id).toBe(dataConsumerRemoteParameters.id);
|
|
739
|
+
expect(dataConsumer.dataProducerId).toBe(dataConsumerRemoteParameters.dataProducerId);
|
|
740
|
+
expect(dataConsumer.closed).toBe(false);
|
|
741
|
+
expect(typeof dataConsumer.sctpStreamParameters).toBe('object');
|
|
742
|
+
expect(typeof dataConsumer.sctpStreamParameters.streamId).toBe('number');
|
|
743
|
+
expect(dataConsumer.label).toBe('FOO');
|
|
744
|
+
expect(dataConsumer.protocol).toBe('BAR');
|
|
745
|
+
}, 500);
|
|
746
|
+
test('transport.consumeData() without remote DataConsumer parameters rejects with TypeError', async () => {
|
|
747
|
+
// @ts-ignore
|
|
748
|
+
await expect(recvTransport.consumeData({}))
|
|
749
|
+
.rejects
|
|
750
|
+
.toThrow(TypeError);
|
|
751
|
+
}, 500);
|
|
752
|
+
test('transport.consumeData() with missing remote DataConsumer parameters rejects with TypeError', async () => {
|
|
753
|
+
// @ts-ignore
|
|
754
|
+
await expect(recvTransport.consumeData({ id: '1234' }))
|
|
755
|
+
.rejects
|
|
756
|
+
.toThrow(TypeError);
|
|
757
|
+
// @ts-ignore
|
|
758
|
+
await expect(recvTransport.consumeData({ id: '1234', dataProducerId: '4444' }))
|
|
759
|
+
.rejects
|
|
760
|
+
.toThrow(TypeError);
|
|
761
|
+
}, 500);
|
|
762
|
+
test('transport.consumeData() in a sending Transport rejects with UnsupportedError', async () => {
|
|
763
|
+
const { id, dataProducerId, sctpStreamParameters } = fakeParameters.generateDataConsumerRemoteParameters();
|
|
764
|
+
await expect(sendTransport.consumeData({
|
|
765
|
+
id,
|
|
766
|
+
dataProducerId,
|
|
767
|
+
sctpStreamParameters
|
|
768
|
+
}))
|
|
769
|
+
.rejects
|
|
770
|
+
.toThrow(errors_1.UnsupportedError);
|
|
771
|
+
}, 500);
|
|
772
|
+
test('transport.consumeData() with a non object appData rejects with TypeError', async () => {
|
|
773
|
+
const dataConsumerRemoteParameters = fakeParameters.generateDataConsumerRemoteParameters();
|
|
774
|
+
// @ts-ignore
|
|
775
|
+
await expect(recvTransport.consumeData({ dataConsumerRemoteParameters, appData: true }))
|
|
776
|
+
.rejects
|
|
777
|
+
.toThrow(TypeError);
|
|
778
|
+
}, 500);
|
|
779
|
+
test('transport.getStats() succeeds', async () => {
|
|
780
|
+
const stats = await sendTransport.getStats();
|
|
781
|
+
expect(typeof stats).toBe('object');
|
|
782
|
+
}, 500);
|
|
783
|
+
test('transport.restartIce() succeeds', async () => {
|
|
784
|
+
await expect(sendTransport.restartIce({
|
|
785
|
+
iceParameters: {
|
|
786
|
+
usernameFragment: 'foo',
|
|
787
|
+
password: 'xxx'
|
|
788
|
+
}
|
|
789
|
+
}))
|
|
790
|
+
.resolves
|
|
791
|
+
.toBe(undefined);
|
|
792
|
+
}, 500);
|
|
793
|
+
test('transport.restartIce() without remote iceParameters rejects with TypeError', async () => {
|
|
794
|
+
// @ts-ignore
|
|
795
|
+
await expect(sendTransport.restartIce({}))
|
|
796
|
+
.rejects
|
|
797
|
+
.toThrow(TypeError);
|
|
798
|
+
}, 500);
|
|
799
|
+
test('transport.updateIceServers() succeeds', async () => {
|
|
800
|
+
await expect(sendTransport.updateIceServers({ iceServers: [] }))
|
|
801
|
+
.resolves
|
|
802
|
+
.toBe(undefined);
|
|
803
|
+
}, 500);
|
|
804
|
+
test('transport.updateIceServers() without iceServers rejects with TypeError', async () => {
|
|
805
|
+
await expect(sendTransport.updateIceServers({}))
|
|
806
|
+
.rejects
|
|
807
|
+
.toThrow(TypeError);
|
|
808
|
+
}, 500);
|
|
809
|
+
test('transport.appData cannot be overridden', () => {
|
|
810
|
+
expect(() => (sendTransport.appData = { lalala: 'LALALA' }))
|
|
811
|
+
.toThrow(Error);
|
|
812
|
+
expect(sendTransport.appData).toEqual({ baz: 'BAZ' });
|
|
813
|
+
}, 500);
|
|
814
|
+
test('transport.appData can be modified', () => {
|
|
815
|
+
sendTransport.appData.lololo = 'LOLOLO';
|
|
816
|
+
recvTransport.appData.nanana = 'NANANA';
|
|
817
|
+
expect(sendTransport.appData).toEqual({ baz: 'BAZ', lololo: 'LOLOLO' });
|
|
818
|
+
expect(recvTransport.appData).toEqual({ nanana: 'NANANA' });
|
|
819
|
+
}, 500);
|
|
820
|
+
test('connection state change fires "connectionstatechange" in live Transport', () => {
|
|
821
|
+
let connectionStateChangeEventNumTimesCalled = 0;
|
|
822
|
+
sendTransport.on('connectionstatechange', (connectionState) => {
|
|
823
|
+
connectionStateChangeEventNumTimesCalled++;
|
|
824
|
+
expect(connectionState).toBe('completed');
|
|
825
|
+
});
|
|
826
|
+
// @ts-ignore
|
|
827
|
+
sendTransport.handler.setConnectionState('completed');
|
|
828
|
+
expect(connectionStateChangeEventNumTimesCalled).toBe(1);
|
|
829
|
+
expect(sendTransport.connectionState).toBe('completed');
|
|
830
|
+
sendTransport.removeAllListeners('connectionstatechange');
|
|
831
|
+
}, 500);
|
|
832
|
+
test('producer.pause() succeeds', () => {
|
|
833
|
+
var _a;
|
|
834
|
+
videoProducer.pause();
|
|
835
|
+
expect(videoProducer.paused).toBe(true);
|
|
836
|
+
// Track will be still enabled due to disableTrackOnPause: false.
|
|
837
|
+
expect((_a = videoProducer.track) === null || _a === void 0 ? void 0 : _a.enabled).toBe(true);
|
|
838
|
+
}, 500);
|
|
839
|
+
test('producer.resume() succeeds', () => {
|
|
840
|
+
var _a;
|
|
841
|
+
videoProducer.resume();
|
|
842
|
+
expect(videoProducer.paused).toBe(false);
|
|
843
|
+
expect((_a = videoProducer.track) === null || _a === void 0 ? void 0 : _a.enabled).toBe(true);
|
|
844
|
+
}, 500);
|
|
845
|
+
test('producer.replaceTrack() with a new track succeeds', async () => {
|
|
846
|
+
var _a;
|
|
847
|
+
// Have the audio Producer paused.
|
|
848
|
+
audioProducer.pause();
|
|
849
|
+
const audioProducerPreviousTrack = audioProducer.track;
|
|
850
|
+
const newAudioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
851
|
+
await expect(audioProducer.replaceTrack({ track: newAudioTrack }))
|
|
852
|
+
.resolves
|
|
853
|
+
.toBe(undefined);
|
|
854
|
+
// Previous track must be 'live' due to stopTracks: false.
|
|
855
|
+
expect(audioProducerPreviousTrack === null || audioProducerPreviousTrack === void 0 ? void 0 : audioProducerPreviousTrack.readyState).toBe('live');
|
|
856
|
+
expect((_a = audioProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
|
|
857
|
+
expect(audioProducer.track).not.toBe(audioProducerPreviousTrack);
|
|
858
|
+
expect(audioProducer.track).toBe(newAudioTrack);
|
|
859
|
+
// Producer was already paused.
|
|
860
|
+
expect(audioProducer.paused).toBe(true);
|
|
861
|
+
// Reset the audio paused state.
|
|
862
|
+
audioProducer.resume();
|
|
863
|
+
const videoProducerPreviousTrack = videoProducer.track;
|
|
864
|
+
const newVideoTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'video' });
|
|
865
|
+
await expect(videoProducer.replaceTrack({ track: newVideoTrack }))
|
|
866
|
+
.resolves
|
|
867
|
+
.toBe(undefined);
|
|
868
|
+
// Previous track must be 'ended' due to stopTracks: true.
|
|
869
|
+
expect(videoProducerPreviousTrack === null || videoProducerPreviousTrack === void 0 ? void 0 : videoProducerPreviousTrack.readyState).toBe('ended');
|
|
870
|
+
expect(videoProducer.track).not.toBe(videoProducerPreviousTrack);
|
|
871
|
+
expect(videoProducer.track).toBe(newVideoTrack);
|
|
872
|
+
expect(videoProducer.paused).toBe(false);
|
|
873
|
+
}, 500);
|
|
874
|
+
test('producer.replaceTrack() with null succeeds', async () => {
|
|
875
|
+
// Have the audio Producer paused.
|
|
876
|
+
audioProducer.pause();
|
|
877
|
+
const audioProducerPreviousTrack = audioProducer.track;
|
|
878
|
+
await expect(audioProducer.replaceTrack({ track: null }))
|
|
879
|
+
.resolves
|
|
880
|
+
.toBe(undefined);
|
|
881
|
+
// Previous track must be 'live' due to stopTracks: false.
|
|
882
|
+
expect(audioProducerPreviousTrack === null || audioProducerPreviousTrack === void 0 ? void 0 : audioProducerPreviousTrack.readyState).toBe('live');
|
|
883
|
+
expect(audioProducer.track).toBeNull();
|
|
884
|
+
// Producer was already paused.
|
|
885
|
+
expect(audioProducer.paused).toBe(true);
|
|
886
|
+
// Reset the audio paused state.
|
|
887
|
+
audioProducer.resume();
|
|
888
|
+
expect(audioProducer.paused).toBe(false);
|
|
889
|
+
// Manually "mute" the original audio track.
|
|
890
|
+
audioProducerPreviousTrack.enabled = false;
|
|
891
|
+
// Set the original audio track back.
|
|
892
|
+
await expect(audioProducer.replaceTrack({ track: audioProducerPreviousTrack }))
|
|
893
|
+
.resolves
|
|
894
|
+
.toBe(undefined);
|
|
895
|
+
// The given audio track was muted but the Producer was not, so the track
|
|
896
|
+
// must not be muted now.
|
|
897
|
+
expect(audioProducer.paused).toBe(false);
|
|
898
|
+
expect(audioProducerPreviousTrack === null || audioProducerPreviousTrack === void 0 ? void 0 : audioProducerPreviousTrack.enabled).toBe(true);
|
|
899
|
+
// Reset the audio paused state.
|
|
900
|
+
audioProducer.resume();
|
|
901
|
+
}, 500);
|
|
902
|
+
test('producer.replaceTrack() with an ended track rejects with InvalidStateError', async () => {
|
|
903
|
+
var _a;
|
|
904
|
+
const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
905
|
+
track.stop();
|
|
906
|
+
await expect(videoProducer.replaceTrack({ track }))
|
|
907
|
+
.rejects
|
|
908
|
+
.toThrow(errors_1.InvalidStateError);
|
|
909
|
+
expect(track.readyState).toBe('ended');
|
|
910
|
+
expect((_a = videoProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
|
|
911
|
+
}, 500);
|
|
912
|
+
test('producer.replaceTrack() with the same track succeeds', async () => {
|
|
913
|
+
var _a;
|
|
914
|
+
await expect(audioProducer.replaceTrack({ track: audioProducer.track }))
|
|
915
|
+
.resolves
|
|
916
|
+
.toBe(undefined);
|
|
917
|
+
expect((_a = audioProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
|
|
918
|
+
}, 500);
|
|
919
|
+
test('producer.setMaxSpatialLayer() succeeds', async () => {
|
|
920
|
+
await expect(videoProducer.setMaxSpatialLayer(0))
|
|
921
|
+
.resolves
|
|
922
|
+
.toBe(undefined);
|
|
923
|
+
expect(videoProducer.maxSpatialLayer).toBe(0);
|
|
924
|
+
}, 500);
|
|
925
|
+
test('producer.setMaxSpatialLayer() in an audio Producer rejects with UnsupportedError', async () => {
|
|
926
|
+
await expect(audioProducer.setMaxSpatialLayer(1))
|
|
927
|
+
.rejects
|
|
928
|
+
.toThrow(errors_1.UnsupportedError);
|
|
929
|
+
expect(audioProducer.maxSpatialLayer).toBe(undefined);
|
|
930
|
+
}, 500);
|
|
931
|
+
test('producer.setMaxSpatialLayer() with invalid spatialLayer rejects with TypeError', async () => {
|
|
932
|
+
// @ts-ignore
|
|
933
|
+
await expect(videoProducer.setMaxSpatialLayer('chicken'))
|
|
934
|
+
.rejects
|
|
935
|
+
.toThrow(TypeError);
|
|
936
|
+
}, 500);
|
|
937
|
+
test('producer.setMaxSpatialLayer() without spatialLayer rejects with TypeError', async () => {
|
|
938
|
+
// @ts-ignore
|
|
939
|
+
await expect(videoProducer.setMaxSpatialLayer())
|
|
940
|
+
.rejects
|
|
941
|
+
.toThrow(TypeError);
|
|
942
|
+
}, 500);
|
|
943
|
+
test('producer.setRtpEncodingParameters() succeeds', async () => {
|
|
944
|
+
await expect(videoProducer.setRtpEncodingParameters({ scaleResolutionDownBy: 2 }))
|
|
945
|
+
.resolves
|
|
946
|
+
.toBe(undefined);
|
|
947
|
+
expect(videoProducer.maxSpatialLayer).toBe(0);
|
|
948
|
+
}, 500);
|
|
949
|
+
test('producer.getStats() succeeds', async () => {
|
|
950
|
+
const stats = await videoProducer.getStats();
|
|
951
|
+
expect(typeof stats).toBe('object');
|
|
952
|
+
}, 500);
|
|
953
|
+
test('producer.appData cannot be overridden', () => {
|
|
954
|
+
expect(() => (videoProducer.appData = { lalala: 'LALALA' }))
|
|
955
|
+
.toThrow(Error);
|
|
956
|
+
expect(videoProducer.appData).toEqual({});
|
|
957
|
+
}, 500);
|
|
958
|
+
test('consumer.resume() succeeds', () => {
|
|
959
|
+
videoConsumer.resume();
|
|
960
|
+
expect(videoConsumer.paused).toBe(false);
|
|
961
|
+
}, 500);
|
|
962
|
+
test('consumer.pause() succeeds', () => {
|
|
963
|
+
videoConsumer.pause();
|
|
964
|
+
expect(videoConsumer.paused).toBe(true);
|
|
965
|
+
}, 500);
|
|
966
|
+
test('consumer.getStats() succeeds', async () => {
|
|
967
|
+
const stats = await videoConsumer.getStats();
|
|
968
|
+
expect(typeof stats).toBe('object');
|
|
969
|
+
}, 500);
|
|
970
|
+
test('cnosumer.appData cannot be overridden', () => {
|
|
971
|
+
expect(() => (audioConsumer.appData = { lalala: 'LALALA' }))
|
|
972
|
+
.toThrow(Error);
|
|
973
|
+
expect(audioConsumer.appData).toEqual({ bar: 'BAR' });
|
|
974
|
+
}, 500);
|
|
975
|
+
test('dataProducer.appData cannot be overridden', () => {
|
|
976
|
+
expect(() => (dataProducer.appData = { lalala: 'LALALA' }))
|
|
977
|
+
.toThrow(Error);
|
|
978
|
+
expect(dataProducer.appData).toEqual({ foo: 'FOO' });
|
|
979
|
+
}, 500);
|
|
980
|
+
test('dataConsumer.appData cannot be overridden', () => {
|
|
981
|
+
expect(() => (dataConsumer.appData = { lalala: 'LALALA' }))
|
|
982
|
+
.toThrow(Error);
|
|
983
|
+
expect(dataConsumer.appData).toEqual({ bar: 'BAR' });
|
|
984
|
+
}, 500);
|
|
985
|
+
test('producer.close() succeed', () => {
|
|
986
|
+
var _a;
|
|
987
|
+
audioProducer.close();
|
|
988
|
+
expect(audioProducer.closed).toBe(true);
|
|
989
|
+
// Track will be still 'live' due to stopTracks: false.
|
|
990
|
+
expect((_a = audioProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
|
|
991
|
+
}, 500);
|
|
992
|
+
test('producer.replaceTrack() rejects with InvalidStateError if closed', async () => {
|
|
993
|
+
const audioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
994
|
+
await expect(audioProducer.replaceTrack({ track: audioTrack }))
|
|
995
|
+
.rejects
|
|
996
|
+
.toThrow(errors_1.InvalidStateError);
|
|
997
|
+
expect(audioTrack.readyState).toBe('live');
|
|
998
|
+
}, 500);
|
|
999
|
+
test('producer.getStats() rejects with InvalidStateError if closed', async () => {
|
|
1000
|
+
await expect(audioProducer.getStats())
|
|
1001
|
+
.rejects
|
|
1002
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1003
|
+
}, 500);
|
|
1004
|
+
test('consumer.close() succeed', () => {
|
|
1005
|
+
audioConsumer.close();
|
|
1006
|
+
expect(audioConsumer.closed).toBe(true);
|
|
1007
|
+
expect(audioConsumer.track.readyState).toBe('ended');
|
|
1008
|
+
}, 500);
|
|
1009
|
+
test('consumer.getStats() rejects with InvalidStateError if closed', async () => {
|
|
1010
|
+
await expect(audioConsumer.getStats())
|
|
1011
|
+
.rejects
|
|
1012
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1013
|
+
}, 500);
|
|
1014
|
+
test('dataProducer.close() succeed', () => {
|
|
1015
|
+
dataProducer.close();
|
|
1016
|
+
expect(dataProducer.closed).toBe(true);
|
|
1017
|
+
}, 500);
|
|
1018
|
+
test('dataConsumer.close() succeed', () => {
|
|
1019
|
+
dataConsumer.close();
|
|
1020
|
+
expect(dataConsumer.closed).toBe(true);
|
|
1021
|
+
}, 500);
|
|
1022
|
+
test('remotetely stopped track fires "trackended" in live Producers/Consumers', () => {
|
|
1023
|
+
let audioProducerTrackendedEventCalled = false;
|
|
1024
|
+
let videoProducerTrackendedEventCalled = false;
|
|
1025
|
+
let audiosConsumerTrackendedEventCalled = false;
|
|
1026
|
+
let videoConsumerTrackendedEventCalled = false;
|
|
1027
|
+
audioProducer.on('trackended', () => {
|
|
1028
|
+
audioProducerTrackendedEventCalled = true;
|
|
1029
|
+
});
|
|
1030
|
+
videoProducer.on('trackended', () => {
|
|
1031
|
+
videoProducerTrackendedEventCalled = true;
|
|
1032
|
+
});
|
|
1033
|
+
audioConsumer.on('trackended', () => {
|
|
1034
|
+
audiosConsumerTrackendedEventCalled = true;
|
|
1035
|
+
});
|
|
1036
|
+
videoConsumer.on('trackended', () => {
|
|
1037
|
+
videoConsumerTrackendedEventCalled = true;
|
|
1038
|
+
});
|
|
1039
|
+
// @ts-ignore
|
|
1040
|
+
audioProducer.track.remoteStop();
|
|
1041
|
+
// Audio Producer was already closed.
|
|
1042
|
+
expect(audioProducerTrackendedEventCalled).toBe(false);
|
|
1043
|
+
// @ts-ignore
|
|
1044
|
+
videoProducer.track.remoteStop();
|
|
1045
|
+
expect(videoProducerTrackendedEventCalled).toBe(true);
|
|
1046
|
+
// @ts-ignore
|
|
1047
|
+
audioConsumer.track.remoteStop();
|
|
1048
|
+
// Audio Consumer was already closed.
|
|
1049
|
+
expect(audiosConsumerTrackendedEventCalled).toBe(false);
|
|
1050
|
+
// @ts-ignore
|
|
1051
|
+
videoConsumer.track.remoteStop();
|
|
1052
|
+
expect(videoConsumerTrackendedEventCalled).toBe(true);
|
|
1053
|
+
audioProducer.removeAllListeners();
|
|
1054
|
+
videoProducer.removeAllListeners();
|
|
1055
|
+
audioConsumer.removeAllListeners();
|
|
1056
|
+
videoConsumer.removeAllListeners();
|
|
1057
|
+
}, 500);
|
|
1058
|
+
test('transport.close() fires "transportclose" in live Producers/Consumers', () => {
|
|
1059
|
+
let audioProducerTransportcloseEventCalled = false;
|
|
1060
|
+
let videoProducerTransportcloseEventCalled = false;
|
|
1061
|
+
let audioConsumerTransportcloseEventCalled = false;
|
|
1062
|
+
let videoConsumerTransportcloseEventCalled = false;
|
|
1063
|
+
audioProducer.on('transportclose', () => {
|
|
1064
|
+
audioProducerTransportcloseEventCalled = true;
|
|
1065
|
+
});
|
|
1066
|
+
videoProducer.on('transportclose', () => {
|
|
1067
|
+
videoProducerTransportcloseEventCalled = true;
|
|
1068
|
+
});
|
|
1069
|
+
audioConsumer.on('transportclose', () => {
|
|
1070
|
+
audioConsumerTransportcloseEventCalled = true;
|
|
1071
|
+
});
|
|
1072
|
+
videoConsumer.on('transportclose', () => {
|
|
1073
|
+
videoConsumerTransportcloseEventCalled = true;
|
|
1074
|
+
});
|
|
1075
|
+
// Audio Producer was already closed.
|
|
1076
|
+
expect(audioProducer.closed).toBe(true);
|
|
1077
|
+
expect(videoProducer.closed).toBe(false);
|
|
1078
|
+
sendTransport.close();
|
|
1079
|
+
expect(sendTransport.closed).toBe(true);
|
|
1080
|
+
expect(videoProducer.closed).toBe(true);
|
|
1081
|
+
// Audio Producer was already closed.
|
|
1082
|
+
expect(audioProducerTransportcloseEventCalled).toBe(false);
|
|
1083
|
+
expect(videoProducerTransportcloseEventCalled).toBe(true);
|
|
1084
|
+
// Audio Consumer was already closed.
|
|
1085
|
+
expect(audioConsumer.closed).toBe(true);
|
|
1086
|
+
expect(videoConsumer.closed).toBe(false);
|
|
1087
|
+
recvTransport.close();
|
|
1088
|
+
expect(recvTransport.closed).toBe(true);
|
|
1089
|
+
expect(videoConsumer.closed).toBe(true);
|
|
1090
|
+
// Audio Consumer was already closed.
|
|
1091
|
+
expect(audioConsumerTransportcloseEventCalled).toBe(false);
|
|
1092
|
+
expect(videoConsumerTransportcloseEventCalled).toBe(true);
|
|
1093
|
+
audioProducer.removeAllListeners();
|
|
1094
|
+
videoProducer.removeAllListeners();
|
|
1095
|
+
audioConsumer.removeAllListeners();
|
|
1096
|
+
videoConsumer.removeAllListeners();
|
|
1097
|
+
}, 500);
|
|
1098
|
+
test('transport.produce() rejects with InvalidStateError if closed', async () => {
|
|
1099
|
+
const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
|
|
1100
|
+
// Add noop listener to avoid the method fail.
|
|
1101
|
+
sendTransport.on('produce', () => { });
|
|
1102
|
+
await expect(sendTransport.produce({ track, stopTracks: false }))
|
|
1103
|
+
.rejects
|
|
1104
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1105
|
+
// The track must be 'live' due to stopTracks: false.
|
|
1106
|
+
expect(track.readyState).toBe('live');
|
|
1107
|
+
sendTransport.removeAllListeners('produce');
|
|
1108
|
+
}, 500);
|
|
1109
|
+
test('transport.consume() rejects with InvalidStateError if closed', async () => {
|
|
1110
|
+
// @ts-ignore
|
|
1111
|
+
await expect(recvTransport.consume({}))
|
|
1112
|
+
.rejects
|
|
1113
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1114
|
+
recvTransport.removeAllListeners();
|
|
1115
|
+
}, 500);
|
|
1116
|
+
test('transport.produceData() rejects with InvalidStateError if closed', async () => {
|
|
1117
|
+
// Add noop listener to avoid the method fail.
|
|
1118
|
+
sendTransport.on('producedata', () => { });
|
|
1119
|
+
await expect(sendTransport.produceData({}))
|
|
1120
|
+
.rejects
|
|
1121
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1122
|
+
sendTransport.removeAllListeners('producedata');
|
|
1123
|
+
}, 500);
|
|
1124
|
+
test('transport.consumeData() rejects with InvalidStateError if closed', async () => {
|
|
1125
|
+
// @ts-ignore
|
|
1126
|
+
await expect(recvTransport.consumeData({}))
|
|
1127
|
+
.rejects
|
|
1128
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1129
|
+
}, 500);
|
|
1130
|
+
test('transport.getStats() rejects with InvalidStateError if closed', async () => {
|
|
1131
|
+
await expect(sendTransport.getStats())
|
|
1132
|
+
.rejects
|
|
1133
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1134
|
+
}, 500);
|
|
1135
|
+
test('transport.restartIce() rejects with InvalidStateError if closed', async () => {
|
|
1136
|
+
// @ts-ignore
|
|
1137
|
+
await expect(sendTransport.restartIce({ ieParameters: {} }))
|
|
1138
|
+
.rejects
|
|
1139
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1140
|
+
}, 500);
|
|
1141
|
+
test('transport.updateIceServers() rejects with InvalidStateError if closed', async () => {
|
|
1142
|
+
await expect(sendTransport.updateIceServers({ iceServers: [] }))
|
|
1143
|
+
.rejects
|
|
1144
|
+
.toThrow(errors_1.InvalidStateError);
|
|
1145
|
+
}, 500);
|
|
1146
|
+
test('connection state change does not fire "connectionstatechange" in closed Transport', () => {
|
|
1147
|
+
let connectionStateChangeEventNumTimesCalled = 0;
|
|
1148
|
+
// eslint-disable-next-line no-unused-vars
|
|
1149
|
+
sendTransport.on('connectionstatechange', ( /* connectionState */) => {
|
|
1150
|
+
connectionStateChangeEventNumTimesCalled++;
|
|
1151
|
+
});
|
|
1152
|
+
// @ts-ignore
|
|
1153
|
+
sendTransport.handler.setConnectionState('disconnected');
|
|
1154
|
+
expect(connectionStateChangeEventNumTimesCalled).toBe(0);
|
|
1155
|
+
expect(sendTransport.connectionState).toBe('disconnected');
|
|
1156
|
+
sendTransport.removeAllListeners('connectionstatechange');
|
|
1157
|
+
}, 500);
|
|
1158
|
+
test('RemoteSdp properly handles multiple streams of the same type in planB', async () => {
|
|
1159
|
+
var _a, _b, _c;
|
|
1160
|
+
let sdp = undefined;
|
|
1161
|
+
let sdpObject = undefined;
|
|
1162
|
+
const remoteSdp = new RemoteSdp_1.RemoteSdp({ planB: true });
|
|
1163
|
+
await remoteSdp.receive({
|
|
1164
|
+
mid: 'video',
|
|
1165
|
+
kind: 'video',
|
|
1166
|
+
offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters,
|
|
1167
|
+
streamId: 'streamId-1',
|
|
1168
|
+
trackId: 'trackId-1'
|
|
1169
|
+
});
|
|
1170
|
+
sdp = remoteSdp.getSdp();
|
|
1171
|
+
sdpObject = sdpTransform.parse(sdp);
|
|
1172
|
+
expect(sdpObject.media.length).toBe(1);
|
|
1173
|
+
expect(sdpObject.media[0].payloads).toBe('101 102');
|
|
1174
|
+
expect(sdpObject.media[0].rtp.length).toBe(2);
|
|
1175
|
+
expect(sdpObject.media[0].rtp[0].payload).toBe(101);
|
|
1176
|
+
expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
|
|
1177
|
+
expect(sdpObject.media[0].rtp[1].payload).toBe(102);
|
|
1178
|
+
expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
|
|
1179
|
+
expect((_a = sdpObject.media[0].ssrcs) === null || _a === void 0 ? void 0 : _a.length).toBe(4);
|
|
1180
|
+
await remoteSdp.receive({
|
|
1181
|
+
mid: 'video',
|
|
1182
|
+
kind: 'video',
|
|
1183
|
+
offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/H264' }).rtpParameters,
|
|
1184
|
+
streamId: 'streamId-2',
|
|
1185
|
+
trackId: 'trackId-2'
|
|
1186
|
+
});
|
|
1187
|
+
sdp = remoteSdp.getSdp();
|
|
1188
|
+
sdpObject = sdpTransform.parse(sdp);
|
|
1189
|
+
expect(sdpObject.media.length).toBe(1);
|
|
1190
|
+
expect(sdpObject.media[0].payloads).toBe('101 102 103 104');
|
|
1191
|
+
expect(sdpObject.media[0].rtp.length).toBe(4);
|
|
1192
|
+
expect(sdpObject.media[0].rtp[0].payload).toBe(101);
|
|
1193
|
+
expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
|
|
1194
|
+
expect(sdpObject.media[0].rtp[1].payload).toBe(102);
|
|
1195
|
+
expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
|
|
1196
|
+
expect(sdpObject.media[0].rtp[2].payload).toBe(103);
|
|
1197
|
+
expect(sdpObject.media[0].rtp[2].codec).toBe('H264');
|
|
1198
|
+
expect(sdpObject.media[0].rtp[3].payload).toBe(104);
|
|
1199
|
+
expect(sdpObject.media[0].rtp[3].codec).toBe('rtx');
|
|
1200
|
+
expect((_b = sdpObject.media[0].ssrcs) === null || _b === void 0 ? void 0 : _b.length).toBe(8);
|
|
1201
|
+
await remoteSdp.planBStopReceiving({
|
|
1202
|
+
mid: 'video',
|
|
1203
|
+
offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/H264' }).rtpParameters
|
|
1204
|
+
});
|
|
1205
|
+
sdp = remoteSdp.getSdp();
|
|
1206
|
+
sdpObject = sdpTransform.parse(sdp);
|
|
1207
|
+
expect(sdpObject.media.length).toBe(1);
|
|
1208
|
+
expect(sdpObject.media[0].payloads).toBe('101 102 103 104');
|
|
1209
|
+
expect(sdpObject.media[0].rtp.length).toBe(4);
|
|
1210
|
+
expect(sdpObject.media[0].rtp[0].payload).toBe(101);
|
|
1211
|
+
expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
|
|
1212
|
+
expect(sdpObject.media[0].rtp[1].payload).toBe(102);
|
|
1213
|
+
expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
|
|
1214
|
+
expect(sdpObject.media[0].rtp[2].payload).toBe(103);
|
|
1215
|
+
expect(sdpObject.media[0].rtp[2].codec).toBe('H264');
|
|
1216
|
+
expect(sdpObject.media[0].rtp[3].payload).toBe(104);
|
|
1217
|
+
expect(sdpObject.media[0].rtp[3].codec).toBe('rtx');
|
|
1218
|
+
expect((_c = sdpObject.media[0].ssrcs) === null || _c === void 0 ? void 0 : _c.length).toBe(4);
|
|
1219
|
+
}, 500);
|
|
1220
|
+
test('RemoteSdp does not duplicate codec descriptions', async () => {
|
|
1221
|
+
var _a, _b;
|
|
1222
|
+
let sdp = undefined;
|
|
1223
|
+
let sdpObject = undefined;
|
|
1224
|
+
const remoteSdp = new RemoteSdp_1.RemoteSdp({ planB: true });
|
|
1225
|
+
await remoteSdp.receive({
|
|
1226
|
+
mid: 'video',
|
|
1227
|
+
kind: 'video',
|
|
1228
|
+
offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters,
|
|
1229
|
+
streamId: 'streamId-1',
|
|
1230
|
+
trackId: 'trackId-1'
|
|
1231
|
+
});
|
|
1232
|
+
sdp = remoteSdp.getSdp();
|
|
1233
|
+
sdpObject = sdpTransform.parse(sdp);
|
|
1234
|
+
expect(sdpObject.media.length).toBe(1);
|
|
1235
|
+
expect(sdpObject.media[0].payloads).toBe('101 102');
|
|
1236
|
+
expect(sdpObject.media[0].rtp.length).toBe(2);
|
|
1237
|
+
expect(sdpObject.media[0].rtp[0].payload).toBe(101);
|
|
1238
|
+
expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
|
|
1239
|
+
expect(sdpObject.media[0].rtp[1].payload).toBe(102);
|
|
1240
|
+
expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
|
|
1241
|
+
expect((_a = sdpObject.media[0].ssrcs) === null || _a === void 0 ? void 0 : _a.length).toBe(4);
|
|
1242
|
+
await remoteSdp.receive({
|
|
1243
|
+
mid: 'video',
|
|
1244
|
+
kind: 'video',
|
|
1245
|
+
offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters,
|
|
1246
|
+
streamId: 'streamId-1',
|
|
1247
|
+
trackId: 'trackId-1'
|
|
1248
|
+
});
|
|
1249
|
+
sdp = remoteSdp.getSdp();
|
|
1250
|
+
sdpObject = sdpTransform.parse(sdp);
|
|
1251
|
+
expect(sdpObject.media.length).toBe(1);
|
|
1252
|
+
expect(sdpObject.media[0].payloads).toBe('101 102');
|
|
1253
|
+
expect(sdpObject.media[0].rtp.length).toBe(2);
|
|
1254
|
+
expect(sdpObject.media[0].rtp[0].payload).toBe(101);
|
|
1255
|
+
expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
|
|
1256
|
+
expect(sdpObject.media[0].rtp[1].payload).toBe(102);
|
|
1257
|
+
expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
|
|
1258
|
+
expect((_b = sdpObject.media[0].ssrcs) === null || _b === void 0 ? void 0 : _b.length).toBe(8);
|
|
1259
|
+
}, 500);
|
|
1260
|
+
test('parseScalabilityMode() works', () => {
|
|
1261
|
+
expect(parseScalabilityMode('L1T3')).toEqual({ spatialLayers: 1, temporalLayers: 3 });
|
|
1262
|
+
expect(parseScalabilityMode('L3T2_KEY')).toEqual({ spatialLayers: 3, temporalLayers: 2 });
|
|
1263
|
+
expect(parseScalabilityMode('S2T3')).toEqual({ spatialLayers: 2, temporalLayers: 3 });
|
|
1264
|
+
expect(parseScalabilityMode('foo')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
|
|
1265
|
+
expect(parseScalabilityMode()).toEqual({ spatialLayers: 1, temporalLayers: 1 });
|
|
1266
|
+
expect(parseScalabilityMode('S0T3')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
|
|
1267
|
+
expect(parseScalabilityMode('S1T0')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
|
|
1268
|
+
expect(parseScalabilityMode('L20T3')).toEqual({ spatialLayers: 20, temporalLayers: 3 });
|
|
1269
|
+
expect(parseScalabilityMode('S200T3')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
|
|
1270
|
+
}, 500);
|