@babblevoice/projectrtp 2.4.5 → 2.4.9

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/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.5",
3
+ "version": "2.4.9",
4
4
  "description": "A scalable Node addon RTP server",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -75,10 +75,6 @@ void projectchannelmux::mixall( void ) {
75
75
  this->postrtpdata( chan, dtmfchan, src );
76
76
  }
77
77
  }
78
-
79
- AQUIRESPINLOCK( chan->rtpbufferlock );
80
- chan->inbuff->poppeeked();
81
- RELEASESPINLOCK( chan->rtpbufferlock );
82
78
  }
83
79
 
84
80
  if( nullptr != src ) {
@@ -94,7 +90,7 @@ void projectchannelmux::mixall( void ) {
94
90
 
95
91
  rtppacket *dst = chan->gettempoutbuf();
96
92
 
97
- this->subtracted.zero();
93
+ /* start with a direct copy */
98
94
  this->subtracted.copy( this->added );
99
95
 
100
96
  if( chan->recv ) {
@@ -141,8 +141,7 @@ void codecx::reset()
141
141
  ## restart
142
142
  Do enough to manage missing packets.
143
143
  */
144
- void codecx::restart( void )
145
- {
144
+ void codecx::restart( void ) {
146
145
  this->lpfilter.reset();
147
146
  this->dcpowerfilter.reset();
148
147
  this->resamplelastsample = 0;
@@ -154,8 +153,7 @@ void codecx::restart( void )
154
153
 
155
154
  From whichever PCM encoding (u or a) encode to the other without having to do intermediate l16.
156
155
  */
157
- bool codecx::alaw2ulaw( void )
158
- {
156
+ bool codecx::alaw2ulaw( void ) {
159
157
  if( 0 == this->pcmaref.size() ) return false;
160
158
  if( this->pcmaref.isdirty() ) return false;
161
159
 
@@ -167,23 +165,22 @@ bool codecx::alaw2ulaw( void )
167
165
  outbufptr = this->pcmuref.c_str();
168
166
  this->pcmuref.size( insize );
169
167
 
170
- if( nullptr == outbufptr || nullptr == inbufptr )
171
- {
168
+ if( nullptr == outbufptr || nullptr == inbufptr ) {
172
169
  std::cerr << "PCMA NULLPTR shouldn't happen (" << (void*)outbufptr << ", " << (void*)inbufptr << ")" << std::endl;
173
170
  return false;
174
171
  }
175
172
 
176
- for( size_t i = 0; i < insize; i++ )
177
- {
178
- *outbufptr++ = alaw_to_ulaw_table[ *inbufptr++ ];
173
+ for( size_t i = 0; i < insize; i++ ) {
174
+ *outbufptr = alaw_to_ulaw_table[ *inbufptr ];
175
+ inbufptr++;
176
+ outbufptr++;
179
177
  }
180
178
 
181
179
  this->pcmuref.dirty( false );
182
180
  return true;
183
181
  }
184
182
 
185
- bool codecx::ulaw2alaw( void )
186
- {
183
+ bool codecx::ulaw2alaw( void ) {
187
184
  if( 0 == this->pcmuref.size() ) return false;
188
185
  if( this->pcmuref.isdirty() ) return false;
189
186
 
@@ -195,15 +192,15 @@ bool codecx::ulaw2alaw( void )
195
192
  outbufptr = this->pcmaref.c_str();
196
193
  this->pcmaref.size( insize );
197
194
 
198
- if( nullptr == outbufptr || nullptr == inbufptr )
199
- {
195
+ if( nullptr == outbufptr || nullptr == inbufptr ) {
200
196
  std::cerr << "PCMU NULLPTR shouldn't happen(" << (void*)outbufptr << ", " << (void*)inbufptr << ")" << std::endl;
201
197
  return false;
202
198
  }
203
199
 
204
- for( size_t i = 0; i < insize; i++ )
205
- {
206
- *outbufptr++ = ulaw_to_alaw_table[ *inbufptr++ ];
200
+ for( size_t i = 0; i < insize; i++ ) {
201
+ *outbufptr = ulaw_to_alaw_table[ *inbufptr ];
202
+ inbufptr++;
203
+ outbufptr++;
207
204
  }
208
205
 
209
206
  this->pcmaref.dirty( false );
@@ -220,20 +217,15 @@ bool codecx::g711tol16( void )
220
217
  int16_t *convert;
221
218
  size_t insize;
222
219
 
223
- if( this->pcmaref.size() > 0 && !this->pcmaref.isdirty() )
224
- {
220
+ if( this->pcmaref.size() > 0 && !this->pcmaref.isdirty() ) {
225
221
  in = this->pcmaref.c_str();
226
222
  convert = _pcmatol16;
227
223
  insize = this->pcmaref.size();
228
- }
229
- else if ( this->pcmuref.size() > 0 && !this->pcmuref.isdirty() )
230
- {
224
+ } else if ( this->pcmuref.size() > 0 && !this->pcmuref.isdirty() ) {
231
225
  in = this->pcmuref.c_str();
232
226
  convert = _pcmutol16;
233
227
  insize = this->pcmuref.size();
234
- }
235
- else
236
- {
228
+ } else {
237
229
  return false;
238
230
  }
239
231
 
@@ -241,9 +233,10 @@ bool codecx::g711tol16( void )
241
233
 
242
234
  int16_t *out = ( int16_t * ) this->l168kref.c_str();
243
235
 
244
- for( size_t i = 0; i < insize; i++ )
245
- {
246
- *out++ = convert[ *in++ ];
236
+ for( size_t i = 0; i < insize; i++ ) {
237
+ *out = convert[ *in ];
238
+ in++;
239
+ out++;
247
240
  }
248
241
 
249
242
  this->l168kref.dirty( false );
@@ -265,9 +258,12 @@ bool codecx::l16topcma( void )
265
258
  size_t l168klength = this->l168kref.size();
266
259
  in = ( int16_t * ) this->l168kref.c_str();
267
260
 
268
- for( size_t i = 0; i < l168klength; i++ )
269
- {
270
- *out++ = _l16topcma[ ( *in++ ) + 32768 ];
261
+ uint16_t index;
262
+ for( size_t i = 0; i < l168klength; i++ ) {
263
+ index = *in + 32768;
264
+ *out = _l16topcma[ index ];
265
+ in++;
266
+ out++;
271
267
  }
272
268
  this->pcmaref.dirty( false );
273
269
  return true;
@@ -276,8 +272,7 @@ bool codecx::l16topcma( void )
276
272
  /*!md
277
273
  ## l16topcmu
278
274
  */
279
- bool codecx::l16topcmu( void )
280
- {
275
+ bool codecx::l16topcmu( void ) {
281
276
  if( 0 == this->l168kref.size() ) return false;
282
277
  if( this->l168kref.isdirty() ) return false;
283
278
 
@@ -287,9 +282,12 @@ bool codecx::l16topcmu( void )
287
282
  size_t l168klength = this->l168kref.size();
288
283
  in = ( int16_t * ) this->l168kref.c_str();
289
284
 
290
- for( size_t i = 0; i < l168klength; i++ )
291
- {
292
- *out++ = _l16topcmu[ ( *in++ ) + 32768 ];
285
+ uint16_t index;
286
+ for( size_t i = 0; i < l168klength; i++ ) {
287
+ index = *in + 32768;
288
+ *out = _l16topcmu[ index ];
289
+ in++;
290
+ out++;
293
291
  }
294
292
  this->pcmuref.dirty( false );
295
293
  return true;
@@ -299,8 +297,7 @@ bool codecx::l16topcmu( void )
299
297
  ## ilbctol16
300
298
  As it says.
301
299
  */
302
- bool codecx::ilbctol16( void )
303
- {
300
+ bool codecx::ilbctol16( void ) {
304
301
  if( 0 == this->ilbcref.size() ) return false;
305
302
  if( this->ilbcref.isdirty() ) return false;
306
303
 
@@ -335,25 +332,20 @@ bool codecx::ilbctol16( void )
335
332
  ## l16tog722
336
333
  As it says.
337
334
  */
338
- bool codecx::l16tog722( void )
339
- {
335
+ bool codecx::l16tog722( void ) {
340
336
  if( 0 == this->l1616kref.size() ) return false;
341
337
  if( this->l1616kref.isdirty() ) return false;
342
338
 
343
- if( nullptr == this->g722encoder )
344
- {
339
+ if( nullptr == this->g722encoder ) {
345
340
  this->g722encoder = g722_encode_init( NULL, 64000, G722_PACKED );
346
341
  }
347
342
 
348
343
  int len = g722_encode( this->g722encoder, this->g722ref.c_str(), ( int16_t * ) this->l1616kref.c_str(), this->g722ref.size() * 2 );
349
344
 
350
- if( len > 0 )
351
- {
345
+ if( len > 0 ) {
352
346
  this->g722ref.size( len );
353
347
  this->g722ref.dirty( false );
354
- }
355
- else
356
- {
348
+ } else {
357
349
  std::cerr << "g722_encode didn't encode any data" << std::endl;
358
350
  this->g722ref.size( 0 );
359
351
  }
@@ -400,19 +392,16 @@ bool codecx::l16toilbc( void ) {
400
392
  ## g722tol16
401
393
  As it says.
402
394
  */
403
- bool codecx::g722tol16( void )
404
- {
395
+ bool codecx::g722tol16( void ) {
405
396
  if( 0 == this->g722ref.size() ) return false;
406
397
  if( this->g722ref.isdirty() ) return false;
407
398
 
408
399
  /* x 2 for 16 bit instead of 8 and then x 2 sample rate */
409
400
  this->l1616kref.malloc( this->g722ref.size(), sizeof( int16_t ), L1616KPAYLOADTYPE );
410
401
 
411
- if( nullptr == this->g722decoder )
412
- {
402
+ if( nullptr == this->g722decoder ) {
413
403
  this->g722decoder = g722_decode_init( NULL, 64000, G722_PACKED );
414
- if( nullptr == this->g722decoder )
415
- {
404
+ if( nullptr == this->g722decoder ) {
416
405
  std::cerr << "Failed to init G722 decoder" << std::endl;
417
406
  }
418
407
  }
@@ -431,8 +420,7 @@ bool codecx::g722tol16( void )
431
420
  ## l16lowtowideband
432
421
  Upsample from narrow to wideband. Take each point and interpolate between them. We require the final sample from the last packet to continue the interpolating.
433
422
  */
434
- bool codecx::l16lowtowideband( void )
435
- {
423
+ bool codecx::l16lowtowideband( void ) {
436
424
  size_t l168klength = this->l168kref.size();
437
425
 
438
426
  if( 0 == l168klength ) return false;
@@ -443,8 +431,7 @@ bool codecx::l16lowtowideband( void )
443
431
  int16_t *in = ( int16_t * ) this->l168kref.c_str();
444
432
  int16_t *out = ( int16_t * ) this->l1616kref.c_str();
445
433
 
446
- for( size_t i = 0; i < l168klength; i++ )
447
- {
434
+ for( size_t i = 0; i < l168klength; i++ ) {
448
435
  *out = ( ( *in - this->resamplelastsample ) / 2 ) + this->resamplelastsample;
449
436
  this->resamplelastsample = *in;
450
437
  out++;
@@ -463,8 +450,7 @@ bool codecx::l16lowtowideband( void )
463
450
  ## requirewideband
464
451
  Search for the relevent data and convert as necessary.
465
452
  */
466
- bool codecx::requirewideband( void )
467
- {
453
+ bool codecx::requirewideband( void ) {
468
454
  if( 0 != this->l1616kref.size() && !this->l1616kref.isdirty() ) return true;
469
455
  if( this->g722tol16() ) return true;
470
456
  if( !this->g711tol16() )
@@ -491,10 +477,12 @@ bool codecx::l16widetonarrowband( void )
491
477
  int16_t *out = ( int16_t * ) this->l168kref.c_str();
492
478
  int16_t *in = ( int16_t * ) this->l1616kref.c_str();
493
479
 
494
- for( size_t i = 0; i < l1616klength / 2; i++ )
495
- {
496
- lpfilter.execute( *in++ );
497
- *out++ = lpfilter.execute( *in++ );
480
+ for( size_t i = 0; i < l1616klength / 2; i++ ) {
481
+ lpfilter.execute( *in );
482
+ in++;
483
+ *out = lpfilter.execute( *in );
484
+ in++;
485
+ out++;
498
486
  }
499
487
 
500
488
  this->l168kref.dirty( false );
@@ -820,6 +808,18 @@ void codectests( void )
820
808
 
821
809
  #ifdef NODE_MODULE
822
810
 
811
+ static napi_value codectest( napi_env env, napi_callback_info info ) {
812
+ napi_value result;
813
+ if( napi_ok != napi_create_object( env, &result ) ) return NULL;
814
+
815
+ codectests();
816
+
817
+ napi_create_uint32( env, 1, &result );
818
+ napi_coerce_to_bool( env, result, &result );
819
+
820
+ return result;
821
+ }
822
+
823
823
  /*
824
824
  Support single number just for now - but TODO detect Buffer input to convert whole bufffer.
825
825
  */
@@ -929,6 +929,9 @@ void initrtpcodecx( napi_env env, napi_value &result ) {
929
929
 
930
930
  if( napi_ok != napi_create_function( env, "exports", NAPI_AUTO_LENGTH, pcmu2linear, nullptr, &funct ) ) return;
931
931
  if( napi_ok != napi_set_named_property( env, codecx, "pcmu2linear16", funct ) ) return;
932
+
933
+ if( napi_ok != napi_create_function( env, "exports", NAPI_AUTO_LENGTH, codectest, nullptr, &funct ) ) return;
934
+ if( napi_ok != napi_set_named_property( env, codecx, "codectests", funct ) ) return;
932
935
  }
933
936
 
934
937
  #endif /* NODE_MODULE */
@@ -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()
@@ -298,36 +298,24 @@ describe( "dtls", function() {
298
298
  it( "Connect then remote to another session", async function() {
299
299
 
300
300
  /*
301
- | internal projectrtp |
302
- Clienta | channela channelb | clientb
303
- | RTP (DTLS) | MIX | RTP |
304
- |<--------------->|<--------------->|<--------------->| Step 1.
305
- |<-(close)-(rem)->|<--------------->|<--------------->| Step 2.
306
- Clientc
307
- |<--------------->|<--------------->|<--------------->|Step 3.
308
- play echo
309
-
301
+ Play, then encrypt at one end, then unencrypt in projectrtp and send onto d
302
+ Create an e channel so that we mix 3
303
+
304
+ | internal |
305
+ channel channel channel channel
306
+ a RTP over DTLS -----------b c RTP d
307
+ play b mix c echo
308
+ mix e f
309
+ echo
310
310
 
311
311
  */
312
312
 
313
313
  this.timeout( 6000 )
314
314
  this.slow( 2500 )
315
315
 
316
- projectrtp.tone.generate( "400+450*0.5/0/400+450*0.5/0:400/200/400/2000", "/tmp/ukringing.wav" )
316
+ projectrtp.tone.generate( "400+450*0.25/0/400+450*0.25/0:400/200/400/2000", "/tmp/ukringing.wav" )
317
317
 
318
- const channeltargeta = {
319
- "address": "localhost",
320
- "port": 0,
321
- "codec": 0,
322
- "dtls": {
323
- "fingerprint": {
324
- "hash": ""
325
- },
326
- "mode": "active" // - is this in the right place and the right way round!
327
- }
328
- }
329
-
330
- const channeltargetc = {
318
+ const targeta = {
331
319
  "address": "localhost",
332
320
  "port": 0,
333
321
  "codec": 0,
@@ -335,36 +323,23 @@ describe( "dtls", function() {
335
323
  "fingerprint": {
336
324
  "hash": ""
337
325
  },
338
- "mode": "active" // - is this in the right place and the right way round!
326
+ "mode": "active"
339
327
  }
340
328
  }
341
329
 
342
- const clienttargeta = {
343
- "address": "localhost",
344
- "port": 12008,
345
- "codec": 0,
346
- "dtls": {
347
- "fingerprint": {
348
- "hash": ""
349
- },
350
- "mode": "passive"
351
- }
352
- }
330
+ const targetb = JSON.parse( JSON.stringify( targeta ) )
353
331
 
354
- const channeltargetb = {
332
+ const targetc = {
355
333
  "address": "localhost",
356
334
  "port": 0,
357
335
  "codec": 0
358
336
  }
359
-
360
- const clienttargetb = {
361
- "address": "localhost",
362
- "port": 12010,
363
- "codec": 0
364
- }
337
+ const targetd = JSON.parse( JSON.stringify( targetc ) )
338
+ const targete = JSON.parse( JSON.stringify( targetc ) )
339
+ const targetf = JSON.parse( JSON.stringify( targetc ) )
365
340
 
366
341
  let done
367
- const finished = new Promise( ( r ) => { done = r } )
342
+ const finished = new Promise( ( resolve ) => { done = resolve } )
368
343
 
369
344
  const channela = await projectrtp.openchannel( {}, function( d ) {
370
345
  if( "close" === d.action ) {
@@ -372,57 +347,66 @@ describe( "dtls", function() {
372
347
  }
373
348
  } )
374
349
 
375
- const clienta = await projectrtp.openchannel( {}, function() {} )
376
-
377
- channeltargeta.dtls.fingerprint.hash = clienta.local.dtls.fingerprint
378
- channeltargeta.port = clienta.local.port
379
- expect( channela.remote( channeltargeta ) ).to.be.true
380
- clienttargeta.port = channela.local.port
381
- clienttargeta.dtls.fingerprint.hash = channela.local.dtls.fingerprint
382
- expect( clienta.remote( clienttargeta ) ).to.be.true
383
-
384
- clienta.play( { "loop": true, "files": [
385
- { "wav": "/tmp/ukringing.wav" } ] } )
350
+ const channelb = await projectrtp.openchannel( {}, function( d ) {
351
+ if( "close" === d.action ) {
352
+ channelc.close()
353
+ }
354
+ } )
386
355
 
356
+ const channelc = await projectrtp.openchannel( {}, function( d ) {
357
+ if( "close" === d.action ) {
358
+ channeld.close()
359
+ }
360
+ } )
387
361
 
388
- const channelb = await projectrtp.openchannel( {}, function( d ) {
362
+ const channeld = await projectrtp.openchannel( {}, function( d ) {
389
363
  if( "close" === d.action ) {
390
- clientb.close()
364
+ channele.close()
391
365
  }
392
366
  } )
393
367
 
394
- const clientb = await projectrtp.openchannel( {}, function( d ) {
368
+ const channele = await projectrtp.openchannel( {}, function( d ) {
395
369
  if( "close" === d.action ) {
396
- clientc.close()
370
+ channelf.close()
397
371
  }
398
372
  } )
399
373
 
400
- let clientcclose = {}
401
- const clientc = await projectrtp.openchannel( {}, function( d ) {
374
+ let channelclose = {}
375
+ const channelf = await projectrtp.openchannel( {}, function( d ) {
402
376
  if( "close" === d.action ) {
403
- clientcclose = d
377
+ channelclose = d
404
378
  done()
405
379
  }
406
380
  } )
407
381
 
408
- channeltargetb.port = clientb.local.port
409
- expect( channelb.remote( channeltargetb ) ).to.be.true
410
- clienttargetb.port = channelb.local.port
411
- expect( clientb.remote( clienttargetb ) ).to.be.true
412
- expect( clientb.echo() ).to.be.true
413
-
414
- channela.mix( channelb )
382
+ targeta.dtls.fingerprint.hash = channelb.local.dtls.fingerprint
383
+ targeta.port = channelb.local.port
384
+ expect( channela.remote( targeta ) ).to.be.true
385
+ targetb.port = channela.local.port
386
+ targetb.dtls.fingerprint.hash = channela.local.dtls.fingerprint
387
+ targetb.dtls.mode = "passive"
388
+ expect( channelb.remote( targetb ) ).to.be.true
415
389
 
416
- await new Promise( ( r ) => { setTimeout( () => r(), 500 ) } )
390
+ targetc.port = channeld.local.port
391
+ expect( channelc.remote( targetc ) ).to.be.true
392
+ targetd.port = channelc.local.port
393
+ expect( channeld.remote( targetd ) ).to.be.true
417
394
 
418
- channeltargetc.dtls.fingerprint.hash = clientc.local.dtls.fingerprint
419
- channeltargetc.port = clientc.local.port
420
- expect( channela.remote( channeltargetc ) ).to.be.true
421
- expect( clientc.remote( clienttargeta ) ).to.be.true
395
+ targete.port = channelf.local.port
396
+ expect( channele.remote( targete ) ).to.be.true
397
+ targetf.port = channele.local.port
398
+ expect( channelf.remote( targetf ) ).to.be.true
422
399
 
423
- clientc.play( { "loop": true, "files": [
400
+ /* play on one end */
401
+ channela.play( { "loop": true, "files": [
424
402
  { "wav": "/tmp/ukringing.wav" } ] } )
425
- clienta.close()
403
+
404
+ /* mix in the middle */
405
+ channelb.mix( channelc )
406
+ channelb.mix( channele )
407
+
408
+ /* echo at the other end */
409
+ expect( channeld.echo() ).to.be.true
426
410
 
427
411
  await new Promise( ( r ) => { setTimeout( () => r(), 1500 ) } )
428
412
 
@@ -431,8 +415,8 @@ describe( "dtls", function() {
431
415
  await fs.promises.unlink( "/tmp/ukringing.wav" ).catch( () => {} )
432
416
  await finished
433
417
 
434
- expect( clientcclose.stats.in.count ).to.be.above( 10 )
435
- expect( clientcclose.stats.in.skip ).to.be.below( 2 ) // allow a little loss in test
418
+ expect( channelclose.stats.in.count ).to.be.above( 30 )
419
+ expect( channelclose.stats.in.skip ).to.be.below( 2 ) // allow a little loss in test
436
420
 
437
421
  } )
438
422
  } )
@@ -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
+ } )
@@ -432,6 +432,69 @@ describe( "channel mix", function() {
432
432
 
433
433
  } )
434
434
 
435
+ it( "mix 2 channels - pcmu <-> g722 with recording", async function() {
436
+
437
+ this.timeout( 3000 )
438
+ this.slow( 2000 )
439
+
440
+ const endpointa = dgram.createSocket( "udp4" )
441
+ const endpointb = dgram.createSocket( "udp4" )
442
+
443
+ let endpointapkcount = 0
444
+ let endpointbpkcount = 0
445
+
446
+ endpointa.on( "message", function( msg ) {
447
+ endpointapkcount++
448
+ expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
449
+ } )
450
+
451
+ endpointb.on( "message", function( msg ) {
452
+
453
+ endpointbpkcount++
454
+ expect( 0x7f & msg [ 1 ] ).to.equal( 9 )
455
+ endpointb.send( msg, channelb.local.port, "localhost" )
456
+ } )
457
+
458
+ endpointa.bind()
459
+ await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } )
460
+
461
+ endpointb.bind()
462
+ await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } )
463
+
464
+ let done
465
+ const finished = new Promise( ( r ) => { done = r } )
466
+
467
+ const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) {
468
+ if( "close" === d.action ) channelb.close()
469
+ } )
470
+
471
+ const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 9 } }, function( d ) {
472
+ if( "close" === d.action ) done()
473
+ } )
474
+
475
+ /* mix */
476
+ expect( channela.mix( channelb ) ).to.be.true
477
+
478
+ channela.record( { "file": "/tmp/g722mix2recording.wav" } )
479
+
480
+ /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */
481
+ for( let i = 0; 50 > i; i ++ ) {
482
+ sendpk( i, i, channela.local.port, endpointa )
483
+ }
484
+
485
+ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } )
486
+
487
+ channela.close()
488
+ endpointa.close()
489
+ endpointb.close()
490
+
491
+ expect( endpointapkcount ).to.be.above( 30 )
492
+ expect( endpointbpkcount ).to.be.above( 30 )
493
+
494
+ await finished
495
+
496
+ } )
497
+
435
498
  it( "mix 3 channels - 1 writer 3 readers", async function() {
436
499
 
437
500
  this.timeout( 3000 )