@babblevoice/babble-drachtio-callmanager 3.3.3 → 3.3.5
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 +77 -31
- package/package.json +1 -1
- package/test/interface/early.js +143 -2
package/lib/call.js
CHANGED
|
@@ -218,7 +218,8 @@ class call {
|
|
|
218
218
|
"audio": undefined,
|
|
219
219
|
"closed": {
|
|
220
220
|
"audio": []
|
|
221
|
-
}
|
|
221
|
+
},
|
|
222
|
+
"count": 0
|
|
222
223
|
}
|
|
223
224
|
|
|
224
225
|
/**
|
|
@@ -1717,13 +1718,20 @@ class call {
|
|
|
1717
1718
|
this.channels.closed.audio.push( e )
|
|
1718
1719
|
}
|
|
1719
1720
|
|
|
1720
|
-
this.channels.
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1721
|
+
this.channels.count--
|
|
1722
|
+
|
|
1723
|
+
if ( 0 === this.channels.count ) {
|
|
1724
|
+
this.channels.audio = undefined
|
|
1725
|
+
|
|
1726
|
+
if( this._state._onhangup ) {
|
|
1727
|
+
this._cleanup()
|
|
1728
|
+
return
|
|
1729
|
+
}
|
|
1725
1730
|
|
|
1726
|
-
|
|
1731
|
+
// This will handle _cleanup() later
|
|
1732
|
+
// based on the above flag
|
|
1733
|
+
this.hangup()
|
|
1734
|
+
}
|
|
1727
1735
|
}
|
|
1728
1736
|
|
|
1729
1737
|
/**
|
|
@@ -2010,9 +2018,13 @@ class call {
|
|
|
2010
2018
|
same as used elsewhere. If a bond has been attached to the call, we will
|
|
2011
2019
|
try to reuse an existing channel.
|
|
2012
2020
|
@param { object } [ channeldef ]
|
|
2021
|
+
@param { object } [ newchannel ]
|
|
2013
2022
|
@private
|
|
2014
2023
|
*/
|
|
2015
|
-
async reinvite( channeldef ) {
|
|
2024
|
+
async reinvite( channeldef, newchannel ) {
|
|
2025
|
+
|
|
2026
|
+
if ( newchannel )
|
|
2027
|
+
this.channels.audio = newchannel
|
|
2016
2028
|
|
|
2017
2029
|
if ( !channeldef )
|
|
2018
2030
|
channeldef = await this.#createchannelremotedef()
|
|
@@ -2045,17 +2057,40 @@ class call {
|
|
|
2045
2057
|
const channeldef = await othercall.#createchannelremotedef()
|
|
2046
2058
|
|
|
2047
2059
|
const oldchannel = othercall.channels.audio
|
|
2048
|
-
|
|
2049
|
-
othercall.channels.audio = await this.channels.audio.openchannel( channeldef, othercall._handlechannelevents.bind( othercall ) )
|
|
2060
|
+
const newchannel = await this.#openchannel( channeldef, othercall )
|
|
2050
2061
|
|
|
2051
|
-
await othercall.bond( this ).reinvite( channeldef )
|
|
2062
|
+
await othercall.bond( this ).reinvite( channeldef, newchannel )
|
|
2052
2063
|
|
|
2053
|
-
await oldchannel.
|
|
2064
|
+
await oldchannel.unmix()
|
|
2065
|
+
oldchannel.close()
|
|
2054
2066
|
}
|
|
2055
2067
|
|
|
2056
2068
|
await this.channels.audio.mix( othercall.channels.audio )
|
|
2057
2069
|
}
|
|
2058
2070
|
|
|
2071
|
+
/**
|
|
2072
|
+
Mix two calls. If the two calls are on a different node
|
|
2073
|
+
the second call is bonded and reinvited.
|
|
2074
|
+
@param { object } channeldef - our call object which is early
|
|
2075
|
+
@param { call } [ bindcall ] - the call which will own the channel
|
|
2076
|
+
*/
|
|
2077
|
+
async #openchannel( channeldef, bindcall ) {
|
|
2078
|
+
|
|
2079
|
+
if ( bindcall && bindcall.channels.audio ) {
|
|
2080
|
+
const chan = await bindcall.channels.audio.openchannel(
|
|
2081
|
+
channeldef, this._handlechannelevents.bind( this ) )
|
|
2082
|
+
|
|
2083
|
+
this.channels.count++
|
|
2084
|
+
return chan
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
const chan = await projectrtp.openchannel(
|
|
2088
|
+
channeldef, this._handlechannelevents.bind( this ) )
|
|
2089
|
+
this.channels.count++
|
|
2090
|
+
|
|
2091
|
+
return chan
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2059
2094
|
/**
|
|
2060
2095
|
*
|
|
2061
2096
|
* @param { object } req
|
|
@@ -2156,20 +2191,32 @@ class call {
|
|
|
2156
2191
|
RTP stall timer will kick in first, but if a call is placed on hold followed
|
|
2157
2192
|
by AWOL... */
|
|
2158
2193
|
this._timers.seinterval = setInterval( async () => {
|
|
2159
|
-
const opts = {
|
|
2160
|
-
"method": "INVITE",
|
|
2161
|
-
"body": this.sdp.local.toString()
|
|
2162
|
-
}
|
|
2163
2194
|
|
|
2164
|
-
|
|
2165
|
-
.catch( ( e ) => {
|
|
2166
|
-
console.trace( e )
|
|
2167
|
-
this.hangup( hangupcodes.USER_GONE )
|
|
2168
|
-
} )
|
|
2195
|
+
try{
|
|
2169
2196
|
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2197
|
+
if( this.destroyed ) {
|
|
2198
|
+
/* this should be done - but we are still running */
|
|
2199
|
+
clearInterval( this._timers.seinterval )
|
|
2200
|
+
return
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
if( "function" != typeof dialog.request ) return
|
|
2204
|
+
|
|
2205
|
+
const opts = {
|
|
2206
|
+
"method": "INVITE",
|
|
2207
|
+
"body": this.sdp.local.toString()
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
const res = await dialog.request( opts )
|
|
2211
|
+
.catch( ( e ) => {
|
|
2212
|
+
console.trace( e )
|
|
2213
|
+
this.hangup( hangupcodes.USER_GONE )
|
|
2214
|
+
} )
|
|
2215
|
+
|
|
2216
|
+
if( !this.destroyed && 200 != res.msg.status ) {
|
|
2217
|
+
this.hangup( hangupcodes.USER_GONE )
|
|
2218
|
+
}
|
|
2219
|
+
} catch( e ) { /* empty */ }
|
|
2173
2220
|
|
|
2174
2221
|
}, callmanager.options.seexpire )
|
|
2175
2222
|
|
|
@@ -2567,7 +2614,6 @@ class call {
|
|
|
2567
2614
|
@private
|
|
2568
2615
|
*/
|
|
2569
2616
|
_cleanup() {
|
|
2570
|
-
|
|
2571
2617
|
if( this.state.cleaned ) return
|
|
2572
2618
|
this.state.cleaned = true
|
|
2573
2619
|
|
|
@@ -2643,7 +2689,7 @@ class call {
|
|
|
2643
2689
|
if( this.channels.audio ) {
|
|
2644
2690
|
this.channels.audio.close()
|
|
2645
2691
|
this._timers.cleanup = setTimeout( () => {
|
|
2646
|
-
console.trace( "Timeout waiting for channel close, cleaning up anyway
|
|
2692
|
+
console.trace( this.uuid + " Timeout waiting for channel close, cleaning up anyway, chan uuid: " + this.channels.audio.uuid + ", channel count: " + this.channels.count )
|
|
2647
2693
|
this._cleanup()
|
|
2648
2694
|
}, 60 * 1000 )
|
|
2649
2695
|
|
|
@@ -2890,24 +2936,24 @@ class call {
|
|
|
2890
2936
|
|
|
2891
2937
|
if( this.channels.audio ) return
|
|
2892
2938
|
|
|
2893
|
-
const
|
|
2939
|
+
const othercall = this.other
|
|
2894
2940
|
/* TODO: this is a hack. projectrtp has become too complicated with both a listen and connect
|
|
2895
2941
|
mechanism. This is causing problems in code like this. There is no interface to
|
|
2896
2942
|
detect which mode the channel is in - but the channels property will exist on a connect
|
|
2897
2943
|
style channel. projectrtp will getrelatives a rewrite to support only one. */
|
|
2898
|
-
if(
|
|
2899
|
-
this.channels.audio = await this
|
|
2944
|
+
if( othercall && othercall.channels.audio ) {
|
|
2945
|
+
this.channels.audio = await this.#openchannel( channeldef, othercall )
|
|
2900
2946
|
return
|
|
2901
2947
|
}
|
|
2902
2948
|
|
|
2903
2949
|
for( const other of this.relatives ) {
|
|
2904
2950
|
if( other.channels && other.channels.audio ) {
|
|
2905
|
-
this.channels.audio = await
|
|
2951
|
+
this.channels.audio = await this.#openchannel( channeldef, other )
|
|
2906
2952
|
return
|
|
2907
2953
|
}
|
|
2908
2954
|
}
|
|
2909
2955
|
|
|
2910
|
-
this.channels.audio = await
|
|
2956
|
+
this.channels.audio = await this.#openchannel( channeldef )
|
|
2911
2957
|
}
|
|
2912
2958
|
|
|
2913
2959
|
/**
|
package/package.json
CHANGED
package/test/interface/early.js
CHANGED
|
@@ -20,8 +20,7 @@ describe( "call early", function() {
|
|
|
20
20
|
clearcallmanager()
|
|
21
21
|
} )
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
it( "Create call and send 183 - early basic", async () => {
|
|
23
|
+
it( "Create call and send 183 - early basic prtp - listen mode", async () => {
|
|
25
24
|
|
|
26
25
|
/*
|
|
27
26
|
Phone BV Gateway
|
|
@@ -163,6 +162,148 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
|
163
162
|
rtpserver.destroy()
|
|
164
163
|
} )
|
|
165
164
|
|
|
165
|
+
|
|
166
|
+
it( "Create call and send 183 - early basic prtp connect mode", async () => {
|
|
167
|
+
|
|
168
|
+
/*
|
|
169
|
+
Phone BV Gateway
|
|
170
|
+
|---------INVITE------>| |(1)
|
|
171
|
+
| |---------INVITE------>|(2)
|
|
172
|
+
| |<--------183 (w-sdp)--|(3)
|
|
173
|
+
|<--------183 (w-sdp)--| |(4)
|
|
174
|
+
|
|
175
|
+
Phone RTP: 192.168.0.200:18540
|
|
176
|
+
BV RTP: 192.168.0.141
|
|
177
|
+
Gateway RTP: 192.168.0.160:21000
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
/* Setup the mock RTP server */
|
|
181
|
+
const srfscenario = new srf.srfscenario()
|
|
182
|
+
const rtpserver = callmanager.projectrtp.proxy.addnode( { host: "127.0.0.1", port: 9002 } )
|
|
183
|
+
|
|
184
|
+
let connection
|
|
185
|
+
const mockrtp = net.createServer()
|
|
186
|
+
mockrtp.on( "connection", ( c ) => {
|
|
187
|
+
connection = c
|
|
188
|
+
|
|
189
|
+
connection.on( "data", ( data ) => {
|
|
190
|
+
projectrtpmessage.parsemessage( messagestate, data, ( msg ) => {
|
|
191
|
+
try{
|
|
192
|
+
channelmessages.push( msg )
|
|
193
|
+
if( "open" === msg.channel ) {
|
|
194
|
+
if( 0 == opencount ) {
|
|
195
|
+
setTimeout( () =>
|
|
196
|
+
connection.write(
|
|
197
|
+
projectrtpmessage.createmessage(
|
|
198
|
+
{"local":{"port":10008,"dtls":
|
|
199
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
200
|
+
"address":"192.168.0.141"},
|
|
201
|
+
"id": msg.id,
|
|
202
|
+
"uuid": uuidv4(),
|
|
203
|
+
"action":"open",
|
|
204
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
205
|
+
} ) ), 2 )
|
|
206
|
+
} else {
|
|
207
|
+
setTimeout( () =>
|
|
208
|
+
connection.write(
|
|
209
|
+
projectrtpmessage.createmessage(
|
|
210
|
+
{"local":{"port": 10010,"dtls":
|
|
211
|
+
{"fingerprint":"Some fingerprint","enabled":false},
|
|
212
|
+
"address":"192.168.0.141"},
|
|
213
|
+
"id": msg.id,
|
|
214
|
+
"uuid": uuidv4(),
|
|
215
|
+
"action":"open",
|
|
216
|
+
"status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
217
|
+
} ) ), 2 )
|
|
218
|
+
}
|
|
219
|
+
opencount++
|
|
220
|
+
} else if ( "close" === msg.channel ) {
|
|
221
|
+
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}}, "status":{"channel":{"available":4995,"current":5},"workercount":12,"instance":"ca0ef6a9-9174-444d-bdeb-4c9eb54d4566"}
|
|
222
|
+
} ) )
|
|
223
|
+
} else if ( "mix" === msg.channel ) {
|
|
224
|
+
mixing = true
|
|
225
|
+
}
|
|
226
|
+
} catch( e ) {
|
|
227
|
+
console.error( e )
|
|
228
|
+
}
|
|
229
|
+
} )
|
|
230
|
+
} )
|
|
231
|
+
} )
|
|
232
|
+
|
|
233
|
+
await new Promise( resolve => mockrtp.listen( 9002, resolve ) )
|
|
234
|
+
|
|
235
|
+
let mixing
|
|
236
|
+
const messagestate = projectrtpmessage.newstate()
|
|
237
|
+
const channelmessages = []
|
|
238
|
+
let opencount = 0
|
|
239
|
+
|
|
240
|
+
/* ensure we are connected */
|
|
241
|
+
await new Promise( ( resolve ) => setTimeout( () => resolve(), 100 ) )
|
|
242
|
+
|
|
243
|
+
srfscenario.oncreateUAC( async ( contact, options, callbacks ) => {
|
|
244
|
+
|
|
245
|
+
/* Step 3. This is the mocked gateway message back to our newcall. */
|
|
246
|
+
callbacks.cbProvisional( {
|
|
247
|
+
"status": 183,
|
|
248
|
+
"get": () => { return "INVITE, UPDATE, OPTIONS" },
|
|
249
|
+
"msg": {
|
|
250
|
+
"body": `v=0
|
|
251
|
+
o=- 1608235282228 0 IN IP4 127.0.0.1
|
|
252
|
+
s=
|
|
253
|
+
c=IN IP4 192.168.0.160
|
|
254
|
+
t=0 0
|
|
255
|
+
m=audio 21000 RTP/AVP 0 101
|
|
256
|
+
a=rtpmap:101 telephone-event/8000
|
|
257
|
+
a=fmtp:101 0-16
|
|
258
|
+
a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
|
|
259
|
+
}
|
|
260
|
+
} )
|
|
261
|
+
|
|
262
|
+
await new Promise( ( resolve ) => setTimeout( () => resolve(), 100 ) )
|
|
263
|
+
throw { "status": 503 }
|
|
264
|
+
} )
|
|
265
|
+
|
|
266
|
+
/* Step 1. Phone sends INVITE */
|
|
267
|
+
const call = await new Promise( ( resolve ) => {
|
|
268
|
+
srfscenario.oncall( async ( call ) => { resolve( call ) } )
|
|
269
|
+
srfscenario.inbound()
|
|
270
|
+
} )
|
|
271
|
+
|
|
272
|
+
/* Step 4. BV sends 183 back to phone */
|
|
273
|
+
let msgsent, msginfo
|
|
274
|
+
srfscenario.res.onsend( ( c, i ) => {
|
|
275
|
+
msgsent = c
|
|
276
|
+
msginfo = i
|
|
277
|
+
} )
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
/* Step 2. New INVITE to the remote Gateway */
|
|
281
|
+
const newcall = await call.newuac( { "contact": "callto" } )
|
|
282
|
+
|
|
283
|
+
await call._onhangup( "wire" )
|
|
284
|
+
expect( newcall.state.early ).to.be.true
|
|
285
|
+
expect( call.state.early ).to.be.true
|
|
286
|
+
expect( mixing ).to.be.true
|
|
287
|
+
expect( msgsent ).to.equal( 183 )
|
|
288
|
+
|
|
289
|
+
expect( channelmessages[ 0 ].channel ).to.equal( "open" )
|
|
290
|
+
expect( channelmessages[ 1 ].channel ).to.equal( "remote" )
|
|
291
|
+
expect( channelmessages[ 1 ].remote.port ).to.equal( 21000 )
|
|
292
|
+
expect( channelmessages[ 1 ].remote.address ).to.equal( "192.168.0.160" )
|
|
293
|
+
expect( channelmessages[ 2 ].channel ).to.equal( "open" )
|
|
294
|
+
expect( channelmessages[ 2 ].remote.port ).to.equal( 18540 )
|
|
295
|
+
expect( channelmessages[ 2 ].remote.address ).to.equal( "192.168.0.200" )
|
|
296
|
+
expect( channelmessages[ 3 ].channel ).to.equal( "mix" )
|
|
297
|
+
expect( channelmessages[ 4 ].channel ).to.equal( "close" )
|
|
298
|
+
expect( channelmessages[ 5 ].channel ).to.equal( "close" )
|
|
299
|
+
|
|
300
|
+
expect( msginfo.body ).to.include( "audio 10010 RTP/AVP" )
|
|
301
|
+
|
|
302
|
+
connection.destroy()
|
|
303
|
+
mockrtp.close()
|
|
304
|
+
rtpserver.destroy()
|
|
305
|
+
} )
|
|
306
|
+
|
|
166
307
|
it( "Create call and send 183 - early - SAVPF", async () => {
|
|
167
308
|
|
|
168
309
|
/*
|