@babblevoice/projectrtp 2.5.26 → 2.5.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/server.js +115 -158
- package/package.json +1 -1
- package/src/projectrtpbuffer.cpp +17 -19
- package/src/projectrtpbuffer.h +1 -0
- package/src/projectrtpchannel.cpp +176 -51
- package/src/projectrtpchannel.h +4 -0
- package/src/projectrtpchannelmux.cpp +20 -2
- package/src/projectrtpchannelrecorder.cpp +1 -0
- package/src/projectrtpchannelrecorder.h +1 -0
- package/src/projectrtpcodecx.cpp +3 -2
- package/test/interface/projectrtpdtmf.js +5 -5
- package/test/interface/projectrtpmix.js +5 -5
- package/test/interface/projectrtprecord.js +4 -0
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ If you wish to build outsode of a Docker image, there are npm target scripts for
|
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
docker buildx prune
|
|
63
|
-
docker buildx build --platform linux/amd64,linux/arm64 -t tinpotnick/projectrtp:2.5.
|
|
63
|
+
docker buildx build --platform linux/amd64,linux/arm64 -t tinpotnick/projectrtp:2.5.27 . --push
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
## Example scripts
|
package/lib/server.js
CHANGED
|
@@ -4,9 +4,10 @@ const net = require( "net" )
|
|
|
4
4
|
const { v4: uuidv4 } = require( "uuid" )
|
|
5
5
|
|
|
6
6
|
const EventEmitter = require( "events" )
|
|
7
|
-
|
|
8
7
|
const message = require( "./message" )
|
|
9
8
|
|
|
9
|
+
const opentimeout = 5000
|
|
10
|
+
|
|
10
11
|
/**
|
|
11
12
|
* Node host address and port object
|
|
12
13
|
* @typedef { object } nodehost
|
|
@@ -105,12 +106,36 @@ class channel {
|
|
|
105
106
|
*/
|
|
106
107
|
openresolve
|
|
107
108
|
|
|
109
|
+
/**
|
|
110
|
+
* @private
|
|
111
|
+
* @type { function }
|
|
112
|
+
*/
|
|
113
|
+
closeresolve
|
|
114
|
+
|
|
108
115
|
/**
|
|
109
116
|
* @private
|
|
110
117
|
* @type { function }
|
|
111
118
|
*/
|
|
112
119
|
openreject
|
|
113
120
|
|
|
121
|
+
/**
|
|
122
|
+
* @private
|
|
123
|
+
* @type { function }
|
|
124
|
+
*/
|
|
125
|
+
closereject
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @private
|
|
129
|
+
* @type { NodeJS.Timeout }
|
|
130
|
+
*/
|
|
131
|
+
opentimer
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @private
|
|
135
|
+
* @type { NodeJS.Timeout }
|
|
136
|
+
*/
|
|
137
|
+
closetimer
|
|
138
|
+
|
|
114
139
|
/**
|
|
115
140
|
* Array of messages sent to and from the remote end.
|
|
116
141
|
* @type { Array< any > }
|
|
@@ -206,14 +231,15 @@ class channel {
|
|
|
206
231
|
}
|
|
207
232
|
|
|
208
233
|
/**
|
|
209
|
-
*
|
|
234
|
+
* Connects to a RTP server then creates channel
|
|
210
235
|
* @param { object } options
|
|
211
236
|
* @param { channelcallback } cb
|
|
212
237
|
* @param { function } openresolve
|
|
238
|
+
* @param { function } openreject
|
|
213
239
|
* @returns { Promise< channel > } - resolves when the connection is connected
|
|
214
240
|
* @internal
|
|
215
241
|
*/
|
|
216
|
-
static _createforconnect( options, cb, openresolve
|
|
242
|
+
static _createforconnect( options, cb, openresolve, openreject ) {
|
|
217
243
|
|
|
218
244
|
const newchannel = new channel( options.id )
|
|
219
245
|
if( cb ) newchannel.em.on( "all", cb )
|
|
@@ -228,6 +254,9 @@ class channel {
|
|
|
228
254
|
"type": nodeconnectiontype.connect
|
|
229
255
|
}
|
|
230
256
|
|
|
257
|
+
newchannel.openresolve = openresolve
|
|
258
|
+
newchannel.openreject = openreject
|
|
259
|
+
|
|
231
260
|
newchannel.connection.sock.setKeepAlive( true )
|
|
232
261
|
newchannel.channels = [ newchannel ]
|
|
233
262
|
|
|
@@ -236,6 +265,15 @@ class channel {
|
|
|
236
265
|
if ( connectresolve ) connectresolve( newchannel )
|
|
237
266
|
} )
|
|
238
267
|
|
|
268
|
+
newchannel.opentimer = setTimeout( () => {
|
|
269
|
+
if( newchannel.openreject ) {
|
|
270
|
+
newchannel.openreject()
|
|
271
|
+
delete newchannel.openresolve
|
|
272
|
+
delete newchannel.openreject
|
|
273
|
+
delete newchannel.opentimer
|
|
274
|
+
}
|
|
275
|
+
}, opentimeout )
|
|
276
|
+
|
|
239
277
|
const state = message.newstate()
|
|
240
278
|
newchannel.connection.sock.on( "data", ( data ) => {
|
|
241
279
|
message.parsemessage( state, data, ( /** @type { object } */ receivedmsg ) => {
|
|
@@ -248,8 +286,6 @@ class channel {
|
|
|
248
286
|
if( receivedmsg && receivedmsg.status && receivedmsg.status.instance ) {
|
|
249
287
|
newchannel.connection.instance = receivedmsg.status.instance
|
|
250
288
|
}
|
|
251
|
-
|
|
252
|
-
if ( openresolve ) openresolve( newchannel )
|
|
253
289
|
} )
|
|
254
290
|
} )
|
|
255
291
|
|
|
@@ -259,6 +295,16 @@ class channel {
|
|
|
259
295
|
} )
|
|
260
296
|
|
|
261
297
|
newchannel.connection.sock.on( "close", () => {
|
|
298
|
+
newchannel.channels.forEach( chnl => {
|
|
299
|
+
channels.delete( chnl.id )
|
|
300
|
+
if( chnl.openreject ) {
|
|
301
|
+
chnl.openreject()
|
|
302
|
+
clearTimeout( chnl.opentimer )
|
|
303
|
+
delete chnl.openresolve
|
|
304
|
+
delete chnl.openreject
|
|
305
|
+
delete chnl.opentimer
|
|
306
|
+
}
|
|
307
|
+
} )
|
|
262
308
|
} )
|
|
263
309
|
} )
|
|
264
310
|
}
|
|
@@ -294,7 +340,17 @@ class channel {
|
|
|
294
340
|
newchannel.channels = this.channels
|
|
295
341
|
newchannel.channels.push( newchannel )
|
|
296
342
|
newchannel.openresolve = resolve
|
|
343
|
+
newchannel.openreject = reject
|
|
297
344
|
newchannel._write( options )
|
|
345
|
+
|
|
346
|
+
newchannel.opentimer = setTimeout( () => {
|
|
347
|
+
if( newchannel.openreject ) {
|
|
348
|
+
newchannel.openreject()
|
|
349
|
+
delete newchannel.openresolve
|
|
350
|
+
delete newchannel.openreject
|
|
351
|
+
delete newchannel.opentimer
|
|
352
|
+
}
|
|
353
|
+
}, opentimeout )
|
|
298
354
|
} )
|
|
299
355
|
|
|
300
356
|
return resolvepromise
|
|
@@ -314,12 +370,24 @@ class channel {
|
|
|
314
370
|
}
|
|
315
371
|
|
|
316
372
|
/**
|
|
317
|
-
*
|
|
373
|
+
* Closes the channel. It might throw a timeout error if the channel is not closed within 5 seconds.
|
|
374
|
+
* @param { object } [ options ]
|
|
375
|
+
* @param { number } [ options.timeout ] - different timeout to wait
|
|
376
|
+
* @returns { Promise< object > }
|
|
318
377
|
*/
|
|
319
|
-
close() {
|
|
378
|
+
close( options ) {
|
|
320
379
|
|
|
321
|
-
|
|
322
|
-
|
|
380
|
+
let timeout = opentimeout
|
|
381
|
+
if( options && options.timeout ) timeout = options.timeout
|
|
382
|
+
|
|
383
|
+
this.closetimer = setTimeout( () => {
|
|
384
|
+
if( this.closereject ) {
|
|
385
|
+
this.closereject()
|
|
386
|
+
delete this.closereject
|
|
387
|
+
delete this.closeresolve
|
|
388
|
+
delete this.closetimer
|
|
389
|
+
}
|
|
390
|
+
}, timeout )
|
|
323
391
|
|
|
324
392
|
/* close us */
|
|
325
393
|
this._write( {
|
|
@@ -327,11 +395,18 @@ class channel {
|
|
|
327
395
|
"uuid": this.uuid
|
|
328
396
|
} )
|
|
329
397
|
|
|
330
|
-
if( this.
|
|
331
|
-
this.
|
|
398
|
+
if( this.openreject ) {
|
|
399
|
+
this.openreject()
|
|
400
|
+
clearTimeout( this.opentimer )
|
|
332
401
|
delete this.openresolve
|
|
333
402
|
delete this.openreject
|
|
403
|
+
delete this.opentimer
|
|
334
404
|
}
|
|
405
|
+
|
|
406
|
+
return new Promise( ( resolve, reject ) => {
|
|
407
|
+
this.closeresolve = resolve
|
|
408
|
+
this.closereject = reject
|
|
409
|
+
} )
|
|
335
410
|
}
|
|
336
411
|
|
|
337
412
|
/**
|
|
@@ -354,142 +429,6 @@ class channel {
|
|
|
354
429
|
return true
|
|
355
430
|
}
|
|
356
431
|
|
|
357
|
-
/**
|
|
358
|
-
* Add _bridge object to both channel
|
|
359
|
-
* @param { object } other - other channel
|
|
360
|
-
* @returns { void }
|
|
361
|
-
*/
|
|
362
|
-
_createbridges( other ) {
|
|
363
|
-
|
|
364
|
-
if( !this._bridge && !other._bridge ) {
|
|
365
|
-
this._bridge = other._bridge = {
|
|
366
|
-
"bridges": [],
|
|
367
|
-
"main": this.connection.instance
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if( !this._bridge && other._bridge ) this._bridge = other._bridge
|
|
372
|
-
if( this._bridge && !other._bridge ) other._bridge = this._bridge
|
|
373
|
-
|
|
374
|
-
if( this._bridge !== other._bridge )
|
|
375
|
-
throw new Error( "some complex mixing occured and this function needs finishing" )
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Any channels which have been mixed we create an
|
|
380
|
-
* object common to both channels so we can keep track of channels which
|
|
381
|
-
* require tidyin up. We try to maintain a star shaped network.
|
|
382
|
-
* In the example diagram, the out mix method mixes between phone1 and this
|
|
383
|
-
* and phone2 and other.
|
|
384
|
-
* Markdown (mermaid)
|
|
385
|
-
graph TD
|
|
386
|
-
this -->|rtp - our created bridge to link the 2 remote channels| other
|
|
387
|
-
other --> this
|
|
388
|
-
|
|
389
|
-
phone1 --> this
|
|
390
|
-
this --> phone1
|
|
391
|
-
|
|
392
|
-
phone2 --> other
|
|
393
|
-
other --> phone2
|
|
394
|
-
* @private
|
|
395
|
-
* @param { object } other - other channel
|
|
396
|
-
* @returns { Promise< boolean > }
|
|
397
|
-
*/
|
|
398
|
-
async _addbridge( other ) {
|
|
399
|
-
|
|
400
|
-
let leftid = this.connection.instance
|
|
401
|
-
const rightid = other.connection.instance
|
|
402
|
-
|
|
403
|
-
this._createbridges( other )
|
|
404
|
-
|
|
405
|
-
/* nothing more to do if we are on the same node */
|
|
406
|
-
if( rightid === leftid ) return false
|
|
407
|
-
|
|
408
|
-
/* It might have changed now we have linked them up */
|
|
409
|
-
leftid = this._bridge.main
|
|
410
|
-
|
|
411
|
-
let found = false
|
|
412
|
-
for( const bridge of this._bridge.bridges ) {
|
|
413
|
-
if( ( bridge.left.connection.instance === leftid || bridge.right.connection.instance === leftid ) &&
|
|
414
|
-
( bridge.left.connection.instance === rightid || bridge.right.connection.instance === rightid ) ) {
|
|
415
|
-
found = true
|
|
416
|
-
bridge.channels.add( this )
|
|
417
|
-
bridge.channels.add( other )
|
|
418
|
-
|
|
419
|
-
this.mix( bridge.left )
|
|
420
|
-
other.mix( bridge.right )
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if( found ) return
|
|
425
|
-
|
|
426
|
-
const bridge = {
|
|
427
|
-
/* TODO: this will need reworking with to support reverse connection types */
|
|
428
|
-
"left": await serverinterface.get().openchannel( { "nodeinstance": leftid } ),
|
|
429
|
-
"right": await serverinterface.get().openchannel( { "nodeinstance": rightid } ),
|
|
430
|
-
"channels": new Set( [ this, other ] )
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const addressleft = bridge.left.local.privateaddress || bridge.left.local.address
|
|
434
|
-
const addressright = bridge.right.local.privateaddress || bridge.right.local.address
|
|
435
|
-
const g722 = 9
|
|
436
|
-
|
|
437
|
-
bridge.left.remote( { "address": addressright, "port": bridge.right.local.port, "codec": g722 } )
|
|
438
|
-
bridge.right.remote( { "address": addressleft, "port": bridge.left.local.port, "codec": g722 } )
|
|
439
|
-
|
|
440
|
-
this._bridge.bridges.push( bridge )
|
|
441
|
-
|
|
442
|
-
this._write( {
|
|
443
|
-
"channel": "mix",
|
|
444
|
-
"other": {
|
|
445
|
-
"id": bridge.left.id,
|
|
446
|
-
"uuid": bridge.left.uuid
|
|
447
|
-
}
|
|
448
|
-
} )
|
|
449
|
-
|
|
450
|
-
other._write( {
|
|
451
|
-
"channel": "mix",
|
|
452
|
-
"other": {
|
|
453
|
-
"id": bridge.right.id,
|
|
454
|
-
"uuid": bridge.right.uuid
|
|
455
|
-
}
|
|
456
|
-
} )
|
|
457
|
-
|
|
458
|
-
return true
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* We check if we are a remote node (i.e.. not the centre of the star)
|
|
463
|
-
* then we close down the link between the two.
|
|
464
|
-
* @private
|
|
465
|
-
* @returns { boolean }
|
|
466
|
-
*/
|
|
467
|
-
_removebridge() {
|
|
468
|
-
|
|
469
|
-
/* no remote bridges have been created */
|
|
470
|
-
if( !this._bridge || !this._bridge.main ) return
|
|
471
|
-
|
|
472
|
-
const usid = this.connection.instance
|
|
473
|
-
|
|
474
|
-
/* The centre of star cannot remove from the network until last man standing */
|
|
475
|
-
if( usid == this._bridge.main ) return
|
|
476
|
-
|
|
477
|
-
this._bridge.bridges = this._bridge.bridges.filter( ( bridge ) => {
|
|
478
|
-
bridge.channels.delete( this )
|
|
479
|
-
|
|
480
|
-
if( 2 > bridge.channels.size ) {
|
|
481
|
-
|
|
482
|
-
bridge.left._write( { "channel": "unmix" } )
|
|
483
|
-
bridge.left._write( { "channel": "unmix" } )
|
|
484
|
-
|
|
485
|
-
bridge.left.close()
|
|
486
|
-
bridge.right.close()
|
|
487
|
-
return false /* remove */
|
|
488
|
-
}
|
|
489
|
-
return true /* keep */
|
|
490
|
-
} )
|
|
491
|
-
}
|
|
492
|
-
|
|
493
432
|
/**
|
|
494
433
|
@summary Removes us from an existing mix
|
|
495
434
|
@returns { boolean }
|
|
@@ -592,6 +531,7 @@ class channel {
|
|
|
592
531
|
}
|
|
593
532
|
|
|
594
533
|
/**
|
|
534
|
+
* We have received teh open confirm message so we should action it
|
|
595
535
|
* @private
|
|
596
536
|
* @param { object } msg
|
|
597
537
|
* @returns { boolean } - is further processing required
|
|
@@ -605,33 +545,50 @@ class channel {
|
|
|
605
545
|
|
|
606
546
|
if( !this.openresolve ) return true
|
|
607
547
|
this.openresolve( this )
|
|
548
|
+
clearTimeout( this.opentimer )
|
|
608
549
|
delete this.openresolve
|
|
609
550
|
delete this.openreject
|
|
551
|
+
delete this.opentimer
|
|
610
552
|
|
|
611
553
|
return true
|
|
612
554
|
}
|
|
613
555
|
|
|
614
556
|
/**
|
|
615
|
-
*
|
|
557
|
+
* We have received a close message so we should action it
|
|
558
|
+
* @param { object } msg
|
|
616
559
|
* @returns { void }
|
|
617
560
|
* @private
|
|
618
561
|
*/
|
|
619
|
-
_runclose() {
|
|
620
|
-
if(
|
|
621
|
-
this.
|
|
562
|
+
_runclose( msg ) {
|
|
563
|
+
if( this.openreject ) {
|
|
564
|
+
this.openreject()
|
|
565
|
+
clearTimeout( this.opentimer )
|
|
622
566
|
delete this.openresolve
|
|
623
567
|
delete this.openreject
|
|
568
|
+
delete this.opentimer
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if( this.closeresolve ) {
|
|
572
|
+
this.closeresolve( msg )
|
|
573
|
+
clearTimeout( this.closetimer )
|
|
574
|
+
delete this.closeresolve
|
|
575
|
+
delete this.closereject
|
|
576
|
+
delete this.closetimer
|
|
624
577
|
}
|
|
625
578
|
|
|
579
|
+
this.#cleanupsharedchannels()
|
|
580
|
+
channels.delete( this.id )
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
#cleanupsharedchannels() {
|
|
626
584
|
if( this.channels ) {
|
|
627
585
|
// adjust with filter
|
|
628
586
|
const index = this.channels.indexOf( this )
|
|
629
587
|
if( -1 < index ) this.channels.splice( index, 1 )
|
|
630
|
-
if( 0 === this.channels.length && this.connection.sock )
|
|
588
|
+
if( 0 === this.channels.length && this.connection.sock ) {
|
|
589
|
+
this.connection.sock.destroy()
|
|
590
|
+
}
|
|
631
591
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
channels.delete( this.id )
|
|
635
592
|
}
|
|
636
593
|
|
|
637
594
|
/**
|
|
@@ -647,7 +604,7 @@ class channel {
|
|
|
647
604
|
this.em.emit( "all", msg )
|
|
648
605
|
this.em.emit( msg.action, msg )
|
|
649
606
|
|
|
650
|
-
if( "close" == msg.action ) this._runclose()
|
|
607
|
+
if( "close" == msg.action ) this._runclose( msg )
|
|
651
608
|
}
|
|
652
609
|
|
|
653
610
|
/**
|
|
@@ -772,9 +729,9 @@ class rtpserver {
|
|
|
772
729
|
request.channel = "open"
|
|
773
730
|
|
|
774
731
|
const node = randomenode( options.nodeinstance )
|
|
775
|
-
|
|
732
|
+
|
|
776
733
|
if ( 0 < nodes.size ) channel._createforlisten( request, node, callback, resolve, reject )
|
|
777
|
-
else channel._createforconnect( request, callback, resolve )
|
|
734
|
+
else channel._createforconnect( request, callback, resolve, reject )
|
|
778
735
|
|
|
779
736
|
} )
|
|
780
737
|
}
|
package/package.json
CHANGED
package/src/projectrtpbuffer.cpp
CHANGED
|
@@ -53,7 +53,21 @@ Returns the next packet in order - does not modify our structure.
|
|
|
53
53
|
rtppacket* rtpbuffer::peek( void ) {
|
|
54
54
|
if( nullptr != this->peekedpopped ) return this->peekedpopped;
|
|
55
55
|
uint16_t bufindex = this->outsn % this->buffercount;
|
|
56
|
-
|
|
56
|
+
rtppacket *out = this->orderedrtpdata.at( bufindex );
|
|
57
|
+
|
|
58
|
+
if( out == nullptr ) {
|
|
59
|
+
this->outsn++;
|
|
60
|
+
return nullptr;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if( out->getsequencenumber() != this->outsn ) {
|
|
64
|
+
this->orderedrtpdata.at( this->outsn % this->buffercount ) = nullptr;
|
|
65
|
+
this->availablertpdata.push( out );
|
|
66
|
+
this->badsn++;
|
|
67
|
+
return nullptr;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this->peekedpopped = out;
|
|
57
71
|
return this->peekedpopped;
|
|
58
72
|
}
|
|
59
73
|
|
|
@@ -72,7 +86,7 @@ rtppacket* rtpbuffer::poppeeked( void ) {
|
|
|
72
86
|
this->availablertpdata.push( out );
|
|
73
87
|
}
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
this->popped++;
|
|
76
90
|
return out;
|
|
77
91
|
}
|
|
78
92
|
|
|
@@ -81,25 +95,9 @@ Returns the next packet in order.
|
|
|
81
95
|
*/
|
|
82
96
|
rtppacket* rtpbuffer::pop( void ) {
|
|
83
97
|
rtppacket *out = this->peek();
|
|
84
|
-
uint16_t oldsn = this->outsn;
|
|
85
|
-
this->outsn++;
|
|
86
|
-
|
|
87
|
-
this->peekedpopped = nullptr;
|
|
88
98
|
if( nullptr == out ) return nullptr;
|
|
99
|
+
this->poppeeked();
|
|
89
100
|
|
|
90
|
-
uint16_t bufindex = oldsn % this->buffercount;
|
|
91
|
-
if( nullptr != this->orderedrtpdata.at( bufindex ) ) {
|
|
92
|
-
this->orderedrtpdata.at( bufindex ) = nullptr;
|
|
93
|
-
this->availablertpdata.push( out );
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
auto outsn = out->getsequencenumber();
|
|
97
|
-
if( outsn != oldsn ) {
|
|
98
|
-
this->badsn++;
|
|
99
|
-
return nullptr;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
this->popped++;
|
|
103
101
|
return out;
|
|
104
102
|
}
|
|
105
103
|
|
package/src/projectrtpbuffer.h
CHANGED
|
@@ -48,6 +48,7 @@ public:
|
|
|
48
48
|
int waterlevel = BUFFERPACKETCAP /* the level we build up before allowing a read */ );
|
|
49
49
|
|
|
50
50
|
rtppacket* peek( void );
|
|
51
|
+
rtppacket* peeked( void ) { return this->peekedpopped; }
|
|
51
52
|
rtppacket* poppeeked( void );
|
|
52
53
|
rtppacket* pop( void );
|
|
53
54
|
void push( void );
|
|
@@ -19,7 +19,49 @@
|
|
|
19
19
|
extern boost::asio::io_context workercontext;
|
|
20
20
|
std::queue < unsigned short >availableports;
|
|
21
21
|
std::atomic_bool availableportslock( false );
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
std::atomic< std::uint32_t > channelscreated{ 0 };
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the next available port.
|
|
27
|
+
*/
|
|
28
|
+
unsigned short getavailableport( void ) {
|
|
29
|
+
AQUIRESPINLOCK( availableportslock );
|
|
30
|
+
auto ourport = availableports.front();
|
|
31
|
+
availableports.pop();
|
|
32
|
+
RELEASESPINLOCK( availableportslock );
|
|
33
|
+
|
|
34
|
+
channelscreated.fetch_add( 1 );
|
|
35
|
+
|
|
36
|
+
return ourport;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns the number of ports we still have available.
|
|
41
|
+
* @returns { size_t }
|
|
42
|
+
*/
|
|
43
|
+
auto getvailableportsize() {
|
|
44
|
+
AQUIRESPINLOCK( availableportslock );
|
|
45
|
+
auto availableportssize = availableports.size();
|
|
46
|
+
RELEASESPINLOCK( availableportslock );
|
|
47
|
+
|
|
48
|
+
return availableportssize;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Return a port number to the available list (to the back) and clear our value
|
|
53
|
+
* so we cannot return it twice.
|
|
54
|
+
*/
|
|
55
|
+
void projectrtpchannel::returnavailableport( void ) {
|
|
56
|
+
|
|
57
|
+
if( 0 == this->port ) return;
|
|
58
|
+
AQUIRESPINLOCK( availableportslock );
|
|
59
|
+
availableports.push( this->port );
|
|
60
|
+
this->port = 0;
|
|
61
|
+
RELEASESPINLOCK( availableportslock );
|
|
62
|
+
|
|
63
|
+
channelscreated.fetch_sub( 1 );
|
|
64
|
+
}
|
|
23
65
|
|
|
24
66
|
/* useful for random string generation */
|
|
25
67
|
const char alphanumsecret[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
|
@@ -53,6 +95,7 @@ projectrtpchannel::projectrtpchannel( unsigned short port ):
|
|
|
53
95
|
totalticktime( 0 ),
|
|
54
96
|
totaltickcount( 0 ),
|
|
55
97
|
tickswithnortpcount( 0 ),
|
|
98
|
+
hardtickswithnortpcount( 0 ),
|
|
56
99
|
outpkwritecount( 0 ),
|
|
57
100
|
outpkcount( 0 ),
|
|
58
101
|
outpkskipcount( 0 ),
|
|
@@ -107,8 +150,6 @@ projectrtpchannel::projectrtpchannel( unsigned short port ):
|
|
|
107
150
|
lastdtmfsn( 0 ),
|
|
108
151
|
tickstarttime() {
|
|
109
152
|
|
|
110
|
-
channelscreated++;
|
|
111
|
-
|
|
112
153
|
gnutls_rnd( GNUTLS_RND_RANDOM, &this->ssrcout, 4 );
|
|
113
154
|
|
|
114
155
|
char localicepwd[ 25 ];
|
|
@@ -241,6 +282,7 @@ void projectrtpchannel::handleremoteresolve (
|
|
|
241
282
|
|
|
242
283
|
this->confirmedrtpsenderendpoint = *it;
|
|
243
284
|
this->remoteconfirmed = true;
|
|
285
|
+
this->hardtickswithnortpcount = 0;
|
|
244
286
|
|
|
245
287
|
/* allow us to re-auto correct */
|
|
246
288
|
this->autoadjust = true;
|
|
@@ -264,29 +306,63 @@ uint32_t projectrtpchannel::requestopen( void ) {
|
|
|
264
306
|
return this->ssrcout;
|
|
265
307
|
}
|
|
266
308
|
|
|
309
|
+
/**
|
|
310
|
+
* handle errors on socket open
|
|
311
|
+
*/
|
|
312
|
+
void projectrtpchannel::badsocketopen( const char *err ) {
|
|
313
|
+
|
|
314
|
+
fprintf( stderr, "Socket issue - refusing a new channel: %s\n", err );
|
|
315
|
+
|
|
316
|
+
if( this->rtpsocket.is_open() ) {
|
|
317
|
+
this->rtpsocket.close();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if( !this->rtcpsocket.is_open() ) {
|
|
321
|
+
this->rtcpsocket.close();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this->returnavailableport();
|
|
325
|
+
|
|
326
|
+
postdatabacktojsfromthread( shared_from_this(), "close", "error.nosocket" );
|
|
327
|
+
}
|
|
328
|
+
|
|
267
329
|
/**
|
|
268
330
|
* Must be called in the workercontext.
|
|
269
331
|
*/
|
|
270
332
|
void projectrtpchannel::doopen( void ) {
|
|
271
333
|
|
|
334
|
+
boost::system::error_code ec;
|
|
335
|
+
|
|
272
336
|
this->outcodec.reset();
|
|
273
337
|
this->incodec.reset();
|
|
274
338
|
|
|
275
|
-
boost::asio::
|
|
276
|
-
|
|
277
|
-
|
|
339
|
+
this->rtpsocket.open( boost::asio::ip::udp::v4(), ec );
|
|
340
|
+
|
|
341
|
+
if( ec ) {
|
|
342
|
+
this->badsocketopen( "failed to open rtp socket" );
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
278
346
|
this->rtpsocket.bind( boost::asio::ip::udp::endpoint(
|
|
279
|
-
boost::asio::ip::udp::v4(), this->port ) );
|
|
347
|
+
boost::asio::ip::udp::v4(), this->port ), ec );
|
|
348
|
+
|
|
349
|
+
if( ec ) {
|
|
350
|
+
this->badsocketopen( "failed to bind rtp socket" );
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this->rtcpsocket.open( boost::asio::ip::udp::v4(), ec );
|
|
355
|
+
|
|
356
|
+
if( ec ) {
|
|
357
|
+
this->badsocketopen( "failed to open rtcp socket" );
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
280
360
|
|
|
281
|
-
this->rtcpsocket.open( boost::asio::ip::udp::v4() );
|
|
282
|
-
this->rtcpsocket.set_option( reuseoption );
|
|
283
361
|
this->rtcpsocket.bind( boost::asio::ip::udp::endpoint(
|
|
284
|
-
boost::asio::ip::udp::v4(), this->port + 1 ) );
|
|
362
|
+
boost::asio::ip::udp::v4(), this->port + 1 ), ec );
|
|
285
363
|
|
|
286
|
-
if(
|
|
287
|
-
|
|
288
|
-
this->requestclose( "error.nosocket" );
|
|
289
|
-
this->doclose();
|
|
364
|
+
if( ec ) {
|
|
365
|
+
this->badsocketopen( "failed to bind rtcp socket" );
|
|
290
366
|
return;
|
|
291
367
|
}
|
|
292
368
|
|
|
@@ -333,11 +409,15 @@ void projectrtpchannel::requestecho( bool e ) {
|
|
|
333
409
|
this->doecho = e;
|
|
334
410
|
}
|
|
335
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Worker thread to perform teh close. It will only run if the channel has
|
|
414
|
+
* been active (i.e. the sockets have been opened sucsessfully)
|
|
415
|
+
*/
|
|
336
416
|
void projectrtpchannel::doclose( void ) {
|
|
337
417
|
|
|
338
418
|
if( !this->active ) return;
|
|
339
|
-
|
|
340
419
|
this->active = false;
|
|
420
|
+
|
|
341
421
|
this->tick.cancel();
|
|
342
422
|
|
|
343
423
|
if( nullptr != this->player ) {
|
|
@@ -365,25 +445,30 @@ void projectrtpchannel::doclose( void ) {
|
|
|
365
445
|
this->rtpsocket.close();
|
|
366
446
|
this->rtcpsocket.close();
|
|
367
447
|
|
|
368
|
-
|
|
369
|
-
availableports.push( this->getport() );
|
|
370
|
-
RELEASESPINLOCK( availableportslock );
|
|
371
|
-
|
|
372
|
-
channelscreated--;
|
|
448
|
+
this->returnavailableport();
|
|
373
449
|
|
|
374
450
|
postdatabacktojsfromthread( shared_from_this(), "close", this->closereason );
|
|
375
451
|
}
|
|
376
452
|
|
|
377
453
|
bool projectrtpchannel::checkidlerecv( void ) {
|
|
378
454
|
if( this->recv && this->active ) {
|
|
379
|
-
|
|
380
|
-
|
|
455
|
+
|
|
456
|
+
this->hardtickswithnortpcount++;
|
|
457
|
+
if( this->remoteconfirmed ) {
|
|
458
|
+
|
|
459
|
+
this->tickswithnortpcount++;
|
|
460
|
+
if( this->tickswithnortpcount > ( 50 * 20 ) ) { /* 50 (@20mS ptime)/S = 20S */
|
|
461
|
+
this->closereason = "idle";
|
|
462
|
+
this->doclose();
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
} else if( this->hardtickswithnortpcount > ( 50 * 60 * 60 ) ) { /* 1hr hard timeout */
|
|
381
467
|
this->closereason = "idle";
|
|
382
468
|
this->doclose();
|
|
383
469
|
return true;
|
|
384
470
|
}
|
|
385
471
|
}
|
|
386
|
-
|
|
387
472
|
return false;
|
|
388
473
|
}
|
|
389
474
|
|
|
@@ -600,20 +685,27 @@ void projectrtpchannel::removeoldrecorders( void ) {
|
|
|
600
685
|
|
|
601
686
|
if( rec->requestfinish ) {
|
|
602
687
|
rec->completed = true;
|
|
688
|
+
rec->maxsincestartpower = 0;
|
|
603
689
|
postdatabacktojsfromthread( shared_from_this(), "record", rec->file, "finished.requested" );
|
|
604
690
|
continue;
|
|
605
691
|
}
|
|
606
692
|
|
|
607
|
-
if( rec->
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
693
|
+
if( rec->lastpowercalc > rec->maxsincestartpower ) {
|
|
694
|
+
rec->maxsincestartpower = rec->lastpowercalc;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if( rec->finishbelowpower > 0 &&
|
|
698
|
+
rec->maxsincestartpower > rec->finishbelowpower &&
|
|
699
|
+
rec->lastpowercalc < rec->finishbelowpower ) {
|
|
700
|
+
rec->completed = true;
|
|
701
|
+
rec->maxsincestartpower = 0;
|
|
702
|
+
postdatabacktojsfromthread( shared_from_this(), "record", rec->file, "finished.belowpower" );
|
|
703
|
+
continue;
|
|
613
704
|
}
|
|
614
705
|
|
|
615
706
|
if( 0 != rec->maxduration && diff.total_milliseconds() > rec->maxduration ) {
|
|
616
707
|
rec->completed = true;
|
|
708
|
+
rec->maxsincestartpower = 0;
|
|
617
709
|
postdatabacktojsfromthread( shared_from_this(), "record", rec->file, "finished.timeout" );
|
|
618
710
|
continue;
|
|
619
711
|
}
|
|
@@ -802,6 +894,7 @@ void projectrtpchannel::correctaddress( void ) {
|
|
|
802
894
|
if( this->autoadjust ) {
|
|
803
895
|
this->confirmedrtpsenderendpoint = this->rtpsenderendpoint;
|
|
804
896
|
this->remoteconfirmed = true;
|
|
897
|
+
this->hardtickswithnortpcount = 0;
|
|
805
898
|
this->autoadjust = false;
|
|
806
899
|
}
|
|
807
900
|
}
|
|
@@ -905,6 +998,7 @@ void projectrtpchannel::readsomertp( void ) {
|
|
|
905
998
|
*/
|
|
906
999
|
|
|
907
1000
|
this->tickswithnortpcount = 0;
|
|
1001
|
+
this->hardtickswithnortpcount = 0;
|
|
908
1002
|
|
|
909
1003
|
/* dynamic payload types */
|
|
910
1004
|
auto pt = buf->getpayloadtype();
|
|
@@ -1168,15 +1262,18 @@ void projectrtpchannel::senddtmf( void ) {
|
|
|
1168
1262
|
return;
|
|
1169
1263
|
}
|
|
1170
1264
|
|
|
1265
|
+
const char volume = 15;
|
|
1266
|
+
const char endofevent = 0x80;
|
|
1267
|
+
|
|
1171
1268
|
rtppacket *dst = this->gettempoutbuf();
|
|
1172
1269
|
dst->setpayloadtype( this->rfc2833pt );
|
|
1173
|
-
dst->setmarker();
|
|
1174
1270
|
dst->setpayloadlength( 4 );
|
|
1175
1271
|
uint8_t *pl = dst->getpayload();
|
|
1176
1272
|
pl[ 0 ] = tosend;
|
|
1177
|
-
pl[ 1 ] =
|
|
1178
|
-
pl[ 2 ]
|
|
1179
|
-
|
|
1273
|
+
pl[ 1 ] = volume; /* end of event & reserved & volume */
|
|
1274
|
+
uint16_t *tmp = ( uint16_t * ) &pl[ 2 ]; /* event duration */
|
|
1275
|
+
*tmp = htons( 160 );
|
|
1276
|
+
|
|
1180
1277
|
this->writepacket( dst );
|
|
1181
1278
|
|
|
1182
1279
|
dst = this->gettempoutbuf();
|
|
@@ -1184,9 +1281,10 @@ void projectrtpchannel::senddtmf( void ) {
|
|
|
1184
1281
|
dst->setpayloadlength( 4 );
|
|
1185
1282
|
pl = dst->getpayload();
|
|
1186
1283
|
pl[ 0 ] = tosend;
|
|
1187
|
-
pl[ 1 ] =
|
|
1188
|
-
pl[ 2 ]
|
|
1189
|
-
|
|
1284
|
+
pl[ 1 ] = volume; /* end of event & reserved & volume */
|
|
1285
|
+
tmp = ( uint16_t * ) &pl[ 2 ]; /* event duration */
|
|
1286
|
+
*tmp = htons( 320 );
|
|
1287
|
+
|
|
1190
1288
|
this->writepacket( dst );
|
|
1191
1289
|
|
|
1192
1290
|
dst = this->gettempoutbuf();
|
|
@@ -1194,9 +1292,44 @@ void projectrtpchannel::senddtmf( void ) {
|
|
|
1194
1292
|
dst->setpayloadlength( 4 );
|
|
1195
1293
|
pl = dst->getpayload();
|
|
1196
1294
|
pl[ 0 ] = tosend;
|
|
1197
|
-
pl[ 1 ] =
|
|
1198
|
-
pl[ 2 ]
|
|
1199
|
-
|
|
1295
|
+
pl[ 1 ] = volume; /* end of event & reserved & volume */
|
|
1296
|
+
tmp = ( uint16_t * ) &pl[ 2 ]; /* event duration */
|
|
1297
|
+
*tmp = htons( 480 );
|
|
1298
|
+
|
|
1299
|
+
this->writepacket( dst );
|
|
1300
|
+
|
|
1301
|
+
dst = this->gettempoutbuf();
|
|
1302
|
+
dst->setpayloadtype( this->rfc2833pt );
|
|
1303
|
+
dst->setmarker();
|
|
1304
|
+
dst->setpayloadlength( 4 );
|
|
1305
|
+
pl = dst->getpayload();
|
|
1306
|
+
pl[ 0 ] = tosend;
|
|
1307
|
+
pl[ 1 ] = endofevent | volume; /* end of event & reserved & volume */
|
|
1308
|
+
tmp = ( uint16_t * ) &pl[ 2 ]; /* event duration */
|
|
1309
|
+
*tmp = htons( 640 );
|
|
1310
|
+
|
|
1311
|
+
this->writepacket( dst );
|
|
1312
|
+
|
|
1313
|
+
dst = this->gettempoutbuf();
|
|
1314
|
+
dst->setpayloadtype( this->rfc2833pt );
|
|
1315
|
+
dst->setpayloadlength( 4 );
|
|
1316
|
+
pl = dst->getpayload();
|
|
1317
|
+
pl[ 0 ] = tosend;
|
|
1318
|
+
pl[ 1 ] = endofevent | volume; /* end of event & reserved & volume */
|
|
1319
|
+
tmp = ( uint16_t * ) &pl[ 2 ]; /* event duration */
|
|
1320
|
+
*tmp = htons( 640 );
|
|
1321
|
+
|
|
1322
|
+
this->writepacket( dst );
|
|
1323
|
+
|
|
1324
|
+
dst = this->gettempoutbuf();
|
|
1325
|
+
dst->setpayloadtype( this->rfc2833pt );
|
|
1326
|
+
dst->setpayloadlength( 4 );
|
|
1327
|
+
pl = dst->getpayload();
|
|
1328
|
+
pl[ 0 ] = tosend;
|
|
1329
|
+
pl[ 1 ] = endofevent | volume; /* end of event & reserved & volume */
|
|
1330
|
+
tmp = ( uint16_t * ) &pl[ 2 ]; /* event duration */
|
|
1331
|
+
*tmp = htons( 640 );
|
|
1332
|
+
|
|
1200
1333
|
this->writepacket( dst );
|
|
1201
1334
|
|
|
1202
1335
|
this->lastdtmfsn = this->snout;
|
|
@@ -1928,10 +2061,6 @@ static napi_value channelcreate( napi_env env, napi_callback_info info ) {
|
|
|
1928
2061
|
napi_value nremote;
|
|
1929
2062
|
|
|
1930
2063
|
napi_value result;
|
|
1931
|
-
AQUIRESPINLOCK( availableportslock );
|
|
1932
|
-
auto ourport = availableports.front();
|
|
1933
|
-
availableports.pop();
|
|
1934
|
-
RELEASESPINLOCK( availableportslock );
|
|
1935
2064
|
|
|
1936
2065
|
size_t bytescopied;
|
|
1937
2066
|
|
|
@@ -1949,7 +2078,7 @@ static napi_value channelcreate( napi_env env, napi_callback_info info ) {
|
|
|
1949
2078
|
return NULL;
|
|
1950
2079
|
}
|
|
1951
2080
|
|
|
1952
|
-
projectrtpchannel::pointer p = projectrtpchannel::create(
|
|
2081
|
+
projectrtpchannel::pointer p = projectrtpchannel::create( getavailableport() );
|
|
1953
2082
|
|
|
1954
2083
|
/* optional - remote */
|
|
1955
2084
|
dtlssession::mode dtlsmode = dtlssession::none;
|
|
@@ -2196,7 +2325,7 @@ nodtls:
|
|
|
2196
2325
|
if( napi_ok != napi_create_object( env, &nlocal ) ||
|
|
2197
2326
|
napi_ok != napi_set_named_property( env, result, "local", nlocal ) ||
|
|
2198
2327
|
|
|
2199
|
-
napi_ok != napi_create_int32( env,
|
|
2328
|
+
napi_ok != napi_create_int32( env, p->getport(), &nourport ) ||
|
|
2200
2329
|
napi_ok != napi_set_named_property( env, nlocal, "port", nourport ) ||
|
|
2201
2330
|
|
|
2202
2331
|
napi_ok != napi_create_uint32( env, ssrc, &nssrc ) ||
|
|
@@ -2227,12 +2356,8 @@ void getchannelstats( napi_env env, napi_value &result ) {
|
|
|
2227
2356
|
|
|
2228
2357
|
napi_value av, chcount;
|
|
2229
2358
|
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
RELEASESPINLOCK( availableportslock );
|
|
2233
|
-
|
|
2234
|
-
if( napi_ok != napi_create_double( env, availableportssize, &av ) ) return;
|
|
2235
|
-
if( napi_ok != napi_create_double( env, channelscreated, &chcount ) ) return;
|
|
2359
|
+
if( napi_ok != napi_create_double( env, getvailableportsize(), &av ) ) return;
|
|
2360
|
+
if( napi_ok != napi_create_double( env, channelscreated.load(), &chcount ) ) return;
|
|
2236
2361
|
|
|
2237
2362
|
if( napi_ok != napi_set_named_property( env, channel, "available", av ) ) return;
|
|
2238
2363
|
if( napi_ok != napi_set_named_property( env, channel, "current", chcount ) ) return;
|
package/src/projectrtpchannel.h
CHANGED
|
@@ -133,6 +133,7 @@ public:
|
|
|
133
133
|
std::atomic_uint64_t totalticktime;
|
|
134
134
|
std::atomic_uint64_t totaltickcount;
|
|
135
135
|
std::atomic_uint16_t tickswithnortpcount;
|
|
136
|
+
std::atomic_uint16_t hardtickswithnortpcount;
|
|
136
137
|
|
|
137
138
|
std::atomic_uint64_t outpkwritecount;
|
|
138
139
|
std::atomic_uint64_t outpkcount;
|
|
@@ -220,6 +221,9 @@ private:
|
|
|
220
221
|
static bool recordercompleted( const channelrecorder::pointer& value );
|
|
221
222
|
void dounmix( void );
|
|
222
223
|
|
|
224
|
+
void badsocketopen( const char *err );
|
|
225
|
+
void returnavailableport( void );
|
|
226
|
+
|
|
223
227
|
std::atomic_bool mixerlock;
|
|
224
228
|
projectchannelmuxptr mixer;
|
|
225
229
|
std::atomic_bool mixing;
|
|
@@ -60,6 +60,8 @@ void projectchannelmux::mixall( void ) {
|
|
|
60
60
|
src = chan->inbuff->peek();
|
|
61
61
|
RELEASESPINLOCK( chan->rtpbufferlock );
|
|
62
62
|
|
|
63
|
+
if( nullptr == src ) break;
|
|
64
|
+
|
|
63
65
|
AQUIRESPINLOCK( chan->rtpdtlslock );
|
|
64
66
|
dtlssession::pointer currentdtlssession = chan->rtpdtls;
|
|
65
67
|
RELEASESPINLOCK( chan->rtpdtlslock );
|
|
@@ -68,6 +70,11 @@ void projectchannelmux::mixall( void ) {
|
|
|
68
70
|
if( !currentdtlssession->unprotect( src ) ) {
|
|
69
71
|
chan->receivedpkskip++;
|
|
70
72
|
src = nullptr;
|
|
73
|
+
|
|
74
|
+
AQUIRESPINLOCK( chan->rtpbufferlock );
|
|
75
|
+
chan->inbuff->poppeeked();
|
|
76
|
+
RELEASESPINLOCK( chan->rtpbufferlock );
|
|
77
|
+
break;
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
80
|
|
|
@@ -93,7 +100,12 @@ void projectchannelmux::mixall( void ) {
|
|
|
93
100
|
|
|
94
101
|
/* Now we subtract this channel to send to this channel. */
|
|
95
102
|
for( auto& chan: this->channels ) {
|
|
96
|
-
if( !chan->send )
|
|
103
|
+
if( !chan->send ) {
|
|
104
|
+
AQUIRESPINLOCK( chan->rtpbufferlock );
|
|
105
|
+
chan->inbuff->poppeeked();
|
|
106
|
+
RELEASESPINLOCK( chan->rtpbufferlock );
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
97
109
|
|
|
98
110
|
rtppacket *dst = chan->gettempoutbuf();
|
|
99
111
|
|
|
@@ -102,7 +114,7 @@ void projectchannelmux::mixall( void ) {
|
|
|
102
114
|
|
|
103
115
|
if( chan->recv ) {
|
|
104
116
|
AQUIRESPINLOCK( chan->rtpbufferlock );
|
|
105
|
-
rtppacket *src = chan->inbuff->
|
|
117
|
+
rtppacket *src = chan->inbuff->peeked();
|
|
106
118
|
RELEASESPINLOCK( chan->rtpbufferlock );
|
|
107
119
|
if( nullptr != src ) {
|
|
108
120
|
this->subtracted -= chan->incodec;
|
|
@@ -136,6 +148,8 @@ void projectchannelmux::mix2( void ) {
|
|
|
136
148
|
src = chan1->inbuff->pop();
|
|
137
149
|
RELEASESPINLOCK( chan1->rtpbufferlock );
|
|
138
150
|
|
|
151
|
+
if( nullptr == src ) break;
|
|
152
|
+
|
|
139
153
|
AQUIRESPINLOCK( chan1->rtpdtlslock );
|
|
140
154
|
dtlssession::pointer currentdtlssession = chan1->rtpdtls;
|
|
141
155
|
RELEASESPINLOCK( chan1->rtpdtlslock );
|
|
@@ -144,6 +158,7 @@ void projectchannelmux::mix2( void ) {
|
|
|
144
158
|
if( !currentdtlssession->unprotect( src ) ) {
|
|
145
159
|
chan1->receivedpkskip++;
|
|
146
160
|
src = nullptr;
|
|
161
|
+
break;
|
|
147
162
|
}
|
|
148
163
|
}
|
|
149
164
|
|
|
@@ -157,6 +172,8 @@ void projectchannelmux::mix2( void ) {
|
|
|
157
172
|
src = chan2->inbuff->pop();
|
|
158
173
|
RELEASESPINLOCK( chan2->rtpbufferlock );
|
|
159
174
|
|
|
175
|
+
if( nullptr == src ) break;
|
|
176
|
+
|
|
160
177
|
AQUIRESPINLOCK( chan2->rtpdtlslock );
|
|
161
178
|
dtlssession::pointer currentdtlssession = chan2->rtpdtls;
|
|
162
179
|
RELEASESPINLOCK( chan2->rtpdtlslock );
|
|
@@ -165,6 +182,7 @@ void projectchannelmux::mix2( void ) {
|
|
|
165
182
|
if( !currentdtlssession->unprotect( src ) ) {
|
|
166
183
|
chan2->receivedpkskip++;
|
|
167
184
|
src = nullptr;
|
|
185
|
+
break;
|
|
168
186
|
}
|
|
169
187
|
}
|
|
170
188
|
|
|
@@ -30,6 +30,7 @@ public:
|
|
|
30
30
|
/* must have started for this to kick in */
|
|
31
31
|
uint16_t startabovepower;
|
|
32
32
|
/* must have started for this to kick in */
|
|
33
|
+
uint16_t maxsincestartpower;
|
|
33
34
|
uint16_t finishbelowpower;
|
|
34
35
|
/* used in conjunction with finishbelowpower */
|
|
35
36
|
uint32_t minduration; /* mSeconds */
|
package/src/projectrtpcodecx.cpp
CHANGED
|
@@ -69,13 +69,14 @@ static const uint8_t alaw_to_ulaw_table[256] =
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
void gen711convertdata( void ) {
|
|
72
|
-
|
|
72
|
+
int32_t i;
|
|
73
|
+
for( i = 0; i != 65536; i++ ) {
|
|
73
74
|
int16_t l16val = i - 32768;
|
|
74
75
|
_l16topcmu[ i ] = linear_to_ulaw( l16val );
|
|
75
76
|
_l16topcma[ i ] = linear_to_alaw( l16val );
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
for(
|
|
79
|
+
for( i = 0; i != 256; i++ ) {
|
|
79
80
|
_pcmatol16[ i ] = alaw_to_linear( i );
|
|
80
81
|
_pcmutol16[ i ] = ulaw_to_linear( i );
|
|
81
82
|
}
|
|
@@ -149,7 +149,7 @@ describe( "dtmf", function() {
|
|
|
149
149
|
|
|
150
150
|
|
|
151
151
|
it( "single channel and request rtp server to send 2833", function( done ) {
|
|
152
|
-
|
|
152
|
+
/* TODO - check tre fuller structure of an RTP event (duration, volume, etc) */
|
|
153
153
|
/* create our RTP/UDP endpoint */
|
|
154
154
|
const server = dgram.createSocket( "udp4" )
|
|
155
155
|
let dtmfpkcount = 0
|
|
@@ -182,7 +182,7 @@ describe( "dtmf", function() {
|
|
|
182
182
|
|
|
183
183
|
if( "close" === d.action ) {
|
|
184
184
|
server.close()
|
|
185
|
-
expect( dtmfpkcount ).to.equal( 2*
|
|
185
|
+
expect( dtmfpkcount ).to.equal( 2*6 )
|
|
186
186
|
done()
|
|
187
187
|
}
|
|
188
188
|
} )
|
|
@@ -263,7 +263,7 @@ describe( "dtmf", function() {
|
|
|
263
263
|
clienta.close()
|
|
264
264
|
clientb.close()
|
|
265
265
|
|
|
266
|
-
expect( dtmfpkcount ).to.equal( 3*
|
|
266
|
+
expect( dtmfpkcount ).to.equal( 3*6 )
|
|
267
267
|
} )
|
|
268
268
|
|
|
269
269
|
it( "2 channels mixing and request rtp server to send 2833 to one with dynamic payloadtype", async function() {
|
|
@@ -331,7 +331,7 @@ describe( "dtmf", function() {
|
|
|
331
331
|
clienta.close()
|
|
332
332
|
clientb.close()
|
|
333
333
|
|
|
334
|
-
expect( dtmfpkcount ).to.equal( 3*
|
|
334
|
+
expect( dtmfpkcount ).to.equal( 3*6 )
|
|
335
335
|
} )
|
|
336
336
|
|
|
337
337
|
it( "3 channels mixing and request rtp server to send 2833 to one", async function() {
|
|
@@ -414,7 +414,7 @@ describe( "dtmf", function() {
|
|
|
414
414
|
clientb.close()
|
|
415
415
|
clientc.close()
|
|
416
416
|
|
|
417
|
-
expect( dtmfpkcount ).to.equal( 5*
|
|
417
|
+
expect( dtmfpkcount ).to.equal( 5*6 )
|
|
418
418
|
|
|
419
419
|
await finished
|
|
420
420
|
} )
|
|
@@ -971,11 +971,11 @@ describe( "channel mix", function() {
|
|
|
971
971
|
endpointb.close()
|
|
972
972
|
endpointc.close()
|
|
973
973
|
|
|
974
|
-
expect( endpointapkcountzero ).to.be.within(
|
|
975
|
-
expect( endpointbpkcountzero ).to.be.within(
|
|
976
|
-
expect( endpointcpkcountzero ).to.be.within(
|
|
977
|
-
expect( endpointapkcountnotzero ).to.be.within( 4,
|
|
978
|
-
expect( endpointbpkcountnotzero ).to.be.within( 4,
|
|
974
|
+
expect( endpointapkcountzero ).to.be.within( 55, 75 )
|
|
975
|
+
expect( endpointbpkcountzero ).to.be.within( 55, 75 )
|
|
976
|
+
expect( endpointcpkcountzero ).to.be.within( 55, 75 )
|
|
977
|
+
expect( endpointapkcountnotzero ).to.be.within( 4, 18 )
|
|
978
|
+
expect( endpointbpkcountnotzero ).to.be.within( 4, 18 )
|
|
979
979
|
expect( endpointcpkcountnotzero ).to.be.below( 2 )
|
|
980
980
|
|
|
981
981
|
await finished
|
|
@@ -67,11 +67,13 @@ describe( "record", function() {
|
|
|
67
67
|
this.timeout( 1500 )
|
|
68
68
|
this.slow( 1200 )
|
|
69
69
|
|
|
70
|
+
let endclose = 0
|
|
70
71
|
server.bind()
|
|
71
72
|
server.on( "listening", async function() {
|
|
72
73
|
|
|
73
74
|
channel = await prtp.projectrtp.openchannel( { "remote": { "address": "localhost", "port": server.address().port, "codec": 0 } }, function( d ) {
|
|
74
75
|
if( "close" === d.action ) {
|
|
76
|
+
endclose = Date.now()
|
|
75
77
|
server.close()
|
|
76
78
|
}
|
|
77
79
|
} )
|
|
@@ -89,6 +91,7 @@ describe( "record", function() {
|
|
|
89
91
|
} )
|
|
90
92
|
|
|
91
93
|
await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1300 ) } )
|
|
94
|
+
const startclose = Date.now()
|
|
92
95
|
channel.close()
|
|
93
96
|
await new Promise( resolve => { server.on( "close", resolve ) } )
|
|
94
97
|
|
|
@@ -102,6 +105,7 @@ describe( "record", function() {
|
|
|
102
105
|
expect( wavinfo.chunksize ).to.be.within( 28000, 33000 )
|
|
103
106
|
expect( wavinfo.fmtchunksize ).to.equal( 16 )
|
|
104
107
|
expect( wavinfo.subchunksize ).to.be.within( 28000, 33000 )
|
|
108
|
+
expect( endclose - startclose ).to.be.below( 100 )
|
|
105
109
|
|
|
106
110
|
const ourfile = await fspromises.open( "/tmp/ourrecording.wav", "r" )
|
|
107
111
|
const buffer = Buffer.alloc( 28204 )
|