|
@@ -26,16 +26,12 @@ function JingleSessionPC(me, sid, peerjid, connection,
|
26
|
26
|
JingleSession.call(this, me, sid, peerjid, connection,
|
27
|
27
|
media_constraints, ice_config, service, eventEmitter);
|
28
|
28
|
this.localSDP = null;
|
29
|
|
- this.remoteSDP = null;
|
30
|
29
|
|
31
|
|
- this.hadstuncandidate = false;
|
32
|
|
- this.hadturncandidate = false;
|
33
|
30
|
this.lasticecandidate = false;
|
34
|
31
|
this.closed = false;
|
35
|
32
|
|
36
|
33
|
this.addssrc = [];
|
37
|
34
|
this.removessrc = [];
|
38
|
|
- this.pendingop = null;
|
39
|
35
|
this.modifyingLocalStreams = false;
|
40
|
36
|
this.modifiedSSRCs = {};
|
41
|
37
|
|
|
@@ -49,34 +45,19 @@ function JingleSessionPC(me, sid, peerjid, connection,
|
49
|
45
|
*/
|
50
|
46
|
this.ssrcOwners = {};
|
51
|
47
|
|
|
48
|
+ this.jingleOfferIq = null;
|
52
|
49
|
this.webrtcIceUdpDisable = !!this.service.options.webrtcIceUdpDisable;
|
53
|
50
|
this.webrtcIceTcpDisable = !!this.service.options.webrtcIceTcpDisable;
|
54
|
51
|
|
55
|
52
|
this.modifySourcesQueue = async.queue(this._modifySources.bind(this), 1);
|
56
|
|
- // We start with the queue paused. We resume it when the signaling state is
|
57
|
|
- // stable and the ice connection state is connected.
|
58
|
|
- this.modifySourcesQueue.pause();
|
59
|
53
|
}
|
60
|
54
|
|
61
|
55
|
JingleSessionPC.prototype = Object.create(JingleSession.prototype);
|
62
|
56
|
JingleSessionPC.prototype.constructor = JingleSessionPC;
|
63
|
57
|
|
64
|
58
|
|
65
|
|
-JingleSessionPC.prototype.updateModifySourcesQueue = function() {
|
66
|
|
- var signalingState = this.peerconnection.signalingState;
|
67
|
|
- var iceConnectionState = this.peerconnection.iceConnectionState;
|
68
|
|
- if (signalingState === 'stable' && iceConnectionState === 'connected') {
|
69
|
|
- this.modifySourcesQueue.resume();
|
70
|
|
- } else {
|
71
|
|
- this.modifySourcesQueue.pause();
|
72
|
|
- }
|
73
|
|
-};
|
74
|
|
-
|
75
|
59
|
JingleSessionPC.prototype.doInitialize = function () {
|
76
|
60
|
var self = this;
|
77
|
|
-
|
78
|
|
- this.hadstuncandidate = false;
|
79
|
|
- this.hadturncandidate = false;
|
80
|
61
|
this.lasticecandidate = false;
|
81
|
62
|
// True if reconnect is in progress
|
82
|
63
|
this.isreconnect = false;
|
|
@@ -125,7 +106,6 @@ JingleSessionPC.prototype.doInitialize = function () {
|
125
|
106
|
if (self.peerconnection.signalingState === 'stable') {
|
126
|
107
|
self.wasstable = true;
|
127
|
108
|
}
|
128
|
|
- self.updateModifySourcesQueue();
|
129
|
109
|
};
|
130
|
110
|
/**
|
131
|
111
|
* The oniceconnectionstatechange event handler contains the code to execute when the iceconnectionstatechange event,
|
|
@@ -141,7 +121,6 @@ JingleSessionPC.prototype.doInitialize = function () {
|
141
|
121
|
self.peerconnection.iceConnectionState] = now;
|
142
|
122
|
logger.log("(TIME) ICE " + self.peerconnection.iceConnectionState +
|
143
|
123
|
":\t", now);
|
144
|
|
- self.updateModifySourcesQueue();
|
145
|
124
|
switch (self.peerconnection.iceConnectionState) {
|
146
|
125
|
case 'connected':
|
147
|
126
|
|
|
@@ -183,12 +162,6 @@ JingleSessionPC.prototype.sendIceCandidate = function (candidate) {
|
183
|
162
|
}
|
184
|
163
|
ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
|
185
|
164
|
|
186
|
|
- if (jcand.type === 'srflx') {
|
187
|
|
- this.hadstuncandidate = true;
|
188
|
|
- } else if (jcand.type === 'relay') {
|
189
|
|
- this.hadturncandidate = true;
|
190
|
|
- }
|
191
|
|
-
|
192
|
165
|
if (this.usedrip) {
|
193
|
166
|
if (this.drip_container.length === 0) {
|
194
|
167
|
// start 20ms callout
|
|
@@ -206,8 +179,6 @@ JingleSessionPC.prototype.sendIceCandidate = function (candidate) {
|
206
|
179
|
logger.log('sendIceCandidate: last candidate.');
|
207
|
180
|
// FIXME: remember to re-think in ICE-restart
|
208
|
181
|
this.lasticecandidate = true;
|
209
|
|
- logger.log('Have we encountered any srflx candidates? ' + this.hadstuncandidate);
|
210
|
|
- logger.log('Have we encountered any relay candidates? ' + this.hadturncandidate);
|
211
|
182
|
}
|
212
|
183
|
};
|
213
|
184
|
|
|
@@ -313,119 +284,14 @@ JingleSessionPC.prototype.acceptOffer = function(jingleOffer,
|
313
|
284
|
JingleSessionPC.prototype.setOfferCycle = function (jingleOfferIq,
|
314
|
285
|
success,
|
315
|
286
|
failure) {
|
316
|
|
- // Set Jingle offer as RD
|
317
|
|
- this.setOffer(jingleOfferIq,
|
318
|
|
- function() {
|
319
|
|
- // Set offer OK, now let's try create an answer
|
320
|
|
- this.createAnswer(function(answer) {
|
321
|
|
- // Create answer OK, set it as local SDP
|
322
|
|
- this.setLocalDescription(answer, success, failure);
|
323
|
|
- }.bind(this),
|
324
|
|
- failure);
|
325
|
|
- }.bind(this),
|
326
|
|
- failure);
|
327
|
|
-};
|
328
|
|
-
|
329
|
|
-/**
|
330
|
|
- * Sets remote offer on PeerConnection by converting given Jingle offer IQ into
|
331
|
|
- * SDP and setting it as remote description.
|
332
|
|
- * @param jingleOfferIq jQuery selector pointing to the jingle element of
|
333
|
|
- * the offer IQ
|
334
|
|
- * @param success callback called when setRemoteDescription on PeerConnection
|
335
|
|
- * succeeds
|
336
|
|
- * @param failure callback called with an error argument when
|
337
|
|
- * setRemoteDescription fails.
|
338
|
|
- */
|
339
|
|
-JingleSessionPC.prototype.setOffer = function (jingleOfferIq, success, failure) {
|
340
|
|
- this.remoteSDP = new SDP('');
|
341
|
|
- if (this.webrtcIceTcpDisable) {
|
342
|
|
- this.remoteSDP.removeTcpCandidates = true;
|
343
|
|
- }
|
344
|
|
- if (this.webrtcIceUdpDisable) {
|
345
|
|
- this.remoteSDP.removeUdpCandidates = true;
|
346
|
|
- }
|
347
|
|
-
|
348
|
|
- this.remoteSDP.fromJingle(jingleOfferIq);
|
349
|
|
- this.readSsrcInfo($(jingleOfferIq).find(">content"));
|
350
|
|
- var remotedesc
|
351
|
|
- = new RTCSessionDescription({type: 'offer', sdp: this.remoteSDP.raw});
|
352
|
|
-
|
353
|
|
- this.peerconnection.setRemoteDescription(remotedesc,
|
354
|
|
- function () {
|
355
|
|
- //logger.log('setRemoteDescription success');
|
356
|
|
- if (success) {
|
357
|
|
- success();
|
358
|
|
- }
|
359
|
|
- },
|
360
|
|
- function (e) {
|
361
|
|
- logger.error('setRemoteDescription error', e);
|
362
|
|
- if (failure)
|
363
|
|
- failure(e);
|
364
|
|
- JingleSessionPC.onJingleFatalError(this, e);
|
365
|
|
- }.bind(this)
|
366
|
|
- );
|
367
|
|
-};
|
368
|
|
-
|
369
|
|
-/**
|
370
|
|
- * This is a wrapper to PeerConnection.createAnswer in order to generate failure
|
371
|
|
- * event when error occurs. It also includes "media_constraints" if any are set
|
372
|
|
- * on this JingleSessionPC instance.
|
373
|
|
- * @param success callback called when PC.createAnswer succeeds, SDP will be
|
374
|
|
- * the first argument
|
375
|
|
- * @param failure callback called with error argument when setAnswer fails
|
376
|
|
- */
|
377
|
|
-JingleSessionPC.prototype.createAnswer = function (success, failure) {
|
378
|
|
- //logger.log('createAnswer');
|
379
|
|
- var self = this;
|
380
|
|
- this.peerconnection.createAnswer(
|
381
|
|
- function (answer) {
|
382
|
|
- var modifiedAnswer = new SDP(answer.sdp);
|
383
|
|
- JingleSessionPC._fixAnswerRFC4145Setup(
|
384
|
|
- /* offer */ self.remoteSDP,
|
385
|
|
- /* answer */ modifiedAnswer);
|
386
|
|
- answer.sdp = modifiedAnswer.raw;
|
387
|
|
- success(answer);
|
388
|
|
- },
|
389
|
|
- function (error) {
|
390
|
|
- logger.error("createAnswer failed", error);
|
391
|
|
- if (failure)
|
392
|
|
- failure(error);
|
393
|
|
- self.room.eventEmitter.emit(
|
394
|
|
- XMPPEvents.CONFERENCE_SETUP_FAILED, error);
|
395
|
|
- },
|
396
|
|
- this.media_constraints
|
397
|
|
- );
|
398
|
|
-};
|
399
|
|
-
|
400
|
|
-JingleSessionPC.prototype.setLocalDescription = function (sdp, success,
|
401
|
|
- failure) {
|
402
|
|
- var self = this;
|
403
|
|
- this.localSDP = new SDP(sdp.sdp);
|
404
|
|
- sdp.sdp = this.localSDP.raw;
|
405
|
|
- this.peerconnection.setLocalDescription(sdp,
|
406
|
|
- function () {
|
407
|
|
- if (success)
|
408
|
|
- success();
|
409
|
|
- },
|
410
|
|
- function (error) {
|
411
|
|
- logger.error('setLocalDescription failed', error);
|
412
|
|
- if (failure)
|
413
|
|
- failure(error);
|
414
|
|
- self.room.eventEmitter.emit(
|
415
|
|
- XMPPEvents.CONFERENCE_SETUP_FAILED, error);
|
416
|
|
- }
|
417
|
|
- );
|
418
|
|
- // Some checks for STUN and TURN candiates present in local SDP
|
419
|
|
- // Eventually could be removed as we don't really care
|
420
|
|
- var cands = SDPUtil.find_lines(this.localSDP.raw, 'a=candidate:');
|
421
|
|
- for (var j = 0; j < cands.length; j++) {
|
422
|
|
- var cand = SDPUtil.parse_icecandidate(cands[j]);
|
423
|
|
- if (cand.type == 'srflx') {
|
424
|
|
- this.hadstuncandidate = true;
|
425
|
|
- } else if (cand.type == 'relay') {
|
426
|
|
- this.hadturncandidate = true;
|
427
|
|
- }
|
428
|
|
- }
|
|
287
|
+ this.jingleOfferIq = jingleOfferIq;
|
|
288
|
+ this.modifySourcesQueue.push(success, function (error) {
|
|
289
|
+ if(!error)
|
|
290
|
+ return;
|
|
291
|
+ if (failure)
|
|
292
|
+ failure(error);
|
|
293
|
+ JingleSessionPC.onJingleFatalError(this, error);
|
|
294
|
+ }.bind(this));
|
429
|
295
|
};
|
430
|
296
|
|
431
|
297
|
/**
|
|
@@ -675,7 +541,6 @@ JingleSessionPC.prototype.onTerminated = function (reasonCondition,
|
675
|
541
|
* @param elem An array of Jingle "content" elements.
|
676
|
542
|
*/
|
677
|
543
|
JingleSessionPC.prototype.addSource = function (elem) {
|
678
|
|
-
|
679
|
544
|
var self = this;
|
680
|
545
|
// FIXME: dirty waiting
|
681
|
546
|
if (!this.peerconnection.localDescription)
|
|
@@ -761,7 +626,6 @@ JingleSessionPC.prototype.addSource = function (elem) {
|
761
|
626
|
* @param elem An array of Jingle "content" elements.
|
762
|
627
|
*/
|
763
|
628
|
JingleSessionPC.prototype.removeSource = function (elem) {
|
764
|
|
-
|
765
|
629
|
var self = this;
|
766
|
630
|
// FIXME: dirty waiting
|
767
|
631
|
if (!this.peerconnection.localDescription) {
|
|
@@ -839,11 +703,11 @@ JingleSessionPC.prototype.removeSource = function (elem) {
|
839
|
703
|
};
|
840
|
704
|
|
841
|
705
|
JingleSessionPC.prototype._modifySources = function (successCallback, queueCallback) {
|
842
|
|
- var self = this;
|
|
706
|
+ var self = this, sdp = null, media_constraints;
|
843
|
707
|
|
844
|
708
|
if (this.peerconnection.signalingState == 'closed') return;
|
845
|
|
- if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null
|
846
|
|
- || this.modifyingLocalStreams)){
|
|
709
|
+ if (!(this.addssrc.length || this.removessrc.length
|
|
710
|
+ || this.modifyingLocalStreams || this.jingleOfferIq !== null)){
|
847
|
711
|
// There is nothing to do since scheduled job might have been
|
848
|
712
|
// executed by another succeeding call
|
849
|
713
|
if(successCallback){
|
|
@@ -853,10 +717,26 @@ JingleSessionPC.prototype._modifySources = function (successCallback, queueCallb
|
853
|
717
|
return;
|
854
|
718
|
}
|
855
|
719
|
|
856
|
|
- // Reset switch streams flags
|
857
|
|
- this.modifyingLocalStreams = false;
|
|
720
|
+ if(this.jingleOfferIq) {
|
|
721
|
+ fromSessionInitiate = true;
|
|
722
|
+ sdp = new SDP('');
|
|
723
|
+ if (this.webrtcIceTcpDisable) {
|
|
724
|
+ sdp.removeTcpCandidates = true;
|
|
725
|
+ }
|
|
726
|
+ if (this.webrtcIceUdpDisable) {
|
|
727
|
+ sdp.removeUdpCandidates = true;
|
|
728
|
+ }
|
858
|
729
|
|
859
|
|
- var sdp = new SDP(this.peerconnection.remoteDescription.sdp);
|
|
730
|
+ sdp.fromJingle(this.jingleOfferIq);
|
|
731
|
+ this.readSsrcInfo($(this.jingleOfferIq).find(">content"));
|
|
732
|
+ this.jingleOfferIq = null;
|
|
733
|
+ media_constraints = this.media_constraints;
|
|
734
|
+ } else {
|
|
735
|
+ // Reset switch streams flags
|
|
736
|
+ this.modifyingLocalStreams = false;
|
|
737
|
+
|
|
738
|
+ sdp = new SDP(this.peerconnection.remoteDescription.sdp);
|
|
739
|
+ }
|
860
|
740
|
|
861
|
741
|
// add sources
|
862
|
742
|
this.addssrc.forEach(function(lines, idx) {
|
|
@@ -903,32 +783,19 @@ JingleSessionPC.prototype._modifySources = function (successCallback, queueCallb
|
903
|
783
|
}
|
904
|
784
|
|
905
|
785
|
self.peerconnection.createAnswer(
|
906
|
|
- function(modifiedAnswer) {
|
907
|
|
- // change video direction, see https://github.com/jitsi/jitmeet/issues/41
|
908
|
|
- if (self.pendingop !== null) {
|
909
|
|
- var sdp = new SDP(modifiedAnswer.sdp);
|
910
|
|
- if (sdp.media.length > 1) {
|
911
|
|
- switch(self.pendingop) {
|
912
|
|
- case 'mute':
|
913
|
|
- sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
|
914
|
|
- break;
|
915
|
|
- case 'unmute':
|
916
|
|
- sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
|
917
|
|
- break;
|
918
|
|
- }
|
919
|
|
- sdp.raw = sdp.session + sdp.media.join('');
|
920
|
|
- modifiedAnswer.sdp = sdp.raw;
|
921
|
|
- }
|
922
|
|
- self.pendingop = null;
|
923
|
|
- }
|
924
|
|
-
|
|
786
|
+ function(answer) {
|
925
|
787
|
// FIXME: pushing down an answer while ice connection state
|
926
|
788
|
// is still checking is bad...
|
927
|
789
|
//logger.log(self.peerconnection.iceConnectionState);
|
928
|
790
|
|
929
|
|
- // trying to work around another chrome bug
|
930
|
|
- //modifiedAnswer.sdp = modifiedAnswer.sdp.replace(/a=setup:active/g, 'a=setup:actpass');
|
931
|
|
- self.peerconnection.setLocalDescription(modifiedAnswer,
|
|
791
|
+ var modifiedAnswer = new SDP(answer.sdp);
|
|
792
|
+ JingleSessionPC._fixAnswerRFC4145Setup(
|
|
793
|
+ /* offer */ sdp,
|
|
794
|
+ /* answer */ modifiedAnswer);
|
|
795
|
+ answer.sdp = modifiedAnswer.raw;
|
|
796
|
+ self.localSDP = new SDP(answer.sdp);
|
|
797
|
+ answer.sdp = self.localSDP.raw;
|
|
798
|
+ self.peerconnection.setLocalDescription(answer,
|
932
|
799
|
function() {
|
933
|
800
|
successCallback && successCallback();
|
934
|
801
|
queueCallback();
|
|
@@ -948,15 +815,15 @@ JingleSessionPC.prototype._modifySources = function (successCallback, queueCallb
|
948
|
815
|
/**
|
949
|
816
|
* Adds stream.
|
950
|
817
|
* @param stream new stream that will be added.
|
951
|
|
- * @param success_callback callback executed after successful stream addition.
|
|
818
|
+ * @param callback callback executed after successful stream addition.
|
|
819
|
+ * @param errorCallback callback executed if stream addition fail.
|
952
|
820
|
* @param ssrcInfo object with information about the SSRCs associated with the
|
953
|
821
|
* stream.
|
954
|
822
|
* @param dontModifySources {boolean} if true _modifySources won't be called.
|
955
|
823
|
* Used for streams added before the call start.
|
956
|
|
- * @throws error if modifySourcesQueue is paused.
|
957
|
824
|
*/
|
958
|
|
-JingleSessionPC.prototype.addStream = function (stream, callback, ssrcInfo,
|
959
|
|
- dontModifySources) {
|
|
825
|
+JingleSessionPC.prototype.addStream = function (stream, callback, errorCallback,
|
|
826
|
+ ssrcInfo, dontModifySources) {
|
960
|
827
|
// Remember SDP to figure out added/removed SSRCs
|
961
|
828
|
var oldSdp = null;
|
962
|
829
|
if(this.peerconnection && this.peerconnection.localDescription) {
|
|
@@ -979,14 +846,6 @@ JingleSessionPC.prototype.addStream = function (stream, callback, ssrcInfo,
|
979
|
846
|
return;
|
980
|
847
|
}
|
981
|
848
|
|
982
|
|
- if(this.modifySourcesQueue.paused) {
|
983
|
|
- // if this.modifySourcesQueue.paused, modifySources won't be called and
|
984
|
|
- // the SDPs won't be updated. Basically removeStream will fail. That's
|
985
|
|
- // why we are throwing the error to inform the callers of the method.
|
986
|
|
- throw new Error("modifySourcesQueue paused");
|
987
|
|
- return;
|
988
|
|
- }
|
989
|
|
-
|
990
|
849
|
if(stream || ssrcInfo)
|
991
|
850
|
this.peerconnection.addStream(stream, ssrcInfo);
|
992
|
851
|
|
|
@@ -1000,10 +859,15 @@ JingleSessionPC.prototype.addStream = function (stream, callback, ssrcInfo,
|
1000
|
859
|
self.modifiedSSRCs[ssrcInfo.type] || [];
|
1001
|
860
|
self.modifiedSSRCs[ssrcInfo.type].push(ssrcInfo);
|
1002
|
861
|
}
|
1003
|
|
- callback();
|
1004
|
862
|
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
1005
|
863
|
logger.log("SDPs", oldSdp, newSdp);
|
1006
|
864
|
self.notifyMySSRCUpdate(oldSdp, newSdp);
|
|
865
|
+ }, function (error) {
|
|
866
|
+ if(!error) {
|
|
867
|
+ callback();
|
|
868
|
+ } else {
|
|
869
|
+ errorCallback(error);
|
|
870
|
+ }
|
1007
|
871
|
});
|
1008
|
872
|
}
|
1009
|
873
|
|
|
@@ -1019,12 +883,13 @@ JingleSessionPC.prototype.generateNewStreamSSRCInfo = function () {
|
1019
|
883
|
/**
|
1020
|
884
|
* Remove streams.
|
1021
|
885
|
* @param stream stream that will be removed.
|
1022
|
|
- * @param success_callback callback executed after successful stream addition.
|
|
886
|
+ * @param callback callback executed after successful stream addition.
|
|
887
|
+ * @param errorCallback callback executed if stream addition fail.
|
1023
|
888
|
* @param ssrcInfo object with information about the SSRCs associated with the
|
1024
|
889
|
* stream.
|
1025
|
|
- * @throws error if modifySourcesQueue is paused.
|
1026
|
890
|
*/
|
1027
|
|
-JingleSessionPC.prototype.removeStream = function (stream, callback, ssrcInfo) {
|
|
891
|
+JingleSessionPC.prototype.removeStream = function (stream, callback, errorCallback,
|
|
892
|
+ ssrcInfo) {
|
1028
|
893
|
// Conference is not active
|
1029
|
894
|
if(!this.peerconnection) {
|
1030
|
895
|
callback();
|
|
@@ -1062,16 +927,9 @@ JingleSessionPC.prototype.removeStream = function (stream, callback, ssrcInfo) {
|
1062
|
927
|
}
|
1063
|
928
|
|
1064
|
929
|
if(!track) {
|
1065
|
|
- logger.log("Cannot remove tracks: no tracks.");
|
1066
|
|
- callback();
|
1067
|
|
- return;
|
1068
|
|
- }
|
1069
|
|
-
|
1070
|
|
- if(this.modifySourcesQueue.paused) {
|
1071
|
|
- // if this.modifySourcesQueue.paused, modifySources won't be called and
|
1072
|
|
- // the SDPs won't be updated. Basically removeStream will fail. That's
|
1073
|
|
- // why we are throwing the error to inform the callers of the method.
|
1074
|
|
- throw new Error("modifySourcesQueue paused");
|
|
930
|
+ var msg = "Cannot remove tracks: no tracks.";
|
|
931
|
+ logger.log(msg);
|
|
932
|
+ errorCallback(new Error(msg));
|
1075
|
933
|
return;
|
1076
|
934
|
}
|
1077
|
935
|
|
|
@@ -1088,12 +946,6 @@ JingleSessionPC.prototype.removeStream = function (stream, callback, ssrcInfo) {
|
1088
|
946
|
} else {
|
1089
|
947
|
logger.log("Cannot remove tracks: no RTPSender.");
|
1090
|
948
|
}
|
1091
|
|
- } else if(this.modifySourcesQueue.paused) {
|
1092
|
|
- // if this.modifySourcesQueue.paused, modifySources won't be called and
|
1093
|
|
- // the SDPs won't be updated. Basically removeStream will fail. That's
|
1094
|
|
- // why we are throwing the error to inform the callers of the method.
|
1095
|
|
- throw new Error("modifySourcesQueue paused");
|
1096
|
|
- return;
|
1097
|
949
|
} else if(stream)
|
1098
|
950
|
this.peerconnection.removeStream(stream, false, ssrcInfo);
|
1099
|
951
|
// else
|
|
@@ -1106,8 +958,6 @@ JingleSessionPC.prototype.removeStream = function (stream, callback, ssrcInfo) {
|
1106
|
958
|
this.modifySourcesQueue.push(function() {
|
1107
|
959
|
logger.log('modify sources done');
|
1108
|
960
|
|
1109
|
|
- callback();
|
1110
|
|
-
|
1111
|
961
|
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
1112
|
962
|
if(ssrcInfo) {
|
1113
|
963
|
self.modifiedSSRCs[ssrcInfo.type] =
|
|
@@ -1116,6 +966,12 @@ JingleSessionPC.prototype.removeStream = function (stream, callback, ssrcInfo) {
|
1116
|
966
|
}
|
1117
|
967
|
logger.log("SDPs", oldSdp, newSdp);
|
1118
|
968
|
self.notifyMySSRCUpdate(oldSdp, newSdp);
|
|
969
|
+ }, function (error) {
|
|
970
|
+ if(!error) {
|
|
971
|
+ callback();
|
|
972
|
+ } else {
|
|
973
|
+ errorCallback(error);
|
|
974
|
+ }
|
1119
|
975
|
});
|
1120
|
976
|
}
|
1121
|
977
|
|