@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 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
- await this.parent.answer()
1052
- .catch( ( err ) => {
1053
- console.trace( err )
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
- this.selectedcodec = this.sdp.remote.intersection( this.options.preferedcodecs, true )
1095
- if( !this.selectedcodec ) {
1071
+
1072
+ if( !this.sdp.remote.intersection( this.options.preferedcodecs, true ) ) {
1096
1073
  return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
1097
1074
  }
1098
1075
 
1099
- const target = this.sdp.remote.getaudio()
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
- this.channels.audio.remote( channeldef )
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
- this.selectedcodec = this.sdp.remote.intersection( this.options.preferedcodecs, true )
1140
- if( !this.selectedcodec ) {
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 target = this.sdp.remote.getaudio()
1145
- if( !target ) return
1146
- let channeldef
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( this.selectedcodec )
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.selectedcodec )
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.select( this.selectedcodec )
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
- 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
-
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 target = this.sdp.remote.getaudio()
1896
- if( target ) /* we should always get in here */
1897
- this.channels.audio.remote( call._createchannelremotedef( target.address, target.port, this.selectedcodec ).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
- Helper function to create a channel target definition
2274
- @private
2275
- @param { string } address - remote address (ip)
2276
- @param { number } port - remote port
2277
- @param { number } codec
2278
- @param { string } [ fingerprint ] - remote sha 256 fingerprint
2279
- @param { string } [ mode ] - "active"|"passive"
2280
- */
2281
- static _createchannelremotedef( address, port, codec, fingerprint, mode /* active|passive */ ) {
2282
- const chandef = {
2283
- "remote": {
2284
- "address": address,
2285
- "port": port,
2286
- "codec": codec
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
- if( fingerprint ) {
2291
- chandef.remote.dtls = {
2292
- "fingerprint": {
2293
- "hash": fingerprint
2294
- },
2295
- "mode": mode
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.reject.channelevent
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( Error( "Call hungup" ) )
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().addcodecs( this.options.preferedcodecs )
2733
- this.sdp.local.setaudioport( this.channels.audio.local.port )
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 } options
2864
- * @param { object } u
2834
+ * @param { object } u
2835
+ * @param { string } u.user
2836
+ * @param { string } u.realm
2865
2837
  */
2866
- #configautoanswerfornewuac( options, u ) {
2867
-
2868
- if( !options.contactparams ) options.contactparams = ""
2838
+ #configautoanswerfornewuac( u ) {
2869
2839
 
2870
- if( true === options.autoanswer ) {
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 { boolean | number } [ options.autoanswer ] - if true add call-info to auto answer, if number delay to add
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.selected !== undefined ) {
300
- payloads = [ this.selected ]
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.getcodec( codec )
366
+ codec = this.#dynamicpts.getpt( codec )
357
367
  }
358
- this.selected = Number( codec )
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "2.3.21",
3
+ "version": "2.3.23",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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
- const c = await new Promise( ( done ) => {
21
+ await new Promise( ( resolve ) => {
22
22
  srfscenario.oncall( async ( call ) => {
23
23
  await call.answer()
24
24
 
25
- codecsselected.push( call.selectedcodec )
25
+ codecsselected.push( call.sdp.remote.selected.name )
26
26
  callnumber++
27
27
 
28
28
  call.hangup()
29
29
 
30
- if( callcount == callnumber ) done()
30
+ if( callcount == callnumber ) resolve()
31
31
  } )
32
32
 
33
33
  for( let i = 0; i < callcount; i++ ) {
@@ -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
- const def = call._createchannelremotedef( remoteaudio.address, remoteaudio.port, remoteaudio.audio.payloads[ 0 ] )
329
-
330
- expect( def.remote.address ).to.equal( "213.166.4.136" )
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
- callmanager.callmanager( { srf: { use: () => {} } } )
685
+
690
686
  const sdpobj = sdp.create( sdpstr )
691
- const target = {}
692
- await call._parsesdpcandidates( target, sdpobj.sdp )
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
  } )