|
|
@@ -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
|
|