Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit 45a4c888a34ff04891530cb0e7b235181acede34 @mscdex mscdex committed Aug 14, 2010
Showing with 217 additions and 0 deletions.
  1. +19 −0 LICENSE
  2. +55 −0 README.md
  3. +26 −0 examples/sendg711.js
  4. +117 −0 lib/rtppacket.js
19 LICENSE
@@ -0,0 +1,19 @@
+Copyright 2010 Brian White. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
@@ -0,0 +1,55 @@
+node-rtp
+========
+
+node-rtp is an RTP module for node.js. It currently only supports sending audio only.
+
+Requirements
+============
+
+- Node.JS v0.1.103+
+
+Example
+=======
+
+See 'examples/sendg711.js' for how to send G.711 audio (you will need to provide the audio file -- the example works with a PCM mu-law encoded audio file).
+Currently, you will need to find a receiver program/device that plays incoming audio at least until receiving audio is implemented in node-rtp.
+FWIW, I am personally testing node-rtp with a Cisco IP phone (7961G with SIP firmware) over a LAN.
+
+API
+===
+
+node-rtp currently exports one objects: RtpPacket.
+
+## RtpPacket
+
+### Constructor: new RtpPacket(payload)
+
+Creates a new instance of an RTP packet.
+
+`payload` is simply a Buffer containing up to 512 bytes of audio data.
+
+Note: The size of the payload may need to be less than 512 bytes, depending on what encoding you are using.
+
+### type
+
+Gets/Sets the RTP packet's payload type. This must be a valid value from the table given in section 6 of RFC3551.
+
+### seq
+
+Gets/Sets the RTP packet's sequence number. This number must be incremented by 1 for each RTP packet in a continuous stream. This is useful for the receiver to detect if it has missed any packets in the stream.
+
+### time
+
+Gets/Sets the RTP packet's timestamp. This number must be incremented by the number of samples contained in the payload (generally the length of the payload buffer) for each RTP packet in a continuous stream.
+
+### source
+
+Gets/Sets the RTP packet's synchronization source identifier. This number must be a unique number that identifies the source of the outgoing audio.
+
+### payload
+
+Gets/Sets the RTP packet's payload. This is a buffer object containing audio samples.
+
+### packet
+
+Returns the fully assembled RTP packet as a buffer object for sending over the network.
@@ -0,0 +1,26 @@
+var fs = require('fs'), udp = require('dgram'), Buffer = require('buffer').Buffer, RtpPacket = require('../lib/rtppacket').RtpPacket,
+ fd, sock, rtp, intvl, buf, bytesRead, ip, port,
+ writeData = function() {
+ if ((bytesRead = fs.readSync(fd, buf, 0, buf.length)) > 0) {
+ if (!rtp)
+ rtp = new RtpPacket(buf);
+ else
+ rtp.payload = buf;
+ rtp.time += buf.length;
+ rtp.seq++;
+ if (!sock)
+ sock = udp.createSocket('udp4');
+ sock.send(rtp.packet, 0, rtp.packet.length, port, ip);
+ } else {
+ if (intvl)
+ clearInterval(intvl);
+ fs.closeSync(fd);
+ if (sock)
+ sock.close(); // dgram module automatically listens on the port even if we only wanted to send... -_-
+ }
+ };
+ip = "10.1.1.243";
+port = 20480;
+buf = new Buffer(160);
+fd = fs.openSync('audio.g711', 'r');
+intvl = setInterval(writeData, 20);
@@ -0,0 +1,117 @@
+var Buffer = require('buffer').Buffer;
+
+Number.prototype.toUnsigned = function() {
+ return ((this >>> 1) * 2 + (this & 1));
+};
+
+var RtpPacket = exports.RtpPacket = function(bufpayload) {
+ this._bufpkt = null;
+ /* See RFC3550 for more details: http://www.ietf.org/rfc/rfc3550.txt
+ V = 2, // version. always 2 for this RFC (2 bits)
+ P = 0, // padding. not supported yet, so always 0 (1 bit)
+ X = 0, // header extension (1 bit)
+ CC = 0, // CSRC count (4 bits)
+ M = 0, // marker (1 bit)
+ PT = 0, // payload type. see section 6 in RFC3551 for valid types: http://www.ietf.org/rfc/rfc3551.txt (7 bits)
+ SN = Math.floor(1000 * Math.random()), // sequence number. SHOULD be random (16 bits)
+ TS = 1, // timestamp in the format of NTP (# sec. since 0h UTC 1 January 1900)? (32 bits)
+ SSRC = 1; // synchronization source (32 bits)
+ //CSRC = 0, // contributing sources. not supported yet (32 bits)
+ //DP = 0, // header extension, 'Defined By Profile'. not supported yet (16 bits)
+ //EL = 0; // header extension length. not supported yet (16 bits)
+ */
+ if (bufpayload.length > 512) {
+ // full packet (generally an incoming packet straight from the socket)
+ this._bufpkt = bufpayload;
+ /*V = (bufpkt[0] >>> 6 & 0x03);
+ P = (bufpkt[0] >>> 5 & 0x01);
+ X = (bufpkt[0] >>> 4 & 0x01);
+ CC = (bufpkt[0] & 0x0F);
+ M = (bufpkt[1] >>> 7 & 0x01);
+ PT = (bufpkt[1] & 0x7F);
+ SN = (bufpkt[2] << 8 | bufpkt[3]);
+ TS = (bufpkt[4] << 24 | bufpkt[5] << 16 | bufpkt[6] << 8 | bufpkt[7]);
+ SSRC = (bufpkt[8] << 24 | bufpkt[9] << 16 | bufpkt[10] << 8 | bufpkt[11]);*/
+ // bufpkt[12..bufpkg.length-1] == payload data
+ } else {
+ // just payload data (for outgoing/sending)
+ this._bufpkt = new Buffer(12 + bufpayload.length); // V..SSRC + payload
+ /*bufpkt[0] = (V << 6 | P << 5 | X << 4 | CC);
+ bufpkt[1] = (M << 7 | PT);
+ bufpkt[2] = (SN >>> 8)
+ bufpkt[3] = (SN & 0xFF);
+ bufpkt[4] = (TS >>> 24);
+ bufpkt[5] = (TS >>> 16 & 0xFF);
+ bufpkt[6] = (TS >>> 8 & 0xFF);
+ bufpkt[7] = (TS & 0xFF);
+ bufpkt[8] = (SSRC >>> 24);
+ bufpkt[9] = (SSRC >>> 16 & 0xFF);
+ bufpkt[10] = (SSRC >>> 8 & 0xFF);
+ bufpkt[11] = (SSRC & 0xFF);*/
+ this._bufpkt[0] = 0x80;
+ this._bufpkt[1] = 0;
+ var SN = Math.floor(1000 * Math.random());
+ this._bufpkt[2] = (SN >>> 8)
+ this._bufpkt[3] = (SN & 0xFF);
+ this._bufpkt[4] = 0;
+ this._bufpkt[5] = 0;
+ this._bufpkt[6] = 0;
+ this._bufpkt[7] = 1;
+ this._bufpkt[8] = 0;
+ this._bufpkt[9] = 0;
+ this._bufpkt[10] = 0;
+ this._bufpkt[11] = 1;
+ bufpayload.copy(this._bufpkt, 12, 0); // append payload data
+ }
+}
+RtpPacket.prototype.__defineGetter__('type', function() { return (this._bufpkt[1] & 0x7F); });
+RtpPacket.prototype.__defineSetter__('type', function(val) {
+ val = val.toUnsigned();
+ if (val <= 127) {
+ this._bufpkt[1] -= (this._bufpkt[1] & 0x7F);
+ this._bufpkt[1] |= val;
+ }
+});
+RtpPacket.prototype.__defineGetter__('seq', function() { return (this._bufpkt[2] << 8 | this._bufpkt[3]); });
+RtpPacket.prototype.__defineSetter__('seq', function(val) {
+ val = val.toUnsigned();
+ if (val <= 65535) {
+ this._bufpkt[2] = (val >>> 8);
+ this._bufpkt[3] = (val & 0xFF);
+ }
+});
+RtpPacket.prototype.__defineGetter__('time', function() { return (this._bufpkt[4] << 24 | this._bufpkt[5] << 16 | this._bufpkt[6] << 8 | this._bufpkt[7]); });
+RtpPacket.prototype.__defineSetter__('time', function(val) {
+ val = val.toUnsigned();
+ if (val <= 4294967295) {
+ this._bufpkt[4] = (val >>> 24);
+ this._bufpkt[5] = (val >>> 16 & 0xFF);
+ this._bufpkt[6] = (val >>> 8 & 0xFF);
+ this._bufpkt[7] = (val & 0xFF);
+ }
+});
+RtpPacket.prototype.__defineGetter__('source', function() { return (this._bufpkt[8] << 24 | this._bufpkt[9] << 16 | this._bufpkt[10] << 8 | this._bufpkt[11]); });
+RtpPacket.prototype.__defineSetter__('source', function(val) {
+ val = val.toUnsigned();
+ if (val <= 4294967295) {
+ this._bufpkt[8] = (val >>> 24);
+ this._bufpkt[9] = (val >>> 16 & 0xFF);
+ this._bufpkt[10] = (val >>> 8 & 0xFF);
+ this._bufpkt[11] = (val & 0xFF);
+ }
+});
+RtpPacket.prototype.__defineGetter__('payload', function() { return (this._bufpkt.slice(12, this._bufpkt.length)); });
+RtpPacket.prototype.__defineSetter__('payload', function(val) {
+ if (Buffer.isBuffer(val) && val.length <= 512) {
+ var newsize = 12 + val.length;
+ if (this._bufpkt.length == newsize)
+ val.copy(this._bufpkt, 12, 0);
+ else {
+ var newbuf = new Buffer(newsize);
+ this._bufpkt.copy(newbuf, 0, 0, 12);
+ val.copy(newbuf, 12, 0);
+ this._bufpkt = newbuf;
+ }
+ }
+});
+RtpPacket.prototype.__defineGetter__('packet', function() { return this._bufpkt; });

0 comments on commit 45a4c88

Please sign in to comment.