@babblevoice/babble-drachtio-callmanager 1.5.0 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/call.js +31 -11
- package/package.json +1 -1
- package/test/interface/early.js +372 -14
- package/test/mock/srf.js +110 -5
package/lib/call.js
CHANGED
|
@@ -843,7 +843,7 @@ class call {
|
|
|
843
843
|
"User-Agent": "project",
|
|
844
844
|
"Supported": "replaces"
|
|
845
845
|
},
|
|
846
|
-
"body": this.sdp.local.toString()
|
|
846
|
+
"body": this.parent.sdp.local.toString()
|
|
847
847
|
} )
|
|
848
848
|
}
|
|
849
849
|
|
|
@@ -1045,6 +1045,14 @@ class call {
|
|
|
1045
1045
|
this.sdp.local.addcodecs( "2833" )
|
|
1046
1046
|
}
|
|
1047
1047
|
|
|
1048
|
+
if( this._iswebrtc ) {
|
|
1049
|
+
let ch = this.channels.audio
|
|
1050
|
+
this.sdp.local.addssrc( ch.local.ssrc )
|
|
1051
|
+
.secure( ch.local.dtls.fingerprint, channeldef.remote.dtls.mode )
|
|
1052
|
+
.addicecandidates( ch.local.address, ch.local.port, ch.local.icepwd )
|
|
1053
|
+
.rtcpmux()
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1048
1056
|
this._dialog = await this._dialog.ack( this.sdp.local.toString() )
|
|
1049
1057
|
this._addevents( this._dialog )
|
|
1050
1058
|
|
|
@@ -1138,6 +1146,10 @@ class call {
|
|
|
1138
1146
|
this._req = req
|
|
1139
1147
|
this._res = res
|
|
1140
1148
|
|
|
1149
|
+
if( this._req.msg && this._req.msg.body ) {
|
|
1150
|
+
this.sdp.remote = sdpgen.create( this._req.msg.body )
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1141
1153
|
this._req.on( "cancel", () => this._oncanceled() )
|
|
1142
1154
|
|
|
1143
1155
|
let authorization = this._auth.parseauthheaders( this._req )
|
|
@@ -1323,18 +1335,23 @@ class call {
|
|
|
1323
1335
|
*/
|
|
1324
1336
|
get _iswebrtc() {
|
|
1325
1337
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1338
|
+
if( !this.sip || !this.sip.contact ) return false
|
|
1339
|
+
|
|
1340
|
+
let contactstr
|
|
1341
|
+
if( Array.isArray( this.sip.contact ) ) {
|
|
1342
|
+
contactstr = this.sip.contact[ 0 ].uri
|
|
1343
|
+
} else {
|
|
1344
|
+
contactstr = this.sip.contact
|
|
1331
1345
|
}
|
|
1332
1346
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
this.sdp.remote.sdp.media[ 0 ] &&
|
|
1347
|
+
/* Have we received remote SDP? */
|
|
1348
|
+
if( this.sdp.remote ) {
|
|
1349
|
+
return this.sdp.remote.sdp.media[ 0 ] &&
|
|
1337
1350
|
-1 !== this.sdp.remote.sdp.media[ 0 ].protocol.toLowerCase().indexOf( "savpf" ) /* 'UDP/TLS/RTP/SAVPF' */
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
return -1 !== contactstr.indexOf( ";transport=ws" )
|
|
1354
|
+
|
|
1338
1355
|
}
|
|
1339
1356
|
|
|
1340
1357
|
/**
|
|
@@ -1352,7 +1369,6 @@ class call {
|
|
|
1352
1369
|
|
|
1353
1370
|
let channeldef
|
|
1354
1371
|
if( this._req.msg && this._req.msg.body ) {
|
|
1355
|
-
this.sdp.remote = sdpgen.create( this._req.msg.body )
|
|
1356
1372
|
/* options.preferedcodecs may have been narrowed down so we still check callmanager as well */
|
|
1357
1373
|
this.selectedcodec = this.sdp.remote.intersection( options.preferedcodecs, true )
|
|
1358
1374
|
if( false === this.selectedcodec ) {
|
|
@@ -2604,6 +2620,10 @@ class call {
|
|
|
2604
2620
|
callmanager.options.em.emit( "call.new", c )
|
|
2605
2621
|
} )
|
|
2606
2622
|
|
|
2623
|
+
if( c._req.msg && c._req.msg.body ) {
|
|
2624
|
+
c.sdp.remote = sdpgen.create( c._req.msg.body )
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2607
2627
|
return c
|
|
2608
2628
|
|
|
2609
2629
|
}
|
package/package.json
CHANGED
package/test/interface/early.js
CHANGED
|
@@ -22,9 +22,159 @@ describe( "call early", function() {
|
|
|
22
22
|
} )
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
it( `Create call and send 183 - early`, async () => {
|
|
25
|
+
it( `Create call and send 183 - early basic`, async () => {
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
Phone BV Gateway
|
|
29
|
+
|---------INVITE------>| |(1)
|
|
30
|
+
| |---------INVITE------>|(2)
|
|
31
|
+
| |<--------183 (w-sdp)--|(3)
|
|
32
|
+
|<--------183 (w-sdp)--| |(4)
|
|
33
|
+
|
|
34
|
+
Phone RTP: 192.168.0.200:18540
|
|
35
|
+
BV RTP: 192.168.0.141
|
|
36
|
+
Gateway RTP: 192.168.0.160:21000
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/* Setup the mock RTP server */
|
|
26
40
|
let srfscenario = new srf.srfscenario()
|
|
41
|
+
let rtpserver = callmanager.projectrtp.proxy.listen()
|
|
42
|
+
|
|
43
|
+
let connection = net.createConnection( 9002, "127.0.0.1" )
|
|
44
|
+
.on( "error", ( e ) => {
|
|
45
|
+
console.error( e )
|
|
46
|
+
} )
|
|
47
|
+
|
|
48
|
+
connection.on( "connect", () => {
|
|
49
|
+
/* announce our node */
|
|
50
|
+
connection.write( projectrtpmessage.createmessage( {"status":{"channel":{"available":5000,"current":0},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}} ) )
|
|
51
|
+
} )
|
|
52
|
+
|
|
53
|
+
let mixing
|
|
54
|
+
let messagestate = projectrtpmessage.newstate()
|
|
55
|
+
let channelmessages = []
|
|
56
|
+
let opencount = 0
|
|
57
|
+
|
|
58
|
+
connection.on( "data", ( data ) => {
|
|
59
|
+
projectrtpmessage.parsemessage( messagestate, data, ( msg ) => {
|
|
60
|
+
try{
|
|
61
|
+
channelmessages.push( msg )
|
|
62
|
+
if( "open" === msg.channel ) {
|
|
63
|
+
if( 0 == opencount ) {
|
|
64
|
+
setTimeout( () =>
|
|
65
|
+
connection.write(
|
|
66
|
+
projectrtpmessage.createmessage(
|
|
67
|
+
{"local":{"port":10008,"dtls":
|
|
68
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
69
|
+
"address":"192.168.0.141"},
|
|
70
|
+
"id": msg.id,
|
|
71
|
+
"uuid": uuidv4(),
|
|
72
|
+
"action":"open",
|
|
73
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
74
|
+
} ) ), 2 )
|
|
75
|
+
} else {
|
|
76
|
+
setTimeout( () =>
|
|
77
|
+
connection.write(
|
|
78
|
+
projectrtpmessage.createmessage(
|
|
79
|
+
{"local":{"port": 10010,"dtls":
|
|
80
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
81
|
+
"address":"192.168.0.141"},
|
|
82
|
+
"id": msg.id,
|
|
83
|
+
"uuid": uuidv4(),
|
|
84
|
+
"action":"open",
|
|
85
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
86
|
+
} ) ), 2 )
|
|
87
|
+
}
|
|
88
|
+
opencount++
|
|
89
|
+
} else if ( "close" === msg.channel ) {
|
|
90
|
+
connection.write( projectrtpmessage.createmessage( {"id": msg.id,"uuid":msg.uuid,"action":"close","reason":"requested","stats":{"in":{"mos":4.5,"count":586,"dropped":0,"skip":0},"out":{"count":303,"skip":0},"tick":{"meanus":124,"maxus":508,"count":597}}} ) )
|
|
91
|
+
} else if ( "mix" === msg.channel ) {
|
|
92
|
+
mixing = true
|
|
93
|
+
}
|
|
94
|
+
} catch( e ) {
|
|
95
|
+
console.error( e )
|
|
96
|
+
}
|
|
97
|
+
} )
|
|
98
|
+
} )
|
|
99
|
+
|
|
100
|
+
/* ensure we are connected */
|
|
101
|
+
await new Promise( ( r ) => setTimeout( () => r(), 100 ) )
|
|
102
|
+
|
|
103
|
+
srfscenario.oncreateUAC( async ( contact, options, callbacks ) => {
|
|
104
|
+
|
|
105
|
+
/* Step 3. This is the mocked gateway message back to our newcall. */
|
|
106
|
+
callbacks.cbProvisional( {
|
|
107
|
+
"status": 183,
|
|
108
|
+
"msg": {
|
|
109
|
+
"body": `v=0
|
|
110
|
+
o=- 1608235282228 0 IN IP4 127.0.0.1
|
|
111
|
+
s=
|
|
112
|
+
c=IN IP4 192.168.0.160
|
|
113
|
+
t=0 0
|
|
114
|
+
m=audio 21000 RTP/AVP 0 101
|
|
115
|
+
a=rtpmap:101 telephone-event/8000
|
|
116
|
+
a=fmtp:101 0-16
|
|
117
|
+
a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
118
|
+
}
|
|
119
|
+
} )
|
|
120
|
+
|
|
121
|
+
await new Promise( ( r ) => setTimeout( () => r(), 100 ) )
|
|
122
|
+
throw { "status": 503 }
|
|
123
|
+
} )
|
|
124
|
+
|
|
125
|
+
/* Step 1. Phone sends INVITE */
|
|
126
|
+
let call = await new Promise( ( resolve ) => {
|
|
127
|
+
srfscenario.oncall( async ( call ) => { resolve( call ) } )
|
|
128
|
+
srfscenario.inbound()
|
|
129
|
+
} )
|
|
130
|
+
|
|
131
|
+
/* Step 4. BV sends 183 back to phone */
|
|
132
|
+
let msgsent, msginfo
|
|
133
|
+
srfscenario.res.onsend( ( c, i ) => {
|
|
134
|
+
msgsent = c
|
|
135
|
+
msginfo = i
|
|
136
|
+
} )
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
/* Step 2. New INVITE to the remote Gateway */
|
|
140
|
+
let newcall = await call.newuac( { "contact": "callto" } )
|
|
141
|
+
|
|
142
|
+
await call._onhangup( "wire" )
|
|
143
|
+
|
|
144
|
+
expect( newcall.state.early ).to.be.true
|
|
145
|
+
expect( call.state.early ).to.be.true
|
|
146
|
+
expect( mixing ).to.be.true
|
|
147
|
+
expect( msgsent ).to.equal( 183 )
|
|
27
148
|
|
|
149
|
+
expect( channelmessages[ 0 ].channel ).to.equal( "open" )
|
|
150
|
+
expect( channelmessages[ 1 ].channel ).to.equal( "remote" )
|
|
151
|
+
expect( channelmessages[ 1 ].remote.port ).to.equal( 21000 )
|
|
152
|
+
expect( channelmessages[ 1 ].remote.address ).to.equal( "192.168.0.160" )
|
|
153
|
+
expect( channelmessages[ 2 ].channel ).to.equal( "open" )
|
|
154
|
+
expect( channelmessages[ 2 ].remote.port ).to.equal( 18540 )
|
|
155
|
+
expect( channelmessages[ 2 ].remote.address ).to.equal( "192.168.0.200" )
|
|
156
|
+
expect( channelmessages[ 3 ].channel ).to.equal( "mix" )
|
|
157
|
+
expect( channelmessages[ 4 ].channel ).to.equal( "close" )
|
|
158
|
+
expect( channelmessages[ 5 ].channel ).to.equal( "close" )
|
|
159
|
+
|
|
160
|
+
expect( msginfo.body ).to.include( "audio 10010 RTP/AVP" )
|
|
161
|
+
|
|
162
|
+
connection.destroy()
|
|
163
|
+
rtpserver.destroy()
|
|
164
|
+
} )
|
|
165
|
+
|
|
166
|
+
it( `Create call and send 183 - early - SAVPF`, async () => {
|
|
167
|
+
|
|
168
|
+
/*
|
|
169
|
+
Phone (SAVPF) BV Gateway
|
|
170
|
+
|---------INVITE------>| |(1)
|
|
171
|
+
| |---------INVITE------>|(2)
|
|
172
|
+
| |<--------183 (w-sdp)--|(3)
|
|
173
|
+
|<--------183 (w-sdp)--| |(4)
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
/* Setup the mock RTP server */
|
|
177
|
+
let srfscenario = new srf.srfscenario( { savpf: true } )
|
|
28
178
|
let rtpserver = callmanager.projectrtp.proxy.listen()
|
|
29
179
|
|
|
30
180
|
let connection = net.createConnection( 9002, "127.0.0.1" )
|
|
@@ -37,18 +187,49 @@ describe( "call early", function() {
|
|
|
37
187
|
connection.write( projectrtpmessage.createmessage( {"status":{"channel":{"available":5000,"current":0},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}} ) )
|
|
38
188
|
} )
|
|
39
189
|
|
|
40
|
-
let msgid
|
|
41
190
|
let mixing
|
|
42
191
|
let messagestate = projectrtpmessage.newstate()
|
|
192
|
+
let channelmessages = []
|
|
193
|
+
let opencount = 0
|
|
194
|
+
|
|
43
195
|
connection.on( "data", ( data ) => {
|
|
44
196
|
projectrtpmessage.parsemessage( messagestate, data, ( msg ) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
197
|
+
try{
|
|
198
|
+
channelmessages.push( msg )
|
|
199
|
+
if( "open" === msg.channel ) {
|
|
200
|
+
if( 0 == opencount ) {
|
|
201
|
+
setTimeout( () =>
|
|
202
|
+
connection.write(
|
|
203
|
+
projectrtpmessage.createmessage(
|
|
204
|
+
{"local":{"port":10008,"dtls":
|
|
205
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
206
|
+
"address":"192.168.0.141"},
|
|
207
|
+
"id": msg.id,
|
|
208
|
+
"uuid": uuidv4(),
|
|
209
|
+
"action":"open",
|
|
210
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
211
|
+
} ) ), 2 )
|
|
212
|
+
} else {
|
|
213
|
+
setTimeout( () =>
|
|
214
|
+
connection.write(
|
|
215
|
+
projectrtpmessage.createmessage(
|
|
216
|
+
{"local":{"port": 10010,"dtls":
|
|
217
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
218
|
+
"address":"192.168.0.141"},
|
|
219
|
+
"id": msg.id,
|
|
220
|
+
"uuid": uuidv4(),
|
|
221
|
+
"action":"open",
|
|
222
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
223
|
+
} ) ), 2 )
|
|
224
|
+
}
|
|
225
|
+
opencount++
|
|
226
|
+
} else if ( "close" === msg.channel ) {
|
|
227
|
+
connection.write( projectrtpmessage.createmessage( {"id": msg.id,"uuid":msg.uuid,"action":"close","reason":"requested","stats":{"in":{"mos":4.5,"count":586,"dropped":0,"skip":0},"out":{"count":303,"skip":0},"tick":{"meanus":124,"maxus":508,"count":597}}} ) )
|
|
228
|
+
} else if ( "mix" === msg.channel ) {
|
|
229
|
+
mixing = true
|
|
230
|
+
}
|
|
231
|
+
} catch( e ) {
|
|
232
|
+
console.error( e )
|
|
52
233
|
}
|
|
53
234
|
} )
|
|
54
235
|
} )
|
|
@@ -57,6 +238,8 @@ describe( "call early", function() {
|
|
|
57
238
|
await new Promise( ( r ) => setTimeout( () => r(), 100 ) )
|
|
58
239
|
|
|
59
240
|
srfscenario.oncreateUAC( async ( contact, options, callbacks ) => {
|
|
241
|
+
|
|
242
|
+
/* Step 3. This is the mocked gateway message back to our newcall. */
|
|
60
243
|
callbacks.cbProvisional( {
|
|
61
244
|
"status": 183,
|
|
62
245
|
"msg": {
|
|
@@ -76,16 +259,30 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
|
76
259
|
throw { "status": 503 }
|
|
77
260
|
} )
|
|
78
261
|
|
|
79
|
-
|
|
262
|
+
/* Step 1. Phone sends INVITE */
|
|
80
263
|
let call = await new Promise( ( resolve ) => {
|
|
81
264
|
srfscenario.oncall( async ( call ) => { resolve( call ) } )
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
265
|
+
|
|
266
|
+
let req = new srf.req( { savpf: true } )
|
|
267
|
+
req.setparsedheader( "contact", [ {
|
|
268
|
+
name: undefined,
|
|
269
|
+
uri: 'sip:u3s2etdo@pc3lfsq1oh86.invalid;transport=ws;ob',
|
|
270
|
+
params: {}
|
|
271
|
+
}
|
|
272
|
+
] )
|
|
273
|
+
|
|
274
|
+
srfscenario.inbound( req )
|
|
275
|
+
} )
|
|
276
|
+
|
|
277
|
+
/* Step 4. BV sends 183 back to phone */
|
|
278
|
+
let msgsent, msginfo
|
|
279
|
+
srfscenario.res.onsend( ( c, i ) => {
|
|
280
|
+
msgsent = c
|
|
281
|
+
msginfo = i
|
|
86
282
|
} )
|
|
87
283
|
|
|
88
284
|
|
|
285
|
+
/* Step 2. New INVITE to the remote Gateway */
|
|
89
286
|
let newcall = await call.newuac( { "contact": "callto" } )
|
|
90
287
|
|
|
91
288
|
await call._onhangup( "wire" )
|
|
@@ -95,6 +292,167 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
|
95
292
|
expect( mixing ).to.be.true
|
|
96
293
|
expect( msgsent ).to.equal( 183 )
|
|
97
294
|
|
|
295
|
+
expect( channelmessages[ 0 ].channel ).to.equal( "open" )
|
|
296
|
+
expect( channelmessages[ 1 ].channel ).to.equal( "remote" )
|
|
297
|
+
expect( channelmessages[ 1 ].remote.port ).to.equal( 20000 )
|
|
298
|
+
expect( channelmessages[ 1 ].remote.address ).to.equal( "192.168.0.141" )
|
|
299
|
+
expect( channelmessages[ 2 ].channel ).to.equal( "open" )
|
|
300
|
+
expect( channelmessages[ 2 ].remote.port ).to.equal( 48356 )
|
|
301
|
+
expect( channelmessages[ 3 ].channel ).to.equal( "mix" )
|
|
302
|
+
expect( channelmessages[ 4 ].channel ).to.equal( "close" )
|
|
303
|
+
expect( channelmessages[ 5 ].channel ).to.equal( "close" )
|
|
304
|
+
|
|
305
|
+
expect( msginfo.body ).to.include( "UDP/TLS/RTP/SAVPF" )
|
|
306
|
+
|
|
307
|
+
connection.destroy()
|
|
308
|
+
rtpserver.destroy()
|
|
309
|
+
} )
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
it( `Create call and send 183 - early - SAVPF and 200 ok`, async () => {
|
|
313
|
+
|
|
314
|
+
/*
|
|
315
|
+
Phone (SAVPF) BV Gateway
|
|
316
|
+
|---------INVITE------>| |(1)
|
|
317
|
+
| |---------INVITE------>|(2)
|
|
318
|
+
| |<--------183 (w-sdp)--|(3)
|
|
319
|
+
|<--------183 (w-sdp)--| |(4)
|
|
320
|
+
| |<--------200 (w-sdp)--|(5)
|
|
321
|
+
|<--------200 (w-sdp)--| |(6)
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
/* Setup the mock RTP server */
|
|
325
|
+
let srfscenario = new srf.srfscenario( { savpf: true } )
|
|
326
|
+
let rtpserver = callmanager.projectrtp.proxy.listen()
|
|
327
|
+
|
|
328
|
+
let connection = net.createConnection( 9002, "127.0.0.1" )
|
|
329
|
+
.on( "error", ( e ) => {
|
|
330
|
+
console.error( e )
|
|
331
|
+
} )
|
|
332
|
+
|
|
333
|
+
connection.on( "connect", () => {
|
|
334
|
+
/* announce our node */
|
|
335
|
+
connection.write( projectrtpmessage.createmessage( {"status":{"channel":{"available":5000,"current":0},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}} ) )
|
|
336
|
+
} )
|
|
337
|
+
|
|
338
|
+
let mixing
|
|
339
|
+
let messagestate = projectrtpmessage.newstate()
|
|
340
|
+
let channelmessages = []
|
|
341
|
+
let opencount = 0
|
|
342
|
+
|
|
343
|
+
connection.on( "data", ( data ) => {
|
|
344
|
+
projectrtpmessage.parsemessage( messagestate, data, ( msg ) => {
|
|
345
|
+
try{
|
|
346
|
+
channelmessages.push( msg )
|
|
347
|
+
if( "open" === msg.channel ) {
|
|
348
|
+
if( 0 == opencount ) {
|
|
349
|
+
setTimeout( () =>
|
|
350
|
+
connection.write(
|
|
351
|
+
projectrtpmessage.createmessage(
|
|
352
|
+
{"local":{"port":10008,"dtls":
|
|
353
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
354
|
+
"address":"192.168.0.141"},
|
|
355
|
+
"id": msg.id,
|
|
356
|
+
"uuid": uuidv4(),
|
|
357
|
+
"action":"open",
|
|
358
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
359
|
+
} ) ), 2 )
|
|
360
|
+
} else {
|
|
361
|
+
setTimeout( () =>
|
|
362
|
+
connection.write(
|
|
363
|
+
projectrtpmessage.createmessage(
|
|
364
|
+
{"local":{"port": 10010,"dtls":
|
|
365
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
366
|
+
"address":"192.168.0.141"},
|
|
367
|
+
"id": msg.id,
|
|
368
|
+
"uuid": uuidv4(),
|
|
369
|
+
"action":"open",
|
|
370
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
371
|
+
} ) ), 2 )
|
|
372
|
+
}
|
|
373
|
+
opencount++
|
|
374
|
+
} else if ( "close" === msg.channel ) {
|
|
375
|
+
connection.write( projectrtpmessage.createmessage( {"id": msg.id,"uuid":msg.uuid,"action":"close","reason":"requested","stats":{"in":{"mos":4.5,"count":586,"dropped":0,"skip":0},"out":{"count":303,"skip":0},"tick":{"meanus":124,"maxus":508,"count":597}}} ) )
|
|
376
|
+
} else if ( "mix" === msg.channel ) {
|
|
377
|
+
mixing = true
|
|
378
|
+
}
|
|
379
|
+
} catch( e ) {
|
|
380
|
+
console.error( e )
|
|
381
|
+
}
|
|
382
|
+
} )
|
|
383
|
+
} )
|
|
384
|
+
|
|
385
|
+
/* ensure we are connected */
|
|
386
|
+
await new Promise( ( r ) => setTimeout( () => r(), 100 ) )
|
|
387
|
+
|
|
388
|
+
let req = new srf.req( { savpf: true } )
|
|
389
|
+
req.setparsedheader( "contact", [ {
|
|
390
|
+
name: undefined,
|
|
391
|
+
uri: 'sip:u3s2etdo@pc3lfsq1oh86.invalid;transport=ws;ob',
|
|
392
|
+
params: {}
|
|
393
|
+
}
|
|
394
|
+
] )
|
|
395
|
+
|
|
396
|
+
srfscenario.oncreateUAC( async ( contact, options, callbacks ) => {
|
|
397
|
+
|
|
398
|
+
/* Step 3. This is the mocked gateway message back to our newcall. */
|
|
399
|
+
callbacks.cbProvisional( {
|
|
400
|
+
"status": 183,
|
|
401
|
+
"msg": {
|
|
402
|
+
"body": `v=0
|
|
403
|
+
o=- 1608235282228 0 IN IP4 127.0.0.1
|
|
404
|
+
s=
|
|
405
|
+
c=IN IP4 192.168.0.141
|
|
406
|
+
t=0 0
|
|
407
|
+
m=audio 20000 RTP/AVP 0 101
|
|
408
|
+
a=rtpmap:101 telephone-event/8000
|
|
409
|
+
a=fmtp:101 0-16
|
|
410
|
+
a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
411
|
+
}
|
|
412
|
+
} )
|
|
413
|
+
|
|
414
|
+
await new Promise( ( r ) => setTimeout( () => r(), 100 ) )
|
|
415
|
+
return new srf.dialog( req )
|
|
416
|
+
} )
|
|
417
|
+
|
|
418
|
+
/* Step 1. Phone sends INVITE */
|
|
419
|
+
let call = await new Promise( ( resolve ) => {
|
|
420
|
+
srfscenario.oncall( async ( call ) => { resolve( call ) } )
|
|
421
|
+
srfscenario.inbound( req )
|
|
422
|
+
} )
|
|
423
|
+
|
|
424
|
+
/* Step 4. BV sends 183 back to phone */
|
|
425
|
+
let msgsent, msginfo
|
|
426
|
+
srfscenario.res.onsend( ( c, i ) => {
|
|
427
|
+
msgsent = c
|
|
428
|
+
msginfo = i
|
|
429
|
+
} )
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
/* Step 2. New INVITE to the remote Gateway */
|
|
433
|
+
let newcall = await call.newuac( { "contact": "callto" } )
|
|
434
|
+
|
|
435
|
+
await new Promise( ( r ) => setTimeout( () => r(), 500 ) )
|
|
436
|
+
await call._onhangup( "wire" )
|
|
437
|
+
|
|
438
|
+
expect( newcall.state.early ).to.be.true
|
|
439
|
+
expect( call.state.early ).to.be.true
|
|
440
|
+
expect( mixing ).to.be.true
|
|
441
|
+
expect( msgsent ).to.equal( 183 )
|
|
442
|
+
|
|
443
|
+
expect( channelmessages[ 0 ].channel ).to.equal( "open" )
|
|
444
|
+
expect( channelmessages[ 1 ].channel ).to.equal( "remote" )
|
|
445
|
+
expect( channelmessages[ 1 ].remote.port ).to.equal( 20000 )
|
|
446
|
+
expect( channelmessages[ 2 ].channel ).to.equal( "open" )
|
|
447
|
+
expect( channelmessages[ 2 ].remote.port ).to.equal( 48356 )
|
|
448
|
+
expect( channelmessages[ 2 ].remote.address ).to.equal( "82.19.206.102" )
|
|
449
|
+
expect( channelmessages[ 3 ].channel ).to.equal( "mix" )
|
|
450
|
+
|
|
451
|
+
expect( channelmessages[ 7 ].channel ).to.equal( "close" )
|
|
452
|
+
expect( channelmessages[ 8 ].channel ).to.equal( "close" )
|
|
453
|
+
|
|
454
|
+
expect( msginfo.body ).to.include( "UDP/TLS/RTP/SAVPF" )
|
|
455
|
+
|
|
98
456
|
connection.destroy()
|
|
99
457
|
rtpserver.destroy()
|
|
100
458
|
} )
|
package/test/mock/srf.js
CHANGED
|
@@ -86,13 +86,103 @@ a=fmtp:97 mode=20
|
|
|
86
86
|
a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
87
87
|
]
|
|
88
88
|
|
|
89
|
+
let possiblesavpsdp = [
|
|
90
|
+
`v=0
|
|
91
|
+
o=- 6278233949897424941 2 IN IP4 127.0.0.1
|
|
92
|
+
s=-
|
|
93
|
+
t=0 0
|
|
94
|
+
a=group:BUNDLE 0
|
|
95
|
+
a=extmap-allow-mixed
|
|
96
|
+
a=msid-semantic: WMS Aq3uReW2RJFKkrlg942QblHcszboGZx9dhvK
|
|
97
|
+
m=audio 48356 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
|
|
98
|
+
c=IN IP4 82.19.206.102
|
|
99
|
+
a=rtcp:9 IN IP4 0.0.0.0
|
|
100
|
+
a=candidate:79019993 1 udp 1686052607 82.19.206.102 48356 typ srflx raddr 172.17.0.1 rport 48356 generation 0 network-id 1
|
|
101
|
+
a=ice-ufrag:K8PO
|
|
102
|
+
a=ice-pwd:3RU9dMh3eDbYsfJIyQ4ki5va
|
|
103
|
+
a=ice-options:trickle
|
|
104
|
+
a=fingerprint:sha-256 73:D5:EC:C0:BF:A7:CA:42:1B:DE:B6:EA:CE:B9:D0:32:38:65:50:4A:27:BF:58:9A:DF:24:B2:10:53:58:88:B9
|
|
105
|
+
a=setup:actpass
|
|
106
|
+
a=mid:0
|
|
107
|
+
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
|
108
|
+
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
|
109
|
+
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
|
110
|
+
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
|
111
|
+
a=sendrecv
|
|
112
|
+
a=msid:Aq3uReW2RJFKkrlg942QblHcszboGZx9dhvK eaa81692-2187-4303-8e59-ed1bcb9591ee
|
|
113
|
+
a=rtcp-mux
|
|
114
|
+
a=rtpmap:111 opus/48000/2
|
|
115
|
+
a=rtcp-fb:111 transport-cc
|
|
116
|
+
a=fmtp:111 minptime=10;useinbandfec=1
|
|
117
|
+
a=rtpmap:63 red/48000/2
|
|
118
|
+
a=fmtp:63 111/111
|
|
119
|
+
a=rtpmap:103 ISAC/16000
|
|
120
|
+
a=rtpmap:104 ISAC/32000
|
|
121
|
+
a=rtpmap:9 G722/8000
|
|
122
|
+
a=rtpmap:0 PCMU/8000
|
|
123
|
+
a=rtpmap:8 PCMA/8000
|
|
124
|
+
a=rtpmap:106 CN/32000
|
|
125
|
+
a=rtpmap:105 CN/16000
|
|
126
|
+
a=rtpmap:13 CN/8000
|
|
127
|
+
a=rtpmap:110 telephone-event/48000
|
|
128
|
+
a=rtpmap:112 telephone-event/32000
|
|
129
|
+
a=rtpmap:113 telephone-event/16000
|
|
130
|
+
a=rtpmap:126 telephone-event/8000
|
|
131
|
+
a=ssrc:3789235955 cname:k129XMgcWznC/heR
|
|
132
|
+
a=ssrc:3789235955 msid:Aq3uReW2RJFKkrlg942QblHcszboGZx9dhvK eaa81692-2187-4303-8e59-ed1bcb9591ee`.replace(/(\r\n|\n|\r)/gm, "\r\n"),
|
|
133
|
+
`v=0
|
|
134
|
+
o=- 6278233949897424941 2 IN IP4 127.0.0.1
|
|
135
|
+
s=-
|
|
136
|
+
t=0 0
|
|
137
|
+
a=group:BUNDLE 0
|
|
138
|
+
a=extmap-allow-mixed
|
|
139
|
+
a=msid-semantic: WMS Aq3uReW2RJFKkrlg942QblHcszboGZx9dhvK
|
|
140
|
+
m=audio 30000 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
|
|
141
|
+
c=IN IP4 8.8.8.8
|
|
142
|
+
a=rtcp:9 IN IP4 0.0.0.0
|
|
143
|
+
a=candidate:79019993 1 udp 1686052607 8.8.8.8 30000 typ srflx raddr 172.17.0.1 rport 30000 generation 0 network-id 1
|
|
144
|
+
a=ice-ufrag:K8PO
|
|
145
|
+
a=ice-pwd:3RU9dMh3eDbYsfJIyQ4ki5va
|
|
146
|
+
a=ice-options:trickle
|
|
147
|
+
a=fingerprint:sha-256 73:D5:EC:C0:BF:A7:CA:42:1B:DE:B6:EA:CE:B9:D0:32:38:65:50:4A:27:BF:58:9A:DF:24:B2:10:53:58:88:B9
|
|
148
|
+
a=setup:actpass
|
|
149
|
+
a=mid:0
|
|
150
|
+
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
|
151
|
+
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
|
152
|
+
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
|
153
|
+
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
|
154
|
+
a=sendrecv
|
|
155
|
+
a=msid:Aq3uReW2RJFKkrlg942QblHcszboGZx9dhvK eaa81692-2187-4303-8e59-ed1bcb9591ee
|
|
156
|
+
a=rtcp-mux
|
|
157
|
+
a=rtpmap:111 opus/48000/2
|
|
158
|
+
a=rtcp-fb:111 transport-cc
|
|
159
|
+
a=fmtp:111 minptime=10;useinbandfec=1
|
|
160
|
+
a=rtpmap:63 red/48000/2
|
|
161
|
+
a=fmtp:63 111/111
|
|
162
|
+
a=rtpmap:103 ISAC/16000
|
|
163
|
+
a=rtpmap:104 ISAC/32000
|
|
164
|
+
a=rtpmap:9 G722/8000
|
|
165
|
+
a=rtpmap:0 PCMU/8000
|
|
166
|
+
a=rtpmap:8 PCMA/8000
|
|
167
|
+
a=rtpmap:106 CN/32000
|
|
168
|
+
a=rtpmap:105 CN/16000
|
|
169
|
+
a=rtpmap:13 CN/8000
|
|
170
|
+
a=rtpmap:110 telephone-event/48000
|
|
171
|
+
a=rtpmap:112 telephone-event/32000
|
|
172
|
+
a=rtpmap:113 telephone-event/16000
|
|
173
|
+
a=rtpmap:126 telephone-event/8000
|
|
174
|
+
a=ssrc:3789235955 cname:k129XMgcWznC/heR
|
|
175
|
+
a=ssrc:3789235955 msid:Aq3uReW2RJFKkrlg942QblHcszboGZx9dhvK eaa81692-2187-4303-8e59-ed1bcb9591ee`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
|
|
89
179
|
let sdpid = 0
|
|
90
180
|
|
|
91
181
|
/*
|
|
92
182
|
Mock req object
|
|
93
183
|
*/
|
|
94
184
|
class req {
|
|
95
|
-
constructor( options ) {
|
|
185
|
+
constructor( options = {} ) {
|
|
96
186
|
this.parsedheaders = {}
|
|
97
187
|
this.headers = {}
|
|
98
188
|
|
|
@@ -109,8 +199,14 @@ class req {
|
|
|
109
199
|
|
|
110
200
|
this.callbacks = {}
|
|
111
201
|
|
|
202
|
+
let sdp = possiblesdp[ sdpid % possiblesdp.length ]
|
|
203
|
+
|
|
204
|
+
if( options.savpf ) {
|
|
205
|
+
sdp = possiblesavpsdp[ sdpid % possiblesavpsdp.length ]
|
|
206
|
+
}
|
|
207
|
+
|
|
112
208
|
this.msg = {
|
|
113
|
-
"body":
|
|
209
|
+
"body": sdp,
|
|
114
210
|
method: "INVITE"
|
|
115
211
|
}
|
|
116
212
|
sdpid++
|
|
@@ -326,7 +422,7 @@ is bad testing.
|
|
|
326
422
|
Our setup of the test and our mock objects need to look simple and are easily explainable.
|
|
327
423
|
*/
|
|
328
424
|
class srfscenario {
|
|
329
|
-
constructor( options ) {
|
|
425
|
+
constructor( options = {} ) {
|
|
330
426
|
/* every scenario we restart spd */
|
|
331
427
|
sdpid = 0
|
|
332
428
|
|
|
@@ -342,6 +438,11 @@ class srfscenario {
|
|
|
342
438
|
"uassdp": possiblesdp[ 1 ]
|
|
343
439
|
}
|
|
344
440
|
|
|
441
|
+
if( options.savpf ) {
|
|
442
|
+
defaultoptions.uacsdp = possiblesavpsdp[ 0 ]
|
|
443
|
+
defaultoptions.uassdp = possiblesavpsdp[ 1 ]
|
|
444
|
+
}
|
|
445
|
+
|
|
345
446
|
this.options = { ...defaultoptions, ...options }
|
|
346
447
|
this.options.srf = new srf()
|
|
347
448
|
|
|
@@ -373,9 +474,13 @@ class srfscenario {
|
|
|
373
474
|
/*
|
|
374
475
|
simulate a new inbound call
|
|
375
476
|
*/
|
|
376
|
-
inbound() {
|
|
477
|
+
inbound( ureq ) {
|
|
377
478
|
if( this.callbacks.call ) {
|
|
378
|
-
|
|
479
|
+
if( ureq ) {
|
|
480
|
+
this.req = ureq
|
|
481
|
+
} else {
|
|
482
|
+
this.req = new req( new options() )
|
|
483
|
+
}
|
|
379
484
|
this.res = new res()
|
|
380
485
|
|
|
381
486
|
let newcall = call.frominvite( this.req, this.res )
|