2222 */
2323
2424
25- import alawmulaw from 'alawmulaw' ; // For now, move to codec.mjs later.
25+ import dgram from 'dgram' ;
26+ import { Codec } from './codec.mjs' ;
2627
2728const Payload = {
2829 Mulaw8K : 0 ,
@@ -136,10 +137,9 @@ class RTPHeader {
136137
137138 // var payloadType = Payload[findPayload(payloadType)] // Brutish validation to check supported Payload type. Not sure we're there yet.
138139 var payloadSize = ( packet . slice ( lengthOfHeader ) . length ) ;
139-
140- var sequenceNo = parseInt ( packet . slice ( 2 , 4 ) ) ;
141- var timeStamp = parseInt ( packet . slice ( 4 , 8 ) ) ;
142- var ssrc = parseInt ( packet . slice ( 8 , 12 ) ) ;
140+ var sequenceNo = packet . slice ( 2 , 4 ) . readUInt16BE ( ) ;
141+ var timeStamp = packet . slice ( 4 , 8 ) . readUInt16BE ( ) ;
142+ var ssrc = packet . slice ( 8 , 12 ) . readUInt16BE ( ) ;
143143
144144 var header = new RTPHeader ( payloadSize , payloadType , ssrc ) ;
145145 header . forceHeader ( sequenceNo , timeStamp ) ;
@@ -217,13 +217,7 @@ class RTPHeader {
217217 class RTPPacket {
218218 constructor ( header , payload = undefined ) {
219219 this . header = header ;
220-
221- if ( this . header . payloadType == Payload . Mulaw8K ) {
222- this . _payload = alawmulaw . mulaw . decode ( payload ) ; // For now, brute force the transcode, since we know we're working in mulaw.
223- } else {
224- console . log ( this . header ) ;
225- throw "Not Mulaw, unsure what to do!" ;
226- }
220+ this . _payload = payload ;
227221 }
228222
229223 /**
@@ -275,14 +269,92 @@ class RTPHeader {
275269}
276270
277271/**
278- * Temporary class - replace with RTPPacket instances when you work out how.
272+ * Should provide a source and sink for RTP Packets. For now, hardcode to the correct values for Combadge audio.
273+ *
274+ * Down the line, we should generalise this to allow the library to be reused elsewhere -
275+ * (since I wouldn't be writing it if there was another approachable JS RTP library)
279276 */
280- class RTPStream {
281- constructor ( ) {
277+ class RTPServer {
278+ constructor ( listenAddress , portNumber , consumer = undefined ) {
282279 this . sampleCount = new Number ( 160 ) ; // At 8 bits/sample, this can be used for both incrementing the timestamp AND counting bytes.
283280 this . header = new RTPHeader ( this . sampleCount ) ;
281+ this . _consumer = undefined ;
282+
283+ if ( portNumber % 2 != 0 ) {
284+ throw `RTP Ports must be even. Odd-numbered ports are reserved for RTCP. Invalid port ${ portNumber } passed.` ;
285+ }
286+
287+ if ( consumer ) {
288+ this . _consumer = consumer ;
289+ }
290+
291+ this . udpServer = dgram . createSocket ( 'udp4' ) ;
292+ this . udpServer . bind ( portNumber , listenAddress )
293+ this . udpServer . on ( 'listening' , ( ) => {
294+ const address = this . udpServer . address ( ) ;
295+ console . log ( `RTP Server spawned at ${ address . address } :${ address . port } ` ) ;
296+ } )
297+ this . udpServer . on ( 'message' , ( message , clientInfo ) => {
298+ var packet = RTPPacket . from ( message ) ;
299+ this . receivePacket ( packet ) ;
300+ } )
301+
302+ /*
303+ this.transcoding = true;
304+ var floatSamples=Float32Array.from(Float32Array.from(this.recSamples).map(x=>x/0x8000));
305+ var newSamples = waveResampler.resample(floatSamples, 8000, 16000); // RETURNS A FLOAT64 NOT AN INT16 READ THE DOCS
306+ var samples1616=Int16Array.from(newSamples.map(x => (x>0 ? x*0x7FFF : x*0x8000)));
307+ var wav16buffer = new Buffer.from(samples1616.buffer);
308+ console.log("Result:", model.stt(wav16buffer));
309+ this.transcoding = false;
310+ */
284311 }
285312
313+ /**
314+ * Function to pass in a function to receive audio from the server.
315+ */
316+ set consumer ( consumer ) {
317+ this . _consumer = consumer ;
318+ }
319+
320+ get consumer ( ) {
321+ return this . _consumer ;
322+ }
323+
324+ /** Do something with the packet, then forward it to the Consumer if set, or cache it if not. */
325+ receivePacket ( packet ) {
326+ var samples = Codec . resampleAudio ( packet . payload ) ;
327+ this . consumer . receiveSamples ( samples ) ;
328+ }
329+
330+ /**
331+ * Queue audio to be sent to a remote UDP target.
332+ */
333+ queueAudio ( audio ) {
334+
335+ }
336+
337+ /**
338+ * Register a recipient to receive audio that's passed to queueAudio - basically, a remote target for an RTP stream.
339+ * Address should be one of my fancy IP address objects, which I need to move outside the cccp protocol I think.
340+ *
341+ * @param {* } address
342+ */
343+ registerRemote ( address ) {
344+
345+ }
346+
347+
348+
349+ /**
350+ * Send audio to a remote device. We'll be explicit about target, rather than just using send() from the responder
351+ * because otherwise I can already see an exploit where a snooper sends 0-length packets to ensure they're always the most recent address.
352+ * socket.send(msg[, offset, length][, port][, address][, callback])
353+ */
354+ sendPacket ( packet , address , port ) {
355+ packetData = packet . toBuffer ( )
356+ this . udpServer . send ( packetData , 0 , packetData . length , port , address )
357+ }
286358
287359 /**
288360 * Return a completed media packet without transcoding.
@@ -308,25 +380,4 @@ class RTPStream {
308380 }
309381}
310382
311- class MediaQueue {
312- constructor ( ) {
313- this . inQueue = new Array ( ) ;
314- this . outQueue = new Array ( ) ;
315- }
316-
317- /**
318- * Add a packet to the queue for RTP decoding
319- */
320- addReceivedPacket ( packet , callback ) {
321- return null ;
322- }
323-
324- /**
325- * Add a media stream for RTP encoding
326- */
327- addTransmitMedia ( samples , callback ) {
328- return null ;
329- }
330- }
331-
332- export { Payload , RTPHeader , RTPStream , RTPPacket , MediaQueue } ;
383+ export { Payload , RTPHeader , RTPPacket , RTPServer } ;
0 commit comments