@babblevoice/projectrtp 2.5.20 → 2.5.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/projectrtp",
3
- "version": "2.5.20",
3
+ "version": "2.5.22",
4
4
  "description": "A scalable Node addon RTP server",
5
5
  "main": "index.js",
6
6
  "directories": {
package/src/globals.h CHANGED
@@ -9,6 +9,9 @@
9
9
  #define G711PAYLOADBYTES 160
10
10
  #define G722PAYLOADBYTES 160
11
11
  #define L16PAYLOADSAMPLES 160
12
+ #define G711PAYLOADSAMPLES 160
13
+ #define G722PAYLOADSAMPLES 160
14
+ #define L1616PAYLOADSAMPLES 320
12
15
  #define L16NARROWBANDBYTES 320
13
16
  #define L16WIDEBANDBYTES 640
14
17
  #define ILBC20PAYLOADBYTES 38
@@ -15,7 +15,8 @@ projectchannelmux::projectchannelmux( boost::asio::io_context &iocontext ):
15
15
  newchannels(),
16
16
  newchannelslock( false ),
17
17
  added(),
18
- subtracted() {
18
+ subtracted(),
19
+ active( false ) {
19
20
  }
20
21
 
21
22
  projectchannelmux::~projectchannelmux() {
@@ -29,22 +30,24 @@ projectchannelmux::pointer projectchannelmux::create( boost::asio::io_context &i
29
30
  void projectchannelmux::mixall( void ) {
30
31
  /* First decide on a common rate (if we only have 8K channels it is pointless
31
32
  upsampling them all and wasting resources) */
32
- int l16krequired = L168KPAYLOADTYPE;
33
+ int l16format = L168KPAYLOADTYPE;
33
34
  size_t insize = L16PAYLOADSAMPLES;
34
35
 
35
36
  for( auto& chan: this->channels ) {
36
37
  switch( chan->codec ) {
37
38
  case G722PAYLOADTYPE:
38
39
  case L1616KPAYLOADTYPE: {
39
- l16krequired = L1616KPAYLOADTYPE;
40
+ l16format = L1616KPAYLOADTYPE;
41
+ insize = L1616PAYLOADSAMPLES;
40
42
  goto endofforloop;
41
43
  }
42
44
  }
43
45
  }
44
46
  endofforloop:
45
47
 
46
- this->added.malloc( insize, sizeof( int16_t ), l16krequired );
47
- this->subtracted.malloc( insize, sizeof( int16_t ), l16krequired );
48
+ /* allocate the max needed */
49
+ this->added.malloc( insize, sizeof( int16_t ), l16format );
50
+ this->subtracted.malloc( insize, sizeof( int16_t ), l16format );
48
51
  this->added.zero();
49
52
 
50
53
  /* We first have to add them all up */
@@ -186,6 +189,8 @@ Our timer handler.
186
189
  void projectchannelmux::handletick( const boost::system::error_code& error ) {
187
190
  if ( error == boost::asio::error::operation_aborted ) return;
188
191
 
192
+ if( !this->active ) return;
193
+
189
194
  this->checkfornewmixes();
190
195
 
191
196
  /* Check for channels which have request removal */
@@ -193,6 +198,7 @@ void projectchannelmux::handletick( const boost::system::error_code& error ) {
193
198
 
194
199
  if( 0 == this->channels.size() ) {
195
200
  /* We're done */
201
+ this->active = false;
196
202
  return;
197
203
  }
198
204
 
@@ -233,6 +239,10 @@ void projectchannelmux::setnexttick( void ) {
233
239
  }
234
240
 
235
241
  void projectchannelmux::go( void ) {
242
+
243
+ if( this->active ) return;
244
+ this->active = true;
245
+
236
246
  this->nexttick = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds( 20 );
237
247
 
238
248
  this->tick.expires_after( this->nexttick - std::chrono::high_resolution_clock::now() );
@@ -22,8 +22,8 @@ typedef std::shared_ptr< projectrtpchannel > projectrtpchannelptr;
22
22
  typedef std::list< projectrtpchannelptr > projectchanptrlist;
23
23
 
24
24
  class projectchannelmux:
25
- public std::enable_shared_from_this< projectchannelmux >
26
- {
25
+ public std::enable_shared_from_this< projectchannelmux > {
26
+
27
27
  public:
28
28
  projectchannelmux( boost::asio::io_context &iocontext );
29
29
  ~projectchannelmux();
@@ -58,6 +58,8 @@ private:
58
58
 
59
59
  rawsound added;
60
60
  rawsound subtracted;
61
+
62
+ bool active;
61
63
  };
62
64
 
63
65
  #endif /* PROJECTRTPCHANNELMUX_H */
@@ -87,7 +87,6 @@ codecx::codecx() :
87
87
  ilbcencoder( nullptr ),
88
88
  ilbcdecoder( nullptr ),
89
89
  lpfilter(),
90
- resamplelastsample( 0 ),
91
90
  l168kref(),
92
91
  l1616kref(),
93
92
  pcmaref(),
@@ -146,7 +145,6 @@ Do enough to manage missing packets.
146
145
  void codecx::restart( void ) {
147
146
  this->lpfilter.reset();
148
147
  this->dcpowerfilter.reset();
149
- this->resamplelastsample = 0;
150
148
  this->_hasdata = false;
151
149
  }
152
150
 
@@ -160,19 +158,17 @@ bool codecx::alaw2ulaw( void ) {
160
158
  if( this->pcmaref.isdirty() ) return false;
161
159
 
162
160
  uint8_t *inbufptr, *outbufptr;
163
- size_t insize;
164
161
 
165
- insize = this->pcmaref.size();
166
162
  inbufptr = this->pcmaref.c_str();
167
163
  outbufptr = this->pcmuref.c_str();
168
- this->pcmuref.size( insize );
164
+ this->pcmuref.size( G711PAYLOADBYTES );
169
165
 
170
166
  if( nullptr == outbufptr || nullptr == inbufptr ) {
171
167
  std::cerr << "PCMA NULLPTR shouldn't happen (" << (void*)outbufptr << ", " << (void*)inbufptr << ")" << std::endl;
172
168
  return false;
173
169
  }
174
170
 
175
- for( size_t i = 0; i < insize; i++ ) {
171
+ for( size_t i = 0; i < G711PAYLOADBYTES; i++ ) {
176
172
  *outbufptr = alaw_to_ulaw_table[ *inbufptr ];
177
173
  inbufptr++;
178
174
  outbufptr++;
@@ -187,19 +183,17 @@ bool codecx::ulaw2alaw( void ) {
187
183
  if( this->pcmuref.isdirty() ) return false;
188
184
 
189
185
  uint8_t *inbufptr, *outbufptr;
190
- size_t insize;
191
186
 
192
- insize = this->pcmuref.size();
193
187
  inbufptr = this->pcmuref.c_str();
194
188
  outbufptr = this->pcmaref.c_str();
195
- this->pcmaref.size( insize );
189
+ this->pcmaref.size( G711PAYLOADBYTES );
196
190
 
197
191
  if( nullptr == outbufptr || nullptr == inbufptr ) {
198
192
  std::cerr << "PCMU NULLPTR shouldn't happen(" << (void*)outbufptr << ", " << (void*)inbufptr << ")" << std::endl;
199
193
  return false;
200
194
  }
201
195
 
202
- for( size_t i = 0; i < insize; i++ ) {
196
+ for( size_t i = 0; i < G711PAYLOADBYTES; i++ ) {
203
197
  *outbufptr = ulaw_to_alaw_table[ *inbufptr ];
204
198
  inbufptr++;
205
199
  outbufptr++;
@@ -217,25 +211,22 @@ bool codecx::g711tol16( void )
217
211
  {
218
212
  uint8_t *in;
219
213
  int16_t *convert;
220
- size_t insize;
221
214
 
222
215
  if( this->pcmaref.size() > 0 && !this->pcmaref.isdirty() ) {
223
216
  in = this->pcmaref.c_str();
224
217
  convert = _pcmatol16;
225
- insize = this->pcmaref.size();
226
218
  } else if ( this->pcmuref.size() > 0 && !this->pcmuref.isdirty() ) {
227
219
  in = this->pcmuref.c_str();
228
220
  convert = _pcmutol16;
229
- insize = this->pcmuref.size();
230
221
  } else {
231
222
  return false;
232
223
  }
233
224
 
234
- this->l168kref.malloc( insize, sizeof( int16_t ), L168KPAYLOADTYPE );
225
+ this->l168kref.malloc( G711PAYLOADSAMPLES, sizeof( int16_t ), L168KPAYLOADTYPE );
235
226
 
236
227
  int16_t *out = ( int16_t * ) this->l168kref.c_str();
237
228
 
238
- for( size_t i = 0; i < insize; i++ ) {
229
+ for( size_t i = 0; i < G711PAYLOADSAMPLES; i++ ) {
239
230
  *out = convert[ *in ];
240
231
  in++;
241
232
  out++;
@@ -257,11 +248,10 @@ bool codecx::l16topcma( void )
257
248
  uint8_t *out = this->pcmaref.c_str();
258
249
 
259
250
  int16_t *in;
260
- size_t l168klength = this->l168kref.size();
261
251
  in = ( int16_t * ) this->l168kref.c_str();
262
252
 
263
253
  uint16_t index;
264
- for( size_t i = 0; i < l168klength; i++ ) {
254
+ for( size_t i = 0; i < G711PAYLOADBYTES; i++ ) {
265
255
  index = *in + 32768;
266
256
  *out = _l16topcma[ index ];
267
257
  in++;
@@ -281,11 +271,10 @@ bool codecx::l16topcmu( void ) {
281
271
  uint8_t *out = this->pcmuref.c_str();;
282
272
 
283
273
  int16_t *in;
284
- size_t l168klength = this->l168kref.size();
285
274
  in = ( int16_t * ) this->l168kref.c_str();
286
275
 
287
276
  uint16_t index;
288
- for( size_t i = 0; i < l168klength; i++ ) {
277
+ for( size_t i = 0; i < G711PAYLOADBYTES; i++ ) {
289
278
  index = *in + 32768;
290
279
  *out = _l16topcmu[ index ];
291
280
  in++;
@@ -315,43 +304,16 @@ bool codecx::ilbctol16( void ) {
315
304
 
316
305
  int16_t l168klength = WebRtcIlbcfix_Decode( this->ilbcdecoder,
317
306
  ( ilbcencodedval ) this->ilbcref.c_str(),
318
- this->ilbcref.size(),
307
+ ILBC20PAYLOADBYTES,
319
308
  ( ilbcdecodedval )this->l168kref.c_str(),
320
309
  &speechType
321
310
  );
322
311
 
323
312
  if( -1 == l168klength ) {
324
- this->l168kref.size( 0 );
325
313
  return false;
326
314
  }
327
315
 
328
316
  this->l168kref.dirty( false );
329
- this->l168kref.size( l168klength );
330
- return true;
331
- }
332
-
333
- /*!md
334
- ## l16tog722
335
- As it says.
336
- */
337
- bool codecx::l16tog722( void ) {
338
- if( 0 == this->l1616kref.size() ) return false;
339
- if( this->l1616kref.isdirty() ) return false;
340
-
341
- if( nullptr == this->g722encoder ) {
342
- this->g722encoder = g722_encode_init( NULL, 64000, G722_PACKED );
343
- }
344
-
345
- int len = g722_encode( this->g722encoder, this->g722ref.c_str(), ( int16_t * ) this->l1616kref.c_str(), this->g722ref.size() * 2 );
346
-
347
- if( len > 0 ) {
348
- this->g722ref.size( len );
349
- this->g722ref.dirty( false );
350
- } else {
351
- std::cerr << "g722_encode didn't encode any data" << std::endl;
352
- this->g722ref.size( 0 );
353
- }
354
-
355
317
  return true;
356
318
  }
357
319
 
@@ -375,11 +337,10 @@ bool codecx::l16toilbc( void ) {
375
337
 
376
338
  int16_t len = WebRtcIlbcfix_Encode( this->ilbcencoder,
377
339
  ( ilbcdecodedval ) this->l168kref.c_str(),
378
- this->l168kref.size(),
340
+ L16PAYLOADSAMPLES,
379
341
  ( ilbcencodedval ) this->ilbcref.c_str()
380
342
  );
381
343
  if ( len > 0 ) {
382
- this->ilbcref.size( len );
383
344
  this->ilbcref.dirty( false );
384
345
  return true;
385
346
  }
@@ -390,6 +351,33 @@ bool codecx::l16toilbc( void ) {
390
351
  }
391
352
 
392
353
 
354
+ /*!md
355
+ ## l16tog722
356
+ As it says.
357
+ */
358
+ bool codecx::l16tog722( void ) {
359
+
360
+ if( 0 == this->l1616kref.size() ) return false;
361
+ if( this->l1616kref.isdirty() ) return false;
362
+
363
+ if( nullptr == this->g722encoder ) {
364
+ this->g722encoder = g722_encode_init( NULL, 64000, G722_PACKED );
365
+ }
366
+
367
+ // TODO - when we convert to g722 - 722 buffer is always output -so malloc need sto be able to detect that.
368
+ //this->g722ref.malloc( G722PAYLOADSAMPLES, sizeof( int8_t ), G722PAYLOADTYPE );
369
+
370
+ int len = g722_encode( this->g722encoder, this->g722ref.c_str(), ( int16_t * ) this->l1616kref.c_str(), L1616PAYLOADSAMPLES );
371
+
372
+ if( 160 != len ) {
373
+ std::cerr << "g722_encode didn't encode correct length of data" << std::endl;
374
+ return false;
375
+ }
376
+
377
+ this->g722ref.dirty( false );
378
+ return true;
379
+ }
380
+
393
381
  /*!md
394
382
  ## g722tol16
395
383
  As it says.
@@ -398,8 +386,7 @@ bool codecx::g722tol16( void ) {
398
386
  if( 0 == this->g722ref.size() ) return false;
399
387
  if( this->g722ref.isdirty() ) return false;
400
388
 
401
- /* x 2 for 16 bit instead of 8 and then x 2 sample rate */
402
- this->l1616kref.malloc( this->g722ref.size(), sizeof( int16_t ), L1616KPAYLOADTYPE );
389
+ this->l1616kref.malloc( L1616PAYLOADSAMPLES, sizeof( int16_t ), L1616KPAYLOADTYPE );
403
390
 
404
391
  if( nullptr == this->g722decoder ) {
405
392
  this->g722decoder = g722_decode_init( NULL, 64000, G722_PACKED );
@@ -411,9 +398,10 @@ bool codecx::g722tol16( void ) {
411
398
  size_t l1616klength = g722_decode( this->g722decoder,
412
399
  ( int16_t * ) this->l1616kref.c_str(),
413
400
  this->g722ref.c_str(),
414
- this->g722ref.size() );
401
+ G722PAYLOADBYTES );
415
402
 
416
- this->l1616kref.size( l1616klength );
403
+ if( 320 != l1616klength ) return false;
404
+
417
405
  this->l1616kref.dirty( false );
418
406
  return true;
419
407
  }
@@ -423,23 +411,19 @@ bool codecx::g722tol16( void ) {
423
411
  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.
424
412
  */
425
413
  bool codecx::l16lowtowideband( void ) {
426
- size_t l168klength = this->l168kref.size();
427
414
 
428
- if( 0 == l168klength ) return false;
415
+ if( 0 == this->l168kref.size() ) return false;
429
416
  if( this->l168kref.isdirty() ) return false;
430
417
 
431
- this->l1616kref.malloc( l168klength, sizeof( int16_t ), L1616KPAYLOADTYPE );
418
+ this->l1616kref.malloc( L1616PAYLOADSAMPLES, sizeof( int16_t ), L1616KPAYLOADTYPE );
432
419
 
433
420
  int16_t *in = ( int16_t * ) this->l168kref.c_str();
434
421
  int16_t *out = ( int16_t * ) this->l1616kref.c_str();
435
422
 
436
- for( size_t i = 0; i < l168klength; i++ ) {
437
- *out = ( ( *in - this->resamplelastsample ) / 2 ) + this->resamplelastsample;
438
- this->resamplelastsample = *in;
423
+ for( size_t i = 0; i < L16PAYLOADSAMPLES; i++ ) {
424
+ *out = this->lpfilter.execute( *in );
439
425
  out++;
440
-
441
- *out = *in;
442
-
426
+ *out = this->lpfilter.execute( 0 );
443
427
  out++;
444
428
  in++;
445
429
  }
@@ -457,7 +441,7 @@ bool codecx::requirewideband( void ) {
457
441
  if( this->g722tol16() ) return true;
458
442
  if( !this->g711tol16() )
459
443
  {
460
- if( this->ilbctol16() ) return false;
444
+ if( !this->ilbctol16() ) return false;
461
445
  }
462
446
 
463
447
  return this->l16lowtowideband();
@@ -467,22 +451,20 @@ bool codecx::requirewideband( void ) {
467
451
  ## l16widetolowband
468
452
  Downsample our L16 wideband samples to 8K. Pass through filter then grab every other sample.
469
453
  */
470
- bool codecx::l16widetonarrowband( void )
471
- {
472
- size_t l1616klength = this->l1616kref.size();
454
+ bool codecx::l16widetonarrowband( void ) {
473
455
 
474
- if( 0 == l1616klength ) return false;
456
+ if( 0 == this->l1616kref.size() ) return false;
475
457
  if( this->l1616kref.isdirty() ) return false;
476
458
 
477
- this->l168kref.malloc( l1616klength / 2, sizeof( int16_t ), L168KPAYLOADTYPE );
459
+ this->l168kref.malloc( L16PAYLOADSAMPLES, sizeof( int16_t ), L168KPAYLOADTYPE );
478
460
 
479
461
  int16_t *out = ( int16_t * ) this->l168kref.c_str();
480
462
  int16_t *in = ( int16_t * ) this->l1616kref.c_str();
481
463
 
482
- for( size_t i = 0; i < l1616klength / 2; i++ ) {
483
- lpfilter.execute( *in );
464
+ for( size_t i = 0; i < L16PAYLOADSAMPLES; i++ ) {
465
+ this->lpfilter.execute( *in );
484
466
  in++;
485
- *out = lpfilter.execute( *in );
467
+ *out = this->lpfilter.execute( *in );
486
468
  in++;
487
469
  out++;
488
470
  }
@@ -628,52 +610,43 @@ We pass a packet in, then we can take multiple out - i.e. we may want different
628
610
 
629
611
  Have a think about if this is where we want to mix audio data.
630
612
  */
631
- codecx& operator << ( codecx& c, rtppacket& pk )
632
- {
613
+ codecx& operator << ( codecx& c, rtppacket& pk ) {
633
614
  c.inpkcount++;
634
615
  rawsound r = rawsound( pk );
635
616
  c << r;
636
617
  return c;
637
618
  }
638
619
 
639
- codecx& operator << ( codecx& c, rawsound& raw )
640
- {
620
+ codecx& operator << ( codecx& c, rawsound& raw ) {
641
621
  int inpayloadtype = raw.getformat();
642
622
 
643
- switch( inpayloadtype )
644
- {
645
- case PCMAPAYLOADTYPE:
646
- {
623
+ switch( inpayloadtype ) {
624
+ case PCMAPAYLOADTYPE: {
647
625
  c.pcmaref = raw;
648
626
  c._hasdata = true;
649
627
  break;
650
628
  }
651
- case PCMUPAYLOADTYPE:
652
- {
629
+ case PCMUPAYLOADTYPE: {
653
630
  c.pcmuref = raw;
654
631
  c._hasdata = true;
655
632
  break;
656
633
  }
657
- case ILBCPAYLOADTYPE:
658
- {
634
+ case ILBCPAYLOADTYPE: {
659
635
  c.ilbcref = raw;
660
636
  c._hasdata = true;
661
637
  break;
662
638
  }
663
- case G722PAYLOADTYPE:
664
- {
639
+ case G722PAYLOADTYPE: {
665
640
  c.g722ref = raw;
666
641
  c._hasdata = true;
667
642
  break;
668
643
  }
669
- case L168KPAYLOADTYPE:
670
- {
644
+ case L168KPAYLOADTYPE: {
671
645
  c.l168kref = raw;
672
646
  c._hasdata = true;
673
647
  break;
674
648
  }
675
- case L1616KPAYLOADTYPE:
676
- {
649
+ case L1616KPAYLOADTYPE: {
677
650
  c.l1616kref = raw;
678
651
  c._hasdata = true;
679
652
  break;
@@ -85,10 +85,8 @@ private:
85
85
  ilbcencinst *ilbcencoder;
86
86
  ilbcdecinst *ilbcdecoder;
87
87
 
88
- /* If we require downsampling */
88
+ /* If we require up/downsampling */
89
89
  lowpass3_4k16k lpfilter;
90
- /* When we up sample we need to interpolate so need last sample */
91
- int16_t resamplelastsample;
92
90
 
93
91
  rawsound l168kref;
94
92
  rawsound l1616kref;
@@ -147,24 +147,17 @@ rawsound::~rawsound()
147
147
  ## zero
148
148
  Reset buffer with zero
149
149
  */
150
- void rawsound::zero( void )
151
- {
152
- if( nullptr != this->data )
153
- {
154
- size_t zeroamount = this->samples * this->bytespersample;
155
- if( L1616KPAYLOADTYPE == format )
156
- {
157
- zeroamount = zeroamount * 2;
158
- }
150
+ void rawsound::zero( void ) {
151
+ if( nullptr == this->data ) return;
159
152
 
160
- if( 0 != this->allocatedlength && zeroamount > this->allocatedlength )
161
- {
162
- std::cerr << "Trying to zero memory but capped by allocated amount" << std::endl;
163
- zeroamount = this->allocatedlength;
164
- }
153
+ size_t zeroamount = this->samples * this->bytespersample;
165
154
 
166
- memset( this->data, 0, zeroamount );
155
+ if( 0 != this->allocatedlength && zeroamount > this->allocatedlength ) {
156
+ std::cerr << "Trying to zero memory but capped by allocated amount" << std::endl;
157
+ zeroamount = this->allocatedlength;
167
158
  }
159
+
160
+ memset( this->data, 0, zeroamount );
168
161
  }
169
162
 
170
163
  /*
@@ -172,49 +165,31 @@ void rawsound::zero( void )
172
165
  * Target (this) MUST have memory available.
173
166
  * format must be set appropriatly before the call
174
167
  */
175
- void rawsound::copy( uint8_t *src, size_t len )
176
- {
177
- if( nullptr != this->data )
178
- {
179
- memcpy( this->data,
180
- src,
181
- len );
168
+ void rawsound::copy( uint8_t *src, size_t len ) {
169
+ if( nullptr == this->data ) return;
182
170
 
183
- switch( this->format )
184
- {
185
- case L168KPAYLOADTYPE:
186
- case L1616KPAYLOADTYPE:
187
- {
188
- this->samples = len / 2;
189
- break;
190
- }
191
- default:
192
- {
193
- this->samples = len;
194
- break;
195
- }
196
- }
197
- }
171
+ memcpy( this->data,
172
+ src,
173
+ len );
198
174
  }
199
175
 
200
176
  /*
201
177
  ## copy (from other)
202
178
  * Target (this) MUST have data allocated.
203
179
  */
204
- void rawsound::copy( rawsound &other )
205
- {
206
- if( nullptr != this->data && this->samples >= other.samples )
207
- {
208
- this->bytespersample = other.bytespersample;
209
- this->samplerate = other.samplerate;
210
- this->format = other.format;
211
- this->samples = other.samples;
180
+ void rawsound::copy( rawsound &other ) {
212
181
 
213
- memcpy( this->data,
214
- other.data,
215
- other.samples * other.bytespersample );
182
+ if( nullptr == this->data || this->samples < other.samples ) return;
183
+
184
+ this->bytespersample = other.bytespersample;
185
+ this->samplerate = other.samplerate;
186
+ this->format = other.format;
187
+ this->samples = other.samples;
188
+
189
+ memcpy( this->data,
190
+ other.data,
191
+ other.samples * other.bytespersample );
216
192
 
217
- }
218
193
  }
219
194
 
220
195
  /*
@@ -237,15 +212,11 @@ void rawsound::subtract( void )
237
212
  ## malloc
238
213
  Allocate our own memory.
239
214
  */
240
- void rawsound::malloc( size_t samplecount, size_t bytespersample, int format )
241
- {
215
+ void rawsound::malloc( size_t samplecount, size_t bytespersample, int format ) {
242
216
  this->samples = samplecount;
243
217
  this->bytespersample = bytespersample;
244
218
  this->format = format;
245
219
  size_t requiredsize = samplecount * bytespersample;
246
- if( L1616KPAYLOADTYPE == format ) {
247
- requiredsize = requiredsize * 2;
248
- }
249
220
 
250
221
  if( this->allocatedlength > 0 ) {
251
222
  if( this->allocatedlength >= requiredsize ) {
@@ -262,46 +233,39 @@ void rawsound::malloc( size_t samplecount, size_t bytespersample, int format )
262
233
  ## operator +=
263
234
  Used for mixing audio
264
235
  */
265
- rawsound& rawsound::operator+=( codecx& rhs )
266
- {
236
+ rawsound& rawsound::operator+=( codecx& rhs ) {
267
237
  size_t length;
268
238
 
269
239
  int16_t *in;
270
240
  int16_t *out = ( int16_t * ) this->data;
271
241
 
272
- switch( this->format )
273
- {
274
- case L168KPAYLOADTYPE:
275
- {
242
+ switch( this->format ) {
243
+ case L168KPAYLOADTYPE: {
276
244
  rhs.requirenarrowband();
277
-
245
+ if( rhs.l168kref.isdirty() ) return *this;
278
246
  length = rhs.l168kref.size();
279
247
  in = ( int16_t * ) rhs.l168kref.c_str();
280
248
  break;
281
249
  }
282
- case L1616KPAYLOADTYPE:
283
- {
250
+ case L1616KPAYLOADTYPE: {
284
251
  rhs.requirewideband();
285
-
252
+ if( rhs.l1616kref.isdirty() ) return *this;
286
253
  length = rhs.l1616kref.size();
287
254
  in = ( int16_t * ) rhs.l1616kref.c_str();
288
255
  break;
289
256
  }
290
- default:
291
- {
257
+ default: {
292
258
  std::cerr << "Attemping to perform an addition on a none linear format" << std::endl;
293
259
  return *this;
294
260
  }
295
261
  }
296
262
 
297
- if( length > this->samples )
298
- {
263
+ if( length > this->samples ) {
299
264
  std::cerr << "We have been asked to add samples but we don't have enough space" << std::endl;
300
265
  length = this->samples;
301
266
  }
302
267
 
303
- for( size_t i = 0; i < length; i++ )
304
- {
268
+ for( size_t i = 0; i < length; i++ ) {
305
269
  *out++ += *in++;
306
270
  }
307
271
 
@@ -132,9 +132,9 @@ module.exports.readpcap = async ( file, maxnumberofpackets = 5000 ) => {
132
132
  etherpacket.ipv4.udp = {}
133
133
  etherpacket.ipv4.udp.srcport = parseInt( toHex( etherpacket.ipv4.data[ 20 ] ) + toHex( etherpacket.ipv4.data[ 21 ] ), 16 )
134
134
  etherpacket.ipv4.udp.dstport = parseInt( toHex( etherpacket.ipv4.data[ 22 ] ) + toHex( etherpacket.ipv4.data[ 23 ] ), 16 )
135
- etherpacket.ipv4.udp.length = parseInt( toHex( etherpacket.ipv4.data[ 24 ] ) + toHex( etherpacket.ipv4.data[ 25 ] ), 16 )
135
+ etherpacket.ipv4.udp.length = parseInt( toHex( etherpacket.ipv4.data[ 24 ] ) + toHex( etherpacket.ipv4.data[ 25 ] ), 16 ) - /* udp header */ 8
136
136
  etherpacket.ipv4.udp.checksum = parseInt( toHex( etherpacket.ipv4.data[ 26 ] ) + toHex( etherpacket.ipv4.data[ 27 ] ), 16 )
137
- etherpacket.ipv4.udp.data = etherpacket.ipv4.data.subarray( 28, 28 + etherpacket.ipv4.data.length )
137
+ etherpacket.ipv4.udp.data = etherpacket.ipv4.data.subarray( 28, 28 + etherpacket.ipv4.udp.length )
138
138
  break
139
139
  case 6:
140
140
  /* TCP */
@@ -1,9 +1,9 @@
1
1
 
2
2
  const expect = require( "chai" ).expect
3
- const projectrtp = require( "../../index.js" ).projectrtp
3
+ const projectrtp = require( "../../index" ).projectrtp
4
4
  const dgram = require( "dgram" )
5
5
 
6
- const pcap = require( "./pcap.js" )
6
+ const pcap = require( "./pcap" )
7
7
 
8
8
  /*
9
9
  i.e. the RTP payload
@@ -1,7 +1,7 @@
1
1
  /* if we want to see what is going on - use nodeplotlib instead of our placeholder */
2
- //const npl = require( "nodeplotlib" )
2
+ const npl = require( "nodeplotlib" )
3
3
  // eslint-disable-next-line no-unused-vars
4
- const npl = { plot: ( /** @type {any} */ a ) => {} }
4
+ //const npl = { plot: ( /** @type {any} */ a ) => {} }
5
5
 
6
6
 
7
7
  const fft = require( "fft-js" ).fft
@@ -9,6 +9,7 @@ const projectrtp = require( "../../index" ).projectrtp
9
9
  const expect = require( "chai" ).expect
10
10
  const dgram = require( "dgram" )
11
11
  const fs = require( "fs" )
12
+ const pcap = require( "./pcap" )
12
13
 
13
14
 
14
15
  /*
@@ -167,6 +168,20 @@ function pcmutolinear( inarray ) {
167
168
  return out
168
169
  }
169
170
 
171
+ /**
172
+ * Send Buffer to server at required time
173
+ * @param { number } sendtime
174
+ * @param { Buffer } pk
175
+ * @param { number } dstport
176
+ * @param { dgram.Socket } server
177
+ * @returns
178
+ */
179
+ function sendpayload( sendtime, pk, dstport, server ) {
180
+ return setTimeout( () => {
181
+ server.send( pk, dstport, "localhost" )
182
+ }, sendtime )
183
+ }
184
+
170
185
  /**
171
186
  *
172
187
  * @param { number } sn - should start from 0 which we use to index into the supplied data buffer
@@ -653,5 +668,154 @@ describe( "Transcode", function() {
653
668
 
654
669
  await fs.promises.unlink( "/tmp/ukringing.wav" ).catch( () => {} )
655
670
  } )
671
+
672
+ it( "replay captured g722 from poly", async () => {
673
+
674
+ const g722endpoint = dgram.createSocket( "udp4" )
675
+ g722endpoint.on( "message", function() {} )
676
+
677
+ const pcmuendpoint = dgram.createSocket( "udp4" )
678
+ let receivedpcmu = []
679
+ pcmuendpoint.on( "message", function( msg ) {
680
+ pcmuendpoint.send( msg, pcmuchannel.local.port, "localhost" )
681
+
682
+ receivedpcmu = [ ...receivedpcmu, ...Array.from( pcmutolinear( parsepk( msg ).payload ) ) ]
683
+ } )
684
+
685
+ g722endpoint.bind()
686
+ await new Promise( resolve => g722endpoint.on( "listening", resolve ) )
687
+ pcmuendpoint.bind()
688
+ await new Promise( resolve => pcmuendpoint.on( "listening", resolve ) )
689
+
690
+ const allstats = {}
691
+
692
+ const g722channel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": g722endpoint.address().port, "codec": 9 } }, function( d ) {
693
+ if( "close" === d.action ) {
694
+ g722endpoint.close()
695
+ pcmuendpoint.close()
696
+ pcmuchannel.close()
697
+ allstats.achannel = { stats: d.stats }
698
+ }
699
+ } )
700
+
701
+ let done
702
+ const allclose = new Promise( resolve => done = resolve )
703
+ const pcmuchannel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": pcmuendpoint.address().port, "codec": 0 } }, function( d ) {
704
+ if( "close" === d.action ) {
705
+ allstats.bchannel = { stats: d.stats }
706
+ done()
707
+ }
708
+ } )
709
+
710
+ const ourpcap = ( await pcap.readpcap( "test/interface/pcaps/440hzinbackgroundg722.pcap" ) ).slice( 0, 50 )
711
+
712
+ g722channel.mix( pcmuchannel )
713
+
714
+ const offset = 0
715
+ ourpcap.forEach( ( packet ) => {
716
+ if( packet.ipv4 && packet.ipv4.udp && 10018 == packet.ipv4.udp.dstport ) {
717
+ sendpayload( ( 1000 * packet.ts_sec_offset ) - offset, packet.ipv4.udp.data, g722channel.local.port, g722endpoint )
718
+ }
719
+ } )
720
+
721
+ await new Promise( resolve => setTimeout( resolve, 1400 ) )
722
+ g722channel.close()
723
+ await allclose
724
+
725
+ npl.plot( [ {
726
+ y: Array.from( receivedpcmu ),
727
+ type: "scatter"
728
+ } ] )
729
+
730
+ const amps = ampbyfrequency( Int16Array.from( receivedpcmu ) )
731
+ const bin = 225
732
+ expect( 20000 < amps[ bin ] ).to.be.true
733
+
734
+ npl.plot( [ {
735
+ y: Array.from( amps ),
736
+ type: "scatter"
737
+ } ] )
738
+
739
+ } )
740
+
741
+ it( "replay captured g722 no transcode from poly 3 way mix", async () => {
742
+
743
+ const g722endpoint = dgram.createSocket( "udp4" )
744
+ g722endpoint.on( "message", function() {} )
745
+
746
+ const pcmuendpoint = dgram.createSocket( "udp4" )
747
+ let receivedpcmu = []
748
+
749
+ pcmuendpoint.on( "message", function( msg ) {
750
+ pcmuendpoint.send( msg, pcmuchannel.local.port, "localhost" )
751
+
752
+ receivedpcmu = [ ...receivedpcmu, ...Array.from( pcmutolinear( parsepk( msg ).payload ) ) ]
753
+ } )
754
+
755
+ g722endpoint.bind()
756
+ await new Promise( resolve => g722endpoint.on( "listening", resolve ) )
757
+ pcmuendpoint.bind()
758
+ await new Promise( resolve => pcmuendpoint.on( "listening", resolve ) )
759
+
760
+ const allstats = {}
761
+
762
+ const g722channel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": g722endpoint.address().port, "codec": 9 } }, function( d ) {
763
+ if( "close" === d.action ) {
764
+ g722endpoint.close()
765
+ pcmuendpoint.close()
766
+ pcmuchannel.close()
767
+ secondg722.close()
768
+ allstats.achannel = { stats: d.stats }
769
+ }
770
+ } )
771
+
772
+ let done
773
+ const allclose = new Promise( resolve => done = resolve )
774
+ const pcmuchannel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": pcmuendpoint.address().port, "codec": 0 } }, function( d ) {
775
+ if( "close" === d.action ) {
776
+ allstats.bchannel = { stats: d.stats }
777
+ done()
778
+ }
779
+ } )
780
+
781
+ const secondg722 = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": 9990, "codec": 9 } }, function( d ) {
782
+ if( "close" === d.action ) {
783
+ allstats.bchannel = { stats: d.stats }
784
+ done()
785
+ }
786
+ } )
787
+
788
+ const ourpcap = ( await pcap.readpcap( "test/interface/pcaps/440hzinbackgroundg722.pcap" ) ).slice( 0, 50 )
789
+
790
+ g722channel.mix( pcmuchannel )
791
+ g722channel.mix( secondg722 )
792
+
793
+ const offset = 0
794
+ ourpcap.forEach( ( packet ) => {
795
+ if( packet.ipv4 && packet.ipv4.udp && 10018 == packet.ipv4.udp.dstport ) {
796
+ sendpayload( ( 1000 * packet.ts_sec_offset ) - offset, packet.ipv4.udp.data, g722channel.local.port, g722endpoint )
797
+ }
798
+ } )
799
+
800
+ await new Promise( resolve => setTimeout( resolve, 1400 ) )
801
+ g722channel.close()
802
+ await allclose
803
+
804
+ npl.plot( [ {
805
+ y: Array.from( receivedpcmu ),
806
+ type: "scatter"
807
+ } ] )
808
+
809
+ const amps = ampbyfrequency( Int16Array.from( receivedpcmu ) )
810
+
811
+ npl.plot( [ {
812
+ y: Array.from( amps ),
813
+ type: "scatter"
814
+ } ] )
815
+
816
+ const bin = 430
817
+ expect( 20000 < amps[ bin ] ).to.be.true
818
+
819
+ } )
656
820
  } )
657
821