浏览代码

Adds SCTP data channels.

master
paweldomas 11 年前
父节点
当前提交
0509b8e3c4
共有 8 个文件被更改,包括 420 次插入94 次删除
  1. 16
    1
      app.js
  2. 2
    1
      config.js
  3. 63
    0
      data_channels.js
  4. 1
    0
      index.html
  5. 275
    81
      libs/colibri/colibri.focus.js
  6. 4
    4
      libs/strophe/strophe.jingle.adapter.js
  7. 49
    7
      libs/strophe/strophe.jingle.sdp.js
  8. 10
    0
      libs/strophe/strophe.jingle.sdp.util.js

+ 16
- 1
app.js 查看文件

@@ -470,6 +470,12 @@ $(document).bind('callincoming.jingle', function (event, sid) {
470 470
     // TODO: do we check activecall == null?
471 471
     activecall = sess;
472 472
 
473
+    // Bind data channel listener in case we're a regular participant
474
+    if (config.openSctp)
475
+    {
476
+        bindDataChannelListener(sess.peerconnection);
477
+    }
478
+
473 479
     // TODO: check affiliation and/or role
474 480
     console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
475 481
     sess.usedrip = true; // not-so-naive trickle ice
@@ -478,6 +484,15 @@ $(document).bind('callincoming.jingle', function (event, sid) {
478 484
 
479 485
 });
480 486
 
487
+$(document).bind('conferenceCreated.jingle', function (event, focus)
488
+{
489
+    // Bind data channel listener in case we're the focus
490
+    if (config.openSctp)
491
+    {
492
+        bindDataChannelListener(focus.peerconnection);
493
+    }
494
+});
495
+
481 496
 $(document).bind('callactive.jingle', function (event, videoelem, sid) {
482 497
     if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
483 498
         // ignore mixedmslabela0 and v0
@@ -507,7 +522,7 @@ $(document).bind('setLocalDescription.jingle', function (event, sid) {
507 522
     var directions = {};
508 523
     var localSDP = new SDP(sess.peerconnection.localDescription.sdp);
509 524
     localSDP.media.forEach(function (media) {
510
-        var type = SDPUtil.parse_mline(media.split('\r\n')[0]).media;
525
+        var type = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
511 526
 
512 527
         if (SDPUtil.find_line(media, 'a=ssrc:')) {
513 528
             // assumes a single local ssrc

+ 2
- 1
config.js 查看文件

@@ -11,5 +11,6 @@ var config = {
11 11
     bosh: '//lambada.jitsi.net/http-bind', // FIXME: use xep-0156 for that
12 12
     desktopSharing: 'ext', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
13 13
     chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
14
-    minChromeExtVersion: '0.1' // Required version of Chrome extension
14
+    minChromeExtVersion: '0.1', // Required version of Chrome extension
15
+    openSctp: true //Toggle to enable/disable SCTP channels
15 16
 };

+ 63
- 0
data_channels.js 查看文件

@@ -0,0 +1,63 @@
1
+/**
2
+ * Callback triggered by PeerConnection when new data channel is opened
3
+ * on the bridge.
4
+ * @param event the event info object.
5
+ */
6
+function onDataChannel(event)
7
+{
8
+    var dataChannel = event.channel;
9
+
10
+    dataChannel.onopen = function ()
11
+    {
12
+        console.info("Data channel opened by the bridge !!!", dataChannel);
13
+
14
+        // Sends String message to the bridge
15
+        dataChannel.send("Hello bridge!");
16
+
17
+        // Sends 12 bytes binary message to the bridge
18
+        dataChannel.send(new ArrayBuffer(12));
19
+    };
20
+
21
+    dataChannel.onerror = function (error)
22
+    {
23
+        console.error("Data Channel Error:", error, dataChannel);
24
+    };
25
+
26
+    dataChannel.onmessage = function (event)
27
+    {
28
+        var msgData = event.data;
29
+        console.info("Got Data Channel Message:", msgData, dataChannel);
30
+    };
31
+
32
+    dataChannel.onclose = function ()
33
+    {
34
+        console.info("The Data Channel closed", dataChannel);
35
+    };
36
+}
37
+
38
+/**
39
+ * Binds "ondatachannel" event listener to given PeerConnection instance.
40
+ * @param peerConnection WebRTC peer connection instance.
41
+ */
42
+function bindDataChannelListener(peerConnection)
43
+{
44
+    peerConnection.ondatachannel = onDataChannel;
45
+
46
+    // Sample code for opening new data channel from Jitsi Meet to the bridge.
47
+    // Although it's not a requirement to open separate channels from both bridge
48
+    // and peer as single channel can be used for sending and receiving data.
49
+    // So either channel opened by the bridge or the one opened here is enough
50
+    // for communication with the bridge.
51
+    var dataChannelOptions =
52
+    {
53
+        reliable: true
54
+    };
55
+    var dataChannel
56
+       = peerConnection.createDataChannel("myChannel", dataChannelOptions);
57
+
58
+    // Can be used only when is in open state
59
+    dataChannel.onopen = function ()
60
+    {
61
+        dataChannel.send("My channel !!!");
62
+    };
63
+}

+ 1
- 0
index.html 查看文件

@@ -24,6 +24,7 @@
24 24
     <script src="muc.js?v=10"></script><!-- simple MUC library -->
25 25
     <script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
26 26
     <script src="desktopsharing.js?v=1"></script><!-- desktop sharing -->
27
+    <script src="data_channels.js?v=1"></script><!-- data channels -->
27 28
     <script src="app.js?v=26"></script><!-- application logic -->
28 29
     <script src="chat.js?v=4"></script><!-- chat logic -->
29 30
     <script src="util.js?v=3"></script><!-- utility functions -->

+ 275
- 81
libs/colibri/colibri.focus.js 查看文件

@@ -44,8 +44,21 @@ function ColibriFocus(connection, bridgejid) {
44 44
     this.peers = [];
45 45
     this.confid = null;
46 46
 
47
+    /**
48
+     * Default channel expire value in seconds.
49
+     * @type {number}
50
+     */
51
+    this.channelExpire = 60;
52
+
47 53
     // media types of the conference
48
-    this.media = ['audio', 'video'];
54
+    if (config.openSctp)
55
+    {
56
+        this.media = ['audio', 'video', 'data'];
57
+    }
58
+    else
59
+    {
60
+        this.media = ['audio', 'video'];
61
+    }
49 62
 
50 63
     this.connection.jingle.sessions[this.sid] = this;
51 64
     this.mychannel = [];
@@ -151,17 +164,29 @@ ColibriFocus.prototype._makeConference = function () {
151 164
     elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri'});
152 165
 
153 166
     this.media.forEach(function (name) {
167
+        var isData = name === 'data';
168
+        var channel = isData ? 'sctpconnection' : 'channel';
169
+
154 170
         elem.c('content', {name: name});
155
-        elem.c('channel', {
171
+
172
+        elem.c(channel, {
156 173
             initiator: 'true',
157 174
             expire: '15',
158
-            endpoint: 'fix_me_focus_endpoint'}).up();
175
+            endpoint: 'fix_me_focus_endpoint'
176
+        });
177
+        if (isData)
178
+            elem.attrs({port:  5000});
179
+        elem.up();// end of channel
180
+
159 181
         for (var j = 0; j < self.peers.length; j++) {
160
-            elem.c('channel', {
182
+            elem.c(channel, {
161 183
                 initiator: 'true',
162 184
                 expire: '15',
163 185
                 endpoint: self.peers[j].substr(1 + self.peers[j].lastIndexOf('/'))
164
-            }).up();
186
+            });
187
+            if (isData)
188
+                elem.attrs({port: 5000});
189
+            elem.up(); // end of channel
165 190
         }
166 191
         elem.up(); // end of content
167 192
     });
@@ -209,8 +234,13 @@ ColibriFocus.prototype.createdConference = function (result) {
209 234
     this.confid = $(result).find('>conference').attr('id');
210 235
     var remotecontents = $(result).find('>conference>content').get();
211 236
     var numparticipants = 0;
212
-    for (var i = 0; i < remotecontents.length; i++) {
213
-        tmp = $(remotecontents[i]).find('>channel').get();
237
+    for (var i = 0; i < remotecontents.length; i++)
238
+    {
239
+        var contentName = $(remotecontents[i]).attr('name');
240
+        var channelName
241
+            = contentName !== 'data' ? '>channel' : '>sctpconnection';
242
+
243
+        tmp = $(remotecontents[i]).find(channelName).get();
214 244
         this.mychannel.push($(tmp.shift()));
215 245
         numparticipants = tmp.length;
216 246
         for (j = 0; j < tmp.length; j++) {
@@ -223,7 +253,55 @@ ColibriFocus.prototype.createdConference = function (result) {
223 253
 
224 254
     console.log('remote channels', this.channels);
225 255
 
226
-    var bridgeSDP = new SDP('v=0\r\no=- 5151055458874951233 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\nm=audio 1 RTP/SAVPF 111 103 104 0 8 106 105 13 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=sendrecv\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:106 CN/32000\r\na=rtpmap:105 CN/16000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:126 telephone-event/8000\r\na=maxptime:60\r\nm=video 1 RTP/SAVPF 100 116 117\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtpmap:100 VP8/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 goog-remb\r\na=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\n');
256
+    // Notify that the focus has created the conference on the bridge
257
+    $(document).trigger('conferenceCreated.jingle', [self]);
258
+
259
+    var bridgeSDP = new SDP(
260
+        'v=0\r\n' +
261
+        'o=- 5151055458874951233 2 IN IP4 127.0.0.1\r\n' +
262
+        's=-\r\n' +
263
+        't=0 0\r\n' +
264
+        /* Audio */
265
+        'm=audio 1 RTP/SAVPF 111 103 104 0 8 106 105 13 126\r\n' +
266
+        'c=IN IP4 0.0.0.0\r\n' +
267
+        'a=rtcp:1 IN IP4 0.0.0.0\r\n' +
268
+        'a=mid:audio\r\n' +
269
+        'a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n' +
270
+        'a=sendrecv\r\n' +
271
+        'a=rtpmap:111 opus/48000/2\r\n' +
272
+        'a=fmtp:111 minptime=10\r\n' +
273
+        'a=rtpmap:103 ISAC/16000\r\n' +
274
+        'a=rtpmap:104 ISAC/32000\r\n' +
275
+        'a=rtpmap:0 PCMU/8000\r\n' +
276
+        'a=rtpmap:8 PCMA/8000\r\n' +
277
+        'a=rtpmap:106 CN/32000\r\n' +
278
+        'a=rtpmap:105 CN/16000\r\n' +
279
+        'a=rtpmap:13 CN/8000\r\n' +
280
+        'a=rtpmap:126 telephone-event/8000\r\n' +
281
+        'a=maxptime:60\r\n' +
282
+        /* Video */
283
+        'm=video 1 RTP/SAVPF 100 116 117\r\n' +
284
+        'c=IN IP4 0.0.0.0\r\n' +
285
+        'a=rtcp:1 IN IP4 0.0.0.0\r\n' +
286
+        'a=mid:video\r\n' +
287
+        'a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n' +
288
+        'a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n' +
289
+        'a=sendrecv\r\n' +
290
+        'a=rtpmap:100 VP8/90000\r\n' +
291
+        'a=rtcp-fb:100 ccm fir\r\n' +
292
+        'a=rtcp-fb:100 nack\r\n' +
293
+        'a=rtcp-fb:100 goog-remb\r\n' +
294
+        'a=rtpmap:116 red/90000\r\n' +
295
+        'a=rtpmap:117 ulpfec/90000\r\n' +
296
+        /* Data SCTP */
297
+        (config.openSctp ?
298
+            'm=application 1 DTLS/SCTP 5000\r\n' +
299
+            'c=IN IP4 0.0.0.0\r\n' +
300
+            'a=sctpmap:5000 webrtc-datachannel\r\n' +
301
+            'a=mid:data\r\n'
302
+            : '')
303
+    );
304
+
227 305
     bridgeSDP.media.length = this.mychannel.length;
228 306
     var channel;
229 307
     /*
@@ -262,12 +340,17 @@ ColibriFocus.prototype.createdConference = function (result) {
262 340
         // get the mixed ssrc
263 341
         tmp = $(this.mychannel[channel]).find('>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
264 342
         // FIXME: check rtp-level-relay-type
265
-        if (tmp.length) {
343
+
344
+        var isData = bridgeSDP.media[channel].indexOf('application') !== -1;
345
+        if (!isData && tmp.length)
346
+        {
266 347
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'cname:mixed' + '\r\n';
267 348
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
268 349
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
269 350
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'mslabel:mixedmslabel' + '\r\n';
270
-        } else {
351
+        }
352
+        else if (!isData)
353
+        {
271 354
             // make chrome happy... '3735928559' == 0xDEADBEEF
272 355
             // FIXME: this currently appears as two streams, should be one
273 356
             bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
@@ -308,21 +391,41 @@ ColibriFocus.prototype.createdConference = function (result) {
308 391
                             elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: self.confid});
309 392
                             var localSDP = new SDP(self.peerconnection.localDescription.sdp);
310 393
                             localSDP.media.forEach(function (media, channel) {
311
-                                var name = SDPUtil.parse_mline(media.split('\r\n')[0]).media;
394
+                                var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
312 395
                                 elem.c('content', {name: name});
313
-                                elem.c('channel', {
314
-                                    initiator: 'true',
315
-                                    expire: '15',
316
-                                    id: self.mychannel[channel].attr('id'),
317
-                                    endpoint: 'fix_me_focus_endpoint'
318
-                                });
319
-
320
-                                // FIXME: should reuse code from .toJingle
321 396
                                 var mline = SDPUtil.parse_mline(media.split('\r\n')[0]);
322
-                                for (var j = 0; j < mline.fmt.length; j++) {
323
-                                    var rtpmap = SDPUtil.find_line(media, 'a=rtpmap:' + mline.fmt[j]);
324
-                                    elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
325
-                                    elem.up();
397
+                                if (name !== 'data')
398
+                                {
399
+                                    elem.c('channel', {
400
+                                        initiator: 'true',
401
+                                        expire: self.channelExpire,
402
+                                        id: self.mychannel[channel].attr('id'),
403
+                                        endpoint: 'fix_me_focus_endpoint'
404
+                                    });
405
+
406
+                                    // FIXME: should reuse code from .toJingle
407
+                                    for (var j = 0; j < mline.fmt.length; j++)
408
+                                    {
409
+                                        var rtpmap = SDPUtil.find_line(media, 'a=rtpmap:' + mline.fmt[j]);
410
+                                        if (rtpmap)
411
+                                        {
412
+                                            elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
413
+                                            elem.up();
414
+                                        }
415
+                                    }
416
+                                }
417
+                                else
418
+                                {
419
+                                    var sctpmap = SDPUtil.find_line(media, 'a=sctpmap:' + mline.fmt[0]);
420
+                                    var sctpPort = SDPUtil.parse_sctpmap(sctpmap);
421
+                                    elem.c("sctpconnection",
422
+                                        {
423
+                                            initiator: 'true',
424
+                                            expire: self.channelExpire,
425
+                                            endpoint: 'fix_me_focus_endpoint',
426
+                                            port: sctpPort
427
+                                        }
428
+                                    );
326 429
                                 }
327 430
 
328 431
                                 localSDP.TransportToJingle(channel, elem);
@@ -336,7 +439,9 @@ ColibriFocus.prototype.createdConference = function (result) {
336 439
                                     // ...
337 440
                                 },
338 441
                                 function (error) {
339
-                                    console.warn(error);
442
+                                    console.error(
443
+                                        "ERROR setLocalDescription succeded",
444
+                                        error, elem);
340 445
                                 }
341 446
                             );
342 447
 
@@ -417,7 +522,10 @@ ColibriFocus.prototype.initiate = function (peer, isInitiator) {
417 522
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
418 523
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
419 524
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'mslabel:mixedmslabel' + '\r\n';
420
-        } else {
525
+        }
526
+        // No SSRCs for 'data', comes when j == 2
527
+        else if (j < 2)
528
+        {
421 529
             // make chrome happy... '3735928559' == 0xDEADBEEF
422 530
             sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
423 531
             sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'label:mixedlabelv0' + '\r\n';
@@ -486,9 +594,17 @@ ColibriFocus.prototype.initiate = function (peer, isInitiator) {
486 594
 // pull in a new participant into the conference
487 595
 ColibriFocus.prototype.addNewParticipant = function (peer) {
488 596
     var self = this;
489
-    if (this.confid === 0) {
597
+    if (this.confid === 0 || !this.peerconnection.localDescription)
598
+    {
490 599
         // bad state
491
-        console.log('confid does not exist yet, postponing', peer);
600
+        if (this.confid === 0)
601
+        {
602
+            console.error('confid does not exist yet, postponing', peer);
603
+        }
604
+        else
605
+        {
606
+            console.error('local description not ready yet, postponing', peer);
607
+        }
492 608
         window.setTimeout(function () {
493 609
             self.addNewParticipant(peer);
494 610
         }, 250);
@@ -502,14 +618,26 @@ ColibriFocus.prototype.addNewParticipant = function (peer) {
502 618
     elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
503 619
     var localSDP = new SDP(this.peerconnection.localDescription.sdp);
504 620
     localSDP.media.forEach(function (media, channel) {
505
-        var name = SDPUtil.parse_mline(media.split('\r\n')[0]).media;
621
+        var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
506 622
         elem.c('content', {name: name});
507
-        elem.c('channel', {
623
+        if (name !== 'data')
624
+        {
625
+            elem.c('channel', {
508 626
                 initiator: 'true',
509
-                expire:'15',
627
+                expire: self.channelExpire,
510 628
                 endpoint: peer.substr(1 + peer.lastIndexOf('/'))
511
-        });
512
-        elem.up(); // end of channel
629
+            });
630
+        }
631
+        else
632
+        {
633
+            elem.c('sctpconnection', {
634
+                endpoint: peer.substr(1 + peer.lastIndexOf('/')),
635
+                initiator: 'true',
636
+                expire: self.channelExpire,
637
+                port: 5000
638
+            });
639
+        }
640
+        elem.up(); // end of channel/sctpconnection
513 641
         elem.up(); // end of content
514 642
     });
515 643
 
@@ -517,7 +645,15 @@ ColibriFocus.prototype.addNewParticipant = function (peer) {
517 645
         function (result) {
518 646
             var contents = $(result).find('>conference>content').get();
519 647
             for (var i = 0; i < contents.length; i++) {
520
-                tmp = $(contents[i]).find('>channel').get();
648
+                var channelXml = $(contents[i]).find('>channel');
649
+                if (channelXml.length)
650
+                {
651
+                    tmp = channelXml.get();
652
+                }
653
+                else
654
+                {
655
+                    tmp = $(contents[i]).find('>sctpconnection').get();
656
+                }
521 657
                 self.channels[index][i] = tmp[0];
522 658
             }
523 659
             self.initiate(peer, true);
@@ -531,37 +667,52 @@ ColibriFocus.prototype.addNewParticipant = function (peer) {
531 667
 // update the channel description (payload-types + dtls fp) for a participant
532 668
 ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
533 669
     console.log('change allocation for', this.confid);
670
+    var self = this;
534 671
     var change = $iq({to: this.bridgejid, type: 'set'});
535 672
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
536
-    for (channel = 0; channel < this.channels[participant].length; channel++) {
537
-        change.c('content', {name: channel === 0 ? 'audio' : 'video'});
538
-        change.c('channel', {
539
-            id: $(this.channels[participant][channel]).attr('id'),
540
-            endpoint: $(this.channels[participant][channel]).attr('endpoint'),
541
-            expire: '15'
542
-        });
673
+    for (channel = 0; channel < this.channels[participant].length; channel++)
674
+    {
675
+        var name = SDPUtil.parse_mid(SDPUtil.find_line(remoteSDP.media[channel], 'a=mid:'));
676
+        change.c('content', {name: name});
677
+        if (name !== 'data')
678
+        {
679
+            change.c('channel', {
680
+                id: $(this.channels[participant][channel]).attr('id'),
681
+                endpoint: $(this.channels[participant][channel]).attr('endpoint'),
682
+                expire: self.channelExpire
683
+            });
543 684
 
544
-        var rtpmap = SDPUtil.find_lines(remoteSDP.media[channel], 'a=rtpmap:');
545
-        rtpmap.forEach(function (val) {
546
-            // TODO: too much copy-paste
547
-            var rtpmap = SDPUtil.parse_rtpmap(val);
548
-            change.c('payload-type', rtpmap);
549
-            //
550
-            // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
551
-            /*
552
-            if (SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id)) {
553
-                tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id));
554
-                for (var k = 0; k < tmp.length; k++) {
555
-                    change.c('parameter', tmp[k]).up();
685
+            var rtpmap = SDPUtil.find_lines(remoteSDP.media[channel], 'a=rtpmap:');
686
+            rtpmap.forEach(function (val) {
687
+                // TODO: too much copy-paste
688
+                var rtpmap = SDPUtil.parse_rtpmap(val);
689
+                change.c('payload-type', rtpmap);
690
+                //
691
+                // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
692
+                /*
693
+                if (SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id)) {
694
+                    tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id));
695
+                    for (var k = 0; k < tmp.length; k++) {
696
+                        change.c('parameter', tmp[k]).up();
697
+                    }
556 698
                 }
557
-            }
558
-            */
559
-            change.up();
560
-        });
699
+                */
700
+                change.up();
701
+            });
702
+        }
703
+        else
704
+        {
705
+            var sctpmap = SDPUtil.find_line(remoteSDP.media[channel], 'a=sctpmap:');
706
+            change.c('sctpconnection', {
707
+                endpoint: $(this.channels[participant][channel]).attr('endpoint'),
708
+                expire: self.channelExpire,
709
+                port: SDPUtil.parse_sctpmap(sctpmap)
710
+            });
711
+        }
561 712
         // now add transport
562 713
         remoteSDP.TransportToJingle(channel, change);
563 714
 
564
-        change.up(); // end of channel
715
+        change.up(); // end of channel/sctpconnection
565 716
         change.up(); // end of content
566 717
     }
567 718
     this.connection.sendIQ(change,
@@ -675,8 +826,11 @@ ColibriFocus.prototype.setRemoteDescription = function (session, elem, desctype)
675 826
     this.remotessrc[session.peerjid] = [];
676 827
     for (channel = 0; channel < this.channels[participant].length; channel++) {
677 828
         //if (channel == 0) continue; FIXME: does not work as intended
678
-        if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length) {
679
-            this.remotessrc[session.peerjid][channel] = SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').join('\r\n') + '\r\n';
829
+        if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length)
830
+        {
831
+            this.remotessrc[session.peerjid][channel] =
832
+                SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:')
833
+                        .join('\r\n') + '\r\n';
680 834
         }
681 835
     }
682 836
 
@@ -702,14 +856,27 @@ ColibriFocus.prototype.addIceCandidate = function (session, elem) {
702 856
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
703 857
     $(elem).each(function () {
704 858
         var name = $(this).attr('name');
859
+
705 860
         var channel = name == 'audio' ? 0 : 1; // FIXME: search mlineindex in localdesc
861
+        if (name != 'audio' && name != 'video')
862
+            channel = 2; // name == 'data'
706 863
 
707 864
         change.c('content', {name: name});
708
-        change.c('channel', {
709
-            id: $(self.channels[participant][channel]).attr('id'),
710
-            endpoint: $(self.channels[participant][channel]).attr('endpoint'),
711
-            expire: '15'
712
-        });
865
+        if (name !== 'data')
866
+        {
867
+            change.c('channel', {
868
+                id: $(self.channels[participant][channel]).attr('id'),
869
+                endpoint: $(self.channels[participant][channel]).attr('endpoint'),
870
+                expire: self.channelExpire
871
+            });
872
+        }
873
+        else
874
+        {
875
+            change.c('sctpconnection', {
876
+                endpoint: $(self.channels[participant][channel]).attr('endpoint'),
877
+                expire: self.channelExpire
878
+            });
879
+        }
713 880
         $(this).find('>transport').each(function () {
714 881
             change.c('transport', {
715 882
                 ufrag: $(this).attr('ufrag'),
@@ -729,7 +896,7 @@ ColibriFocus.prototype.addIceCandidate = function (session, elem) {
729 896
             });
730 897
             change.up(); // end of transport
731 898
         });
732
-        change.up(); // end of channel
899
+        change.up(); // end of channel/sctpconnection
733 900
         change.up(); // end of content
734 901
     });
735 902
     // FIXME: need to check if there is at least one candidate when filtering TCP ones
@@ -769,21 +936,35 @@ ColibriFocus.prototype.sendIceCandidates = function (candidates) {
769 936
     mycands.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
770 937
     // FIXME: multi-candidate logic is taken from strophe.jingle, should be refactored there
771 938
     var localSDP = new SDP(this.peerconnection.localDescription.sdp);
772
-    for (var mid = 0; mid < localSDP.media.length; mid++) {
939
+    for (var mid = 0; mid < localSDP.media.length; mid++)
940
+    {
773 941
         var cands = candidates.filter(function (el) { return el.sdpMLineIndex == mid; });
774
-        if (cands.length > 0) {
775
-            mycands.c('content', {name: cands[0].sdpMid });
776
-            mycands.c('channel', {
777
-                id: $(this.mychannel[cands[0].sdpMLineIndex]).attr('id'),
778
-                endpoint: $(this.mychannel[cands[0].sdpMLineIndex]).attr('endpoint'),
779
-                expire: '15'
780
-            });
942
+        if (cands.length > 0)
943
+        {
944
+            var name = cands[0].sdpMid;
945
+            mycands.c('content', {name: name });
946
+            if (name !== 'data')
947
+            {
948
+                mycands.c('channel', {
949
+                    id: $(this.mychannel[cands[0].sdpMLineIndex]).attr('id'),
950
+                    endpoint: $(this.mychannel[cands[0].sdpMLineIndex]).attr('endpoint'),
951
+                    expire: self.channelExpire
952
+                });
953
+            }
954
+            else
955
+            {
956
+                mycands.c('sctpconnection', {
957
+                    endpoint: $(this.mychannel[cands[0].sdpMLineIndex]).attr('endpoint'),
958
+                    port: $(this.mychannel[cands[0].sdpMLineIndex]).attr('port'),
959
+                    expire: self.channelExpire
960
+                });
961
+            }
781 962
             mycands.c('transport', {xmlns: 'urn:xmpp:jingle:transports:ice-udp:1'});
782 963
             for (var i = 0; i < cands.length; i++) {
783 964
                 mycands.c('candidate', SDPUtil.candidateToJingle(cands[i].candidate)).up();
784 965
             }
785 966
             mycands.up(); // transport
786
-            mycands.up(); // channel
967
+            mycands.up(); // channel / sctpconnection
787 968
             mycands.up(); // content
788 969
         }
789 970
     }
@@ -814,13 +995,26 @@ ColibriFocus.prototype.terminate = function (session, reason) {
814 995
     var change = $iq({to: this.bridgejid, type: 'set'});
815 996
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
816 997
     for (var channel = 0; channel < this.channels[participant].length; channel++) {
817
-        change.c('content', {name: channel === 0 ? 'audio' : 'video'});
818
-        change.c('channel', {
819
-            id: $(this.channels[participant][channel]).attr('id'),
820
-            endpoint: $(this.channels[participant][channel]).attr('endpoint'),
821
-            expire: '0'
822
-        });
823
-        change.up(); // end of channel
998
+        var name = channel === 0 ? 'audio' : 'video';
999
+        if (channel == 2)
1000
+            name = 'data';
1001
+        change.c('content', {name: name});
1002
+        if (name !== 'data')
1003
+        {
1004
+            change.c('channel', {
1005
+                id: $(this.channels[participant][channel]).attr('id'),
1006
+                endpoint: $(this.channels[participant][channel]).attr('endpoint'),
1007
+                expire: '0'
1008
+            });
1009
+        }
1010
+        else
1011
+        {
1012
+            change.c('sctpconnection', {
1013
+                endpoint: $(this.channels[participant][channel]).attr('endpoint'),
1014
+                expire: '0'
1015
+            });
1016
+        }
1017
+        change.up(); // end of channel/sctpconnection
824 1018
         change.up(); // end of content
825 1019
     }
826 1020
     this.connection.sendIQ(change,

+ 4
- 4
libs/strophe/strophe.jingle.adapter.js 查看文件

@@ -32,8 +32,8 @@ function TraceablePeerConnection(ice_config, constraints) {
32 32
     this.switchstreams = false;
33 33
 
34 34
     // override as desired
35
-    this.trace = function(what, info) {
36
-        //console.warn('WTRACE', what, info);
35
+    this.trace = function (what, info) {
36
+        console.warn('WTRACE', what, info);
37 37
         self.updateLog.push({
38 38
             time: new Date(),
39 39
             type: what,
@@ -144,8 +144,8 @@ TraceablePeerConnection.prototype.removeStream = function (stream) {
144 144
 
145 145
 TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
146 146
     this.trace('createDataChannel', label, opts);
147
-    this.peerconnection.createDataChannel(label, opts);
148
-}
147
+    return this.peerconnection.createDataChannel(label, opts);
148
+};
149 149
 
150 150
 TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
151 151
     var self = this;

+ 49
- 7
libs/strophe/strophe.jingle.sdp.js 查看文件

@@ -155,7 +155,10 @@ SDP.prototype.toJingle = function (elem, thecreator) {
155 155
     }
156 156
     for (i = 0; i < this.media.length; i++) {
157 157
         mline = SDPUtil.parse_mline(this.media[i].split('\r\n')[0]);
158
-        if (!(mline.media == 'audio' || mline.media == 'video')) {
158
+        if (!(mline.media === 'audio' ||
159
+              mline.media === 'video' ||
160
+              mline.media === 'application'))
161
+        {
159 162
             continue;
160 163
         }
161 164
         if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
@@ -171,12 +174,32 @@ SDP.prototype.toJingle = function (elem, thecreator) {
171 174
             elem.attrs({ name: mid });
172 175
 
173 176
             // old BUNDLE plan, to be removed
174
-            if (bundle.indexOf(mid) != -1) {
177
+            if (bundle.indexOf(mid) !== -1) {
175 178
                 elem.c('bundle', {xmlns: 'http://estos.de/ns/bundle'}).up();
176 179
                 bundle.splice(bundle.indexOf(mid), 1);
177 180
             }
178 181
         }
179
-        if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length) {
182
+        // Sctp
183
+        if (SDPUtil.find_line(this.media[i], 'a=sctpmap:').length)
184
+        {
185
+            for (j = 0; j < mline.fmt.length; j++)
186
+            {
187
+                var sctpmap = SDPUtil.find_line(
188
+                    this.media[i], 'a=sctpmap:' + mline.fmt[j]);
189
+                if (sctpmap)
190
+                {
191
+                    elem.c('sctp',
192
+                        {
193
+                            xmlns: 'http://jitsi.org/ns/sctp',
194
+                            port : SDPUtil.parse_sctpmap(sctpmap)
195
+                        });
196
+                    elem.up();
197
+                }
198
+            }
199
+        }
200
+
201
+        if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length)
202
+        {
180 203
             elem.c('description',
181 204
                 {xmlns: 'urn:xmpp:jingle:apps:rtp:1',
182 205
                     media: mline.media });
@@ -438,6 +461,11 @@ SDP.prototype.jingle2media = function (content) {
438 461
         ssrc = desc.attr('ssrc'),
439 462
         self = this,
440 463
         tmp;
464
+    var sctp = null;
465
+    if (!desc.length)
466
+    {
467
+        sctp = content.find('sctp');
468
+    }
441 469
 
442 470
     tmp = { media: desc.attr('media') };
443 471
     tmp.port = '1';
@@ -446,14 +474,28 @@ SDP.prototype.jingle2media = function (content) {
446 474
         tmp.port = '0';
447 475
     }
448 476
     if (content.find('>transport>fingerprint').length || desc.find('encryption').length) {
449
-        tmp.proto = 'RTP/SAVPF';
477
+        if (sctp)
478
+            tmp.proto = 'DTLS/SCTP';
479
+        else
480
+            tmp.proto = 'RTP/SAVPF';
450 481
     } else {
451 482
         tmp.proto = 'RTP/AVPF';
452 483
     }
453
-    tmp.fmt = desc.find('payload-type').map(function () { return this.getAttribute('id'); }).get();
454
-    media += SDPUtil.build_mline(tmp) + '\r\n';
484
+    if (!sctp)
485
+    {
486
+        tmp.fmt = desc.find('payload-type').map(
487
+            function () { return this.getAttribute('id'); }).get();
488
+        media += SDPUtil.build_mline(tmp) + '\r\n';
489
+    }
490
+    else
491
+    {
492
+        media += 'm=application 1 DTLS/SCTP ' + sctp.attr('port') + '\r\n';
493
+        media += 'a=sctpmap:' + sctp.attr('port') + ' webrtc-datachannel\r\n';
494
+    }
495
+
455 496
     media += 'c=IN IP4 0.0.0.0\r\n';
456
-    media += 'a=rtcp:1 IN IP4 0.0.0.0\r\n';
497
+    if (!sctp)
498
+        media += 'a=rtcp:1 IN IP4 0.0.0.0\r\n';
457 499
     tmp = content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
458 500
     if (tmp.length) {
459 501
         if (tmp.attr('ufrag')) {

+ 10
- 0
libs/strophe/strophe.jingle.sdp.util.js 查看文件

@@ -90,6 +90,16 @@ SDPUtil = {
90 90
         data.channels = parts.length ? parts.shift() : '1';
91 91
         return data;
92 92
     },
93
+    /**
94
+     * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
95
+     * @param line eg. "a=sctpmap:5000 webrtc-datachannel"
96
+     * @returns SCTP port number
97
+     */
98
+    parse_sctpmap: function (line)
99
+    {
100
+        var parts = line.substring(10).split(' ');
101
+        return parts[0];// SCTP port
102
+    },
93 103
     build_rtpmap: function (el) {
94 104
         var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
95 105
         if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {

正在加载...
取消
保存