├── LICENSE ├── README.md ├── examples └── sendg711.js └── lib └── rtppacket.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010 Brian White. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-rtp 2 | ======== 3 | 4 | node-rtp is an RTP module for node.js. It currently only supports sending audio only. 5 | 6 | Requirements 7 | ============ 8 | 9 | - Node.JS v0.1.103+ 10 | 11 | Example 12 | ======= 13 | 14 | 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). 15 | Currently, you will need to find a receiver program/device that plays incoming audio at least until receiving audio is implemented in node-rtp. 16 | FWIW, I am personally testing node-rtp with a Cisco IP phone (7961G with SIP firmware) over a LAN. 17 | 18 | API 19 | === 20 | 21 | node-rtp currently exports one objects: RtpPacket. 22 | 23 | ## RtpPacket 24 | 25 | ### Constructor: new RtpPacket(payload) 26 | 27 | Creates a new instance of an RTP packet. 28 | 29 | `payload` is simply a Buffer containing up to 512 bytes of audio data. 30 | 31 | Note: The size of the payload may need to be less than 512 bytes, depending on what encoding you are using. 32 | 33 | ### type 34 | 35 | Gets/Sets the RTP packet's payload type. This must be a valid value from the table given in section 6 of RFC3551. 36 | 37 | ### seq 38 | 39 | 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. 40 | 41 | ### time 42 | 43 | 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. 44 | 45 | ### source 46 | 47 | Gets/Sets the RTP packet's synchronization source identifier. This number must be a unique number that identifies the source of the outgoing audio. 48 | 49 | ### payload 50 | 51 | Gets/Sets the RTP packet's payload. This is a buffer object containing audio samples. 52 | 53 | ### packet 54 | 55 | Returns the fully assembled RTP packet as a buffer object for sending over the network. -------------------------------------------------------------------------------- /examples/sendg711.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), udp = require('dgram'), Buffer = require('buffer').Buffer, RtpPacket = require('../lib/rtppacket').RtpPacket, 2 | fd, sock, rtp, intvl, buf, bytesRead, ip, port, 3 | writeData = function() { 4 | if ((bytesRead = fs.readSync(fd, buf, 0, buf.length)) > 0) { 5 | if (!rtp) 6 | rtp = new RtpPacket(buf); 7 | else 8 | rtp.payload = buf; 9 | rtp.time += buf.length; 10 | rtp.seq++; 11 | if (!sock) 12 | sock = udp.createSocket('udp4'); 13 | sock.send(rtp.packet, 0, rtp.packet.length, port, ip); 14 | } else { 15 | if (intvl) 16 | clearInterval(intvl); 17 | fs.closeSync(fd); 18 | if (sock) 19 | sock.close(); // dgram module automatically listens on the port even if we only wanted to send... -_- 20 | } 21 | }; 22 | ip = "10.1.1.243"; 23 | port = 20480; 24 | buf = new Buffer(160); 25 | fd = fs.openSync('audio.g711', 'r'); 26 | intvl = setInterval(writeData, 20); -------------------------------------------------------------------------------- /lib/rtppacket.js: -------------------------------------------------------------------------------- 1 | var Buffer = require('buffer').Buffer; 2 | 3 | Number.prototype.toUnsigned = function() { 4 | return ((this >>> 1) * 2 + (this & 1)); 5 | }; 6 | 7 | var RtpPacket = exports.RtpPacket = function(bufpayload) { 8 | this._bufpkt = null; 9 | /* See RFC3550 for more details: http://www.ietf.org/rfc/rfc3550.txt 10 | V = 2, // version. always 2 for this RFC (2 bits) 11 | P = 0, // padding. not supported yet, so always 0 (1 bit) 12 | X = 0, // header extension (1 bit) 13 | CC = 0, // CSRC count (4 bits) 14 | M = 0, // marker (1 bit) 15 | PT = 0, // payload type. see section 6 in RFC3551 for valid types: http://www.ietf.org/rfc/rfc3551.txt (7 bits) 16 | SN = Math.floor(1000 * Math.random()), // sequence number. SHOULD be random (16 bits) 17 | TS = 1, // timestamp in the format of NTP (# sec. since 0h UTC 1 January 1900)? (32 bits) 18 | SSRC = 1; // synchronization source (32 bits) 19 | //CSRC = 0, // contributing sources. not supported yet (32 bits) 20 | //DP = 0, // header extension, 'Defined By Profile'. not supported yet (16 bits) 21 | //EL = 0; // header extension length. not supported yet (16 bits) 22 | */ 23 | if (bufpayload.length > 512) { 24 | // full packet (generally an incoming packet straight from the socket) 25 | this._bufpkt = bufpayload; 26 | /*V = (bufpkt[0] >>> 6 & 0x03); 27 | P = (bufpkt[0] >>> 5 & 0x01); 28 | X = (bufpkt[0] >>> 4 & 0x01); 29 | CC = (bufpkt[0] & 0x0F); 30 | M = (bufpkt[1] >>> 7 & 0x01); 31 | PT = (bufpkt[1] & 0x7F); 32 | SN = (bufpkt[2] << 8 | bufpkt[3]); 33 | TS = (bufpkt[4] << 24 | bufpkt[5] << 16 | bufpkt[6] << 8 | bufpkt[7]); 34 | SSRC = (bufpkt[8] << 24 | bufpkt[9] << 16 | bufpkt[10] << 8 | bufpkt[11]);*/ 35 | // bufpkt[12..bufpkg.length-1] == payload data 36 | } else { 37 | // just payload data (for outgoing/sending) 38 | this._bufpkt = new Buffer(12 + bufpayload.length); // V..SSRC + payload 39 | /*bufpkt[0] = (V << 6 | P << 5 | X << 4 | CC); 40 | bufpkt[1] = (M << 7 | PT); 41 | bufpkt[2] = (SN >>> 8) 42 | bufpkt[3] = (SN & 0xFF); 43 | bufpkt[4] = (TS >>> 24); 44 | bufpkt[5] = (TS >>> 16 & 0xFF); 45 | bufpkt[6] = (TS >>> 8 & 0xFF); 46 | bufpkt[7] = (TS & 0xFF); 47 | bufpkt[8] = (SSRC >>> 24); 48 | bufpkt[9] = (SSRC >>> 16 & 0xFF); 49 | bufpkt[10] = (SSRC >>> 8 & 0xFF); 50 | bufpkt[11] = (SSRC & 0xFF);*/ 51 | this._bufpkt[0] = 0x80; 52 | this._bufpkt[1] = 0; 53 | var SN = Math.floor(1000 * Math.random()); 54 | this._bufpkt[2] = (SN >>> 8) 55 | this._bufpkt[3] = (SN & 0xFF); 56 | this._bufpkt[4] = 0; 57 | this._bufpkt[5] = 0; 58 | this._bufpkt[6] = 0; 59 | this._bufpkt[7] = 1; 60 | this._bufpkt[8] = 0; 61 | this._bufpkt[9] = 0; 62 | this._bufpkt[10] = 0; 63 | this._bufpkt[11] = 1; 64 | bufpayload.copy(this._bufpkt, 12, 0); // append payload data 65 | } 66 | } 67 | RtpPacket.prototype.__defineGetter__('type', function() { return (this._bufpkt[1] & 0x7F); }); 68 | RtpPacket.prototype.__defineSetter__('type', function(val) { 69 | val = val.toUnsigned(); 70 | if (val <= 127) { 71 | this._bufpkt[1] -= (this._bufpkt[1] & 0x7F); 72 | this._bufpkt[1] |= val; 73 | } 74 | }); 75 | RtpPacket.prototype.__defineGetter__('seq', function() { return (this._bufpkt[2] << 8 | this._bufpkt[3]); }); 76 | RtpPacket.prototype.__defineSetter__('seq', function(val) { 77 | val = val.toUnsigned(); 78 | if (val <= 65535) { 79 | this._bufpkt[2] = (val >>> 8); 80 | this._bufpkt[3] = (val & 0xFF); 81 | } 82 | }); 83 | RtpPacket.prototype.__defineGetter__('time', function() { return (this._bufpkt[4] << 24 | this._bufpkt[5] << 16 | this._bufpkt[6] << 8 | this._bufpkt[7]); }); 84 | RtpPacket.prototype.__defineSetter__('time', function(val) { 85 | val = val.toUnsigned(); 86 | if (val <= 4294967295) { 87 | this._bufpkt[4] = (val >>> 24); 88 | this._bufpkt[5] = (val >>> 16 & 0xFF); 89 | this._bufpkt[6] = (val >>> 8 & 0xFF); 90 | this._bufpkt[7] = (val & 0xFF); 91 | } 92 | }); 93 | RtpPacket.prototype.__defineGetter__('source', function() { return (this._bufpkt[8] << 24 | this._bufpkt[9] << 16 | this._bufpkt[10] << 8 | this._bufpkt[11]); }); 94 | RtpPacket.prototype.__defineSetter__('source', function(val) { 95 | val = val.toUnsigned(); 96 | if (val <= 4294967295) { 97 | this._bufpkt[8] = (val >>> 24); 98 | this._bufpkt[9] = (val >>> 16 & 0xFF); 99 | this._bufpkt[10] = (val >>> 8 & 0xFF); 100 | this._bufpkt[11] = (val & 0xFF); 101 | } 102 | }); 103 | RtpPacket.prototype.__defineGetter__('payload', function() { return (this._bufpkt.slice(12, this._bufpkt.length)); }); 104 | RtpPacket.prototype.__defineSetter__('payload', function(val) { 105 | if (Buffer.isBuffer(val) && val.length <= 512) { 106 | var newsize = 12 + val.length; 107 | if (this._bufpkt.length == newsize) 108 | val.copy(this._bufpkt, 12, 0); 109 | else { 110 | var newbuf = new Buffer(newsize); 111 | this._bufpkt.copy(newbuf, 0, 0, 12); 112 | val.copy(newbuf, 12, 0); 113 | this._bufpkt = newbuf; 114 | } 115 | } 116 | }); 117 | RtpPacket.prototype.__defineGetter__('packet', function() { return this._bufpkt; }); --------------------------------------------------------------------------------