UNPKG

@babblevoice/projectrtp

Version:
1,142 lines (814 loc) 36.9 kB
const expect = require( "chai" ).expect const dgram = require( "dgram" ) const projectrtp = require( "../../index.js" ).projectrtp const fs = require( "node:fs" ).promises function sendpk( sn, sendtime, dstport, server, data = undefined, pt = 0, ssrc = 25 ) { const pklength = 172 return setTimeout( () => { let payload if( undefined !== data ) { payload = data } else { payload = Buffer.alloc( pklength - 12 ).fill( projectrtp.codecx.linear162pcmu( sn ) & 0xff ) } const subheader = Buffer.alloc( 10 ) const ts = sn * 160 subheader.writeUInt16BE( ( sn + 100 ) % ( 2**16 ) /* just some offset */ ) subheader.writeUInt32BE( ts, 2 ) subheader.writeUInt32BE( ssrc, 6 ) const header = Buffer.from( [ 0x80, 0x00 ] ) header.writeUInt8( pt, 1 ) // payload type const rtppacket = Buffer.concat( [ header, subheader, payload ] ) server.send( rtppacket, dstport, "localhost" ) }, sendtime * 20 ) } describe( "channel mix", function() { this.beforeAll( () => { projectrtp.tone.generate( "400+450*0.5/0/400+450*0.5/0:400/200/400/2000", "/tmp/ukringing.wav" ) } ) this.afterAll( async () => { await fs.unlink( "/tmp/ukringing.wav" ) } ) it( "basic mix 2 channels", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( r ) => { endpointa.on( "listening", function() { r() } ) } ) endpointb.bind() await new Promise( ( r ) => { endpointb.on( "listening", function() { r() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1300 ) } ) channela.close() endpointa.close() endpointb.close() expect( endpointapkcount ).to.be.within( 30, 51 ) expect( endpointbpkcount ).to.be.within( 30, 51 ) await finished } ) it( "basic mix 2 channels with start 2 packets wrong payload type", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 8 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( r ) => { endpointa.on( "listening", function() { r() } ) } ) endpointb.bind() await new Promise( ( r ) => { endpointb.on( "listening", function() { r() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( {}, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* a problem was highlighted that would remote would be called before rtp stream was updated */ channela.remote( { "address": "localhost", "port": endpointa.address().port, "codec": 8 } ) sendpk( 0, 0, channela.local.port, endpointa ) sendpk( 1, 1, channela.local.port, endpointa ) /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 2; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa, undefined, 8, 27 ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1300 ) } ) channela.close() endpointa.close() endpointb.close() expect( endpointapkcount ).to.be.within( 30, 51 ) expect( endpointbpkcount ).to.be.within( 30, 51 ) await finished } ) it( "mix 2 channels then unmix", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let promisearesolve, promisebresolve const promisea = new Promise( r => promisearesolve = r ) const promiseb = new Promise( r => promisebresolve = r ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) { promisearesolve() } } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) { promisebresolve() } } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } /* 1S for 50 packets to get through, 0.5 seconds to allow all through jitter buffers */ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1700 ) } ) channelb.unmix() channelb.close() channela.close() endpointa.close() endpointb.close() /* have we cleaned up? */ await Promise.all( [ promisea, promiseb ] ) expect( endpointapkcount ).to.be.within( 49, 51 ) expect( endpointbpkcount ).to.be.within( 49, 51 ) } ) it( "mix 2 channels then unmix then mix again", async function() { this.timeout( 6000 ) this.slow( 5000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) const endpointapkcount = [ 0, 0, 0, 0 ] const endpointbpkcount = [ 0, 0, 0, 0 ] endpointa.on( "message", function( msg ) { endpointapkcount[ 0x7f & msg[ 50 ] ]++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount[ 0x7f & msg[ 50 ] ]++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let promisearesolve, promisebresolve const promisea = new Promise( r => promisearesolve = r ) const promiseb = new Promise( r => promisebresolve = r ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) { promisearesolve() } } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) { promisebresolve() } } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ let dataa = Buffer.alloc( 172 - 12 ).fill( 0 ) let datab = Buffer.alloc( 172 - 12 ).fill( 1 ) for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa, dataa ) sendpk( i, i, channelb.local.port, endpointb, datab ) } /* 1S for 50 packets to get through, 0.5 seconds to allow all through jitter buffers */ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1700 ) } ) channela.direction( { send: true, recv: false } ) expect( channelb.unmix( channela ) ).to.be.true channelb.play( { "loop": true, "files": [ { "wav": "/tmp/ukringing.wav" } ] } ) await new Promise( ( resolve ) => { setTimeout( () => resolve(), 200 ) } ) channela.direction( { send: true, recv: true } ) expect( channelb.mix( channela ) ).to.be.true dataa = Buffer.alloc( 172 - 12 ).fill( 2 ) datab = Buffer.alloc( 172 - 12 ).fill( 3 ) for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa, dataa ) sendpk( i, i, channelb.local.port, endpointb, datab ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1700 ) } ) channelb.close() channela.close() endpointa.close() endpointb.close() /* have we cleaned up? */ await Promise.all( [ promisea, promiseb ] ) expect( endpointapkcount[ 1 ] ).to.be.within( 49, 51 ) expect( endpointapkcount[ 3 ] ).to.be.within( 49, 51 ) expect( endpointbpkcount[ 0 ] ).to.be.within( 49, 51 ) expect( endpointbpkcount[ 2 ] ).to.be.within( 49, 51 ) } ) it( "mix 2 channels then close b", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let promisearesolve, promisebresolve const promisea = new Promise( r => promisearesolve = r ) const promiseb = new Promise( r => promisebresolve = r ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) { promisearesolve() } } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) { promisebresolve() } } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } /* 1S for 50 packets @ 20mS 0.5 seconds to allow through jitter buffers */ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1700 ) } ) channelb.close() channela.close() endpointa.close() endpointb.close() /* have we closed? */ await Promise.all( [ promisea, promiseb ] ) expect( endpointapkcount ).to.be.within( 49, 51 ) expect( endpointbpkcount ).to.be.within( 49, 51 ) } ) it( "mix 2 channels - pcmu <-> pcma", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 8 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 8 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } ) channela.close() endpointa.close() endpointb.close() expect( endpointapkcount ).to.be.within( 30, 51 ) expect( endpointbpkcount ).to.be.within( 30, 51 ) await finished } ) it( "mix 2 channels - pcmu <-> ilbc", async function() { this.timeout( 3000 ) this.slow( 2500 ) const bssrc = 1 const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( msg.length ).to.equal( 172 ) expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( msg.length ).to.equal( 50 ) expect( 0x7f & msg [ 1 ] ).to.equal( 97 ) msg.writeUInt32BE( bssrc, 8 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 97 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1400 ) } ) channela.close() endpointa.close() endpointb.close() expect( endpointapkcount ).to.be.above( 30 ) expect( endpointbpkcount ).to.be.above( 30 ) await finished } ) it( "mix 2 channels - pcmu <-> g722", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( 0x7f & msg [ 1 ] ).to.equal( 9 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 9 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } ) channela.close() endpointa.close() endpointb.close() expect( endpointapkcount ).to.be.above( 30 ) expect( endpointbpkcount ).to.be.above( 30 ) await finished } ) it( "mix 2 channels - pcmu <-> g722 with recording", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( 0x7f & msg [ 1 ] ).to.equal( 9 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 9 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true channela.record( { "file": "/tmp/g722mix2recording.wav" } ) /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } ) channela.close() endpointa.close() endpointb.close() expect( endpointapkcount ).to.be.above( 30 ) expect( endpointbpkcount ).to.be.above( 30 ) await finished } ) it( "playback prompt then mix 2 channels - pcmu <-> g722 with recording", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( 0x7f & msg [ 1 ] ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( 0x7f & msg [ 1 ] ).to.equal( 9 ) endpointb.send( msg, channelb.local.port, "localhost" ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) channela.play( { "loop": true, "files": [ { "wav": "/tmp/ukringing.wav" } ] } ) await new Promise( resolve => setTimeout( resolve, 500 ) ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 9 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true channela.record( { "file": "/tmp/g722mix2recording.wav" } ) /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channela.local.port, endpointa ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } ) channela.close() endpointa.close() endpointb.close() expect( endpointapkcount ).to.be.above( 30 ) expect( endpointbpkcount ).to.be.above( 30 ) await finished } ) it( "mix 3 channels - 1 writer 3 readers", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) const endpointc = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointc.on( "message", function( msg ) { expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) endpointc.bind() await new Promise( ( resolve ) => { endpointc.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelc.close() } ) const channelc = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointc.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true expect( channela.mix( channelc ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channelc.local.port, endpointc, Buffer.alloc( 160 ).fill( projectrtp.codecx.linear162pcmu( 8 ) ) ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1200 ) } ) channela.close() endpointa.close() endpointb.close() endpointc.close() /* This value is based on timeing so may vary very slightly */ expect( endpointapkcount ).to.be.within( 59, 61 ) expect( endpointbpkcount ).to.be.within( 59, 61 ) await finished } ) it( "mix 3 channels - 1 writer 1 readers (2 silenced)", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) const endpointc = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 let endpointcpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointc.on( "message", function( msg ) { endpointcpkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) endpointc.bind() await new Promise( ( resolve ) => { endpointc.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "direction": { "send": false }, "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "direction": { "send": false }, "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelc.close() } ) const channelc = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointc.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true expect( channela.mix( channelc ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channelc.local.port, endpointc, Buffer.alloc( 160 ).fill( projectrtp.codecx.linear162pcmu( 8 ) ) ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1200 ) } ) channela.close() endpointa.close() endpointb.close() endpointc.close() expect( endpointapkcount ).to.be.equal( 0 ) expect( endpointbpkcount ).to.be.equal( 0 ) expect( endpointcpkcount ).to.be.within( 59, 61 ) await finished } ) it( "mix 3 channels - 1 writer 3 recevers but writer recv=false", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) const endpointc = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 let endpointcpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.equal( 0 ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.equal( 0 ) } ) endpointc.on( "message", function( msg ) { endpointcpkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.equal( 0 ) } ) endpointa.bind() await new Promise( ( r ) => { endpointa.on( "listening", function() { r() } ) } ) endpointb.bind() await new Promise( ( r ) => { endpointb.on( "listening", function() { r() } ) } ) endpointc.bind() await new Promise( ( r ) => { endpointc.on( "listening", function() { r() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelc.close() } ) const channelc = await projectrtp.openchannel( { "direction": { "recv": false }, "remote": { "address": "localhost", "port": endpointc.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) done() } ) /* mix */ expect( channela.mix( channelb ) ).to.be.true expect( channela.mix( channelc ) ).to.be.true /* Send data - which should be ignored but receviers should recevie silence (in payload) */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channelc.local.port, endpointc ) } await new Promise( ( r ) => { setTimeout( () => r(), 1500 ) } ) channela.close() endpointa.close() endpointb.close() endpointc.close() expect( endpointapkcount ).to.be.within( 70, 80 ) expect( endpointbpkcount ).to.be.within( 70, 80 ) expect( endpointcpkcount ).to.be.within( 70, 80 ) await finished } ) it( "mix 3 channels - 1 writer 3 recevers but writer delayed recv=false", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) const endpointc = dgram.createSocket( "udp4" ) let endpointapkcountzero = 0 let endpointbpkcountzero = 0 let endpointcpkcountzero = 0 let endpointapkcountnotzero = 0 let endpointbpkcountnotzero = 0 let endpointcpkcountnotzero = 0 endpointa.on( "message", function( msg ) { if( 0 == projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ) { endpointapkcountzero++ } else { endpointapkcountnotzero++ } } ) endpointb.on( "message", function( msg ) { if( 0 == projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ) { endpointbpkcountzero++ } else { endpointbpkcountnotzero++ } } ) endpointc.on( "message", function( msg ) { if( 0 == projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ) { endpointcpkcountzero++ } else { endpointcpkcountnotzero++ } } ) endpointa.bind() await new Promise( ( r ) => { endpointa.on( "listening", function() { r() } ) } ) endpointb.bind() await new Promise( ( r ) => { endpointb.on( "listening", function() { r() } ) } ) endpointc.bind() await new Promise( ( r ) => { endpointc.on( "listening", function() { r() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelc.close() } ) const channelc = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointc.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) done() } ) setTimeout( () => expect( channelc.direction( { "recv": false } ) ).to.be.true , 400 ) /* mix */ expect( channela.mix( channelb ) ).to.be.true expect( channela.mix( channelc ) ).to.be.true /* Send data - which should be ignored but receviers should recevie silence (in payload) */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channelc.local.port, endpointc, Buffer.alloc( 160 ).fill( projectrtp.codecx.linear162pcmu( 8 ) ) ) } await new Promise( ( r ) => { setTimeout( () => r(), 1500 ) } ) channela.close() endpointa.close() endpointb.close() endpointc.close() expect( endpointapkcountzero ).to.be.within( 55, 75 ) expect( endpointbpkcountzero ).to.be.within( 55, 75 ) expect( endpointcpkcountzero ).to.be.within( 55, 75 ) expect( endpointapkcountnotzero ).to.be.within( 4, 18 ) expect( endpointbpkcountnotzero ).to.be.within( 4, 18 ) expect( endpointcpkcountnotzero ).to.be.below( 2 ) await finished } ) it( "mix 3 channels - 1 writer 1 readers (2 silenced but delayed)", async function() { this.timeout( 3000 ) this.slow( 2000 ) const endpointa = dgram.createSocket( "udp4" ) const endpointb = dgram.createSocket( "udp4" ) const endpointc = dgram.createSocket( "udp4" ) let endpointapkcount = 0 let endpointbpkcount = 0 let endpointcpkcount = 0 endpointa.on( "message", function( msg ) { endpointapkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointb.on( "message", function( msg ) { endpointbpkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointc.on( "message", function( msg ) { endpointcpkcount++ expect( projectrtp.codecx.pcmu2linear16( msg[ 30 ] ) ).to.be.oneOf([0 , 8 ] ) } ) endpointa.bind() await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } ) endpointb.bind() await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } ) endpointc.bind() await new Promise( ( resolve ) => { endpointc.on( "listening", function() { resolve() } ) } ) let done const finished = new Promise( ( r ) => { done = r } ) const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelb.close() } ) const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) channelc.close() } ) const channelc = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointc.address().port, "codec": 0 } }, function( d ) { if( "close" === d.action ) done() } ) setTimeout( () => expect( channela.direction( { "send": false } ) ).to.be.true , 200 ) setTimeout( () => expect( channelb.direction( { "send": false } ) ).to.be.true , 200 ) /* mix */ expect( channela.mix( channelb ) ).to.be.true expect( channela.mix( channelc ) ).to.be.true /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */ for( let i = 0; 50 > i; i ++ ) { sendpk( i, i, channelc.local.port, endpointc, Buffer.alloc( 160 ).fill( projectrtp.codecx.linear162pcmu( 8 ) ) ) } await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } ) channela.close() endpointa.close() endpointb.close() endpointc.close() expect( endpointapkcount ).to.be.at.most( 15 ) expect( endpointbpkcount ).to.be.at.most( 15 ) expect( endpointcpkcount ).to.be.within( 59, 75 ) await finished } ) } )