@babblevoice/projectrtp 2.5.20 → 2.5.25

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.
@@ -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
 
@@ -101,6 +101,7 @@ soundfilereader::soundfilereader( std::string &url ) :
101
101
  blocksize( L16NARROWBANDBYTES ),
102
102
  badheader( false ),
103
103
  headerread( false ),
104
+ bodyread( false ),
104
105
  initseekmseconds( 0 ),
105
106
  ploadtype( L168KPAYLOADTYPE ) {
106
107
 
@@ -127,15 +128,13 @@ soundfilereader::soundfilereader( std::string &url ) :
127
128
  /* read */
128
129
  if ( aio_read( &this->cbwavheader ) == -1 ) {
129
130
  fprintf( stderr, "aio_read read of header failed in soundfile\n" );
130
- close( this->file );
131
- this->file = -1;
131
+ this->badheader = true;
132
132
  return;
133
133
  }
134
134
 
135
135
  if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
136
136
  fprintf( stderr, "aio_read read of block failed in soundfile" );
137
- close( this->file );
138
- this->file = -1;
137
+ this->bodyread = true;
139
138
  return;
140
139
  }
141
140
 
@@ -153,8 +152,7 @@ soundfilereader::~soundfilereader() {
153
152
  # create
154
153
  Shared pointer version of us.
155
154
  */
156
- soundfilereader::pointer soundfilereader::create( std::string url )
157
- {
155
+ soundfilereader::pointer soundfilereader::create( std::string url ) {
158
156
  return pointer( new soundfilereader( url ) );
159
157
  }
160
158
 
@@ -177,16 +175,17 @@ bool soundfilereader::read( rawsound &out ) {
177
175
 
178
176
  if( this->badheader ) {
179
177
  fprintf( stderr, "Bad wav file\n" );
180
- return false;
181
- }
182
-
183
- if( !this->headerread &&
184
- aio_error( &this->cbwavheader ) == EINPROGRESS ) {
185
- fprintf( stderr, "Read of soundfile wav header has not completed\n" );
186
- return false;
178
+ out.zero();
179
+ return true;
187
180
  }
188
181
 
189
182
  if( !this->headerread ) {
183
+ if( aio_error( &this->cbwavheader ) == EINPROGRESS ) {
184
+ fprintf( stderr, "Read of soundfile wav header has not completed\n" );
185
+ out.zero();
186
+ return true;
187
+ }
188
+
190
189
  if( 'W' != this->ourwavheader.wave_header[ 0 ] ) {
191
190
  this->badheader = true;
192
191
  }
@@ -197,7 +196,9 @@ bool soundfilereader::read( rawsound &out ) {
197
196
  break;
198
197
  default:
199
198
  fprintf( stderr, "Bad sample rate in wav\n" );
200
- return false;
199
+ this->badheader = true;
200
+ out.zero();
201
+ return true;
201
202
  }
202
203
 
203
204
  this->ploadtype = L168KPAYLOADTYPE;
@@ -211,7 +212,9 @@ bool soundfilereader::read( rawsound &out ) {
211
212
  this->ploadtype = L1616KPAYLOADTYPE;
212
213
  this->blocksize = L16WIDEBANDBYTES;
213
214
  } else {
214
- return false;
215
+ this->badheader = true;
216
+ out.zero();
217
+ return true;
215
218
  }
216
219
  break;
217
220
  }
@@ -236,8 +239,10 @@ bool soundfilereader::read( rawsound &out ) {
236
239
  break;
237
240
  }
238
241
  default: {
242
+ this->badheader = true;
243
+ out.zero();
239
244
  fprintf( stderr, "Bad audio format in wav\n" );
240
- return false;
245
+ return true;
241
246
  }
242
247
  }
243
248
  this->headerread = true;
@@ -245,12 +250,15 @@ bool soundfilereader::read( rawsound &out ) {
245
250
 
246
251
  if( this->initseekmseconds > 0 ) {
247
252
  this->setposition( this->initseekmseconds );
253
+ out.zero();
248
254
  return true;
249
255
  }
250
256
 
251
257
  if( aio_error( &this->cbwavblock[ this->currentcbindex ] ) == EINPROGRESS ) {
252
258
  fprintf( stderr, "Read of soundfile wav block has not completed\n" );
253
- return false;
259
+ /* return some silence */
260
+ out.zero();
261
+ return true;
254
262
  }
255
263
 
256
264
  /* success? */
@@ -258,12 +266,20 @@ bool soundfilereader::read( rawsound &out ) {
258
266
 
259
267
  if( -1 == numbytes ) {
260
268
  fprintf( stderr, "Bad call to aio_return\n" );
261
- return false;
269
+ this->bodyread = true;
270
+ out.zero();
271
+ return true;
262
272
  }
263
273
 
264
274
  uint8_t *current = ( uint8_t * ) this->cbwavblock[ this->currentcbindex ].aio_buf;
265
275
  out = rawsound( current, this->blocksize, this->ploadtype, this->ourwavheader.sample_rate );
266
276
 
277
+ if( numbytes < this->blocksize ) {
278
+ this->bodyread = true;
279
+ /* TODO if we get a partial read we probably should not zero that part */
280
+ out.zero();
281
+ }
282
+
267
283
  /* Get the next block reading */
268
284
  auto lastreadoffset = this->cbwavblock[ this->currentcbindex ].aio_offset;
269
285
  this->currentcbindex = ( this->currentcbindex + 1 ) % SOUNDFILENUMBUFFERS;
@@ -277,10 +293,10 @@ bool soundfilereader::read( rawsound &out ) {
277
293
  this->cbwavblock[ this->currentcbindex ].aio_nbytes = this->blocksize;
278
294
 
279
295
  /* read next block */
280
- if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
281
- close( this->file );
282
- this->file = -1;
283
- return false;
296
+ if ( !this->bodyread && aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
297
+ this->bodyread = true;
298
+ out.zero();
299
+ return true;
284
300
  }
285
301
 
286
302
  return true;
@@ -291,11 +307,10 @@ bool soundfilereader::read( rawsound &out ) {
291
307
  Have we completed reading the file.
292
308
  */
293
309
  bool soundfilereader::complete( void ) {
294
- if( this->badheader || !this->headerread ) {
295
- return false;
296
- }
297
-
298
- return ( this->cbwavblock[ this->currentcbindex ].aio_offset + this->blocksize ) > this->ourwavheader.chunksize;
310
+ if( this->badheader ) return true;
311
+ if( !this->headerread ) return false;
312
+ if( -1 == this->file ) return true;
313
+ return this->bodyread;
299
314
  }
300
315
 
301
316
  /*!md
@@ -315,35 +330,31 @@ void soundfilereader::setposition( long mseconds ) {
315
330
  this->headerread = true;
316
331
  }
317
332
 
333
+ if( !this->headerread ) {
334
+ this->initseekmseconds = mseconds;
335
+ return;
336
+ }
318
337
 
319
- if( this->headerread ) {
320
- while( AIO_NOTCANCELED == aio_cancel( this->file, NULL ) );
338
+ this->bodyread = false;
339
+ while( AIO_NOTCANCELED == aio_cancel( this->file, NULL ) );
321
340
 
322
- this->currentcbindex = ( this->currentcbindex + 1 ) % SOUNDFILENUMBUFFERS;
341
+ this->currentcbindex = ( this->currentcbindex + 1 ) % SOUNDFILENUMBUFFERS;
323
342
 
324
- off_t our_aio_offset = ( this->ourwavheader.bit_depth /*16*/ / 8 ) * ( this->ourwavheader.sample_rate / 1000 ) * mseconds; /* bytes per sample */
325
- our_aio_offset = ( our_aio_offset / this->blocksize ) * this->blocksize; /* realign to the nearest block */
326
- our_aio_offset += sizeof( wavheader );
343
+ off_t our_aio_offset = ( this->ourwavheader.bit_depth /*16*/ / 8 ) * ( this->ourwavheader.sample_rate / 1000 ) * mseconds; /* bytes per sample */
344
+ our_aio_offset = ( our_aio_offset / this->blocksize ) * this->blocksize; /* realign to the nearest block */
345
+ our_aio_offset += sizeof( wavheader );
327
346
 
328
- for( auto i = 0; i < SOUNDFILENUMBUFFERS; i++ ) {
329
- memset( &this->cbwavblock[ i ], 0, sizeof( aiocb ) );
330
- this->cbwavblock[ i ].aio_nbytes = L16WIDEBANDBYTES;
331
- this->cbwavblock[ i ].aio_fildes = this->file;
332
- this->cbwavblock[ i ].aio_offset = sizeof( wavheader ) + our_aio_offset + ( i * L16WIDEBANDBYTES );
333
- this->cbwavblock[ i ].aio_buf = this->buffer + ( i * L16WIDEBANDBYTES );
334
- }
335
-
336
- /* read ahead */
337
- this->currentcbindex = 0;
338
- if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
339
- close( this->file );
340
- this->file = -1;
341
- }
347
+ for( auto i = 0; i < SOUNDFILENUMBUFFERS; i++ ) {
348
+ this->cbwavblock[ ( this->currentcbindex + i ) % SOUNDFILENUMBUFFERS ].aio_offset = our_aio_offset + ( i * L16WIDEBANDBYTES );
349
+ }
342
350
 
343
- this->initseekmseconds = 0;
344
- } else {
345
- this->initseekmseconds = mseconds;
351
+ /* read ahead */
352
+ if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
353
+ this->bodyread = true;
346
354
  }
355
+
356
+ this->initseekmseconds = 0;
357
+
347
358
  }
348
359
 
349
360
  long soundfilereader::offtomsecs( void ) {
@@ -423,8 +434,7 @@ soundfilewriter::~soundfilewriter() {
423
434
  # create
424
435
  Shared pointer for writing.
425
436
  */
426
- soundfilewriter::pointer soundfilewriter::create( std::string &url, int16_t numchannels, int32_t samplerate )
427
- {
437
+ soundfilewriter::pointer soundfilewriter::create( std::string &url, int16_t numchannels, int32_t samplerate ) {
428
438
  return pointer( new soundfilewriter( url, numchannels, samplerate ) );
429
439
  }
430
440
 
@@ -119,6 +119,7 @@ private:
119
119
  int blocksize;
120
120
  bool badheader;
121
121
  bool headerread;
122
+ bool bodyread;
122
123
  long initseekmseconds;
123
124
  int ploadtype;
124
125
  };
@@ -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 */
@@ -5,6 +5,7 @@ const dgram = require( "dgram" )
5
5
  const projectrtp = require( "../../index" ).projectrtp
6
6
  const node = require( "../../lib/node" ).interface
7
7
  const server = require( "../../lib/server" ).interface
8
+ const rtputil = require( "../util/rtp" )
8
9
 
9
10
  /**
10
11
  * Common channel tester
@@ -360,15 +361,9 @@ describe( "rtpchannel", function() {
360
361
  let totalsndiff = 0
361
362
  let totaltsdiff = 0
362
363
  server.on( "message", function( msg ) {
363
- let sn = 0
364
- sn = msg[ 2 ] << 8
365
- sn = sn | msg[ 3 ]
366
-
367
- let ts = 0
368
- ts = msg[ 4 ] << 24
369
- ts = ts | ( msg[ 5 ] << 16 )
370
- ts = ts | ( msg[ 6 ] << 8 )
371
- ts = ts | msg[ 7 ]
364
+ const pk = rtputil.parsepk( msg )
365
+ const sn = pk.sn
366
+ const ts = pk.ts
372
367
 
373
368
  if( -1 !== lastsn ) {
374
369
  totalsndiff += sn - lastsn - 1
@@ -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
@@ -66,16 +66,14 @@ function sendpk( sn, ts, sendtime, dstport, server, pt = 0, ssrc ) {
66
66
  /*
67
67
 
68
68
  */
69
- function senddtmf( sn, ts, sendtime, dstport, server, endofevent, ev ) {
69
+ function senddtmf( sn, ts, sendtime, dstport, server, endofevent, ev, pt = 101 ) {
70
70
 
71
71
  return setTimeout( () => {
72
72
  const ssrc = 25
73
- const pklength = 58
73
+ const pklength = 16
74
74
 
75
75
  const header = Buffer.alloc( pklength )
76
76
 
77
- const pt = 101
78
-
79
77
  header.writeUInt8( 0x80 )
80
78
  header.writeUInt8( pt, 1 ) // payload type
81
79
  header.writeUInt16BE( ( sn ) % ( 2**16 ), 2 )
@@ -715,6 +713,149 @@ describe( "dtmf", function() {
715
713
  } )
716
714
 
717
715
 
716
+ it( "mix 2 channels - pcmu <-> pcma and send DTMF different 2833 pt", async function() {
717
+
718
+ /*
719
+ When mixing 2 channels, we expect the second leg to receive the 2833 packets
720
+ and our server to emit events indicating the DTMF on the first channel.
721
+ */
722
+ this.timeout( 3000 )
723
+ this.slow( 2000 )
724
+
725
+ const endpointa = dgram.createSocket( "udp4" )
726
+ const endpointb = dgram.createSocket( "udp4" )
727
+
728
+ const receivedmessages = []
729
+
730
+ let endpointapkcount = 0
731
+ let endpointbpkcount = 0
732
+ let dtmfpkcount = 0
733
+
734
+ endpointa.on( "message", function( msg ) {
735
+ endpointapkcount++
736
+ expect( msg.length ).to.equal( 172 )
737
+ expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
738
+ } )
739
+
740
+ endpointb.on( "message", function( msg ) {
741
+ endpointbpkcount++
742
+ if( 101 == ( 0x7f & msg [ 1 ] ) ) {
743
+ dtmfpkcount++
744
+ } else {
745
+ expect( msg.length ).to.equal( 172 )
746
+ expect( 0x7f & msg [ 1 ] ).to.equal( 8 )
747
+ endpointb.send( msg, channelb.local.port, "localhost" )
748
+ }
749
+ } )
750
+
751
+ endpointa.bind()
752
+ await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } )
753
+
754
+ endpointb.bind()
755
+ await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } )
756
+
757
+ let done
758
+ const finished = new Promise( ( r ) => { done = r } )
759
+
760
+ const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0, "rfc2833pt": 127 } }, function( d ) {
761
+ receivedmessages.push( d )
762
+
763
+ if( "close" === d.action ) channelb.close()
764
+ } )
765
+
766
+ const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 8, "rfc2833pt": 101 } }, function( d ) {
767
+ if( "close" === d.action ) done()
768
+ } )
769
+
770
+ /* mix */
771
+ expect( channela.mix( channelb ) ).to.be.true
772
+
773
+ /* send a packet every 20mS x 50 */
774
+ sendpk( 0, 0, 0, channela.local.port, endpointa )
775
+ sendpk( 1, 1*160, 1*20, channela.local.port, endpointa )
776
+ sendpk( 2, 2*160, 2*20, channela.local.port, endpointa )
777
+ sendpk( 3, 3*160, 3*20, channela.local.port, endpointa )
778
+ sendpk( 4, 4*160, 4*20, channela.local.port, endpointa )
779
+ sendpk( 5, 5*160, 5*20, channela.local.port, endpointa )
780
+ sendpk( 6, 6*160, 6*20, channela.local.port, endpointa )
781
+ sendpk( 7, 7*160, 7*20, channela.local.port, endpointa )
782
+ sendpk( 8, 8*160, 8*20, channela.local.port, endpointa )
783
+ sendpk( 9, 9*160, 9*20, channela.local.port, endpointa )
784
+ sendpk( 10, 10*160, 10*20, channela.local.port, endpointa )
785
+ sendpk( 11, 11*160, 11*20, channela.local.port, endpointa )
786
+ sendpk( 12, 12*160, 12*20, channela.local.port, endpointa )
787
+
788
+ senddtmf( 13, 13*160, 13*20, channela.local.port, endpointa, false, "4", 127 )
789
+ sendpk( 14, 13*160, 13*20, channela.local.port, endpointa, 0 )
790
+ sendpk( 15, 14*160, 14*20, channela.local.port, endpointa, 0 )
791
+ senddtmf( 16, (15*160)+10, (15*20)+10, channela.local.port, endpointa, false, "4", 127 )
792
+ sendpk( 17, 15*160, 15*20, channela.local.port, endpointa, 0 )
793
+ sendpk( 18, 16*160, 16*20, channela.local.port, endpointa, 0 )
794
+ senddtmf( 19, (17*160)+20, (17*20)+20, channela.local.port, endpointa, true, "4", 127 )
795
+ sendpk( 20, 17*160, 17*20, channela.local.port, endpointa, 0 )
796
+ sendpk( 21, 18*160, 18*20, channela.local.port, endpointa, 0 )
797
+ senddtmf( 22, (18*160)+30, (18*20)+30, channela.local.port, endpointa, true, "4", 127 )
798
+ sendpk( 23, 19*160, 19*20, channela.local.port, endpointa, 0 )
799
+ sendpk( 24, 20*160, 20*20, channela.local.port, endpointa, 0 )
800
+ sendpk( 25, 21*160, 21*20, channela.local.port, endpointa, 0 )
801
+ sendpk( 26, 22*160, 22*20, channela.local.port, endpointa, 0 )
802
+ sendpk( 27, 23*160, 23*20, channela.local.port, endpointa, 0 )
803
+ sendpk( 28, 24*160, 24*20, channela.local.port, endpointa, 0 )
804
+ sendpk( 29, 25*160, 25*20, channela.local.port, endpointa, 0 )
805
+
806
+ senddtmf( 30, 26*160, 26*20, channela.local.port, endpointa, false, "5", 127 )
807
+ sendpk( 31, 26*160, 26*20, channela.local.port, endpointa, 0 )
808
+ sendpk( 32, 27*160, 27*20, channela.local.port, endpointa, 0 )
809
+ senddtmf( 33, (27*160)+10, (27*20)+10, channela.local.port, endpointa, false, "5", 127 )
810
+ sendpk( 34, 28*160, 28*20, channela.local.port, endpointa, 0 )
811
+ sendpk( 35, 29*160, 28*20, channela.local.port, endpointa, 0 )
812
+ senddtmf( 36, (28*160)+20, (28*20)+20, channela.local.port, endpointa, true, "5", 127 )
813
+ sendpk( 37, 30*160, 29*20, channela.local.port, endpointa, 0 )
814
+ sendpk( 38, 31*160, 30*20, channela.local.port, endpointa, 0 )
815
+
816
+ senddtmf( 39, (38*160)+30, (30*20)+30, channela.local.port, endpointa, true, "5", 127 )
817
+ sendpk( 40, 32*160, 31*20, channela.local.port, endpointa, 0 )
818
+ sendpk( 41, 33*160, 32*20, channela.local.port, endpointa, 0 )
819
+
820
+ sendpk( 42, 34*160, 33*20, channela.local.port, endpointa, 0 )
821
+ sendpk( 43, 35*160, 34*20, channela.local.port, endpointa, 0 )
822
+ sendpk( 44, 36*160, 35*20, channela.local.port, endpointa, 0 )
823
+ sendpk( 45, 37*160, 36*20, channela.local.port, endpointa, 0 )
824
+ sendpk( 46, 38*160, 37*20, channela.local.port, endpointa, 0 )
825
+ sendpk( 47, 39*160, 38*20, channela.local.port, endpointa, 0 )
826
+ sendpk( 48, 40*160, 39*20, channela.local.port, endpointa, 0 )
827
+ sendpk( 49, 51*160, 40*20, channela.local.port, endpointa, 0 )
828
+
829
+ await new Promise( ( r ) => { setTimeout( () => r(), 1400 ) } )
830
+
831
+ channela.close()
832
+ endpointa.close()
833
+ endpointb.close()
834
+
835
+ await finished
836
+
837
+ expect( endpointapkcount ).to.be.within( 30, 51 )
838
+ expect( endpointbpkcount ).to.be.within( 30, 51 )
839
+
840
+ expect( receivedmessages.length ).to.equal( 5 )
841
+
842
+ expect( dtmfpkcount ).to.be.within( 4, 8 )
843
+
844
+ expect( receivedmessages[ 0 ].action ).to.equal( "mix" )
845
+ expect( receivedmessages[ 1 ].action ).to.equal( "telephone-event" )
846
+ expect( receivedmessages[ 2 ].action ).to.equal( "telephone-event" )
847
+ expect( receivedmessages[ 3 ].action ).to.equal( "mix" )
848
+ expect( receivedmessages[ 3 ].event ).to.equal( "finished" )
849
+ expect( receivedmessages[ 4 ].action ).to.equal( "close" )
850
+
851
+ expect( receivedmessages[ 0 ].event ).to.equal( "start" )
852
+ expect( receivedmessages[ 1 ].event ).to.equal( "4" )
853
+ expect( receivedmessages[ 2 ].event ).to.equal( "5" )
854
+ expect( receivedmessages[ 3 ].event ).to.equal( "finished" )
855
+
856
+ } )
857
+
858
+
718
859
  it( "mix 3 channels - pcmu <-> pcma and ilbc and send DTMF", async function() {
719
860
 
720
861
  this.timeout( 3000 )