@babblevoice/babble-drachtio-callmanager 2.3.21 → 2.3.22
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 +113 -143
- package/lib/sdp.js +85 -4
- package/package.json +1 -1
- package/test/interface/callsdp.js +3 -3
- package/test/interface/sdp.js +43 -11
package/lib/call.js
CHANGED
|
@@ -1019,39 +1019,14 @@ class call {
|
|
|
1019
1019
|
} )
|
|
1020
1020
|
}
|
|
1021
1021
|
|
|
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
1022
|
async #answerparent() {
|
|
1049
1023
|
if( this.parent ) {
|
|
1050
1024
|
if( !this.parent.established ) {
|
|
1051
|
-
|
|
1052
|
-
.
|
|
1053
|
-
|
|
1054
|
-
|
|
1025
|
+
try {
|
|
1026
|
+
await this.parent.answer()
|
|
1027
|
+
} catch( e ) {
|
|
1028
|
+
console.trace( e )
|
|
1029
|
+
}
|
|
1055
1030
|
|
|
1056
1031
|
if( !this.parent.established ) {
|
|
1057
1032
|
return this.hangup( hangupcodes.USER_GONE )
|
|
@@ -1091,35 +1066,19 @@ class call {
|
|
|
1091
1066
|
this._addevents( this._dialog )
|
|
1092
1067
|
|
|
1093
1068
|
this.sdp.remote = sdpgen.create( this._dialog.remote.sdp )
|
|
1094
|
-
|
|
1095
|
-
if( !this.
|
|
1069
|
+
|
|
1070
|
+
if( !this.sdp.remote.intersection( this.options.preferedcodecs, true ) ) {
|
|
1096
1071
|
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1097
1072
|
}
|
|
1098
1073
|
|
|
1099
|
-
|
|
1100
|
-
if( !target ) return
|
|
1074
|
+
this.sdp.remote.setdynamepayloadtypes( this.sdp.local )
|
|
1101
1075
|
|
|
1102
|
-
|
|
1103
|
-
if(
|
|
1104
|
-
|
|
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
|
-
}
|
|
1118
|
-
|
|
1119
|
-
this.channels.audio.remote( channeldef )
|
|
1076
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1077
|
+
if( !channeldef )
|
|
1078
|
+
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1120
1079
|
|
|
1080
|
+
this.channels.audio.remote( channeldef.remote )
|
|
1121
1081
|
await this.#answerparent()
|
|
1122
|
-
|
|
1123
1082
|
return this
|
|
1124
1083
|
}
|
|
1125
1084
|
|
|
@@ -1132,38 +1091,24 @@ class call {
|
|
|
1132
1091
|
*/
|
|
1133
1092
|
async _onlatebridge() {
|
|
1134
1093
|
|
|
1135
|
-
/* Calculate the best codec for both legs - find a common codec if possible
|
|
1136
|
-
if not - transcode */
|
|
1137
1094
|
this.sdp.remote = sdpgen.create( this._req.msg.body )
|
|
1138
1095
|
|
|
1139
|
-
|
|
1140
|
-
if( !
|
|
1096
|
+
const selectedcodec = this.sdp.remote.intersection( this.options.preferedcodecs, true )
|
|
1097
|
+
if( !selectedcodec ) {
|
|
1141
1098
|
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1142
1099
|
}
|
|
1143
1100
|
|
|
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
|
-
}
|
|
1101
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1102
|
+
if( !channeldef )
|
|
1103
|
+
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1160
1104
|
|
|
1161
1105
|
await this.#openrelatedchannel()
|
|
1162
1106
|
|
|
1163
1107
|
this.sdp.local = sdpgen.create()
|
|
1164
|
-
.addcodecs(
|
|
1108
|
+
.addcodecs( selectedcodec )
|
|
1165
1109
|
.setconnectionaddress( this.channels.audio.local.address )
|
|
1166
1110
|
.setaudioport( this.channels.audio.local.port )
|
|
1111
|
+
.setdynamepayloadtypes( this.sdp.remote )
|
|
1167
1112
|
|
|
1168
1113
|
if( true === this.options.rfc2833 ) {
|
|
1169
1114
|
this.sdp.local.addcodecs( "2833" )
|
|
@@ -1476,9 +1421,10 @@ class call {
|
|
|
1476
1421
|
await this.#openrelatedchannel( channeldef )
|
|
1477
1422
|
|
|
1478
1423
|
this.sdp.local = sdpgen.create()
|
|
1479
|
-
.addcodecs( this.
|
|
1424
|
+
.addcodecs( this.sdp.remote.selected.name )
|
|
1480
1425
|
.setconnectionaddress( this.channels.audio.local.address )
|
|
1481
1426
|
.setaudioport( this.channels.audio.local.port )
|
|
1427
|
+
.setdynamepayloadtypes( this.sdp.remote )
|
|
1482
1428
|
|
|
1483
1429
|
this.#checkandadd2833()
|
|
1484
1430
|
|
|
@@ -1498,30 +1444,14 @@ class call {
|
|
|
1498
1444
|
*/
|
|
1499
1445
|
async #choosecodecforanswer() {
|
|
1500
1446
|
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
1447
|
|
|
1506
|
-
this.sdp.remote.
|
|
1507
|
-
|
|
1508
|
-
const remoteaudio = this.sdp.remote.getaudio()
|
|
1509
|
-
if( !remoteaudio ) {
|
|
1448
|
+
if( !this.sdp.remote.intersection( this.options.preferedcodecs, true ) ) {
|
|
1510
1449
|
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1511
1450
|
}
|
|
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
|
-
|
|
1517
|
-
if( this._iswebrtc ) {
|
|
1518
|
-
channeldef.remote.dtls = {
|
|
1519
|
-
"fingerprint": this.sdp.remote.sdp.media[ 0 ].fingerprint,
|
|
1520
|
-
"mode": "passive" === this.sdp.remote.sdp.media[ 0 ].setup?"active":"passive" /* prefer passive for us */
|
|
1521
|
-
}
|
|
1522
1451
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1452
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1453
|
+
if( !channeldef )
|
|
1454
|
+
return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1525
1455
|
|
|
1526
1456
|
/* We might have already opened our audio when we received 183 (early). */
|
|
1527
1457
|
await this.#openchannelsforanswer( channeldef )
|
|
@@ -1669,6 +1599,7 @@ class call {
|
|
|
1669
1599
|
} )
|
|
1670
1600
|
|
|
1671
1601
|
this._timers.anyevent = setTimeout( () => {
|
|
1602
|
+
|
|
1672
1603
|
const r = this._promises.resolve.channelevent
|
|
1673
1604
|
this._promises.promise.channelevent = undefined
|
|
1674
1605
|
this._promises.resolve.channelevent = undefined
|
|
@@ -1861,7 +1792,7 @@ class call {
|
|
|
1861
1792
|
}
|
|
1862
1793
|
|
|
1863
1794
|
/**
|
|
1864
|
-
Send out modified SDP to get the audio to the new location.
|
|
1795
|
+
Send out pre-modified SDP to get the audio to the new location.
|
|
1865
1796
|
This method might be use, but there are problems - so when it is used
|
|
1866
1797
|
it can be re-written but should use a mthod to create a codec - which is the
|
|
1867
1798
|
same as used elsewhere.
|
|
@@ -1869,35 +1800,22 @@ class call {
|
|
|
1869
1800
|
*/
|
|
1870
1801
|
async reinvite() {
|
|
1871
1802
|
|
|
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
1803
|
const remotesdp = await this._dialog.modify( this.sdp.local.toString() )
|
|
1888
1804
|
.catch( ( e ) => {
|
|
1889
1805
|
console.trace( e )
|
|
1890
1806
|
} )
|
|
1807
|
+
|
|
1808
|
+
if( this.destroyed ) return
|
|
1891
1809
|
|
|
1892
|
-
if( remotesdp )
|
|
1810
|
+
if( remotesdp ) {
|
|
1893
1811
|
this.sdp.remote = sdpgen.create( remotesdp )
|
|
1812
|
+
this.sdp.remote.setdynamepayloadtypes( this.sdp.local )
|
|
1813
|
+
}
|
|
1894
1814
|
|
|
1895
|
-
const
|
|
1896
|
-
if(
|
|
1897
|
-
this.channels.audio.remote(
|
|
1815
|
+
const channeldef = await this.#createchannelremotedef()
|
|
1816
|
+
if( channeldef ) /* we should always get in here */
|
|
1817
|
+
this.channels.audio.remote( channeldef.remote )
|
|
1898
1818
|
|
|
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
1819
|
}
|
|
1902
1820
|
|
|
1903
1821
|
/**
|
|
@@ -2270,29 +2188,81 @@ class call {
|
|
|
2270
2188
|
}
|
|
2271
2189
|
|
|
2272
2190
|
/**
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2191
|
+
* Obtain IP address from SDP - including looking at candidates if required
|
|
2192
|
+
* @param { boolean } parsecandidates - check the candidates in sdp also
|
|
2193
|
+
* @returns { Promise< object | undefined > }
|
|
2194
|
+
*/
|
|
2195
|
+
async #getremotetarget( parsecandidates ) {
|
|
2196
|
+
|
|
2197
|
+
const target = this.sdp.remote.getaudio()
|
|
2198
|
+
if( !target ) return
|
|
2199
|
+
if( !parsecandidates ) return target
|
|
2200
|
+
|
|
2201
|
+
const sdp = this.sdp.remote.sdp
|
|
2202
|
+
|
|
2203
|
+
if( Array.isArray( sdp.media[ 0 ].candidates ) ) {
|
|
2204
|
+
let candidates = sdp.media[ 0 ].candidates
|
|
2205
|
+
if( 0 < candidates.length ) {
|
|
2206
|
+
if( callmanager.options.ignoreipv6candidates ) {
|
|
2207
|
+
candidates = candidates.filter( ( c ) => {
|
|
2208
|
+
const ismatch = ipv6regex.test( c.ip )
|
|
2209
|
+
return !ismatch
|
|
2210
|
+
} )
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
candidates.sort( ( l, r ) => { return r.priority - l.priority } )
|
|
2214
|
+
target.port = candidates[ 0 ].port
|
|
2215
|
+
|
|
2216
|
+
await new Promise( ( resolve ) => {
|
|
2217
|
+
dns.lookup( candidates[ 0 ].ip, ( err, result ) => {
|
|
2218
|
+
if( !err ) target.address = result
|
|
2219
|
+
resolve()
|
|
2220
|
+
} )
|
|
2221
|
+
} )
|
|
2287
2222
|
}
|
|
2288
2223
|
}
|
|
2289
2224
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2225
|
+
return target
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
/**
|
|
2229
|
+
* Returns and object we can pass into an openchannel function,
|
|
2230
|
+
* { remote: {} } - the remote can be passed into set remote on
|
|
2231
|
+
* an already open channel.
|
|
2232
|
+
* @returns { Promise< object | undefined > }
|
|
2233
|
+
*/
|
|
2234
|
+
async #createchannelremotedef() {
|
|
2235
|
+
|
|
2236
|
+
const iswebrtc = this._iswebrtc
|
|
2237
|
+
const target = await this.#getremotetarget( iswebrtc )
|
|
2238
|
+
|
|
2239
|
+
if( !target ) return
|
|
2240
|
+
const address = target.address
|
|
2241
|
+
const port = target.port
|
|
2242
|
+
|
|
2243
|
+
const codec = this.sdp.remote.selected
|
|
2244
|
+
if( !codec ) return
|
|
2245
|
+
|
|
2246
|
+
const chandef = {
|
|
2247
|
+
"remote": { address, port, codec: codec.pt }
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/* dynamic payload types */
|
|
2251
|
+
const dpts = this.sdp.remote.getdynamicpayloadtypes()
|
|
2252
|
+
if( "rfc2833" in dpts ) chandef.remote.rfc2833pt = dpts.rfc2833.payload
|
|
2253
|
+
if( "ilbc" in dpts ) chandef.remote.ilbcpt = dpts.ilbc.payload
|
|
2254
|
+
|
|
2255
|
+
if( iswebrtc ) {
|
|
2256
|
+
|
|
2257
|
+
const hash = this.sdp.remote.sdp.media[ 0 ].fingerprint.hash
|
|
2258
|
+
let mode = "active"
|
|
2259
|
+
if( "active" == this.sdp.remote.sdp.media[ 0 ].setup ) mode = "passive" /* act|pass|actpass */
|
|
2260
|
+
|
|
2261
|
+
if( hash ) {
|
|
2262
|
+
chandef.remote.dtls = {
|
|
2263
|
+
"fingerprint": { hash },
|
|
2264
|
+
mode
|
|
2265
|
+
}
|
|
2296
2266
|
}
|
|
2297
2267
|
}
|
|
2298
2268
|
return chandef
|
|
@@ -2340,11 +2310,11 @@ class call {
|
|
|
2340
2310
|
this._promises.resolve.auth = undefined
|
|
2341
2311
|
if( authreject ) authreject( new SipError( hangupcodes.REQUEST_TIMEOUT, "Auth timed out (cleanup)" ) )
|
|
2342
2312
|
|
|
2343
|
-
const chanev = this._promises.
|
|
2313
|
+
const chanev = this._promises.resolve.channelevent
|
|
2344
2314
|
this._promises.resolve.channelevent = undefined
|
|
2345
2315
|
this._promises.reject.channelevent = undefined
|
|
2346
2316
|
this._promises.promise.channelevent = undefined
|
|
2347
|
-
if( chanev ) chanev(
|
|
2317
|
+
if( chanev ) chanev()
|
|
2348
2318
|
|
|
2349
2319
|
const resolves = []
|
|
2350
2320
|
for ( const [ key, value ] of Object.entries( this._promises.resolve ) ) {
|
|
@@ -2729,8 +2699,9 @@ class call {
|
|
|
2729
2699
|
|
|
2730
2700
|
await this.#openrelatedchannel( channeldef )
|
|
2731
2701
|
|
|
2732
|
-
this.sdp.local = sdpgen.create()
|
|
2733
|
-
|
|
2702
|
+
this.sdp.local = sdpgen.create()
|
|
2703
|
+
.addcodecs( this.options.preferedcodecs )
|
|
2704
|
+
.setaudioport( this.channels.audio.local.port )
|
|
2734
2705
|
.setconnectionaddress( this.channels.audio.local.address )
|
|
2735
2706
|
|
|
2736
2707
|
this.#checkandadd2833()
|
|
@@ -2761,8 +2732,6 @@ class call {
|
|
|
2761
2732
|
return this
|
|
2762
2733
|
}
|
|
2763
2734
|
|
|
2764
|
-
this.sdp.remote = sdpgen.create( this._dialog.remote.sdp )
|
|
2765
|
-
|
|
2766
2735
|
if( this.parent ) {
|
|
2767
2736
|
for( const child of this.parent.children ) {
|
|
2768
2737
|
if( child.uuid !== this.uuid && !child.state.established ) {
|
|
@@ -3144,6 +3113,7 @@ class call {
|
|
|
3144
3113
|
|
|
3145
3114
|
if( c._req.msg && c._req.msg.body ) {
|
|
3146
3115
|
c.sdp.remote = sdpgen.create( c._req.msg.body )
|
|
3116
|
+
c.sdp.remote.intersection( callmanager.options.preferedcodecs, true )
|
|
3147
3117
|
}
|
|
3148
3118
|
|
|
3149
3119
|
/* 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
|
@@ -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
|
} )
|