@babblevoice/projectrtp 2.5.20 → 2.5.25
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 +9 -1
- package/package.json +1 -1
- package/src/globals.h +3 -0
- package/src/projectrtpchannel.cpp +16 -2
- package/src/projectrtpchannelmux.cpp +17 -7
- package/src/projectrtpchannelmux.h +4 -2
- package/src/projectrtpcodecx.cpp +76 -93
- package/src/projectrtpcodecx.h +1 -3
- package/src/projectrtprawsound.cpp +34 -70
- package/src/projectrtpsoundfile.cpp +62 -52
- package/src/projectrtpsoundfile.h +1 -0
- package/test/interface/pcap.js +2 -2
- package/test/interface/pcaps/440hzinbackgroundg722.pcap +0 -0
- package/test/interface/projectrtpchannel.js +4 -9
- package/test/interface/projectrtpdtmf.js +147 -6
- package/test/interface/projectrtpmix.js +77 -6
- package/test/interface/projectrtpsound.js +145 -91
- package/test/interface/transcode.js +164 -0
- package/test/util/rtp.js +17 -0
|
@@ -5,9 +5,8 @@ const expect = require( "chai" ).expect
|
|
|
5
5
|
const dgram = require( "dgram" )
|
|
6
6
|
const projectrtp = require( "../../index.js" ).projectrtp
|
|
7
7
|
|
|
8
|
-
function sendpk( sn, sendtime, dstport, server, data = undefined ) {
|
|
8
|
+
function sendpk( sn, sendtime, dstport, server, data = undefined, pt = 0, ssrc = 25 ) {
|
|
9
9
|
|
|
10
|
-
const ssrc = 25
|
|
11
10
|
const pklength = 172
|
|
12
11
|
|
|
13
12
|
return setTimeout( () => {
|
|
@@ -27,8 +26,11 @@ function sendpk( sn, sendtime, dstport, server, data = undefined ) {
|
|
|
27
26
|
subheader.writeUInt32BE( ts, 2 )
|
|
28
27
|
subheader.writeUInt32BE( ssrc, 6 )
|
|
29
28
|
|
|
29
|
+
const header = Buffer.from( [ 0x80, 0x00 ] )
|
|
30
|
+
header.writeUInt8( pt, 1 ) // payload type
|
|
31
|
+
|
|
30
32
|
const rtppacket = Buffer.concat( [
|
|
31
|
-
|
|
33
|
+
header,
|
|
32
34
|
subheader,
|
|
33
35
|
payload ] )
|
|
34
36
|
|
|
@@ -101,6 +103,75 @@ describe( "channel mix", function() {
|
|
|
101
103
|
|
|
102
104
|
} )
|
|
103
105
|
|
|
106
|
+
it( "basic mix 2 channels with start 2 packets wrong payload type", async function() {
|
|
107
|
+
|
|
108
|
+
this.timeout( 3000 )
|
|
109
|
+
this.slow( 2000 )
|
|
110
|
+
|
|
111
|
+
const endpointa = dgram.createSocket( "udp4" )
|
|
112
|
+
const endpointb = dgram.createSocket( "udp4" )
|
|
113
|
+
|
|
114
|
+
let endpointapkcount = 0
|
|
115
|
+
let endpointbpkcount = 0
|
|
116
|
+
|
|
117
|
+
endpointa.on( "message", function( msg ) {
|
|
118
|
+
endpointapkcount++
|
|
119
|
+
expect( msg.length ).to.equal( 172 )
|
|
120
|
+
expect( 0x7f & msg [ 1 ] ).to.equal( 8 )
|
|
121
|
+
} )
|
|
122
|
+
|
|
123
|
+
endpointb.on( "message", function( msg ) {
|
|
124
|
+
endpointbpkcount++
|
|
125
|
+
|
|
126
|
+
expect( msg.length ).to.equal( 172 )
|
|
127
|
+
expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
|
|
128
|
+
endpointb.send( msg, channelb.local.port, "localhost" )
|
|
129
|
+
} )
|
|
130
|
+
|
|
131
|
+
endpointa.bind()
|
|
132
|
+
await new Promise( ( r ) => { endpointa.on( "listening", function() { r() } ) } )
|
|
133
|
+
|
|
134
|
+
endpointb.bind()
|
|
135
|
+
await new Promise( ( r ) => { endpointb.on( "listening", function() { r() } ) } )
|
|
136
|
+
|
|
137
|
+
let done
|
|
138
|
+
const finished = new Promise( ( r ) => { done = r } )
|
|
139
|
+
|
|
140
|
+
const channela = await projectrtp.openchannel( {}, function( d ) {
|
|
141
|
+
if( "close" === d.action ) channelb.close()
|
|
142
|
+
} )
|
|
143
|
+
|
|
144
|
+
const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) {
|
|
145
|
+
if( "close" === d.action ) done()
|
|
146
|
+
} )
|
|
147
|
+
|
|
148
|
+
/* mix */
|
|
149
|
+
expect( channela.mix( channelb ) ).to.be.true
|
|
150
|
+
|
|
151
|
+
/* a problem was highlighted that would remote would be called before rtp stream was updated */
|
|
152
|
+
channela.remote( { "address": "localhost", "port": endpointa.address().port, "codec": 8 } )
|
|
153
|
+
|
|
154
|
+
sendpk( 0, 0, channela.local.port, endpointa )
|
|
155
|
+
sendpk( 1, 1, channela.local.port, endpointa )
|
|
156
|
+
|
|
157
|
+
/* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */
|
|
158
|
+
for( let i = 2; 50 > i; i ++ ) {
|
|
159
|
+
sendpk( i, i, channela.local.port, endpointa, undefined, 8, 27 )
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1300 ) } )
|
|
163
|
+
|
|
164
|
+
channela.close()
|
|
165
|
+
endpointa.close()
|
|
166
|
+
endpointb.close()
|
|
167
|
+
|
|
168
|
+
expect( endpointapkcount ).to.be.within( 30, 51 )
|
|
169
|
+
expect( endpointbpkcount ).to.be.within( 30, 51 )
|
|
170
|
+
|
|
171
|
+
await finished
|
|
172
|
+
|
|
173
|
+
} )
|
|
174
|
+
|
|
104
175
|
|
|
105
176
|
it( "mix 2 channels then unmix", async function() {
|
|
106
177
|
|
|
@@ -900,9 +971,9 @@ describe( "channel mix", function() {
|
|
|
900
971
|
endpointb.close()
|
|
901
972
|
endpointc.close()
|
|
902
973
|
|
|
903
|
-
expect( endpointapkcountzero ).to.be.within(
|
|
904
|
-
expect( endpointbpkcountzero ).to.be.within(
|
|
905
|
-
expect( endpointcpkcountzero ).to.be.within(
|
|
974
|
+
expect( endpointapkcountzero ).to.be.within( 60, 75 )
|
|
975
|
+
expect( endpointbpkcountzero ).to.be.within( 60, 75 )
|
|
976
|
+
expect( endpointcpkcountzero ).to.be.within( 60, 75 )
|
|
906
977
|
expect( endpointapkcountnotzero ).to.be.within( 4, 12 )
|
|
907
978
|
expect( endpointbpkcountnotzero ).to.be.within( 4, 12 )
|
|
908
979
|
expect( endpointcpkcountnotzero ).to.be.below( 2 )
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
|
|
2
|
+
|
|
3
|
+
//const npl = require( "nodeplotlib" )
|
|
4
|
+
// eslint-disable-next-line no-unused-vars
|
|
5
|
+
const npl = { plot: ( /** @type {any} */ a ) => {} }
|
|
6
|
+
|
|
2
7
|
/*
|
|
3
8
|
Note, timing in Node doesn't appear that accurate. This requires more work if
|
|
4
9
|
we can measure jitter. The pcap traces show 0.01 mS jitter but we are getting 4mS
|
|
@@ -8,7 +13,48 @@ when playing in node. For now, leave checking of timing.
|
|
|
8
13
|
const expect = require( "chai" ).expect
|
|
9
14
|
const fs = require( "fs" )
|
|
10
15
|
const dgram = require( "dgram" )
|
|
11
|
-
const prtp = require( "../../index
|
|
16
|
+
const prtp = require( "../../index" )
|
|
17
|
+
const rtputils = require( "../util/rtp" )
|
|
18
|
+
|
|
19
|
+
function gensignal( hz, datalength, magnitude ) {
|
|
20
|
+
|
|
21
|
+
const y = Buffer.alloc( datalength * 2 )
|
|
22
|
+
|
|
23
|
+
for( let i = 0; i < datalength; i ++ ) {
|
|
24
|
+
y.writeInt16LE( Math.sin( i * ( Math.PI * 2 * ( 1 / 8000 ) ) * hz ) * magnitude, i * 2 )
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return y
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @param { Array< number > } inarray
|
|
33
|
+
* @returns { Int16Array }
|
|
34
|
+
*/
|
|
35
|
+
function pcmutolinear( inarray ) {
|
|
36
|
+
const out = new Int16Array( inarray.length )
|
|
37
|
+
for( let i = 0; i < inarray.length; i++ ) {
|
|
38
|
+
out[ i ] = prtp.projectrtp.codecx.pcmu2linear16( inarray[ i ] )
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return out
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Limitation of not parsing ccrc.
|
|
46
|
+
* @param { Buffer } packet
|
|
47
|
+
* @return { object }
|
|
48
|
+
*/
|
|
49
|
+
function parsertppk( packet ) {
|
|
50
|
+
return {
|
|
51
|
+
sn: packet.readUInt16BE( 2 ),
|
|
52
|
+
ts: packet.readUInt32BE( 4 ),
|
|
53
|
+
pt: packet.readUInt8( 1 ) & 0x7f,
|
|
54
|
+
ssrc: packet.readUInt32BE( 8 ),
|
|
55
|
+
payload: new Uint8Array( packet.slice( 12 ) )
|
|
56
|
+
}
|
|
57
|
+
}
|
|
12
58
|
|
|
13
59
|
/**
|
|
14
60
|
* @param { number } samples
|
|
@@ -75,13 +121,16 @@ describe( "rtpsound", function() {
|
|
|
75
121
|
/* create our RTP/UDP endpoint */
|
|
76
122
|
const server = dgram.createSocket( "udp4" )
|
|
77
123
|
let receviedpkcount = 0
|
|
124
|
+
let receivedcorrectvalue = 0
|
|
78
125
|
|
|
79
126
|
/** @type { prtp.channel } */
|
|
80
127
|
let channel
|
|
81
128
|
|
|
82
129
|
server.on( "message", function( msg ) {
|
|
83
130
|
/* This is PCMA encoded data from our flat file */
|
|
84
|
-
|
|
131
|
+
const pk = parsertppk( msg )
|
|
132
|
+
/* we can also receive silence */
|
|
133
|
+
if( 228 == pk.payload[ 10 ] ) receivedcorrectvalue++
|
|
85
134
|
receviedpkcount++
|
|
86
135
|
} )
|
|
87
136
|
|
|
@@ -114,10 +163,11 @@ describe( "rtpsound", function() {
|
|
|
114
163
|
|
|
115
164
|
await finished
|
|
116
165
|
|
|
117
|
-
expect( receviedpkcount ).to.be.within( 48,
|
|
166
|
+
expect( receviedpkcount ).to.be.within( 48, 60 )
|
|
167
|
+
expect( receivedcorrectvalue ).to.be.within( 48, 60 )
|
|
118
168
|
} )
|
|
119
169
|
|
|
120
|
-
it( "loop in soundsoup and check udp data", function(
|
|
170
|
+
it( "loop in soundsoup and check udp data", async function() {
|
|
121
171
|
|
|
122
172
|
this.timeout( 6000 )
|
|
123
173
|
this.slow( 5000 )
|
|
@@ -125,45 +175,52 @@ describe( "rtpsound", function() {
|
|
|
125
175
|
/* create our RTP/UDP endpoint */
|
|
126
176
|
const server = dgram.createSocket( "udp4" )
|
|
127
177
|
let receviedpkcount = 0
|
|
128
|
-
let
|
|
178
|
+
let correctvalcount = 0
|
|
179
|
+
let done, cleanedup
|
|
180
|
+
const completpromise = new Promise( resolve => done = resolve )
|
|
181
|
+
const cleanedpromise = new Promise( resolve => cleanedup = resolve )
|
|
129
182
|
|
|
130
183
|
server.on( "message", function( msg ) {
|
|
131
184
|
|
|
132
185
|
/* This is PCMA encoded data from our flat file */
|
|
133
|
-
|
|
186
|
+
if( 228 == msg[ 16 ] ) correctvalcount++
|
|
134
187
|
receviedpkcount++
|
|
135
188
|
/*
|
|
136
189
|
flat.wav has 1 S of audio (8000 samples). 160 per packet compressed = 320 PCM.
|
|
137
190
|
200 packets is 64000 samples so this must have looped to work.
|
|
138
191
|
*/
|
|
139
192
|
if( 220 < receviedpkcount ) {
|
|
140
|
-
|
|
193
|
+
done()
|
|
141
194
|
}
|
|
142
195
|
} )
|
|
143
196
|
|
|
144
197
|
server.bind()
|
|
145
|
-
server.on( "listening",
|
|
198
|
+
await new Promise( resolve => server.on( "listening", resolve ) )
|
|
146
199
|
|
|
147
|
-
|
|
200
|
+
const ourport = server.address().port
|
|
148
201
|
|
|
149
|
-
|
|
202
|
+
const channel = await prtp.projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
|
|
203
|
+
if( "close" == d.action ) cleanedup()
|
|
204
|
+
} )
|
|
150
205
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
206
|
+
expect( channel.play( {
|
|
207
|
+
"loop": true,
|
|
208
|
+
"files": [
|
|
209
|
+
{ "wav": "/tmp/flat.wav" }
|
|
210
|
+
]
|
|
211
|
+
} ) ).to.be.true
|
|
156
212
|
|
|
157
|
-
|
|
158
|
-
"loop": true,
|
|
159
|
-
"files": [
|
|
160
|
-
{ "wav": "/tmp/flat.wav" }
|
|
161
|
-
]
|
|
162
|
-
} ) ).to.be.true
|
|
213
|
+
await completpromise
|
|
163
214
|
|
|
164
|
-
|
|
215
|
+
channel.close()
|
|
216
|
+
server.close()
|
|
217
|
+
|
|
218
|
+
await cleanedpromise
|
|
219
|
+
expect( correctvalcount ).to.be.above( 210 )
|
|
165
220
|
} )
|
|
166
221
|
|
|
222
|
+
/* I have also used this test to play with file codec conversion to check it is working properly */
|
|
223
|
+
|
|
167
224
|
it( "loop in soundsoup file and check udp data", function( done ) {
|
|
168
225
|
|
|
169
226
|
this.timeout( 6000 )
|
|
@@ -174,10 +231,12 @@ describe( "rtpsound", function() {
|
|
|
174
231
|
let receviedpkcount = 0
|
|
175
232
|
let channel
|
|
176
233
|
|
|
234
|
+
let receivedpcmu = []
|
|
177
235
|
server.on( "message", function( msg ) {
|
|
236
|
+
receivedpcmu = [ ...receivedpcmu, ...Array.from( pcmutolinear( rtputils.parsepk( msg ).payload ) ) ]
|
|
178
237
|
|
|
179
238
|
/* This is PCMA encoded data from our flat file */
|
|
180
|
-
expect( msg[ 16 ] ).to.equal( 0x99 )
|
|
239
|
+
//expect( msg[ 16 ] ).to.equal( 0x99 )
|
|
181
240
|
|
|
182
241
|
receviedpkcount++
|
|
183
242
|
/*
|
|
@@ -199,13 +258,20 @@ describe( "rtpsound", function() {
|
|
|
199
258
|
|
|
200
259
|
if( "close" === d.action ) {
|
|
201
260
|
server.close()
|
|
261
|
+
|
|
262
|
+
npl.plot( [ {
|
|
263
|
+
y: receivedpcmu,
|
|
264
|
+
type: "scatter"
|
|
265
|
+
} ] )
|
|
266
|
+
|
|
267
|
+
|
|
202
268
|
done()
|
|
203
269
|
}
|
|
204
270
|
} )
|
|
205
271
|
|
|
206
272
|
expect( channel.play( {
|
|
207
273
|
"files": [
|
|
208
|
-
{ "loop": true, "wav": "/tmp/
|
|
274
|
+
{ "loop": true, "wav": "/tmp/440sine.wav" }
|
|
209
275
|
]
|
|
210
276
|
} ) ).to.be.true
|
|
211
277
|
|
|
@@ -220,6 +286,7 @@ describe( "rtpsound", function() {
|
|
|
220
286
|
/* create our RTP/UDP endpoint */
|
|
221
287
|
const server = dgram.createSocket( "udp4" )
|
|
222
288
|
let receviedpkcount = 0
|
|
289
|
+
let receviedgoodcount = 0
|
|
223
290
|
|
|
224
291
|
/** @type { prtp.channel } */
|
|
225
292
|
let channel
|
|
@@ -232,8 +299,7 @@ describe( "rtpsound", function() {
|
|
|
232
299
|
receviedpkcount++
|
|
233
300
|
|
|
234
301
|
/* This is PCMA encoded data from our flat file */
|
|
235
|
-
|
|
236
|
-
expect( msg[ 150 ] ).to.equal( 153 /* 0x99 */ )
|
|
302
|
+
if( 228 == msg[ 17 ] ) receviedgoodcount++
|
|
237
303
|
} )
|
|
238
304
|
|
|
239
305
|
server.bind()
|
|
@@ -258,7 +324,8 @@ describe( "rtpsound", function() {
|
|
|
258
324
|
8000 samples looped twice. 100 packets (50 packets/S).
|
|
259
325
|
*/
|
|
260
326
|
|
|
261
|
-
expect( receviedpkcount ).to.
|
|
327
|
+
expect( receviedpkcount ).to.above( 90 )
|
|
328
|
+
expect( receviedgoodcount ).to.be.above( 90 )
|
|
262
329
|
} )
|
|
263
330
|
|
|
264
331
|
it( "slightly more complex soundsoup file and check udp data", async function() {
|
|
@@ -270,73 +337,61 @@ describe( "rtpsound", function() {
|
|
|
270
337
|
const server = dgram.createSocket( "udp4" )
|
|
271
338
|
let receviedpkcount = 0
|
|
272
339
|
|
|
273
|
-
|
|
274
|
-
|
|
340
|
+
let enoughpackets
|
|
341
|
+
const enoughpacketspromise = new Promise( resolve => enoughpackets = resolve )
|
|
275
342
|
|
|
276
343
|
let done
|
|
277
344
|
const finished = new Promise( ( r ) => { done = r } )
|
|
278
345
|
|
|
346
|
+
// eslint-disable-next-line complexity
|
|
279
347
|
server.on( "message", function( msg ) {
|
|
280
348
|
|
|
281
349
|
receviedpkcount++
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
expect( msg[ 150 ] ).to.equal( 153 /* 0x99 */ )
|
|
295
|
-
} else if( 125 == receviedpkcount ) {
|
|
296
|
-
/* flat2.wav */
|
|
297
|
-
expect( msg[ 17 ] ).to.equal( 3 )
|
|
298
|
-
expect( msg[ 150 ] ).to.equal( 3 )
|
|
299
|
-
} else if( 175 == receviedpkcount ) {
|
|
300
|
-
/* flat.wav */
|
|
301
|
-
expect( msg[ 17 ] ).to.equal( 153 /* 0x99 */ )
|
|
302
|
-
expect( msg[ 150 ] ).to.equal( 153 /* 0x99 */ )
|
|
303
|
-
} else if( 250 == receviedpkcount ) {
|
|
304
|
-
/* flat.wav */
|
|
305
|
-
expect( msg[ 17 ] ).to.equal( 153 )
|
|
306
|
-
expect( msg[ 150 ] ).to.equal( 153 )
|
|
307
|
-
} else if( 325 == receviedpkcount ) {
|
|
350
|
+
const secondouterloopoffset = 205
|
|
351
|
+
|
|
352
|
+
/* This is PCMA encoded data from our soundsoup */
|
|
353
|
+
if( 25 == receviedpkcount || ( 25 + secondouterloopoffset ) == receviedpkcount ) {
|
|
354
|
+
/* flat.wav first loop */
|
|
355
|
+
expect( msg[ 17 ] ).to.equal( 228 )
|
|
356
|
+
expect( msg[ 150 ] ).to.equal( 228 )
|
|
357
|
+
} else if( 75 == receviedpkcount || ( 75 + secondouterloopoffset ) == receviedpkcount ) {
|
|
358
|
+
/* flat.wav second loop */
|
|
359
|
+
expect( msg[ 17 ] ).to.equal( 228 )
|
|
360
|
+
expect( msg[ 150 ] ).to.equal( 228 )
|
|
361
|
+
} else if( 125 == receviedpkcount || ( 125 + secondouterloopoffset ) == receviedpkcount ) {
|
|
308
362
|
/* flat2.wav */
|
|
309
|
-
expect( msg[ 17 ] ).to.equal(
|
|
310
|
-
expect( msg[ 150 ] ).to.equal(
|
|
311
|
-
} else if(
|
|
363
|
+
expect( msg[ 17 ] ).to.equal( 223 )
|
|
364
|
+
expect( msg[ 150 ] ).to.equal( 223 )
|
|
365
|
+
} else if( 175 == receviedpkcount || ( 175 + secondouterloopoffset ) == receviedpkcount ) {
|
|
312
366
|
/* flat.wav */
|
|
313
|
-
expect( msg[ 17 ] ).to.equal(
|
|
314
|
-
expect( msg[ 150 ] ).to.equal(
|
|
315
|
-
} else if(
|
|
367
|
+
expect( msg[ 17 ] ).to.equal( 228 )
|
|
368
|
+
expect( msg[ 150 ] ).to.equal( 228 )
|
|
369
|
+
} else if( 206 == receviedpkcount || 413 == receviedpkcount ) {
|
|
316
370
|
/* flat3.wav */
|
|
317
|
-
expect( msg[ 17 ] ).to.equal(
|
|
318
|
-
expect( msg[ 150 ] ).to.equal(
|
|
371
|
+
expect( msg[ 17 ] ).to.equal( 220 )
|
|
372
|
+
expect( msg[ 150 ] ).to.equal( 220 )
|
|
373
|
+
} else if( ( 207 * 2 ) == receviedpkcount ) {
|
|
374
|
+
enoughpackets()
|
|
319
375
|
}
|
|
320
376
|
} )
|
|
321
377
|
|
|
322
378
|
server.bind()
|
|
323
|
-
server.on( "listening",
|
|
324
|
-
|
|
325
|
-
const ourport = server.address().port
|
|
379
|
+
await new Promise( resolve => server.on( "listening", resolve ) )
|
|
326
380
|
|
|
327
|
-
|
|
328
|
-
if( "close" === d.action ) done()
|
|
329
|
-
} )
|
|
381
|
+
const ourport = server.address().port
|
|
330
382
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
{ "wav": "/tmp/flat2.wav" },
|
|
334
|
-
{ "wav": "/tmp/flat.wav" },
|
|
335
|
-
{ "wav": "/tmp/flat3.wav", "start": 40, "stop": 60 }, /* should be 2 packets */
|
|
336
|
-
] } ) ).to.be.true
|
|
383
|
+
const channel = await prtp.projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
|
|
384
|
+
if( "close" === d.action ) done()
|
|
337
385
|
} )
|
|
338
386
|
|
|
339
|
-
|
|
387
|
+
expect( channel.play( { "loop": 2, "files": [
|
|
388
|
+
{ "wav": "/tmp/flat.wav", "loop": 2 },
|
|
389
|
+
{ "wav": "/tmp/flat2.wav" },
|
|
390
|
+
{ "wav": "/tmp/flat.wav" },
|
|
391
|
+
{ "wav": "/tmp/flat3.wav", "start": 40, "stop": 100 }, /* should be 4 packets */
|
|
392
|
+
] } ) ).to.be.true
|
|
393
|
+
|
|
394
|
+
await enoughpacketspromise
|
|
340
395
|
channel.close()
|
|
341
396
|
server.close()
|
|
342
397
|
|
|
@@ -345,7 +400,7 @@ describe( "rtpsound", function() {
|
|
|
345
400
|
/*
|
|
346
401
|
8000 samples looped twice with 3 sections to play. 400 packets (50 packets/S).
|
|
347
402
|
*/
|
|
348
|
-
expect( receviedpkcount ).to.be.
|
|
403
|
+
expect( receviedpkcount ).to.be.above( 400 )
|
|
349
404
|
} )
|
|
350
405
|
|
|
351
406
|
before( async () => {
|
|
@@ -355,35 +410,34 @@ describe( "rtpsound", function() {
|
|
|
355
410
|
const wavheader = createwavheader( samples )
|
|
356
411
|
const values = Buffer.alloc( samples * bytespersample )
|
|
357
412
|
for( let i = 0; i < samples; i++ ) {
|
|
358
|
-
values.
|
|
413
|
+
values.writeUInt16LE( 300, i * 2 )
|
|
359
414
|
}
|
|
360
415
|
/* Put a marker at the start of the file */
|
|
361
|
-
values.
|
|
416
|
+
values.writeUInt16LE( 1000, 50 )
|
|
362
417
|
|
|
363
|
-
await fs.writeFile( "/tmp/flat.wav", Buffer.concat( [ wavheader, values ] )
|
|
418
|
+
await fs.promises.writeFile( "/tmp/flat.wav", Buffer.concat( [ wavheader, values ] ) )
|
|
364
419
|
|
|
365
420
|
for( let i = 0; i < samples; i++ ) {
|
|
366
|
-
values.
|
|
421
|
+
values.writeUInt16LE( 400, i * 2 )
|
|
367
422
|
}
|
|
368
423
|
|
|
369
|
-
await fs.writeFile( "/tmp/flat2.wav", Buffer.concat( [ wavheader, values ] )
|
|
424
|
+
await fs.promises.writeFile( "/tmp/flat2.wav", Buffer.concat( [ wavheader, values ] ) )
|
|
370
425
|
|
|
371
426
|
for( let i = 0; i < samples; i++ ) {
|
|
372
|
-
values.
|
|
427
|
+
values.writeUInt16LE( 500, i * 2 )
|
|
373
428
|
}
|
|
374
429
|
|
|
430
|
+
await fs.promises.writeFile( "/tmp/flat3.wav", Buffer.concat( [ wavheader, values ] ) )
|
|
375
431
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
await fs.writeFile( "/tmp/flat3.wav", Buffer.concat( [ wavheader, values ] ), function() {} )
|
|
432
|
+
const sig = gensignal( 440, 8000, 1000 )
|
|
433
|
+
await fs.promises.writeFile( "/tmp/440sine.wav", Buffer.concat( [ wavheader, sig ] ) )
|
|
381
434
|
|
|
382
435
|
} )
|
|
383
436
|
|
|
384
437
|
after( async () => {
|
|
385
|
-
await
|
|
386
|
-
await
|
|
387
|
-
await
|
|
438
|
+
await fs.promises.unlink( "/tmp/flat.wav" )
|
|
439
|
+
await fs.promises.unlink( "/tmp/flat2.wav" )
|
|
440
|
+
await fs.promises.unlink( "/tmp/flat3.wav" )
|
|
441
|
+
await fs.promises.unlink( "/tmp/440sine.wav" )
|
|
388
442
|
} )
|
|
389
443
|
} )
|
|
@@ -9,6 +9,7 @@ const projectrtp = require( "../../index" ).projectrtp
|
|
|
9
9
|
const expect = require( "chai" ).expect
|
|
10
10
|
const dgram = require( "dgram" )
|
|
11
11
|
const fs = require( "fs" )
|
|
12
|
+
const pcap = require( "./pcap" )
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
/*
|
|
@@ -167,6 +168,20 @@ function pcmutolinear( inarray ) {
|
|
|
167
168
|
return out
|
|
168
169
|
}
|
|
169
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Send Buffer to server at required time
|
|
173
|
+
* @param { number } sendtime
|
|
174
|
+
* @param { Buffer } pk
|
|
175
|
+
* @param { number } dstport
|
|
176
|
+
* @param { dgram.Socket } server
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
179
|
+
function sendpayload( sendtime, pk, dstport, server ) {
|
|
180
|
+
return setTimeout( () => {
|
|
181
|
+
server.send( pk, dstport, "localhost" )
|
|
182
|
+
}, sendtime )
|
|
183
|
+
}
|
|
184
|
+
|
|
170
185
|
/**
|
|
171
186
|
*
|
|
172
187
|
* @param { number } sn - should start from 0 which we use to index into the supplied data buffer
|
|
@@ -653,5 +668,154 @@ describe( "Transcode", function() {
|
|
|
653
668
|
|
|
654
669
|
await fs.promises.unlink( "/tmp/ukringing.wav" ).catch( () => {} )
|
|
655
670
|
} )
|
|
671
|
+
|
|
672
|
+
it( "replay captured g722 from poly", async () => {
|
|
673
|
+
|
|
674
|
+
const g722endpoint = dgram.createSocket( "udp4" )
|
|
675
|
+
g722endpoint.on( "message", function() {} )
|
|
676
|
+
|
|
677
|
+
const pcmuendpoint = dgram.createSocket( "udp4" )
|
|
678
|
+
let receivedpcmu = []
|
|
679
|
+
pcmuendpoint.on( "message", function( msg ) {
|
|
680
|
+
pcmuendpoint.send( msg, pcmuchannel.local.port, "localhost" )
|
|
681
|
+
|
|
682
|
+
receivedpcmu = [ ...receivedpcmu, ...Array.from( pcmutolinear( parsepk( msg ).payload ) ) ]
|
|
683
|
+
} )
|
|
684
|
+
|
|
685
|
+
g722endpoint.bind()
|
|
686
|
+
await new Promise( resolve => g722endpoint.on( "listening", resolve ) )
|
|
687
|
+
pcmuendpoint.bind()
|
|
688
|
+
await new Promise( resolve => pcmuendpoint.on( "listening", resolve ) )
|
|
689
|
+
|
|
690
|
+
const allstats = {}
|
|
691
|
+
|
|
692
|
+
const g722channel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": g722endpoint.address().port, "codec": 9 } }, function( d ) {
|
|
693
|
+
if( "close" === d.action ) {
|
|
694
|
+
g722endpoint.close()
|
|
695
|
+
pcmuendpoint.close()
|
|
696
|
+
pcmuchannel.close()
|
|
697
|
+
allstats.achannel = { stats: d.stats }
|
|
698
|
+
}
|
|
699
|
+
} )
|
|
700
|
+
|
|
701
|
+
let done
|
|
702
|
+
const allclose = new Promise( resolve => done = resolve )
|
|
703
|
+
const pcmuchannel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": pcmuendpoint.address().port, "codec": 0 } }, function( d ) {
|
|
704
|
+
if( "close" === d.action ) {
|
|
705
|
+
allstats.bchannel = { stats: d.stats }
|
|
706
|
+
done()
|
|
707
|
+
}
|
|
708
|
+
} )
|
|
709
|
+
|
|
710
|
+
const ourpcap = ( await pcap.readpcap( "test/interface/pcaps/440hzinbackgroundg722.pcap" ) ).slice( 0, 50 )
|
|
711
|
+
|
|
712
|
+
g722channel.mix( pcmuchannel )
|
|
713
|
+
|
|
714
|
+
const offset = 0
|
|
715
|
+
ourpcap.forEach( ( packet ) => {
|
|
716
|
+
if( packet.ipv4 && packet.ipv4.udp && 10018 == packet.ipv4.udp.dstport ) {
|
|
717
|
+
sendpayload( ( 1000 * packet.ts_sec_offset ) - offset, packet.ipv4.udp.data, g722channel.local.port, g722endpoint )
|
|
718
|
+
}
|
|
719
|
+
} )
|
|
720
|
+
|
|
721
|
+
await new Promise( resolve => setTimeout( resolve, 1400 ) )
|
|
722
|
+
g722channel.close()
|
|
723
|
+
await allclose
|
|
724
|
+
|
|
725
|
+
npl.plot( [ {
|
|
726
|
+
y: Array.from( receivedpcmu ),
|
|
727
|
+
type: "scatter"
|
|
728
|
+
} ] )
|
|
729
|
+
|
|
730
|
+
const amps = ampbyfrequency( Int16Array.from( receivedpcmu ) )
|
|
731
|
+
const bin = 225
|
|
732
|
+
expect( 20000 < amps[ bin ] ).to.be.true
|
|
733
|
+
|
|
734
|
+
npl.plot( [ {
|
|
735
|
+
y: Array.from( amps ),
|
|
736
|
+
type: "scatter"
|
|
737
|
+
} ] )
|
|
738
|
+
|
|
739
|
+
} )
|
|
740
|
+
|
|
741
|
+
it( "replay captured g722 no transcode from poly 3 way mix", async () => {
|
|
742
|
+
|
|
743
|
+
const g722endpoint = dgram.createSocket( "udp4" )
|
|
744
|
+
g722endpoint.on( "message", function() {} )
|
|
745
|
+
|
|
746
|
+
const pcmuendpoint = dgram.createSocket( "udp4" )
|
|
747
|
+
let receivedpcmu = []
|
|
748
|
+
|
|
749
|
+
pcmuendpoint.on( "message", function( msg ) {
|
|
750
|
+
pcmuendpoint.send( msg, pcmuchannel.local.port, "localhost" )
|
|
751
|
+
|
|
752
|
+
receivedpcmu = [ ...receivedpcmu, ...Array.from( pcmutolinear( parsepk( msg ).payload ) ) ]
|
|
753
|
+
} )
|
|
754
|
+
|
|
755
|
+
g722endpoint.bind()
|
|
756
|
+
await new Promise( resolve => g722endpoint.on( "listening", resolve ) )
|
|
757
|
+
pcmuendpoint.bind()
|
|
758
|
+
await new Promise( resolve => pcmuendpoint.on( "listening", resolve ) )
|
|
759
|
+
|
|
760
|
+
const allstats = {}
|
|
761
|
+
|
|
762
|
+
const g722channel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": g722endpoint.address().port, "codec": 9 } }, function( d ) {
|
|
763
|
+
if( "close" === d.action ) {
|
|
764
|
+
g722endpoint.close()
|
|
765
|
+
pcmuendpoint.close()
|
|
766
|
+
pcmuchannel.close()
|
|
767
|
+
secondg722.close()
|
|
768
|
+
allstats.achannel = { stats: d.stats }
|
|
769
|
+
}
|
|
770
|
+
} )
|
|
771
|
+
|
|
772
|
+
let done
|
|
773
|
+
const allclose = new Promise( resolve => done = resolve )
|
|
774
|
+
const pcmuchannel = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": pcmuendpoint.address().port, "codec": 0 } }, function( d ) {
|
|
775
|
+
if( "close" === d.action ) {
|
|
776
|
+
allstats.bchannel = { stats: d.stats }
|
|
777
|
+
done()
|
|
778
|
+
}
|
|
779
|
+
} )
|
|
780
|
+
|
|
781
|
+
const secondg722 = await projectrtp.openchannel( { "id": "4", "remote": { "address": "localhost", "port": 9990, "codec": 9 } }, function( d ) {
|
|
782
|
+
if( "close" === d.action ) {
|
|
783
|
+
allstats.bchannel = { stats: d.stats }
|
|
784
|
+
done()
|
|
785
|
+
}
|
|
786
|
+
} )
|
|
787
|
+
|
|
788
|
+
const ourpcap = ( await pcap.readpcap( "test/interface/pcaps/440hzinbackgroundg722.pcap" ) ).slice( 0, 50 )
|
|
789
|
+
|
|
790
|
+
g722channel.mix( pcmuchannel )
|
|
791
|
+
g722channel.mix( secondg722 )
|
|
792
|
+
|
|
793
|
+
const offset = 0
|
|
794
|
+
ourpcap.forEach( ( packet ) => {
|
|
795
|
+
if( packet.ipv4 && packet.ipv4.udp && 10018 == packet.ipv4.udp.dstport ) {
|
|
796
|
+
sendpayload( ( 1000 * packet.ts_sec_offset ) - offset, packet.ipv4.udp.data, g722channel.local.port, g722endpoint )
|
|
797
|
+
}
|
|
798
|
+
} )
|
|
799
|
+
|
|
800
|
+
await new Promise( resolve => setTimeout( resolve, 1400 ) )
|
|
801
|
+
g722channel.close()
|
|
802
|
+
await allclose
|
|
803
|
+
|
|
804
|
+
npl.plot( [ {
|
|
805
|
+
y: Array.from( receivedpcmu ),
|
|
806
|
+
type: "scatter"
|
|
807
|
+
} ] )
|
|
808
|
+
|
|
809
|
+
const amps = ampbyfrequency( Int16Array.from( receivedpcmu ) )
|
|
810
|
+
|
|
811
|
+
npl.plot( [ {
|
|
812
|
+
y: Array.from( amps ),
|
|
813
|
+
type: "scatter"
|
|
814
|
+
} ] )
|
|
815
|
+
|
|
816
|
+
const bin = 430
|
|
817
|
+
expect( 20000 < amps[ bin ] ).to.be.true
|
|
818
|
+
|
|
819
|
+
} )
|
|
656
820
|
} )
|
|
657
821
|
|