@babblevoice/babble-drachtio-callmanager 2.3.21 → 2.3.23
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 +132 -156
- package/lib/sdp.js +85 -4
- package/package.json +1 -1
- package/test/interface/call.js +1 -2
- package/test/interface/callsdp.js +3 -3
- package/test/interface/sdp.js +43 -11
package/lib/call.js
CHANGED
|
@@ -334,6 +334,8 @@ class call {
|
|
|
334
334
|
* @property { boolean } [ rfc2833 ] if set to true enable 2833
|
|
335
335
|
* @property { string } [ display ] how the user should be displayed
|
|
336
336
|
* @property { Array } [ headers ]
|
|
337
|
+
* @property { string } [ contactparams]
|
|
338
|
+
* @property { number | true } [ autoanswer ] autoanswer true or delay
|
|
337
339
|
* @property { number } [ uactimeout ] timeout in mS
|
|
338
340
|
* @property { boolean } [ late ] - do we use late negotiation
|
|
339
341
|
* @property { boolean } [ noAck ] do not use - converts our options into Drachtio options
|
|
@@ -1019,39 +1021,14 @@ class call {
|
|
|
1019
1021
|
} )
|
|
1020
1022
|
}
|
|
1021
1023
|
|
|
1022
|
-
/* A simple implimentation if we are offered candidates */
|
|
1023
|
-
static async _parsesdpcandidates( target, sdp ) {
|
|
1024
|
-
|
|
1025
|
-
if( Array.isArray( sdp.media[ 0 ].candidates ) ) {
|
|
1026
|
-
let candidates = sdp.media[ 0 ].candidates
|
|
1027
|
-
if( 0 < candidates.length ) {
|
|
1028
|
-
if( callmanager.options.ignoreipv6candidates ) {
|
|
1029
|
-
candidates = candidates.filter( ( c ) => {
|
|
1030
|
-
const ismatch = ipv6regex.test( c.ip )
|
|
1031
|
-
return !ismatch
|
|
1032
|
-
} )
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
candidates.sort( ( l, r ) => { return r.priority - l.priority } )
|
|
1036
|
-
target.port = candidates[ 0 ].port
|
|
1037
|
-
|
|
1038
|
-
await new Promise( ( resolve ) => {
|
|
1039
|
-
dns.lookup( candidates[ 0 ].ip, ( err, result ) => {
|
|
1040
|
-
if( !err ) target.address = result
|
|
1041
|
-
resolve()
|
|
1042
|
-
} )
|
|
1043
|
-
} )
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
1024
|
async #answerparent() {
|
|
1049
1025
|
if( this.parent ) {
|
|
1050
1026
|
if( !this.parent.established ) {
|
|
1051
|
-
|
|
1052
|
-
.
|
|
1053
|
-
|
|
1054
|
-
|
|
1027
|
+
try {
|
|
1028
|
+
await this.parent.answer()
|
|
1029
|
+
} catch( e ) {
|
|
1030
|
+
console.trace( e )
|
|
1031
|
+
}
|
|
1055
1032
|
|
|
1056
1033
|
if( !this.parent.established ) {
|
|
1057
1034
|
return this.hangup( hangupcodes.USER_GONE )
|
|
@@ -1091,35 +1068,19 @@ class call {
|
|
|
1091
1068
|
this._addevents( this._dialog )
|
|
1092
1069
|
|
|
1093
1070
|
this.sdp.remote = sdpgen.create( this._dialog.remote.sdp )
|
|
1094
|
-
|
|
1095
|
-
if( !this.
|
|
1071
|
+
|
|
1072
|
+
if( !this.sdp.remote.intersection( this.options.preferedcodecs, true ) ) {
|
|
1096
1073
|
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1097
1074
|
}
|
|
1098
1075
|
|
|
1099
|
-
|
|
1100
|
-
if( !target ) return
|
|
1101
|
-
|
|
1102
|
-
let channeldef
|
|
1103
|
-
if( this._iswebrtc ) {
|
|
1104
|
-
let actpass = "active"
|
|
1105
|
-
if( "active" == this.sdp.remote.sdp.media[ 0 ].setup ) actpass = "passive" /* act|pass|actpass */
|
|
1106
|
-
|
|
1107
|
-
await call._parsesdpcandidates( target, this.sdp.remote.sdp )
|
|
1108
|
-
|
|
1109
|
-
channeldef = call._createchannelremotedef(
|
|
1110
|
-
target.address,
|
|
1111
|
-
target.port,
|
|
1112
|
-
target.audio.payloads[ 0 ],
|
|
1113
|
-
this.sdp.remote.sdp.media[ 0 ].fingerprint.hash,
|
|
1114
|
-
actpass ).remote
|
|
1115
|
-
} else {
|
|
1116
|
-
channeldef = call._createchannelremotedef( target.address, target.port, target.audio.payloads[ 0 ] ).remote
|
|
1117
|
-
}
|
|
1076
|
+
this.sdp.remote.setdynamepayloadtypes( this.sdp.local )
|
|
1118
1077
|
|
|
1119
|
-
|
|
1078
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1079
|
+
if( !channeldef )
|
|
1080
|
+
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1120
1081
|
|
|
1082
|
+
this.channels.audio.remote( channeldef.remote )
|
|
1121
1083
|
await this.#answerparent()
|
|
1122
|
-
|
|
1123
1084
|
return this
|
|
1124
1085
|
}
|
|
1125
1086
|
|
|
@@ -1132,38 +1093,24 @@ class call {
|
|
|
1132
1093
|
*/
|
|
1133
1094
|
async _onlatebridge() {
|
|
1134
1095
|
|
|
1135
|
-
/* Calculate the best codec for both legs - find a common codec if possible
|
|
1136
|
-
if not - transcode */
|
|
1137
1096
|
this.sdp.remote = sdpgen.create( this._req.msg.body )
|
|
1138
1097
|
|
|
1139
|
-
|
|
1140
|
-
if( !
|
|
1098
|
+
const selectedcodec = this.sdp.remote.intersection( this.options.preferedcodecs, true )
|
|
1099
|
+
if( !selectedcodec ) {
|
|
1141
1100
|
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1142
1101
|
}
|
|
1143
1102
|
|
|
1144
|
-
const
|
|
1145
|
-
if( !
|
|
1146
|
-
|
|
1147
|
-
if( this._iswebrtc ) {
|
|
1148
|
-
let actpass = "active"
|
|
1149
|
-
if( "act" == this.sdp.remote.sdp.media[ 0 ].setup ) actpass = "passive" /* act|pass|actpass */
|
|
1150
|
-
|
|
1151
|
-
channeldef = call._createchannelremotedef(
|
|
1152
|
-
target.address,
|
|
1153
|
-
target.port,
|
|
1154
|
-
target.audio.payloads[ 0 ],
|
|
1155
|
-
this.sdp.remote.sdp.media[ 0 ].fingerprint.hash,
|
|
1156
|
-
actpass )
|
|
1157
|
-
} else {
|
|
1158
|
-
channeldef = call._createchannelremotedef( target.address, target.port, target.audio.payloads[ 0 ] )
|
|
1159
|
-
}
|
|
1103
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1104
|
+
if( !channeldef )
|
|
1105
|
+
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1160
1106
|
|
|
1161
1107
|
await this.#openrelatedchannel()
|
|
1162
1108
|
|
|
1163
1109
|
this.sdp.local = sdpgen.create()
|
|
1164
|
-
.addcodecs(
|
|
1110
|
+
.addcodecs( selectedcodec )
|
|
1165
1111
|
.setconnectionaddress( this.channels.audio.local.address )
|
|
1166
1112
|
.setaudioport( this.channels.audio.local.port )
|
|
1113
|
+
.setdynamepayloadtypes( this.sdp.remote )
|
|
1167
1114
|
|
|
1168
1115
|
if( true === this.options.rfc2833 ) {
|
|
1169
1116
|
this.sdp.local.addcodecs( "2833" )
|
|
@@ -1476,9 +1423,10 @@ class call {
|
|
|
1476
1423
|
await this.#openrelatedchannel( channeldef )
|
|
1477
1424
|
|
|
1478
1425
|
this.sdp.local = sdpgen.create()
|
|
1479
|
-
.addcodecs( this.
|
|
1426
|
+
.addcodecs( this.sdp.remote.selected.name )
|
|
1480
1427
|
.setconnectionaddress( this.channels.audio.local.address )
|
|
1481
1428
|
.setaudioport( this.channels.audio.local.port )
|
|
1429
|
+
.setdynamepayloadtypes( this.sdp.remote )
|
|
1482
1430
|
|
|
1483
1431
|
this.#checkandadd2833()
|
|
1484
1432
|
|
|
@@ -1498,30 +1446,14 @@ class call {
|
|
|
1498
1446
|
*/
|
|
1499
1447
|
async #choosecodecforanswer() {
|
|
1500
1448
|
if( this._req.msg && this._req.msg.body ) {
|
|
1501
|
-
this.selectedcodec = this.sdp.remote.intersection( this.options.preferedcodecs, true )
|
|
1502
|
-
if( !this.selectedcodec ) {
|
|
1503
|
-
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1504
|
-
}
|
|
1505
1449
|
|
|
1506
|
-
this.sdp.remote.
|
|
1507
|
-
|
|
1508
|
-
const remoteaudio = this.sdp.remote.getaudio()
|
|
1509
|
-
if( !remoteaudio ) {
|
|
1450
|
+
if( !this.sdp.remote.intersection( this.options.preferedcodecs, true ) ) {
|
|
1510
1451
|
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1511
1452
|
}
|
|
1512
|
-
|
|
1513
|
-
await call._parsesdpcandidates( remoteaudio, this.sdp.remote.sdp )
|
|
1514
|
-
|
|
1515
|
-
const channeldef = call._createchannelremotedef( remoteaudio.address, remoteaudio.port, remoteaudio.audio.payloads[ 0 ] )
|
|
1516
1453
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
"mode": "passive" === this.sdp.remote.sdp.media[ 0 ].setup?"active":"passive" /* prefer passive for us */
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
channeldef.remote.icepwd = this.sdp.remote.sdp.media[ 0 ].icePwd
|
|
1524
|
-
}
|
|
1454
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1455
|
+
if( !channeldef )
|
|
1456
|
+
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1525
1457
|
|
|
1526
1458
|
/* We might have already opened our audio when we received 183 (early). */
|
|
1527
1459
|
await this.#openchannelsforanswer( channeldef )
|
|
@@ -1669,6 +1601,7 @@ class call {
|
|
|
1669
1601
|
} )
|
|
1670
1602
|
|
|
1671
1603
|
this._timers.anyevent = setTimeout( () => {
|
|
1604
|
+
|
|
1672
1605
|
const r = this._promises.resolve.channelevent
|
|
1673
1606
|
this._promises.promise.channelevent = undefined
|
|
1674
1607
|
this._promises.resolve.channelevent = undefined
|
|
@@ -1861,7 +1794,7 @@ class call {
|
|
|
1861
1794
|
}
|
|
1862
1795
|
|
|
1863
1796
|
/**
|
|
1864
|
-
Send out modified SDP to get the audio to the new location.
|
|
1797
|
+
Send out pre-modified SDP to get the audio to the new location.
|
|
1865
1798
|
This method might be use, but there are problems - so when it is used
|
|
1866
1799
|
it can be re-written but should use a mthod to create a codec - which is the
|
|
1867
1800
|
same as used elsewhere.
|
|
@@ -1869,35 +1802,22 @@ class call {
|
|
|
1869
1802
|
*/
|
|
1870
1803
|
async reinvite() {
|
|
1871
1804
|
|
|
1872
|
-
this.sdp.local = sdpgen.create().addcodecs( this.selectedcodec )
|
|
1873
|
-
this.sdp.local.setaudioport( this.channels.audio.local.port )
|
|
1874
|
-
.setconnectionaddress( this.channels.audio.local.address )
|
|
1875
|
-
|
|
1876
|
-
this.#checkandadd2833()
|
|
1877
|
-
|
|
1878
|
-
/* DTLS is only supported ( outbound ) on websocket connections */
|
|
1879
|
-
if( this._iswebrtc ) {
|
|
1880
|
-
this.sdp.local
|
|
1881
|
-
.addssrc( this.channels.audio.local.ssrc )
|
|
1882
|
-
.secure( this.channels.audio.local.dtls.fingerprint ,"passive" )
|
|
1883
|
-
.addicecandidates( this.channels.audio.local.address, this.channels.audio.local.port, this.channels.audio.local.icepwd )
|
|
1884
|
-
.rtcpmux()
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
1805
|
const remotesdp = await this._dialog.modify( this.sdp.local.toString() )
|
|
1888
1806
|
.catch( ( e ) => {
|
|
1889
1807
|
console.trace( e )
|
|
1890
1808
|
} )
|
|
1809
|
+
|
|
1810
|
+
if( this.destroyed ) return
|
|
1891
1811
|
|
|
1892
|
-
if( remotesdp )
|
|
1812
|
+
if( remotesdp ) {
|
|
1893
1813
|
this.sdp.remote = sdpgen.create( remotesdp )
|
|
1814
|
+
this.sdp.remote.setdynamepayloadtypes( this.sdp.local )
|
|
1815
|
+
}
|
|
1894
1816
|
|
|
1895
|
-
const
|
|
1896
|
-
if(
|
|
1897
|
-
this.channels.audio.remote(
|
|
1817
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1818
|
+
if( channeldef ) /* we should always get in here */
|
|
1819
|
+
this.channels.audio.remote( channeldef.remote )
|
|
1898
1820
|
|
|
1899
|
-
/* Now inform our RTP server also - we might need to wait until the target has completed so need a notify mechanism */
|
|
1900
|
-
this.channels.audio.direction( { "send": true, "recv": true } )
|
|
1901
1821
|
}
|
|
1902
1822
|
|
|
1903
1823
|
/**
|
|
@@ -2270,29 +2190,81 @@ class call {
|
|
|
2270
2190
|
}
|
|
2271
2191
|
|
|
2272
2192
|
/**
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2193
|
+
* Obtain IP address from SDP - including looking at candidates if required
|
|
2194
|
+
* @param { boolean } parsecandidates - check the candidates in sdp also
|
|
2195
|
+
* @returns { Promise< object | undefined > }
|
|
2196
|
+
*/
|
|
2197
|
+
async #getremotetarget( parsecandidates ) {
|
|
2198
|
+
|
|
2199
|
+
const target = this.sdp.remote.getaudio()
|
|
2200
|
+
if( !target ) return
|
|
2201
|
+
if( !parsecandidates ) return target
|
|
2202
|
+
|
|
2203
|
+
const sdp = this.sdp.remote.sdp
|
|
2204
|
+
|
|
2205
|
+
if( Array.isArray( sdp.media[ 0 ].candidates ) ) {
|
|
2206
|
+
let candidates = sdp.media[ 0 ].candidates
|
|
2207
|
+
if( 0 < candidates.length ) {
|
|
2208
|
+
if( callmanager.options.ignoreipv6candidates ) {
|
|
2209
|
+
candidates = candidates.filter( ( c ) => {
|
|
2210
|
+
const ismatch = ipv6regex.test( c.ip )
|
|
2211
|
+
return !ismatch
|
|
2212
|
+
} )
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
candidates.sort( ( l, r ) => { return r.priority - l.priority } )
|
|
2216
|
+
target.port = candidates[ 0 ].port
|
|
2217
|
+
|
|
2218
|
+
await new Promise( ( resolve ) => {
|
|
2219
|
+
dns.lookup( candidates[ 0 ].ip, ( err, result ) => {
|
|
2220
|
+
if( !err ) target.address = result
|
|
2221
|
+
resolve()
|
|
2222
|
+
} )
|
|
2223
|
+
} )
|
|
2287
2224
|
}
|
|
2288
2225
|
}
|
|
2289
2226
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2227
|
+
return target
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* Returns and object we can pass into an openchannel function,
|
|
2232
|
+
* { remote: {} } - the remote can be passed into set remote on
|
|
2233
|
+
* an already open channel.
|
|
2234
|
+
* @returns { Promise< object | undefined > }
|
|
2235
|
+
*/
|
|
2236
|
+
async #createchannelremotedef() {
|
|
2237
|
+
|
|
2238
|
+
const iswebrtc = this._iswebrtc
|
|
2239
|
+
const target = await this.#getremotetarget( iswebrtc )
|
|
2240
|
+
|
|
2241
|
+
if( !target ) return
|
|
2242
|
+
const address = target.address
|
|
2243
|
+
const port = target.port
|
|
2244
|
+
|
|
2245
|
+
const codec = this.sdp.remote.selected
|
|
2246
|
+
if( !codec ) return
|
|
2247
|
+
|
|
2248
|
+
const chandef = {
|
|
2249
|
+
"remote": { address, port, codec: codec.pt }
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
/* dynamic payload types */
|
|
2253
|
+
const dpts = this.sdp.remote.getdynamicpayloadtypes()
|
|
2254
|
+
if( "rfc2833" in dpts ) chandef.remote.rfc2833pt = dpts.rfc2833.payload
|
|
2255
|
+
if( "ilbc" in dpts ) chandef.remote.ilbcpt = dpts.ilbc.payload
|
|
2256
|
+
|
|
2257
|
+
if( iswebrtc ) {
|
|
2258
|
+
|
|
2259
|
+
const hash = this.sdp.remote.sdp.media[ 0 ].fingerprint.hash
|
|
2260
|
+
let mode = "active"
|
|
2261
|
+
if( "active" == this.sdp.remote.sdp.media[ 0 ].setup ) mode = "passive" /* act|pass|actpass */
|
|
2262
|
+
|
|
2263
|
+
if( hash ) {
|
|
2264
|
+
chandef.remote.dtls = {
|
|
2265
|
+
"fingerprint": { hash },
|
|
2266
|
+
mode
|
|
2267
|
+
}
|
|
2296
2268
|
}
|
|
2297
2269
|
}
|
|
2298
2270
|
return chandef
|
|
@@ -2340,11 +2312,11 @@ class call {
|
|
|
2340
2312
|
this._promises.resolve.auth = undefined
|
|
2341
2313
|
if( authreject ) authreject( new SipError( hangupcodes.REQUEST_TIMEOUT, "Auth timed out (cleanup)" ) )
|
|
2342
2314
|
|
|
2343
|
-
const chanev = this._promises.
|
|
2315
|
+
const chanev = this._promises.resolve.channelevent
|
|
2344
2316
|
this._promises.resolve.channelevent = undefined
|
|
2345
2317
|
this._promises.reject.channelevent = undefined
|
|
2346
2318
|
this._promises.promise.channelevent = undefined
|
|
2347
|
-
if( chanev ) chanev(
|
|
2319
|
+
if( chanev ) chanev()
|
|
2348
2320
|
|
|
2349
2321
|
const resolves = []
|
|
2350
2322
|
for ( const [ key, value ] of Object.entries( this._promises.resolve ) ) {
|
|
@@ -2729,8 +2701,9 @@ class call {
|
|
|
2729
2701
|
|
|
2730
2702
|
await this.#openrelatedchannel( channeldef )
|
|
2731
2703
|
|
|
2732
|
-
this.sdp.local = sdpgen.create()
|
|
2733
|
-
|
|
2704
|
+
this.sdp.local = sdpgen.create()
|
|
2705
|
+
.addcodecs( this.options.preferedcodecs )
|
|
2706
|
+
.setaudioport( this.channels.audio.local.port )
|
|
2734
2707
|
.setconnectionaddress( this.channels.audio.local.address )
|
|
2735
2708
|
|
|
2736
2709
|
this.#checkandadd2833()
|
|
@@ -2761,8 +2734,6 @@ class call {
|
|
|
2761
2734
|
return this
|
|
2762
2735
|
}
|
|
2763
2736
|
|
|
2764
|
-
this.sdp.remote = sdpgen.create( this._dialog.remote.sdp )
|
|
2765
|
-
|
|
2766
2737
|
if( this.parent ) {
|
|
2767
2738
|
for( const child of this.parent.children ) {
|
|
2768
2739
|
if( child.uuid !== this.uuid && !child.state.established ) {
|
|
@@ -2860,19 +2831,19 @@ class call {
|
|
|
2860
2831
|
|
|
2861
2832
|
/**
|
|
2862
2833
|
*
|
|
2863
|
-
* @param { object }
|
|
2864
|
-
* @param {
|
|
2834
|
+
* @param { object } u
|
|
2835
|
+
* @param { string } u.user
|
|
2836
|
+
* @param { string } u.realm
|
|
2865
2837
|
*/
|
|
2866
|
-
#configautoanswerfornewuac(
|
|
2867
|
-
|
|
2868
|
-
if( !options.contactparams ) options.contactparams = ""
|
|
2838
|
+
#configautoanswerfornewuac( u ) {
|
|
2869
2839
|
|
|
2870
|
-
if(
|
|
2840
|
+
if( !this.options.contactparams ) this.options.contactparams = ""
|
|
2841
|
+
if( true === this.options.autoanswer ) {
|
|
2871
2842
|
this.options.headers[ "Call-Info" ] = `<sip:${u.user}@${u.realm}>;answer-after=0`
|
|
2872
|
-
options.contactparams += ";intercom=true"
|
|
2873
|
-
} else if ( "number" == typeof options.autoanswer ) {
|
|
2874
|
-
this.options.headers[ "Call-Info" ] = `<sip:${u.user}@${u.realm}>;answer-after=${options.autoanswer}`
|
|
2875
|
-
options.contactparams += ";intercom=true"
|
|
2843
|
+
this.options.contactparams += ";intercom=true"
|
|
2844
|
+
} else if ( "number" == typeof this.options.autoanswer ) {
|
|
2845
|
+
this.options.headers[ "Call-Info" ] = `<sip:${u.user}@${u.realm}>;answer-after=${this.options.autoanswer}`
|
|
2846
|
+
this.options.contactparams += ";intercom=true"
|
|
2876
2847
|
}
|
|
2877
2848
|
}
|
|
2878
2849
|
|
|
@@ -2886,6 +2857,7 @@ class call {
|
|
|
2886
2857
|
this.propagate.headers[ header ] = options.headers[ header ]
|
|
2887
2858
|
}
|
|
2888
2859
|
}
|
|
2860
|
+
if ( options.autoanswer ) this.propagate.autoanswer = options.autoanswer
|
|
2889
2861
|
}
|
|
2890
2862
|
|
|
2891
2863
|
/**
|
|
@@ -2898,6 +2870,9 @@ class call {
|
|
|
2898
2870
|
this.options.headers[ header ] = this.parent.propagate.headers[ header ]
|
|
2899
2871
|
}
|
|
2900
2872
|
}
|
|
2873
|
+
if( "autoanswer" in this.parent.propagate ) {
|
|
2874
|
+
this.options.autoanswer = this.parent.propagate.autoanswer
|
|
2875
|
+
}
|
|
2901
2876
|
}
|
|
2902
2877
|
}
|
|
2903
2878
|
|
|
@@ -2922,7 +2897,7 @@ class call {
|
|
|
2922
2897
|
@param { string } [ options.auth.password ] - If SIP auth required password
|
|
2923
2898
|
@param { object } [ options.headers ] - Object containing extra sip headers required.
|
|
2924
2899
|
@param { object } [ options.uactimeout ] - override the deault timeout
|
|
2925
|
-
@param {
|
|
2900
|
+
@param { true | number } [ options.autoanswer ] - if true add call-info to auto answer, if number delay to add
|
|
2926
2901
|
@param { boolean } [ options.late ] - late negotiation
|
|
2927
2902
|
@param { boolean } [ options.privacy ] - sets the privacy
|
|
2928
2903
|
@param { entity } [ options.entity ] - used to store this call against and look up a contact string if not supplied.
|
|
@@ -2972,7 +2947,6 @@ class call {
|
|
|
2972
2947
|
if( options.privacy ) this.privacy = true
|
|
2973
2948
|
|
|
2974
2949
|
const u = newcall.#configcalleridfornewuac( options )
|
|
2975
|
-
newcall.#configautoanswerfornewuac( options, u )
|
|
2976
2950
|
|
|
2977
2951
|
|
|
2978
2952
|
// Polycom
|
|
@@ -2987,12 +2961,13 @@ class call {
|
|
|
2987
2961
|
newcall.options.headers = tmpheaders
|
|
2988
2962
|
newcall.import()
|
|
2989
2963
|
|
|
2964
|
+
newcall.#configautoanswerfornewuac( u )
|
|
2990
2965
|
newcall.#confignetwork( options )
|
|
2991
2966
|
await newcall.#openchannelsfornewuac()
|
|
2992
2967
|
|
|
2993
2968
|
let newdialog
|
|
2994
2969
|
try {
|
|
2995
|
-
newdialog = await callmanager.options.srf.createUAC( options.contact + options.contactparams, newcall.options, {
|
|
2970
|
+
newdialog = await callmanager.options.srf.createUAC( options.contact + newcall.options.contactparams, newcall.options, {
|
|
2996
2971
|
cbRequest: ( err, req ) => {
|
|
2997
2972
|
|
|
2998
2973
|
if( !req ) {
|
|
@@ -3144,6 +3119,7 @@ class call {
|
|
|
3144
3119
|
|
|
3145
3120
|
if( c._req.msg && c._req.msg.body ) {
|
|
3146
3121
|
c.sdp.remote = sdpgen.create( c._req.msg.body )
|
|
3122
|
+
c.sdp.remote.intersection( callmanager.options.preferedcodecs, true )
|
|
3147
3123
|
}
|
|
3148
3124
|
|
|
3149
3125
|
/* set a timer using the default */
|
package/lib/sdp.js
CHANGED
|
@@ -11,6 +11,15 @@ const crypto = require( "crypto" )
|
|
|
11
11
|
*/
|
|
12
12
|
let sessionidcounter = Math.floor( Math.random() * 100000 )
|
|
13
13
|
|
|
14
|
+
|
|
15
|
+
const prtpcodecpts = {
|
|
16
|
+
"pcmu": 0,
|
|
17
|
+
"pcma": 8,
|
|
18
|
+
"g722": 9,
|
|
19
|
+
"ilbc": 97,
|
|
20
|
+
"2833": 101
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
class codecconv {
|
|
15
24
|
|
|
16
25
|
#pt2name = {
|
|
@@ -174,6 +183,7 @@ function getconfigforpt( audio, pt ) {
|
|
|
174
183
|
class sdp {
|
|
175
184
|
|
|
176
185
|
#dynamicpts
|
|
186
|
+
#selected
|
|
177
187
|
|
|
178
188
|
constructor( sdp ) {
|
|
179
189
|
|
|
@@ -296,8 +306,8 @@ class sdp {
|
|
|
296
306
|
if ( m ) {
|
|
297
307
|
|
|
298
308
|
let payloads = m.payloads
|
|
299
|
-
if ( this
|
|
300
|
-
payloads = [ this
|
|
309
|
+
if ( this.#selected !== undefined ) {
|
|
310
|
+
payloads = [ this.#selected ]
|
|
301
311
|
}
|
|
302
312
|
|
|
303
313
|
let address
|
|
@@ -353,13 +363,31 @@ class sdp {
|
|
|
353
363
|
select( codec ) {
|
|
354
364
|
if ( isNaN( codec ) ) {
|
|
355
365
|
if ( undefined === this.#dynamicpts.hascodec( codec ) ) return
|
|
356
|
-
codec = this.#dynamicpts.
|
|
366
|
+
codec = this.#dynamicpts.getpt( codec )
|
|
357
367
|
}
|
|
358
|
-
this
|
|
368
|
+
this.#selected = codec
|
|
359
369
|
|
|
360
370
|
return this
|
|
361
371
|
}
|
|
362
372
|
|
|
373
|
+
/**
|
|
374
|
+
* @returns { object | undefined }
|
|
375
|
+
* @property { string } name - the name of the codec - i.e. pcma
|
|
376
|
+
* @property { number } pt - the payload type (static) used for prtp
|
|
377
|
+
* @property { number } dpt - the dynamic payload type negotiated for this session
|
|
378
|
+
*/
|
|
379
|
+
get selected() {
|
|
380
|
+
|
|
381
|
+
if( undefined === this.#selected ) return undefined
|
|
382
|
+
|
|
383
|
+
const name = this.#dynamicpts.getcodec( this.#selected )
|
|
384
|
+
return {
|
|
385
|
+
name,
|
|
386
|
+
pt: prtpcodecpts[ name ],
|
|
387
|
+
dpt: this.#selected
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
363
391
|
static create( from ) {
|
|
364
392
|
return new sdp( from )
|
|
365
393
|
}
|
|
@@ -619,6 +647,59 @@ class sdp {
|
|
|
619
647
|
|
|
620
648
|
}
|
|
621
649
|
|
|
650
|
+
/**
|
|
651
|
+
* @returns { object } an object of codec name to payload type
|
|
652
|
+
* i.e.
|
|
653
|
+
* {
|
|
654
|
+
* "ilbc": { payload: 101, codec: "iLBC", rate: 8000 }
|
|
655
|
+
* }
|
|
656
|
+
* NB: it only returns a) our supported codecs and b) dynamic codecs - pcma, pcmu, g722 are statically defined
|
|
657
|
+
*/
|
|
658
|
+
getdynamicpayloadtypes() {
|
|
659
|
+
const retval = {}
|
|
660
|
+
|
|
661
|
+
this.sdp.media.forEach( ( v ) => {
|
|
662
|
+
if( "rtp" in v ) {
|
|
663
|
+
v.rtp.forEach( r => {
|
|
664
|
+
const cname = r.codec.toLowerCase()
|
|
665
|
+
switch( cname ) {
|
|
666
|
+
case "ilbc":
|
|
667
|
+
if( 8000 == r.rate )
|
|
668
|
+
retval[ cname ] = r
|
|
669
|
+
break
|
|
670
|
+
case "telephone-event":
|
|
671
|
+
if( 8000 == r.rate )
|
|
672
|
+
retval[ "rfc2833" ] = r
|
|
673
|
+
}
|
|
674
|
+
} )
|
|
675
|
+
}
|
|
676
|
+
} )
|
|
677
|
+
|
|
678
|
+
return retval
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Takes an object as returned by getdynamicpayloadtypes on another object
|
|
683
|
+
* to set the dynameic payloadtypes on this object
|
|
684
|
+
* @param { sdp } othersdp
|
|
685
|
+
*/
|
|
686
|
+
setdynamepayloadtypes( othersdp ) {
|
|
687
|
+
|
|
688
|
+
if( !othersdp ) return this
|
|
689
|
+
|
|
690
|
+
const o = othersdp.getdynamicpayloadtypes()
|
|
691
|
+
|
|
692
|
+
if( "ilbc" in o && 8000 == o.ilbc.rate ) {
|
|
693
|
+
this.#dynamicpts.setdynamicpt( "ilbc", "ilbc", o.ilbc.payload )
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if( "rfc2833" in o && 8000 == o.rfc2833.rate ) {
|
|
697
|
+
this.#dynamicpts.setdynamicpt( "2833", "telephone-event", o.rfc2833.payload )
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return this
|
|
701
|
+
}
|
|
702
|
+
|
|
622
703
|
toString() {
|
|
623
704
|
|
|
624
705
|
/* We need to convert payloads back to string to stop a , being added */
|
package/package.json
CHANGED
package/test/interface/call.js
CHANGED
|
@@ -858,7 +858,6 @@ describe( "call object", function() {
|
|
|
858
858
|
"late": true
|
|
859
859
|
}
|
|
860
860
|
|
|
861
|
-
const earlycallbackcalled = false
|
|
862
861
|
const c = await call.newuac( options )
|
|
863
862
|
|
|
864
863
|
/* mock */
|
|
@@ -1015,7 +1014,7 @@ describe( "call object", function() {
|
|
|
1015
1014
|
const c = await call.newuac( options )
|
|
1016
1015
|
|
|
1017
1016
|
let eventfired = false
|
|
1018
|
-
c.on( "call.pick", ( callobject ) => {
|
|
1017
|
+
c.on( "call.pick", ( /*callobject*/ ) => {
|
|
1019
1018
|
eventfired = true
|
|
1020
1019
|
} )
|
|
1021
1020
|
|
|
@@ -18,16 +18,16 @@ describe( "call sdp generation", function() {
|
|
|
18
18
|
let callnumber = 0
|
|
19
19
|
const callcount = 6
|
|
20
20
|
const codecsselected = []
|
|
21
|
-
|
|
21
|
+
await new Promise( ( resolve ) => {
|
|
22
22
|
srfscenario.oncall( async ( call ) => {
|
|
23
23
|
await call.answer()
|
|
24
24
|
|
|
25
|
-
codecsselected.push( call.
|
|
25
|
+
codecsselected.push( call.sdp.remote.selected.name )
|
|
26
26
|
callnumber++
|
|
27
27
|
|
|
28
28
|
call.hangup()
|
|
29
29
|
|
|
30
|
-
if( callcount == callnumber )
|
|
30
|
+
if( callcount == callnumber ) resolve()
|
|
31
31
|
} )
|
|
32
32
|
|
|
33
33
|
for( let i = 0; i < callcount; i++ ) {
|
package/test/interface/sdp.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const expect = require( "chai" ).expect
|
|
4
4
|
const sdp = require( "../../lib/sdp" )
|
|
5
|
-
const call = require( "../../lib/call" )
|
|
6
|
-
const callmanager = require( "../../index" )
|
|
7
5
|
|
|
8
6
|
describe( "sdp", function() {
|
|
9
7
|
|
|
@@ -325,11 +323,9 @@ a=rtpmap:8 PCMA/8000`
|
|
|
325
323
|
const oursdp = sdp.create( testsdp )
|
|
326
324
|
const remoteaudio = oursdp.getaudio()
|
|
327
325
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
expect(
|
|
331
|
-
expect( def.remote.port ).to.equal( 48380 )
|
|
332
|
-
expect( def.remote.codec ).to.equal( 8 )
|
|
326
|
+
expect( remoteaudio.address ).to.equal( "213.166.4.136" )
|
|
327
|
+
expect( remoteaudio.port ).to.equal( 48380 )
|
|
328
|
+
expect( remoteaudio.audio.payloads[ 0 ] ).to.equal( 8 )
|
|
333
329
|
} )
|
|
334
330
|
|
|
335
331
|
it( "another real life example", async function() {
|
|
@@ -686,11 +682,11 @@ a=rtpmap:126 telephone-event/8000
|
|
|
686
682
|
a=ssrc:222390620 cname:q7Is0hRbTrTbcMJM
|
|
687
683
|
a=ssrc:222390620 msid:a46039f4-1857-410e-b1cc-215c09878068 ce8c9c25-2ea0-4079-aaa5-54f771d53310
|
|
688
684
|
`
|
|
689
|
-
|
|
685
|
+
|
|
690
686
|
const sdpobj = sdp.create( sdpstr )
|
|
691
|
-
const target =
|
|
692
|
-
|
|
693
|
-
sdpobj.intersection( "g722", true )
|
|
687
|
+
const target = sdpobj.getaudio()
|
|
688
|
+
|
|
689
|
+
expect( sdpobj.intersection( "g722", true ) ).to.equal( "g722" )
|
|
694
690
|
|
|
695
691
|
/* our default is to ignore IPv6 addresses (until projectrtp supports it) */
|
|
696
692
|
expect( target.port ).to.equal( 41645 )
|
|
@@ -799,9 +795,45 @@ a=rtpmap:127 telephone-event/8000`
|
|
|
799
795
|
|
|
800
796
|
const sdpilbc = sdp.create( ilbcpt )
|
|
801
797
|
|
|
798
|
+
const expectdps = {
|
|
799
|
+
ilbc: { payload: 110, codec: "iLBC", rate: 8000 },
|
|
800
|
+
rfc2833: { payload: 127, codec: "telephone-event", rate: 8000 }
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
expect( sdpilbc.getdynamicpayloadtypes() ).to.deep.equal( expectdps )
|
|
804
|
+
|
|
802
805
|
expect( sdpilbc.has( "ilbc" ) ).to.be.true
|
|
803
806
|
expect( sdpilbc.has( "pcma" ) ).to.be.true
|
|
804
807
|
expect( sdpilbc.has( "pcmu" ) ).to.be.true
|
|
805
808
|
expect( sdpilbc.has( "g722" ) ).to.be.true
|
|
809
|
+
|
|
810
|
+
const othersdp = sdp.create().addcodecs( "ilbc 2833" ).setdynamepayloadtypes( sdpilbc )
|
|
811
|
+
|
|
812
|
+
expect( othersdp.toString() ).to.match( new RegExp( `v=0
|
|
813
|
+
o=- \\d+ 0 IN IP4 127.0.0.1
|
|
814
|
+
s=project
|
|
815
|
+
c=IN IP4 127.0.0.1
|
|
816
|
+
t=0 0
|
|
817
|
+
m=audio 0 RTP/AVP 97 101
|
|
818
|
+
a=rtpmap:110 ilbc/8000
|
|
819
|
+
a=rtpmap:127 telephone-event/8000
|
|
820
|
+
a=fmtp:110 mode=20
|
|
821
|
+
a=fmtp:127 0-16
|
|
822
|
+
a=ptime:20
|
|
823
|
+
a=sendrecv
|
|
824
|
+
`.replace( /\r\n/g, "\n" ).replace( /\n/g, "\r\n" ) ) )
|
|
825
|
+
|
|
826
|
+
sdpilbc.select( "pcma" )
|
|
827
|
+
|
|
828
|
+
const audio = sdpilbc.getaudio()
|
|
829
|
+
|
|
830
|
+
expect( audio.port ).to.equal( 63000 )
|
|
831
|
+
expect( audio.address ).to.equal( "82.19.206.102" )
|
|
832
|
+
expect( audio.audio.payloads[ 0 ] ).to.equal( 8 )
|
|
833
|
+
|
|
834
|
+
expect( sdpilbc.selected ).to.deep.equal( { name: "pcma", pt: 8, dpt: 8 } )
|
|
835
|
+
sdpilbc.select( "ilbc" )
|
|
836
|
+
expect( sdpilbc.selected ).to.deep.equal( { name: "ilbc", pt: 97, dpt: 110 } )
|
|
837
|
+
|
|
806
838
|
} )
|
|
807
839
|
} )
|