|
@@ -4,6 +4,18 @@ var SDPUtil = require("./SDPUtil");
|
4
|
4
|
|
5
|
5
|
// SDP STUFF
|
6
|
6
|
function SDP(sdp) {
|
|
7
|
+ /**
|
|
8
|
+ * Whether or not to remove TCP ice candidates when translating from/to jingle.
|
|
9
|
+ * @type {boolean}
|
|
10
|
+ */
|
|
11
|
+ this.removeTcpCandidates = false;
|
|
12
|
+
|
|
13
|
+ /**
|
|
14
|
+ * Whether or not to remove UDP ice candidates when translating from/to jingle.
|
|
15
|
+ * @type {boolean}
|
|
16
|
+ */
|
|
17
|
+ this.removeUdpCandidates = false;
|
|
18
|
+
|
7
|
19
|
this.media = sdp.split('\r\nm=');
|
8
|
20
|
for (var i = 1; i < this.media.length; i++) {
|
9
|
21
|
this.media[i] = 'm=' + this.media[i];
|
|
@@ -14,6 +26,7 @@ function SDP(sdp) {
|
14
|
26
|
this.session = this.media.shift() + '\r\n';
|
15
|
27
|
this.raw = this.session + this.media.join('');
|
16
|
28
|
}
|
|
29
|
+
|
17
|
30
|
/**
|
18
|
31
|
* Returns map of MediaChannel mapped per channel idx.
|
19
|
32
|
*/
|
|
@@ -63,18 +76,16 @@ SDP.prototype.getMediaSsrcMap = function() {
|
63
|
76
|
*/
|
64
|
77
|
SDP.prototype.containsSSRC = function(ssrc) {
|
65
|
78
|
var medias = this.getMediaSsrcMap();
|
66
|
|
- var contains = false;
|
67
|
79
|
Object.keys(medias).forEach(function(mediaindex){
|
68
|
80
|
var media = medias[mediaindex];
|
69
|
81
|
//console.log("Check", channel, ssrc);
|
70
|
82
|
if(Object.keys(media.ssrcs).indexOf(ssrc) != -1){
|
71
|
|
- contains = true;
|
|
83
|
+ return true;
|
72
|
84
|
}
|
73
|
85
|
});
|
74
|
|
- return contains;
|
|
86
|
+ return false;
|
75
|
87
|
};
|
76
|
88
|
|
77
|
|
-
|
78
|
89
|
// remove iSAC and CN from SDP
|
79
|
90
|
SDP.prototype.mangle = function () {
|
80
|
91
|
var i, j, mline, lines, rtpmap, newdesc;
|
|
@@ -129,8 +140,7 @@ SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
|
129
|
140
|
// add content's to a jingle element
|
130
|
141
|
SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
131
|
142
|
// console.log("SSRC" + ssrcs["audio"] + " - " + ssrcs["video"]);
|
132
|
|
- var i, j, k, mline, ssrc, rtpmap, tmp, line, lines;
|
133
|
|
- var self = this;
|
|
143
|
+ var i, j, k, mline, ssrc, rtpmap, tmp, lines;
|
134
|
144
|
// new bundle plan
|
135
|
145
|
if (SDPUtil.find_line(this.session, 'a=group:')) {
|
136
|
146
|
lines = SDPUtil.find_lines(this.session, 'a=group:');
|
|
@@ -155,12 +165,11 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
155
|
165
|
if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
|
156
|
166
|
ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first
|
157
|
167
|
} else {
|
158
|
|
- if(ssrcs && ssrcs[mline.media])
|
159
|
|
- {
|
|
168
|
+ if(ssrcs && ssrcs[mline.media]) {
|
160
|
169
|
ssrc = ssrcs[mline.media];
|
161
|
|
- }
|
162
|
|
- else
|
|
170
|
+ } else {
|
163
|
171
|
ssrc = false;
|
|
172
|
+ }
|
164
|
173
|
}
|
165
|
174
|
|
166
|
175
|
elem.c('content', {creator: thecreator, name: mline.media});
|
|
@@ -170,8 +179,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
170
|
179
|
elem.attrs({ name: mid });
|
171
|
180
|
}
|
172
|
181
|
|
173
|
|
- if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length)
|
174
|
|
- {
|
|
182
|
+ if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length) {
|
175
|
183
|
elem.c('description',
|
176
|
184
|
{xmlns: 'urn:xmpp:jingle:apps:rtp:1',
|
177
|
185
|
media: mline.media });
|
|
@@ -188,7 +196,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
188
|
196
|
elem.c('parameter', tmp[k]).up();
|
189
|
197
|
}
|
190
|
198
|
}
|
191
|
|
- this.RtcpFbToJingle(i, elem, mline.fmt[j]); // XEP-0293 -- map a=rtcp-fb
|
|
199
|
+ this.rtcpFbToJingle(i, elem, mline.fmt[j]); // XEP-0293 -- map a=rtcp-fb
|
192
|
200
|
|
193
|
201
|
elem.up();
|
194
|
202
|
}
|
|
@@ -208,7 +216,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
208
|
216
|
var ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
|
209
|
217
|
if(ssrclines.length > 0) {
|
210
|
218
|
ssrclines.forEach(function (line) {
|
211
|
|
- idx = line.indexOf(' ');
|
|
219
|
+ var idx = line.indexOf(' ');
|
212
|
220
|
var linessrc = line.substr(0, idx).substr(7);
|
213
|
221
|
if (linessrc != ssrc) {
|
214
|
222
|
elem.up();
|
|
@@ -229,9 +237,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
229
|
237
|
}
|
230
|
238
|
elem.up();
|
231
|
239
|
});
|
232
|
|
- }
|
233
|
|
- else
|
234
|
|
- {
|
|
240
|
+ } else {
|
235
|
241
|
elem.up();
|
236
|
242
|
elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
237
|
243
|
elem.c('parameter');
|
|
@@ -261,7 +267,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
261
|
267
|
// XEP-0339 handle ssrc-group attributes
|
262
|
268
|
var ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
|
263
|
269
|
ssrc_group_lines.forEach(function(line) {
|
264
|
|
- idx = line.indexOf(' ');
|
|
270
|
+ var idx = line.indexOf(' ');
|
265
|
271
|
var semantics = line.substr(0, idx).substr(13);
|
266
|
272
|
var ssrcs = line.substr(14 + semantics.length).split(' ');
|
267
|
273
|
if (ssrcs.length) {
|
|
@@ -280,7 +286,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
280
|
286
|
}
|
281
|
287
|
|
282
|
288
|
// XEP-0293 -- map a=rtcp-fb:*
|
283
|
|
- this.RtcpFbToJingle(i, elem, '*');
|
|
289
|
+ this.rtcpFbToJingle(i, elem, '*');
|
284
|
290
|
|
285
|
291
|
// XEP-0294
|
286
|
292
|
if (SDPUtil.find_line(this.media[i], 'a=extmap:')) {
|
|
@@ -314,7 +320,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
314
|
320
|
}
|
315
|
321
|
|
316
|
322
|
// map ice-ufrag/pwd, dtls fingerprint, candidates
|
317
|
|
- this.TransportToJingle(i, elem);
|
|
323
|
+ this.transportToJingle(i, elem);
|
318
|
324
|
|
319
|
325
|
if (SDPUtil.find_line(this.media[i], 'a=sendrecv', this.session)) {
|
320
|
326
|
elem.attrs({senders: 'both'});
|
|
@@ -335,25 +341,24 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
335
|
341
|
return elem;
|
336
|
342
|
};
|
337
|
343
|
|
338
|
|
-SDP.prototype.TransportToJingle = function (mediaindex, elem) {
|
339
|
|
- var i = mediaindex;
|
340
|
|
- var tmp;
|
|
344
|
+SDP.prototype.transportToJingle = function (mediaindex, elem) {
|
|
345
|
+ var tmp, sctpmap, sctpAttrs, fingerprints;
|
341
|
346
|
var self = this;
|
342
|
347
|
elem.c('transport');
|
343
|
348
|
|
344
|
349
|
// XEP-0343 DTLS/SCTP
|
345
|
350
|
if (SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:').length)
|
346
|
351
|
{
|
347
|
|
- var sctpmap = SDPUtil.find_line(
|
348
|
|
- this.media[i], 'a=sctpmap:', self.session);
|
|
352
|
+ sctpmap = SDPUtil.find_line(
|
|
353
|
+ this.media[mediaindex], 'a=sctpmap:', self.session);
|
349
|
354
|
if (sctpmap)
|
350
|
355
|
{
|
351
|
|
- var sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
|
|
356
|
+ sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
|
352
|
357
|
elem.c('sctpmap',
|
353
|
358
|
{
|
354
|
359
|
xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
|
355
|
360
|
number: sctpAttrs[0], /* SCTP port */
|
356
|
|
- protocol: sctpAttrs[1], /* protocol */
|
|
361
|
+ protocol: sctpAttrs[1] /* protocol */
|
357
|
362
|
});
|
358
|
363
|
// Optional stream count attribute
|
359
|
364
|
if (sctpAttrs.length > 2)
|
|
@@ -362,7 +367,7 @@ SDP.prototype.TransportToJingle = function (mediaindex, elem) {
|
362
|
367
|
}
|
363
|
368
|
}
|
364
|
369
|
// XEP-0320
|
365
|
|
- var fingerprints = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
|
|
370
|
+ fingerprints = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
|
366
|
371
|
fingerprints.forEach(function(line) {
|
367
|
372
|
tmp = SDPUtil.parse_fingerprint(line);
|
368
|
373
|
tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
|
|
@@ -383,14 +388,22 @@ SDP.prototype.TransportToJingle = function (mediaindex, elem) {
|
383
|
388
|
if (SDPUtil.find_line(this.media[mediaindex], 'a=candidate:', this.session)) { // add any a=candidate lines
|
384
|
389
|
var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=candidate:', this.session);
|
385
|
390
|
lines.forEach(function (line) {
|
386
|
|
- elem.c('candidate', SDPUtil.candidateToJingle(line)).up();
|
|
391
|
+ var candidate = SDPUtil.candidateToJingle(line);
|
|
392
|
+ var protocol = (candidate &&
|
|
393
|
+ typeof candidate.protocol === 'string')
|
|
394
|
+ ? candidate.protocol.toLowerCase() : '';
|
|
395
|
+ if ((self.removeTcpCandidates && protocol === 'tcp') ||
|
|
396
|
+ (self.removeUdpCandidates && protocol === 'udp')) {
|
|
397
|
+ return;
|
|
398
|
+ }
|
|
399
|
+ elem.c('candidate', candidate).up();
|
387
|
400
|
});
|
388
|
401
|
}
|
389
|
402
|
}
|
390
|
403
|
elem.up(); // end of transport
|
391
|
404
|
};
|
392
|
405
|
|
393
|
|
-SDP.prototype.RtcpFbToJingle = function (mediaindex, elem, payloadtype) { // XEP-0293
|
|
406
|
+SDP.prototype.rtcpFbToJingle = function (mediaindex, elem, payloadtype) { // XEP-0293
|
394
|
407
|
var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=rtcp-fb:' + payloadtype);
|
395
|
408
|
lines.forEach(function (line) {
|
396
|
409
|
var tmp = SDPUtil.parse_rtcpfb(line);
|
|
@@ -407,7 +420,7 @@ SDP.prototype.RtcpFbToJingle = function (mediaindex, elem, payloadtype) { // XEP
|
407
|
420
|
});
|
408
|
421
|
};
|
409
|
422
|
|
410
|
|
-SDP.prototype.RtcpFbFromJingle = function (elem, payloadtype) { // XEP-0293
|
|
423
|
+SDP.prototype.rtcpFbFromJingle = function (elem, payloadtype) { // XEP-0293
|
411
|
424
|
var media = '';
|
412
|
425
|
var tmp = elem.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
|
413
|
426
|
if (tmp.length) {
|
|
@@ -434,7 +447,7 @@ SDP.prototype.RtcpFbFromJingle = function (elem, payloadtype) { // XEP-0293
|
434
|
447
|
SDP.prototype.fromJingle = function (jingle) {
|
435
|
448
|
var self = this;
|
436
|
449
|
this.raw = 'v=0\r\n' +
|
437
|
|
- 'o=- ' + '1923518516' + ' 2 IN IP4 0.0.0.0\r\n' +// FIXME
|
|
450
|
+ 'o=- 1923518516 2 IN IP4 0.0.0.0\r\n' +// FIXME
|
438
|
451
|
's=-\r\n' +
|
439
|
452
|
't=0 0\r\n';
|
440
|
453
|
// http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04#section-8
|
|
@@ -490,14 +503,11 @@ SDP.prototype.jingle2media = function (content) {
|
490
|
503
|
} else {
|
491
|
504
|
tmp.proto = 'RTP/AVPF';
|
492
|
505
|
}
|
493
|
|
- if (!sctp.length)
|
494
|
|
- {
|
|
506
|
+ if (!sctp.length) {
|
495
|
507
|
tmp.fmt = desc.find('payload-type').map(
|
496
|
508
|
function () { return this.getAttribute('id'); }).get();
|
497
|
509
|
media += SDPUtil.build_mline(tmp) + '\r\n';
|
498
|
|
- }
|
499
|
|
- else
|
500
|
|
- {
|
|
510
|
+ } else {
|
501
|
511
|
media += 'm=application 1 DTLS/SCTP ' + sctp.attr('number') + '\r\n';
|
502
|
512
|
media += 'a=sctpmap:' + sctp.attr('number') +
|
503
|
513
|
' ' + sctp.attr('protocol');
|
|
@@ -568,15 +578,19 @@ SDP.prototype.jingle2media = function (content) {
|
568
|
578
|
media += SDPUtil.build_rtpmap(this) + '\r\n';
|
569
|
579
|
if ($(this).find('>parameter').length) {
|
570
|
580
|
media += 'a=fmtp:' + this.getAttribute('id') + ' ';
|
571
|
|
- media += $(this).find('parameter').map(function () { return (this.getAttribute('name') ? (this.getAttribute('name') + '=') : '') + this.getAttribute('value'); }).get().join('; ');
|
|
581
|
+ media += $(this).find('parameter').map(function () {
|
|
582
|
+ return (this.getAttribute('name')
|
|
583
|
+ ? (this.getAttribute('name') + '=') : '') +
|
|
584
|
+ this.getAttribute('value');
|
|
585
|
+ }).get().join('; ');
|
572
|
586
|
media += '\r\n';
|
573
|
587
|
}
|
574
|
588
|
// xep-0293
|
575
|
|
- media += self.RtcpFbFromJingle($(this), this.getAttribute('id'));
|
|
589
|
+ media += self.rtcpFbFromJingle($(this), this.getAttribute('id'));
|
576
|
590
|
});
|
577
|
591
|
|
578
|
592
|
// xep-0293
|
579
|
|
- media += self.RtcpFbFromJingle(desc, '*');
|
|
593
|
+ media += self.rtcpFbFromJingle(desc, '*');
|
580
|
594
|
|
581
|
595
|
// xep-0294
|
582
|
596
|
tmp = desc.find('>rtp-hdrext[xmlns="urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"]');
|
|
@@ -585,11 +599,19 @@ SDP.prototype.jingle2media = function (content) {
|
585
|
599
|
});
|
586
|
600
|
|
587
|
601
|
content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function () {
|
|
602
|
+ var protocol = this.getAttribute('protocol');
|
|
603
|
+ protocol = (typeof protocol === 'string') ? protocol.toLowerCase(): '';
|
|
604
|
+
|
|
605
|
+ if ((self.removeTcpCandidates && protocol === 'tcp') ||
|
|
606
|
+ (self.removeUdpCandidates && protocol === 'udp')) {
|
|
607
|
+ return;
|
|
608
|
+ }
|
|
609
|
+
|
588
|
610
|
media += SDPUtil.candidateFromJingle(this);
|
589
|
611
|
});
|
590
|
612
|
|
591
|
613
|
// XEP-0339 handle ssrc-group attributes
|
592
|
|
- tmp = content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
|
|
614
|
+ content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
|
593
|
615
|
var semantics = this.getAttribute('semantics');
|
594
|
616
|
var ssrcs = $(this).find('>source').map(function() {
|
595
|
617
|
return this.getAttribute('ssrc');
|