@babblevoice/babble-drachtio-callmanager 3.3.2 → 3.3.4

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/jsconfig.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "module": "commonJS",
3
+ "module": "node16",
4
4
  "target": "es6",
5
- "moduleResolution": "node",
5
+ "moduleResolution": "node16",
6
6
  "checkJs": true
7
7
  },
8
8
  "include": [ "index.js", "babble/*.js", "babble/apps/*.js" ],
package/lib/call.js CHANGED
@@ -10,7 +10,7 @@ const callstore = require( "./store.js" )
10
10
 
11
11
  const sipauth = require( "@babblevoice/babble-drachtio-auth" )
12
12
 
13
- const ipv6regex = /^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$/gm
13
+ const ipv6regex = /^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/
14
14
  const parseurire = /^(sips?):(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/
15
15
  const parseuriparamsre = /([^;=]+)(=([^;=]+))?/g
16
16
  const parseuriheadersre = /[^&=]+=[^&=]+/g
@@ -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
  /**
@@ -554,9 +555,9 @@ class call {
554
555
  #fromcontact( startingpoint ) {
555
556
  const dest = parseuri( this.options.contact )
556
557
  if( !dest ) return
557
- startingpoint.uri = dest.uri
558
- startingpoint.user = dest.user
559
- startingpoint.host = dest.host
558
+ if( dest.uri ) startingpoint.uri = dest.uri
559
+ if( dest.user ) startingpoint.user = dest.user
560
+ if( dest.host ) startingpoint.host = dest.host
560
561
  }
561
562
 
562
563
  #fromdestination( startingpoint ) {
@@ -657,7 +658,6 @@ class call {
657
658
  this.#calleridforuac( startingpoint )
658
659
  } else {
659
660
  const other = this.other
660
-
661
661
  if( other ) {
662
662
  if( "uas" == other.type ) {
663
663
  other.#calleridforuas( startingpoint )
@@ -731,6 +731,9 @@ class call {
731
731
  * @param { string } c
732
732
  */
733
733
  set callerid( c ) {
734
+
735
+ if( undefined == c ) return
736
+
734
737
  if( !this.options.callerid ) this.options.callerid = {}
735
738
  if( !this.options.callerid.number ) this.options.callerid.number = ""
736
739
 
@@ -742,6 +745,8 @@ class call {
742
745
  */
743
746
  set calleridname( c ) {
744
747
 
748
+ if( undefined == c ) return
749
+
745
750
  if( !this.options.callerid ) this.options.callerid = {}
746
751
  if( !this.options.callerid.name ) this.options.callerid.name = ""
747
752
 
@@ -1713,13 +1718,20 @@ class call {
1713
1718
  this.channels.closed.audio.push( e )
1714
1719
  }
1715
1720
 
1716
- this.channels.audio = false
1717
- if( this._state._onhangup ) {
1718
- this._cleanup()
1719
- return
1720
- }
1721
+ this.channels.count--
1722
+
1723
+ if ( 0 === this.channels.count ) {
1724
+ this.channels.audio = false
1721
1725
 
1722
- this.hangup() /* ? */
1726
+ if( this._state._onhangup ) {
1727
+ this._cleanup()
1728
+ return
1729
+ }
1730
+
1731
+ // This will handle _cleanup() later
1732
+ // based on the above flag
1733
+ this.hangup()
1734
+ }
1723
1735
  }
1724
1736
 
1725
1737
  /**
@@ -2006,9 +2018,13 @@ class call {
2006
2018
  same as used elsewhere. If a bond has been attached to the call, we will
2007
2019
  try to reuse an existing channel.
2008
2020
  @param { object } [ channeldef ]
2021
+ @param { object } [ newchannel ]
2009
2022
  @private
2010
2023
  */
2011
- async reinvite( channeldef ) {
2024
+ async reinvite( channeldef, newchannel ) {
2025
+
2026
+ if ( newchannel )
2027
+ this.channels.audio = newchannel
2012
2028
 
2013
2029
  if ( !channeldef )
2014
2030
  channeldef = await this.#createchannelremotedef()
@@ -2041,17 +2057,43 @@ class call {
2041
2057
  const channeldef = await othercall.#createchannelremotedef()
2042
2058
 
2043
2059
  const oldchannel = othercall.channels.audio
2044
- // eslint-disable-next-line require-atomic-updates
2045
- othercall.channels.audio = await this.channels.audio.openchannel( channeldef, othercall._handlechannelevents.bind( othercall ) )
2060
+ const newchannel = await this.#openchannel( channeldef, othercall )
2046
2061
 
2047
- await othercall.bond( this ).reinvite( channeldef )
2062
+ await othercall.bond( this ).reinvite( channeldef, newchannel )
2048
2063
 
2049
- await oldchannel.close()
2064
+ await oldchannel.unmix()
2065
+ oldchannel.close()
2050
2066
  }
2051
2067
 
2052
2068
  await this.channels.audio.mix( othercall.channels.audio )
2053
2069
  }
2054
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
+ let chan = undefined
2080
+
2081
+ if ( !bindcall || !this.channels.audio ) {
2082
+ chan = await projectrtp.openchannel(
2083
+ channeldef, this._handlechannelevents.bind( this ) )
2084
+ } else {
2085
+ chan = await this.channels.audio.openchannel(
2086
+ channeldef, bindcall._handlechannelevents.bind( bindcall ) )
2087
+ }
2088
+
2089
+ if ( bindcall )
2090
+ bindcall.channels.count++
2091
+ else
2092
+ this.channels.count++
2093
+
2094
+ return chan
2095
+ }
2096
+
2055
2097
  /**
2056
2098
  *
2057
2099
  * @param { object } req
@@ -2152,20 +2194,32 @@ class call {
2152
2194
  RTP stall timer will kick in first, but if a call is placed on hold followed
2153
2195
  by AWOL... */
2154
2196
  this._timers.seinterval = setInterval( async () => {
2155
- const opts = {
2156
- "method": "INVITE",
2157
- "body": this.sdp.local.toString()
2158
- }
2159
2197
 
2160
- const res = await dialog.request( opts )
2161
- .catch( ( e ) => {
2162
- console.trace( e )
2163
- this.hangup( hangupcodes.USER_GONE )
2164
- } )
2198
+ try{
2165
2199
 
2166
- if( !this.destroyed && 200 != res.msg.status ) {
2167
- this.hangup( hangupcodes.USER_GONE )
2168
- }
2200
+ if( this.destroyed ) {
2201
+ /* this should be done - but we are still running */
2202
+ clearInterval( this._timers.seinterval )
2203
+ return
2204
+ }
2205
+
2206
+ if( "function" != typeof dialog.request ) return
2207
+
2208
+ const opts = {
2209
+ "method": "INVITE",
2210
+ "body": this.sdp.local.toString()
2211
+ }
2212
+
2213
+ const res = await dialog.request( opts )
2214
+ .catch( ( e ) => {
2215
+ console.trace( e )
2216
+ this.hangup( hangupcodes.USER_GONE )
2217
+ } )
2218
+
2219
+ if( !this.destroyed && 200 != res.msg.status ) {
2220
+ this.hangup( hangupcodes.USER_GONE )
2221
+ }
2222
+ } catch( e ) { /* empty */ }
2169
2223
 
2170
2224
  }, callmanager.options.seexpire )
2171
2225
 
@@ -2563,7 +2617,6 @@ class call {
2563
2617
  @private
2564
2618
  */
2565
2619
  _cleanup() {
2566
-
2567
2620
  if( this.state.cleaned ) return
2568
2621
  this.state.cleaned = true
2569
2622
 
@@ -2621,7 +2674,7 @@ class call {
2621
2674
  }
2622
2675
  this._state._onhangup = true
2623
2676
 
2624
- /* hangup our children */
2677
+ /* hangup our children but, no other relations - i.e. children of our parent */
2625
2678
  const hangups = []
2626
2679
  for( const child of this.children ) {
2627
2680
  hangups.push( child.hangup( this.hangup_cause ) )
@@ -2639,7 +2692,7 @@ class call {
2639
2692
  if( this.channels.audio ) {
2640
2693
  this.channels.audio.close()
2641
2694
  this._timers.cleanup = setTimeout( () => {
2642
- console.trace( "Timeout waiting for channel close, cleaning up anyway", this.uuid )
2695
+ console.trace( this.uuid + " Timeout waiting for channel close, cleaning up anyway, chan uuid: " + this.channels.audio.uuid + ", channel count: " + this.channels.count )
2643
2696
  this._cleanup()
2644
2697
  }, 60 * 1000 )
2645
2698
 
@@ -2704,12 +2757,19 @@ class call {
2704
2757
  * Send an UPDATE. Use to updated called id, caller id, sdp etc. Send in dialog - TODO look how to send
2705
2758
  * early as this is recomended in the RFC.
2706
2759
  */
2707
- async update() {
2760
+ async update( remote ) {
2708
2761
 
2709
2762
  /* if we are asked to update it suggests we have received new information and overrides should go */
2710
2763
  delete this.options.callerid
2711
2764
  delete this.options.calledid
2712
2765
 
2766
+ if( remote ) {
2767
+ if( remote.callerid ) {
2768
+ this.callerid = remote.callerid.number
2769
+ this.calleridname = remote.callerid.name
2770
+ }
2771
+ }
2772
+
2713
2773
  this._em.emit( "call.updated", this )
2714
2774
  callmanager.options.em.emit( "call.updated", this )
2715
2775
 
@@ -2841,11 +2901,11 @@ class call {
2841
2901
  if( callbacks.prebridge ) return callbacks.prebridge( c )
2842
2902
  }
2843
2903
 
2844
- ourcallbacks.confirm = ( c, queuedcall ) => {
2904
+ ourcallbacks.confirm = ( /** @type { call } */ c, /** @type { any } */ cookie ) => {
2845
2905
  if( false !== waitonchildrenresolve ) {
2846
2906
  waitonchildrenresolve( c )
2847
2907
  waitonchildrenresolve = false
2848
- if( callbacks.confirm ) callbacks.confirm( c, queuedcall )
2908
+ if( callbacks.confirm ) callbacks.confirm( c, cookie )
2849
2909
  }
2850
2910
  }
2851
2911
 
@@ -2885,18 +2945,18 @@ class call {
2885
2945
  detect which mode the channel is in - but the channels property will exist on a connect
2886
2946
  style channel. projectrtp will getrelatives a rewrite to support only one. */
2887
2947
  if( relatedcall && relatedcall.channels.audio && relatedcall.channels.audio.channels ) {
2888
- this.channels.audio = await this.other.channels.audio.openchannel( channeldef, this._handlechannelevents.bind( this ) )
2948
+ this.channels.audio = await this.#openchannel( channeldef, this )
2889
2949
  return
2890
2950
  }
2891
2951
 
2892
2952
  for( const other of this.relatives ) {
2893
2953
  if( other.channels && other.channels.audio ) {
2894
- this.channels.audio = await other.channels.audio.openchannel( channeldef, this._handlechannelevents.bind( this ) )
2954
+ this.channels.audio = await other.#openchannel( channeldef, this )
2895
2955
  return
2896
2956
  }
2897
2957
  }
2898
2958
 
2899
- this.channels.audio = await projectrtp.openchannel( channeldef, this._handlechannelevents.bind( this ) )
2959
+ this.channels.audio = await this.#openchannel( channeldef )
2900
2960
  }
2901
2961
 
2902
2962
  /**
@@ -2953,17 +3013,19 @@ class call {
2953
3013
  return this
2954
3014
  }
2955
3015
 
3016
+ const hangups = []
2956
3017
  if( this.parent ) {
2957
3018
  for( const child of this.parent.children ) {
2958
- if( child.uuid !== this.uuid && !child.state.established ) {
3019
+ if( child !== this ) {
2959
3020
  child.detach()
2960
3021
  /* do not await - we do not want to delay the winner in
2961
3022
  connecting by waiting for the completion of the hangups */
2962
- child.hangup( hangupcodes.LOSE_RACE )
3023
+ hangups.push( child.hangup( hangupcodes.LOSE_RACE ) )
2963
3024
  }
2964
3025
  }
2965
3026
  }
2966
3027
 
3028
+ await Promise.all( hangups )
2967
3029
  callstore.set( this )
2968
3030
 
2969
3031
  if ( this._dialog.sip )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "3.3.2",
3
+ "version": "3.3.4",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -237,6 +237,7 @@ describe( "call object", function() {
237
237
  "type": "callerid"
238
238
  } )
239
239
 
240
+ /* when newuacv returns, only one of the contacts should hacve succeeded */
240
241
  const child = await call.newuac( { "entity": { "uri": "1000@dummy" } } )
241
242
 
242
243
  expect( await callstore.stats() ).to.deep.include( {
@@ -257,7 +258,7 @@ describe( "call object", function() {
257
258
  child.update()
258
259
 
259
260
  expect( requestoptions.method ).to.equal( "UPDATE" )
260
- expect( requestoptions.headers[ "P-Asserted-Identity" ] ).to.equal( "\"\" <sip:0123456789@someotherrealm.com>" )
261
+ expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "\"\" <sip:0123456789@someotherrealm.com>;party=calling;screen=yes" )
261
262
 
262
263
  await call.hangup()
263
264
 
@@ -404,8 +405,8 @@ describe( "call object", function() {
404
405
  expect( children[ 0 ].hangup_cause.reason ).equal( "USER_BUSY" )
405
406
 
406
407
  expect( await callstore.stats() ).to.deep.include( {
407
- "storebycallid": 1,
408
- "storebyuuid": 1,
408
+ "storebycallid": 2,
409
+ "storebyuuid": 2,
409
410
  "storebyentity": 0
410
411
  } )
411
412
 
@@ -446,6 +447,9 @@ describe( "call object", function() {
446
447
  expect( children[ 0 ].hangup_cause.reason ).equal( "REQUEST_TIMEOUT" )
447
448
  expect( children[ 0 ].hangup_cause.src ).equal( "us" )
448
449
 
450
+ /* when we get here the clean up is on teh event loop */
451
+ await new Promise( resolve => setTimeout( resolve, 100 ) )
452
+
449
453
  expect( await callstore.stats() ).to.deep.include( {
450
454
  "storebycallid": 1,
451
455
  "storebyuuid": 1,
@@ -785,7 +789,7 @@ describe( "call object", function() {
785
789
 
786
790
  await call.newuac( options, { "early": ( c ) => c.hangup() } )
787
791
 
788
- expect( createuacoptions.headers[ "Remote-Party-ID" ] ).to.equal( "\"\" <sip:0000000000@localhost.localdomain>" )
792
+ expect( createuacoptions.headers[ "remote-party-id" ] ).to.equal( "\"\" <sip:0000000000@localhost.localdomain>;party=calling;screen=yes" )
789
793
  expect( createuacoptions.late ).to.be.true
790
794
  } )
791
795
 
@@ -836,17 +840,16 @@ describe( "call object", function() {
836
840
  let requestoptions
837
841
  c._dialog.on( "request", ( options ) => requestoptions = options )
838
842
 
839
- await c.update( { "remote": {
840
- "display": "Kermit",
841
- "realm": "muppetshow.com",
842
- "username": "kermy"
843
+ await c.update( { "callerid": {
844
+ "name": "Kermit",
845
+ "number": "kermy"
843
846
  } } )
844
847
 
845
848
  c.hangup()
846
849
 
847
850
  expect( requestoptions.method ).to.equal( "UPDATE" )
848
851
  expect( requestoptions.body ).to.be.a( "string" )
849
- expect( requestoptions.headers[ "P-Asserted-Identity" ] ).to.equal( "\"Kermit\" <sip:kermy@muppetshow.com>" )
852
+ expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "\"Kermit\" <sip:kermy@localhost.localdomain>;party=calling;screen=yes" )
850
853
 
851
854
  } )
852
855
 
@@ -1148,7 +1151,7 @@ describe( "call object", function() {
1148
1151
  const c = await call.newuac( options )
1149
1152
 
1150
1153
  /* no default configured */
1151
- expect( c.options.headers[ "Remote-Party-ID" ] ).to.equal( "\"Hello\" <sip:0000000000@localhost.localdomain>" )
1154
+ expect( c.options.headers[ "remote-party-id" ] ).to.equal( "\"Hello\" <sip:0000000000@localhost.localdomain>;party=calling;screen=yes" )
1152
1155
 
1153
1156
  c._onhangup( "wire" )
1154
1157
 
@@ -1169,7 +1172,7 @@ describe( "call object", function() {
1169
1172
 
1170
1173
 
1171
1174
  /* no default configured */
1172
- expect( c.options.headers[ "Remote-Party-ID" ] ).to.equal( "\"\" <sip:012345789@localhost.localdomain>" )
1175
+ expect( c.options.headers[ "remote-party-id" ] ).to.equal( "\"\" <sip:012345789@localhost.localdomain>;party=calling;screen=yes" )
1173
1176
 
1174
1177
  c._onhangup( "wire" )
1175
1178
 
@@ -3,6 +3,8 @@
3
3
  const expect = require( "chai" ).expect
4
4
  const sdp = require( "../../lib/sdp" )
5
5
 
6
+ const dns = require( "node:dns" )
7
+
6
8
  describe( "sdp", function() {
7
9
 
8
10
  it( "create new sdp object", async function() {
@@ -927,4 +929,77 @@ a=rtpmap:127 telephone-event/8000
927
929
  expect( outsdpstring ).include( outsdpstring, "a=rtpmap:110 ilbc/8000" )
928
930
 
929
931
  } )
932
+
933
+ it( "SDP IP V6 candidate", async () => {
934
+ const wsssdp = `v=0
935
+ o=- 4684977919666729506 2 IN IP4 127.0.0.1
936
+ s=-
937
+ t=0 0
938
+ a=group:BUNDLE 0
939
+ a=extmap-allow-mixed
940
+ a=msid-semantic: WMS 2e008e23-265a-42a0-ba6f-d147a9d0ca42
941
+ m=audio 28022 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126
942
+ c=IN IP4 209.35.84.57
943
+ a=rtcp:9 IN IP4 0.0.0.0
944
+ a=candidate:1446275835 1 udp 2122129151 192.168.1.108 52396 typ host generation 0 network-id 1
945
+ a=candidate:1833640547 1 udp 2122063615 172.25.160.1 52397 typ host generation 0 network-id 4
946
+ a=candidate:3642338680 1 udp 2122262783 2a01:4b00:ea24:b300:10a8:8a7e:234c:3161 52398 typ host generation 0 network-id 2
947
+ a=candidate:4227024272 1 udp 2122197247 2a01:4b00:ea24:b300:f50b:e799:7a1:8cc3 52399 typ host generation 0 network-id 3
948
+ a=candidate:2218323828 1 udp 1685921535 209.35.84.57 28022 typ srflx raddr 192.168.1.108 rport 52396 generation 0 network-id 1
949
+ a=ice-ufrag:5emx
950
+ a=ice-pwd:T78uDKroul30L+4WytFCzZiv
951
+ a=ice-options:trickle
952
+ a=fingerprint:sha-256 AE:24:0F:8F:23:5C:EE:D5:F8:BD:EB:2C:86:15:13:44:A2:D1:30:9E:74:68:87:8C:50:80:F7:9A:6A:D9:3A:56
953
+ a=setup:actpass
954
+ a=mid:0
955
+ a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
956
+ a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
957
+ a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
958
+ a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
959
+ a=sendrecv
960
+ a=msid:2e008e23-265a-42a0-ba6f-d147a9d0ca42 ff52617f-a48b-454e-9d50-47367329f318
961
+ a=rtcp-mux
962
+ a=rtpmap:111 opus/48000/2
963
+ a=rtcp-fb:111 transport-cc
964
+ a=fmtp:111 minptime=10;useinbandfec=1
965
+ a=rtpmap:63 red/48000/2
966
+ a=fmtp:63 111/111
967
+ a=rtpmap:9 G722/8000
968
+ a=rtpmap:0 PCMU/8000
969
+ a=rtpmap:8 PCMA/8000
970
+ a=rtpmap:13 CN/8000
971
+ a=rtpmap:110 telephone-event/48000
972
+ a=rtpmap:126 telephone-event/8000
973
+ a=ssrc:2953770750 cname:/DZABa/Op+oV703F
974
+ a=ssrc:2953770750 msid:2e008e23-265a-42a0-ba6f-d147a9d0ca42 ff52617f-a48b-454e-9d50-47367329f318
975
+ `.replace( /\r\n/g, "\n" ).replace( /\n/g, "\r\n" )
976
+
977
+ const sdpobj = sdp.create( wsssdp )
978
+ const target = sdpobj.getaudio()
979
+ const ignoreipv6candidates = true
980
+ const ipv6regex = /^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/
981
+
982
+ let candidates = sdpobj.sdp.media[ 0 ].candidates
983
+ if( 0 < candidates.length ) {
984
+ if( ignoreipv6candidates ) {
985
+ candidates = candidates.filter( ( c ) => {
986
+ const ismatch = ipv6regex.test( c.ip )
987
+ return !ismatch
988
+ } )
989
+ }
990
+
991
+ candidates.sort( ( l, r ) => { return r.priority - l.priority } )
992
+ target.port = candidates[ 0 ].port
993
+
994
+ await new Promise( ( resolve ) => {
995
+ dns.lookup( candidates[ 0 ].ip, ( err, result ) => {
996
+ if( !err ) target.address = result
997
+ resolve()
998
+ } )
999
+ } )
1000
+
1001
+ expect( target.address ).to.equal( "192.168.1.108" )
1002
+ expect( target.port ).to.equal( 52396 )
1003
+ }
1004
+ } )
930
1005
  } )