@@ -38,6 +38,12 @@ RTCSession = function(ua) {
38
38
this . earlyDialogs = { } ;
39
39
this . rtcMediaHandler = null ;
40
40
41
+ // RTCSession confirmation flag
42
+ this . is_confirmed = false ;
43
+
44
+ // is late SDP being negotiated
45
+ this . late_sdp = false ;
46
+
41
47
// Session Timers
42
48
this . timers = {
43
49
ackTimer : null ,
@@ -286,11 +292,19 @@ RTCSession.prototype.answer = function(options) {
286
292
return ;
287
293
}
288
294
289
- self . rtcMediaHandler . createAnswer (
290
- answerCreationSucceeded ,
291
- answerCreationFailed ,
292
- RTCAnswerConstraints
293
- ) ;
295
+ if ( self . late_sdp ) {
296
+ self . rtcMediaHandler . createOffer (
297
+ sdpCreationSucceeded ,
298
+ sdpCreationFailed ,
299
+ RTCAnswerConstraints
300
+ ) ;
301
+ } else {
302
+ self . rtcMediaHandler . createAnswer (
303
+ sdpCreationSucceeded ,
304
+ sdpCreationFailed ,
305
+ RTCAnswerConstraints
306
+ ) ;
307
+ }
294
308
} ,
295
309
296
310
// rtcMediaHandler.addStream failed
@@ -302,8 +316,8 @@ RTCSession.prototype.answer = function(options) {
302
316
self . failed ( 'system' , null , JsSIP . C . causes . WEBRTC_ERROR ) ;
303
317
} ,
304
318
305
- // rtcMediaHandler.createAnswer succeeded
306
- answerCreationSucceeded = function ( body ) {
319
+ // rtcMediaHandler.createAnswer or rtcMediaHandler.createOffer succeeded
320
+ sdpCreationSucceeded = function ( body ) {
307
321
var
308
322
// run for reply success callback
309
323
replySucceeded = function ( ) {
@@ -326,8 +340,8 @@ RTCSession.prototype.answer = function(options) {
326
340
) ;
327
341
} ,
328
342
329
- // rtcMediaHandler.createAnsewr failed
330
- answerCreationFailed = function ( ) {
343
+ // rtcMediaHandler.createAnswer or rtcMediaHandler.createOffer failed
344
+ sdpCreationFailed = function ( ) {
331
345
if ( self . status === C . STATUS_TERMINATED ) {
332
346
return ;
333
347
}
@@ -804,10 +818,45 @@ RTCSession.prototype.getRemoteStreams = function() {
804
818
RTCSession . prototype . init_incoming = function ( request ) {
805
819
var expires ,
806
820
self = this ,
807
- contentType = request . getHeader ( 'Content-Type' ) ;
821
+ contentType = request . getHeader ( 'Content-Type' ) ,
822
+
823
+ waitForAnswer = function ( ) {
824
+ self . status = C . STATUS_WAITING_FOR_ANSWER ;
825
+
826
+ // Set userNoAnswerTimer
827
+ self . timers . userNoAnswerTimer = window . setTimeout ( function ( ) {
828
+ request . reply ( 408 ) ;
829
+ self . failed ( 'local' , null , JsSIP . C . causes . NO_ANSWER ) ;
830
+ } , self . ua . configuration . no_answer_timeout
831
+ ) ;
832
+
833
+ /* Set expiresTimer
834
+ * RFC3261 13.3.1
835
+ */
836
+ if ( expires ) {
837
+ self . timers . expiresTimer = window . setTimeout ( function ( ) {
838
+ if ( self . status === C . STATUS_WAITING_FOR_ANSWER ) {
839
+ request . reply ( 487 ) ;
840
+ self . failed ( 'system' , null , JsSIP . C . causes . EXPIRES ) ;
841
+ }
842
+ } , expires
843
+ ) ;
844
+ }
845
+
846
+ // Fire 'newRTCSession' event.
847
+ self . newRTCSession ( 'remote' , request ) ;
848
+
849
+ // Reply 180.
850
+ request . reply ( 180 , null , [ 'Contact: ' + self . contact ] ) ;
851
+
852
+ // Fire 'progress' event.
853
+ // TODO: Document that 'response' field in 'progress' event is null for
854
+ // incoming calls.
855
+ self . progress ( 'local' , null ) ;
856
+ } ;
808
857
809
858
// Check body and content type
810
- if ( ! request . body || ( contentType !== 'application/sdp' ) ) {
859
+ if ( request . body && ( contentType !== 'application/sdp' ) ) {
811
860
request . reply ( 415 ) ;
812
861
return ;
813
862
}
@@ -845,57 +894,29 @@ RTCSession.prototype.init_incoming = function(request) {
845
894
constraints : { "optional" : [ { 'DtlsSrtpKeyAgreement' : 'true' } ] }
846
895
} ) ;
847
896
848
- this . rtcMediaHandler . onMessage (
849
- 'offer' ,
850
- request . body ,
851
- /*
852
- * onSuccess
853
- * SDP Offer is valid. Fire UA newRTCSession
854
- */
855
- function ( ) {
856
- self . status = C . STATUS_WAITING_FOR_ANSWER ;
857
-
858
- // Set userNoAnswerTimer
859
- self . timers . userNoAnswerTimer = window . setTimeout ( function ( ) {
860
- request . reply ( 408 ) ;
861
- self . failed ( 'local' , null , JsSIP . C . causes . NO_ANSWER ) ;
862
- } , self . ua . configuration . no_answer_timeout
863
- ) ;
864
-
865
- /* Set expiresTimer
866
- * RFC3261 13.3.1
897
+ if ( request . body ) {
898
+ this . rtcMediaHandler . onMessage (
899
+ 'offer' ,
900
+ request . body ,
901
+ /*
902
+ * onSuccess
903
+ * SDP Offer is valid. Fire UA newRTCSession
867
904
*/
868
- if ( expires ) {
869
- self . timers . expiresTimer = window . setTimeout ( function ( ) {
870
- if ( self . status === C . STATUS_WAITING_FOR_ANSWER ) {
871
- request . reply ( 487 ) ;
872
- self . failed ( 'system' , null , JsSIP . C . causes . EXPIRES ) ;
873
- }
874
- } , expires
875
- ) ;
905
+ waitForAnswer ,
906
+ /*
907
+ * onFailure
908
+ * Bad media description
909
+ */
910
+ function ( e ) {
911
+ self . logger . warn ( 'invalid SDP' ) ;
912
+ self . logger . warn ( e ) ;
913
+ request . reply ( 488 ) ;
876
914
}
877
-
878
- // Fire 'newRTCSession' event.
879
- self . newRTCSession ( 'remote' , request ) ;
880
-
881
- // Reply 180.
882
- request . reply ( 180 , null , [ 'Contact: ' + self . contact ] ) ;
883
-
884
- // Fire 'progress' event.
885
- // TODO: Document that 'response' field in 'progress' event is null for
886
- // incoming calls.
887
- self . progress ( 'local' , null ) ;
888
- } ,
889
- /*
890
- * onFailure
891
- * Bad media description
892
- */
893
- function ( e ) {
894
- self . logger . warn ( 'invalid SDP' ) ;
895
- self . logger . warn ( e ) ;
896
- request . reply ( 488 ) ;
897
- }
898
- ) ;
915
+ ) ;
916
+ } else {
917
+ this . late_sdp = true ;
918
+ waitForAnswer ( ) ;
919
+ }
899
920
} ;
900
921
901
922
RTCSession . prototype . connect = function ( target , options ) {
@@ -1106,39 +1127,18 @@ RTCSession.prototype.receiveReinvite = function(request) {
1106
1127
sdp , idx , direction ,
1107
1128
self = this ,
1108
1129
contentType = request . getHeader ( 'Content-Type' ) ,
1109
- hold = true ;
1110
-
1111
- if ( ! request . body ) {
1112
- this . logger . warn ( 're-INVITE without body not implemented' ) ;
1113
- request . reply ( 415 , 're-INVITE Without Body Not Implemented' ) ;
1114
- return ;
1115
- }
1130
+ hold = false ,
1116
1131
1117
- if ( contentType !== 'application/sdp' ) {
1118
- this . logger . warn ( 'invalid Content-Type' ) ;
1119
- request . reply ( 415 ) ;
1120
- return ;
1121
- }
1122
-
1123
- sdp = JsSIP . Parser . parseSDP ( request . body ) ;
1124
-
1125
- for ( idx = 0 ; idx < sdp . media . length ; idx ++ ) {
1126
- direction = sdp . direction || sdp . media [ idx ] . direction || 'sendrecv' ;
1127
-
1128
- if ( direction !== 'sendonly' && direction !== 'inactive' ) {
1129
- hold = false ;
1130
- }
1131
- }
1132
+ createSdp = function ( onSuccess , onFailure ) {
1133
+ if ( self . late_sdp ) {
1134
+ self . rtcMediaHandler . createOffer ( onSuccess , onFailure ) ;
1135
+ } else {
1136
+ self . rtcMediaHandler . createAnswer ( onSuccess , onFailure ) ;
1137
+ }
1138
+ } ,
1132
1139
1133
- this . rtcMediaHandler . onMessage (
1134
- 'offer' ,
1135
- request . body ,
1136
- /*
1137
- * onSuccess
1138
- * SDP Offer is valid
1139
- */
1140
- function ( ) {
1141
- self . rtcMediaHandler . createAnswer (
1140
+ answer = function ( ) {
1141
+ createSdp (
1142
1142
// onSuccess
1143
1143
function ( body ) {
1144
1144
request . reply ( 200 , null , [ 'Contact: ' + self . contact ] , body ,
@@ -1160,16 +1160,47 @@ RTCSession.prototype.receiveReinvite = function(request) {
1160
1160
request . reply ( 500 ) ;
1161
1161
}
1162
1162
) ;
1163
- } ,
1164
- /*
1165
- * onFailure
1166
- * Bad media description
1167
- */
1168
- function ( e ) {
1169
- self . logger . error ( e ) ;
1170
- request . reply ( 488 ) ;
1163
+ } ;
1164
+
1165
+
1166
+ if ( request . body ) {
1167
+ if ( contentType !== 'application/sdp' ) {
1168
+ this . logger . warn ( 'invalid Content-Type' ) ;
1169
+ request . reply ( 415 ) ;
1170
+ return ;
1171
1171
}
1172
- ) ;
1172
+
1173
+ sdp = JsSIP . Parser . parseSDP ( request . body ) ;
1174
+
1175
+ for ( idx = 0 ; idx < sdp . media . length ; idx ++ ) {
1176
+ direction = sdp . direction || sdp . media [ idx ] . direction || 'sendrecv' ;
1177
+
1178
+ if ( direction === 'sendonly' || direction === 'inactive' ) {
1179
+ hold = true ;
1180
+ }
1181
+ }
1182
+
1183
+ this . rtcMediaHandler . onMessage (
1184
+ 'offer' ,
1185
+ request . body ,
1186
+ /*
1187
+ * onSuccess
1188
+ * SDP Offer is valid
1189
+ */
1190
+ answer ,
1191
+ /*
1192
+ * onFailure
1193
+ * Bad media description
1194
+ */
1195
+ function ( e ) {
1196
+ self . logger . error ( e ) ;
1197
+ request . reply ( 488 ) ;
1198
+ }
1199
+ ) ;
1200
+ } else {
1201
+ this . late_sdp = true ;
1202
+ answer ( ) ;
1203
+ }
1173
1204
} ;
1174
1205
1175
1206
/**
@@ -1244,7 +1275,8 @@ RTCSession.prototype.receiveUpdate = function(request) {
1244
1275
* In dialog Request Reception
1245
1276
*/
1246
1277
RTCSession . prototype . receiveRequest = function ( request ) {
1247
- var contentType ;
1278
+ var contentType ,
1279
+ self = this ;
1248
1280
1249
1281
if ( request . method === JsSIP . C . CANCEL ) {
1250
1282
/* RFC3261 15 States that a UAS may have accepted an invitation while a CANCEL
@@ -1270,8 +1302,40 @@ RTCSession.prototype.receiveRequest = function(request) {
1270
1302
if ( this . status === C . STATUS_WAITING_FOR_ACK ) {
1271
1303
window . clearTimeout ( this . timers . ackTimer ) ;
1272
1304
window . clearTimeout ( this . timers . invite2xxTimer ) ;
1273
- this . status = C . STATUS_CONFIRMED ;
1274
- this . confirmed ( 'remote' , request ) ;
1305
+
1306
+ if ( this . late_sdp ) {
1307
+ if ( ! request . body ) {
1308
+ self . ended ( 'remote' , request , JsSIP . C . causes . MISSING_SDP ) ;
1309
+ break ;
1310
+ }
1311
+
1312
+ this . rtcMediaHandler . onMessage (
1313
+ 'answer' ,
1314
+ request . body ,
1315
+ /*
1316
+ * onSuccess
1317
+ * SDP Answer fits with Offer. Media will start
1318
+ */
1319
+ function ( ) {
1320
+ self . late_sdp = false ;
1321
+ self . status = C . STATUS_CONFIRMED ;
1322
+ } ,
1323
+ /*
1324
+ * onFailure
1325
+ * SDP Answer does not fit the Offer. Accept the call and Terminate.
1326
+ */
1327
+ function ( e ) {
1328
+ self . logger . warn ( e ) ;
1329
+ self . ended ( 'remote' , request , JsSIP . C . causes . BAD_MEDIA_DESCRIPTION ) ;
1330
+ }
1331
+ ) ;
1332
+ } else {
1333
+ this . status = C . STATUS_CONFIRMED ;
1334
+ }
1335
+
1336
+ if ( this . status === C . STATUS_CONFIRMED && ! this . is_confirmed ) {
1337
+ this . confirmed ( 'remote' , request ) ;
1338
+ }
1275
1339
}
1276
1340
break ;
1277
1341
case JsSIP . C . BYE :
@@ -1579,7 +1643,7 @@ RTCSession.prototype.receiveInviteResponse = function(response) {
1579
1643
this . status = C . STATUS_CONFIRMED ;
1580
1644
1581
1645
if ( ! response . body ) {
1582
- this . acceptAndTerminate ( response , 400 , 'Missing session description' ) ;
1646
+ this . acceptAndTerminate ( response , 400 , JsSIP . C . causes . MISSING_SDP ) ;
1583
1647
this . failed ( 'remote' , response , JsSIP . C . causes . BAD_MEDIA_DESCRIPTION ) ;
1584
1648
break ;
1585
1649
}
@@ -1597,10 +1661,8 @@ RTCSession.prototype.receiveInviteResponse = function(response) {
1597
1661
* SDP Answer fits with Offer. Media will start
1598
1662
*/
1599
1663
function ( ) {
1600
- var ack ;
1601
-
1602
1664
session . accepted ( 'remote' , response ) ;
1603
- ack = session . sendRequest ( JsSIP . C . ACK ) ;
1665
+ session . sendRequest ( JsSIP . C . ACK ) ;
1604
1666
session . confirmed ( 'local' , null ) ;
1605
1667
} ,
1606
1668
/*
@@ -1812,6 +1874,8 @@ RTCSession.prototype.confirmed = function(originator, ack) {
1812
1874
var session = this ,
1813
1875
event_name = 'confirmed' ;
1814
1876
1877
+ this . is_confirmed = true ;
1878
+
1815
1879
session . emit ( event_name , session , {
1816
1880
originator : originator ,
1817
1881
ack : ack || null
0 commit comments