Przeglądaj źródła

Merge pull request #41 from jitsi/ice_restart

Ice restart
dev1
hristoterezov 9 lat temu
rodzic
commit
7759fbe9e8
2 zmienionych plików z 233 dodań i 54 usunięć
  1. 219
    54
      modules/xmpp/JingleSessionPC.js
  2. 14
    0
      modules/xmpp/strophe.jingle.js

+ 219
- 54
modules/xmpp/JingleSessionPC.js Wyświetl plik

@@ -284,19 +284,69 @@ JingleSessionPC.prototype.readSsrcInfo = function (contents) {
284 284
     });
285 285
 };
286 286
 
287
+/**
288
+ * Does accept incoming Jingle 'session-initiate' and should send
289
+ * 'session-accept' in result.
290
+ * @param jingleOffer jQuery selector pointing to the jingle element of
291
+ *        the offer IQ
292
+ * @param success callback called when we accept incoming session successfully
293
+ *        and receive RESULT packet to 'session-accept' sent.
294
+ * @param failure function(error) called if for any reason we fail to accept
295
+ *        the incoming offer. 'error' argument can be used to log some details
296
+ *        about the error.
297
+ */
287 298
 JingleSessionPC.prototype.acceptOffer = function(jingleOffer,
288 299
                                                  success, failure) {
289 300
     this.state = 'active';
290
-    this.setRemoteDescription(jingleOffer, 'offer',
301
+    this.setOfferCycle(jingleOffer,
302
+        function() {
303
+            // setOfferCycle succeeded, now we have self.localSDP up to date
304
+            // Let's send an answer !
305
+            // FIXME we may not care about RESULT packet for session-accept
306
+            // then we should either call 'success' here immediately or
307
+            // modify sendSessionAccept method to do that
308
+            this.sendSessionAccept(this.localSDP, success, failure);
309
+        }.bind(this),
310
+        failure);
311
+};
312
+
313
+/**
314
+ * This is a setRemoteDescription/setLocalDescription cycle which starts at
315
+ * converting Strophe Jingle IQ into remote offer SDP. Once converted
316
+ * setRemoteDescription, createAnswer and setLocalDescription calls follow.
317
+ * @param jingleOfferIq jQuery selector pointing to the jingle element of
318
+ *        the offer IQ
319
+ * @param success callback called when sRD/sLD cycle finishes successfully.
320
+ * @param failure callback called with an error object as an argument if we fail
321
+ *        at any point during setRD, createAnswer, setLD.
322
+ */
323
+JingleSessionPC.prototype.setOfferCycle = function (jingleOfferIq,
324
+                                                          success,
325
+                                                          failure) {
326
+    // Set Jingle offer as RD
327
+    this.setOffer(jingleOfferIq,
291 328
         function() {
292
-            this.sendAnswer(success, failure);
329
+            // Set offer OK, now let's try create an answer
330
+            this.createAnswer(function(answer) {
331
+                    // Create answer OK, set it as local SDP
332
+                    this.setLocalDescription(answer, success, failure);
333
+                }.bind(this),
334
+                failure);
293 335
         }.bind(this),
294 336
         failure);
295 337
 };
296 338
 
297
-JingleSessionPC.prototype.setRemoteDescription = function (elem, desctype,
298
-                                                           success, failure) {
299
-    //logger.log('setting remote description... ', desctype);
339
+/**
340
+ * Sets remote offer on PeerConnection by converting given Jingle offer IQ into
341
+ * SDP and setting it as remote description.
342
+ * @param jingleOfferIq  jQuery selector pointing to the jingle element of
343
+ *        the offer IQ
344
+ * @param success callback called when setRemoteDescription on PeerConnection
345
+ *        succeeds
346
+ * @param failure callback called with an error argument when
347
+ *        setRemoteDescription fails.
348
+ */
349
+JingleSessionPC.prototype.setOffer = function (jingleOfferIq, success, failure) {
300 350
     this.remoteSDP = new SDP('');
301 351
     if (this.webrtcIceTcpDisable) {
302 352
         this.remoteSDP.removeTcpCandidates = true;
@@ -305,9 +355,10 @@ JingleSessionPC.prototype.setRemoteDescription = function (elem, desctype,
305 355
         this.remoteSDP.removeUdpCandidates = true;
306 356
     }
307 357
 
308
-    this.remoteSDP.fromJingle(elem);
309
-    this.readSsrcInfo($(elem).find(">content"));
310
-    var remotedesc = new RTCSessionDescription({type: desctype, sdp: this.remoteSDP.raw});
358
+    this.remoteSDP.fromJingle(jingleOfferIq);
359
+    this.readSsrcInfo($(jingleOfferIq).find(">content"));
360
+    var remotedesc
361
+        = new RTCSessionDescription({type: 'offer', sdp: this.remoteSDP.raw});
311 362
 
312 363
     this.peerconnection.setRemoteDescription(remotedesc,
313 364
         function () {
@@ -325,66 +376,46 @@ JingleSessionPC.prototype.setRemoteDescription = function (elem, desctype,
325 376
     );
326 377
 };
327 378
 
328
-JingleSessionPC.prototype.sendAnswer = function (success, failure) {
379
+/**
380
+ * This is a wrapper to PeerConnection.createAnswer in order to generate failure
381
+ * event when error occurs. It also includes "media_constraints" if any are set
382
+ * on this JingleSessionPC instance.
383
+ * @param success callback called when PC.createAnswer succeeds, SDP will be
384
+ *        the first argument
385
+ * @param failure callback called with error argument when setAnswer fails
386
+ */
387
+JingleSessionPC.prototype.createAnswer = function (success, failure) {
329 388
     //logger.log('createAnswer');
389
+    var self = this;
330 390
     this.peerconnection.createAnswer(
331
-        function (sdp) {
332
-            this.createdAnswer(sdp, success, failure);
333
-        }.bind(this),
391
+        function (answer) {
392
+            var modifiedAnswer = new SDP(answer.sdp);
393
+            JingleSessionPC._fixAnswerRFC4145Setup(
394
+                /* offer */ self.remoteSDP,
395
+                /* answer */ modifiedAnswer);
396
+            answer.sdp = modifiedAnswer.raw;
397
+            success(answer);
398
+        },
334 399
         function (error) {
335 400
             logger.error("createAnswer failed", error);
336 401
             if (failure)
337 402
                 failure(error);
338
-            this.room.eventEmitter.emit(
403
+            self.room.eventEmitter.emit(
339 404
                     XMPPEvents.CONFERENCE_SETUP_FAILED, error);
340
-        }.bind(this),
405
+        },
341 406
         this.media_constraints
342 407
     );
343 408
 };
344 409
 
345
-JingleSessionPC.prototype.createdAnswer = function (sdp, success, failure) {
346
-    //logger.log('createAnswer callback');
347
-
410
+JingleSessionPC.prototype.setLocalDescription = function (sdp, success,
411
+                                                               failure) {
348 412
     var self = this;
349 413
     this.localSDP = new SDP(sdp.sdp);
350
-
351
-    this._fixAnswerRFC4145Setup(
352
-            /* offer */ this.remoteSDP,
353
-            /* answer */ this.localSDP);
354
-
355
-    var sendJingle = function (ssrcs) {
356
-        var accept
357
-            = $iq({ to: self.peerjid, type: 'set' })
358
-                .c('jingle', { xmlns: 'urn:xmpp:jingle:1',
359
-                               action: 'session-accept',
360
-                               initiator: self.initiator,
361
-                               responder: self.responder,
362
-                               sid: self.sid });
363
-        if (self.webrtcIceTcpDisable) {
364
-            self.localSDP.removeTcpCandidates = true;
365
-        }
366
-        if (self.webrtcIceUdpDisable) {
367
-            self.localSDP.removeUdpCandidates = true;
368
-        }
369
-        self.localSDP.toJingle(
370
-                accept,
371
-                self.initiator == self.me ? 'initiator' : 'responder',
372
-                ssrcs);
373
-        self.fixJingle(accept);
374
-        self.connection.sendIQ(accept,
375
-                success,
376
-                self.newJingleErrorHandler(accept, failure),
377
-                IQ_TIMEOUT);
378
-        // XXX Videobridge needs WebRTC's answer (ICE ufrag and pwd, DTLS
379
-        // fingerprint and setup) ASAP in order to start the connection
380
-        // establishment.
381
-        self.connection.flush();
382
-    };
383 414
     sdp.sdp = this.localSDP.raw;
384 415
     this.peerconnection.setLocalDescription(sdp,
385 416
         function () {
386
-            //logger.log('setLocalDescription success');
387
-            sendJingle(success, failure);
417
+            if (success)
418
+                success();
388 419
         },
389 420
         function (error) {
390 421
             logger.error('setLocalDescription failed', error);
@@ -393,6 +424,8 @@ JingleSessionPC.prototype.createdAnswer = function (sdp, success, failure) {
393 424
             self.room.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
394 425
         }
395 426
     );
427
+    // Some checks for STUN and TURN candiates present in local SDP
428
+    // Eventually could be removed as we don't really care
396 429
     var cands = SDPUtil.find_lines(this.localSDP.raw, 'a=candidate:');
397 430
     for (var j = 0; j < cands.length; j++) {
398 431
         var cand = SDPUtil.parse_icecandidate(cands[j]);
@@ -415,7 +448,7 @@ JingleSessionPC.prototype.createdAnswer = function (sdp, success, failure) {
415 448
  * @param {SDP} answer - the SDP to modify
416 449
  * @private
417 450
  */
418
-JingleSessionPC.prototype._fixAnswerRFC4145Setup = function (offer, answer) {
451
+JingleSessionPC._fixAnswerRFC4145Setup = function (offer, answer) {
419 452
     if (!RTCBrowserType.isChrome()) {
420 453
         // It looks like Firefox doesn't agree with the fix (at least in its
421 454
         // current implementation) because it effectively remains active even
@@ -461,7 +494,135 @@ JingleSessionPC.prototype._fixAnswerRFC4145Setup = function (offer, answer) {
461 494
         });
462 495
         answer.raw = answer.session + answer.media.join('');
463 496
     }
464
-}
497
+};
498
+
499
+/**
500
+ * Although it states "replace transport" it does accept full Jingle offer
501
+ * which should contain new ICE transport details.
502
+ * @param jingleOfferElem an element Jingle IQ that contains new offer and
503
+ *        transport info.
504
+ * @param success callback called when we succeed to accept new offer.
505
+ * @param failure function(error) called when we fail to accept new offer.
506
+ */
507
+JingleSessionPC.prototype.replaceTransport = function (jingleOfferElem,
508
+                                                       success,
509
+                                                       failure) {
510
+    // Set offer as RD
511
+    this.setOfferCycle(jingleOfferElem,
512
+        function () {
513
+            // Set local description OK, now localSDP up to date
514
+            this.sendTransportAccept(this.localSDP, success, failure);
515
+        }.bind(this),
516
+        failure);
517
+};
518
+
519
+/**
520
+ * Sends Jingle 'session-accept' message.
521
+ * @param localSDP the 'SDP' object with local session description
522
+ * @param success callback called when we recive 'RESULT' packet for
523
+ *        'session-accept'
524
+ * @param failure function(error) called when we receive an error response or
525
+ *        when the request has timed out.
526
+ */
527
+JingleSessionPC.prototype.sendSessionAccept = function (localSDP,
528
+                                                        success, failure) {
529
+    var accept = $iq({to: this.peerjid,
530
+        type: 'set'})
531
+        .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
532
+            action: 'session-accept',
533
+            initiator: this.initiator,
534
+            responder: this.responder,
535
+            sid: this.sid });
536
+    if (this.webrtcIceTcpDisable) {
537
+        localSDP.removeTcpCandidates = true;
538
+    }
539
+    if (this.webrtcIceUdpDisable) {
540
+        localSDP.removeUdpCandidates = true;
541
+    }
542
+    localSDP.toJingle(
543
+        accept,
544
+        this.initiator == this.me ? 'initiator' : 'responder',
545
+        null);
546
+    this.fixJingle(accept);
547
+
548
+    // Calling tree() to print something useful
549
+    accept = accept.tree();
550
+    logger.info("Sending session-accept", accept);
551
+
552
+    this.connection.sendIQ(accept,
553
+        success,
554
+        this.newJingleErrorHandler(accept, failure),
555
+        IQ_TIMEOUT);
556
+    // XXX Videobridge needs WebRTC's answer (ICE ufrag and pwd, DTLS
557
+    // fingerprint and setup) ASAP in order to start the connection
558
+    // establishment.
559
+    this.connection.flush();
560
+};
561
+
562
+/**
563
+ * Sends Jingle 'transport-accept' message which is a response to
564
+ * 'transport-replace'.
565
+ * @param localSDP the 'SDP' object with local session description
566
+ * @param success callback called when we receive 'RESULT' packet for
567
+ *        'transport-replace'
568
+ * @param failure function(error) called when we receive an error response or
569
+ *        when the request has timed out.
570
+ */
571
+JingleSessionPC.prototype.sendTransportAccept = function(localSDP, success,
572
+                                                         failure) {
573
+    var self = this;
574
+    var tAccept = $iq({to: this.peerjid, type: 'set'})
575
+        .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
576
+            action: 'transport-accept',
577
+            initiator: this.initiator,
578
+            sid: this.sid});
579
+
580
+    localSDP.media.forEach(function(medialines, idx){
581
+        var mline = SDPUtil.parse_mline(medialines.split('\r\n')[0]);
582
+        tAccept.c('content',
583
+            { creator: self.initiator == self.me ? 'initiator' : 'responder',
584
+              name: mline.media
585
+            }
586
+        );
587
+        localSDP.transportToJingle(idx, tAccept);
588
+        tAccept.up();
589
+    });
590
+
591
+    // Calling tree() to print something useful to the logger
592
+    tAccept = tAccept.tree();
593
+    console.info("Sending transport-accept: ", tAccept);
594
+
595
+    self.connection.sendIQ(tAccept,
596
+        success,
597
+        self.newJingleErrorHandler(tAccept, failure),
598
+        IQ_TIMEOUT);
599
+};
600
+
601
+/**
602
+ * Sends Jingle 'transport-reject' message which is a response to
603
+ * 'transport-replace'.
604
+ * @param success callback called when we receive 'RESULT' packet for
605
+ *        'transport-replace'
606
+ * @param failure function(error) called when we receive an error response or
607
+ *        when the request has timed out.
608
+ */
609
+JingleSessionPC.prototype.sendTransportReject = function(success, failure) {
610
+    // Send 'transport-reject', so that the focus will
611
+    // know that we've failed
612
+    var tReject = $iq({to: this.peerjid, type: 'set'})
613
+        .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
614
+            action: 'transport-reject',
615
+            initiator: this.initiator,
616
+            sid: this.sid});
617
+
618
+    tReject = tReject.tree();
619
+    logger.info("Sending 'transport-reject", tReject);
620
+
621
+    this.connection.sendIQ(tReject,
622
+        success,
623
+        this.newJingleErrorHandler(tReject, failure),
624
+        IQ_TIMEOUT);
625
+};
465 626
 
466 627
 JingleSessionPC.prototype.terminate = function (reason,  text,
467 628
                                                 success, failure) {
@@ -478,6 +639,10 @@ JingleSessionPC.prototype.terminate = function (reason,  text,
478 639
         term.up().c('text').t(text);
479 640
     }
480 641
 
642
+    // Calling tree() to print something useful
643
+    term = term.tree();
644
+    logger.info("Sending session-terminate", term);
645
+
481 646
     this.connection.sendIQ(
482 647
         term, success, this.newJingleErrorHandler(term, failure), IQ_TIMEOUT);
483 648
 

+ 14
- 0
modules/xmpp/strophe.jingle.js Wyświetl plik

@@ -139,6 +139,20 @@ module.exports = function(XMPP, eventEmitter) {
139 139
                     }
140 140
                     this.terminate(sess.sid, reasonCondition, reasonText);
141 141
                     break;
142
+                case 'transport-replace':
143
+                    logger.info("(TIME) Start transport replace",
144
+                                window.performance.now());
145
+                    sess.replaceTransport($(iq).find('>jingle'),
146
+                        function () {
147
+                            logger.info(
148
+                                "(TIME) Transport replace success!",
149
+                                window.performance.now());
150
+                        },
151
+                        function(error) {
152
+                            logger.error('Transport replace failed', error);
153
+                            sess.sendTransportReject();
154
+                        });
155
+                    break;
142 156
                 case 'addsource': // FIXME: proprietary, un-jingleish
143 157
                 case 'source-add': // FIXME: proprietary
144 158
                     sess.addSource($(iq).find('>jingle>content'));

Ładowanie…
Anuluj
Zapisz