@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 +1 -1
- package/src/globals.h +3 -0
- package/src/projectrtpchannelmux.cpp +15 -5
- package/src/projectrtpchannelmux.h +4 -2
- package/src/projectrtpcodecx.cpp +62 -89
- package/src/projectrtpcodecx.h +1 -3
- package/src/projectrtprawsound.cpp +34 -70
- package/test/interface/pcap.js +2 -2
- package/test/interface/pcaps/440hzinbackgroundg722.pcap +0 -0
- package/test/interface/projectrtpdtmf.js +2 -2
- package/test/interface/transcode.js +166 -2
package/package.json
CHANGED
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
|
|
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
|
-
|
|
40
|
+
l16format = L1616KPAYLOADTYPE;
|
|
41
|
+
insize = L1616PAYLOADSAMPLES;
|
|
40
42
|
goto endofforloop;
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
endofforloop:
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
this->
|
|
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 */
|
package/src/projectrtpcodecx.cpp
CHANGED
|
@@ -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(
|
|
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 <
|
|
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(
|
|
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 <
|
|
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(
|
|
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 <
|
|
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 <
|
|
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 <
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
401
|
+
G722PAYLOADBYTES );
|
|
415
402
|
|
|
416
|
-
|
|
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 ==
|
|
415
|
+
if( 0 == this->l168kref.size() ) return false;
|
|
429
416
|
if( this->l168kref.isdirty() ) return false;
|
|
430
417
|
|
|
431
|
-
this->l1616kref.malloc(
|
|
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 <
|
|
437
|
-
*out = (
|
|
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 ==
|
|
456
|
+
if( 0 == this->l1616kref.size() ) return false;
|
|
475
457
|
if( this->l1616kref.isdirty() ) return false;
|
|
476
458
|
|
|
477
|
-
this->l168kref.malloc(
|
|
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 <
|
|
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;
|
package/src/projectrtpcodecx.h
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
|
package/test/interface/pcap.js
CHANGED
|
@@ -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.
|
|
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 */
|
|
Binary file
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
const expect = require( "chai" ).expect
|
|
3
|
-
const projectrtp = require( "../../index
|
|
3
|
+
const projectrtp = require( "../../index" ).projectrtp
|
|
4
4
|
const dgram = require( "dgram" )
|
|
5
5
|
|
|
6
|
-
const pcap = require( "./pcap
|
|
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
|
-
|
|
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
|
|