@babblevoice/projectrtp 2.4.3 → 2.4.8

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # ProjectRTP
2
2
 
3
+ [![Build](https://github.com/babblevoice/projectrtp/actions/workflows/buildimage.yaml/badge.svg)](https://github.com/babblevoice/projectrtp/actions/workflows/buildimage.yaml)
4
+
3
5
  An RTP node addon which offers functionality to process RTP data streams and mix it. All performance tasks are implemented in C++ and use boost::asio for IO completion ports for high concurrency.
4
6
 
5
7
  ProjectRTP is designed to scale to multiple servers serving other signalling servers. RTP and signalling should be kept separate, and this architecture allows that. It is provided with a proxy server and client to allow for remote nodes.
@@ -30,21 +32,9 @@ Given a version number MAJOR.MINOR.PATCH, increment the:
30
32
 
31
33
  As part of this, we maintain jsdoc documentation to document our public interface to this programme.
32
34
 
33
- Only when we release version 1.0.0 will the public API be settled.
34
-
35
35
  ## Release process
36
36
 
37
- 1. Update package.json to the new version number (see Version numbers above)
38
- 2. Update Dockerfile with the new version number
39
- 3. npm test && stress testing
40
- 4. git commit && push
41
- 5. npm publish
42
- 6. docker buildx prune
43
- 7. docker buildx build --platform linux/amd64,linux/arm64 -t tinpotnick/projectrtp:<version> . --push
44
- 8. Git release
45
- 9. Github tag <version>
46
-
47
- <version> should simply the semantic version number. For late stage test builds this could be <version>betax.
37
+ Releases are managed on GitHub actions. It builds and tests any version.
48
38
 
49
39
  ## Docker
50
40
 
@@ -102,27 +92,6 @@ has a submodule so (although this is handled inteh Dockerfile if you are buildin
102
92
  git submodule update --init --recursive
103
93
  ```
104
94
 
105
- ### Fedora
106
-
107
- After installing standard build tools - including g++.
108
-
109
- ```
110
- dnf install ilbc-devel spandsp-devel boost gnutls libsrtp libsrtp-devel g++
111
- ```
112
-
113
- ### Ubuntu
114
-
115
- Build ilbc from the sub module plus install other dependancies.
116
-
117
- ```bash
118
-
119
- apt install libboost-dev libboost-system-dev libspandsp-dev gnutls-dev libsrtp2-dev cmake ccache
120
-
121
- cd libilbc
122
- cmake . -DCMAKE_INSTALL_LIBDIR=/lib -DCMAKE_INSTALL_INCLUDEDIR=/usr/include; cmake --build .; cmake --install .
123
- cd ..
124
- ```
125
-
126
95
  ## Docker/Podman
127
96
 
128
97
  Podman has some shortfalls. Remove and install docker.
package/lib/node.js CHANGED
@@ -22,6 +22,198 @@ const channelmap = {
22
22
  "direction": ( chan, msg ) => chan.direction( msg.options )
23
23
  }
24
24
 
25
+ /**
26
+ * One for each connection spawned by rtpnode.
27
+ */
28
+ class rtpnodeconnection {
29
+ constructor( parent, connection ) {
30
+ this.connectionid = uuidv4()
31
+ this.connection = connection
32
+ this.connectionlength = 0
33
+ this.mode = "listen"
34
+ this.parent = parent
35
+ this.messagestate = message.newstate()
36
+ this._reconnecttime = 500 /* mS */
37
+ this._destroying = false
38
+
39
+ // spawned from listen
40
+ if( connection ) {
41
+ connection.setKeepAlive( true )
42
+ connection.on( "data", this._onsocketdata.bind( this ) )
43
+ }
44
+ }
45
+
46
+ static create( parent, connection ) {
47
+ return new rtpnodeconnection( parent, connection )
48
+ }
49
+
50
+ destroy() {
51
+ this._destroying = true
52
+ if( this._reconnecttimerid ) {
53
+ clearTimeout( this._reconnecttimerid )
54
+ delete this._reconnecttimerid
55
+ }
56
+
57
+ this.connection.destroy()
58
+ }
59
+
60
+ _onsocketdata( data ) {
61
+ message.parsemessage( this.messagestate, data, ( msg ) => {
62
+ try {
63
+ this.parent._pre( msg, ( modifiedmsg ) => {
64
+ this._processmessage( modifiedmsg )
65
+ } )
66
+ } catch( e ) {
67
+ console.error( "Unhandled exception in babble-rtp", e )
68
+ }
69
+ } )
70
+ }
71
+
72
+ /**
73
+ * @private
74
+ * @param { object } msg
75
+ * @returns boolean
76
+ */
77
+ _updatechannel( msg ) {
78
+
79
+ if( undefined === msg.channel ) return false
80
+ if( undefined === msg.uuid ) return false
81
+
82
+ const chan = channels.get( msg.uuid )
83
+ if( undefined == chan ) return false
84
+
85
+ if( msg.channel in channelmap ) {
86
+ channelmap[ msg.channel ]( chan, msg )
87
+ } else {
88
+ const channelidentifiers = {
89
+ "id": msg.id,
90
+ "uuid": msg.uuid
91
+ }
92
+ this.send( { ...{ "error": "Unknown method" }, ...channelidentifiers } )
93
+ }
94
+
95
+ return true
96
+ }
97
+
98
+ /**
99
+ *
100
+ * @param { object } msg
101
+ * @returns { Promise< Boolean > }
102
+ */
103
+ async _processmessage( msg ) {
104
+ if( "open" == msg.channel ) return await this._openchannel( msg )
105
+
106
+ return this._updatechannel( msg )
107
+ }
108
+
109
+ /**
110
+ * Send a message back to the main server, include stats to help with load balancing.
111
+ * @param { object } msg
112
+ * @returns { void }
113
+ */
114
+ send( msg, cb = undefined ) {
115
+ this.parent._post( msg, ( modifiedmsg ) => {
116
+ if( this._destroying ) return
117
+ msg.status = this.parent.prtp.stats()
118
+ msg.status.instance = instance
119
+ this.connection.write( message.createmessage( modifiedmsg ) )
120
+ if (cb !== undefined) cb(msg)
121
+ } )
122
+ }
123
+
124
+
125
+ /**
126
+ * @private
127
+ * @param { object } msg
128
+ * @returns { Promise< Boolean > }
129
+ */
130
+ async _openchannel( msg ) {
131
+ this.connectionlength += 1
132
+ msg.forcelocal = true
133
+
134
+ const chan = await this.parent.prtp.openchannel( msg, ( cookie ) => {
135
+ // TODO: we might want to ensure the actual event has been written
136
+ // to the server before cleaning up the channel on our side?
137
+ this.send( { ...{ "id": chan.id, "uuid": chan.uuid }, ...cookie },
138
+ ( cookie ) => {
139
+ if ( "close" === cookie.action ) {
140
+ this.connectionlength -= 1
141
+ channels.delete( chan.uuid )
142
+
143
+ if( 0 == this.connectionlength && "listen" == this.mode ) {
144
+ this.parent.connections.delete( this.connectionid )
145
+ this.connection.destroy()
146
+ }
147
+ }
148
+ } )
149
+ } )
150
+ channels.set( chan.uuid, chan )
151
+ this.send( { ...chan, ...{ "action": "open" } } )
152
+
153
+ return true
154
+ }
155
+
156
+
157
+ /**
158
+ * @summary Connect to a server.
159
+ * @param {string} host
160
+ * @param {number} port
161
+ * @return {Promise} - Promise which resolves to an rtpnode
162
+ */
163
+ connect( host, port ) {
164
+ this.host = host
165
+ this.port = port
166
+ this.mode = "connect"
167
+
168
+ return new Promise( resolve => {
169
+
170
+ this.connection = net.createConnection( this.port, this.host )
171
+ this.connection.on( "data", this._onsocketdata.bind( this ) )
172
+
173
+ this.connection.setKeepAlive( true )
174
+
175
+ this.connection.on( "connect", () => {
176
+ this.send( {} )
177
+ resolve()
178
+ this._reconnecttime = 500 /* mS */
179
+ } )
180
+
181
+ this.connection.on( "error", () => {
182
+ if( this._destroying ) return
183
+ this._runreconnect()
184
+ } )
185
+ this.connection.on( "close", () => {
186
+ if( this._destroying ) return
187
+ this._runreconnect()
188
+ } )
189
+ } )
190
+ }
191
+
192
+ /**
193
+ * @private
194
+ * @returns { void }
195
+ */
196
+ _reconnect() {
197
+ delete this._reconnecttimerid
198
+ this.connect( this.host, this.port )
199
+ }
200
+
201
+ /**
202
+ * @private
203
+ * @returns { void }
204
+ */
205
+ _runreconnect() {
206
+ if( this._reconnecttimerid ) return
207
+ console.log( "Disconnected - trying to reconnect to " + this.host + ":" + this.port )
208
+
209
+ this._reconnecttimerid = setTimeout( this._reconnect.bind( this ), this._reconnecttime )
210
+
211
+ this._reconnecttime = this._reconnecttime * 2
212
+ if( this._reconnecttime > ( 1000 * 2 ) ) this._reconnecttime = this._reconnecttime / 2
213
+ }
214
+
215
+ }
216
+
25
217
  /**
26
218
  * @summary An RTP node. We are a remote RTP node which waits for intruction from our server.
27
219
  */
@@ -38,7 +230,6 @@ class rtpnode {
38
230
  this.port = port
39
231
  this.instance = uuidv4()
40
232
  this.connections = new Map()
41
- this.messagestate = message.newstate()
42
233
 
43
234
  /* pre callbacks are called when we receive an instruction from
44
235
  our server and we want to check if there is anything to do such as
@@ -50,42 +241,19 @@ class rtpnode {
50
241
  to our server - this is to, for example, upload a recording to a remote storage
51
242
  facility. */
52
243
  this._post = this._defaulthandler.bind( this )
53
-
54
- /**
55
- * @type { NodeJS.Timeout }
56
- */
57
- this._reconnecttimerid
58
244
  }
59
245
 
60
246
  /**
61
247
  * @summary Connect to a server.
62
- * @param {number} port
63
248
  * @param {string} host
249
+ * @param {number} port
64
250
  * @return {Promise<rtpnode>} - Promise which resolves to an rtpnode
65
251
  */
66
- connect( port, host ) {
67
- this.host = host
68
- this.port = port
69
- this._destroying = false
70
- this._reconnecttime = 500 /* mS */
71
-
72
- return new Promise( resolve => {
73
- this._onsocketreadypromiseresolve = resolve
74
- const connection = net.createConnection( this.port, this.host )
75
- this.connection = connection
76
- this.connection.setKeepAlive( true )
77
- this.connection.on( "connect", this._onsocketconnect.bind( this ) )
78
- const con = {
79
- connectionid: uuidv4(),
80
- connection,
81
- "connectionlength": 0,
82
- "mode": "connect"
83
- }
84
- this.connections[ con.connectionid ] = con
85
- this.connection.on( "data", this._onsocketdata.bind( this, con ) )
86
- this.connection.on( "error", this._onsocketerror.bind( this ) )
87
- this.connection.on( "close", this._onsocketclose.bind( this ) )
88
- } )
252
+ async connect( host, port ) {
253
+ const con = rtpnodeconnection.create( this )
254
+ await con.connect( host, port )
255
+ this.connections.set( con.connectionid, con )
256
+ return this
89
257
  }
90
258
 
91
259
  /**
@@ -95,15 +263,12 @@ class rtpnode {
95
263
  let listenresolve
96
264
  const listenpromise = new Promise( ( r ) => listenresolve = r )
97
265
  this.server = net.createServer( ( connection ) => {
98
- const con = {
99
- connectionid: uuidv4(),
100
- connection,
101
- "connectionlength": 0,
102
- "mode": "listen"
266
+ try {
267
+ const con = rtpnodeconnection.create( this, connection )
268
+ this.connections.set( con.connectionid, con )
269
+ } catch( e ) {
270
+ console.error( "Uncaught exception in node listen", e )
103
271
  }
104
- this.connections[ con.connectionid ] = con
105
- connection.setKeepAlive( true )
106
- connection.on( "data", this._onsocketdata.bind( this, con ) )
107
272
  } )
108
273
 
109
274
  this.server.listen( this.port, this.address )
@@ -112,17 +277,6 @@ class rtpnode {
112
277
  return listenpromise
113
278
  }
114
279
 
115
- /**
116
- * @private
117
- * @returns { void }
118
- */
119
- _onsocketconnect( /* sock */ ) {
120
- //console.log( "Connected to " + this.host + ":" + this.port )
121
- this.send( {}, this.connection )
122
- this._onsocketreadypromiseresolve( this )
123
- this._reconnecttime = 500 /* mS */
124
- }
125
-
126
280
  /**
127
281
  * This callback is displayed as a global member.
128
282
  * @callback requestComplete
@@ -159,22 +313,6 @@ class rtpnode {
159
313
  this._post = cb
160
314
  }
161
315
 
162
- /**
163
- * Send a message back to the main server, include stats to help with load balancing.
164
- * @param { object } msg
165
- * @param { object } connection
166
- * @returns { void }
167
- */
168
- send( msg, connection, cb = undefined ) {
169
- this._post( msg, ( modifiedmsg ) => {
170
- if( this._destroying ) return
171
- msg.status = this.prtp.stats()
172
- msg.status.instance = instance
173
- connection.write( message.createmessage( modifiedmsg ) )
174
- if (cb !== undefined) cb(msg)
175
- } )
176
- }
177
-
178
316
  /**
179
317
  * @summary Destroy this node
180
318
  * @returns { void }
@@ -183,24 +321,10 @@ class rtpnode {
183
321
  this._destroying = true
184
322
  nodeinterface.clean()
185
323
 
186
- if( this._reconnecttimerid ) clearTimeout( this._reconnecttimerid )
187
- if( this.connection ) this.connection.destroy()
188
- this.connections.forEach( ( connection ) => {
189
- connection.destroy()
190
- } )
191
- if ( this.server ) this.server.close()
192
- }
193
-
194
- /**
195
- *
196
- * @param { object } msg
197
- * @param { object } con
198
- * @returns { Promise< Boolean > }
199
- */
200
- async _processmessage( msg, con ) {
201
- if( "open" == msg.channel ) return await this._openchannel( msg, con )
324
+ this.connections.forEach( ( con ) => con.destroy() )
325
+ this.connections.clear()
202
326
 
203
- return this._updatechannel( msg, con )
327
+ if ( this.server ) this.server.close()
204
328
  }
205
329
 
206
330
  /**
@@ -211,137 +335,6 @@ class rtpnode {
211
335
  _defaulthandler( msg, cb ) {
212
336
  cb( msg )
213
337
  }
214
-
215
- /**
216
- * @private
217
- * @returns { void }
218
- */
219
- _reconnect() {
220
-
221
- delete this._reconnecttimerid
222
- this.connection = net.createConnection( this.port, this.host )
223
- .on( "error", () => {
224
- this._runreconnect()
225
- } )
226
-
227
- this.connection.setKeepAlive( true )
228
-
229
- this.connection.on( "connect", this._onsocketconnect.bind( this ) )
230
- this.connection.on( "data", this._onsocketdata.bind( this ) )
231
- this.connection.on( "error", this._onsocketerror.bind( this ) )
232
- this.connection.on( "close", this._onsocketclose.bind( this ) )
233
- }
234
-
235
- /**
236
- * @private
237
- * @returns { void }
238
- */
239
- _runreconnect() {
240
- if( this._reconnecttimerid ) return
241
- console.log( "Disconnected - trying to reconnect to " + this.host + ":" + this.port )
242
-
243
- this._reconnecttimerid = setTimeout( this._reconnect.bind( this ), this._reconnecttime )
244
-
245
- this._reconnecttime = this._reconnecttime * 2
246
- if( this._reconnecttime > ( 1000 * 2 ) ) this._reconnecttime = this._reconnecttime / 2
247
- }
248
-
249
- /**
250
- * @private
251
- * @returns { void }
252
- */
253
- _onsocketclose() {
254
- if( this._destroying ) return
255
- this._runreconnect()
256
- }
257
-
258
- /**
259
- * Reconnect on failure.
260
- * @private
261
- * @returns { void }
262
- */
263
- _onsocketerror() {
264
- if( this._destroying ) return
265
- this._runreconnect()
266
- }
267
-
268
- /**
269
- * @privateopenchannel
270
- * @param { Buffer } data
271
- * @param { object } con
272
- * @returns { void }
273
- */
274
- _onsocketdata( con, data ) {
275
- message.parsemessage( this.messagestate, data, ( msg ) => {
276
- try {
277
- this._pre( msg, ( modifiedmsg ) => {
278
- this._processmessage( modifiedmsg, con )
279
- } )
280
- } catch( e ) {
281
- console.error( "Unhandled exception in babble-rtp", e )
282
- }
283
- } )
284
- }
285
-
286
- /**
287
- * @private
288
- * @param { object } msg
289
- * @param { object } con
290
- * @returns { Promise< Boolean > }
291
- */
292
- async _openchannel( msg, con ) {
293
- con.connectionlength += 1
294
- msg.forcelocal = true
295
-
296
- const chan = await this.prtp.openchannel( msg, ( cookie ) => {
297
- // TODO: we might want to ensure the actual event has been written
298
- // to the server before cleaning up the channel on our side?
299
- this.send( { ...{ "id": chan.id, "uuid": chan.uuid }, ...cookie },
300
- con.connection,
301
- ( cookie ) => {
302
- if ( "close" === cookie.action ) {
303
- con.connectionlength -= 1
304
- channels.delete( chan.uuid )
305
-
306
- if( 0 == con.connectionlength && "listen" == con.mode ) {
307
- this.connections.delete( con.instance )
308
- con.connection.destroy()
309
- }
310
- }
311
- } )
312
- } )
313
- channels.set( chan.uuid, chan )
314
- this.send( { ...chan, ...{ "action": "open" } }, con.connection )
315
-
316
- return true
317
- }
318
-
319
- /**
320
- * @private
321
- * @param { object } msg
322
- * @param { object } con
323
- * @returns boolean
324
- */
325
- _updatechannel( msg, con ) {
326
-
327
- if( undefined === msg.channel ) return false
328
- if( undefined === msg.uuid ) return false
329
-
330
- const chan = channels.get( msg.uuid )
331
- if( undefined == chan ) return false
332
-
333
- if( msg.channel in channelmap ) {
334
- channelmap[ msg.channel ]( chan, msg )
335
- } else {
336
- const channelidentifiers = {
337
- "id": msg.id,
338
- "uuid": msg.uuid
339
- }
340
- this.send( { ...{ "error": "Unknown method" }, ...channelidentifiers }, con.connection )
341
- }
342
-
343
- return true
344
- }
345
338
  }
346
339
 
347
340
  /**
@@ -386,7 +379,7 @@ class nodeinterface {
386
379
  if( nodeinterface._n ) return new Promise( r => r( nodeinterface._n ) )
387
380
 
388
381
  nodeinterface._n = new rtpnode( this.prtp )
389
- return nodeinterface._n.connect( port, host )
382
+ return nodeinterface._n.connect( host, port )
390
383
  }
391
384
 
392
385
  /**
package/lib/server.js CHANGED
@@ -5,7 +5,7 @@ const { v4: uuidv4 } = require( "uuid" )
5
5
 
6
6
  const EventEmitter = require( "events" )
7
7
 
8
- const message = require( "./message.js" )
8
+ const message = require( "./message" )
9
9
 
10
10
  /**
11
11
  * Node host address and port object
@@ -599,11 +599,10 @@ class channel {
599
599
 
600
600
  /**
601
601
  *
602
- * @param { object } msg
603
602
  * @returns { void }
604
603
  * @private
605
604
  */
606
- _runclose( msg ) {
605
+ _runclose() {
607
606
  if( undefined !== this.openresolve ) {
608
607
  this.openresolve()
609
608
  delete this.openresolve
@@ -634,7 +633,7 @@ class channel {
634
633
  this.em.emit( "all", msg )
635
634
  this.em.emit( msg.action, msg )
636
635
 
637
- if( "close" == msg.action ) this._runclose( msg )
636
+ if( "close" == msg.action ) this._runclose()
638
637
  }
639
638
 
640
639
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/projectrtp",
3
- "version": "2.4.3",
3
+ "version": "2.4.8",
4
4
  "description": "A scalable Node addon RTP server",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "scripts": {
12
12
  "test": "./node_modules/mocha/bin/_mocha test/**/*.js --check-leaks",
13
- "github:check": "node test/basictests.js; npm update; npm test",
13
+ "github:check": "node test/basictests.js; npm update; ./node_modules/mocha/bin/_mocha test/**/*.js --check-leaks --exit",
14
14
  "check": "tsc --checkJs --noEmit --target es6 --module commonjs --skipLibCheck *.js test/**/*.js; ./node_modules/eslint/bin/eslint.js ./",
15
15
  "docs": "jsdoc -c jsdoc.conf.json ./README.md",
16
16
  "stress": "node stress/index.js",
@@ -25,14 +25,14 @@
25
25
  },
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "git+https://github.com/tinpotnick/projectrtp.git"
28
+ "url": "git+https://github.com/babblevoice/projectrtp.git"
29
29
  },
30
30
  "author": "Nick Knight",
31
31
  "license": "MIT",
32
32
  "bugs": {
33
- "url": "https://github.com/tinpotnick/projectrtp/issues"
33
+ "url": "https://github.com/babblevoice/projectrtp/issues"
34
34
  },
35
- "homepage": "https://github.com/tinpotnick/projectrtp#readme",
35
+ "homepage": "https://github.com/babblevoice/projectrtp#readme",
36
36
  "dependencies": {
37
37
  "uuid": "^9.0.0"
38
38
  },
@@ -23,8 +23,23 @@ async function testopen() {
23
23
  }
24
24
  }
25
25
 
26
+ let retval = 0
27
+ console.log( "Running project RTP" )
26
28
  prtp.projectrtp.run()
27
- testopen()
29
+
30
+ console.log( "Test open channel" )
31
+
32
+ try{
33
+ testopen()
34
+ } catch( e ) {
35
+ console.log( e )
36
+ retval = 1
37
+ }
38
+
39
+ console.log( "Shutting down projectrtp" )
28
40
  prtp.projectrtp.shutdown()
41
+ console.log( "Shutdown - good to proceed" )
42
+
43
+ process.exit( retval )
29
44
 
30
45
 
@@ -96,7 +96,7 @@ describe( "rtpchannel", function() {
96
96
  await finished
97
97
  } )
98
98
 
99
- it( "call create channel and check the structure of the returned object ( server as listener )", async function() {
99
+ it( "call create channel and check the structure of the returned object - server as listener", async function() {
100
100
 
101
101
  const ourport = 45433
102
102
  await projectrtp.server.listen( ourport, "127.0.0.1" )
@@ -119,7 +119,7 @@ describe( "rtpchannel", function() {
119
119
  await server.destroy()
120
120
  } )
121
121
 
122
- it( "call create channel and check the structure of the returned object ( node as listener )", async function() {
122
+ it( "call create channel and check the structure of the returned object - node as listener", async function() {
123
123
 
124
124
  const ourport = 45432
125
125
  projectrtp.server.clearnodes()
@@ -437,7 +437,7 @@ describe( "rtpchannel", function() {
437
437
  expect( closedstats.stats.in.count ).to.equal( 300 )
438
438
  expect( closedstats.stats.out.count ).to.be.within( 180, 190 )
439
439
  expect( totalsndiff ).to.equal( 0 ) // received should be reordered
440
- expect( totaltsdiff ).to.equal( 18400 )
440
+ expect( totaltsdiff ).to.be.within( 18240, 18400 ) // Allow some loss in test
441
441
  expect( lastsn - firstsn ).to.be.within( 180, 192 )
442
442
  } )
443
443
 
@@ -432,7 +432,7 @@ describe( "dtls", function() {
432
432
  await finished
433
433
 
434
434
  expect( clientcclose.stats.in.count ).to.be.above( 10 )
435
- expect( clientcclose.stats.in.skip ).to.equal( 0 )
435
+ expect( clientcclose.stats.in.skip ).to.be.below( 2 ) // allow a little loss in test
436
436
 
437
437
  } )
438
438
  } )
@@ -338,7 +338,7 @@ describe( "dtmf", function() {
338
338
  } )
339
339
 
340
340
 
341
- it( "Send multiple 2833 DTMF and check event", function( done ) {
341
+ it( "Send multiple 2833 DTMF and check event", async function() {
342
342
 
343
343
  /* create our RTP/UDP endpoint */
344
344
  const server = dgram.createSocket( "udp4" )
@@ -348,53 +348,58 @@ describe( "dtmf", function() {
348
348
  this.slow( 2500 )
349
349
 
350
350
  server.bind()
351
- server.on( "listening", async function() {
351
+ await new Promise( ( resolve ) => { server.on( "listening", () => resolve() ) } )
352
352
 
353
- const ourport = server.address().port
353
+ const ourport = server.address().port
354
354
 
355
- let expectedmessagecount = 0
356
- const expectedmessages = [
357
- { action: "telephone-event", event: "4" },
358
- { action: "telephone-event", event: "5" },
359
- { action: "close" }
360
- ]
355
+ const receivedmessages = []
356
+ const channel = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
357
+ receivedmessages.push( d )
358
+ if( "close" === d.action ) {
359
+ server.close()
360
+ }
361
+ } )
361
362
 
362
- const channel = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
363
- expect( d ).to.deep.include( expectedmessages[ expectedmessagecount ] )
364
- expectedmessagecount++
363
+ expect( channel.echo() ).to.be.true
365
364
 
366
- if( "close" === d.action ) {
367
- server.close()
368
- done()
369
- }
370
- } )
365
+ /* send a packet every 20mS x 50 */
366
+ for( let i = 0; 13 > i; i ++ ) {
367
+ sendpk( i, i*160, i*20, channel.local.port, server )
368
+ }
371
369
 
372
- expect( channel.echo() ).to.be.true
370
+ /* DTMF every 50mS */
371
+ senddtmf( 13, 12*160, 13*20, channel.local.port, server, false, "4" )
372
+ sendpk( 14, 13*160, 13*20, channel.local.port, server )
373
+ sendpk( 15, 14*160, 14*20, channel.local.port, server )
374
+ senddtmf( 16, 14*160, (13*20)+50, channel.local.port, server, false, "4" )
375
+ // Packet loss
376
+ // senddtmf( 15, 12 * 160, 15*20, channel.port, server, true, "4" )
373
377
 
374
- /* send a packet every 20mS x 50 */
375
- for( let i = 0; 13 > i; i ++ ) {
376
- sendpk( i, i*160, i*20, channel.local.port, server )
377
- }
378
+ for( let i = 17; 30 > i; i ++ ) {
379
+ sendpk( i, (i-2)*160, (i-2)*20, channel.local.port, server )
380
+ }
378
381
 
379
- senddtmf( 13, 12 * 160, 13*20, channel.local.port, server, false, "4" )
380
- senddtmf( 14, 12 * 160, 14*20, channel.local.port, server, false, "4" )
381
- // Packet loss
382
- // senddtmf( 15, 12 * 160, 15*20, channel.port, server, true, "4" )
382
+ senddtmf( 31, 29*160, 29*20, channel.local.port, server, false, "5" )
383
+ sendpk( 32, 29*160, 29*20, channel.local.port, server )
384
+ sendpk( 33, 30*160, 30*20, channel.local.port, server )
385
+ senddtmf( 34, 30*160, (29*20)+50, channel.local.port, server, false, "5" )
386
+ sendpk( 35, 31*160, 31*20, channel.local.port, server )
387
+ sendpk( 36, 32*160, 32*20, channel.local.port, server )
388
+ senddtmf( 37, 32*160, (29*20)+100, channel.local.port, server, true, "5" )
383
389
 
384
- for( let i = 16; 23 > i; i ++ ) {
385
- sendpk( i, i*160, (i-3)*20, channel.local.port, server )
386
- }
390
+ for( let i = 37; 45 > i; i ++ ) {
391
+ sendpk( i, i*160, (i-6)*20, channel.local.port, server )
392
+ }
387
393
 
388
- senddtmf( 23, 22 * 160, 23*20, channel.local.port, server, false, "5" )
389
- senddtmf( 24, 22 * 160, 24*20, channel.local.port, server, false, "5" )
390
- senddtmf( 25, 22 * 160, 25*20, channel.local.port, server, true, "5" )
394
+ setTimeout( () => channel.close(), 1100 )
395
+ await new Promise( resolve => { server.on( "close", resolve ) } )
391
396
 
392
- for( let i = 26; 33 > i; i ++ ) {
393
- sendpk( i, i*160, (i-6)*20, channel.local.port, server )
394
- }
397
+ expect( receivedmessages[ 0 ].action ).to.equal( "telephone-event" )
398
+ expect( receivedmessages[ 0 ].event ).to.equal( "4" )
399
+ expect( receivedmessages[ 1 ].action ).to.equal( "telephone-event" )
400
+ expect( receivedmessages[ 1 ].event ).to.equal( "5" )
401
+ expect( receivedmessages[ 2 ].action ).to.equal( "close" )
395
402
 
396
- setTimeout( () => channel.close(), 1000 )
397
- } )
398
403
  } )
399
404
 
400
405
  it( "Lose end packet", async function() {
@@ -686,9 +691,6 @@ describe( "dtmf", function() {
686
691
 
687
692
  const receveiedmessages = []
688
693
 
689
- let done
690
- const finished = new Promise( ( r ) => { done = r } )
691
-
692
694
  const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) {
693
695
  receveiedmessages.push( d )
694
696
 
@@ -702,7 +704,11 @@ describe( "dtmf", function() {
702
704
 
703
705
  const channelc = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointc.address().port, "codec": 97 } }, function( d ) {
704
706
  expect( d.action).to.not.equal( "telephone-event" )
705
- if( "close" === d.action ) done()
707
+ if( "close" === d.action ) {
708
+ endpointa.close()
709
+ endpointb.close()
710
+ endpointc.close()
711
+ }
706
712
  } )
707
713
 
708
714
  /* mix */
@@ -735,32 +741,32 @@ describe( "dtmf", function() {
735
741
  senddtmf( 13, 13*160, 13*20, channela.local.port, endpointa, false, "4" )
736
742
  sendpk( 14, 13*160, 13*20, channela.local.port, endpointa, 0 )
737
743
  sendpk( 15, 14*160, 14*20, channela.local.port, endpointa, 0 )
738
- senddtmf( 16, (16*160), (13*20)+50, channela.local.port, endpointa, false, "4" )
739
- sendpk( 17, 15*160, 15*20, channela.local.port, endpointa, 0 )
744
+ sendpk( 16, 15*160, 15*20, channela.local.port, endpointa, 0 )
745
+ senddtmf( 17, (15*160), (13*20)+50, channela.local.port, endpointa, false, "4" )
740
746
  sendpk( 18, 16*160, 16*20, channela.local.port, endpointa, 0 )
741
- senddtmf( 19, (19*160), (13*20)+100, channela.local.port, endpointa, true, "4" )
742
- sendpk( 20, 17*160, 17*20, channela.local.port, endpointa, 0 )
747
+ sendpk( 19, 17*160, 17*20, channela.local.port, endpointa, 0 )
748
+ senddtmf( 20, (17*160), (13*20)+100, channela.local.port, endpointa, true, "4" )
743
749
  sendpk( 21, 18*160, 18*20, channela.local.port, endpointa, 0 )
744
- senddtmf( 22, (22*160)+20, (13*20)+150, channela.local.port, endpointa, true, "4" )
745
- sendpk( 23, 19*160, 19*20, channela.local.port, endpointa, 0 )
750
+ sendpk( 22, 19*160, 19*20, channela.local.port, endpointa, 0 )
751
+ sendpk( 23, 20*160, 20*20, channela.local.port, endpointa, 0 )
752
+ senddtmf( 24, (20*160), (13*20)+150, channela.local.port, endpointa, true, "4" )
753
+ sendpk( 25, 21*160, 21*20, channela.local.port, endpointa, 0 )
754
+ sendpk( 26, 22*160, 22*20, channela.local.port, endpointa, 0 )
755
+ sendpk( 27, 23*160, 23*20, channela.local.port, endpointa, 0 )
756
+ sendpk( 28, 24*160, 24*20, channela.local.port, endpointa, 0 )
757
+ sendpk( 29, 25*160, 25*20, channela.local.port, endpointa, 0 )
746
758
 
747
- senddtmf( 24, 20*160, 20*20, channela.local.port, endpointa, false, "5" )
748
- sendpk( 25, 20*160, 20*20, channela.local.port, endpointa, 0 )
749
- sendpk( 26, 21*160, 21*20, channela.local.port, endpointa, 0 )
750
- senddtmf( 27, (27*160), (20*20)+50, channela.local.port, endpointa, false, "5" )
751
- sendpk( 28, 22*160, 22*20, channela.local.port, endpointa, 0 )
752
- sendpk( 29, 23*160, 23*20, channela.local.port, endpointa, 0 )
753
- senddtmf( 30, (30*160), (20*20)+100, channela.local.port, endpointa, true, "5" )
754
- sendpk( 31, 24*160, 24*20, channela.local.port, endpointa, 0 )
755
- sendpk( 32, 25*160, 25*20, channela.local.port, endpointa, 0 )
756
- senddtmf( 33, (33*160), (20*20)+150, channela.local.port, endpointa, true, "5" )
757
- sendpk( 34, 26*160, 26*20, channela.local.port, endpointa, 0 )
758
- sendpk( 35, 27*160, 27*20, channela.local.port, endpointa, 0 )
759
- sendpk( 36, 28*160, 28*20, channela.local.port, endpointa, 0 )
760
- sendpk( 37, 29*160, 29*20, channela.local.port, endpointa, 0 )
761
- sendpk( 38, 30*160, 30*20, channela.local.port, endpointa, 0 )
762
- sendpk( 39, 31*160, 31*20, channela.local.port, endpointa, 0 )
763
- sendpk( 40, 32*160, 32*20, channela.local.port, endpointa, 0 )
759
+ senddtmf( 30, 25*160, 25*20, channela.local.port, endpointa, false, "5" )
760
+ sendpk( 31, 26*160, 26*20, channela.local.port, endpointa, 0 )
761
+ sendpk( 32, 27*160, 27*20, channela.local.port, endpointa, 0 )
762
+ senddtmf( 33, (27*160), (25*20)+50, channela.local.port, endpointa, false, "5" )
763
+ sendpk( 34, 28*160, 28*20, channela.local.port, endpointa, 0 )
764
+ sendpk( 35, 29*160, 29*20, channela.local.port, endpointa, 0 )
765
+ senddtmf( 36, (29*160), (25*20)+100, channela.local.port, endpointa, true, "5" )
766
+ sendpk( 37, 30*160, 30*20, channela.local.port, endpointa, 0 )
767
+ sendpk( 38, 31*160, 31*20, channela.local.port, endpointa, 0 )
768
+ sendpk( 39, 32*160, 32*20, channela.local.port, endpointa, 0 )
769
+ senddtmf( 40, (32*160), (25*20)+150, channela.local.port, endpointa, true, "5" )
764
770
  sendpk( 41, 33*160, 33*20, channela.local.port, endpointa, 0 )
765
771
  sendpk( 42, 34*160, 34*20, channela.local.port, endpointa, 0 )
766
772
  sendpk( 43, 35*160, 35*20, channela.local.port, endpointa, 0 )
@@ -774,15 +780,22 @@ describe( "dtmf", function() {
774
780
  sendpk( 51, 43*160, 43*20, channela.local.port, endpointa, 0 )
775
781
  sendpk( 52, 44*160, 44*20, channela.local.port, endpointa, 0 )
776
782
  sendpk( 53, 45*160, 45*20, channela.local.port, endpointa, 0 )
783
+ sendpk( 54, 46*160, 46*20, channela.local.port, endpointa, 0 )
784
+ sendpk( 55, 47*160, 47*20, channela.local.port, endpointa, 0 )
785
+ sendpk( 56, 48*160, 48*20, channela.local.port, endpointa, 0 )
786
+ sendpk( 57, 49*160, 49*20, channela.local.port, endpointa, 0 )
787
+ sendpk( 58, 50*160, 50*20, channela.local.port, endpointa, 0 )
788
+ sendpk( 59, 52*160, 51*20, channela.local.port, endpointa, 0 )
777
789
 
778
790
  await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1200 ) } )
779
791
 
780
792
  channela.close()
781
- endpointa.close()
782
- endpointb.close()
783
- endpointc.close()
784
793
 
785
- await finished
794
+ await Promise.all( [
795
+ new Promise( resolve => { endpointa.on( "close", resolve ) } ),
796
+ new Promise( resolve => { endpointb.on( "close", resolve ) } ),
797
+ new Promise( resolve => { endpointc.on( "close", resolve ) } )
798
+ ] )
786
799
 
787
800
  expect( endpointapkcount ).to.be.within( 59, 70 )
788
801
  expect( endpointbpkcount ).to.be.within( 59, 70 )
@@ -0,0 +1,83 @@
1
+
2
+ const prtp = require( "../../index" )
3
+ const node = require( "../../lib/node" )
4
+ const expect = require( "chai" ).expect
5
+
6
+ /**
7
+ * Test file to run tests acting as a remote note. Starts a babble-rtp node in the background
8
+ * before any tests begin then runs tests to open, play, polly etc.
9
+ */
10
+ describe( "server connect interface", () => {
11
+
12
+ before( async () => {
13
+ const host = "127.0.0.1"
14
+ const port = 9002
15
+ await prtp.projectrtp.node.listen( host, port )
16
+ prtp.projectrtp.server.addnode( { host, port} )
17
+
18
+ } )
19
+
20
+ after( () => {
21
+
22
+ prtp.projectrtp.server.clearnodes()
23
+ node.interface.destroy()
24
+ } )
25
+
26
+ it( "server connect and open channel", async function () {
27
+ this.timeout( 6000 )
28
+ this.slow( 3000 )
29
+
30
+ const totalotherchannelcount = 100
31
+ let chanclosecount = 0
32
+ let allchannelsclosedresolve
33
+ const allchannelsclosed = new Promise( resolve => allchannelsclosedresolve = resolve )
34
+ const onclose = ( e ) => {
35
+ if( "close" == e.action ) chanclosecount++
36
+ if( totalotherchannelcount == chanclosecount ) allchannelsclosedresolve()
37
+ }
38
+
39
+ // A very short wav file
40
+ prtp.projectrtp.tone.generate( "350+440*0.5:100", "/tmp/serverconnecttestwav.wav" )
41
+ prtp.projectrtp.tone.generate( "350+440*0.5:100", "/tmp/otherserverconnecttestwav.wav" )
42
+
43
+ const channels = []
44
+ for( let i = 0; totalotherchannelcount > i; i++ ) {
45
+ channels.push( await prtp.projectrtp.openchannel( onclose ) )
46
+ }
47
+
48
+ for( let i = 0; 3 > i; i++ ) {
49
+ let done
50
+ const finished = new Promise( resolve => done = resolve )
51
+
52
+ const receivedmessages = []
53
+ const chan = await prtp.projectrtp.openchannel( ( e ) => {
54
+ receivedmessages.push( e )
55
+ if( "play" == e.action && "end" == e.event ) chan.close()
56
+ if( "close" == e.action ) done()
57
+ } )
58
+
59
+ chan.play( { "interupt":true, "files": [ { "wav": "/tmp/serverconnecttestwav.wav" }, { "wav": "/tmp/otherserverconnecttestwav.wav" } ] } )
60
+
61
+ await finished
62
+
63
+ //console.log(receivedmessages)
64
+ expect( receivedmessages.length ).to.equal( 3 )
65
+ expect( receivedmessages[ 0 ].action ).to.equal( "play" )
66
+ expect( receivedmessages[ 0 ].event ).to.equal( "start" )
67
+ expect( receivedmessages[ 0 ].reason ).to.equal( "new" )
68
+ expect( receivedmessages[ 1 ].action ).to.equal( "play" )
69
+ expect( receivedmessages[ 1 ].event ).to.equal( "end" )
70
+ expect( receivedmessages[ 1 ].reason ).to.equal( "completed" )
71
+ expect( receivedmessages[ 2 ].action ).to.equal( "close" )
72
+ expect( receivedmessages[ 2 ].reason ).to.equal( "requested" )
73
+ }
74
+
75
+ for( const chan of channels ) {
76
+ chan.close()
77
+ }
78
+
79
+ await allchannelsclosed
80
+ expect( chanclosecount ).to.equal( totalotherchannelcount )
81
+ } )
82
+
83
+ } )