@babblevoice/projectrtp 2.4.12 → 2.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/projectrtp",
3
- "version": "2.4.12",
3
+ "version": "2.4.16",
4
4
  "description": "A scalable Node addon RTP server",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -121,11 +121,13 @@ projectrtpchannel::projectrtpchannel( unsigned short port ):
121
121
  }
122
122
 
123
123
  void projectrtpchannel::requestclose( std::string reason ) {
124
- if( !this->_requestclose.exchange( true, std::memory_order_acquire ) ) {
125
- this->closereason = reason;
126
- }
127
124
 
128
- this->removemixer = true;
125
+ this->closereason = reason;
126
+ if( this->mixing ) {
127
+ this->removemixer = true;
128
+ } else {
129
+ this->_requestclose.exchange( true, std::memory_order_acquire );
130
+ }
129
131
  }
130
132
 
131
133
  void projectrtpchannel::remote( std::string address,
@@ -390,6 +392,11 @@ void projectrtpchannel::handletick( const boost::system::error_code& error ) {
390
392
  if( error == boost::asio::error::operation_aborted ) return;
391
393
  if( !this->active ) return;
392
394
 
395
+ if( this->_requestclose ) {
396
+ this->doclose();
397
+ return;
398
+ }
399
+
393
400
  if( this->dtlsnegotiate() ) {
394
401
  this->setnexttick();
395
402
  return;
@@ -400,11 +407,6 @@ void projectrtpchannel::handletick( const boost::system::error_code& error ) {
400
407
  return;
401
408
  };
402
409
 
403
- if( this->_requestclose ) {
404
- this->doclose();
405
- return;
406
- }
407
-
408
410
  this->startticktimer();
409
411
 
410
412
  this->incrtsout();
@@ -1025,21 +1027,20 @@ bool projectrtpchannel::mix( projectrtpchannel::pointer other ) {
1025
1027
  if( nullptr == this->mixer && nullptr != other->mixer ) {
1026
1028
  this->mixer = other->mixer;
1027
1029
  this->mixer->addchannel( shared_from_this() );
1028
- postdatabacktojsfromthread( shared_from_this(), "mix", "start" );
1030
+
1029
1031
  } else if ( nullptr != this->mixer && nullptr == other->mixer ) {
1030
1032
  other->mixer = this->mixer;
1031
1033
  this->mixer->addchannel( other );
1032
- postdatabacktojsfromthread( shared_from_this(), "mix", "start" );
1034
+
1033
1035
  } else if( nullptr == this->mixer && nullptr == other->mixer ) {
1034
1036
  this->mixer = projectchannelmux::create( workercontext );
1035
1037
  other->mixer = this->mixer;
1036
1038
 
1037
1039
  this->mixer->addchannels( shared_from_this(), other );
1038
- postdatabacktojsfromthread( shared_from_this(), "mix", "start" );
1039
- postdatabacktojsfromthread( other, "mix", "start" );
1040
1040
  } else {
1041
1041
  /* If we get here this and other are already mixing and should be cleaned up first */
1042
1042
  RELEASESPINLOCK( this->mixerlock );
1043
+ postdatabacktojsfromthread( shared_from_this(), "mix", "busy" );
1043
1044
  return false;
1044
1045
  }
1045
1046
 
@@ -1050,6 +1051,9 @@ bool projectrtpchannel::mix( projectrtpchannel::pointer other ) {
1050
1051
 
1051
1052
  RELEASESPINLOCK( this->mixerlock );
1052
1053
 
1054
+ postdatabacktojsfromthread( shared_from_this(), "mix", "start" );
1055
+ postdatabacktojsfromthread( other, "mix", "start" );
1056
+
1053
1057
  return true;
1054
1058
  }
1055
1059
 
@@ -1059,7 +1063,11 @@ Add the other to a mixer - both channels have access to the same mixer.
1059
1063
  n way relationship. Adds to queue for when our main thread calls into us.
1060
1064
  */
1061
1065
  bool projectrtpchannel::unmix( void ) {
1062
- this->removemixer = true;
1066
+
1067
+ AQUIRESPINLOCK( this->mixerlock );
1068
+ if( nullptr != this->mixer ) this->removemixer = true;
1069
+ RELEASESPINLOCK( this->mixerlock );
1070
+
1063
1071
  return true;
1064
1072
  }
1065
1073
 
@@ -1075,6 +1083,10 @@ void projectrtpchannel::dounmix( void ) {
1075
1083
  RELEASESPINLOCK( this->mixerlock );
1076
1084
 
1077
1085
  postdatabacktojsfromthread( shared_from_this(), "mix", "finished" );
1086
+
1087
+ if( this->closereason.length() > 0 ) {
1088
+ this->_requestclose.exchange( true, std::memory_order_acquire );
1089
+ }
1078
1090
  }
1079
1091
 
1080
1092
  /*
@@ -559,7 +559,7 @@ rawsound& codecx::getref( int pt ) {
559
559
  }
560
560
  case PCMAPAYLOADTYPE: {
561
561
  if( this->pcmuref.size() > 0 ) {
562
- this->alaw2ulaw();
562
+ this->ulaw2alaw();
563
563
  } else {
564
564
  this->requirenarrowband();
565
565
  this->l16topcma();
@@ -568,7 +568,7 @@ rawsound& codecx::getref( int pt ) {
568
568
  }
569
569
  case PCMUPAYLOADTYPE: {
570
570
  if( this->pcmaref.size() > 0 ) {
571
- this->ulaw2alaw();
571
+ this->alaw2ulaw();
572
572
  } else {
573
573
  this->requirenarrowband();
574
574
  this->l16topcmu();
@@ -697,8 +697,7 @@ codecx& operator << ( codecx& c, const char& a )
697
697
 
698
698
 
699
699
 
700
- void codectests( void )
701
- {
700
+ void codectests( void ) {
702
701
  /* init transcoding stuff */
703
702
  gen711convertdata();
704
703
 
@@ -715,8 +714,7 @@ void codectests( void )
715
714
 
716
715
 
717
716
  g722_decode_state_t *g722decoder = g722_decode_init( NULL, 64000, G722_PACKED );
718
- if( nullptr == g722decoder )
719
- {
717
+ if( nullptr == g722decoder ) {
720
718
  std::cerr << "Failed to init G722 decoder" << std::endl;
721
719
  }
722
720
 
@@ -727,15 +725,13 @@ void codectests( void )
727
725
  g722samplepk,
728
726
  160 );
729
727
  std::cout << "g722_decode returned " << l1616klength << " for an in packet of 160 bytes" << std::endl;
730
- if( 320 != l1616klength )
731
- {
728
+ if( 320 != l1616klength ) {
732
729
  std::cerr << "ERROR - decoded length is not 320 bytes" << std::endl;
733
730
  }
734
731
  g722_decode_free( g722decoder );
735
732
 
736
733
  std::cout << "L16 OUT (g722_decode) =" << std::endl;
737
- for( int i = 0; i < 160; i ++ )
738
- {
734
+ for( int i = 0; i < 160; i ++ ) {
739
735
  std::cout << unsigned( outbuf[ i ] ) << " ";
740
736
  }
741
737
  std::cout << std::endl;
@@ -744,66 +740,123 @@ void codectests( void )
744
740
  /*
745
741
  Move onto test our interface.
746
742
  */
747
- rawsound r( g722samplepk, sizeof( g722samplepk ), G722PAYLOADTYPE, 16000 );
748
- std::cout << "G722 raw packet size " << r.size() << " with a format of " << r.getformat() << " and sample rate " << r.getsamplerate() << std::endl;
743
+ {
744
+ rawsound r( g722samplepk, sizeof( g722samplepk ), G722PAYLOADTYPE, 16000 );
745
+ std::cout << "G722 raw packet size " << r.size() << " with a format of " << r.getformat() << " and sample rate " << r.getsamplerate() << std::endl;
749
746
 
750
- codecx ourcodec;
747
+ codecx ourcodec;
751
748
 
752
- ourcodec << codecx::next;
753
- ourcodec << r;
749
+ ourcodec << codecx::next;
750
+ ourcodec << r;
754
751
 
755
- rtppacket outpk;
756
- outpk.setpayloadtype( PCMAPAYLOADTYPE );
757
- outpk.setpayloadlength( 160 );
758
- outpk.setsequencenumber( 0 );
759
- outpk.settimestamp( 0 );
760
- outpk << ourcodec;
752
+
761
753
 
762
- std::cout << "PCMA OUT =" << std::endl;
763
- uint8_t *pl = outpk.getpayload();
754
+ rtppacket outpk;
755
+ outpk.setpayloadtype( PCMAPAYLOADTYPE );
756
+ outpk.setpayloadlength( 160 );
757
+ outpk.setsequencenumber( 0 );
758
+ outpk.settimestamp( 0 );
759
+ outpk << ourcodec;
764
760
 
765
- for( int i = 0; i < 160; i ++ )
766
- {
767
- std::cout << unsigned( pl[ i ] ) << " ";
768
- }
769
- std::cout << std::endl;
761
+ std::cout << "PCMA OUT =" << std::endl;
762
+ uint8_t *pl = outpk.getpayload();
763
+
764
+ for( int i = 0; i < 160; i ++ ) {
765
+ std::cout << unsigned( pl[ i ] ) << " ";
766
+ }
767
+ std::cout << std::endl;
768
+
769
+ if( 213 != pl[ 0 ] ) std::cout << "ERROR ERROR First byte should be 213???" << std::endl;
770
+ if( 85 != pl[ 9 ] ) std::cout << "ERROR ERROR 9th byte should be 85???" << std::endl;
771
+
772
+ rawsound &ref8k = ourcodec.getref( L168KPAYLOADTYPE );
773
+
774
+ if( ref8k.isdirty() ) std::cout << "ERROR our 8k out ref should not be dirty" << std::endl;
775
+
776
+ /* Repeat as this will use a different bit of code getting cached bit */
777
+ ref8k = ourcodec.getref( L168KPAYLOADTYPE );
778
+ if( ref8k.isdirty() ) std::cout << "ERROR our 8k out ref should not be dirty" << std::endl;
779
+
780
+ std::cout << "8k is dirty: " << std::boolalpha << ref8k.isdirty() << std::endl;
781
+ std::cout << "8k size: " << ref8k.size() << std::endl;
782
+ std::cout << "8k bytes per sample (should be 2): " << ref8k.getbytespersample() << std::endl;
770
783
 
771
- if( 213 != pl[ 0 ] ) std::cout << "ERROR ERROR First byte should be 213???" << std::endl;
772
- if( 85 != pl[ 9 ] ) std::cout << "ERROR ERROR 9th byte should be 85???" << std::endl;
784
+ /* 2 bytes per sample */
785
+ int16_t *pl16 = ( int16_t * ) ref8k.c_str();
786
+ for( size_t i = 0; i < ref8k.size(); i ++ ) {
787
+ std::cout << static_cast<int16_t>( pl16[ i ] ) << " ";
788
+ }
789
+ std::cout << std::endl;
773
790
 
774
- rawsound &ref8k = ourcodec.getref( L168KPAYLOADTYPE );
775
791
 
776
- if( ref8k.isdirty() ) std::cout << "ERROR our 8k out ref should not be dirty" << std::endl;
792
+ rawsound &ref16k = ourcodec.getref( L1616KPAYLOADTYPE );
793
+ if( ref16k.isdirty() ) std::cout << "ERROR our 16k out ref should not be dirty" << std::endl;
794
+ std::cout << "16k is dirty: " << std::boolalpha << ref16k.isdirty() << std::endl;
795
+ std::cout << "16k size: " << ref16k.size() << std::endl;
796
+ std::cout << "16k bytes per sample (should be 2): " << ref16k.getbytespersample() << std::endl;
777
797
 
778
- /* Repeat as this will use a different bit of code getting cached bit */
779
- ref8k = ourcodec.getref( L168KPAYLOADTYPE );
780
- if( ref8k.isdirty() ) std::cout << "ERROR our 8k out ref should not be dirty" << std::endl;
798
+ pl16 = ( int16_t * ) ref16k.c_str();
799
+ for( size_t i = 0; i < ref16k.size(); i ++ ) {
800
+ std::cout << static_cast<int16_t>( pl16[ i ] ) << " ";
801
+ }
802
+ std::cout << std::endl;
803
+ }
781
804
 
782
- std::cout << "8k is dirty: " << std::boolalpha << ref8k.isdirty() << std::endl;
783
- std::cout << "8k size: " << ref8k.size() << std::endl;
784
- std::cout << "8k bytes per sample (should be 2): " << ref8k.getbytespersample() << std::endl;
785
805
 
786
- /* 2 bytes per sample */
787
- int16_t *pl16 = ( int16_t * ) ref8k.c_str();
788
- for( size_t i = 0; i < ref8k.size(); i ++ )
789
806
  {
790
- std::cout << static_cast<int16_t>( pl16[ i ] ) << " ";
807
+ /* problem with converting pcmu to pcma */
808
+ /* use the same data, but pretend it is PCMU */
809
+ rawsound r( g722samplepk, sizeof( g722samplepk ), PCMUPAYLOADTYPE, 8000 );
810
+
811
+ codecx ourcodec;
812
+
813
+ ourcodec << codecx::next;
814
+ ourcodec << r;
815
+
816
+ rtppacket outpk;
817
+ outpk.setpayloadtype( PCMAPAYLOADTYPE );
818
+ outpk.setpayloadlength( 160 );
819
+ outpk.setsequencenumber( 0 );
820
+ outpk.settimestamp( 0 );
821
+ outpk << ourcodec;
822
+
823
+ std::cout << "PCMA OUT (it should not be all zeros) = " << std::endl;
824
+
825
+ uint8_t *pl = outpk.getpayload();
826
+
827
+ for( int i = 0; i < 160; i ++ ) {
828
+ std::cout << unsigned( pl[ i ] ) << " ";
829
+ }
830
+ std::cout << std::endl;
791
831
  }
792
- std::cout << std::endl;
793
832
 
833
+ {
834
+ /* problem with converting pcmu to pcma */
835
+ /* use the same data, but pretend it is PCMU */
836
+ rawsound r( g722samplepk, sizeof( g722samplepk ), PCMAPAYLOADTYPE, 8000 );
794
837
 
795
- rawsound &ref16k = ourcodec.getref( L1616KPAYLOADTYPE );
796
- if( ref16k.isdirty() ) std::cout << "ERROR our 16k out ref should not be dirty" << std::endl;
797
- std::cout << "16k is dirty: " << std::boolalpha << ref16k.isdirty() << std::endl;
798
- std::cout << "16k size: " << ref16k.size() << std::endl;
799
- std::cout << "16k bytes per sample (should be 2): " << ref16k.getbytespersample() << std::endl;
838
+ codecx ourcodec;
800
839
 
801
- pl16 = ( int16_t * ) ref16k.c_str();
802
- for( size_t i = 0; i < ref16k.size(); i ++ )
803
- {
804
- std::cout << static_cast<int16_t>( pl16[ i ] ) << " ";
840
+ ourcodec << codecx::next;
841
+ ourcodec << r;
842
+
843
+ rtppacket outpk;
844
+ outpk.setpayloadtype( PCMUPAYLOADTYPE );
845
+ outpk.setpayloadlength( 160 );
846
+ outpk.setsequencenumber( 0 );
847
+ outpk.settimestamp( 0 );
848
+ outpk << ourcodec;
849
+
850
+ std::cout << "PCMU OUT (it should not be all zeros) = " << std::endl;
851
+
852
+ uint8_t *pl = outpk.getpayload();
853
+
854
+ for( int i = 0; i < 160; i ++ ) {
855
+ std::cout << unsigned( pl[ i ] ) << " ";
856
+ }
857
+ std::cout << std::endl;
805
858
  }
806
- std::cout << std::endl;
859
+
807
860
  }
808
861
 
809
862
  #ifdef NODE_MODULE
@@ -0,0 +1,10 @@
1
+
2
+
3
+ const prtp = require( "../index.js" )
4
+
5
+ prtp.projectrtp.run()
6
+
7
+
8
+ prtp.projectrtp.codecx.codectests()
9
+
10
+ prtp.projectrtp.shutdown()
@@ -620,6 +620,7 @@ describe( "dtmf", function() {
620
620
  expect( receivedmessages[ 1 ].action ).to.equal( "telephone-event" )
621
621
  expect( receivedmessages[ 2 ].action ).to.equal( "telephone-event" )
622
622
  expect( receivedmessages[ 3 ].action ).to.equal( "mix" )
623
+ expect( receivedmessages[ 3 ].event ).to.equal( "finished" )
623
624
  expect( receivedmessages[ 4 ].action ).to.equal( "close" )
624
625
 
625
626
  expect( receivedmessages[ 0 ].event ).to.equal( "start" )
@@ -2,6 +2,7 @@
2
2
  const prtp = require( "../../index" )
3
3
  const node = require( "../../lib/node" )
4
4
  const expect = require( "chai" ).expect
5
+ const fs = require( "node:fs" )
5
6
 
6
7
  /**
7
8
  * Test file to run tests acting as a remote note. Starts a babble-rtp node in the background
@@ -25,7 +26,7 @@ describe( "server connect interface", () => {
25
26
 
26
27
  it( "server connect and open channel", async function () {
27
28
  this.timeout( 6000 )
28
- this.slow( 3000 )
29
+ this.slow( 30000 )
29
30
 
30
31
  const totalotherchannelcount = 100
31
32
  let chanclosecount = 0
@@ -37,6 +38,8 @@ describe( "server connect interface", () => {
37
38
  }
38
39
 
39
40
  // A very short wav file
41
+ await fs.promises.rm( "/tmp/serverconnecttestwav.wav", { force: true } )
42
+ await fs.promises.rm( "/tmp/otherserverconnecttestwav.wav", { force: true } )
40
43
  prtp.projectrtp.tone.generate( "350+440*0.5:100", "/tmp/serverconnecttestwav.wav" )
41
44
  prtp.projectrtp.tone.generate( "350+440*0.5:100", "/tmp/otherserverconnecttestwav.wav" )
42
45
 
@@ -48,16 +51,13 @@ describe( "server connect interface", () => {
48
51
  for( let i = 0; 3 > i; i++ ) {
49
52
  let done
50
53
  const finished = new Promise( resolve => done = resolve )
51
-
52
54
  const receivedmessages = []
53
55
  const chan = await prtp.projectrtp.openchannel( ( e ) => {
54
56
  receivedmessages.push( e )
55
57
  if( "play" == e.action && "end" == e.event ) chan.close()
56
58
  if( "close" == e.action ) done()
57
59
  } )
58
-
59
60
  chan.play( { "interupt":true, "files": [ { "wav": "/tmp/serverconnecttestwav.wav" }, { "wav": "/tmp/otherserverconnecttestwav.wav" } ] } )
60
-
61
61
  await finished
62
62
 
63
63
  //console.log(receivedmessages)
@@ -172,6 +172,101 @@ describe( "channel mix", function() {
172
172
 
173
173
  } )
174
174
 
175
+
176
+ it( "mix 2 channels then unmix then mix again", async function() {
177
+
178
+ this.timeout( 6000 )
179
+ this.slow( 5000 )
180
+
181
+ const endpointa = dgram.createSocket( "udp4" )
182
+ const endpointb = dgram.createSocket( "udp4" )
183
+
184
+ const endpointapkcount = [ 0, 0, 0, 0 ]
185
+ const endpointbpkcount = [ 0, 0, 0, 0 ]
186
+
187
+ endpointa.on( "message", function( msg ) {
188
+ endpointapkcount[ 0x7f & msg[ 50 ] ]++
189
+ expect( msg.length ).to.equal( 172 )
190
+ expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
191
+ } )
192
+
193
+ endpointb.on( "message", function( msg ) {
194
+ endpointbpkcount[ 0x7f & msg[ 50 ] ]++
195
+ expect( msg.length ).to.equal( 172 )
196
+ expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
197
+ } )
198
+
199
+ endpointa.bind()
200
+ await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } )
201
+
202
+ endpointb.bind()
203
+ await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } )
204
+
205
+ let promisearesolve, promisebresolve
206
+ const promisea = new Promise( r => promisearesolve = r )
207
+ const promiseb = new Promise( r => promisebresolve = r )
208
+
209
+ const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) {
210
+ if( "close" === d.action ) {
211
+ promisearesolve()
212
+ }
213
+ } )
214
+
215
+ const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) {
216
+ if( "close" === d.action ) {
217
+ promisebresolve()
218
+ }
219
+ } )
220
+
221
+ /* mix */
222
+ expect( channela.mix( channelb ) ).to.be.true
223
+ /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */
224
+ let dataa = Buffer.alloc( 172 - 12 ).fill( 0 )
225
+ let datab = Buffer.alloc( 172 - 12 ).fill( 1 )
226
+
227
+ for( let i = 0; 50 > i; i ++ ) {
228
+ sendpk( i, i, channela.local.port, endpointa, dataa )
229
+ sendpk( i, i, channelb.local.port, endpointb, datab )
230
+ }
231
+
232
+ /* 1S for 50 packets to get through, 0.5 seconds to allow all through jitter buffers */
233
+ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1700 ) } )
234
+
235
+ channela.direction( { send: true, recv: false } )
236
+ expect( channelb.unmix( channela ) ).to.be.true
237
+ channelb.play( { "loop": true, "files": [
238
+ { "wav": "/tmp/ukringing.wav" } ] } )
239
+
240
+ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 200 ) } )
241
+
242
+ channela.direction( { send: true, recv: true } )
243
+ expect( channelb.mix( channela ) ).to.be.true
244
+
245
+
246
+ dataa = Buffer.alloc( 172 - 12 ).fill( 2 )
247
+ datab = Buffer.alloc( 172 - 12 ).fill( 3 )
248
+ for( let i = 0; 50 > i; i ++ ) {
249
+ sendpk( i, i, channela.local.port, endpointa, dataa )
250
+ sendpk( i, i, channelb.local.port, endpointb, datab )
251
+ }
252
+
253
+ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1700 ) } )
254
+
255
+ channelb.close()
256
+ channela.close()
257
+ endpointa.close()
258
+ endpointb.close()
259
+
260
+ /* have we cleaned up? */
261
+ await Promise.all( [ promisea, promiseb ] )
262
+
263
+ expect( endpointapkcount[ 1 ] ).to.be.within( 49, 51 )
264
+ expect( endpointapkcount[ 3 ] ).to.be.within( 49, 51 )
265
+ expect( endpointbpkcount[ 0 ] ).to.be.within( 49, 51 )
266
+ expect( endpointbpkcount[ 2 ] ).to.be.within( 49, 51 )
267
+
268
+ } )
269
+
175
270
  it( "mix 2 channels then close b", async function() {
176
271
 
177
272
  this.timeout( 3000 )