mediasoup-client 3.6.80 → 3.6.81
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/lib/Consumer.d.ts.map +1 -1
- package/lib/Consumer.js +6 -3
- package/lib/DataConsumer.d.ts.map +1 -1
- package/lib/DataConsumer.js +14 -7
- package/lib/DataProducer.d.ts.map +1 -1
- package/lib/DataProducer.js +18 -9
- package/lib/Device.d.ts.map +1 -1
- package/lib/Device.js +24 -12
- package/lib/Producer.d.ts +1 -0
- package/lib/Producer.d.ts.map +1 -1
- package/lib/Producer.js +28 -14
- package/lib/handlers/Chrome111.d.ts.map +1 -1
- package/lib/handlers/Chrome111.js +33 -15
- package/lib/handlers/Chrome55.d.ts.map +1 -1
- package/lib/handlers/Chrome55.js +12 -6
- package/lib/handlers/Chrome67.d.ts.map +1 -1
- package/lib/handlers/Chrome67.js +32 -16
- package/lib/handlers/Chrome70.d.ts.map +1 -1
- package/lib/handlers/Chrome70.js +28 -14
- package/lib/handlers/Chrome74.d.ts.map +1 -1
- package/lib/handlers/Chrome74.js +35 -16
- package/lib/handlers/Edge11.d.ts.map +1 -1
- package/lib/handlers/Edge11.js +30 -15
- package/lib/handlers/FakeHandler.d.ts.map +1 -1
- package/lib/handlers/FakeHandler.js +16 -8
- package/lib/handlers/Firefox60.d.ts.map +1 -1
- package/lib/handlers/Firefox60.js +38 -19
- package/lib/handlers/ReactNative.d.ts.map +1 -1
- package/lib/handlers/ReactNative.js +12 -6
- package/lib/handlers/ReactNativeUnifiedPlan.d.ts.map +1 -1
- package/lib/handlers/ReactNativeUnifiedPlan.js +35 -16
- package/lib/handlers/Safari11.d.ts.map +1 -1
- package/lib/handlers/Safari11.js +32 -16
- package/lib/handlers/Safari12.d.ts.map +1 -1
- package/lib/handlers/Safari12.js +37 -17
- package/lib/handlers/ortc/edgeUtils.d.ts.map +1 -1
- package/lib/handlers/ortc/edgeUtils.js +8 -4
- package/lib/handlers/ortc/utils.d.ts +6 -0
- package/lib/handlers/ortc/utils.d.ts.map +1 -0
- package/lib/handlers/ortc/utils.js +20 -0
- package/lib/handlers/sdp/MediaSection.d.ts.map +1 -1
- package/lib/handlers/sdp/MediaSection.js +56 -23
- package/lib/handlers/sdp/RemoteSdp.d.ts.map +1 -1
- package/lib/handlers/sdp/RemoteSdp.js +8 -4
- package/lib/handlers/sdp/commonUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/commonUtils.js +26 -13
- package/lib/handlers/sdp/plainRtpUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/plainRtpUtils.js +8 -4
- package/lib/handlers/sdp/planBUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/planBUtils.js +22 -11
- package/lib/handlers/sdp/unifiedPlanUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/unifiedPlanUtils.js +16 -8
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/ortc.d.ts.map +1 -1
- package/lib/ortc.js +196 -98
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +2 -1
- package/package.json +3 -3
package/lib/ortc.js
CHANGED
|
@@ -35,21 +35,26 @@ const RTP_PROBATOR_CODEC_PAYLOAD_TYPE = 127;
|
|
|
35
35
|
* It throws if invalid.
|
|
36
36
|
*/
|
|
37
37
|
function validateRtpCapabilities(caps) {
|
|
38
|
-
if (typeof caps !== 'object')
|
|
38
|
+
if (typeof caps !== 'object') {
|
|
39
39
|
throw new TypeError('caps is not an object');
|
|
40
|
+
}
|
|
40
41
|
// codecs is optional. If unset, fill with an empty array.
|
|
41
|
-
if (caps.codecs && !Array.isArray(caps.codecs))
|
|
42
|
+
if (caps.codecs && !Array.isArray(caps.codecs)) {
|
|
42
43
|
throw new TypeError('caps.codecs is not an array');
|
|
43
|
-
|
|
44
|
+
}
|
|
45
|
+
else if (!caps.codecs) {
|
|
44
46
|
caps.codecs = [];
|
|
47
|
+
}
|
|
45
48
|
for (const codec of caps.codecs) {
|
|
46
49
|
validateRtpCodecCapability(codec);
|
|
47
50
|
}
|
|
48
51
|
// headerExtensions is optional. If unset, fill with an empty array.
|
|
49
|
-
if (caps.headerExtensions && !Array.isArray(caps.headerExtensions))
|
|
52
|
+
if (caps.headerExtensions && !Array.isArray(caps.headerExtensions)) {
|
|
50
53
|
throw new TypeError('caps.headerExtensions is not an array');
|
|
51
|
-
|
|
54
|
+
}
|
|
55
|
+
else if (!caps.headerExtensions) {
|
|
52
56
|
caps.headerExtensions = [];
|
|
57
|
+
}
|
|
53
58
|
for (const ext of caps.headerExtensions) {
|
|
54
59
|
validateRtpHeaderExtension(ext);
|
|
55
60
|
}
|
|
@@ -62,33 +67,40 @@ exports.validateRtpCapabilities = validateRtpCapabilities;
|
|
|
62
67
|
*/
|
|
63
68
|
function validateRtpCodecCapability(codec) {
|
|
64
69
|
const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i');
|
|
65
|
-
if (typeof codec !== 'object')
|
|
70
|
+
if (typeof codec !== 'object') {
|
|
66
71
|
throw new TypeError('codec is not an object');
|
|
72
|
+
}
|
|
67
73
|
// mimeType is mandatory.
|
|
68
|
-
if (!codec.mimeType || typeof codec.mimeType !== 'string')
|
|
74
|
+
if (!codec.mimeType || typeof codec.mimeType !== 'string') {
|
|
69
75
|
throw new TypeError('missing codec.mimeType');
|
|
76
|
+
}
|
|
70
77
|
const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType);
|
|
71
|
-
if (!mimeTypeMatch)
|
|
78
|
+
if (!mimeTypeMatch) {
|
|
72
79
|
throw new TypeError('invalid codec.mimeType');
|
|
80
|
+
}
|
|
73
81
|
// Just override kind with media component of mimeType.
|
|
74
82
|
codec.kind = mimeTypeMatch[1].toLowerCase();
|
|
75
83
|
// preferredPayloadType is optional.
|
|
76
|
-
if (codec.preferredPayloadType && typeof codec.preferredPayloadType !== 'number')
|
|
84
|
+
if (codec.preferredPayloadType && typeof codec.preferredPayloadType !== 'number') {
|
|
77
85
|
throw new TypeError('invalid codec.preferredPayloadType');
|
|
86
|
+
}
|
|
78
87
|
// clockRate is mandatory.
|
|
79
|
-
if (typeof codec.clockRate !== 'number')
|
|
88
|
+
if (typeof codec.clockRate !== 'number') {
|
|
80
89
|
throw new TypeError('missing codec.clockRate');
|
|
90
|
+
}
|
|
81
91
|
// channels is optional. If unset, set it to 1 (just if audio).
|
|
82
92
|
if (codec.kind === 'audio') {
|
|
83
|
-
if (typeof codec.channels !== 'number')
|
|
93
|
+
if (typeof codec.channels !== 'number') {
|
|
84
94
|
codec.channels = 1;
|
|
95
|
+
}
|
|
85
96
|
}
|
|
86
97
|
else {
|
|
87
98
|
delete codec.channels;
|
|
88
99
|
}
|
|
89
100
|
// parameters is optional. If unset, set it to an empty object.
|
|
90
|
-
if (!codec.parameters || typeof codec.parameters !== 'object')
|
|
101
|
+
if (!codec.parameters || typeof codec.parameters !== 'object') {
|
|
91
102
|
codec.parameters = {};
|
|
103
|
+
}
|
|
92
104
|
for (const key of Object.keys(codec.parameters)) {
|
|
93
105
|
let value = codec.parameters[key];
|
|
94
106
|
if (value === undefined) {
|
|
@@ -100,13 +112,15 @@ function validateRtpCodecCapability(codec) {
|
|
|
100
112
|
}
|
|
101
113
|
// Specific parameters validation.
|
|
102
114
|
if (key === 'apt') {
|
|
103
|
-
if (typeof value !== 'number')
|
|
115
|
+
if (typeof value !== 'number') {
|
|
104
116
|
throw new TypeError('invalid codec apt parameter');
|
|
117
|
+
}
|
|
105
118
|
}
|
|
106
119
|
}
|
|
107
120
|
// rtcpFeedback is optional. If unset, set it to an empty array.
|
|
108
|
-
if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback))
|
|
121
|
+
if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) {
|
|
109
122
|
codec.rtcpFeedback = [];
|
|
123
|
+
}
|
|
110
124
|
for (const fb of codec.rtcpFeedback) {
|
|
111
125
|
validateRtcpFeedback(fb);
|
|
112
126
|
}
|
|
@@ -118,14 +132,17 @@ exports.validateRtpCodecCapability = validateRtpCodecCapability;
|
|
|
118
132
|
* It throws if invalid.
|
|
119
133
|
*/
|
|
120
134
|
function validateRtcpFeedback(fb) {
|
|
121
|
-
if (typeof fb !== 'object')
|
|
135
|
+
if (typeof fb !== 'object') {
|
|
122
136
|
throw new TypeError('fb is not an object');
|
|
137
|
+
}
|
|
123
138
|
// type is mandatory.
|
|
124
|
-
if (!fb.type || typeof fb.type !== 'string')
|
|
139
|
+
if (!fb.type || typeof fb.type !== 'string') {
|
|
125
140
|
throw new TypeError('missing fb.type');
|
|
141
|
+
}
|
|
126
142
|
// parameter is optional. If unset set it to an empty string.
|
|
127
|
-
if (!fb.parameter || typeof fb.parameter !== 'string')
|
|
143
|
+
if (!fb.parameter || typeof fb.parameter !== 'string') {
|
|
128
144
|
fb.parameter = '';
|
|
145
|
+
}
|
|
129
146
|
}
|
|
130
147
|
exports.validateRtcpFeedback = validateRtcpFeedback;
|
|
131
148
|
/**
|
|
@@ -134,27 +151,35 @@ exports.validateRtcpFeedback = validateRtcpFeedback;
|
|
|
134
151
|
* It throws if invalid.
|
|
135
152
|
*/
|
|
136
153
|
function validateRtpHeaderExtension(ext) {
|
|
137
|
-
if (typeof ext !== 'object')
|
|
154
|
+
if (typeof ext !== 'object') {
|
|
138
155
|
throw new TypeError('ext is not an object');
|
|
156
|
+
}
|
|
139
157
|
// kind is mandatory.
|
|
140
|
-
if (ext.kind !== 'audio' && ext.kind !== 'video')
|
|
158
|
+
if (ext.kind !== 'audio' && ext.kind !== 'video') {
|
|
141
159
|
throw new TypeError('invalid ext.kind');
|
|
160
|
+
}
|
|
142
161
|
// uri is mandatory.
|
|
143
|
-
if (!ext.uri || typeof ext.uri !== 'string')
|
|
162
|
+
if (!ext.uri || typeof ext.uri !== 'string') {
|
|
144
163
|
throw new TypeError('missing ext.uri');
|
|
164
|
+
}
|
|
145
165
|
// preferredId is mandatory.
|
|
146
|
-
if (typeof ext.preferredId !== 'number')
|
|
166
|
+
if (typeof ext.preferredId !== 'number') {
|
|
147
167
|
throw new TypeError('missing ext.preferredId');
|
|
168
|
+
}
|
|
148
169
|
// preferredEncrypt is optional. If unset set it to false.
|
|
149
|
-
if (ext.preferredEncrypt && typeof ext.preferredEncrypt !== 'boolean')
|
|
170
|
+
if (ext.preferredEncrypt && typeof ext.preferredEncrypt !== 'boolean') {
|
|
150
171
|
throw new TypeError('invalid ext.preferredEncrypt');
|
|
151
|
-
|
|
172
|
+
}
|
|
173
|
+
else if (!ext.preferredEncrypt) {
|
|
152
174
|
ext.preferredEncrypt = false;
|
|
175
|
+
}
|
|
153
176
|
// direction is optional. If unset set it to sendrecv.
|
|
154
|
-
if (ext.direction && typeof ext.direction !== 'string')
|
|
177
|
+
if (ext.direction && typeof ext.direction !== 'string') {
|
|
155
178
|
throw new TypeError('invalid ext.direction');
|
|
156
|
-
|
|
179
|
+
}
|
|
180
|
+
else if (!ext.direction) {
|
|
157
181
|
ext.direction = 'sendrecv';
|
|
182
|
+
}
|
|
158
183
|
}
|
|
159
184
|
exports.validateRtpHeaderExtension = validateRtpHeaderExtension;
|
|
160
185
|
/**
|
|
@@ -163,38 +188,47 @@ exports.validateRtpHeaderExtension = validateRtpHeaderExtension;
|
|
|
163
188
|
* It throws if invalid.
|
|
164
189
|
*/
|
|
165
190
|
function validateRtpParameters(params) {
|
|
166
|
-
if (typeof params !== 'object')
|
|
191
|
+
if (typeof params !== 'object') {
|
|
167
192
|
throw new TypeError('params is not an object');
|
|
193
|
+
}
|
|
168
194
|
// mid is optional.
|
|
169
|
-
if (params.mid && typeof params.mid !== 'string')
|
|
195
|
+
if (params.mid && typeof params.mid !== 'string') {
|
|
170
196
|
throw new TypeError('params.mid is not a string');
|
|
197
|
+
}
|
|
171
198
|
// codecs is mandatory.
|
|
172
|
-
if (!Array.isArray(params.codecs))
|
|
199
|
+
if (!Array.isArray(params.codecs)) {
|
|
173
200
|
throw new TypeError('missing params.codecs');
|
|
201
|
+
}
|
|
174
202
|
for (const codec of params.codecs) {
|
|
175
203
|
validateRtpCodecParameters(codec);
|
|
176
204
|
}
|
|
177
205
|
// headerExtensions is optional. If unset, fill with an empty array.
|
|
178
|
-
if (params.headerExtensions && !Array.isArray(params.headerExtensions))
|
|
206
|
+
if (params.headerExtensions && !Array.isArray(params.headerExtensions)) {
|
|
179
207
|
throw new TypeError('params.headerExtensions is not an array');
|
|
180
|
-
|
|
208
|
+
}
|
|
209
|
+
else if (!params.headerExtensions) {
|
|
181
210
|
params.headerExtensions = [];
|
|
211
|
+
}
|
|
182
212
|
for (const ext of params.headerExtensions) {
|
|
183
213
|
validateRtpHeaderExtensionParameters(ext);
|
|
184
214
|
}
|
|
185
215
|
// encodings is optional. If unset, fill with an empty array.
|
|
186
|
-
if (params.encodings && !Array.isArray(params.encodings))
|
|
216
|
+
if (params.encodings && !Array.isArray(params.encodings)) {
|
|
187
217
|
throw new TypeError('params.encodings is not an array');
|
|
188
|
-
|
|
218
|
+
}
|
|
219
|
+
else if (!params.encodings) {
|
|
189
220
|
params.encodings = [];
|
|
221
|
+
}
|
|
190
222
|
for (const encoding of params.encodings) {
|
|
191
223
|
validateRtpEncodingParameters(encoding);
|
|
192
224
|
}
|
|
193
225
|
// rtcp is optional. If unset, fill with an empty object.
|
|
194
|
-
if (params.rtcp && typeof params.rtcp !== 'object')
|
|
226
|
+
if (params.rtcp && typeof params.rtcp !== 'object') {
|
|
195
227
|
throw new TypeError('params.rtcp is not an object');
|
|
196
|
-
|
|
228
|
+
}
|
|
229
|
+
else if (!params.rtcp) {
|
|
197
230
|
params.rtcp = {};
|
|
231
|
+
}
|
|
198
232
|
validateRtcpParameters(params.rtcp);
|
|
199
233
|
}
|
|
200
234
|
exports.validateRtpParameters = validateRtpParameters;
|
|
@@ -205,32 +239,39 @@ exports.validateRtpParameters = validateRtpParameters;
|
|
|
205
239
|
*/
|
|
206
240
|
function validateRtpCodecParameters(codec) {
|
|
207
241
|
const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i');
|
|
208
|
-
if (typeof codec !== 'object')
|
|
242
|
+
if (typeof codec !== 'object') {
|
|
209
243
|
throw new TypeError('codec is not an object');
|
|
244
|
+
}
|
|
210
245
|
// mimeType is mandatory.
|
|
211
|
-
if (!codec.mimeType || typeof codec.mimeType !== 'string')
|
|
246
|
+
if (!codec.mimeType || typeof codec.mimeType !== 'string') {
|
|
212
247
|
throw new TypeError('missing codec.mimeType');
|
|
248
|
+
}
|
|
213
249
|
const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType);
|
|
214
|
-
if (!mimeTypeMatch)
|
|
250
|
+
if (!mimeTypeMatch) {
|
|
215
251
|
throw new TypeError('invalid codec.mimeType');
|
|
252
|
+
}
|
|
216
253
|
// payloadType is mandatory.
|
|
217
|
-
if (typeof codec.payloadType !== 'number')
|
|
254
|
+
if (typeof codec.payloadType !== 'number') {
|
|
218
255
|
throw new TypeError('missing codec.payloadType');
|
|
256
|
+
}
|
|
219
257
|
// clockRate is mandatory.
|
|
220
|
-
if (typeof codec.clockRate !== 'number')
|
|
258
|
+
if (typeof codec.clockRate !== 'number') {
|
|
221
259
|
throw new TypeError('missing codec.clockRate');
|
|
260
|
+
}
|
|
222
261
|
const kind = mimeTypeMatch[1].toLowerCase();
|
|
223
262
|
// channels is optional. If unset, set it to 1 (just if audio).
|
|
224
263
|
if (kind === 'audio') {
|
|
225
|
-
if (typeof codec.channels !== 'number')
|
|
264
|
+
if (typeof codec.channels !== 'number') {
|
|
226
265
|
codec.channels = 1;
|
|
266
|
+
}
|
|
227
267
|
}
|
|
228
268
|
else {
|
|
229
269
|
delete codec.channels;
|
|
230
270
|
}
|
|
231
271
|
// parameters is optional. If unset, set it to an empty object.
|
|
232
|
-
if (!codec.parameters || typeof codec.parameters !== 'object')
|
|
272
|
+
if (!codec.parameters || typeof codec.parameters !== 'object') {
|
|
233
273
|
codec.parameters = {};
|
|
274
|
+
}
|
|
234
275
|
for (const key of Object.keys(codec.parameters)) {
|
|
235
276
|
let value = codec.parameters[key];
|
|
236
277
|
if (value === undefined) {
|
|
@@ -242,13 +283,15 @@ function validateRtpCodecParameters(codec) {
|
|
|
242
283
|
}
|
|
243
284
|
// Specific parameters validation.
|
|
244
285
|
if (key === 'apt') {
|
|
245
|
-
if (typeof value !== 'number')
|
|
286
|
+
if (typeof value !== 'number') {
|
|
246
287
|
throw new TypeError('invalid codec apt parameter');
|
|
288
|
+
}
|
|
247
289
|
}
|
|
248
290
|
}
|
|
249
291
|
// rtcpFeedback is optional. If unset, set it to an empty array.
|
|
250
|
-
if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback))
|
|
292
|
+
if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) {
|
|
251
293
|
codec.rtcpFeedback = [];
|
|
294
|
+
}
|
|
252
295
|
for (const fb of codec.rtcpFeedback) {
|
|
253
296
|
validateRtcpFeedback(fb);
|
|
254
297
|
}
|
|
@@ -260,30 +303,37 @@ exports.validateRtpCodecParameters = validateRtpCodecParameters;
|
|
|
260
303
|
* It throws if invalid.
|
|
261
304
|
*/
|
|
262
305
|
function validateRtpHeaderExtensionParameters(ext) {
|
|
263
|
-
if (typeof ext !== 'object')
|
|
306
|
+
if (typeof ext !== 'object') {
|
|
264
307
|
throw new TypeError('ext is not an object');
|
|
308
|
+
}
|
|
265
309
|
// uri is mandatory.
|
|
266
|
-
if (!ext.uri || typeof ext.uri !== 'string')
|
|
310
|
+
if (!ext.uri || typeof ext.uri !== 'string') {
|
|
267
311
|
throw new TypeError('missing ext.uri');
|
|
312
|
+
}
|
|
268
313
|
// id is mandatory.
|
|
269
|
-
if (typeof ext.id !== 'number')
|
|
314
|
+
if (typeof ext.id !== 'number') {
|
|
270
315
|
throw new TypeError('missing ext.id');
|
|
316
|
+
}
|
|
271
317
|
// encrypt is optional. If unset set it to false.
|
|
272
|
-
if (ext.encrypt && typeof ext.encrypt !== 'boolean')
|
|
318
|
+
if (ext.encrypt && typeof ext.encrypt !== 'boolean') {
|
|
273
319
|
throw new TypeError('invalid ext.encrypt');
|
|
274
|
-
|
|
320
|
+
}
|
|
321
|
+
else if (!ext.encrypt) {
|
|
275
322
|
ext.encrypt = false;
|
|
323
|
+
}
|
|
276
324
|
// parameters is optional. If unset, set it to an empty object.
|
|
277
|
-
if (!ext.parameters || typeof ext.parameters !== 'object')
|
|
325
|
+
if (!ext.parameters || typeof ext.parameters !== 'object') {
|
|
278
326
|
ext.parameters = {};
|
|
327
|
+
}
|
|
279
328
|
for (const key of Object.keys(ext.parameters)) {
|
|
280
329
|
let value = ext.parameters[key];
|
|
281
330
|
if (value === undefined) {
|
|
282
331
|
ext.parameters[key] = '';
|
|
283
332
|
value = '';
|
|
284
333
|
}
|
|
285
|
-
if (typeof value !== 'string' && typeof value !== 'number')
|
|
334
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
286
335
|
throw new TypeError('invalid header extension parameter');
|
|
336
|
+
}
|
|
287
337
|
}
|
|
288
338
|
}
|
|
289
339
|
exports.validateRtpHeaderExtensionParameters = validateRtpHeaderExtensionParameters;
|
|
@@ -293,29 +343,35 @@ exports.validateRtpHeaderExtensionParameters = validateRtpHeaderExtensionParamet
|
|
|
293
343
|
* It throws if invalid.
|
|
294
344
|
*/
|
|
295
345
|
function validateRtpEncodingParameters(encoding) {
|
|
296
|
-
if (typeof encoding !== 'object')
|
|
346
|
+
if (typeof encoding !== 'object') {
|
|
297
347
|
throw new TypeError('encoding is not an object');
|
|
348
|
+
}
|
|
298
349
|
// ssrc is optional.
|
|
299
|
-
if (encoding.ssrc && typeof encoding.ssrc !== 'number')
|
|
350
|
+
if (encoding.ssrc && typeof encoding.ssrc !== 'number') {
|
|
300
351
|
throw new TypeError('invalid encoding.ssrc');
|
|
352
|
+
}
|
|
301
353
|
// rid is optional.
|
|
302
|
-
if (encoding.rid && typeof encoding.rid !== 'string')
|
|
354
|
+
if (encoding.rid && typeof encoding.rid !== 'string') {
|
|
303
355
|
throw new TypeError('invalid encoding.rid');
|
|
356
|
+
}
|
|
304
357
|
// rtx is optional.
|
|
305
358
|
if (encoding.rtx && typeof encoding.rtx !== 'object') {
|
|
306
359
|
throw new TypeError('invalid encoding.rtx');
|
|
307
360
|
}
|
|
308
361
|
else if (encoding.rtx) {
|
|
309
362
|
// RTX ssrc is mandatory if rtx is present.
|
|
310
|
-
if (typeof encoding.rtx.ssrc !== 'number')
|
|
363
|
+
if (typeof encoding.rtx.ssrc !== 'number') {
|
|
311
364
|
throw new TypeError('missing encoding.rtx.ssrc');
|
|
365
|
+
}
|
|
312
366
|
}
|
|
313
367
|
// dtx is optional. If unset set it to false.
|
|
314
|
-
if (!encoding.dtx || typeof encoding.dtx !== 'boolean')
|
|
368
|
+
if (!encoding.dtx || typeof encoding.dtx !== 'boolean') {
|
|
315
369
|
encoding.dtx = false;
|
|
370
|
+
}
|
|
316
371
|
// scalabilityMode is optional.
|
|
317
|
-
if (encoding.scalabilityMode && typeof encoding.scalabilityMode !== 'string')
|
|
372
|
+
if (encoding.scalabilityMode && typeof encoding.scalabilityMode !== 'string') {
|
|
318
373
|
throw new TypeError('invalid encoding.scalabilityMode');
|
|
374
|
+
}
|
|
319
375
|
}
|
|
320
376
|
exports.validateRtpEncodingParameters = validateRtpEncodingParameters;
|
|
321
377
|
/**
|
|
@@ -324,14 +380,17 @@ exports.validateRtpEncodingParameters = validateRtpEncodingParameters;
|
|
|
324
380
|
* It throws if invalid.
|
|
325
381
|
*/
|
|
326
382
|
function validateRtcpParameters(rtcp) {
|
|
327
|
-
if (typeof rtcp !== 'object')
|
|
383
|
+
if (typeof rtcp !== 'object') {
|
|
328
384
|
throw new TypeError('rtcp is not an object');
|
|
385
|
+
}
|
|
329
386
|
// cname is optional.
|
|
330
|
-
if (rtcp.cname && typeof rtcp.cname !== 'string')
|
|
387
|
+
if (rtcp.cname && typeof rtcp.cname !== 'string') {
|
|
331
388
|
throw new TypeError('invalid rtcp.cname');
|
|
389
|
+
}
|
|
332
390
|
// reducedSize is optional. If unset set it to true.
|
|
333
|
-
if (!rtcp.reducedSize || typeof rtcp.reducedSize !== 'boolean')
|
|
391
|
+
if (!rtcp.reducedSize || typeof rtcp.reducedSize !== 'boolean') {
|
|
334
392
|
rtcp.reducedSize = true;
|
|
393
|
+
}
|
|
335
394
|
}
|
|
336
395
|
exports.validateRtcpParameters = validateRtcpParameters;
|
|
337
396
|
/**
|
|
@@ -340,11 +399,13 @@ exports.validateRtcpParameters = validateRtcpParameters;
|
|
|
340
399
|
* It throws if invalid.
|
|
341
400
|
*/
|
|
342
401
|
function validateSctpCapabilities(caps) {
|
|
343
|
-
if (typeof caps !== 'object')
|
|
402
|
+
if (typeof caps !== 'object') {
|
|
344
403
|
throw new TypeError('caps is not an object');
|
|
404
|
+
}
|
|
345
405
|
// numStreams is mandatory.
|
|
346
|
-
if (!caps.numStreams || typeof caps.numStreams !== 'object')
|
|
406
|
+
if (!caps.numStreams || typeof caps.numStreams !== 'object') {
|
|
347
407
|
throw new TypeError('missing caps.numStreams');
|
|
408
|
+
}
|
|
348
409
|
validateNumSctpStreams(caps.numStreams);
|
|
349
410
|
}
|
|
350
411
|
exports.validateSctpCapabilities = validateSctpCapabilities;
|
|
@@ -354,14 +415,17 @@ exports.validateSctpCapabilities = validateSctpCapabilities;
|
|
|
354
415
|
* It throws if invalid.
|
|
355
416
|
*/
|
|
356
417
|
function validateNumSctpStreams(numStreams) {
|
|
357
|
-
if (typeof numStreams !== 'object')
|
|
418
|
+
if (typeof numStreams !== 'object') {
|
|
358
419
|
throw new TypeError('numStreams is not an object');
|
|
420
|
+
}
|
|
359
421
|
// OS is mandatory.
|
|
360
|
-
if (typeof numStreams.OS !== 'number')
|
|
422
|
+
if (typeof numStreams.OS !== 'number') {
|
|
361
423
|
throw new TypeError('missing numStreams.OS');
|
|
424
|
+
}
|
|
362
425
|
// MIS is mandatory.
|
|
363
|
-
if (typeof numStreams.MIS !== 'number')
|
|
426
|
+
if (typeof numStreams.MIS !== 'number') {
|
|
364
427
|
throw new TypeError('missing numStreams.MIS');
|
|
428
|
+
}
|
|
365
429
|
}
|
|
366
430
|
exports.validateNumSctpStreams = validateNumSctpStreams;
|
|
367
431
|
/**
|
|
@@ -370,20 +434,25 @@ exports.validateNumSctpStreams = validateNumSctpStreams;
|
|
|
370
434
|
* It throws if invalid.
|
|
371
435
|
*/
|
|
372
436
|
function validateSctpParameters(params) {
|
|
373
|
-
if (typeof params !== 'object')
|
|
437
|
+
if (typeof params !== 'object') {
|
|
374
438
|
throw new TypeError('params is not an object');
|
|
439
|
+
}
|
|
375
440
|
// port is mandatory.
|
|
376
|
-
if (typeof params.port !== 'number')
|
|
441
|
+
if (typeof params.port !== 'number') {
|
|
377
442
|
throw new TypeError('missing params.port');
|
|
443
|
+
}
|
|
378
444
|
// OS is mandatory.
|
|
379
|
-
if (typeof params.OS !== 'number')
|
|
445
|
+
if (typeof params.OS !== 'number') {
|
|
380
446
|
throw new TypeError('missing params.OS');
|
|
447
|
+
}
|
|
381
448
|
// MIS is mandatory.
|
|
382
|
-
if (typeof params.MIS !== 'number')
|
|
449
|
+
if (typeof params.MIS !== 'number') {
|
|
383
450
|
throw new TypeError('missing params.MIS');
|
|
451
|
+
}
|
|
384
452
|
// maxMessageSize is mandatory.
|
|
385
|
-
if (typeof params.maxMessageSize !== 'number')
|
|
453
|
+
if (typeof params.maxMessageSize !== 'number') {
|
|
386
454
|
throw new TypeError('missing params.maxMessageSize');
|
|
455
|
+
}
|
|
387
456
|
}
|
|
388
457
|
exports.validateSctpParameters = validateSctpParameters;
|
|
389
458
|
/**
|
|
@@ -392,25 +461,32 @@ exports.validateSctpParameters = validateSctpParameters;
|
|
|
392
461
|
* It throws if invalid.
|
|
393
462
|
*/
|
|
394
463
|
function validateSctpStreamParameters(params) {
|
|
395
|
-
if (typeof params !== 'object')
|
|
464
|
+
if (typeof params !== 'object') {
|
|
396
465
|
throw new TypeError('params is not an object');
|
|
466
|
+
}
|
|
397
467
|
// streamId is mandatory.
|
|
398
|
-
if (typeof params.streamId !== 'number')
|
|
468
|
+
if (typeof params.streamId !== 'number') {
|
|
399
469
|
throw new TypeError('missing params.streamId');
|
|
470
|
+
}
|
|
400
471
|
// ordered is optional.
|
|
401
472
|
let orderedGiven = false;
|
|
402
|
-
if (typeof params.ordered === 'boolean')
|
|
473
|
+
if (typeof params.ordered === 'boolean') {
|
|
403
474
|
orderedGiven = true;
|
|
404
|
-
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
405
477
|
params.ordered = true;
|
|
478
|
+
}
|
|
406
479
|
// maxPacketLifeTime is optional.
|
|
407
|
-
if (params.maxPacketLifeTime && typeof params.maxPacketLifeTime !== 'number')
|
|
480
|
+
if (params.maxPacketLifeTime && typeof params.maxPacketLifeTime !== 'number') {
|
|
408
481
|
throw new TypeError('invalid params.maxPacketLifeTime');
|
|
482
|
+
}
|
|
409
483
|
// maxRetransmits is optional.
|
|
410
|
-
if (params.maxRetransmits && typeof params.maxRetransmits !== 'number')
|
|
484
|
+
if (params.maxRetransmits && typeof params.maxRetransmits !== 'number') {
|
|
411
485
|
throw new TypeError('invalid params.maxRetransmits');
|
|
412
|
-
|
|
486
|
+
}
|
|
487
|
+
if (params.maxPacketLifeTime && params.maxRetransmits) {
|
|
413
488
|
throw new TypeError('cannot provide both maxPacketLifeTime and maxRetransmits');
|
|
489
|
+
}
|
|
414
490
|
if (orderedGiven &&
|
|
415
491
|
params.ordered &&
|
|
416
492
|
(params.maxPacketLifeTime || params.maxRetransmits)) {
|
|
@@ -420,11 +496,13 @@ function validateSctpStreamParameters(params) {
|
|
|
420
496
|
params.ordered = false;
|
|
421
497
|
}
|
|
422
498
|
// label is optional.
|
|
423
|
-
if (params.label && typeof params.label !== 'string')
|
|
499
|
+
if (params.label && typeof params.label !== 'string') {
|
|
424
500
|
throw new TypeError('invalid params.label');
|
|
501
|
+
}
|
|
425
502
|
// protocol is optional.
|
|
426
|
-
if (params.protocol && typeof params.protocol !== 'string')
|
|
503
|
+
if (params.protocol && typeof params.protocol !== 'string') {
|
|
427
504
|
throw new TypeError('invalid params.protocol');
|
|
505
|
+
}
|
|
428
506
|
}
|
|
429
507
|
exports.validateSctpStreamParameters = validateSctpStreamParameters;
|
|
430
508
|
/**
|
|
@@ -437,12 +515,14 @@ function getExtendedRtpCapabilities(localCaps, remoteCaps) {
|
|
|
437
515
|
};
|
|
438
516
|
// Match media codecs and keep the order preferred by remoteCaps.
|
|
439
517
|
for (const remoteCodec of remoteCaps.codecs || []) {
|
|
440
|
-
if (isRtxCodec(remoteCodec))
|
|
518
|
+
if (isRtxCodec(remoteCodec)) {
|
|
441
519
|
continue;
|
|
520
|
+
}
|
|
442
521
|
const matchingLocalCodec = (localCaps.codecs || [])
|
|
443
522
|
.find((localCodec) => (matchCodecs(localCodec, remoteCodec, { strict: true, modify: true })));
|
|
444
|
-
if (!matchingLocalCodec)
|
|
523
|
+
if (!matchingLocalCodec) {
|
|
445
524
|
continue;
|
|
525
|
+
}
|
|
446
526
|
const extendedCodec = {
|
|
447
527
|
mimeType: matchingLocalCodec.mimeType,
|
|
448
528
|
kind: matchingLocalCodec.kind,
|
|
@@ -475,8 +555,9 @@ function getExtendedRtpCapabilities(localCaps, remoteCaps) {
|
|
|
475
555
|
for (const remoteExt of remoteCaps.headerExtensions) {
|
|
476
556
|
const matchingLocalExt = localCaps.headerExtensions
|
|
477
557
|
.find((localExt) => (matchHeaderExtensions(localExt, remoteExt)));
|
|
478
|
-
if (!matchingLocalExt)
|
|
558
|
+
if (!matchingLocalExt) {
|
|
479
559
|
continue;
|
|
560
|
+
}
|
|
480
561
|
const extendedExt = {
|
|
481
562
|
kind: remoteExt.kind,
|
|
482
563
|
uri: remoteExt.uri,
|
|
@@ -525,8 +606,9 @@ function getRecvRtpCapabilities(extendedRtpCapabilities) {
|
|
|
525
606
|
};
|
|
526
607
|
rtpCapabilities.codecs.push(codec);
|
|
527
608
|
// Add RTX codec.
|
|
528
|
-
if (!extendedCodec.remoteRtxPayloadType)
|
|
609
|
+
if (!extendedCodec.remoteRtxPayloadType) {
|
|
529
610
|
continue;
|
|
611
|
+
}
|
|
530
612
|
const rtxCodec = {
|
|
531
613
|
mimeType: `${extendedCodec.kind}/rtx`,
|
|
532
614
|
kind: extendedCodec.kind,
|
|
@@ -571,8 +653,9 @@ function getSendingRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
571
653
|
rtcp: {}
|
|
572
654
|
};
|
|
573
655
|
for (const extendedCodec of extendedRtpCapabilities.codecs) {
|
|
574
|
-
if (extendedCodec.kind !== kind)
|
|
656
|
+
if (extendedCodec.kind !== kind) {
|
|
575
657
|
continue;
|
|
658
|
+
}
|
|
576
659
|
const codec = {
|
|
577
660
|
mimeType: extendedCodec.mimeType,
|
|
578
661
|
payloadType: extendedCodec.localPayloadType,
|
|
@@ -626,8 +709,9 @@ function getSendingRemoteRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
626
709
|
rtcp: {}
|
|
627
710
|
};
|
|
628
711
|
for (const extendedCodec of extendedRtpCapabilities.codecs) {
|
|
629
|
-
if (extendedCodec.kind !== kind)
|
|
712
|
+
if (extendedCodec.kind !== kind) {
|
|
630
713
|
continue;
|
|
714
|
+
}
|
|
631
715
|
const codec = {
|
|
632
716
|
mimeType: extendedCodec.mimeType,
|
|
633
717
|
payloadType: extendedCodec.localPayloadType,
|
|
@@ -704,21 +788,24 @@ function reduceCodecs(codecs, capCodec) {
|
|
|
704
788
|
// If no capability codec is given, take the first one (and RTX).
|
|
705
789
|
if (!capCodec) {
|
|
706
790
|
filteredCodecs.push(codecs[0]);
|
|
707
|
-
if (isRtxCodec(codecs[1]))
|
|
791
|
+
if (isRtxCodec(codecs[1])) {
|
|
708
792
|
filteredCodecs.push(codecs[1]);
|
|
793
|
+
}
|
|
709
794
|
}
|
|
710
795
|
// Otherwise look for a compatible set of codecs.
|
|
711
796
|
else {
|
|
712
797
|
for (let idx = 0; idx < codecs.length; ++idx) {
|
|
713
798
|
if (matchCodecs(codecs[idx], capCodec)) {
|
|
714
799
|
filteredCodecs.push(codecs[idx]);
|
|
715
|
-
if (isRtxCodec(codecs[idx + 1]))
|
|
800
|
+
if (isRtxCodec(codecs[idx + 1])) {
|
|
716
801
|
filteredCodecs.push(codecs[idx + 1]);
|
|
802
|
+
}
|
|
717
803
|
break;
|
|
718
804
|
}
|
|
719
805
|
}
|
|
720
|
-
if (filteredCodecs.length === 0)
|
|
806
|
+
if (filteredCodecs.length === 0) {
|
|
721
807
|
throw new TypeError('no matching codec found');
|
|
808
|
+
}
|
|
722
809
|
}
|
|
723
810
|
return filteredCodecs;
|
|
724
811
|
}
|
|
@@ -759,27 +846,32 @@ exports.canSend = canSend;
|
|
|
759
846
|
function canReceive(rtpParameters, extendedRtpCapabilities) {
|
|
760
847
|
// This may throw.
|
|
761
848
|
validateRtpParameters(rtpParameters);
|
|
762
|
-
if (rtpParameters.codecs.length === 0)
|
|
849
|
+
if (rtpParameters.codecs.length === 0) {
|
|
763
850
|
return false;
|
|
851
|
+
}
|
|
764
852
|
const firstMediaCodec = rtpParameters.codecs[0];
|
|
765
853
|
return extendedRtpCapabilities.codecs
|
|
766
854
|
.some((codec) => codec.remotePayloadType === firstMediaCodec.payloadType);
|
|
767
855
|
}
|
|
768
856
|
exports.canReceive = canReceive;
|
|
769
857
|
function isRtxCodec(codec) {
|
|
770
|
-
if (!codec)
|
|
858
|
+
if (!codec) {
|
|
771
859
|
return false;
|
|
860
|
+
}
|
|
772
861
|
return /.+\/rtx$/i.test(codec.mimeType);
|
|
773
862
|
}
|
|
774
863
|
function matchCodecs(aCodec, bCodec, { strict = false, modify = false } = {}) {
|
|
775
864
|
const aMimeType = aCodec.mimeType.toLowerCase();
|
|
776
865
|
const bMimeType = bCodec.mimeType.toLowerCase();
|
|
777
|
-
if (aMimeType !== bMimeType)
|
|
866
|
+
if (aMimeType !== bMimeType) {
|
|
778
867
|
return false;
|
|
779
|
-
|
|
868
|
+
}
|
|
869
|
+
if (aCodec.clockRate !== bCodec.clockRate) {
|
|
780
870
|
return false;
|
|
781
|
-
|
|
871
|
+
}
|
|
872
|
+
if (aCodec.channels !== bCodec.channels) {
|
|
782
873
|
return false;
|
|
874
|
+
}
|
|
783
875
|
// Per codec special checks.
|
|
784
876
|
switch (aMimeType) {
|
|
785
877
|
case 'video/h264':
|
|
@@ -787,10 +879,12 @@ function matchCodecs(aCodec, bCodec, { strict = false, modify = false } = {}) {
|
|
|
787
879
|
if (strict) {
|
|
788
880
|
const aPacketizationMode = aCodec.parameters['packetization-mode'] || 0;
|
|
789
881
|
const bPacketizationMode = bCodec.parameters['packetization-mode'] || 0;
|
|
790
|
-
if (aPacketizationMode !== bPacketizationMode)
|
|
882
|
+
if (aPacketizationMode !== bPacketizationMode) {
|
|
791
883
|
return false;
|
|
792
|
-
|
|
884
|
+
}
|
|
885
|
+
if (!h264.isSameProfile(aCodec.parameters, bCodec.parameters)) {
|
|
793
886
|
return false;
|
|
887
|
+
}
|
|
794
888
|
let selectedProfileLevelId;
|
|
795
889
|
try {
|
|
796
890
|
selectedProfileLevelId =
|
|
@@ -817,8 +911,9 @@ function matchCodecs(aCodec, bCodec, { strict = false, modify = false } = {}) {
|
|
|
817
911
|
if (strict) {
|
|
818
912
|
const aProfileId = aCodec.parameters['profile-id'] || 0;
|
|
819
913
|
const bProfileId = bCodec.parameters['profile-id'] || 0;
|
|
820
|
-
if (aProfileId !== bProfileId)
|
|
914
|
+
if (aProfileId !== bProfileId) {
|
|
821
915
|
return false;
|
|
916
|
+
}
|
|
822
917
|
}
|
|
823
918
|
break;
|
|
824
919
|
}
|
|
@@ -826,10 +921,12 @@ function matchCodecs(aCodec, bCodec, { strict = false, modify = false } = {}) {
|
|
|
826
921
|
return true;
|
|
827
922
|
}
|
|
828
923
|
function matchHeaderExtensions(aExt, bExt) {
|
|
829
|
-
if (aExt.kind && bExt.kind && aExt.kind !== bExt.kind)
|
|
924
|
+
if (aExt.kind && bExt.kind && aExt.kind !== bExt.kind) {
|
|
830
925
|
return false;
|
|
831
|
-
|
|
926
|
+
}
|
|
927
|
+
if (aExt.uri !== bExt.uri) {
|
|
832
928
|
return false;
|
|
929
|
+
}
|
|
833
930
|
return true;
|
|
834
931
|
}
|
|
835
932
|
function reduceRtcpFeedback(codecA, codecB) {
|
|
@@ -838,8 +935,9 @@ function reduceRtcpFeedback(codecA, codecB) {
|
|
|
838
935
|
const matchingBFb = (codecB.rtcpFeedback || [])
|
|
839
936
|
.find((bFb) => (bFb.type === aFb.type &&
|
|
840
937
|
(bFb.parameter === aFb.parameter || (!bFb.parameter && !aFb.parameter))));
|
|
841
|
-
if (matchingBFb)
|
|
938
|
+
if (matchingBFb) {
|
|
842
939
|
reducedRtcpFeedback.push(matchingBFb);
|
|
940
|
+
}
|
|
843
941
|
}
|
|
844
942
|
return reducedRtcpFeedback;
|
|
845
943
|
}
|