@babblevoice/projectrtp 2.4.5 → 2.4.9
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/lib/node.js +207 -214
- package/lib/server.js +3 -4
- package/package.json +1 -1
- package/src/projectrtpchannelmux.cpp +1 -5
- package/src/projectrtpcodecx.cpp +65 -62
- package/test/interface/projectrtpchannel.js +2 -2
- package/test/interface/projectrtpdtls.js +61 -77
- package/test/interface/projectrtpfullserver.js +83 -0
- package/test/interface/projectrtpmix.js +63 -0
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
|
@@ -75,10 +75,6 @@ void projectchannelmux::mixall( void ) {
|
|
|
75
75
|
this->postrtpdata( chan, dtmfchan, src );
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
AQUIRESPINLOCK( chan->rtpbufferlock );
|
|
80
|
-
chan->inbuff->poppeeked();
|
|
81
|
-
RELEASESPINLOCK( chan->rtpbufferlock );
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
if( nullptr != src ) {
|
|
@@ -94,7 +90,7 @@ void projectchannelmux::mixall( void ) {
|
|
|
94
90
|
|
|
95
91
|
rtppacket *dst = chan->gettempoutbuf();
|
|
96
92
|
|
|
97
|
-
|
|
93
|
+
/* start with a direct copy */
|
|
98
94
|
this->subtracted.copy( this->added );
|
|
99
95
|
|
|
100
96
|
if( chan->recv ) {
|
package/src/projectrtpcodecx.cpp
CHANGED
|
@@ -141,8 +141,7 @@ void codecx::reset()
|
|
|
141
141
|
## restart
|
|
142
142
|
Do enough to manage missing packets.
|
|
143
143
|
*/
|
|
144
|
-
void codecx::restart( void )
|
|
145
|
-
{
|
|
144
|
+
void codecx::restart( void ) {
|
|
146
145
|
this->lpfilter.reset();
|
|
147
146
|
this->dcpowerfilter.reset();
|
|
148
147
|
this->resamplelastsample = 0;
|
|
@@ -154,8 +153,7 @@ void codecx::restart( void )
|
|
|
154
153
|
|
|
155
154
|
From whichever PCM encoding (u or a) encode to the other without having to do intermediate l16.
|
|
156
155
|
*/
|
|
157
|
-
bool codecx::alaw2ulaw( void )
|
|
158
|
-
{
|
|
156
|
+
bool codecx::alaw2ulaw( void ) {
|
|
159
157
|
if( 0 == this->pcmaref.size() ) return false;
|
|
160
158
|
if( this->pcmaref.isdirty() ) return false;
|
|
161
159
|
|
|
@@ -167,23 +165,22 @@ bool codecx::alaw2ulaw( void )
|
|
|
167
165
|
outbufptr = this->pcmuref.c_str();
|
|
168
166
|
this->pcmuref.size( insize );
|
|
169
167
|
|
|
170
|
-
if( nullptr == outbufptr || nullptr == inbufptr )
|
|
171
|
-
{
|
|
168
|
+
if( nullptr == outbufptr || nullptr == inbufptr ) {
|
|
172
169
|
std::cerr << "PCMA NULLPTR shouldn't happen (" << (void*)outbufptr << ", " << (void*)inbufptr << ")" << std::endl;
|
|
173
170
|
return false;
|
|
174
171
|
}
|
|
175
172
|
|
|
176
|
-
for( size_t i = 0; i < insize; i++ )
|
|
177
|
-
|
|
178
|
-
|
|
173
|
+
for( size_t i = 0; i < insize; i++ ) {
|
|
174
|
+
*outbufptr = alaw_to_ulaw_table[ *inbufptr ];
|
|
175
|
+
inbufptr++;
|
|
176
|
+
outbufptr++;
|
|
179
177
|
}
|
|
180
178
|
|
|
181
179
|
this->pcmuref.dirty( false );
|
|
182
180
|
return true;
|
|
183
181
|
}
|
|
184
182
|
|
|
185
|
-
bool codecx::ulaw2alaw( void )
|
|
186
|
-
{
|
|
183
|
+
bool codecx::ulaw2alaw( void ) {
|
|
187
184
|
if( 0 == this->pcmuref.size() ) return false;
|
|
188
185
|
if( this->pcmuref.isdirty() ) return false;
|
|
189
186
|
|
|
@@ -195,15 +192,15 @@ bool codecx::ulaw2alaw( void )
|
|
|
195
192
|
outbufptr = this->pcmaref.c_str();
|
|
196
193
|
this->pcmaref.size( insize );
|
|
197
194
|
|
|
198
|
-
if( nullptr == outbufptr || nullptr == inbufptr )
|
|
199
|
-
{
|
|
195
|
+
if( nullptr == outbufptr || nullptr == inbufptr ) {
|
|
200
196
|
std::cerr << "PCMU NULLPTR shouldn't happen(" << (void*)outbufptr << ", " << (void*)inbufptr << ")" << std::endl;
|
|
201
197
|
return false;
|
|
202
198
|
}
|
|
203
199
|
|
|
204
|
-
for( size_t i = 0; i < insize; i++ )
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
for( size_t i = 0; i < insize; i++ ) {
|
|
201
|
+
*outbufptr = ulaw_to_alaw_table[ *inbufptr ];
|
|
202
|
+
inbufptr++;
|
|
203
|
+
outbufptr++;
|
|
207
204
|
}
|
|
208
205
|
|
|
209
206
|
this->pcmaref.dirty( false );
|
|
@@ -220,20 +217,15 @@ bool codecx::g711tol16( void )
|
|
|
220
217
|
int16_t *convert;
|
|
221
218
|
size_t insize;
|
|
222
219
|
|
|
223
|
-
if( this->pcmaref.size() > 0 && !this->pcmaref.isdirty() )
|
|
224
|
-
{
|
|
220
|
+
if( this->pcmaref.size() > 0 && !this->pcmaref.isdirty() ) {
|
|
225
221
|
in = this->pcmaref.c_str();
|
|
226
222
|
convert = _pcmatol16;
|
|
227
223
|
insize = this->pcmaref.size();
|
|
228
|
-
}
|
|
229
|
-
else if ( this->pcmuref.size() > 0 && !this->pcmuref.isdirty() )
|
|
230
|
-
{
|
|
224
|
+
} else if ( this->pcmuref.size() > 0 && !this->pcmuref.isdirty() ) {
|
|
231
225
|
in = this->pcmuref.c_str();
|
|
232
226
|
convert = _pcmutol16;
|
|
233
227
|
insize = this->pcmuref.size();
|
|
234
|
-
}
|
|
235
|
-
else
|
|
236
|
-
{
|
|
228
|
+
} else {
|
|
237
229
|
return false;
|
|
238
230
|
}
|
|
239
231
|
|
|
@@ -241,9 +233,10 @@ bool codecx::g711tol16( void )
|
|
|
241
233
|
|
|
242
234
|
int16_t *out = ( int16_t * ) this->l168kref.c_str();
|
|
243
235
|
|
|
244
|
-
for( size_t i = 0; i < insize; i++ )
|
|
245
|
-
|
|
246
|
-
|
|
236
|
+
for( size_t i = 0; i < insize; i++ ) {
|
|
237
|
+
*out = convert[ *in ];
|
|
238
|
+
in++;
|
|
239
|
+
out++;
|
|
247
240
|
}
|
|
248
241
|
|
|
249
242
|
this->l168kref.dirty( false );
|
|
@@ -265,9 +258,12 @@ bool codecx::l16topcma( void )
|
|
|
265
258
|
size_t l168klength = this->l168kref.size();
|
|
266
259
|
in = ( int16_t * ) this->l168kref.c_str();
|
|
267
260
|
|
|
268
|
-
|
|
269
|
-
{
|
|
270
|
-
|
|
261
|
+
uint16_t index;
|
|
262
|
+
for( size_t i = 0; i < l168klength; i++ ) {
|
|
263
|
+
index = *in + 32768;
|
|
264
|
+
*out = _l16topcma[ index ];
|
|
265
|
+
in++;
|
|
266
|
+
out++;
|
|
271
267
|
}
|
|
272
268
|
this->pcmaref.dirty( false );
|
|
273
269
|
return true;
|
|
@@ -276,8 +272,7 @@ bool codecx::l16topcma( void )
|
|
|
276
272
|
/*!md
|
|
277
273
|
## l16topcmu
|
|
278
274
|
*/
|
|
279
|
-
bool codecx::l16topcmu( void )
|
|
280
|
-
{
|
|
275
|
+
bool codecx::l16topcmu( void ) {
|
|
281
276
|
if( 0 == this->l168kref.size() ) return false;
|
|
282
277
|
if( this->l168kref.isdirty() ) return false;
|
|
283
278
|
|
|
@@ -287,9 +282,12 @@ bool codecx::l16topcmu( void )
|
|
|
287
282
|
size_t l168klength = this->l168kref.size();
|
|
288
283
|
in = ( int16_t * ) this->l168kref.c_str();
|
|
289
284
|
|
|
290
|
-
|
|
291
|
-
{
|
|
292
|
-
|
|
285
|
+
uint16_t index;
|
|
286
|
+
for( size_t i = 0; i < l168klength; i++ ) {
|
|
287
|
+
index = *in + 32768;
|
|
288
|
+
*out = _l16topcmu[ index ];
|
|
289
|
+
in++;
|
|
290
|
+
out++;
|
|
293
291
|
}
|
|
294
292
|
this->pcmuref.dirty( false );
|
|
295
293
|
return true;
|
|
@@ -299,8 +297,7 @@ bool codecx::l16topcmu( void )
|
|
|
299
297
|
## ilbctol16
|
|
300
298
|
As it says.
|
|
301
299
|
*/
|
|
302
|
-
bool codecx::ilbctol16( void )
|
|
303
|
-
{
|
|
300
|
+
bool codecx::ilbctol16( void ) {
|
|
304
301
|
if( 0 == this->ilbcref.size() ) return false;
|
|
305
302
|
if( this->ilbcref.isdirty() ) return false;
|
|
306
303
|
|
|
@@ -335,25 +332,20 @@ bool codecx::ilbctol16( void )
|
|
|
335
332
|
## l16tog722
|
|
336
333
|
As it says.
|
|
337
334
|
*/
|
|
338
|
-
bool codecx::l16tog722( void )
|
|
339
|
-
{
|
|
335
|
+
bool codecx::l16tog722( void ) {
|
|
340
336
|
if( 0 == this->l1616kref.size() ) return false;
|
|
341
337
|
if( this->l1616kref.isdirty() ) return false;
|
|
342
338
|
|
|
343
|
-
if( nullptr == this->g722encoder )
|
|
344
|
-
{
|
|
339
|
+
if( nullptr == this->g722encoder ) {
|
|
345
340
|
this->g722encoder = g722_encode_init( NULL, 64000, G722_PACKED );
|
|
346
341
|
}
|
|
347
342
|
|
|
348
343
|
int len = g722_encode( this->g722encoder, this->g722ref.c_str(), ( int16_t * ) this->l1616kref.c_str(), this->g722ref.size() * 2 );
|
|
349
344
|
|
|
350
|
-
if( len > 0 )
|
|
351
|
-
{
|
|
345
|
+
if( len > 0 ) {
|
|
352
346
|
this->g722ref.size( len );
|
|
353
347
|
this->g722ref.dirty( false );
|
|
354
|
-
}
|
|
355
|
-
else
|
|
356
|
-
{
|
|
348
|
+
} else {
|
|
357
349
|
std::cerr << "g722_encode didn't encode any data" << std::endl;
|
|
358
350
|
this->g722ref.size( 0 );
|
|
359
351
|
}
|
|
@@ -400,19 +392,16 @@ bool codecx::l16toilbc( void ) {
|
|
|
400
392
|
## g722tol16
|
|
401
393
|
As it says.
|
|
402
394
|
*/
|
|
403
|
-
bool codecx::g722tol16( void )
|
|
404
|
-
{
|
|
395
|
+
bool codecx::g722tol16( void ) {
|
|
405
396
|
if( 0 == this->g722ref.size() ) return false;
|
|
406
397
|
if( this->g722ref.isdirty() ) return false;
|
|
407
398
|
|
|
408
399
|
/* x 2 for 16 bit instead of 8 and then x 2 sample rate */
|
|
409
400
|
this->l1616kref.malloc( this->g722ref.size(), sizeof( int16_t ), L1616KPAYLOADTYPE );
|
|
410
401
|
|
|
411
|
-
if( nullptr == this->g722decoder )
|
|
412
|
-
{
|
|
402
|
+
if( nullptr == this->g722decoder ) {
|
|
413
403
|
this->g722decoder = g722_decode_init( NULL, 64000, G722_PACKED );
|
|
414
|
-
if( nullptr == this->g722decoder )
|
|
415
|
-
{
|
|
404
|
+
if( nullptr == this->g722decoder ) {
|
|
416
405
|
std::cerr << "Failed to init G722 decoder" << std::endl;
|
|
417
406
|
}
|
|
418
407
|
}
|
|
@@ -431,8 +420,7 @@ bool codecx::g722tol16( void )
|
|
|
431
420
|
## l16lowtowideband
|
|
432
421
|
Upsample from narrow to wideband. Take each point and interpolate between them. We require the final sample from the last packet to continue the interpolating.
|
|
433
422
|
*/
|
|
434
|
-
bool codecx::l16lowtowideband( void )
|
|
435
|
-
{
|
|
423
|
+
bool codecx::l16lowtowideband( void ) {
|
|
436
424
|
size_t l168klength = this->l168kref.size();
|
|
437
425
|
|
|
438
426
|
if( 0 == l168klength ) return false;
|
|
@@ -443,8 +431,7 @@ bool codecx::l16lowtowideband( void )
|
|
|
443
431
|
int16_t *in = ( int16_t * ) this->l168kref.c_str();
|
|
444
432
|
int16_t *out = ( int16_t * ) this->l1616kref.c_str();
|
|
445
433
|
|
|
446
|
-
for( size_t i = 0; i < l168klength; i++ )
|
|
447
|
-
{
|
|
434
|
+
for( size_t i = 0; i < l168klength; i++ ) {
|
|
448
435
|
*out = ( ( *in - this->resamplelastsample ) / 2 ) + this->resamplelastsample;
|
|
449
436
|
this->resamplelastsample = *in;
|
|
450
437
|
out++;
|
|
@@ -463,8 +450,7 @@ bool codecx::l16lowtowideband( void )
|
|
|
463
450
|
## requirewideband
|
|
464
451
|
Search for the relevent data and convert as necessary.
|
|
465
452
|
*/
|
|
466
|
-
bool codecx::requirewideband( void )
|
|
467
|
-
{
|
|
453
|
+
bool codecx::requirewideband( void ) {
|
|
468
454
|
if( 0 != this->l1616kref.size() && !this->l1616kref.isdirty() ) return true;
|
|
469
455
|
if( this->g722tol16() ) return true;
|
|
470
456
|
if( !this->g711tol16() )
|
|
@@ -491,10 +477,12 @@ bool codecx::l16widetonarrowband( void )
|
|
|
491
477
|
int16_t *out = ( int16_t * ) this->l168kref.c_str();
|
|
492
478
|
int16_t *in = ( int16_t * ) this->l1616kref.c_str();
|
|
493
479
|
|
|
494
|
-
for( size_t i = 0; i < l1616klength / 2; i++ )
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
*out
|
|
480
|
+
for( size_t i = 0; i < l1616klength / 2; i++ ) {
|
|
481
|
+
lpfilter.execute( *in );
|
|
482
|
+
in++;
|
|
483
|
+
*out = lpfilter.execute( *in );
|
|
484
|
+
in++;
|
|
485
|
+
out++;
|
|
498
486
|
}
|
|
499
487
|
|
|
500
488
|
this->l168kref.dirty( false );
|
|
@@ -820,6 +808,18 @@ void codectests( void )
|
|
|
820
808
|
|
|
821
809
|
#ifdef NODE_MODULE
|
|
822
810
|
|
|
811
|
+
static napi_value codectest( napi_env env, napi_callback_info info ) {
|
|
812
|
+
napi_value result;
|
|
813
|
+
if( napi_ok != napi_create_object( env, &result ) ) return NULL;
|
|
814
|
+
|
|
815
|
+
codectests();
|
|
816
|
+
|
|
817
|
+
napi_create_uint32( env, 1, &result );
|
|
818
|
+
napi_coerce_to_bool( env, result, &result );
|
|
819
|
+
|
|
820
|
+
return result;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
823
|
/*
|
|
824
824
|
Support single number just for now - but TODO detect Buffer input to convert whole bufffer.
|
|
825
825
|
*/
|
|
@@ -929,6 +929,9 @@ void initrtpcodecx( napi_env env, napi_value &result ) {
|
|
|
929
929
|
|
|
930
930
|
if( napi_ok != napi_create_function( env, "exports", NAPI_AUTO_LENGTH, pcmu2linear, nullptr, &funct ) ) return;
|
|
931
931
|
if( napi_ok != napi_set_named_property( env, codecx, "pcmu2linear16", funct ) ) return;
|
|
932
|
+
|
|
933
|
+
if( napi_ok != napi_create_function( env, "exports", NAPI_AUTO_LENGTH, codectest, nullptr, &funct ) ) return;
|
|
934
|
+
if( napi_ok != napi_set_named_property( env, codecx, "codectests", funct ) ) return;
|
|
932
935
|
}
|
|
933
936
|
|
|
934
937
|
#endif /* NODE_MODULE */
|
|
@@ -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()
|
|
@@ -298,36 +298,24 @@ describe( "dtls", function() {
|
|
|
298
298
|
it( "Connect then remote to another session", async function() {
|
|
299
299
|
|
|
300
300
|
/*
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
301
|
+
Play, then encrypt at one end, then unencrypt in projectrtp and send onto d
|
|
302
|
+
Create an e channel so that we mix 3
|
|
303
|
+
|
|
304
|
+
| internal |
|
|
305
|
+
channel channel channel channel
|
|
306
|
+
a RTP over DTLS -----------b c RTP d
|
|
307
|
+
play b mix c echo
|
|
308
|
+
mix e f
|
|
309
|
+
echo
|
|
310
310
|
|
|
311
311
|
*/
|
|
312
312
|
|
|
313
313
|
this.timeout( 6000 )
|
|
314
314
|
this.slow( 2500 )
|
|
315
315
|
|
|
316
|
-
projectrtp.tone.generate( "400+450*0.
|
|
316
|
+
projectrtp.tone.generate( "400+450*0.25/0/400+450*0.25/0:400/200/400/2000", "/tmp/ukringing.wav" )
|
|
317
317
|
|
|
318
|
-
const
|
|
319
|
-
"address": "localhost",
|
|
320
|
-
"port": 0,
|
|
321
|
-
"codec": 0,
|
|
322
|
-
"dtls": {
|
|
323
|
-
"fingerprint": {
|
|
324
|
-
"hash": ""
|
|
325
|
-
},
|
|
326
|
-
"mode": "active" // - is this in the right place and the right way round!
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const channeltargetc = {
|
|
318
|
+
const targeta = {
|
|
331
319
|
"address": "localhost",
|
|
332
320
|
"port": 0,
|
|
333
321
|
"codec": 0,
|
|
@@ -335,36 +323,23 @@ describe( "dtls", function() {
|
|
|
335
323
|
"fingerprint": {
|
|
336
324
|
"hash": ""
|
|
337
325
|
},
|
|
338
|
-
"mode": "active"
|
|
326
|
+
"mode": "active"
|
|
339
327
|
}
|
|
340
328
|
}
|
|
341
329
|
|
|
342
|
-
const
|
|
343
|
-
"address": "localhost",
|
|
344
|
-
"port": 12008,
|
|
345
|
-
"codec": 0,
|
|
346
|
-
"dtls": {
|
|
347
|
-
"fingerprint": {
|
|
348
|
-
"hash": ""
|
|
349
|
-
},
|
|
350
|
-
"mode": "passive"
|
|
351
|
-
}
|
|
352
|
-
}
|
|
330
|
+
const targetb = JSON.parse( JSON.stringify( targeta ) )
|
|
353
331
|
|
|
354
|
-
const
|
|
332
|
+
const targetc = {
|
|
355
333
|
"address": "localhost",
|
|
356
334
|
"port": 0,
|
|
357
335
|
"codec": 0
|
|
358
336
|
}
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
"port": 12010,
|
|
363
|
-
"codec": 0
|
|
364
|
-
}
|
|
337
|
+
const targetd = JSON.parse( JSON.stringify( targetc ) )
|
|
338
|
+
const targete = JSON.parse( JSON.stringify( targetc ) )
|
|
339
|
+
const targetf = JSON.parse( JSON.stringify( targetc ) )
|
|
365
340
|
|
|
366
341
|
let done
|
|
367
|
-
const finished = new Promise( (
|
|
342
|
+
const finished = new Promise( ( resolve ) => { done = resolve } )
|
|
368
343
|
|
|
369
344
|
const channela = await projectrtp.openchannel( {}, function( d ) {
|
|
370
345
|
if( "close" === d.action ) {
|
|
@@ -372,57 +347,66 @@ describe( "dtls", function() {
|
|
|
372
347
|
}
|
|
373
348
|
} )
|
|
374
349
|
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
clienttargeta.port = channela.local.port
|
|
381
|
-
clienttargeta.dtls.fingerprint.hash = channela.local.dtls.fingerprint
|
|
382
|
-
expect( clienta.remote( clienttargeta ) ).to.be.true
|
|
383
|
-
|
|
384
|
-
clienta.play( { "loop": true, "files": [
|
|
385
|
-
{ "wav": "/tmp/ukringing.wav" } ] } )
|
|
350
|
+
const channelb = await projectrtp.openchannel( {}, function( d ) {
|
|
351
|
+
if( "close" === d.action ) {
|
|
352
|
+
channelc.close()
|
|
353
|
+
}
|
|
354
|
+
} )
|
|
386
355
|
|
|
356
|
+
const channelc = await projectrtp.openchannel( {}, function( d ) {
|
|
357
|
+
if( "close" === d.action ) {
|
|
358
|
+
channeld.close()
|
|
359
|
+
}
|
|
360
|
+
} )
|
|
387
361
|
|
|
388
|
-
const
|
|
362
|
+
const channeld = await projectrtp.openchannel( {}, function( d ) {
|
|
389
363
|
if( "close" === d.action ) {
|
|
390
|
-
|
|
364
|
+
channele.close()
|
|
391
365
|
}
|
|
392
366
|
} )
|
|
393
367
|
|
|
394
|
-
const
|
|
368
|
+
const channele = await projectrtp.openchannel( {}, function( d ) {
|
|
395
369
|
if( "close" === d.action ) {
|
|
396
|
-
|
|
370
|
+
channelf.close()
|
|
397
371
|
}
|
|
398
372
|
} )
|
|
399
373
|
|
|
400
|
-
let
|
|
401
|
-
const
|
|
374
|
+
let channelclose = {}
|
|
375
|
+
const channelf = await projectrtp.openchannel( {}, function( d ) {
|
|
402
376
|
if( "close" === d.action ) {
|
|
403
|
-
|
|
377
|
+
channelclose = d
|
|
404
378
|
done()
|
|
405
379
|
}
|
|
406
380
|
} )
|
|
407
381
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
382
|
+
targeta.dtls.fingerprint.hash = channelb.local.dtls.fingerprint
|
|
383
|
+
targeta.port = channelb.local.port
|
|
384
|
+
expect( channela.remote( targeta ) ).to.be.true
|
|
385
|
+
targetb.port = channela.local.port
|
|
386
|
+
targetb.dtls.fingerprint.hash = channela.local.dtls.fingerprint
|
|
387
|
+
targetb.dtls.mode = "passive"
|
|
388
|
+
expect( channelb.remote( targetb ) ).to.be.true
|
|
415
389
|
|
|
416
|
-
|
|
390
|
+
targetc.port = channeld.local.port
|
|
391
|
+
expect( channelc.remote( targetc ) ).to.be.true
|
|
392
|
+
targetd.port = channelc.local.port
|
|
393
|
+
expect( channeld.remote( targetd ) ).to.be.true
|
|
417
394
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
expect(
|
|
395
|
+
targete.port = channelf.local.port
|
|
396
|
+
expect( channele.remote( targete ) ).to.be.true
|
|
397
|
+
targetf.port = channele.local.port
|
|
398
|
+
expect( channelf.remote( targetf ) ).to.be.true
|
|
422
399
|
|
|
423
|
-
|
|
400
|
+
/* play on one end */
|
|
401
|
+
channela.play( { "loop": true, "files": [
|
|
424
402
|
{ "wav": "/tmp/ukringing.wav" } ] } )
|
|
425
|
-
|
|
403
|
+
|
|
404
|
+
/* mix in the middle */
|
|
405
|
+
channelb.mix( channelc )
|
|
406
|
+
channelb.mix( channele )
|
|
407
|
+
|
|
408
|
+
/* echo at the other end */
|
|
409
|
+
expect( channeld.echo() ).to.be.true
|
|
426
410
|
|
|
427
411
|
await new Promise( ( r ) => { setTimeout( () => r(), 1500 ) } )
|
|
428
412
|
|
|
@@ -431,8 +415,8 @@ describe( "dtls", function() {
|
|
|
431
415
|
await fs.promises.unlink( "/tmp/ukringing.wav" ).catch( () => {} )
|
|
432
416
|
await finished
|
|
433
417
|
|
|
434
|
-
expect(
|
|
435
|
-
expect(
|
|
418
|
+
expect( channelclose.stats.in.count ).to.be.above( 30 )
|
|
419
|
+
expect( channelclose.stats.in.skip ).to.be.below( 2 ) // allow a little loss in test
|
|
436
420
|
|
|
437
421
|
} )
|
|
438
422
|
} )
|
|
@@ -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
|
+
} )
|
|
@@ -432,6 +432,69 @@ describe( "channel mix", function() {
|
|
|
432
432
|
|
|
433
433
|
} )
|
|
434
434
|
|
|
435
|
+
it( "mix 2 channels - pcmu <-> g722 with recording", async function() {
|
|
436
|
+
|
|
437
|
+
this.timeout( 3000 )
|
|
438
|
+
this.slow( 2000 )
|
|
439
|
+
|
|
440
|
+
const endpointa = dgram.createSocket( "udp4" )
|
|
441
|
+
const endpointb = dgram.createSocket( "udp4" )
|
|
442
|
+
|
|
443
|
+
let endpointapkcount = 0
|
|
444
|
+
let endpointbpkcount = 0
|
|
445
|
+
|
|
446
|
+
endpointa.on( "message", function( msg ) {
|
|
447
|
+
endpointapkcount++
|
|
448
|
+
expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
|
|
449
|
+
} )
|
|
450
|
+
|
|
451
|
+
endpointb.on( "message", function( msg ) {
|
|
452
|
+
|
|
453
|
+
endpointbpkcount++
|
|
454
|
+
expect( 0x7f & msg [ 1 ] ).to.equal( 9 )
|
|
455
|
+
endpointb.send( msg, channelb.local.port, "localhost" )
|
|
456
|
+
} )
|
|
457
|
+
|
|
458
|
+
endpointa.bind()
|
|
459
|
+
await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } )
|
|
460
|
+
|
|
461
|
+
endpointb.bind()
|
|
462
|
+
await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } )
|
|
463
|
+
|
|
464
|
+
let done
|
|
465
|
+
const finished = new Promise( ( r ) => { done = r } )
|
|
466
|
+
|
|
467
|
+
const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0 } }, function( d ) {
|
|
468
|
+
if( "close" === d.action ) channelb.close()
|
|
469
|
+
} )
|
|
470
|
+
|
|
471
|
+
const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 9 } }, function( d ) {
|
|
472
|
+
if( "close" === d.action ) done()
|
|
473
|
+
} )
|
|
474
|
+
|
|
475
|
+
/* mix */
|
|
476
|
+
expect( channela.mix( channelb ) ).to.be.true
|
|
477
|
+
|
|
478
|
+
channela.record( { "file": "/tmp/g722mix2recording.wav" } )
|
|
479
|
+
|
|
480
|
+
/* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */
|
|
481
|
+
for( let i = 0; 50 > i; i ++ ) {
|
|
482
|
+
sendpk( i, i, channela.local.port, endpointa )
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1500 ) } )
|
|
486
|
+
|
|
487
|
+
channela.close()
|
|
488
|
+
endpointa.close()
|
|
489
|
+
endpointb.close()
|
|
490
|
+
|
|
491
|
+
expect( endpointapkcount ).to.be.above( 30 )
|
|
492
|
+
expect( endpointbpkcount ).to.be.above( 30 )
|
|
493
|
+
|
|
494
|
+
await finished
|
|
495
|
+
|
|
496
|
+
} )
|
|
497
|
+
|
|
435
498
|
it( "mix 3 channels - 1 writer 3 readers", async function() {
|
|
436
499
|
|
|
437
500
|
this.timeout( 3000 )
|