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.
Files changed (92) hide show
  1. package/README.md +17 -3
  2. package/lib/Consumer.d.ts +3 -3
  3. package/lib/Consumer.d.ts.map +1 -1
  4. package/lib/DataConsumer.d.ts +3 -3
  5. package/lib/DataConsumer.d.ts.map +1 -1
  6. package/lib/DataProducer.d.ts +3 -3
  7. package/lib/DataProducer.d.ts.map +1 -1
  8. package/lib/Device.d.ts +3 -3
  9. package/lib/Device.d.ts.map +1 -1
  10. package/lib/EnhancedEventEmitter.d.ts +1 -1
  11. package/lib/EnhancedEventEmitter.d.ts.map +1 -1
  12. package/lib/Producer.d.ts +4 -4
  13. package/lib/Producer.d.ts.map +1 -1
  14. package/lib/RtpParameters.d.ts +11 -11
  15. package/lib/RtpParameters.d.ts.map +1 -1
  16. package/lib/SctpParameters.d.ts +4 -4
  17. package/lib/SctpParameters.d.ts.map +1 -1
  18. package/lib/Transport.d.ts +11 -11
  19. package/lib/Transport.d.ts.map +1 -1
  20. package/lib/handlers/Chrome55.js +6 -6
  21. package/lib/handlers/Chrome67.js +6 -6
  22. package/lib/handlers/Chrome70.d.ts.map +1 -1
  23. package/lib/handlers/Chrome70.js +10 -7
  24. package/lib/handlers/Chrome74.d.ts.map +1 -1
  25. package/lib/handlers/Chrome74.js +10 -7
  26. package/lib/handlers/Edge11.js +6 -6
  27. package/lib/handlers/FakeHandler.d.ts +1 -1
  28. package/lib/handlers/FakeHandler.d.ts.map +1 -1
  29. package/lib/handlers/FakeHandler.js +6 -6
  30. package/lib/handlers/Firefox60.d.ts.map +1 -1
  31. package/lib/handlers/Firefox60.js +10 -7
  32. package/lib/handlers/HandlerInterface.d.ts +11 -11
  33. package/lib/handlers/HandlerInterface.d.ts.map +1 -1
  34. package/lib/handlers/ReactNative.js +6 -6
  35. package/lib/handlers/Safari11.js +6 -6
  36. package/lib/handlers/Safari12.d.ts.map +1 -1
  37. package/lib/handlers/Safari12.js +10 -7
  38. package/lib/index.d.ts +1 -1
  39. package/lib/index.js +1 -1
  40. package/lib/scalabilityModes.d.ts +1 -1
  41. package/lib/scalabilityModes.d.ts.map +1 -1
  42. package/lib/tests/fakeParameters.d.ts +20 -0
  43. package/lib/tests/fakeParameters.d.ts.map +1 -0
  44. package/lib/tests/fakeParameters.js +636 -0
  45. package/lib/tests/test.d.ts +6 -0
  46. package/lib/tests/test.d.ts.map +1 -0
  47. package/lib/tests/test.js +1270 -0
  48. package/npm-scripts.js +143 -28
  49. package/package.json +38 -11
  50. package/.eslintrc.js +0 -243
  51. package/.github/FUNDING.yml +0 -12
  52. package/.github/ISSUE_TEMPLATE/Bug_Report.md +0 -24
  53. package/.github/ISSUE_TEMPLATE/Feature_Request.md +0 -7
  54. package/.github/ISSUE_TEMPLATE/Support_Question.md +0 -12
  55. package/.travis.yml +0 -26
  56. package/src/Consumer.ts +0 -334
  57. package/src/DataConsumer.ts +0 -271
  58. package/src/DataProducer.ts +0 -291
  59. package/src/Device.ts +0 -584
  60. package/src/EnhancedEventEmitter.ts +0 -133
  61. package/src/Logger.ts +0 -47
  62. package/src/Producer.ts +0 -534
  63. package/src/RtpParameters.ts +0 -326
  64. package/src/SctpParameters.ts +0 -74
  65. package/src/Transport.ts +0 -1275
  66. package/src/errors.ts +0 -45
  67. package/src/handlers/Chrome55.ts +0 -866
  68. package/src/handlers/Chrome67.ts +0 -951
  69. package/src/handlers/Chrome70.ts +0 -964
  70. package/src/handlers/Chrome74.ts +0 -1058
  71. package/src/handlers/Edge11.ts +0 -667
  72. package/src/handlers/FakeHandler.ts +0 -448
  73. package/src/handlers/Firefox60.ts +0 -1040
  74. package/src/handlers/HandlerInterface.ts +0 -162
  75. package/src/handlers/ReactNative.ts +0 -886
  76. package/src/handlers/Safari11.ts +0 -944
  77. package/src/handlers/Safari12.ts +0 -1009
  78. package/src/handlers/ortc/edgeUtils.ts +0 -77
  79. package/src/handlers/sdp/MediaSection.ts +0 -836
  80. package/src/handlers/sdp/RemoteSdp.ts +0 -474
  81. package/src/handlers/sdp/commonUtils.ts +0 -259
  82. package/src/handlers/sdp/plainRtpUtils.ts +0 -58
  83. package/src/handlers/sdp/planBUtils.ts +0 -227
  84. package/src/handlers/sdp/unifiedPlanUtils.ts +0 -188
  85. package/src/index.ts +0 -28
  86. package/src/ortc.ts +0 -1169
  87. package/src/scalabilityModes.ts +0 -27
  88. package/src/types.ts +0 -11
  89. package/src/utils.ts +0 -18
  90. package/test/fakeParameters.js +0 -701
  91. package/test/test.js +0 -1672
  92. 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);