@babblevoice/projectrtp 2.4.3 → 2.4.8
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 +3 -34
- package/lib/node.js +207 -214
- package/lib/server.js +3 -4
- package/package.json +5 -5
- package/test/basictests.js +16 -1
- package/test/interface/projectrtpchannel.js +3 -3
- package/test/interface/projectrtpdtls.js +1 -1
- package/test/interface/projectrtpdtmf.js +81 -68
- package/test/interface/projectrtpfullserver.js +83 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# ProjectRTP
|
|
2
2
|
|
|
3
|
+
[](https://github.com/babblevoice/projectrtp/actions/workflows/buildimage.yaml)
|
|
4
|
+
|
|
3
5
|
An RTP node addon which offers functionality to process RTP data streams and mix it. All performance tasks are implemented in C++ and use boost::asio for IO completion ports for high concurrency.
|
|
4
6
|
|
|
5
7
|
ProjectRTP is designed to scale to multiple servers serving other signalling servers. RTP and signalling should be kept separate, and this architecture allows that. It is provided with a proxy server and client to allow for remote nodes.
|
|
@@ -30,21 +32,9 @@ Given a version number MAJOR.MINOR.PATCH, increment the:
|
|
|
30
32
|
|
|
31
33
|
As part of this, we maintain jsdoc documentation to document our public interface to this programme.
|
|
32
34
|
|
|
33
|
-
Only when we release version 1.0.0 will the public API be settled.
|
|
34
|
-
|
|
35
35
|
## Release process
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
2. Update Dockerfile with the new version number
|
|
39
|
-
3. npm test && stress testing
|
|
40
|
-
4. git commit && push
|
|
41
|
-
5. npm publish
|
|
42
|
-
6. docker buildx prune
|
|
43
|
-
7. docker buildx build --platform linux/amd64,linux/arm64 -t tinpotnick/projectrtp:<version> . --push
|
|
44
|
-
8. Git release
|
|
45
|
-
9. Github tag <version>
|
|
46
|
-
|
|
47
|
-
<version> should simply the semantic version number. For late stage test builds this could be <version>betax.
|
|
37
|
+
Releases are managed on GitHub actions. It builds and tests any version.
|
|
48
38
|
|
|
49
39
|
## Docker
|
|
50
40
|
|
|
@@ -102,27 +92,6 @@ has a submodule so (although this is handled inteh Dockerfile if you are buildin
|
|
|
102
92
|
git submodule update --init --recursive
|
|
103
93
|
```
|
|
104
94
|
|
|
105
|
-
### Fedora
|
|
106
|
-
|
|
107
|
-
After installing standard build tools - including g++.
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
dnf install ilbc-devel spandsp-devel boost gnutls libsrtp libsrtp-devel g++
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Ubuntu
|
|
114
|
-
|
|
115
|
-
Build ilbc from the sub module plus install other dependancies.
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
|
|
119
|
-
apt install libboost-dev libboost-system-dev libspandsp-dev gnutls-dev libsrtp2-dev cmake ccache
|
|
120
|
-
|
|
121
|
-
cd libilbc
|
|
122
|
-
cmake . -DCMAKE_INSTALL_LIBDIR=/lib -DCMAKE_INSTALL_INCLUDEDIR=/usr/include; cmake --build .; cmake --install .
|
|
123
|
-
cd ..
|
|
124
|
-
```
|
|
125
|
-
|
|
126
95
|
## Docker/Podman
|
|
127
96
|
|
|
128
97
|
Podman has some shortfalls. Remove and install docker.
|
package/lib/node.js
CHANGED
|
@@ -22,6 +22,198 @@ const channelmap = {
|
|
|
22
22
|
"direction": ( chan, msg ) => chan.direction( msg.options )
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* One for each connection spawned by rtpnode.
|
|
27
|
+
*/
|
|
28
|
+
class rtpnodeconnection {
|
|
29
|
+
constructor( parent, connection ) {
|
|
30
|
+
this.connectionid = uuidv4()
|
|
31
|
+
this.connection = connection
|
|
32
|
+
this.connectionlength = 0
|
|
33
|
+
this.mode = "listen"
|
|
34
|
+
this.parent = parent
|
|
35
|
+
this.messagestate = message.newstate()
|
|
36
|
+
this._reconnecttime = 500 /* mS */
|
|
37
|
+
this._destroying = false
|
|
38
|
+
|
|
39
|
+
// spawned from listen
|
|
40
|
+
if( connection ) {
|
|
41
|
+
connection.setKeepAlive( true )
|
|
42
|
+
connection.on( "data", this._onsocketdata.bind( this ) )
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static create( parent, connection ) {
|
|
47
|
+
return new rtpnodeconnection( parent, connection )
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
destroy() {
|
|
51
|
+
this._destroying = true
|
|
52
|
+
if( this._reconnecttimerid ) {
|
|
53
|
+
clearTimeout( this._reconnecttimerid )
|
|
54
|
+
delete this._reconnecttimerid
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.connection.destroy()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_onsocketdata( data ) {
|
|
61
|
+
message.parsemessage( this.messagestate, data, ( msg ) => {
|
|
62
|
+
try {
|
|
63
|
+
this.parent._pre( msg, ( modifiedmsg ) => {
|
|
64
|
+
this._processmessage( modifiedmsg )
|
|
65
|
+
} )
|
|
66
|
+
} catch( e ) {
|
|
67
|
+
console.error( "Unhandled exception in babble-rtp", e )
|
|
68
|
+
}
|
|
69
|
+
} )
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @private
|
|
74
|
+
* @param { object } msg
|
|
75
|
+
* @returns boolean
|
|
76
|
+
*/
|
|
77
|
+
_updatechannel( msg ) {
|
|
78
|
+
|
|
79
|
+
if( undefined === msg.channel ) return false
|
|
80
|
+
if( undefined === msg.uuid ) return false
|
|
81
|
+
|
|
82
|
+
const chan = channels.get( msg.uuid )
|
|
83
|
+
if( undefined == chan ) return false
|
|
84
|
+
|
|
85
|
+
if( msg.channel in channelmap ) {
|
|
86
|
+
channelmap[ msg.channel ]( chan, msg )
|
|
87
|
+
} else {
|
|
88
|
+
const channelidentifiers = {
|
|
89
|
+
"id": msg.id,
|
|
90
|
+
"uuid": msg.uuid
|
|
91
|
+
}
|
|
92
|
+
this.send( { ...{ "error": "Unknown method" }, ...channelidentifiers } )
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
*
|
|
100
|
+
* @param { object } msg
|
|
101
|
+
* @returns { Promise< Boolean > }
|
|
102
|
+
*/
|
|
103
|
+
async _processmessage( msg ) {
|
|
104
|
+
if( "open" == msg.channel ) return await this._openchannel( msg )
|
|
105
|
+
|
|
106
|
+
return this._updatechannel( msg )
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Send a message back to the main server, include stats to help with load balancing.
|
|
111
|
+
* @param { object } msg
|
|
112
|
+
* @returns { void }
|
|
113
|
+
*/
|
|
114
|
+
send( msg, cb = undefined ) {
|
|
115
|
+
this.parent._post( msg, ( modifiedmsg ) => {
|
|
116
|
+
if( this._destroying ) return
|
|
117
|
+
msg.status = this.parent.prtp.stats()
|
|
118
|
+
msg.status.instance = instance
|
|
119
|
+
this.connection.write( message.createmessage( modifiedmsg ) )
|
|
120
|
+
if (cb !== undefined) cb(msg)
|
|
121
|
+
} )
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @private
|
|
127
|
+
* @param { object } msg
|
|
128
|
+
* @returns { Promise< Boolean > }
|
|
129
|
+
*/
|
|
130
|
+
async _openchannel( msg ) {
|
|
131
|
+
this.connectionlength += 1
|
|
132
|
+
msg.forcelocal = true
|
|
133
|
+
|
|
134
|
+
const chan = await this.parent.prtp.openchannel( msg, ( cookie ) => {
|
|
135
|
+
// TODO: we might want to ensure the actual event has been written
|
|
136
|
+
// to the server before cleaning up the channel on our side?
|
|
137
|
+
this.send( { ...{ "id": chan.id, "uuid": chan.uuid }, ...cookie },
|
|
138
|
+
( cookie ) => {
|
|
139
|
+
if ( "close" === cookie.action ) {
|
|
140
|
+
this.connectionlength -= 1
|
|
141
|
+
channels.delete( chan.uuid )
|
|
142
|
+
|
|
143
|
+
if( 0 == this.connectionlength && "listen" == this.mode ) {
|
|
144
|
+
this.parent.connections.delete( this.connectionid )
|
|
145
|
+
this.connection.destroy()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} )
|
|
149
|
+
} )
|
|
150
|
+
channels.set( chan.uuid, chan )
|
|
151
|
+
this.send( { ...chan, ...{ "action": "open" } } )
|
|
152
|
+
|
|
153
|
+
return true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @summary Connect to a server.
|
|
159
|
+
* @param {string} host
|
|
160
|
+
* @param {number} port
|
|
161
|
+
* @return {Promise} - Promise which resolves to an rtpnode
|
|
162
|
+
*/
|
|
163
|
+
connect( host, port ) {
|
|
164
|
+
this.host = host
|
|
165
|
+
this.port = port
|
|
166
|
+
this.mode = "connect"
|
|
167
|
+
|
|
168
|
+
return new Promise( resolve => {
|
|
169
|
+
|
|
170
|
+
this.connection = net.createConnection( this.port, this.host )
|
|
171
|
+
this.connection.on( "data", this._onsocketdata.bind( this ) )
|
|
172
|
+
|
|
173
|
+
this.connection.setKeepAlive( true )
|
|
174
|
+
|
|
175
|
+
this.connection.on( "connect", () => {
|
|
176
|
+
this.send( {} )
|
|
177
|
+
resolve()
|
|
178
|
+
this._reconnecttime = 500 /* mS */
|
|
179
|
+
} )
|
|
180
|
+
|
|
181
|
+
this.connection.on( "error", () => {
|
|
182
|
+
if( this._destroying ) return
|
|
183
|
+
this._runreconnect()
|
|
184
|
+
} )
|
|
185
|
+
this.connection.on( "close", () => {
|
|
186
|
+
if( this._destroying ) return
|
|
187
|
+
this._runreconnect()
|
|
188
|
+
} )
|
|
189
|
+
} )
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @private
|
|
194
|
+
* @returns { void }
|
|
195
|
+
*/
|
|
196
|
+
_reconnect() {
|
|
197
|
+
delete this._reconnecttimerid
|
|
198
|
+
this.connect( this.host, this.port )
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @private
|
|
203
|
+
* @returns { void }
|
|
204
|
+
*/
|
|
205
|
+
_runreconnect() {
|
|
206
|
+
if( this._reconnecttimerid ) return
|
|
207
|
+
console.log( "Disconnected - trying to reconnect to " + this.host + ":" + this.port )
|
|
208
|
+
|
|
209
|
+
this._reconnecttimerid = setTimeout( this._reconnect.bind( this ), this._reconnecttime )
|
|
210
|
+
|
|
211
|
+
this._reconnecttime = this._reconnecttime * 2
|
|
212
|
+
if( this._reconnecttime > ( 1000 * 2 ) ) this._reconnecttime = this._reconnecttime / 2
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
25
217
|
/**
|
|
26
218
|
* @summary An RTP node. We are a remote RTP node which waits for intruction from our server.
|
|
27
219
|
*/
|
|
@@ -38,7 +230,6 @@ class rtpnode {
|
|
|
38
230
|
this.port = port
|
|
39
231
|
this.instance = uuidv4()
|
|
40
232
|
this.connections = new Map()
|
|
41
|
-
this.messagestate = message.newstate()
|
|
42
233
|
|
|
43
234
|
/* pre callbacks are called when we receive an instruction from
|
|
44
235
|
our server and we want to check if there is anything to do such as
|
|
@@ -50,42 +241,19 @@ class rtpnode {
|
|
|
50
241
|
to our server - this is to, for example, upload a recording to a remote storage
|
|
51
242
|
facility. */
|
|
52
243
|
this._post = this._defaulthandler.bind( this )
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @type { NodeJS.Timeout }
|
|
56
|
-
*/
|
|
57
|
-
this._reconnecttimerid
|
|
58
244
|
}
|
|
59
245
|
|
|
60
246
|
/**
|
|
61
247
|
* @summary Connect to a server.
|
|
62
|
-
* @param {number} port
|
|
63
248
|
* @param {string} host
|
|
249
|
+
* @param {number} port
|
|
64
250
|
* @return {Promise<rtpnode>} - Promise which resolves to an rtpnode
|
|
65
251
|
*/
|
|
66
|
-
connect(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
this.
|
|
70
|
-
this
|
|
71
|
-
|
|
72
|
-
return new Promise( resolve => {
|
|
73
|
-
this._onsocketreadypromiseresolve = resolve
|
|
74
|
-
const connection = net.createConnection( this.port, this.host )
|
|
75
|
-
this.connection = connection
|
|
76
|
-
this.connection.setKeepAlive( true )
|
|
77
|
-
this.connection.on( "connect", this._onsocketconnect.bind( this ) )
|
|
78
|
-
const con = {
|
|
79
|
-
connectionid: uuidv4(),
|
|
80
|
-
connection,
|
|
81
|
-
"connectionlength": 0,
|
|
82
|
-
"mode": "connect"
|
|
83
|
-
}
|
|
84
|
-
this.connections[ con.connectionid ] = con
|
|
85
|
-
this.connection.on( "data", this._onsocketdata.bind( this, con ) )
|
|
86
|
-
this.connection.on( "error", this._onsocketerror.bind( this ) )
|
|
87
|
-
this.connection.on( "close", this._onsocketclose.bind( this ) )
|
|
88
|
-
} )
|
|
252
|
+
async connect( host, port ) {
|
|
253
|
+
const con = rtpnodeconnection.create( this )
|
|
254
|
+
await con.connect( host, port )
|
|
255
|
+
this.connections.set( con.connectionid, con )
|
|
256
|
+
return this
|
|
89
257
|
}
|
|
90
258
|
|
|
91
259
|
/**
|
|
@@ -95,15 +263,12 @@ class rtpnode {
|
|
|
95
263
|
let listenresolve
|
|
96
264
|
const listenpromise = new Promise( ( r ) => listenresolve = r )
|
|
97
265
|
this.server = net.createServer( ( connection ) => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
266
|
+
try {
|
|
267
|
+
const con = rtpnodeconnection.create( this, connection )
|
|
268
|
+
this.connections.set( con.connectionid, con )
|
|
269
|
+
} catch( e ) {
|
|
270
|
+
console.error( "Uncaught exception in node listen", e )
|
|
103
271
|
}
|
|
104
|
-
this.connections[ con.connectionid ] = con
|
|
105
|
-
connection.setKeepAlive( true )
|
|
106
|
-
connection.on( "data", this._onsocketdata.bind( this, con ) )
|
|
107
272
|
} )
|
|
108
273
|
|
|
109
274
|
this.server.listen( this.port, this.address )
|
|
@@ -112,17 +277,6 @@ class rtpnode {
|
|
|
112
277
|
return listenpromise
|
|
113
278
|
}
|
|
114
279
|
|
|
115
|
-
/**
|
|
116
|
-
* @private
|
|
117
|
-
* @returns { void }
|
|
118
|
-
*/
|
|
119
|
-
_onsocketconnect( /* sock */ ) {
|
|
120
|
-
//console.log( "Connected to " + this.host + ":" + this.port )
|
|
121
|
-
this.send( {}, this.connection )
|
|
122
|
-
this._onsocketreadypromiseresolve( this )
|
|
123
|
-
this._reconnecttime = 500 /* mS */
|
|
124
|
-
}
|
|
125
|
-
|
|
126
280
|
/**
|
|
127
281
|
* This callback is displayed as a global member.
|
|
128
282
|
* @callback requestComplete
|
|
@@ -159,22 +313,6 @@ class rtpnode {
|
|
|
159
313
|
this._post = cb
|
|
160
314
|
}
|
|
161
315
|
|
|
162
|
-
/**
|
|
163
|
-
* Send a message back to the main server, include stats to help with load balancing.
|
|
164
|
-
* @param { object } msg
|
|
165
|
-
* @param { object } connection
|
|
166
|
-
* @returns { void }
|
|
167
|
-
*/
|
|
168
|
-
send( msg, connection, cb = undefined ) {
|
|
169
|
-
this._post( msg, ( modifiedmsg ) => {
|
|
170
|
-
if( this._destroying ) return
|
|
171
|
-
msg.status = this.prtp.stats()
|
|
172
|
-
msg.status.instance = instance
|
|
173
|
-
connection.write( message.createmessage( modifiedmsg ) )
|
|
174
|
-
if (cb !== undefined) cb(msg)
|
|
175
|
-
} )
|
|
176
|
-
}
|
|
177
|
-
|
|
178
316
|
/**
|
|
179
317
|
* @summary Destroy this node
|
|
180
318
|
* @returns { void }
|
|
@@ -183,24 +321,10 @@ class rtpnode {
|
|
|
183
321
|
this._destroying = true
|
|
184
322
|
nodeinterface.clean()
|
|
185
323
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this.connections.forEach( ( connection ) => {
|
|
189
|
-
connection.destroy()
|
|
190
|
-
} )
|
|
191
|
-
if ( this.server ) this.server.close()
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
*
|
|
196
|
-
* @param { object } msg
|
|
197
|
-
* @param { object } con
|
|
198
|
-
* @returns { Promise< Boolean > }
|
|
199
|
-
*/
|
|
200
|
-
async _processmessage( msg, con ) {
|
|
201
|
-
if( "open" == msg.channel ) return await this._openchannel( msg, con )
|
|
324
|
+
this.connections.forEach( ( con ) => con.destroy() )
|
|
325
|
+
this.connections.clear()
|
|
202
326
|
|
|
203
|
-
|
|
327
|
+
if ( this.server ) this.server.close()
|
|
204
328
|
}
|
|
205
329
|
|
|
206
330
|
/**
|
|
@@ -211,137 +335,6 @@ class rtpnode {
|
|
|
211
335
|
_defaulthandler( msg, cb ) {
|
|
212
336
|
cb( msg )
|
|
213
337
|
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* @private
|
|
217
|
-
* @returns { void }
|
|
218
|
-
*/
|
|
219
|
-
_reconnect() {
|
|
220
|
-
|
|
221
|
-
delete this._reconnecttimerid
|
|
222
|
-
this.connection = net.createConnection( this.port, this.host )
|
|
223
|
-
.on( "error", () => {
|
|
224
|
-
this._runreconnect()
|
|
225
|
-
} )
|
|
226
|
-
|
|
227
|
-
this.connection.setKeepAlive( true )
|
|
228
|
-
|
|
229
|
-
this.connection.on( "connect", this._onsocketconnect.bind( this ) )
|
|
230
|
-
this.connection.on( "data", this._onsocketdata.bind( this ) )
|
|
231
|
-
this.connection.on( "error", this._onsocketerror.bind( this ) )
|
|
232
|
-
this.connection.on( "close", this._onsocketclose.bind( this ) )
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* @private
|
|
237
|
-
* @returns { void }
|
|
238
|
-
*/
|
|
239
|
-
_runreconnect() {
|
|
240
|
-
if( this._reconnecttimerid ) return
|
|
241
|
-
console.log( "Disconnected - trying to reconnect to " + this.host + ":" + this.port )
|
|
242
|
-
|
|
243
|
-
this._reconnecttimerid = setTimeout( this._reconnect.bind( this ), this._reconnecttime )
|
|
244
|
-
|
|
245
|
-
this._reconnecttime = this._reconnecttime * 2
|
|
246
|
-
if( this._reconnecttime > ( 1000 * 2 ) ) this._reconnecttime = this._reconnecttime / 2
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* @private
|
|
251
|
-
* @returns { void }
|
|
252
|
-
*/
|
|
253
|
-
_onsocketclose() {
|
|
254
|
-
if( this._destroying ) return
|
|
255
|
-
this._runreconnect()
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Reconnect on failure.
|
|
260
|
-
* @private
|
|
261
|
-
* @returns { void }
|
|
262
|
-
*/
|
|
263
|
-
_onsocketerror() {
|
|
264
|
-
if( this._destroying ) return
|
|
265
|
-
this._runreconnect()
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* @privateopenchannel
|
|
270
|
-
* @param { Buffer } data
|
|
271
|
-
* @param { object } con
|
|
272
|
-
* @returns { void }
|
|
273
|
-
*/
|
|
274
|
-
_onsocketdata( con, data ) {
|
|
275
|
-
message.parsemessage( this.messagestate, data, ( msg ) => {
|
|
276
|
-
try {
|
|
277
|
-
this._pre( msg, ( modifiedmsg ) => {
|
|
278
|
-
this._processmessage( modifiedmsg, con )
|
|
279
|
-
} )
|
|
280
|
-
} catch( e ) {
|
|
281
|
-
console.error( "Unhandled exception in babble-rtp", e )
|
|
282
|
-
}
|
|
283
|
-
} )
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* @private
|
|
288
|
-
* @param { object } msg
|
|
289
|
-
* @param { object } con
|
|
290
|
-
* @returns { Promise< Boolean > }
|
|
291
|
-
*/
|
|
292
|
-
async _openchannel( msg, con ) {
|
|
293
|
-
con.connectionlength += 1
|
|
294
|
-
msg.forcelocal = true
|
|
295
|
-
|
|
296
|
-
const chan = await this.prtp.openchannel( msg, ( cookie ) => {
|
|
297
|
-
// TODO: we might want to ensure the actual event has been written
|
|
298
|
-
// to the server before cleaning up the channel on our side?
|
|
299
|
-
this.send( { ...{ "id": chan.id, "uuid": chan.uuid }, ...cookie },
|
|
300
|
-
con.connection,
|
|
301
|
-
( cookie ) => {
|
|
302
|
-
if ( "close" === cookie.action ) {
|
|
303
|
-
con.connectionlength -= 1
|
|
304
|
-
channels.delete( chan.uuid )
|
|
305
|
-
|
|
306
|
-
if( 0 == con.connectionlength && "listen" == con.mode ) {
|
|
307
|
-
this.connections.delete( con.instance )
|
|
308
|
-
con.connection.destroy()
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
} )
|
|
312
|
-
} )
|
|
313
|
-
channels.set( chan.uuid, chan )
|
|
314
|
-
this.send( { ...chan, ...{ "action": "open" } }, con.connection )
|
|
315
|
-
|
|
316
|
-
return true
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* @private
|
|
321
|
-
* @param { object } msg
|
|
322
|
-
* @param { object } con
|
|
323
|
-
* @returns boolean
|
|
324
|
-
*/
|
|
325
|
-
_updatechannel( msg, con ) {
|
|
326
|
-
|
|
327
|
-
if( undefined === msg.channel ) return false
|
|
328
|
-
if( undefined === msg.uuid ) return false
|
|
329
|
-
|
|
330
|
-
const chan = channels.get( msg.uuid )
|
|
331
|
-
if( undefined == chan ) return false
|
|
332
|
-
|
|
333
|
-
if( msg.channel in channelmap ) {
|
|
334
|
-
channelmap[ msg.channel ]( chan, msg )
|
|
335
|
-
} else {
|
|
336
|
-
const channelidentifiers = {
|
|
337
|
-
"id": msg.id,
|
|
338
|
-
"uuid": msg.uuid
|
|
339
|
-
}
|
|
340
|
-
this.send( { ...{ "error": "Unknown method" }, ...channelidentifiers }, con.connection )
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return true
|
|
344
|
-
}
|
|
345
338
|
}
|
|
346
339
|
|
|
347
340
|
/**
|
|
@@ -386,7 +379,7 @@ class nodeinterface {
|
|
|
386
379
|
if( nodeinterface._n ) return new Promise( r => r( nodeinterface._n ) )
|
|
387
380
|
|
|
388
381
|
nodeinterface._n = new rtpnode( this.prtp )
|
|
389
|
-
return nodeinterface._n.connect(
|
|
382
|
+
return nodeinterface._n.connect( host, port )
|
|
390
383
|
}
|
|
391
384
|
|
|
392
385
|
/**
|
package/lib/server.js
CHANGED
|
@@ -5,7 +5,7 @@ const { v4: uuidv4 } = require( "uuid" )
|
|
|
5
5
|
|
|
6
6
|
const EventEmitter = require( "events" )
|
|
7
7
|
|
|
8
|
-
const message = require( "./message
|
|
8
|
+
const message = require( "./message" )
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Node host address and port object
|
|
@@ -599,11 +599,10 @@ class channel {
|
|
|
599
599
|
|
|
600
600
|
/**
|
|
601
601
|
*
|
|
602
|
-
* @param { object } msg
|
|
603
602
|
* @returns { void }
|
|
604
603
|
* @private
|
|
605
604
|
*/
|
|
606
|
-
_runclose(
|
|
605
|
+
_runclose() {
|
|
607
606
|
if( undefined !== this.openresolve ) {
|
|
608
607
|
this.openresolve()
|
|
609
608
|
delete this.openresolve
|
|
@@ -634,7 +633,7 @@ class channel {
|
|
|
634
633
|
this.em.emit( "all", msg )
|
|
635
634
|
this.em.emit( msg.action, msg )
|
|
636
635
|
|
|
637
|
-
if( "close" == msg.action ) this._runclose(
|
|
636
|
+
if( "close" == msg.action ) this._runclose()
|
|
638
637
|
}
|
|
639
638
|
|
|
640
639
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babblevoice/projectrtp",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.8",
|
|
4
4
|
"description": "A scalable Node addon RTP server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"test": "./node_modules/mocha/bin/_mocha test/**/*.js --check-leaks",
|
|
13
|
-
"github:check": "node test/basictests.js; npm update;
|
|
13
|
+
"github:check": "node test/basictests.js; npm update; ./node_modules/mocha/bin/_mocha test/**/*.js --check-leaks --exit",
|
|
14
14
|
"check": "tsc --checkJs --noEmit --target es6 --module commonjs --skipLibCheck *.js test/**/*.js; ./node_modules/eslint/bin/eslint.js ./",
|
|
15
15
|
"docs": "jsdoc -c jsdoc.conf.json ./README.md",
|
|
16
16
|
"stress": "node stress/index.js",
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "git+https://github.com/
|
|
28
|
+
"url": "git+https://github.com/babblevoice/projectrtp.git"
|
|
29
29
|
},
|
|
30
30
|
"author": "Nick Knight",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"bugs": {
|
|
33
|
-
"url": "https://github.com/
|
|
33
|
+
"url": "https://github.com/babblevoice/projectrtp/issues"
|
|
34
34
|
},
|
|
35
|
-
"homepage": "https://github.com/
|
|
35
|
+
"homepage": "https://github.com/babblevoice/projectrtp#readme",
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"uuid": "^9.0.0"
|
|
38
38
|
},
|
package/test/basictests.js
CHANGED
|
@@ -23,8 +23,23 @@ async function testopen() {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
let retval = 0
|
|
27
|
+
console.log( "Running project RTP" )
|
|
26
28
|
prtp.projectrtp.run()
|
|
27
|
-
|
|
29
|
+
|
|
30
|
+
console.log( "Test open channel" )
|
|
31
|
+
|
|
32
|
+
try{
|
|
33
|
+
testopen()
|
|
34
|
+
} catch( e ) {
|
|
35
|
+
console.log( e )
|
|
36
|
+
retval = 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log( "Shutting down projectrtp" )
|
|
28
40
|
prtp.projectrtp.shutdown()
|
|
41
|
+
console.log( "Shutdown - good to proceed" )
|
|
42
|
+
|
|
43
|
+
process.exit( retval )
|
|
29
44
|
|
|
30
45
|
|
|
@@ -96,7 +96,7 @@ describe( "rtpchannel", function() {
|
|
|
96
96
|
await finished
|
|
97
97
|
} )
|
|
98
98
|
|
|
99
|
-
it( "call create channel and check the structure of the returned object
|
|
99
|
+
it( "call create channel and check the structure of the returned object - server as listener", async function() {
|
|
100
100
|
|
|
101
101
|
const ourport = 45433
|
|
102
102
|
await projectrtp.server.listen( ourport, "127.0.0.1" )
|
|
@@ -119,7 +119,7 @@ describe( "rtpchannel", function() {
|
|
|
119
119
|
await server.destroy()
|
|
120
120
|
} )
|
|
121
121
|
|
|
122
|
-
it( "call create channel and check the structure of the returned object
|
|
122
|
+
it( "call create channel and check the structure of the returned object - node as listener", async function() {
|
|
123
123
|
|
|
124
124
|
const ourport = 45432
|
|
125
125
|
projectrtp.server.clearnodes()
|
|
@@ -437,7 +437,7 @@ describe( "rtpchannel", function() {
|
|
|
437
437
|
expect( closedstats.stats.in.count ).to.equal( 300 )
|
|
438
438
|
expect( closedstats.stats.out.count ).to.be.within( 180, 190 )
|
|
439
439
|
expect( totalsndiff ).to.equal( 0 ) // received should be reordered
|
|
440
|
-
expect( totaltsdiff ).to.
|
|
440
|
+
expect( totaltsdiff ).to.be.within( 18240, 18400 ) // Allow some loss in test
|
|
441
441
|
expect( lastsn - firstsn ).to.be.within( 180, 192 )
|
|
442
442
|
} )
|
|
443
443
|
|
|
@@ -432,7 +432,7 @@ describe( "dtls", function() {
|
|
|
432
432
|
await finished
|
|
433
433
|
|
|
434
434
|
expect( clientcclose.stats.in.count ).to.be.above( 10 )
|
|
435
|
-
expect( clientcclose.stats.in.skip ).to.
|
|
435
|
+
expect( clientcclose.stats.in.skip ).to.be.below( 2 ) // allow a little loss in test
|
|
436
436
|
|
|
437
437
|
} )
|
|
438
438
|
} )
|
|
@@ -338,7 +338,7 @@ describe( "dtmf", function() {
|
|
|
338
338
|
} )
|
|
339
339
|
|
|
340
340
|
|
|
341
|
-
it( "Send multiple 2833 DTMF and check event", function(
|
|
341
|
+
it( "Send multiple 2833 DTMF and check event", async function() {
|
|
342
342
|
|
|
343
343
|
/* create our RTP/UDP endpoint */
|
|
344
344
|
const server = dgram.createSocket( "udp4" )
|
|
@@ -348,53 +348,58 @@ describe( "dtmf", function() {
|
|
|
348
348
|
this.slow( 2500 )
|
|
349
349
|
|
|
350
350
|
server.bind()
|
|
351
|
-
server.on( "listening",
|
|
351
|
+
await new Promise( ( resolve ) => { server.on( "listening", () => resolve() ) } )
|
|
352
352
|
|
|
353
|
-
|
|
353
|
+
const ourport = server.address().port
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
355
|
+
const receivedmessages = []
|
|
356
|
+
const channel = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
|
|
357
|
+
receivedmessages.push( d )
|
|
358
|
+
if( "close" === d.action ) {
|
|
359
|
+
server.close()
|
|
360
|
+
}
|
|
361
|
+
} )
|
|
361
362
|
|
|
362
|
-
|
|
363
|
-
expect( d ).to.deep.include( expectedmessages[ expectedmessagecount ] )
|
|
364
|
-
expectedmessagecount++
|
|
363
|
+
expect( channel.echo() ).to.be.true
|
|
365
364
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
} )
|
|
365
|
+
/* send a packet every 20mS x 50 */
|
|
366
|
+
for( let i = 0; 13 > i; i ++ ) {
|
|
367
|
+
sendpk( i, i*160, i*20, channel.local.port, server )
|
|
368
|
+
}
|
|
371
369
|
|
|
372
|
-
|
|
370
|
+
/* DTMF every 50mS */
|
|
371
|
+
senddtmf( 13, 12*160, 13*20, channel.local.port, server, false, "4" )
|
|
372
|
+
sendpk( 14, 13*160, 13*20, channel.local.port, server )
|
|
373
|
+
sendpk( 15, 14*160, 14*20, channel.local.port, server )
|
|
374
|
+
senddtmf( 16, 14*160, (13*20)+50, channel.local.port, server, false, "4" )
|
|
375
|
+
// Packet loss
|
|
376
|
+
// senddtmf( 15, 12 * 160, 15*20, channel.port, server, true, "4" )
|
|
373
377
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
+
for( let i = 17; 30 > i; i ++ ) {
|
|
379
|
+
sendpk( i, (i-2)*160, (i-2)*20, channel.local.port, server )
|
|
380
|
+
}
|
|
378
381
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
382
|
+
senddtmf( 31, 29*160, 29*20, channel.local.port, server, false, "5" )
|
|
383
|
+
sendpk( 32, 29*160, 29*20, channel.local.port, server )
|
|
384
|
+
sendpk( 33, 30*160, 30*20, channel.local.port, server )
|
|
385
|
+
senddtmf( 34, 30*160, (29*20)+50, channel.local.port, server, false, "5" )
|
|
386
|
+
sendpk( 35, 31*160, 31*20, channel.local.port, server )
|
|
387
|
+
sendpk( 36, 32*160, 32*20, channel.local.port, server )
|
|
388
|
+
senddtmf( 37, 32*160, (29*20)+100, channel.local.port, server, true, "5" )
|
|
383
389
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
390
|
+
for( let i = 37; 45 > i; i ++ ) {
|
|
391
|
+
sendpk( i, i*160, (i-6)*20, channel.local.port, server )
|
|
392
|
+
}
|
|
387
393
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
senddtmf( 25, 22 * 160, 25*20, channel.local.port, server, true, "5" )
|
|
394
|
+
setTimeout( () => channel.close(), 1100 )
|
|
395
|
+
await new Promise( resolve => { server.on( "close", resolve ) } )
|
|
391
396
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
397
|
+
expect( receivedmessages[ 0 ].action ).to.equal( "telephone-event" )
|
|
398
|
+
expect( receivedmessages[ 0 ].event ).to.equal( "4" )
|
|
399
|
+
expect( receivedmessages[ 1 ].action ).to.equal( "telephone-event" )
|
|
400
|
+
expect( receivedmessages[ 1 ].event ).to.equal( "5" )
|
|
401
|
+
expect( receivedmessages[ 2 ].action ).to.equal( "close" )
|
|
395
402
|
|
|
396
|
-
setTimeout( () => channel.close(), 1000 )
|
|
397
|
-
} )
|
|
398
403
|
} )
|
|
399
404
|
|
|
400
405
|
it( "Lose end packet", async function() {
|
|
@@ -686,9 +691,6 @@ describe( "dtmf", function() {
|
|
|
686
691
|
|
|
687
692
|
const receveiedmessages = []
|
|
688
693
|
|
|
689
|
-
let done
|
|
690
|
-
const finished = new Promise( ( r ) => { done = r } )
|
|
691
|
-
|
|
692
694
|
const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) {
|
|
693
695
|
receveiedmessages.push( d )
|
|
694
696
|
|
|
@@ -702,7 +704,11 @@ describe( "dtmf", function() {
|
|
|
702
704
|
|
|
703
705
|
const channelc = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointc.address().port, "codec": 97 } }, function( d ) {
|
|
704
706
|
expect( d.action).to.not.equal( "telephone-event" )
|
|
705
|
-
if( "close" === d.action )
|
|
707
|
+
if( "close" === d.action ) {
|
|
708
|
+
endpointa.close()
|
|
709
|
+
endpointb.close()
|
|
710
|
+
endpointc.close()
|
|
711
|
+
}
|
|
706
712
|
} )
|
|
707
713
|
|
|
708
714
|
/* mix */
|
|
@@ -735,32 +741,32 @@ describe( "dtmf", function() {
|
|
|
735
741
|
senddtmf( 13, 13*160, 13*20, channela.local.port, endpointa, false, "4" )
|
|
736
742
|
sendpk( 14, 13*160, 13*20, channela.local.port, endpointa, 0 )
|
|
737
743
|
sendpk( 15, 14*160, 14*20, channela.local.port, endpointa, 0 )
|
|
738
|
-
|
|
739
|
-
|
|
744
|
+
sendpk( 16, 15*160, 15*20, channela.local.port, endpointa, 0 )
|
|
745
|
+
senddtmf( 17, (15*160), (13*20)+50, channela.local.port, endpointa, false, "4" )
|
|
740
746
|
sendpk( 18, 16*160, 16*20, channela.local.port, endpointa, 0 )
|
|
741
|
-
|
|
742
|
-
|
|
747
|
+
sendpk( 19, 17*160, 17*20, channela.local.port, endpointa, 0 )
|
|
748
|
+
senddtmf( 20, (17*160), (13*20)+100, channela.local.port, endpointa, true, "4" )
|
|
743
749
|
sendpk( 21, 18*160, 18*20, channela.local.port, endpointa, 0 )
|
|
744
|
-
|
|
745
|
-
sendpk( 23,
|
|
750
|
+
sendpk( 22, 19*160, 19*20, channela.local.port, endpointa, 0 )
|
|
751
|
+
sendpk( 23, 20*160, 20*20, channela.local.port, endpointa, 0 )
|
|
752
|
+
senddtmf( 24, (20*160), (13*20)+150, channela.local.port, endpointa, true, "4" )
|
|
753
|
+
sendpk( 25, 21*160, 21*20, channela.local.port, endpointa, 0 )
|
|
754
|
+
sendpk( 26, 22*160, 22*20, channela.local.port, endpointa, 0 )
|
|
755
|
+
sendpk( 27, 23*160, 23*20, channela.local.port, endpointa, 0 )
|
|
756
|
+
sendpk( 28, 24*160, 24*20, channela.local.port, endpointa, 0 )
|
|
757
|
+
sendpk( 29, 25*160, 25*20, channela.local.port, endpointa, 0 )
|
|
746
758
|
|
|
747
|
-
senddtmf(
|
|
748
|
-
sendpk(
|
|
749
|
-
sendpk(
|
|
750
|
-
senddtmf(
|
|
751
|
-
sendpk(
|
|
752
|
-
sendpk(
|
|
753
|
-
senddtmf(
|
|
754
|
-
sendpk(
|
|
755
|
-
sendpk(
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
sendpk( 35, 27*160, 27*20, channela.local.port, endpointa, 0 )
|
|
759
|
-
sendpk( 36, 28*160, 28*20, channela.local.port, endpointa, 0 )
|
|
760
|
-
sendpk( 37, 29*160, 29*20, channela.local.port, endpointa, 0 )
|
|
761
|
-
sendpk( 38, 30*160, 30*20, channela.local.port, endpointa, 0 )
|
|
762
|
-
sendpk( 39, 31*160, 31*20, channela.local.port, endpointa, 0 )
|
|
763
|
-
sendpk( 40, 32*160, 32*20, channela.local.port, endpointa, 0 )
|
|
759
|
+
senddtmf( 30, 25*160, 25*20, channela.local.port, endpointa, false, "5" )
|
|
760
|
+
sendpk( 31, 26*160, 26*20, channela.local.port, endpointa, 0 )
|
|
761
|
+
sendpk( 32, 27*160, 27*20, channela.local.port, endpointa, 0 )
|
|
762
|
+
senddtmf( 33, (27*160), (25*20)+50, channela.local.port, endpointa, false, "5" )
|
|
763
|
+
sendpk( 34, 28*160, 28*20, channela.local.port, endpointa, 0 )
|
|
764
|
+
sendpk( 35, 29*160, 29*20, channela.local.port, endpointa, 0 )
|
|
765
|
+
senddtmf( 36, (29*160), (25*20)+100, channela.local.port, endpointa, true, "5" )
|
|
766
|
+
sendpk( 37, 30*160, 30*20, channela.local.port, endpointa, 0 )
|
|
767
|
+
sendpk( 38, 31*160, 31*20, channela.local.port, endpointa, 0 )
|
|
768
|
+
sendpk( 39, 32*160, 32*20, channela.local.port, endpointa, 0 )
|
|
769
|
+
senddtmf( 40, (32*160), (25*20)+150, channela.local.port, endpointa, true, "5" )
|
|
764
770
|
sendpk( 41, 33*160, 33*20, channela.local.port, endpointa, 0 )
|
|
765
771
|
sendpk( 42, 34*160, 34*20, channela.local.port, endpointa, 0 )
|
|
766
772
|
sendpk( 43, 35*160, 35*20, channela.local.port, endpointa, 0 )
|
|
@@ -774,15 +780,22 @@ describe( "dtmf", function() {
|
|
|
774
780
|
sendpk( 51, 43*160, 43*20, channela.local.port, endpointa, 0 )
|
|
775
781
|
sendpk( 52, 44*160, 44*20, channela.local.port, endpointa, 0 )
|
|
776
782
|
sendpk( 53, 45*160, 45*20, channela.local.port, endpointa, 0 )
|
|
783
|
+
sendpk( 54, 46*160, 46*20, channela.local.port, endpointa, 0 )
|
|
784
|
+
sendpk( 55, 47*160, 47*20, channela.local.port, endpointa, 0 )
|
|
785
|
+
sendpk( 56, 48*160, 48*20, channela.local.port, endpointa, 0 )
|
|
786
|
+
sendpk( 57, 49*160, 49*20, channela.local.port, endpointa, 0 )
|
|
787
|
+
sendpk( 58, 50*160, 50*20, channela.local.port, endpointa, 0 )
|
|
788
|
+
sendpk( 59, 52*160, 51*20, channela.local.port, endpointa, 0 )
|
|
777
789
|
|
|
778
790
|
await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1200 ) } )
|
|
779
791
|
|
|
780
792
|
channela.close()
|
|
781
|
-
endpointa.close()
|
|
782
|
-
endpointb.close()
|
|
783
|
-
endpointc.close()
|
|
784
793
|
|
|
785
|
-
await
|
|
794
|
+
await Promise.all( [
|
|
795
|
+
new Promise( resolve => { endpointa.on( "close", resolve ) } ),
|
|
796
|
+
new Promise( resolve => { endpointb.on( "close", resolve ) } ),
|
|
797
|
+
new Promise( resolve => { endpointc.on( "close", resolve ) } )
|
|
798
|
+
] )
|
|
786
799
|
|
|
787
800
|
expect( endpointapkcount ).to.be.within( 59, 70 )
|
|
788
801
|
expect( endpointbpkcount ).to.be.within( 59, 70 )
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
const prtp = require( "../../index" )
|
|
3
|
+
const node = require( "../../lib/node" )
|
|
4
|
+
const expect = require( "chai" ).expect
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Test file to run tests acting as a remote note. Starts a babble-rtp node in the background
|
|
8
|
+
* before any tests begin then runs tests to open, play, polly etc.
|
|
9
|
+
*/
|
|
10
|
+
describe( "server connect interface", () => {
|
|
11
|
+
|
|
12
|
+
before( async () => {
|
|
13
|
+
const host = "127.0.0.1"
|
|
14
|
+
const port = 9002
|
|
15
|
+
await prtp.projectrtp.node.listen( host, port )
|
|
16
|
+
prtp.projectrtp.server.addnode( { host, port} )
|
|
17
|
+
|
|
18
|
+
} )
|
|
19
|
+
|
|
20
|
+
after( () => {
|
|
21
|
+
|
|
22
|
+
prtp.projectrtp.server.clearnodes()
|
|
23
|
+
node.interface.destroy()
|
|
24
|
+
} )
|
|
25
|
+
|
|
26
|
+
it( "server connect and open channel", async function () {
|
|
27
|
+
this.timeout( 6000 )
|
|
28
|
+
this.slow( 3000 )
|
|
29
|
+
|
|
30
|
+
const totalotherchannelcount = 100
|
|
31
|
+
let chanclosecount = 0
|
|
32
|
+
let allchannelsclosedresolve
|
|
33
|
+
const allchannelsclosed = new Promise( resolve => allchannelsclosedresolve = resolve )
|
|
34
|
+
const onclose = ( e ) => {
|
|
35
|
+
if( "close" == e.action ) chanclosecount++
|
|
36
|
+
if( totalotherchannelcount == chanclosecount ) allchannelsclosedresolve()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// A very short wav file
|
|
40
|
+
prtp.projectrtp.tone.generate( "350+440*0.5:100", "/tmp/serverconnecttestwav.wav" )
|
|
41
|
+
prtp.projectrtp.tone.generate( "350+440*0.5:100", "/tmp/otherserverconnecttestwav.wav" )
|
|
42
|
+
|
|
43
|
+
const channels = []
|
|
44
|
+
for( let i = 0; totalotherchannelcount > i; i++ ) {
|
|
45
|
+
channels.push( await prtp.projectrtp.openchannel( onclose ) )
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for( let i = 0; 3 > i; i++ ) {
|
|
49
|
+
let done
|
|
50
|
+
const finished = new Promise( resolve => done = resolve )
|
|
51
|
+
|
|
52
|
+
const receivedmessages = []
|
|
53
|
+
const chan = await prtp.projectrtp.openchannel( ( e ) => {
|
|
54
|
+
receivedmessages.push( e )
|
|
55
|
+
if( "play" == e.action && "end" == e.event ) chan.close()
|
|
56
|
+
if( "close" == e.action ) done()
|
|
57
|
+
} )
|
|
58
|
+
|
|
59
|
+
chan.play( { "interupt":true, "files": [ { "wav": "/tmp/serverconnecttestwav.wav" }, { "wav": "/tmp/otherserverconnecttestwav.wav" } ] } )
|
|
60
|
+
|
|
61
|
+
await finished
|
|
62
|
+
|
|
63
|
+
//console.log(receivedmessages)
|
|
64
|
+
expect( receivedmessages.length ).to.equal( 3 )
|
|
65
|
+
expect( receivedmessages[ 0 ].action ).to.equal( "play" )
|
|
66
|
+
expect( receivedmessages[ 0 ].event ).to.equal( "start" )
|
|
67
|
+
expect( receivedmessages[ 0 ].reason ).to.equal( "new" )
|
|
68
|
+
expect( receivedmessages[ 1 ].action ).to.equal( "play" )
|
|
69
|
+
expect( receivedmessages[ 1 ].event ).to.equal( "end" )
|
|
70
|
+
expect( receivedmessages[ 1 ].reason ).to.equal( "completed" )
|
|
71
|
+
expect( receivedmessages[ 2 ].action ).to.equal( "close" )
|
|
72
|
+
expect( receivedmessages[ 2 ].reason ).to.equal( "requested" )
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for( const chan of channels ) {
|
|
76
|
+
chan.close()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await allchannelsclosed
|
|
80
|
+
expect( chanclosecount ).to.equal( totalotherchannelcount )
|
|
81
|
+
} )
|
|
82
|
+
|
|
83
|
+
} )
|