mediasoup-client 3.7.1 → 3.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -112
- package/lib/Consumer.d.ts +1 -1
- package/lib/Consumer.d.ts.map +1 -1
- package/lib/Consumer.js +1 -1
- package/lib/DataConsumer.d.ts +1 -1
- package/lib/DataConsumer.d.ts.map +1 -1
- package/lib/DataConsumer.js +1 -1
- package/lib/DataProducer.d.ts +1 -1
- package/lib/DataProducer.d.ts.map +1 -1
- package/lib/DataProducer.js +1 -1
- package/lib/Device.d.ts +3 -3
- package/lib/Device.d.ts.map +1 -1
- package/lib/Device.js +66 -89
- package/lib/EnhancedEventEmitter.d.ts.map +1 -1
- package/lib/Logger.d.ts.map +1 -1
- package/lib/Producer.d.ts +5 -15
- package/lib/Producer.d.ts.map +1 -1
- package/lib/Producer.js +2 -2
- package/lib/RtpParameters.d.ts.map +1 -1
- package/lib/SctpParameters.d.ts.map +1 -1
- package/lib/Transport.d.ts +21 -11
- package/lib/Transport.d.ts.map +1 -1
- package/lib/Transport.js +73 -55
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +6 -6
- package/lib/handlers/Chrome111.d.ts +4 -4
- package/lib/handlers/Chrome111.d.ts.map +1 -1
- package/lib/handlers/Chrome111.js +79 -78
- package/lib/handlers/Chrome55.d.ts +4 -4
- package/lib/handlers/Chrome55.d.ts.map +1 -1
- package/lib/handlers/Chrome55.js +88 -81
- package/lib/handlers/Chrome67.d.ts +4 -4
- package/lib/handlers/Chrome67.d.ts.map +1 -1
- package/lib/handlers/Chrome67.js +90 -84
- package/lib/handlers/Chrome70.d.ts +4 -4
- package/lib/handlers/Chrome70.d.ts.map +1 -1
- package/lib/handlers/Chrome70.js +81 -79
- package/lib/handlers/Chrome74.d.ts +4 -4
- package/lib/handlers/Chrome74.d.ts.map +1 -1
- package/lib/handlers/Chrome74.js +80 -79
- package/lib/handlers/Edge11.d.ts +1 -1
- package/lib/handlers/Edge11.d.ts.map +1 -1
- package/lib/handlers/Edge11.js +66 -78
- package/lib/handlers/FakeHandler.d.ts +3 -3
- package/lib/handlers/FakeHandler.d.ts.map +1 -1
- package/lib/handlers/FakeHandler.js +20 -22
- package/lib/handlers/Firefox60.d.ts +4 -4
- package/lib/handlers/Firefox60.d.ts.map +1 -1
- package/lib/handlers/Firefox60.js +80 -73
- package/lib/handlers/HandlerInterface.d.ts +1 -0
- package/lib/handlers/HandlerInterface.d.ts.map +1 -1
- package/lib/handlers/ReactNative.d.ts +4 -4
- package/lib/handlers/ReactNative.d.ts.map +1 -1
- package/lib/handlers/ReactNative.js +89 -83
- package/lib/handlers/ReactNativeUnifiedPlan.d.ts +4 -4
- package/lib/handlers/ReactNativeUnifiedPlan.d.ts.map +1 -1
- package/lib/handlers/ReactNativeUnifiedPlan.js +80 -79
- package/lib/handlers/Safari11.d.ts +4 -4
- package/lib/handlers/Safari11.d.ts.map +1 -1
- package/lib/handlers/Safari11.js +89 -82
- package/lib/handlers/Safari12.d.ts +4 -4
- package/lib/handlers/Safari12.d.ts.map +1 -1
- package/lib/handlers/Safari12.js +80 -78
- package/lib/handlers/ortc/edgeUtils.d.ts.map +1 -1
- package/lib/handlers/ortc/utils.d.ts.map +1 -1
- package/lib/handlers/ortc/utils.js +2 -2
- package/lib/handlers/sdp/MediaSection.d.ts +5 -5
- package/lib/handlers/sdp/MediaSection.d.ts.map +1 -1
- package/lib/handlers/sdp/MediaSection.js +296 -318
- package/lib/handlers/sdp/RemoteSdp.d.ts +5 -5
- package/lib/handlers/sdp/RemoteSdp.d.ts.map +1 -1
- package/lib/handlers/sdp/RemoteSdp.js +28 -30
- package/lib/handlers/sdp/commonUtils.d.ts +8 -5
- package/lib/handlers/sdp/commonUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/commonUtils.js +71 -59
- package/lib/handlers/sdp/plainRtpUtils.d.ts +2 -2
- package/lib/handlers/sdp/plainRtpUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/plainRtpUtils.js +5 -7
- package/lib/handlers/sdp/planBUtils.d.ts +2 -2
- package/lib/handlers/sdp/planBUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/planBUtils.js +11 -14
- package/lib/handlers/sdp/unifiedPlanUtils.d.ts +2 -2
- package/lib/handlers/sdp/unifiedPlanUtils.d.ts.map +1 -1
- package/lib/handlers/sdp/unifiedPlanUtils.js +11 -14
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/ortc.d.ts +4 -58
- package/lib/ortc.d.ts.map +1 -1
- package/lib/ortc.js +432 -478
- package/lib/scalabilityModes.d.ts.map +1 -1
- package/lib/scalabilityModes.js +2 -2
- package/lib/{tests → test}/fakeParameters.d.ts +3 -2
- package/lib/test/fakeParameters.d.ts.map +1 -0
- package/lib/test/fakeParameters.js +654 -0
- package/lib/{tests → test}/test.d.ts.map +1 -1
- package/lib/{tests → test}/test.js +469 -496
- package/lib/test/uaTestCases.d.ts.map +1 -0
- package/lib/{tests → test}/uaTestCases.js +17 -17
- package/lib/types.d.ts.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/npm-scripts.mjs +75 -103
- package/package.json +93 -90
- package/lib/tests/fakeParameters.d.ts.map +0 -1
- package/lib/tests/fakeParameters.js +0 -662
- package/lib/tests/uaTestCases.d.ts.map +0 -1
- /package/lib/{tests → test}/test.d.ts +0 -0
- /package/lib/{tests → test}/uaTestCases.d.ts +0 -0
package/lib/ortc.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.canReceive = exports.canSend = exports.generateProbatorRtpParameters = exports.reduceCodecs = exports.getSendingRemoteRtpParameters = exports.getSendingRtpParameters = exports.getRecvRtpCapabilities = exports.getExtendedRtpCapabilities = exports.
|
|
26
|
+
exports.canReceive = exports.canSend = exports.generateProbatorRtpParameters = exports.reduceCodecs = exports.getSendingRemoteRtpParameters = exports.getSendingRtpParameters = exports.getRecvRtpCapabilities = exports.getExtendedRtpCapabilities = exports.validateSctpCapabilities = exports.validateSctpStreamParameters = exports.validateRtpParameters = exports.validateRtpCapabilities = void 0;
|
|
27
27
|
const h264 = __importStar(require("h264-profile-level-id"));
|
|
28
28
|
const utils = __importStar(require("./utils"));
|
|
29
29
|
const RTP_PROBATOR_MID = 'probator';
|
|
@@ -60,128 +60,6 @@ function validateRtpCapabilities(caps) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
exports.validateRtpCapabilities = validateRtpCapabilities;
|
|
63
|
-
/**
|
|
64
|
-
* Validates RtpCodecCapability. It may modify given data by adding missing
|
|
65
|
-
* fields with default values.
|
|
66
|
-
* It throws if invalid.
|
|
67
|
-
*/
|
|
68
|
-
function validateRtpCodecCapability(codec) {
|
|
69
|
-
const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i');
|
|
70
|
-
if (typeof codec !== 'object') {
|
|
71
|
-
throw new TypeError('codec is not an object');
|
|
72
|
-
}
|
|
73
|
-
// mimeType is mandatory.
|
|
74
|
-
if (!codec.mimeType || typeof codec.mimeType !== 'string') {
|
|
75
|
-
throw new TypeError('missing codec.mimeType');
|
|
76
|
-
}
|
|
77
|
-
const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType);
|
|
78
|
-
if (!mimeTypeMatch) {
|
|
79
|
-
throw new TypeError('invalid codec.mimeType');
|
|
80
|
-
}
|
|
81
|
-
// Just override kind with media component of mimeType.
|
|
82
|
-
codec.kind = mimeTypeMatch[1].toLowerCase();
|
|
83
|
-
// preferredPayloadType is optional.
|
|
84
|
-
if (codec.preferredPayloadType && typeof codec.preferredPayloadType !== 'number') {
|
|
85
|
-
throw new TypeError('invalid codec.preferredPayloadType');
|
|
86
|
-
}
|
|
87
|
-
// clockRate is mandatory.
|
|
88
|
-
if (typeof codec.clockRate !== 'number') {
|
|
89
|
-
throw new TypeError('missing codec.clockRate');
|
|
90
|
-
}
|
|
91
|
-
// channels is optional. If unset, set it to 1 (just if audio).
|
|
92
|
-
if (codec.kind === 'audio') {
|
|
93
|
-
if (typeof codec.channels !== 'number') {
|
|
94
|
-
codec.channels = 1;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
delete codec.channels;
|
|
99
|
-
}
|
|
100
|
-
// parameters is optional. If unset, set it to an empty object.
|
|
101
|
-
if (!codec.parameters || typeof codec.parameters !== 'object') {
|
|
102
|
-
codec.parameters = {};
|
|
103
|
-
}
|
|
104
|
-
for (const key of Object.keys(codec.parameters)) {
|
|
105
|
-
let value = codec.parameters[key];
|
|
106
|
-
if (value === undefined) {
|
|
107
|
-
codec.parameters[key] = '';
|
|
108
|
-
value = '';
|
|
109
|
-
}
|
|
110
|
-
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
111
|
-
throw new TypeError(`invalid codec parameter [key:${key}s, value:${value}]`);
|
|
112
|
-
}
|
|
113
|
-
// Specific parameters validation.
|
|
114
|
-
if (key === 'apt') {
|
|
115
|
-
if (typeof value !== 'number') {
|
|
116
|
-
throw new TypeError('invalid codec apt parameter');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// rtcpFeedback is optional. If unset, set it to an empty array.
|
|
121
|
-
if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) {
|
|
122
|
-
codec.rtcpFeedback = [];
|
|
123
|
-
}
|
|
124
|
-
for (const fb of codec.rtcpFeedback) {
|
|
125
|
-
validateRtcpFeedback(fb);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
exports.validateRtpCodecCapability = validateRtpCodecCapability;
|
|
129
|
-
/**
|
|
130
|
-
* Validates RtcpFeedback. It may modify given data by adding missing
|
|
131
|
-
* fields with default values.
|
|
132
|
-
* It throws if invalid.
|
|
133
|
-
*/
|
|
134
|
-
function validateRtcpFeedback(fb) {
|
|
135
|
-
if (typeof fb !== 'object') {
|
|
136
|
-
throw new TypeError('fb is not an object');
|
|
137
|
-
}
|
|
138
|
-
// type is mandatory.
|
|
139
|
-
if (!fb.type || typeof fb.type !== 'string') {
|
|
140
|
-
throw new TypeError('missing fb.type');
|
|
141
|
-
}
|
|
142
|
-
// parameter is optional. If unset set it to an empty string.
|
|
143
|
-
if (!fb.parameter || typeof fb.parameter !== 'string') {
|
|
144
|
-
fb.parameter = '';
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
exports.validateRtcpFeedback = validateRtcpFeedback;
|
|
148
|
-
/**
|
|
149
|
-
* Validates RtpHeaderExtension. It may modify given data by adding missing
|
|
150
|
-
* fields with default values.
|
|
151
|
-
* It throws if invalid.
|
|
152
|
-
*/
|
|
153
|
-
function validateRtpHeaderExtension(ext) {
|
|
154
|
-
if (typeof ext !== 'object') {
|
|
155
|
-
throw new TypeError('ext is not an object');
|
|
156
|
-
}
|
|
157
|
-
// kind is mandatory.
|
|
158
|
-
if (ext.kind !== 'audio' && ext.kind !== 'video') {
|
|
159
|
-
throw new TypeError('invalid ext.kind');
|
|
160
|
-
}
|
|
161
|
-
// uri is mandatory.
|
|
162
|
-
if (!ext.uri || typeof ext.uri !== 'string') {
|
|
163
|
-
throw new TypeError('missing ext.uri');
|
|
164
|
-
}
|
|
165
|
-
// preferredId is mandatory.
|
|
166
|
-
if (typeof ext.preferredId !== 'number') {
|
|
167
|
-
throw new TypeError('missing ext.preferredId');
|
|
168
|
-
}
|
|
169
|
-
// preferredEncrypt is optional. If unset set it to false.
|
|
170
|
-
if (ext.preferredEncrypt && typeof ext.preferredEncrypt !== 'boolean') {
|
|
171
|
-
throw new TypeError('invalid ext.preferredEncrypt');
|
|
172
|
-
}
|
|
173
|
-
else if (!ext.preferredEncrypt) {
|
|
174
|
-
ext.preferredEncrypt = false;
|
|
175
|
-
}
|
|
176
|
-
// direction is optional. If unset set it to sendrecv.
|
|
177
|
-
if (ext.direction && typeof ext.direction !== 'string') {
|
|
178
|
-
throw new TypeError('invalid ext.direction');
|
|
179
|
-
}
|
|
180
|
-
else if (!ext.direction) {
|
|
181
|
-
ext.direction = 'sendrecv';
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
exports.validateRtpHeaderExtension = validateRtpHeaderExtension;
|
|
185
63
|
/**
|
|
186
64
|
* Validates RtpParameters. It may modify given data by adding missing
|
|
187
65
|
* fields with default values.
|
|
@@ -233,293 +111,87 @@ function validateRtpParameters(params) {
|
|
|
233
111
|
}
|
|
234
112
|
exports.validateRtpParameters = validateRtpParameters;
|
|
235
113
|
/**
|
|
236
|
-
* Validates
|
|
114
|
+
* Validates SctpStreamParameters. It may modify given data by adding missing
|
|
237
115
|
* fields with default values.
|
|
238
116
|
* It throws if invalid.
|
|
239
117
|
*/
|
|
240
|
-
function
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
throw new TypeError('codec is not an object');
|
|
118
|
+
function validateSctpStreamParameters(params) {
|
|
119
|
+
if (typeof params !== 'object') {
|
|
120
|
+
throw new TypeError('params is not an object');
|
|
244
121
|
}
|
|
245
|
-
//
|
|
246
|
-
if (
|
|
247
|
-
throw new TypeError('missing
|
|
122
|
+
// streamId is mandatory.
|
|
123
|
+
if (typeof params.streamId !== 'number') {
|
|
124
|
+
throw new TypeError('missing params.streamId');
|
|
248
125
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
126
|
+
// ordered is optional.
|
|
127
|
+
let orderedGiven = false;
|
|
128
|
+
if (typeof params.ordered === 'boolean') {
|
|
129
|
+
orderedGiven = true;
|
|
252
130
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
throw new TypeError('missing codec.payloadType');
|
|
131
|
+
else {
|
|
132
|
+
params.ordered = true;
|
|
256
133
|
}
|
|
257
|
-
//
|
|
258
|
-
if (
|
|
259
|
-
|
|
134
|
+
// maxPacketLifeTime is optional.
|
|
135
|
+
if (params.maxPacketLifeTime &&
|
|
136
|
+
typeof params.maxPacketLifeTime !== 'number') {
|
|
137
|
+
throw new TypeError('invalid params.maxPacketLifeTime');
|
|
260
138
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (typeof codec.channels !== 'number') {
|
|
265
|
-
codec.channels = 1;
|
|
266
|
-
}
|
|
139
|
+
// maxRetransmits is optional.
|
|
140
|
+
if (params.maxRetransmits && typeof params.maxRetransmits !== 'number') {
|
|
141
|
+
throw new TypeError('invalid params.maxRetransmits');
|
|
267
142
|
}
|
|
268
|
-
|
|
269
|
-
|
|
143
|
+
if (params.maxPacketLifeTime && params.maxRetransmits) {
|
|
144
|
+
throw new TypeError('cannot provide both maxPacketLifeTime and maxRetransmits');
|
|
270
145
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
146
|
+
if (orderedGiven &&
|
|
147
|
+
params.ordered &&
|
|
148
|
+
(params.maxPacketLifeTime || params.maxRetransmits)) {
|
|
149
|
+
throw new TypeError('cannot be ordered with maxPacketLifeTime or maxRetransmits');
|
|
274
150
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
codec.parameters[key] = '';
|
|
279
|
-
value = '';
|
|
280
|
-
}
|
|
281
|
-
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
282
|
-
throw new TypeError(`invalid codec parameter [key:${key}s, value:${value}]`);
|
|
283
|
-
}
|
|
284
|
-
// Specific parameters validation.
|
|
285
|
-
if (key === 'apt') {
|
|
286
|
-
if (typeof value !== 'number') {
|
|
287
|
-
throw new TypeError('invalid codec apt parameter');
|
|
288
|
-
}
|
|
289
|
-
}
|
|
151
|
+
else if (!orderedGiven &&
|
|
152
|
+
(params.maxPacketLifeTime || params.maxRetransmits)) {
|
|
153
|
+
params.ordered = false;
|
|
290
154
|
}
|
|
291
|
-
//
|
|
292
|
-
if (
|
|
293
|
-
|
|
155
|
+
// label is optional.
|
|
156
|
+
if (params.label && typeof params.label !== 'string') {
|
|
157
|
+
throw new TypeError('invalid params.label');
|
|
294
158
|
}
|
|
295
|
-
|
|
296
|
-
|
|
159
|
+
// protocol is optional.
|
|
160
|
+
if (params.protocol && typeof params.protocol !== 'string') {
|
|
161
|
+
throw new TypeError('invalid params.protocol');
|
|
297
162
|
}
|
|
298
163
|
}
|
|
299
|
-
exports.
|
|
164
|
+
exports.validateSctpStreamParameters = validateSctpStreamParameters;
|
|
300
165
|
/**
|
|
301
|
-
* Validates
|
|
166
|
+
* Validates SctpCapabilities. It may modify given data by adding missing
|
|
302
167
|
* fields with default values.
|
|
303
168
|
* It throws if invalid.
|
|
304
169
|
*/
|
|
305
|
-
function
|
|
306
|
-
if (typeof
|
|
307
|
-
throw new TypeError('
|
|
308
|
-
}
|
|
309
|
-
// uri is mandatory.
|
|
310
|
-
if (!ext.uri || typeof ext.uri !== 'string') {
|
|
311
|
-
throw new TypeError('missing ext.uri');
|
|
312
|
-
}
|
|
313
|
-
// id is mandatory.
|
|
314
|
-
if (typeof ext.id !== 'number') {
|
|
315
|
-
throw new TypeError('missing ext.id');
|
|
316
|
-
}
|
|
317
|
-
// encrypt is optional. If unset set it to false.
|
|
318
|
-
if (ext.encrypt && typeof ext.encrypt !== 'boolean') {
|
|
319
|
-
throw new TypeError('invalid ext.encrypt');
|
|
320
|
-
}
|
|
321
|
-
else if (!ext.encrypt) {
|
|
322
|
-
ext.encrypt = false;
|
|
323
|
-
}
|
|
324
|
-
// parameters is optional. If unset, set it to an empty object.
|
|
325
|
-
if (!ext.parameters || typeof ext.parameters !== 'object') {
|
|
326
|
-
ext.parameters = {};
|
|
170
|
+
function validateSctpCapabilities(caps) {
|
|
171
|
+
if (typeof caps !== 'object') {
|
|
172
|
+
throw new TypeError('caps is not an object');
|
|
327
173
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
ext.parameters[key] = '';
|
|
332
|
-
value = '';
|
|
333
|
-
}
|
|
334
|
-
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
335
|
-
throw new TypeError('invalid header extension parameter');
|
|
336
|
-
}
|
|
174
|
+
// numStreams is mandatory.
|
|
175
|
+
if (!caps.numStreams || typeof caps.numStreams !== 'object') {
|
|
176
|
+
throw new TypeError('missing caps.numStreams');
|
|
337
177
|
}
|
|
178
|
+
validateNumSctpStreams(caps.numStreams);
|
|
338
179
|
}
|
|
339
|
-
exports.
|
|
180
|
+
exports.validateSctpCapabilities = validateSctpCapabilities;
|
|
340
181
|
/**
|
|
341
|
-
*
|
|
342
|
-
* fields with default values.
|
|
343
|
-
* It throws if invalid.
|
|
344
|
-
*/
|
|
345
|
-
function validateRtpEncodingParameters(encoding) {
|
|
346
|
-
if (typeof encoding !== 'object') {
|
|
347
|
-
throw new TypeError('encoding is not an object');
|
|
348
|
-
}
|
|
349
|
-
// ssrc is optional.
|
|
350
|
-
if (encoding.ssrc && typeof encoding.ssrc !== 'number') {
|
|
351
|
-
throw new TypeError('invalid encoding.ssrc');
|
|
352
|
-
}
|
|
353
|
-
// rid is optional.
|
|
354
|
-
if (encoding.rid && typeof encoding.rid !== 'string') {
|
|
355
|
-
throw new TypeError('invalid encoding.rid');
|
|
356
|
-
}
|
|
357
|
-
// rtx is optional.
|
|
358
|
-
if (encoding.rtx && typeof encoding.rtx !== 'object') {
|
|
359
|
-
throw new TypeError('invalid encoding.rtx');
|
|
360
|
-
}
|
|
361
|
-
else if (encoding.rtx) {
|
|
362
|
-
// RTX ssrc is mandatory if rtx is present.
|
|
363
|
-
if (typeof encoding.rtx.ssrc !== 'number') {
|
|
364
|
-
throw new TypeError('missing encoding.rtx.ssrc');
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
// dtx is optional. If unset set it to false.
|
|
368
|
-
if (!encoding.dtx || typeof encoding.dtx !== 'boolean') {
|
|
369
|
-
encoding.dtx = false;
|
|
370
|
-
}
|
|
371
|
-
// scalabilityMode is optional.
|
|
372
|
-
if (encoding.scalabilityMode && typeof encoding.scalabilityMode !== 'string') {
|
|
373
|
-
throw new TypeError('invalid encoding.scalabilityMode');
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
exports.validateRtpEncodingParameters = validateRtpEncodingParameters;
|
|
377
|
-
/**
|
|
378
|
-
* Validates RtcpParameters. It may modify given data by adding missing
|
|
379
|
-
* fields with default values.
|
|
380
|
-
* It throws if invalid.
|
|
381
|
-
*/
|
|
382
|
-
function validateRtcpParameters(rtcp) {
|
|
383
|
-
if (typeof rtcp !== 'object') {
|
|
384
|
-
throw new TypeError('rtcp is not an object');
|
|
385
|
-
}
|
|
386
|
-
// cname is optional.
|
|
387
|
-
if (rtcp.cname && typeof rtcp.cname !== 'string') {
|
|
388
|
-
throw new TypeError('invalid rtcp.cname');
|
|
389
|
-
}
|
|
390
|
-
// reducedSize is optional. If unset set it to true.
|
|
391
|
-
if (!rtcp.reducedSize || typeof rtcp.reducedSize !== 'boolean') {
|
|
392
|
-
rtcp.reducedSize = true;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
exports.validateRtcpParameters = validateRtcpParameters;
|
|
396
|
-
/**
|
|
397
|
-
* Validates SctpCapabilities. It may modify given data by adding missing
|
|
398
|
-
* fields with default values.
|
|
399
|
-
* It throws if invalid.
|
|
400
|
-
*/
|
|
401
|
-
function validateSctpCapabilities(caps) {
|
|
402
|
-
if (typeof caps !== 'object') {
|
|
403
|
-
throw new TypeError('caps is not an object');
|
|
404
|
-
}
|
|
405
|
-
// numStreams is mandatory.
|
|
406
|
-
if (!caps.numStreams || typeof caps.numStreams !== 'object') {
|
|
407
|
-
throw new TypeError('missing caps.numStreams');
|
|
408
|
-
}
|
|
409
|
-
validateNumSctpStreams(caps.numStreams);
|
|
410
|
-
}
|
|
411
|
-
exports.validateSctpCapabilities = validateSctpCapabilities;
|
|
412
|
-
/**
|
|
413
|
-
* Validates NumSctpStreams. It may modify given data by adding missing
|
|
414
|
-
* fields with default values.
|
|
415
|
-
* It throws if invalid.
|
|
416
|
-
*/
|
|
417
|
-
function validateNumSctpStreams(numStreams) {
|
|
418
|
-
if (typeof numStreams !== 'object') {
|
|
419
|
-
throw new TypeError('numStreams is not an object');
|
|
420
|
-
}
|
|
421
|
-
// OS is mandatory.
|
|
422
|
-
if (typeof numStreams.OS !== 'number') {
|
|
423
|
-
throw new TypeError('missing numStreams.OS');
|
|
424
|
-
}
|
|
425
|
-
// MIS is mandatory.
|
|
426
|
-
if (typeof numStreams.MIS !== 'number') {
|
|
427
|
-
throw new TypeError('missing numStreams.MIS');
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
exports.validateNumSctpStreams = validateNumSctpStreams;
|
|
431
|
-
/**
|
|
432
|
-
* Validates SctpParameters. It may modify given data by adding missing
|
|
433
|
-
* fields with default values.
|
|
434
|
-
* It throws if invalid.
|
|
435
|
-
*/
|
|
436
|
-
function validateSctpParameters(params) {
|
|
437
|
-
if (typeof params !== 'object') {
|
|
438
|
-
throw new TypeError('params is not an object');
|
|
439
|
-
}
|
|
440
|
-
// port is mandatory.
|
|
441
|
-
if (typeof params.port !== 'number') {
|
|
442
|
-
throw new TypeError('missing params.port');
|
|
443
|
-
}
|
|
444
|
-
// OS is mandatory.
|
|
445
|
-
if (typeof params.OS !== 'number') {
|
|
446
|
-
throw new TypeError('missing params.OS');
|
|
447
|
-
}
|
|
448
|
-
// MIS is mandatory.
|
|
449
|
-
if (typeof params.MIS !== 'number') {
|
|
450
|
-
throw new TypeError('missing params.MIS');
|
|
451
|
-
}
|
|
452
|
-
// maxMessageSize is mandatory.
|
|
453
|
-
if (typeof params.maxMessageSize !== 'number') {
|
|
454
|
-
throw new TypeError('missing params.maxMessageSize');
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
exports.validateSctpParameters = validateSctpParameters;
|
|
458
|
-
/**
|
|
459
|
-
* Validates SctpStreamParameters. It may modify given data by adding missing
|
|
460
|
-
* fields with default values.
|
|
461
|
-
* It throws if invalid.
|
|
462
|
-
*/
|
|
463
|
-
function validateSctpStreamParameters(params) {
|
|
464
|
-
if (typeof params !== 'object') {
|
|
465
|
-
throw new TypeError('params is not an object');
|
|
466
|
-
}
|
|
467
|
-
// streamId is mandatory.
|
|
468
|
-
if (typeof params.streamId !== 'number') {
|
|
469
|
-
throw new TypeError('missing params.streamId');
|
|
470
|
-
}
|
|
471
|
-
// ordered is optional.
|
|
472
|
-
let orderedGiven = false;
|
|
473
|
-
if (typeof params.ordered === 'boolean') {
|
|
474
|
-
orderedGiven = true;
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
params.ordered = true;
|
|
478
|
-
}
|
|
479
|
-
// maxPacketLifeTime is optional.
|
|
480
|
-
if (params.maxPacketLifeTime && typeof params.maxPacketLifeTime !== 'number') {
|
|
481
|
-
throw new TypeError('invalid params.maxPacketLifeTime');
|
|
482
|
-
}
|
|
483
|
-
// maxRetransmits is optional.
|
|
484
|
-
if (params.maxRetransmits && typeof params.maxRetransmits !== 'number') {
|
|
485
|
-
throw new TypeError('invalid params.maxRetransmits');
|
|
486
|
-
}
|
|
487
|
-
if (params.maxPacketLifeTime && params.maxRetransmits) {
|
|
488
|
-
throw new TypeError('cannot provide both maxPacketLifeTime and maxRetransmits');
|
|
489
|
-
}
|
|
490
|
-
if (orderedGiven &&
|
|
491
|
-
params.ordered &&
|
|
492
|
-
(params.maxPacketLifeTime || params.maxRetransmits)) {
|
|
493
|
-
throw new TypeError('cannot be ordered with maxPacketLifeTime or maxRetransmits');
|
|
494
|
-
}
|
|
495
|
-
else if (!orderedGiven && (params.maxPacketLifeTime || params.maxRetransmits)) {
|
|
496
|
-
params.ordered = false;
|
|
497
|
-
}
|
|
498
|
-
// label is optional.
|
|
499
|
-
if (params.label && typeof params.label !== 'string') {
|
|
500
|
-
throw new TypeError('invalid params.label');
|
|
501
|
-
}
|
|
502
|
-
// protocol is optional.
|
|
503
|
-
if (params.protocol && typeof params.protocol !== 'string') {
|
|
504
|
-
throw new TypeError('invalid params.protocol');
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
exports.validateSctpStreamParameters = validateSctpStreamParameters;
|
|
508
|
-
/**
|
|
509
|
-
* Generate extended RTP capabilities for sending and receiving.
|
|
182
|
+
* Generate extended RTP capabilities for sending and receiving.
|
|
510
183
|
*/
|
|
511
184
|
function getExtendedRtpCapabilities(localCaps, remoteCaps) {
|
|
512
185
|
const extendedRtpCapabilities = {
|
|
513
186
|
codecs: [],
|
|
514
|
-
headerExtensions: []
|
|
187
|
+
headerExtensions: [],
|
|
515
188
|
};
|
|
516
189
|
// Match media codecs and keep the order preferred by remoteCaps.
|
|
517
190
|
for (const remoteCodec of remoteCaps.codecs || []) {
|
|
518
191
|
if (isRtxCodec(remoteCodec)) {
|
|
519
192
|
continue;
|
|
520
193
|
}
|
|
521
|
-
const matchingLocalCodec = (localCaps.codecs || [])
|
|
522
|
-
.find((localCodec) => (matchCodecs(localCodec, remoteCodec, { strict: true, modify: true })));
|
|
194
|
+
const matchingLocalCodec = (localCaps.codecs || []).find((localCodec) => matchCodecs(localCodec, remoteCodec, { strict: true, modify: true }));
|
|
523
195
|
if (!matchingLocalCodec) {
|
|
524
196
|
continue;
|
|
525
197
|
}
|
|
@@ -534,27 +206,26 @@ function getExtendedRtpCapabilities(localCaps, remoteCaps) {
|
|
|
534
206
|
remoteRtxPayloadType: undefined,
|
|
535
207
|
localParameters: matchingLocalCodec.parameters,
|
|
536
208
|
remoteParameters: remoteCodec.parameters,
|
|
537
|
-
rtcpFeedback: reduceRtcpFeedback(matchingLocalCodec, remoteCodec)
|
|
209
|
+
rtcpFeedback: reduceRtcpFeedback(matchingLocalCodec, remoteCodec),
|
|
538
210
|
};
|
|
539
211
|
extendedRtpCapabilities.codecs.push(extendedCodec);
|
|
540
212
|
}
|
|
541
213
|
// Match RTX codecs.
|
|
542
214
|
for (const extendedCodec of extendedRtpCapabilities.codecs) {
|
|
543
|
-
const matchingLocalRtxCodec = localCaps.codecs
|
|
544
|
-
.
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
.find((remoteCodec) => (isRtxCodec(remoteCodec) &&
|
|
548
|
-
remoteCodec.parameters.apt === extendedCodec.remotePayloadType));
|
|
215
|
+
const matchingLocalRtxCodec = localCaps.codecs.find((localCodec) => isRtxCodec(localCodec) &&
|
|
216
|
+
localCodec.parameters.apt === extendedCodec.localPayloadType);
|
|
217
|
+
const matchingRemoteRtxCodec = remoteCaps.codecs.find((remoteCodec) => isRtxCodec(remoteCodec) &&
|
|
218
|
+
remoteCodec.parameters.apt === extendedCodec.remotePayloadType);
|
|
549
219
|
if (matchingLocalRtxCodec && matchingRemoteRtxCodec) {
|
|
550
|
-
extendedCodec.localRtxPayloadType =
|
|
551
|
-
|
|
220
|
+
extendedCodec.localRtxPayloadType =
|
|
221
|
+
matchingLocalRtxCodec.preferredPayloadType;
|
|
222
|
+
extendedCodec.remoteRtxPayloadType =
|
|
223
|
+
matchingRemoteRtxCodec.preferredPayloadType;
|
|
552
224
|
}
|
|
553
225
|
}
|
|
554
226
|
// Match header extensions.
|
|
555
227
|
for (const remoteExt of remoteCaps.headerExtensions) {
|
|
556
|
-
const matchingLocalExt = localCaps.headerExtensions
|
|
557
|
-
.find((localExt) => (matchHeaderExtensions(localExt, remoteExt)));
|
|
228
|
+
const matchingLocalExt = localCaps.headerExtensions.find((localExt) => matchHeaderExtensions(localExt, remoteExt));
|
|
558
229
|
if (!matchingLocalExt) {
|
|
559
230
|
continue;
|
|
560
231
|
}
|
|
@@ -564,29 +235,25 @@ function getExtendedRtpCapabilities(localCaps, remoteCaps) {
|
|
|
564
235
|
sendId: matchingLocalExt.preferredId,
|
|
565
236
|
recvId: remoteExt.preferredId,
|
|
566
237
|
encrypt: matchingLocalExt.preferredEncrypt,
|
|
567
|
-
direction: 'sendrecv'
|
|
238
|
+
direction: 'sendrecv',
|
|
568
239
|
};
|
|
569
240
|
switch (remoteExt.direction) {
|
|
570
|
-
case 'sendrecv':
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
{
|
|
587
|
-
extendedExt.direction = 'inactive';
|
|
588
|
-
break;
|
|
589
|
-
}
|
|
241
|
+
case 'sendrecv': {
|
|
242
|
+
extendedExt.direction = 'sendrecv';
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
case 'recvonly': {
|
|
246
|
+
extendedExt.direction = 'sendonly';
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case 'sendonly': {
|
|
250
|
+
extendedExt.direction = 'recvonly';
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
case 'inactive': {
|
|
254
|
+
extendedExt.direction = 'inactive';
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
590
257
|
}
|
|
591
258
|
extendedRtpCapabilities.headerExtensions.push(extendedExt);
|
|
592
259
|
}
|
|
@@ -600,7 +267,7 @@ exports.getExtendedRtpCapabilities = getExtendedRtpCapabilities;
|
|
|
600
267
|
function getRecvRtpCapabilities(extendedRtpCapabilities) {
|
|
601
268
|
const rtpCapabilities = {
|
|
602
269
|
codecs: [],
|
|
603
|
-
headerExtensions: []
|
|
270
|
+
headerExtensions: [],
|
|
604
271
|
};
|
|
605
272
|
for (const extendedCodec of extendedRtpCapabilities.codecs) {
|
|
606
273
|
const codec = {
|
|
@@ -610,7 +277,7 @@ function getRecvRtpCapabilities(extendedRtpCapabilities) {
|
|
|
610
277
|
clockRate: extendedCodec.clockRate,
|
|
611
278
|
channels: extendedCodec.channels,
|
|
612
279
|
parameters: extendedCodec.localParameters,
|
|
613
|
-
rtcpFeedback: extendedCodec.rtcpFeedback
|
|
280
|
+
rtcpFeedback: extendedCodec.rtcpFeedback,
|
|
614
281
|
};
|
|
615
282
|
rtpCapabilities.codecs.push(codec);
|
|
616
283
|
// Add RTX codec.
|
|
@@ -623,9 +290,9 @@ function getRecvRtpCapabilities(extendedRtpCapabilities) {
|
|
|
623
290
|
preferredPayloadType: extendedCodec.remoteRtxPayloadType,
|
|
624
291
|
clockRate: extendedCodec.clockRate,
|
|
625
292
|
parameters: {
|
|
626
|
-
apt: extendedCodec.remotePayloadType
|
|
293
|
+
apt: extendedCodec.remotePayloadType,
|
|
627
294
|
},
|
|
628
|
-
rtcpFeedback: []
|
|
295
|
+
rtcpFeedback: [],
|
|
629
296
|
};
|
|
630
297
|
rtpCapabilities.codecs.push(rtxCodec);
|
|
631
298
|
// TODO: In the future, we need to add FEC, CN, etc, codecs.
|
|
@@ -641,7 +308,7 @@ function getRecvRtpCapabilities(extendedRtpCapabilities) {
|
|
|
641
308
|
uri: extendedExtension.uri,
|
|
642
309
|
preferredId: extendedExtension.recvId,
|
|
643
310
|
preferredEncrypt: extendedExtension.encrypt,
|
|
644
|
-
direction: extendedExtension.direction
|
|
311
|
+
direction: extendedExtension.direction,
|
|
645
312
|
};
|
|
646
313
|
rtpCapabilities.headerExtensions.push(ext);
|
|
647
314
|
}
|
|
@@ -658,7 +325,7 @@ function getSendingRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
658
325
|
codecs: [],
|
|
659
326
|
headerExtensions: [],
|
|
660
327
|
encodings: [],
|
|
661
|
-
rtcp: {}
|
|
328
|
+
rtcp: {},
|
|
662
329
|
};
|
|
663
330
|
for (const extendedCodec of extendedRtpCapabilities.codecs) {
|
|
664
331
|
if (extendedCodec.kind !== kind) {
|
|
@@ -670,7 +337,7 @@ function getSendingRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
670
337
|
clockRate: extendedCodec.clockRate,
|
|
671
338
|
channels: extendedCodec.channels,
|
|
672
339
|
parameters: extendedCodec.localParameters,
|
|
673
|
-
rtcpFeedback: extendedCodec.rtcpFeedback
|
|
340
|
+
rtcpFeedback: extendedCodec.rtcpFeedback,
|
|
674
341
|
};
|
|
675
342
|
rtpParameters.codecs.push(codec);
|
|
676
343
|
// Add RTX codec.
|
|
@@ -680,9 +347,9 @@ function getSendingRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
680
347
|
payloadType: extendedCodec.localRtxPayloadType,
|
|
681
348
|
clockRate: extendedCodec.clockRate,
|
|
682
349
|
parameters: {
|
|
683
|
-
apt: extendedCodec.localPayloadType
|
|
350
|
+
apt: extendedCodec.localPayloadType,
|
|
684
351
|
},
|
|
685
|
-
rtcpFeedback: []
|
|
352
|
+
rtcpFeedback: [],
|
|
686
353
|
};
|
|
687
354
|
rtpParameters.codecs.push(rtxCodec);
|
|
688
355
|
}
|
|
@@ -698,7 +365,7 @@ function getSendingRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
698
365
|
uri: extendedExtension.uri,
|
|
699
366
|
id: extendedExtension.sendId,
|
|
700
367
|
encrypt: extendedExtension.encrypt,
|
|
701
|
-
parameters: {}
|
|
368
|
+
parameters: {},
|
|
702
369
|
};
|
|
703
370
|
rtpParameters.headerExtensions.push(ext);
|
|
704
371
|
}
|
|
@@ -714,7 +381,7 @@ function getSendingRemoteRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
714
381
|
codecs: [],
|
|
715
382
|
headerExtensions: [],
|
|
716
383
|
encodings: [],
|
|
717
|
-
rtcp: {}
|
|
384
|
+
rtcp: {},
|
|
718
385
|
};
|
|
719
386
|
for (const extendedCodec of extendedRtpCapabilities.codecs) {
|
|
720
387
|
if (extendedCodec.kind !== kind) {
|
|
@@ -726,7 +393,7 @@ function getSendingRemoteRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
726
393
|
clockRate: extendedCodec.clockRate,
|
|
727
394
|
channels: extendedCodec.channels,
|
|
728
395
|
parameters: extendedCodec.remoteParameters,
|
|
729
|
-
rtcpFeedback: extendedCodec.rtcpFeedback
|
|
396
|
+
rtcpFeedback: extendedCodec.rtcpFeedback,
|
|
730
397
|
};
|
|
731
398
|
rtpParameters.codecs.push(codec);
|
|
732
399
|
// Add RTX codec.
|
|
@@ -736,9 +403,9 @@ function getSendingRemoteRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
736
403
|
payloadType: extendedCodec.localRtxPayloadType,
|
|
737
404
|
clockRate: extendedCodec.clockRate,
|
|
738
405
|
parameters: {
|
|
739
|
-
apt: extendedCodec.localPayloadType
|
|
406
|
+
apt: extendedCodec.localPayloadType,
|
|
740
407
|
},
|
|
741
|
-
rtcpFeedback: []
|
|
408
|
+
rtcpFeedback: [],
|
|
742
409
|
};
|
|
743
410
|
rtpParameters.codecs.push(rtxCodec);
|
|
744
411
|
}
|
|
@@ -754,28 +421,25 @@ function getSendingRemoteRtpParameters(kind, extendedRtpCapabilities) {
|
|
|
754
421
|
uri: extendedExtension.uri,
|
|
755
422
|
id: extendedExtension.sendId,
|
|
756
423
|
encrypt: extendedExtension.encrypt,
|
|
757
|
-
parameters: {}
|
|
424
|
+
parameters: {},
|
|
758
425
|
};
|
|
759
426
|
rtpParameters.headerExtensions.push(ext);
|
|
760
427
|
}
|
|
761
428
|
// Reduce codecs' RTCP feedback. Use Transport-CC if available, REMB otherwise.
|
|
762
|
-
if (rtpParameters.headerExtensions.some(
|
|
429
|
+
if (rtpParameters.headerExtensions.some(ext => ext.uri ===
|
|
430
|
+
'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01')) {
|
|
763
431
|
for (const codec of rtpParameters.codecs) {
|
|
764
|
-
codec.rtcpFeedback = (codec.rtcpFeedback || [])
|
|
765
|
-
.filter((fb) => fb.type !== 'goog-remb');
|
|
432
|
+
codec.rtcpFeedback = (codec.rtcpFeedback || []).filter((fb) => fb.type !== 'goog-remb');
|
|
766
433
|
}
|
|
767
434
|
}
|
|
768
|
-
else if (rtpParameters.headerExtensions.some(
|
|
435
|
+
else if (rtpParameters.headerExtensions.some(ext => ext.uri === 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time')) {
|
|
769
436
|
for (const codec of rtpParameters.codecs) {
|
|
770
|
-
codec.rtcpFeedback = (codec.rtcpFeedback || [])
|
|
771
|
-
.filter((fb) => fb.type !== 'transport-cc');
|
|
437
|
+
codec.rtcpFeedback = (codec.rtcpFeedback || []).filter(fb => fb.type !== 'transport-cc');
|
|
772
438
|
}
|
|
773
439
|
}
|
|
774
440
|
else {
|
|
775
441
|
for (const codec of rtpParameters.codecs) {
|
|
776
|
-
codec.rtcpFeedback = (codec.rtcpFeedback || [])
|
|
777
|
-
.filter((fb) => (fb.type !== 'transport-cc' &&
|
|
778
|
-
fb.type !== 'goog-remb'));
|
|
442
|
+
codec.rtcpFeedback = (codec.rtcpFeedback || []).filter((fb) => fb.type !== 'transport-cc' && fb.type !== 'goog-remb');
|
|
779
443
|
}
|
|
780
444
|
}
|
|
781
445
|
return rtpParameters;
|
|
@@ -831,7 +495,7 @@ function generateProbatorRtpParameters(videoRtpParameters) {
|
|
|
831
495
|
codecs: [],
|
|
832
496
|
headerExtensions: [],
|
|
833
497
|
encodings: [{ ssrc: RTP_PROBATOR_SSRC }],
|
|
834
|
-
rtcp: { cname: 'probator' }
|
|
498
|
+
rtcp: { cname: 'probator' },
|
|
835
499
|
};
|
|
836
500
|
rtpParameters.codecs.push(videoRtpParameters.codecs[0]);
|
|
837
501
|
rtpParameters.codecs[0].payloadType = RTP_PROBATOR_CODEC_PAYLOAD_TYPE;
|
|
@@ -843,8 +507,7 @@ exports.generateProbatorRtpParameters = generateProbatorRtpParameters;
|
|
|
843
507
|
* Whether media can be sent based on the given RTP capabilities.
|
|
844
508
|
*/
|
|
845
509
|
function canSend(kind, extendedRtpCapabilities) {
|
|
846
|
-
return extendedRtpCapabilities.codecs.
|
|
847
|
-
some((codec) => codec.kind === kind);
|
|
510
|
+
return extendedRtpCapabilities.codecs.some((codec) => codec.kind === kind);
|
|
848
511
|
}
|
|
849
512
|
exports.canSend = canSend;
|
|
850
513
|
/**
|
|
@@ -858,10 +521,305 @@ function canReceive(rtpParameters, extendedRtpCapabilities) {
|
|
|
858
521
|
return false;
|
|
859
522
|
}
|
|
860
523
|
const firstMediaCodec = rtpParameters.codecs[0];
|
|
861
|
-
return extendedRtpCapabilities.codecs
|
|
862
|
-
.some((codec) => codec.remotePayloadType === firstMediaCodec.payloadType);
|
|
524
|
+
return extendedRtpCapabilities.codecs.some((codec) => codec.remotePayloadType === firstMediaCodec.payloadType);
|
|
863
525
|
}
|
|
864
526
|
exports.canReceive = canReceive;
|
|
527
|
+
/**
|
|
528
|
+
* Validates RtpCodecCapability. It may modify given data by adding missing
|
|
529
|
+
* fields with default values.
|
|
530
|
+
* It throws if invalid.
|
|
531
|
+
*/
|
|
532
|
+
function validateRtpCodecCapability(codec) {
|
|
533
|
+
const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i');
|
|
534
|
+
if (typeof codec !== 'object') {
|
|
535
|
+
throw new TypeError('codec is not an object');
|
|
536
|
+
}
|
|
537
|
+
// mimeType is mandatory.
|
|
538
|
+
if (!codec.mimeType || typeof codec.mimeType !== 'string') {
|
|
539
|
+
throw new TypeError('missing codec.mimeType');
|
|
540
|
+
}
|
|
541
|
+
const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType);
|
|
542
|
+
if (!mimeTypeMatch) {
|
|
543
|
+
throw new TypeError('invalid codec.mimeType');
|
|
544
|
+
}
|
|
545
|
+
// Just override kind with media component of mimeType.
|
|
546
|
+
codec.kind = mimeTypeMatch[1].toLowerCase();
|
|
547
|
+
// preferredPayloadType is optional.
|
|
548
|
+
if (codec.preferredPayloadType &&
|
|
549
|
+
typeof codec.preferredPayloadType !== 'number') {
|
|
550
|
+
throw new TypeError('invalid codec.preferredPayloadType');
|
|
551
|
+
}
|
|
552
|
+
// clockRate is mandatory.
|
|
553
|
+
if (typeof codec.clockRate !== 'number') {
|
|
554
|
+
throw new TypeError('missing codec.clockRate');
|
|
555
|
+
}
|
|
556
|
+
// channels is optional. If unset, set it to 1 (just if audio).
|
|
557
|
+
if (codec.kind === 'audio') {
|
|
558
|
+
if (typeof codec.channels !== 'number') {
|
|
559
|
+
codec.channels = 1;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
delete codec.channels;
|
|
564
|
+
}
|
|
565
|
+
// parameters is optional. If unset, set it to an empty object.
|
|
566
|
+
if (!codec.parameters || typeof codec.parameters !== 'object') {
|
|
567
|
+
codec.parameters = {};
|
|
568
|
+
}
|
|
569
|
+
for (const key of Object.keys(codec.parameters)) {
|
|
570
|
+
let value = codec.parameters[key];
|
|
571
|
+
if (value === undefined) {
|
|
572
|
+
codec.parameters[key] = '';
|
|
573
|
+
value = '';
|
|
574
|
+
}
|
|
575
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
576
|
+
throw new TypeError(`invalid codec parameter [key:${key}s, value:${value}]`);
|
|
577
|
+
}
|
|
578
|
+
// Specific parameters validation.
|
|
579
|
+
if (key === 'apt') {
|
|
580
|
+
if (typeof value !== 'number') {
|
|
581
|
+
throw new TypeError('invalid codec apt parameter');
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// rtcpFeedback is optional. If unset, set it to an empty array.
|
|
586
|
+
if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) {
|
|
587
|
+
codec.rtcpFeedback = [];
|
|
588
|
+
}
|
|
589
|
+
for (const fb of codec.rtcpFeedback) {
|
|
590
|
+
validateRtcpFeedback(fb);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Validates RtcpFeedback. It may modify given data by adding missing
|
|
595
|
+
* fields with default values.
|
|
596
|
+
* It throws if invalid.
|
|
597
|
+
*/
|
|
598
|
+
function validateRtcpFeedback(fb) {
|
|
599
|
+
if (typeof fb !== 'object') {
|
|
600
|
+
throw new TypeError('fb is not an object');
|
|
601
|
+
}
|
|
602
|
+
// type is mandatory.
|
|
603
|
+
if (!fb.type || typeof fb.type !== 'string') {
|
|
604
|
+
throw new TypeError('missing fb.type');
|
|
605
|
+
}
|
|
606
|
+
// parameter is optional. If unset set it to an empty string.
|
|
607
|
+
if (!fb.parameter || typeof fb.parameter !== 'string') {
|
|
608
|
+
fb.parameter = '';
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Validates RtpHeaderExtension. It may modify given data by adding missing
|
|
613
|
+
* fields with default values.
|
|
614
|
+
* It throws if invalid.
|
|
615
|
+
*/
|
|
616
|
+
function validateRtpHeaderExtension(ext) {
|
|
617
|
+
if (typeof ext !== 'object') {
|
|
618
|
+
throw new TypeError('ext is not an object');
|
|
619
|
+
}
|
|
620
|
+
// kind is mandatory.
|
|
621
|
+
if (ext.kind !== 'audio' && ext.kind !== 'video') {
|
|
622
|
+
throw new TypeError('invalid ext.kind');
|
|
623
|
+
}
|
|
624
|
+
// uri is mandatory.
|
|
625
|
+
if (!ext.uri || typeof ext.uri !== 'string') {
|
|
626
|
+
throw new TypeError('missing ext.uri');
|
|
627
|
+
}
|
|
628
|
+
// preferredId is mandatory.
|
|
629
|
+
if (typeof ext.preferredId !== 'number') {
|
|
630
|
+
throw new TypeError('missing ext.preferredId');
|
|
631
|
+
}
|
|
632
|
+
// preferredEncrypt is optional. If unset set it to false.
|
|
633
|
+
if (ext.preferredEncrypt && typeof ext.preferredEncrypt !== 'boolean') {
|
|
634
|
+
throw new TypeError('invalid ext.preferredEncrypt');
|
|
635
|
+
}
|
|
636
|
+
else if (!ext.preferredEncrypt) {
|
|
637
|
+
ext.preferredEncrypt = false;
|
|
638
|
+
}
|
|
639
|
+
// direction is optional. If unset set it to sendrecv.
|
|
640
|
+
if (ext.direction && typeof ext.direction !== 'string') {
|
|
641
|
+
throw new TypeError('invalid ext.direction');
|
|
642
|
+
}
|
|
643
|
+
else if (!ext.direction) {
|
|
644
|
+
ext.direction = 'sendrecv';
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Validates RtpCodecParameters. It may modify given data by adding missing
|
|
649
|
+
* fields with default values.
|
|
650
|
+
* It throws if invalid.
|
|
651
|
+
*/
|
|
652
|
+
function validateRtpCodecParameters(codec) {
|
|
653
|
+
const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i');
|
|
654
|
+
if (typeof codec !== 'object') {
|
|
655
|
+
throw new TypeError('codec is not an object');
|
|
656
|
+
}
|
|
657
|
+
// mimeType is mandatory.
|
|
658
|
+
if (!codec.mimeType || typeof codec.mimeType !== 'string') {
|
|
659
|
+
throw new TypeError('missing codec.mimeType');
|
|
660
|
+
}
|
|
661
|
+
const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType);
|
|
662
|
+
if (!mimeTypeMatch) {
|
|
663
|
+
throw new TypeError('invalid codec.mimeType');
|
|
664
|
+
}
|
|
665
|
+
// payloadType is mandatory.
|
|
666
|
+
if (typeof codec.payloadType !== 'number') {
|
|
667
|
+
throw new TypeError('missing codec.payloadType');
|
|
668
|
+
}
|
|
669
|
+
// clockRate is mandatory.
|
|
670
|
+
if (typeof codec.clockRate !== 'number') {
|
|
671
|
+
throw new TypeError('missing codec.clockRate');
|
|
672
|
+
}
|
|
673
|
+
const kind = mimeTypeMatch[1].toLowerCase();
|
|
674
|
+
// channels is optional. If unset, set it to 1 (just if audio).
|
|
675
|
+
if (kind === 'audio') {
|
|
676
|
+
if (typeof codec.channels !== 'number') {
|
|
677
|
+
codec.channels = 1;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
delete codec.channels;
|
|
682
|
+
}
|
|
683
|
+
// parameters is optional. If unset, set it to an empty object.
|
|
684
|
+
if (!codec.parameters || typeof codec.parameters !== 'object') {
|
|
685
|
+
codec.parameters = {};
|
|
686
|
+
}
|
|
687
|
+
for (const key of Object.keys(codec.parameters)) {
|
|
688
|
+
let value = codec.parameters[key];
|
|
689
|
+
if (value === undefined) {
|
|
690
|
+
codec.parameters[key] = '';
|
|
691
|
+
value = '';
|
|
692
|
+
}
|
|
693
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
694
|
+
throw new TypeError(`invalid codec parameter [key:${key}s, value:${value}]`);
|
|
695
|
+
}
|
|
696
|
+
// Specific parameters validation.
|
|
697
|
+
if (key === 'apt') {
|
|
698
|
+
if (typeof value !== 'number') {
|
|
699
|
+
throw new TypeError('invalid codec apt parameter');
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
// rtcpFeedback is optional. If unset, set it to an empty array.
|
|
704
|
+
if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) {
|
|
705
|
+
codec.rtcpFeedback = [];
|
|
706
|
+
}
|
|
707
|
+
for (const fb of codec.rtcpFeedback) {
|
|
708
|
+
validateRtcpFeedback(fb);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Validates RtpHeaderExtensionParameteters. It may modify given data by adding missing
|
|
713
|
+
* fields with default values.
|
|
714
|
+
* It throws if invalid.
|
|
715
|
+
*/
|
|
716
|
+
function validateRtpHeaderExtensionParameters(ext) {
|
|
717
|
+
if (typeof ext !== 'object') {
|
|
718
|
+
throw new TypeError('ext is not an object');
|
|
719
|
+
}
|
|
720
|
+
// uri is mandatory.
|
|
721
|
+
if (!ext.uri || typeof ext.uri !== 'string') {
|
|
722
|
+
throw new TypeError('missing ext.uri');
|
|
723
|
+
}
|
|
724
|
+
// id is mandatory.
|
|
725
|
+
if (typeof ext.id !== 'number') {
|
|
726
|
+
throw new TypeError('missing ext.id');
|
|
727
|
+
}
|
|
728
|
+
// encrypt is optional. If unset set it to false.
|
|
729
|
+
if (ext.encrypt && typeof ext.encrypt !== 'boolean') {
|
|
730
|
+
throw new TypeError('invalid ext.encrypt');
|
|
731
|
+
}
|
|
732
|
+
else if (!ext.encrypt) {
|
|
733
|
+
ext.encrypt = false;
|
|
734
|
+
}
|
|
735
|
+
// parameters is optional. If unset, set it to an empty object.
|
|
736
|
+
if (!ext.parameters || typeof ext.parameters !== 'object') {
|
|
737
|
+
ext.parameters = {};
|
|
738
|
+
}
|
|
739
|
+
for (const key of Object.keys(ext.parameters)) {
|
|
740
|
+
let value = ext.parameters[key];
|
|
741
|
+
if (value === undefined) {
|
|
742
|
+
ext.parameters[key] = '';
|
|
743
|
+
value = '';
|
|
744
|
+
}
|
|
745
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
746
|
+
throw new TypeError('invalid header extension parameter');
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Validates RtpEncodingParameters. It may modify given data by adding missing
|
|
752
|
+
* fields with default values.
|
|
753
|
+
* It throws if invalid.
|
|
754
|
+
*/
|
|
755
|
+
function validateRtpEncodingParameters(encoding) {
|
|
756
|
+
if (typeof encoding !== 'object') {
|
|
757
|
+
throw new TypeError('encoding is not an object');
|
|
758
|
+
}
|
|
759
|
+
// ssrc is optional.
|
|
760
|
+
if (encoding.ssrc && typeof encoding.ssrc !== 'number') {
|
|
761
|
+
throw new TypeError('invalid encoding.ssrc');
|
|
762
|
+
}
|
|
763
|
+
// rid is optional.
|
|
764
|
+
if (encoding.rid && typeof encoding.rid !== 'string') {
|
|
765
|
+
throw new TypeError('invalid encoding.rid');
|
|
766
|
+
}
|
|
767
|
+
// rtx is optional.
|
|
768
|
+
if (encoding.rtx && typeof encoding.rtx !== 'object') {
|
|
769
|
+
throw new TypeError('invalid encoding.rtx');
|
|
770
|
+
}
|
|
771
|
+
else if (encoding.rtx) {
|
|
772
|
+
// RTX ssrc is mandatory if rtx is present.
|
|
773
|
+
if (typeof encoding.rtx.ssrc !== 'number') {
|
|
774
|
+
throw new TypeError('missing encoding.rtx.ssrc');
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
// dtx is optional. If unset set it to false.
|
|
778
|
+
if (!encoding.dtx || typeof encoding.dtx !== 'boolean') {
|
|
779
|
+
encoding.dtx = false;
|
|
780
|
+
}
|
|
781
|
+
// scalabilityMode is optional.
|
|
782
|
+
if (encoding.scalabilityMode &&
|
|
783
|
+
typeof encoding.scalabilityMode !== 'string') {
|
|
784
|
+
throw new TypeError('invalid encoding.scalabilityMode');
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Validates RtcpParameters. It may modify given data by adding missing
|
|
789
|
+
* fields with default values.
|
|
790
|
+
* It throws if invalid.
|
|
791
|
+
*/
|
|
792
|
+
function validateRtcpParameters(rtcp) {
|
|
793
|
+
if (typeof rtcp !== 'object') {
|
|
794
|
+
throw new TypeError('rtcp is not an object');
|
|
795
|
+
}
|
|
796
|
+
// cname is optional.
|
|
797
|
+
if (rtcp.cname && typeof rtcp.cname !== 'string') {
|
|
798
|
+
throw new TypeError('invalid rtcp.cname');
|
|
799
|
+
}
|
|
800
|
+
// reducedSize is optional. If unset set it to true.
|
|
801
|
+
if (!rtcp.reducedSize || typeof rtcp.reducedSize !== 'boolean') {
|
|
802
|
+
rtcp.reducedSize = true;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Validates NumSctpStreams. It may modify given data by adding missing
|
|
807
|
+
* fields with default values.
|
|
808
|
+
* It throws if invalid.
|
|
809
|
+
*/
|
|
810
|
+
function validateNumSctpStreams(numStreams) {
|
|
811
|
+
if (typeof numStreams !== 'object') {
|
|
812
|
+
throw new TypeError('numStreams is not an object');
|
|
813
|
+
}
|
|
814
|
+
// OS is mandatory.
|
|
815
|
+
if (typeof numStreams.OS !== 'number') {
|
|
816
|
+
throw new TypeError('missing numStreams.OS');
|
|
817
|
+
}
|
|
818
|
+
// MIS is mandatory.
|
|
819
|
+
if (typeof numStreams.MIS !== 'number') {
|
|
820
|
+
throw new TypeError('missing numStreams.MIS');
|
|
821
|
+
}
|
|
822
|
+
}
|
|
865
823
|
function isRtxCodec(codec) {
|
|
866
824
|
if (!codec) {
|
|
867
825
|
return false;
|
|
@@ -882,49 +840,46 @@ function matchCodecs(aCodec, bCodec, { strict = false, modify = false } = {}) {
|
|
|
882
840
|
}
|
|
883
841
|
// Per codec special checks.
|
|
884
842
|
switch (aMimeType) {
|
|
885
|
-
case 'video/h264':
|
|
886
|
-
{
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
843
|
+
case 'video/h264': {
|
|
844
|
+
if (strict) {
|
|
845
|
+
const aPacketizationMode = aCodec.parameters['packetization-mode'] || 0;
|
|
846
|
+
const bPacketizationMode = bCodec.parameters['packetization-mode'] || 0;
|
|
847
|
+
if (aPacketizationMode !== bPacketizationMode) {
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
if (!h264.isSameProfile(aCodec.parameters, bCodec.parameters)) {
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
let selectedProfileLevelId;
|
|
854
|
+
try {
|
|
855
|
+
selectedProfileLevelId = h264.generateProfileLevelIdStringForAnswer(aCodec.parameters, bCodec.parameters);
|
|
856
|
+
}
|
|
857
|
+
catch (error) {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
if (modify) {
|
|
861
|
+
if (selectedProfileLevelId) {
|
|
862
|
+
aCodec.parameters['profile-level-id'] = selectedProfileLevelId;
|
|
863
|
+
bCodec.parameters['profile-level-id'] = selectedProfileLevelId;
|
|
903
864
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
bCodec.parameters['profile-level-id'] = selectedProfileLevelId;
|
|
908
|
-
}
|
|
909
|
-
else {
|
|
910
|
-
delete aCodec.parameters['profile-level-id'];
|
|
911
|
-
delete bCodec.parameters['profile-level-id'];
|
|
912
|
-
}
|
|
865
|
+
else {
|
|
866
|
+
delete aCodec.parameters['profile-level-id'];
|
|
867
|
+
delete bCodec.parameters['profile-level-id'];
|
|
913
868
|
}
|
|
914
869
|
}
|
|
915
|
-
break;
|
|
916
870
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
case 'video/vp9': {
|
|
874
|
+
if (strict) {
|
|
875
|
+
const aProfileId = aCodec.parameters['profile-id'] || 0;
|
|
876
|
+
const bProfileId = bCodec.parameters['profile-id'] || 0;
|
|
877
|
+
if (aProfileId !== bProfileId) {
|
|
878
|
+
return false;
|
|
925
879
|
}
|
|
926
|
-
break;
|
|
927
880
|
}
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
928
883
|
}
|
|
929
884
|
return true;
|
|
930
885
|
}
|
|
@@ -940,9 +895,8 @@ function matchHeaderExtensions(aExt, bExt) {
|
|
|
940
895
|
function reduceRtcpFeedback(codecA, codecB) {
|
|
941
896
|
const reducedRtcpFeedback = [];
|
|
942
897
|
for (const aFb of codecA.rtcpFeedback || []) {
|
|
943
|
-
const matchingBFb = (codecB.rtcpFeedback || [])
|
|
944
|
-
|
|
945
|
-
(bFb.parameter === aFb.parameter || (!bFb.parameter && !aFb.parameter))));
|
|
898
|
+
const matchingBFb = (codecB.rtcpFeedback || []).find((bFb) => bFb.type === aFb.type &&
|
|
899
|
+
(bFb.parameter === aFb.parameter || (!bFb.parameter && !aFb.parameter)));
|
|
946
900
|
if (matchingBFb) {
|
|
947
901
|
reducedRtcpFeedback.push(matchingBFb);
|
|
948
902
|
}
|