@babblevoice/projectrtp 2.5.35 → 2.5.38
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/.dockerignore +2 -0
- package/Dockerfile +5 -7
- package/Dockerfile.debian +42 -0
- package/README.md +18 -1
- package/asan.options +2 -0
- package/binding.gyp +30 -3
- package/index.js +6 -1
- package/jsconfig.json +1 -1
- package/lib/server.js +6 -4
- package/package.json +6 -6
- package/src/globals.h +3 -0
- package/src/projectrtpbuffer.cpp +3 -0
- package/src/projectrtpchannel.cpp +176 -181
- package/src/projectrtpchannel.h +5 -6
- package/src/projectrtpchannelmux.cpp +45 -46
- package/src/projectrtpchannelmux.h +2 -4
- package/src/projectrtpchannelrecorder.h +4 -4
- package/src/projectrtpcodecx.cpp +36 -56
- package/src/projectrtpnodemain.cpp +3 -0
- package/src/projectrtppacket.cpp +3 -0
- package/src/projectrtprawsound.cpp +30 -11
- package/src/projectrtprawsound.h +3 -1
- package/src/projectrtpsoundfile.cpp +260 -182
- package/src/projectrtpsoundfile.h +14 -3
- package/src/projectrtpsoundsoup.cpp +2 -0
- package/src/projectrtpstun.cpp +3 -0
- package/stress/echorecord.scenario.js +4 -1
- package/stress/index.js +4 -1
- package/stress/playbackrecordtoofast.scenario.js +106 -0
- package/stress/playbackthenmix.scenario.js +70 -0
- package/stress/utils.js +27 -3
- package/test/interface/projectrtpdtmf.js +41 -12
- package/test/interface/projectrtpmix.js +78 -0
- package/test/interface/projectrtprecord.js +1 -1
- package/test/interface/projectrtpsound.js +121 -0
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include <string>
|
|
7
7
|
|
|
8
8
|
#include <memory>
|
|
9
|
+
#include <vector>
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
#include <sys/types.h>
|
|
@@ -25,6 +26,8 @@
|
|
|
25
26
|
#define SOUNDFILENUMBUFFERS 8
|
|
26
27
|
#define MAXNUMBEROFCHANNELS 2
|
|
27
28
|
|
|
29
|
+
typedef std::vector< int16_t > soundbuffer;
|
|
30
|
+
|
|
28
31
|
typedef struct {
|
|
29
32
|
/* RIFF Header */
|
|
30
33
|
uint8_t riff_header[ 4 ]; /* Contains "RIFF" */
|
|
@@ -83,12 +86,14 @@ protected:
|
|
|
83
86
|
wavheader ourwavheader;
|
|
84
87
|
|
|
85
88
|
/* asynchronous variables */
|
|
86
|
-
|
|
89
|
+
u_int8_t currentcbindex;
|
|
87
90
|
aiocb cbwavheader;
|
|
88
91
|
aiocb cbwavblock[ SOUNDFILENUMBUFFERS ];
|
|
89
92
|
|
|
90
93
|
/* buffer for data */
|
|
91
|
-
|
|
94
|
+
soundbuffer buffer[ SOUNDFILENUMBUFFERS ];
|
|
95
|
+
|
|
96
|
+
std::atomic_bool filelock;
|
|
92
97
|
};
|
|
93
98
|
|
|
94
99
|
|
|
@@ -109,14 +114,20 @@ public:
|
|
|
109
114
|
bool complete( void );
|
|
110
115
|
|
|
111
116
|
void setposition( long mseconds );
|
|
117
|
+
|
|
112
118
|
long getposition( void );
|
|
113
119
|
|
|
114
120
|
inline bool isopen( void ) { return this->file != -1; }
|
|
115
121
|
|
|
116
122
|
private:
|
|
117
123
|
long offtomsecs( void );
|
|
124
|
+
void parseheader( void );
|
|
125
|
+
|
|
126
|
+
/* called by us - so we have a lock */
|
|
127
|
+
void realsetposition( long mseconds );
|
|
118
128
|
|
|
119
|
-
|
|
129
|
+
size_t bytecount;
|
|
130
|
+
size_t samplecount;
|
|
120
131
|
bool badheader;
|
|
121
132
|
bool headerread;
|
|
122
133
|
bool bodyread;
|
package/src/projectrtpstun.cpp
CHANGED
|
@@ -41,8 +41,11 @@ module.exports = async ( mstimeout ) => {
|
|
|
41
41
|
channela.echo()
|
|
42
42
|
|
|
43
43
|
await utils.waitbetween( 0, 500 )
|
|
44
|
+
/* include settings which terminate the recording before OR after the channel closes */
|
|
44
45
|
channela.record( {
|
|
45
|
-
"file": recording
|
|
46
|
+
"file": recording,
|
|
47
|
+
"maxduration": utils.waitbetween( 50, mstimeout * 2 ),
|
|
48
|
+
"finishbelowpower": 200
|
|
46
49
|
} )
|
|
47
50
|
|
|
48
51
|
}
|
package/stress/index.js
CHANGED
|
@@ -14,6 +14,8 @@ scenarios.push( require( "./echodualrecordpower.scenario.js" ) )
|
|
|
14
14
|
scenarios.push( require( "./echodualrecordpausestop.scenario.js" ) )
|
|
15
15
|
scenarios.push( require( "./mix2.scenario.js" ) )
|
|
16
16
|
scenarios.push( require( "./mixunmix.scenario.js" ) )
|
|
17
|
+
scenarios.push( require( "./playbackthenmix.scenario.js" ) )
|
|
18
|
+
scenarios.push( require( "./playbackrecordtoofast.scenario.js" ) )
|
|
17
19
|
|
|
18
20
|
/*
|
|
19
21
|
The purpose of this script is to load up projectrtp to expose any issues with timing.
|
|
@@ -23,7 +25,7 @@ folder is for.
|
|
|
23
25
|
We have a problem with "we should never get here - we have no more buffer available on port"
|
|
24
26
|
*/
|
|
25
27
|
|
|
26
|
-
const maxnumberofsessions =
|
|
28
|
+
const maxnumberofsessions = 1000
|
|
27
29
|
const secondsruntime = 3600*12
|
|
28
30
|
const minmscalllength = 50
|
|
29
31
|
const maxmscalllength = 1000 * 60 * 10 /* 1000 mS per second 60 seconds per minute , n minutes */
|
|
@@ -43,6 +45,7 @@ const run = async () => {
|
|
|
43
45
|
if ( "node" === process.argv.slice(2)[0] ) {
|
|
44
46
|
console.log( "Mode: node as listener" )
|
|
45
47
|
projectrtp.proxy.addnode( { host: "127.0.0.1", port: 9002 } )
|
|
48
|
+
// @ts-ignore
|
|
46
49
|
await node.listen( projectrtp, "127.0.0.1", 9002 )
|
|
47
50
|
} else console.log( "Mode: server as listener" )
|
|
48
51
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
const dgram = require( "dgram" )
|
|
4
|
+
|
|
5
|
+
const projectrtp = require( "../index.js" ).projectrtp
|
|
6
|
+
const fs = require( "fs" )
|
|
7
|
+
const utils = require( "./utils.js" )
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
js client (too fast) ---> channela (play/record)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function sendpk( sn, sendtime, dstport, server, data = undefined, pt = 0, ssrc = 25 ) {
|
|
14
|
+
|
|
15
|
+
const pklength = 172
|
|
16
|
+
|
|
17
|
+
return setTimeout( () => {
|
|
18
|
+
|
|
19
|
+
let payload
|
|
20
|
+
if( undefined !== data ) {
|
|
21
|
+
payload = data
|
|
22
|
+
} else {
|
|
23
|
+
payload = Buffer.alloc( pklength - 12 ).fill( projectrtp.codecx.linear162pcmu( sn ) & 0xff )
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const subheader = Buffer.alloc( 10 )
|
|
27
|
+
|
|
28
|
+
const ts = sn * 160
|
|
29
|
+
|
|
30
|
+
subheader.writeUInt16BE( ( sn + 100 ) % ( 2**16 ) /* just some offset */ )
|
|
31
|
+
subheader.writeUInt32BE( ts, 2 )
|
|
32
|
+
subheader.writeUInt32BE( ssrc, 6 )
|
|
33
|
+
|
|
34
|
+
const header = Buffer.from( [ 0x80, 0x00 ] )
|
|
35
|
+
header.writeUInt8( pt, 1 ) // payload type
|
|
36
|
+
|
|
37
|
+
const rtppacket = Buffer.concat( [
|
|
38
|
+
header,
|
|
39
|
+
subheader,
|
|
40
|
+
payload ] )
|
|
41
|
+
|
|
42
|
+
server.send( rtppacket, dstport, "localhost" )
|
|
43
|
+
}, sendtime * 17 )
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function createclient() {
|
|
47
|
+
const client = dgram.createSocket( "udp4" )
|
|
48
|
+
client.on( "message", function( msg ) {
|
|
49
|
+
} )
|
|
50
|
+
|
|
51
|
+
client.bind()
|
|
52
|
+
await new Promise( ( r ) => { client.on( "listening", function() { r() } ) } )
|
|
53
|
+
|
|
54
|
+
return client
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = async ( mstimeout ) => {
|
|
58
|
+
|
|
59
|
+
const acodec = utils.randcodec()
|
|
60
|
+
|
|
61
|
+
utils.log( `Starting playback with record for ${mstimeout} mS but sending packets too quickly` )
|
|
62
|
+
const recording = utils.mktempwav()
|
|
63
|
+
|
|
64
|
+
const clienta = await createclient()
|
|
65
|
+
const clientb = await createclient()
|
|
66
|
+
|
|
67
|
+
const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": clienta.address().port, "codec": acodec } }, async ( d ) => {
|
|
68
|
+
if( "close" === d.action ) {
|
|
69
|
+
utils.logclosechannel( `Playback with record for ${mstimeout} mS completed with reason '${d.reason}'`, d, mstimeout )
|
|
70
|
+
await fs.promises.unlink( recording ).catch( () => {} )
|
|
71
|
+
}
|
|
72
|
+
} )
|
|
73
|
+
|
|
74
|
+
const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": clientb.address().port, "codec": acodec } }, async ( d ) => {
|
|
75
|
+
if( "close" === d.action ) {
|
|
76
|
+
utils.logclosechannel( `Playback with record for ${mstimeout} mS completed with reason '${d.reason}'`, d, mstimeout )
|
|
77
|
+
await fs.promises.unlink( recording ).catch( () => {} )
|
|
78
|
+
}recording
|
|
79
|
+
} )
|
|
80
|
+
|
|
81
|
+
utils.lognewchannel()
|
|
82
|
+
utils.lognewchannel()
|
|
83
|
+
|
|
84
|
+
const pkcount = Math.floor( mstimeout / 20 )
|
|
85
|
+
for( let i = 0; i < pkcount; i++ ) {
|
|
86
|
+
sendpk( i, i, channela.local.port, clienta, )
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for( let i = 0; i < pkcount; i++ ) {
|
|
90
|
+
sendpk( i, i, channelb.local.port, clientb, )
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await utils.waitbetween( 0, 500 )
|
|
94
|
+
channela.mix( channelb )
|
|
95
|
+
|
|
96
|
+
await utils.waitbetween( 0, 500 )
|
|
97
|
+
channela.record( {
|
|
98
|
+
"file": recording,
|
|
99
|
+
"numchannels": utils.between( 0, 1 )
|
|
100
|
+
} )
|
|
101
|
+
|
|
102
|
+
await utils.wait( mstimeout )
|
|
103
|
+
|
|
104
|
+
clienta.close()
|
|
105
|
+
channela.close()
|
|
106
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
|
|
2
|
+
const projectrtp = require( "../index.js" ).projectrtp
|
|
3
|
+
const utils = require( "./utils.js" )
|
|
4
|
+
const expect = require( "chai" ).expect
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* clienta (echo) ---> channela (play/mix)
|
|
8
|
+
* @param { number } mstimeout
|
|
9
|
+
*/
|
|
10
|
+
module.exports = async ( mstimeout ) => {
|
|
11
|
+
|
|
12
|
+
const acodec = utils.randcodec()
|
|
13
|
+
const bcodec = utils.randcodec()
|
|
14
|
+
|
|
15
|
+
utils.log( `Starting playback then mix for ${mstimeout} mS` )
|
|
16
|
+
|
|
17
|
+
const clienta = await projectrtp.openchannel( {}, ( d ) => {
|
|
18
|
+
if( "close" === d.action ) {
|
|
19
|
+
channela.close()
|
|
20
|
+
utils.logclosechannel( `Mix 2 after play (clienta) for ${mstimeout} mS completed with reason '${d.reason}'`, d, mstimeout )
|
|
21
|
+
}
|
|
22
|
+
} )
|
|
23
|
+
utils.lognewchannel()
|
|
24
|
+
|
|
25
|
+
const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": clienta.local.port, "codec": acodec } }, async ( d ) => {
|
|
26
|
+
if( "play" === d.action && "end" === d.event && "channelmixing" === d.reason ) {
|
|
27
|
+
utils.log( "Channel stopped playback because channel mixing" )
|
|
28
|
+
} else if( "close" === d.action ) {
|
|
29
|
+
utils.logclosechannel( `Playback then mix for ${mstimeout} mS completed with reason '${d.reason}'`, d, mstimeout )
|
|
30
|
+
}
|
|
31
|
+
} )
|
|
32
|
+
|
|
33
|
+
clienta.remote( { "address": "localhost", "port": channela.local.port, "codec": acodec } )
|
|
34
|
+
clienta.echo()
|
|
35
|
+
utils.lognewchannel()
|
|
36
|
+
|
|
37
|
+
channela.play( {
|
|
38
|
+
"loop": true,
|
|
39
|
+
"files": [
|
|
40
|
+
{ "wav": "/tmp/ukringing.wav" }
|
|
41
|
+
]
|
|
42
|
+
} )
|
|
43
|
+
|
|
44
|
+
await utils.waitbetween( 100, 1000 )
|
|
45
|
+
|
|
46
|
+
const clientb = await projectrtp.openchannel( {}, ( d ) => {
|
|
47
|
+
if( "close" === d.action ) {
|
|
48
|
+
channelb.close()
|
|
49
|
+
utils.logclosechannel( `Mix 2 after play (clientb) for ${mstimeout} mS completed with reason '${d.reason}'`, d, mstimeout )
|
|
50
|
+
}
|
|
51
|
+
} )
|
|
52
|
+
utils.lognewchannel()
|
|
53
|
+
|
|
54
|
+
const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": clientb.local.port, "codec": bcodec } }, ( d ) => {
|
|
55
|
+
if( "close" === d.action ) {
|
|
56
|
+
utils.logclosechannel( `Mix 2 after play (channelb) for ${mstimeout} mS completed with reason '${d.reason}'`, d, mstimeout )
|
|
57
|
+
}
|
|
58
|
+
} )
|
|
59
|
+
clientb.remote( { "address": "localhost", "port": channelb.local.port, "codec": bcodec } )
|
|
60
|
+
utils.lognewchannel()
|
|
61
|
+
|
|
62
|
+
expect( await channela.mix( channelb ) ).to.be.true
|
|
63
|
+
expect( clienta.play( { "loop": true, "files": [ { "wav": "/tmp/ukringing.wav" } ] } ) ).to.be.true
|
|
64
|
+
expect( clientb.echo() ).to.be.true
|
|
65
|
+
|
|
66
|
+
await new Promise( ( r ) => { setTimeout( () => r(), Math.max( mstimeout, 110 ) ) } )
|
|
67
|
+
clienta.close()
|
|
68
|
+
clientb.close()
|
|
69
|
+
|
|
70
|
+
}
|
package/stress/utils.js
CHANGED
|
@@ -21,8 +21,8 @@ module.exports.logclosechannel = ( message, d, mstimeout ) => {
|
|
|
21
21
|
totalcount++
|
|
22
22
|
module.exports.log( message )
|
|
23
23
|
|
|
24
|
-
const score = ( d.stats.in["count"] / mstimeout * 20 )
|
|
25
|
-
let scoremsg = ` Score: ${ score }`
|
|
24
|
+
const score = ( d.stats.in["count"] / mstimeout * 20 )
|
|
25
|
+
let scoremsg = ` Score: ${ score.toFixed( 2 ) }`
|
|
26
26
|
|
|
27
27
|
// Colour based on score: red, yellow, green
|
|
28
28
|
if( 0.25 >= score ) scoremsg = "\x1B[31m" + scoremsg
|
|
@@ -50,17 +50,41 @@ module.exports.mktempwav = () => {
|
|
|
50
50
|
return "/tmp/project_" + (Math.random() + 1).toString(36).substring(7) + ".wav"
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
* @param { number } min - lower value
|
|
56
|
+
* @param { number } max - upper value
|
|
57
|
+
* @returns { number }
|
|
58
|
+
*/
|
|
53
59
|
module.exports.between = ( min, max ) => {
|
|
54
60
|
return Math.floor(
|
|
55
61
|
Math.random() * ( max - min ) + min
|
|
56
62
|
)
|
|
57
63
|
}
|
|
58
64
|
|
|
65
|
+
/**
|
|
66
|
+
*
|
|
67
|
+
* @param { number } min - min time mS
|
|
68
|
+
* @param { number } max - max time mS
|
|
69
|
+
* @returns { Promise }
|
|
70
|
+
*/
|
|
59
71
|
module.exports.waitbetween = async ( min, max ) => {
|
|
60
|
-
|
|
72
|
+
await new Promise( ( resolve ) => { setTimeout( () => resolve(), module.exports.between( min, max ) ) } )
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param { number } ms
|
|
78
|
+
*/
|
|
79
|
+
module.exports.wait = async ( ms ) => {
|
|
80
|
+
await new Promise( ( resolve ) => { setTimeout( () => resolve(), ms ) } )
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
const possiblecodecs = [ 0, 8, 9, 97 ]
|
|
84
|
+
/**
|
|
85
|
+
* Returns random supported CODEC
|
|
86
|
+
* @returns { number }
|
|
87
|
+
*/
|
|
64
88
|
module.exports.randcodec = () => {
|
|
65
89
|
return possiblecodecs[ module.exports.between( 0, possiblecodecs.length ) ]
|
|
66
90
|
}
|
|
@@ -38,6 +38,26 @@ function parsepk( packet ) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Limitation of not parsing ccrc.
|
|
43
|
+
* @param { Buffer } packet
|
|
44
|
+
* @return { object }
|
|
45
|
+
*/
|
|
46
|
+
function parse2833pk( packet ) {
|
|
47
|
+
return {
|
|
48
|
+
sn: packet.readUInt16BE( 2 ),
|
|
49
|
+
ts: packet.readUInt32BE( 4 ),
|
|
50
|
+
pt: packet.readUInt8( 1 ) & 0x7f,
|
|
51
|
+
ssrc: packet.readUInt32BE( 8 ),
|
|
52
|
+
event: {
|
|
53
|
+
id: packet.readUInt8( 12 ),
|
|
54
|
+
eoe: ( packet.readUInt8( 13 ) & 0x80 ) == 0x80,
|
|
55
|
+
volume: packet.readUInt8( 13 ) & 0x7f,
|
|
56
|
+
duration: packet.readUInt16BE( 14 ),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
/* helper functions */
|
|
42
62
|
function sendpk( sn, ts, sendtime, dstport, server, pt = 0, ssrc ) {
|
|
43
63
|
|
|
@@ -182,7 +202,7 @@ describe( "dtmf", function() {
|
|
|
182
202
|
|
|
183
203
|
if( "close" === d.action ) {
|
|
184
204
|
server.close()
|
|
185
|
-
expect( dtmfpkcount ).to.equal( 2*
|
|
205
|
+
expect( dtmfpkcount ).to.equal( 2*7 )
|
|
186
206
|
done()
|
|
187
207
|
}
|
|
188
208
|
} )
|
|
@@ -206,10 +226,10 @@ describe( "dtmf", function() {
|
|
|
206
226
|
const clienta = dgram.createSocket( "udp4" )
|
|
207
227
|
const clientb = dgram.createSocket( "udp4" )
|
|
208
228
|
|
|
209
|
-
|
|
229
|
+
const dtmfpks = []
|
|
210
230
|
clienta.on( "message", function( msg ) {
|
|
211
231
|
if( 101 == ( 0x7f & msg [ 1 ] ) ) {
|
|
212
|
-
|
|
232
|
+
dtmfpks.push( parse2833pk( msg ) )
|
|
213
233
|
} else {
|
|
214
234
|
expect( msg.length ).to.equal( 172 )
|
|
215
235
|
expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
|
|
@@ -219,7 +239,6 @@ describe( "dtmf", function() {
|
|
|
219
239
|
clientb.on( "message", function( msg ) {
|
|
220
240
|
if( 101 == ( 0x7f & msg [ 1 ] ) ) {
|
|
221
241
|
expect( true ).to.equal( false ) //here = bad
|
|
222
|
-
dtmfpkcount++
|
|
223
242
|
}
|
|
224
243
|
clientb.send( msg, channelb.local.port, "localhost" )
|
|
225
244
|
} )
|
|
@@ -263,7 +282,19 @@ describe( "dtmf", function() {
|
|
|
263
282
|
clienta.close()
|
|
264
283
|
clientb.close()
|
|
265
284
|
|
|
266
|
-
expect(
|
|
285
|
+
expect( dtmfpks.length ).to.equal( 3*7 )
|
|
286
|
+
expect( dtmfpks[ 0 ].event.id ).to.equal( 10 )
|
|
287
|
+
expect( dtmfpks[ 0 ].event.duration ).to.equal( 160 )
|
|
288
|
+
expect( dtmfpks[ 0 ].event.eoe ).to.be.false
|
|
289
|
+
|
|
290
|
+
expect( dtmfpks[ 4 ].event.id ).to.equal( 10 )
|
|
291
|
+
expect( dtmfpks[ 4 ].event.duration ).to.equal( 800 )
|
|
292
|
+
expect( dtmfpks[ 4 ].event.eoe ).to.be.true
|
|
293
|
+
|
|
294
|
+
expect( dtmfpks[ 6 ].event.id ).to.equal( 10 )
|
|
295
|
+
expect( dtmfpks[ 6 ].event.duration ).to.equal( 800 )
|
|
296
|
+
expect( dtmfpks[ 6 ].event.eoe ).to.be.true
|
|
297
|
+
|
|
267
298
|
} )
|
|
268
299
|
|
|
269
300
|
it( "2 channels mixing and request rtp server to send 2833 to one with dynamic payloadtype", async function() {
|
|
@@ -331,7 +362,7 @@ describe( "dtmf", function() {
|
|
|
331
362
|
clienta.close()
|
|
332
363
|
clientb.close()
|
|
333
364
|
|
|
334
|
-
expect( dtmfpkcount ).to.equal( 3*
|
|
365
|
+
expect( dtmfpkcount ).to.equal( 3*7 )
|
|
335
366
|
} )
|
|
336
367
|
|
|
337
368
|
it( "3 channels mixing and request rtp server to send 2833 to one", async function() {
|
|
@@ -341,11 +372,11 @@ describe( "dtmf", function() {
|
|
|
341
372
|
const clientb = dgram.createSocket( "udp4" )
|
|
342
373
|
const clientc = dgram.createSocket( "udp4" )
|
|
343
374
|
|
|
344
|
-
let
|
|
375
|
+
let dtmfpks = []
|
|
345
376
|
clienta.on( "message", function( msg ) {
|
|
346
377
|
const pk = parsepk( msg )
|
|
347
378
|
if( 101 == pk.pt ) {
|
|
348
|
-
|
|
379
|
+
dtmfpks.push( parse2833pk( msg ) )
|
|
349
380
|
} else {
|
|
350
381
|
expect( msg.length ).to.equal( 172 )
|
|
351
382
|
expect( pk.pt ).to.equal( 0 )
|
|
@@ -355,7 +386,6 @@ describe( "dtmf", function() {
|
|
|
355
386
|
clientb.on( "message", function( msg ) {
|
|
356
387
|
if( 101 == ( 0x7f & msg [ 1 ] ) ) {
|
|
357
388
|
expect( true ).to.equal( false ) //here = bad
|
|
358
|
-
dtmfpkcount++
|
|
359
389
|
}
|
|
360
390
|
clientb.send( msg, channelb.local.port, "localhost" )
|
|
361
391
|
} )
|
|
@@ -363,7 +393,6 @@ describe( "dtmf", function() {
|
|
|
363
393
|
clientc.on( "message", function( msg ) {
|
|
364
394
|
if( 101 == ( 0x7f & msg [ 1 ] ) ) {
|
|
365
395
|
expect( true ).to.equal( false ) //here = bad
|
|
366
|
-
dtmfpkcount++
|
|
367
396
|
}
|
|
368
397
|
clientb.send( msg, channelb.local.port, "localhost" )
|
|
369
398
|
} )
|
|
@@ -407,14 +436,14 @@ describe( "dtmf", function() {
|
|
|
407
436
|
|
|
408
437
|
await new Promise( ( resolve ) => { setTimeout( () => resolve(), 100 ) } )
|
|
409
438
|
channela.dtmf( "*9ABD" )
|
|
410
|
-
await new Promise( ( resolve ) => { setTimeout( () => resolve(),
|
|
439
|
+
await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } )
|
|
411
440
|
channela.close()
|
|
412
441
|
|
|
413
442
|
clienta.close()
|
|
414
443
|
clientb.close()
|
|
415
444
|
clientc.close()
|
|
416
445
|
|
|
417
|
-
expect(
|
|
446
|
+
expect( dtmfpks.length ).to.equal( 5*7 )
|
|
418
447
|
|
|
419
448
|
await finished
|
|
420
449
|
} )
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const expect = require( "chai" ).expect
|
|
5
5
|
const dgram = require( "dgram" )
|
|
6
6
|
const projectrtp = require( "../../index.js" ).projectrtp
|
|
7
|
+
const fs = require( "node:fs" ).promises
|
|
7
8
|
|
|
8
9
|
function sendpk( sn, sendtime, dstport, server, data = undefined, pt = 0, ssrc = 25 ) {
|
|
9
10
|
|
|
@@ -41,6 +42,14 @@ function sendpk( sn, sendtime, dstport, server, data = undefined, pt = 0, ssrc =
|
|
|
41
42
|
|
|
42
43
|
describe( "channel mix", function() {
|
|
43
44
|
|
|
45
|
+
this.beforeAll( () => {
|
|
46
|
+
projectrtp.tone.generate( "400+450*0.5/0/400+450*0.5/0:400/200/400/2000", "/tmp/ukringing.wav" )
|
|
47
|
+
} )
|
|
48
|
+
|
|
49
|
+
this.afterAll( async () => {
|
|
50
|
+
await fs.unlink( "/tmp/ukringing.wav" )
|
|
51
|
+
} )
|
|
52
|
+
|
|
44
53
|
it( "basic mix 2 channels", async function() {
|
|
45
54
|
|
|
46
55
|
this.timeout( 3000 )
|
|
@@ -661,6 +670,75 @@ describe( "channel mix", function() {
|
|
|
661
670
|
|
|
662
671
|
} )
|
|
663
672
|
|
|
673
|
+
|
|
674
|
+
it( "playback prompt then mix 2 channels - pcmu <-> g722 with recording", async function() {
|
|
675
|
+
|
|
676
|
+
this.timeout( 3000 )
|
|
677
|
+
this.slow( 2000 )
|
|
678
|
+
|
|
679
|
+
const endpointa = dgram.createSocket( "udp4" )
|
|
680
|
+
const endpointb = dgram.createSocket( "udp4" )
|
|
681
|
+
|
|
682
|
+
let endpointapkcount = 0
|
|
683
|
+
let endpointbpkcount = 0
|
|
684
|
+
|
|
685
|
+
endpointa.on( "message", function( msg ) {
|
|
686
|
+
endpointapkcount++
|
|
687
|
+
expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
|
|
688
|
+
} )
|
|
689
|
+
|
|
690
|
+
endpointb.on( "message", function( msg ) {
|
|
691
|
+
|
|
692
|
+
endpointbpkcount++
|
|
693
|
+
expect( 0x7f & msg [ 1 ] ).to.equal( 9 )
|
|
694
|
+
endpointb.send( msg, channelb.local.port, "localhost" )
|
|
695
|
+
} )
|
|
696
|
+
|
|
697
|
+
endpointa.bind()
|
|
698
|
+
await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } )
|
|
699
|
+
|
|
700
|
+
endpointb.bind()
|
|
701
|
+
await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } )
|
|
702
|
+
|
|
703
|
+
let done
|
|
704
|
+
const finished = new Promise( ( r ) => { done = r } )
|
|
705
|
+
|
|
706
|
+
const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) {
|
|
707
|
+
if( "close" === d.action ) channelb.close()
|
|
708
|
+
} )
|
|
709
|
+
|
|
710
|
+
channela.play( { "loop": true, "files": [
|
|
711
|
+
{ "wav": "/tmp/ukringing.wav" } ] } )
|
|
712
|
+
|
|
713
|
+
await new Promise( resolve => setTimeout( resolve, 500 ) )
|
|
714
|
+
|
|
715
|
+
const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 9 } }, function( d ) {
|
|
716
|
+
if( "close" === d.action ) done()
|
|
717
|
+
} )
|
|
718
|
+
|
|
719
|
+
/* mix */
|
|
720
|
+
expect( channela.mix( channelb ) ).to.be.true
|
|
721
|
+
|
|
722
|
+
channela.record( { "file": "/tmp/g722mix2recording.wav" } )
|
|
723
|
+
|
|
724
|
+
/* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */
|
|
725
|
+
for( let i = 0; 50 > i; i ++ ) {
|
|
726
|
+
sendpk( i, i, channela.local.port, endpointa )
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } )
|
|
730
|
+
|
|
731
|
+
channela.close()
|
|
732
|
+
endpointa.close()
|
|
733
|
+
endpointb.close()
|
|
734
|
+
|
|
735
|
+
expect( endpointapkcount ).to.be.above( 30 )
|
|
736
|
+
expect( endpointbpkcount ).to.be.above( 30 )
|
|
737
|
+
|
|
738
|
+
await finished
|
|
739
|
+
|
|
740
|
+
} )
|
|
741
|
+
|
|
664
742
|
it( "mix 3 channels - 1 writer 3 readers", async function() {
|
|
665
743
|
|
|
666
744
|
this.timeout( 3000 )
|
|
@@ -470,7 +470,7 @@ describe( "record", function() {
|
|
|
470
470
|
server.close()
|
|
471
471
|
|
|
472
472
|
let stats = fs.statSync( "/tmp/dualrecordingpower.wav" )
|
|
473
|
-
expect( stats.size ).to.be.within( 30000 ,
|
|
473
|
+
expect( stats.size ).to.be.within( 30000 , 41000 )
|
|
474
474
|
|
|
475
475
|
stats = fs.statSync( "/tmp/dualrecording.wav" )
|
|
476
476
|
expect( stats.size ).to.be.within( 110000, 190000 )
|