ソースを参照

Adds SCTP data channels.

master
paweldomas 11年前
コミット
0509b8e3c4

+ 16
- 1
app.js ファイルの表示

470
     // TODO: do we check activecall == null?
470
     // TODO: do we check activecall == null?
471
     activecall = sess;
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
     // TODO: check affiliation and/or role
479
     // TODO: check affiliation and/or role
474
     console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
480
     console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
475
     sess.usedrip = true; // not-so-naive trickle ice
481
     sess.usedrip = true; // not-so-naive trickle ice
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
 $(document).bind('callactive.jingle', function (event, videoelem, sid) {
496
 $(document).bind('callactive.jingle', function (event, videoelem, sid) {
482
     if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
497
     if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
483
         // ignore mixedmslabela0 and v0
498
         // ignore mixedmslabela0 and v0
507
     var directions = {};
522
     var directions = {};
508
     var localSDP = new SDP(sess.peerconnection.localDescription.sdp);
523
     var localSDP = new SDP(sess.peerconnection.localDescription.sdp);
509
     localSDP.media.forEach(function (media) {
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
         if (SDPUtil.find_line(media, 'a=ssrc:')) {
527
         if (SDPUtil.find_line(media, 'a=ssrc:')) {
513
             // assumes a single local ssrc
528
             // assumes a single local ssrc

+ 2
- 1
config.js ファイルの表示

11
     bosh: '//lambada.jitsi.net/http-bind', // FIXME: use xep-0156 for that
11
     bosh: '//lambada.jitsi.net/http-bind', // FIXME: use xep-0156 for that
12
     desktopSharing: 'ext', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
12
     desktopSharing: 'ext', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
13
     chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
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 ファイルの表示

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

+ 275
- 81
libs/colibri/colibri.focus.js ファイルの表示

44
     this.peers = [];
44
     this.peers = [];
45
     this.confid = null;
45
     this.confid = null;
46
 
46
 
47
+    /**
48
+     * Default channel expire value in seconds.
49
+     * @type {number}
50
+     */
51
+    this.channelExpire = 60;
52
+
47
     // media types of the conference
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
     this.connection.jingle.sessions[this.sid] = this;
63
     this.connection.jingle.sessions[this.sid] = this;
51
     this.mychannel = [];
64
     this.mychannel = [];
151
     elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri'});
164
     elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri'});
152
 
165
 
153
     this.media.forEach(function (name) {
166
     this.media.forEach(function (name) {
167
+        var isData = name === 'data';
168
+        var channel = isData ? 'sctpconnection' : 'channel';
169
+
154
         elem.c('content', {name: name});
170
         elem.c('content', {name: name});
155
-        elem.c('channel', {
171
+
172
+        elem.c(channel, {
156
             initiator: 'true',
173
             initiator: 'true',
157
             expire: '15',
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
         for (var j = 0; j < self.peers.length; j++) {
181
         for (var j = 0; j < self.peers.length; j++) {
160
-            elem.c('channel', {
182
+            elem.c(channel, {
161
                 initiator: 'true',
183
                 initiator: 'true',
162
                 expire: '15',
184
                 expire: '15',
163
                 endpoint: self.peers[j].substr(1 + self.peers[j].lastIndexOf('/'))
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
         elem.up(); // end of content
191
         elem.up(); // end of content
167
     });
192
     });
209
     this.confid = $(result).find('>conference').attr('id');
234
     this.confid = $(result).find('>conference').attr('id');
210
     var remotecontents = $(result).find('>conference>content').get();
235
     var remotecontents = $(result).find('>conference>content').get();
211
     var numparticipants = 0;
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
         this.mychannel.push($(tmp.shift()));
244
         this.mychannel.push($(tmp.shift()));
215
         numparticipants = tmp.length;
245
         numparticipants = tmp.length;
216
         for (j = 0; j < tmp.length; j++) {
246
         for (j = 0; j < tmp.length; j++) {
223
 
253
 
224
     console.log('remote channels', this.channels);
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
     bridgeSDP.media.length = this.mychannel.length;
305
     bridgeSDP.media.length = this.mychannel.length;
228
     var channel;
306
     var channel;
229
     /*
307
     /*
262
         // get the mixed ssrc
340
         // get the mixed ssrc
263
         tmp = $(this.mychannel[channel]).find('>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
341
         tmp = $(this.mychannel[channel]).find('>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
264
         // FIXME: check rtp-level-relay-type
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
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'cname:mixed' + '\r\n';
347
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'cname:mixed' + '\r\n';
267
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
348
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
268
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
349
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
269
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'mslabel:mixedmslabel' + '\r\n';
350
             bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'mslabel:mixedmslabel' + '\r\n';
270
-        } else {
351
+        }
352
+        else if (!isData)
353
+        {
271
             // make chrome happy... '3735928559' == 0xDEADBEEF
354
             // make chrome happy... '3735928559' == 0xDEADBEEF
272
             // FIXME: this currently appears as two streams, should be one
355
             // FIXME: this currently appears as two streams, should be one
273
             bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
356
             bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
308
                             elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: self.confid});
391
                             elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: self.confid});
309
                             var localSDP = new SDP(self.peerconnection.localDescription.sdp);
392
                             var localSDP = new SDP(self.peerconnection.localDescription.sdp);
310
                             localSDP.media.forEach(function (media, channel) {
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
                                 elem.c('content', {name: name});
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
                                 var mline = SDPUtil.parse_mline(media.split('\r\n')[0]);
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
                                 localSDP.TransportToJingle(channel, elem);
431
                                 localSDP.TransportToJingle(channel, elem);
336
                                     // ...
439
                                     // ...
337
                                 },
440
                                 },
338
                                 function (error) {
441
                                 function (error) {
339
-                                    console.warn(error);
442
+                                    console.error(
443
+                                        "ERROR setLocalDescription succeded",
444
+                                        error, elem);
340
                                 }
445
                                 }
341
                             );
446
                             );
342
 
447
 
417
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
522
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
418
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
523
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
419
             sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'mslabel:mixedmslabel' + '\r\n';
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
             // make chrome happy... '3735928559' == 0xDEADBEEF
529
             // make chrome happy... '3735928559' == 0xDEADBEEF
422
             sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
530
             sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
423
             sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'label:mixedlabelv0' + '\r\n';
531
             sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'label:mixedlabelv0' + '\r\n';
486
 // pull in a new participant into the conference
594
 // pull in a new participant into the conference
487
 ColibriFocus.prototype.addNewParticipant = function (peer) {
595
 ColibriFocus.prototype.addNewParticipant = function (peer) {
488
     var self = this;
596
     var self = this;
489
-    if (this.confid === 0) {
597
+    if (this.confid === 0 || !this.peerconnection.localDescription)
598
+    {
490
         // bad state
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
         window.setTimeout(function () {
608
         window.setTimeout(function () {
493
             self.addNewParticipant(peer);
609
             self.addNewParticipant(peer);
494
         }, 250);
610
         }, 250);
502
     elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
618
     elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
503
     var localSDP = new SDP(this.peerconnection.localDescription.sdp);
619
     var localSDP = new SDP(this.peerconnection.localDescription.sdp);
504
     localSDP.media.forEach(function (media, channel) {
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
         elem.c('content', {name: name});
622
         elem.c('content', {name: name});
507
-        elem.c('channel', {
623
+        if (name !== 'data')
624
+        {
625
+            elem.c('channel', {
508
                 initiator: 'true',
626
                 initiator: 'true',
509
-                expire:'15',
627
+                expire: self.channelExpire,
510
                 endpoint: peer.substr(1 + peer.lastIndexOf('/'))
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
         elem.up(); // end of content
641
         elem.up(); // end of content
514
     });
642
     });
515
 
643
 
517
         function (result) {
645
         function (result) {
518
             var contents = $(result).find('>conference>content').get();
646
             var contents = $(result).find('>conference>content').get();
519
             for (var i = 0; i < contents.length; i++) {
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
                 self.channels[index][i] = tmp[0];
657
                 self.channels[index][i] = tmp[0];
522
             }
658
             }
523
             self.initiate(peer, true);
659
             self.initiate(peer, true);
531
 // update the channel description (payload-types + dtls fp) for a participant
667
 // update the channel description (payload-types + dtls fp) for a participant
532
 ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
668
 ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
533
     console.log('change allocation for', this.confid);
669
     console.log('change allocation for', this.confid);
670
+    var self = this;
534
     var change = $iq({to: this.bridgejid, type: 'set'});
671
     var change = $iq({to: this.bridgejid, type: 'set'});
535
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
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
         // now add transport
712
         // now add transport
562
         remoteSDP.TransportToJingle(channel, change);
713
         remoteSDP.TransportToJingle(channel, change);
563
 
714
 
564
-        change.up(); // end of channel
715
+        change.up(); // end of channel/sctpconnection
565
         change.up(); // end of content
716
         change.up(); // end of content
566
     }
717
     }
567
     this.connection.sendIQ(change,
718
     this.connection.sendIQ(change,
675
     this.remotessrc[session.peerjid] = [];
826
     this.remotessrc[session.peerjid] = [];
676
     for (channel = 0; channel < this.channels[participant].length; channel++) {
827
     for (channel = 0; channel < this.channels[participant].length; channel++) {
677
         //if (channel == 0) continue; FIXME: does not work as intended
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
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
856
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
703
     $(elem).each(function () {
857
     $(elem).each(function () {
704
         var name = $(this).attr('name');
858
         var name = $(this).attr('name');
859
+
705
         var channel = name == 'audio' ? 0 : 1; // FIXME: search mlineindex in localdesc
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
         change.c('content', {name: name});
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
         $(this).find('>transport').each(function () {
880
         $(this).find('>transport').each(function () {
714
             change.c('transport', {
881
             change.c('transport', {
715
                 ufrag: $(this).attr('ufrag'),
882
                 ufrag: $(this).attr('ufrag'),
729
             });
896
             });
730
             change.up(); // end of transport
897
             change.up(); // end of transport
731
         });
898
         });
732
-        change.up(); // end of channel
899
+        change.up(); // end of channel/sctpconnection
733
         change.up(); // end of content
900
         change.up(); // end of content
734
     });
901
     });
735
     // FIXME: need to check if there is at least one candidate when filtering TCP ones
902
     // FIXME: need to check if there is at least one candidate when filtering TCP ones
769
     mycands.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
936
     mycands.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
770
     // FIXME: multi-candidate logic is taken from strophe.jingle, should be refactored there
937
     // FIXME: multi-candidate logic is taken from strophe.jingle, should be refactored there
771
     var localSDP = new SDP(this.peerconnection.localDescription.sdp);
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
         var cands = candidates.filter(function (el) { return el.sdpMLineIndex == mid; });
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
             mycands.c('transport', {xmlns: 'urn:xmpp:jingle:transports:ice-udp:1'});
962
             mycands.c('transport', {xmlns: 'urn:xmpp:jingle:transports:ice-udp:1'});
782
             for (var i = 0; i < cands.length; i++) {
963
             for (var i = 0; i < cands.length; i++) {
783
                 mycands.c('candidate', SDPUtil.candidateToJingle(cands[i].candidate)).up();
964
                 mycands.c('candidate', SDPUtil.candidateToJingle(cands[i].candidate)).up();
784
             }
965
             }
785
             mycands.up(); // transport
966
             mycands.up(); // transport
786
-            mycands.up(); // channel
967
+            mycands.up(); // channel / sctpconnection
787
             mycands.up(); // content
968
             mycands.up(); // content
788
         }
969
         }
789
     }
970
     }
814
     var change = $iq({to: this.bridgejid, type: 'set'});
995
     var change = $iq({to: this.bridgejid, type: 'set'});
815
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
996
     change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
816
     for (var channel = 0; channel < this.channels[participant].length; channel++) {
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
         change.up(); // end of content
1018
         change.up(); // end of content
825
     }
1019
     }
826
     this.connection.sendIQ(change,
1020
     this.connection.sendIQ(change,

+ 4
- 4
libs/strophe/strophe.jingle.adapter.js ファイルの表示

32
     this.switchstreams = false;
32
     this.switchstreams = false;
33
 
33
 
34
     // override as desired
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
         self.updateLog.push({
37
         self.updateLog.push({
38
             time: new Date(),
38
             time: new Date(),
39
             type: what,
39
             type: what,
144
 
144
 
145
 TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
145
 TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
146
     this.trace('createDataChannel', label, opts);
146
     this.trace('createDataChannel', label, opts);
147
-    this.peerconnection.createDataChannel(label, opts);
148
-}
147
+    return this.peerconnection.createDataChannel(label, opts);
148
+};
149
 
149
 
150
 TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
150
 TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
151
     var self = this;
151
     var self = this;

+ 49
- 7
libs/strophe/strophe.jingle.sdp.js ファイルの表示

155
     }
155
     }
156
     for (i = 0; i < this.media.length; i++) {
156
     for (i = 0; i < this.media.length; i++) {
157
         mline = SDPUtil.parse_mline(this.media[i].split('\r\n')[0]);
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
             continue;
162
             continue;
160
         }
163
         }
161
         if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
164
         if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
171
             elem.attrs({ name: mid });
174
             elem.attrs({ name: mid });
172
 
175
 
173
             // old BUNDLE plan, to be removed
176
             // old BUNDLE plan, to be removed
174
-            if (bundle.indexOf(mid) != -1) {
177
+            if (bundle.indexOf(mid) !== -1) {
175
                 elem.c('bundle', {xmlns: 'http://estos.de/ns/bundle'}).up();
178
                 elem.c('bundle', {xmlns: 'http://estos.de/ns/bundle'}).up();
176
                 bundle.splice(bundle.indexOf(mid), 1);
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
             elem.c('description',
203
             elem.c('description',
181
                 {xmlns: 'urn:xmpp:jingle:apps:rtp:1',
204
                 {xmlns: 'urn:xmpp:jingle:apps:rtp:1',
182
                     media: mline.media });
205
                     media: mline.media });
438
         ssrc = desc.attr('ssrc'),
461
         ssrc = desc.attr('ssrc'),
439
         self = this,
462
         self = this,
440
         tmp;
463
         tmp;
464
+    var sctp = null;
465
+    if (!desc.length)
466
+    {
467
+        sctp = content.find('sctp');
468
+    }
441
 
469
 
442
     tmp = { media: desc.attr('media') };
470
     tmp = { media: desc.attr('media') };
443
     tmp.port = '1';
471
     tmp.port = '1';
446
         tmp.port = '0';
474
         tmp.port = '0';
447
     }
475
     }
448
     if (content.find('>transport>fingerprint').length || desc.find('encryption').length) {
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
     } else {
481
     } else {
451
         tmp.proto = 'RTP/AVPF';
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
     media += 'c=IN IP4 0.0.0.0\r\n';
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
     tmp = content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
499
     tmp = content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
458
     if (tmp.length) {
500
     if (tmp.length) {
459
         if (tmp.attr('ufrag')) {
501
         if (tmp.attr('ufrag')) {

+ 10
- 0
libs/strophe/strophe.jingle.sdp.util.js ファイルの表示

90
         data.channels = parts.length ? parts.shift() : '1';
90
         data.channels = parts.length ? parts.shift() : '1';
91
         return data;
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
     build_rtpmap: function (el) {
103
     build_rtpmap: function (el) {
94
         var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
104
         var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
95
         if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
105
         if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {

読み込み中…
キャンセル
保存