Переглянути джерело

Merge pull request #44 from jitsi/early-dtls

Fix early DTLS
dev1
hristoterezov 9 роки тому
джерело
коміт
4526a72338
3 змінених файлів з 114 додано та 56 видалено
  1. 81
    22
      modules/xmpp/JingleSessionPC.js
  2. 28
    30
      modules/xmpp/SDP.js
  3. 5
    4
      modules/xmpp/SDPUtil.js

+ 81
- 22
modules/xmpp/JingleSessionPC.js Переглянути файл

@@ -339,31 +339,41 @@ JingleSessionPC.prototype.sendAnswer = function (success, failure) {
339 339
 
340 340
 JingleSessionPC.prototype.createdAnswer = function (sdp, success, failure) {
341 341
     //logger.log('createAnswer callback');
342
+
342 343
     var self = this;
343 344
     this.localSDP = new SDP(sdp.sdp);
345
+
346
+    this._fixAnswerRFC4145Setup(
347
+            /* offer */ this.remoteSDP,
348
+            /* answer */ this.localSDP);
349
+
344 350
     var sendJingle = function (ssrcs) {
345
-                var accept = $iq({to: self.peerjid,
346
-                    type: 'set'})
347
-                    .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
348
-                        action: 'session-accept',
349
-                        initiator: self.initiator,
350
-                        responder: self.responder,
351
-                        sid: self.sid });
352
-                if (self.webrtcIceTcpDisable) {
353
-                    self.localSDP.removeTcpCandidates = true;
354
-                }
355
-                if (self.webrtcIceUdpDisable) {
356
-                    self.localSDP.removeUdpCandidates = true;
357
-                }
358
-                self.localSDP.toJingle(
359
-                    accept,
360
-                    self.initiator == self.me ? 'initiator' : 'responder',
361
-                    ssrcs);
362
-                self.fixJingle(accept);
363
-                self.connection.sendIQ(accept,
364
-                    success,
365
-                    self.newJingleErrorHandler(accept, failure),
366
-                    IQ_TIMEOUT);
351
+        var accept
352
+            = $iq({ to: self.peerjid, type: 'set' })
353
+                .c('jingle', { xmlns: 'urn:xmpp:jingle:1',
354
+                               action: 'session-accept',
355
+                               initiator: self.initiator,
356
+                               responder: self.responder,
357
+                               sid: self.sid });
358
+        if (self.webrtcIceTcpDisable) {
359
+            self.localSDP.removeTcpCandidates = true;
360
+        }
361
+        if (self.webrtcIceUdpDisable) {
362
+            self.localSDP.removeUdpCandidates = true;
363
+        }
364
+        self.localSDP.toJingle(
365
+                accept,
366
+                self.initiator == self.me ? 'initiator' : 'responder',
367
+                ssrcs);
368
+        self.fixJingle(accept);
369
+        self.connection.sendIQ(accept,
370
+                success,
371
+                self.newJingleErrorHandler(accept, failure),
372
+                IQ_TIMEOUT);
373
+        // XXX Videobridge needs WebRTC's answer (ICE ufrag and pwd, DTLS
374
+        // fingerprint and setup) ASAP in order to start the connection
375
+        // establishment.
376
+        self.connection.flush();
367 377
     };
368 378
     sdp.sdp = this.localSDP.raw;
369 379
     this.peerconnection.setLocalDescription(sdp,
@@ -389,6 +399,55 @@ JingleSessionPC.prototype.createdAnswer = function (sdp, success, failure) {
389 399
     }
390 400
 };
391 401
 
402
+/**
403
+ * Modifies the values of the setup attributes (defined by
404
+ * {@link http://tools.ietf.org/html/rfc4145#section-4}) of a specific SDP
405
+ * answer in order to overcome a delay of 1 second in the connection
406
+ * establishment between Chrome and Videobridge.
407
+ *
408
+ * @param {SDP} offer - the SDP offer to which the specified SDP answer is
409
+ * being prepared to respond
410
+ * @param {SDP} answer - the SDP to modify
411
+ * @private
412
+ */
413
+JingleSessionPC.prototype._fixAnswerRFC4145Setup = function (offer, answer) {
414
+    // XXX Videobridge is the (SDP) offerer and WebRTC (e.g. Chrome) is the
415
+    // answerer (as orchestrated by Jicofo). In accord with
416
+    // http://tools.ietf.org/html/rfc5245#section-5.2 and because both peers
417
+    // are ICE FULL agents, Videobridge will take on the controlling role and
418
+    // WebRTC will take on the controlled role. In accord with
419
+    // https://tools.ietf.org/html/rfc5763#section-5, Videobridge will use the
420
+    // setup attribute value of setup:actpass and WebRTC will be allowed to
421
+    // choose either the setup attribute value of setup:active or
422
+    // setup:passive. Chrome will by default choose setup:active because it is
423
+    // RECOMMENDED by the respective RFC since setup:passive adds additional
424
+    // latency. The case of setup:active allows WebRTC to send a DTLS
425
+    // ClientHello as soon as an ICE connectivity check of its succeeds.
426
+    // Unfortunately, Videobridge will be unable to respond immediately because
427
+    // may not have WebRTC's answer or may have not completed the ICE
428
+    // connectivity establishment. Even more unfortunate is that in the
429
+    // described scenario Chrome's DTLS implementation will insist on
430
+    // retransmitting its ClientHello after a second (the time is in accord
431
+    // with the respective RFC) and will thus cause the whole connection
432
+    // establishment to exceed at least 1 second. To work around Chrome's
433
+    // idiosyncracy, don't allow it to send a ClientHello i.e. change its
434
+    // default choice of setup:active to setup:passive.
435
+    if (offer && answer
436
+            && offer.media && answer.media
437
+            && offer.media.length == answer.media.length) {
438
+        answer.media.forEach(function (a, i) {
439
+            if (SDPUtil.find_line(
440
+                    offer.media[i],
441
+                    'a=setup:actpass',
442
+                    offer.session)) {
443
+                answer.media[i]
444
+                    = a.replace(/a=setup:active/g, 'a=setup:passive');
445
+            }
446
+        });
447
+        answer.raw = answer.session + answer.media.join('');
448
+    }
449
+}
450
+
392 451
 JingleSessionPC.prototype.terminate = function (reason,  text,
393 452
                                                 success, failure) {
394 453
     var term = $iq({to: this.peerjid,

+ 28
- 30
modules/xmpp/SDP.js Переглянути файл

@@ -146,8 +146,8 @@ SDP.prototype.toJingle = function (elem, thecreator) {
146 146
     var self = this;
147 147
     var i, j, k, mline, ssrc, rtpmap, tmp, lines;
148 148
     // new bundle plan
149
-    if (SDPUtil.find_line(this.session, 'a=group:')) {
150
-        lines = SDPUtil.find_lines(this.session, 'a=group:');
149
+    lines = SDPUtil.find_lines(this.session, 'a=group:');
150
+    if (lines.length) {
151 151
         for (i = 0; i < lines.length; i++) {
152 152
             tmp = lines[i].split(' ');
153 153
             var semantics = tmp.shift().substr(8);
@@ -166,16 +166,18 @@ SDP.prototype.toJingle = function (elem, thecreator) {
166 166
         {
167 167
             continue;
168 168
         }
169
-        if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
170
-            ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first
169
+        var assrcline = SDPUtil.find_line(this.media[i], 'a=ssrc:');
170
+        if (assrcline) {
171
+            ssrc = assrcline.substring(7).split(' ')[0]; // take the first
171 172
         } else {
172 173
             ssrc = false;
173 174
         }
174 175
 
175 176
         elem.c('content', {creator: thecreator, name: mline.media});
176
-        if (SDPUtil.find_line(this.media[i], 'a=mid:')) {
177
+        var amidline = SDPUtil.find_line(this.media[i], 'a=mid:');
178
+        if (amidline) {
177 179
             // prefer identifier from a=mid if present
178
-            var mid = SDPUtil.parse_mid(SDPUtil.find_line(this.media[i], 'a=mid:'));
180
+            var mid = SDPUtil.parse_mid(amidline);
179 181
             elem.attrs({ name: mid });
180 182
         }
181 183
 
@@ -190,8 +192,9 @@ SDP.prototype.toJingle = function (elem, thecreator) {
190 192
                 rtpmap = SDPUtil.find_line(this.media[i], 'a=rtpmap:' + mline.fmt[j]);
191 193
                 elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
192 194
                 // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
193
-                if (SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j])) {
194
-                    tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j]));
195
+                var afmtpline = SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j]);
196
+                if (afmtpline) {
197
+                    tmp = SDPUtil.parse_fmtp(afmtpline);
195 198
                     for (k = 0; k < tmp.length; k++) {
196 199
                         elem.c('parameter', tmp[k]).up();
197 200
                     }
@@ -200,9 +203,9 @@ SDP.prototype.toJingle = function (elem, thecreator) {
200 203
 
201 204
                 elem.up();
202 205
             }
203
-            if (SDPUtil.find_line(this.media[i], 'a=crypto:', this.session)) {
206
+            var crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
207
+            if (crypto.length) {
204 208
                 elem.c('encryption', {required: 1});
205
-                var crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
206 209
                 crypto.forEach(function(line) {
207 210
                     elem.c('crypto', SDPUtil.parse_crypto(line)).up();
208 211
                 });
@@ -293,8 +296,8 @@ SDP.prototype.toJingle = function (elem, thecreator) {
293 296
             this.rtcpFbToJingle(i, elem, '*');
294 297
 
295 298
             // XEP-0294
296
-            if (SDPUtil.find_line(this.media[i], 'a=extmap:')) {
297
-                lines = SDPUtil.find_lines(this.media[i], 'a=extmap:');
299
+            lines = SDPUtil.find_lines(this.media[i], 'a=extmap:');
300
+            if (lines.length) {
298 301
                 for (j = 0; j < lines.length; j++) {
299 302
                     tmp = SDPUtil.parse_extmap(lines[j]);
300 303
                     elem.c('rtp-hdrext', { xmlns: 'urn:xmpp:jingle:apps:rtp:rtp-hdrext:0',
@@ -351,24 +354,19 @@ SDP.prototype.transportToJingle = function (mediaindex, elem) {
351 354
     elem.c('transport');
352 355
 
353 356
     // XEP-0343 DTLS/SCTP
354
-    if (SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:').length)
355
-    {
356
-        sctpmap = SDPUtil.find_line(
357
-            this.media[mediaindex], 'a=sctpmap:', self.session);
358
-        if (sctpmap)
359
-        {
360
-            sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
361
-            elem.c('sctpmap',
362
-                {
363
-                    xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
364
-                    number: sctpAttrs[0], /* SCTP port */
365
-                    protocol: sctpAttrs[1] /* protocol */
366
-                });
367
-            // Optional stream count attribute
368
-            if (sctpAttrs.length > 2)
369
-                elem.attrs({ streams: sctpAttrs[2]});
370
-            elem.up();
371
-        }
357
+    sctpmap
358
+        = SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:', self.session);
359
+    if (sctpmap) {
360
+        sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
361
+        elem.c('sctpmap', {
362
+                xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
363
+                number: sctpAttrs[0], /* SCTP port */
364
+                protocol: sctpAttrs[1] /* protocol */
365
+            });
366
+        // Optional stream count attribute
367
+        if (sctpAttrs.length > 2)
368
+            elem.attrs({ streams: sctpAttrs[2]});
369
+        elem.up();
372 370
     }
373 371
     // XEP-0320
374 372
     fingerprints = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);

+ 5
- 4
modules/xmpp/SDPUtil.js Переглянути файл

@@ -8,11 +8,12 @@ SDPUtil = {
8 8
     },
9 9
     iceparams: function (mediadesc, sessiondesc) {
10 10
         var data = null;
11
-        if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
12
-            SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
11
+        var ufrag, pwd;
12
+        if ((ufrag = SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc))
13
+                && (pwd = SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))) {
13 14
             data = {
14
-                ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
15
-                pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
15
+                ufrag: SDPUtil.parse_iceufrag(ufrag),
16
+                pwd: SDPUtil.parse_icepwd(pwd)
16 17
             };
17 18
         }
18 19
         return data;

Завантаження…
Відмінити
Зберегти