|
@@ -674,8 +674,6 @@ export default class JingleSessionPC extends JingleSession {
|
674
|
674
|
this.setOfferAnswerCycle(
|
675
|
675
|
jingleOffer,
|
676
|
676
|
() => {
|
677
|
|
- this.state = JingleSessionState.ACTIVE;
|
678
|
|
-
|
679
|
677
|
// FIXME we may not care about RESULT packet for session-accept
|
680
|
678
|
// then we should either call 'success' here immediately or
|
681
|
679
|
// modify sendSessionAccept method to do that
|
|
@@ -697,22 +695,49 @@ export default class JingleSessionPC extends JingleSession {
|
697
|
695
|
if (!this.isInitiator) {
|
698
|
696
|
throw new Error('Trying to invite from the responder session');
|
699
|
697
|
}
|
700
|
|
- for (const localTrack of localTracks) {
|
701
|
|
- this.peerconnection.addTrack(localTrack);
|
702
|
|
- }
|
703
|
|
- this.peerconnection.createOffer(
|
704
|
|
- this.sendSessionInitiate.bind(this),
|
705
|
|
- error => logger.error('Failed to create offer', error),
|
706
|
|
- this.mediaConstraints);
|
|
698
|
+ const workFunction = finishedCallback => {
|
|
699
|
+ for (const localTrack of localTracks) {
|
|
700
|
+ this.peerconnection.addTrack(localTrack);
|
|
701
|
+ }
|
|
702
|
+ this.peerconnection.createOffer(
|
|
703
|
+ sdp => {
|
|
704
|
+ this.sendSessionInitiate(
|
|
705
|
+ sdp,
|
|
706
|
+ finishedCallback,
|
|
707
|
+ finishedCallback
|
|
708
|
+ );
|
|
709
|
+ },
|
|
710
|
+ error => {
|
|
711
|
+ logger.error(
|
|
712
|
+ 'Failed to create an offer',
|
|
713
|
+ error,
|
|
714
|
+ this.mediaConstraints);
|
|
715
|
+ finishedCallback(error);
|
|
716
|
+ },
|
|
717
|
+ this.mediaConstraints);
|
|
718
|
+ };
|
|
719
|
+
|
|
720
|
+ this.modificationQueue.push(
|
|
721
|
+ workFunction,
|
|
722
|
+ error => {
|
|
723
|
+ if (error) {
|
|
724
|
+ logger.error('invite error', error);
|
|
725
|
+ } else {
|
|
726
|
+ logger.debug('invite executed - OK');
|
|
727
|
+ }
|
|
728
|
+ });
|
707
|
729
|
}
|
708
|
730
|
|
709
|
731
|
/**
|
710
|
732
|
* Sends 'session-initiate' to the remote peer.
|
711
|
733
|
* @param {object} sdp the local session description object as defined by
|
712
|
734
|
* the WebRTC standard.
|
|
735
|
+ * @param {function} success executed when the operation succeeds.
|
|
736
|
+ * @param {function(error)} failure executed when the operation fails with
|
|
737
|
+ * an error passed as an argument.
|
713
|
738
|
* @private
|
714
|
739
|
*/
|
715
|
|
- sendSessionInitiate(sdp) {
|
|
740
|
+ sendSessionInitiate(sdp, success, failure) {
|
716
|
741
|
logger.log('createdOffer', sdp);
|
717
|
742
|
const sendJingle = () => {
|
718
|
743
|
let init = $iq({
|
|
@@ -740,12 +765,17 @@ export default class JingleSessionPC extends JingleSession {
|
740
|
765
|
logger.error('"session-initiate" error', error);
|
741
|
766
|
},
|
742
|
767
|
IQ_TIMEOUT);
|
|
768
|
+
|
|
769
|
+ // NOTE the callback is executed immediately as we don't want to
|
|
770
|
+ // wait for the XMPP response which would delay the startup process.
|
|
771
|
+ success();
|
743
|
772
|
};
|
744
|
773
|
|
745
|
774
|
this.peerconnection.setLocalDescription(
|
746
|
775
|
sdp, sendJingle,
|
747
|
776
|
error => {
|
748
|
777
|
logger.error('session-init setLocalDescription failed', error);
|
|
778
|
+ failure(error);
|
749
|
779
|
}
|
750
|
780
|
);
|
751
|
781
|
}
|
|
@@ -761,7 +791,6 @@ export default class JingleSessionPC extends JingleSession {
|
761
|
791
|
this.setOfferAnswerCycle(
|
762
|
792
|
jingleAnswer,
|
763
|
793
|
() => {
|
764
|
|
- this.state = JingleSessionState.ACTIVE;
|
765
|
794
|
logger.info('setAnswer - succeeded');
|
766
|
795
|
},
|
767
|
796
|
error => {
|
|
@@ -795,9 +824,27 @@ export default class JingleSessionPC extends JingleSession {
|
795
|
824
|
|
796
|
825
|
const newRemoteSdp
|
797
|
826
|
= this._processNewJingleOfferIq(jingleOfferAnswerIq);
|
|
827
|
+ const oldLocalSdp
|
|
828
|
+ = this.peerconnection.localDescription.sdp;
|
798
|
829
|
|
799
|
|
- this._renegotiate(newRemoteSdp)
|
|
830
|
+ this._renegotiate(newRemoteSdp.raw)
|
800
|
831
|
.then(() => {
|
|
832
|
+ if (this.state === JingleSessionState.PENDING) {
|
|
833
|
+ this.state = JingleSessionState.ACTIVE;
|
|
834
|
+ }
|
|
835
|
+
|
|
836
|
+ // Old local SDP will be available when we're setting answer
|
|
837
|
+ // for the first time, but not when offer and it's fine
|
|
838
|
+ // since we're generating an answer now it will contain all
|
|
839
|
+ // our SSRCs
|
|
840
|
+ if (oldLocalSdp) {
|
|
841
|
+ const newLocalSdp
|
|
842
|
+ = new SDP(this.peerconnection.localDescription.sdp);
|
|
843
|
+
|
|
844
|
+ this.notifyMySSRCUpdate(
|
|
845
|
+ new SDP(oldLocalSdp), newLocalSdp);
|
|
846
|
+ }
|
|
847
|
+
|
801
|
848
|
finishedCallback();
|
802
|
849
|
}, error => {
|
803
|
850
|
logger.error(
|
|
@@ -1004,45 +1051,51 @@ export default class JingleSessionPC extends JingleSession {
|
1004
|
1051
|
IQ_TIMEOUT);
|
1005
|
1052
|
}
|
1006
|
1053
|
|
1007
|
|
- /* eslint-disable max-params */
|
1008
|
|
-
|
1009
|
1054
|
/**
|
1010
|
1055
|
* @inheritDoc
|
1011
|
1056
|
*/
|
1012
|
|
- terminate(reason, text, success, failure) {
|
1013
|
|
- let sessionTerminate = $iq({
|
1014
|
|
- to: this.peerjid,
|
1015
|
|
- type: 'set'
|
1016
|
|
- })
|
1017
|
|
- .c('jingle', {
|
1018
|
|
- xmlns: 'urn:xmpp:jingle:1',
|
1019
|
|
- action: 'session-terminate',
|
1020
|
|
- initiator: this.initiator,
|
1021
|
|
- sid: this.sid
|
1022
|
|
- })
|
1023
|
|
- .c('reason')
|
1024
|
|
- .c(reason || 'success');
|
1025
|
|
-
|
1026
|
|
- if (text) {
|
1027
|
|
- // eslint-disable-next-line newline-per-chained-call
|
1028
|
|
- sessionTerminate.up().c('text').t(text);
|
|
1057
|
+ terminate(success, failure, options) {
|
|
1058
|
+ if (this.state === JingleSessionState.ENDED) {
|
|
1059
|
+ return;
|
1029
|
1060
|
}
|
1030
|
1061
|
|
1031
|
|
- // Calling tree() to print something useful
|
1032
|
|
- sessionTerminate = sessionTerminate.tree();
|
1033
|
|
- logger.info('Sending session-terminate', sessionTerminate);
|
|
1062
|
+ if (!options || Boolean(options.sendSessionTerminate)) {
|
|
1063
|
+ let sessionTerminate
|
|
1064
|
+ = $iq({
|
|
1065
|
+ to: this.peerjid,
|
|
1066
|
+ type: 'set'
|
|
1067
|
+ })
|
|
1068
|
+ .c('jingle', {
|
|
1069
|
+ xmlns: 'urn:xmpp:jingle:1',
|
|
1070
|
+ action: 'session-terminate',
|
|
1071
|
+ initiator: this.initiator,
|
|
1072
|
+ sid: this.sid
|
|
1073
|
+ })
|
|
1074
|
+ .c('reason')
|
|
1075
|
+ .c((options && options.reason) || 'success');
|
|
1076
|
+
|
|
1077
|
+ if (options && options.reasonDescription) {
|
|
1078
|
+ sessionTerminate.up()
|
|
1079
|
+ .c('text')
|
|
1080
|
+ .t(options.reasonDescription);
|
|
1081
|
+ }
|
1034
|
1082
|
|
1035
|
|
- this.connection.sendIQ(
|
1036
|
|
- sessionTerminate,
|
1037
|
|
- success,
|
1038
|
|
- this.newJingleErrorHandler(sessionTerminate, failure), IQ_TIMEOUT);
|
|
1083
|
+ // Calling tree() to print something useful
|
|
1084
|
+ sessionTerminate = sessionTerminate.tree();
|
|
1085
|
+ logger.info('Sending session-terminate', sessionTerminate);
|
|
1086
|
+ this.connection.sendIQ(
|
|
1087
|
+ sessionTerminate,
|
|
1088
|
+ success,
|
|
1089
|
+ this.newJingleErrorHandler(sessionTerminate, failure),
|
|
1090
|
+ IQ_TIMEOUT);
|
|
1091
|
+ } else {
|
|
1092
|
+ logger.info(`Skipped sending session-terminate for ${this}`);
|
|
1093
|
+ }
|
1039
|
1094
|
|
1040
|
1095
|
// this should result in 'onTerminated' being called by strope.jingle.js
|
1041
|
1096
|
this.connection.jingle.terminate(this.sid);
|
1042
|
1097
|
}
|
1043
|
1098
|
|
1044
|
|
- /* eslint-enable max-params */
|
1045
|
|
-
|
1046
|
1099
|
/**
|
1047
|
1100
|
*
|
1048
|
1101
|
* @param reasonCondition
|
|
@@ -1197,7 +1250,7 @@ export default class JingleSessionPC extends JingleSession {
|
1197
|
1250
|
? this._processRemoteAddSource(addOrRemoveSsrcInfo)
|
1198
|
1251
|
: this._processRemoteRemoveSource(addOrRemoveSsrcInfo);
|
1199
|
1252
|
|
1200
|
|
- this._renegotiate(newRemoteSdp)
|
|
1253
|
+ this._renegotiate(newRemoteSdp.raw)
|
1201
|
1254
|
.then(() => {
|
1202
|
1255
|
const newLocalSdp
|
1203
|
1256
|
= new SDP(this.peerconnection.localDescription.sdp);
|
|
@@ -1302,7 +1355,7 @@ export default class JingleSessionPC extends JingleSession {
|
1302
|
1355
|
|
1303
|
1356
|
/**
|
1304
|
1357
|
* Do a new o/a flow using the existing remote description
|
1305
|
|
- * @param {SDP object} optionalRemoteSdp optional remote sdp
|
|
1358
|
+ * @param {string} [optionalRemoteSdp] optional, raw remote sdp
|
1306
|
1359
|
* to use. If not provided, the remote sdp from the
|
1307
|
1360
|
* peerconnection will be used
|
1308
|
1361
|
* @returns {Promise} promise which resolves when the
|
|
@@ -1311,11 +1364,17 @@ export default class JingleSessionPC extends JingleSession {
|
1311
|
1364
|
*/
|
1312
|
1365
|
_renegotiate(optionalRemoteSdp) {
|
1313
|
1366
|
const remoteSdp
|
1314
|
|
- = optionalRemoteSdp
|
1315
|
|
- || new SDP(this.peerconnection.remoteDescription.sdp);
|
|
1367
|
+ = optionalRemoteSdp || this.peerconnection.remoteDescription.sdp;
|
|
1368
|
+
|
|
1369
|
+ if (!remoteSdp) {
|
|
1370
|
+ return Promise.reject(
|
|
1371
|
+ 'Can not renegotiate without remote description,'
|
|
1372
|
+ + `- current state: ${this.state}`);
|
|
1373
|
+ }
|
|
1374
|
+
|
1316
|
1375
|
const remoteDescription = new RTCSessionDescription({
|
1317
|
1376
|
type: this.isInitiator ? 'answer' : 'offer',
|
1318
|
|
- sdp: remoteSdp.raw
|
|
1377
|
+ sdp: remoteSdp
|
1319
|
1378
|
});
|
1320
|
1379
|
|
1321
|
1380
|
return new Promise((resolve, reject) => {
|
|
@@ -1461,7 +1520,8 @@ export default class JingleSessionPC extends JingleSession {
|
1461
|
1520
|
this.peerconnection.addTrack(newTrack);
|
1462
|
1521
|
}
|
1463
|
1522
|
|
1464
|
|
- if ((oldTrack || newTrack) && oldLocalSdp) {
|
|
1523
|
+ if ((oldTrack || newTrack)
|
|
1524
|
+ && this.state === JingleSessionState.ACTIVE) {
|
1465
|
1525
|
this._renegotiate()
|
1466
|
1526
|
.then(() => {
|
1467
|
1527
|
const newLocalSDP
|
|
@@ -1695,6 +1755,16 @@ export default class JingleSessionPC extends JingleSession {
|
1695
|
1755
|
*/
|
1696
|
1756
|
setMediaTransferActive(active) {
|
1697
|
1757
|
const workFunction = finishedCallback => {
|
|
1758
|
+
|
|
1759
|
+ // Because the value is modified on the queue it's impossible to
|
|
1760
|
+ // check it's final value reliably prior to submitting the task.
|
|
1761
|
+ // The rule here is that the last submitted state counts.
|
|
1762
|
+ // Check the value here to avoid unnecessary renegotiation cycle.
|
|
1763
|
+ if (this.mediaTransferActive === active) {
|
|
1764
|
+ finishedCallback();
|
|
1765
|
+
|
|
1766
|
+ return;
|
|
1767
|
+ }
|
1698
|
1768
|
this.mediaTransferActive = active;
|
1699
|
1769
|
if (this.peerconnection) {
|
1700
|
1770
|
this.peerconnection.setMediaTransferActive(
|
|
@@ -1844,9 +1914,10 @@ export default class JingleSessionPC extends JingleSession {
|
1844
|
1914
|
// FIXME: Maybe we can include part of the session object
|
1845
|
1915
|
// error.session = this;
|
1846
|
1916
|
|
1847
|
|
- logger.error('Jingle error', error);
|
1848
|
1917
|
if (failureCb) {
|
1849
|
1918
|
failureCb(error);
|
|
1919
|
+ } else {
|
|
1920
|
+ logger.error('Jingle error', error);
|
1850
|
1921
|
}
|
1851
|
1922
|
};
|
1852
|
1923
|
}
|