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