Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
"use strict";
/*
* Parses an RTP packet
* @param buffer an ArrayBuffer that contains the packet
* @return { type: "rtp", header: {...}, payload: a DataView }
*/
var ParseRtpPacket = buffer => {
// DataView.getFooInt returns big endian numbers by default
let view = new DataView(buffer);
// Standard Header Fields
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P|X| CC |M| PT | sequence number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | timestamp |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | synchronization source (SSRC) identifier |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | contributing source (CSRC) identifiers |
// | .... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
let header = {};
let offset = 0;
// Note that incrementing the offset happens as close to reading the data as
// possible. This simplifies ensuring that the number of read bytes and the
// offset increment match. Data may be manipulated between when the offset is
// incremented and before the next read.
let byte = view.getUint8(offset);
offset++;
// Version 2 Bit
header.version = (0xc0 & byte) >> 6;
// Padding 1 Bit
header.padding = (0x30 & byte) >> 5;
// Extension 1 Bit
header.extensionsPresent = (0x10 & byte) >> 4 == 1;
// CSRC count 4 Bit
header.csrcCount = 0xf & byte;
byte = view.getUint8(offset);
offset++;
// Marker 1 Bit
header.marker = (0x80 & byte) >> 7;
// Payload Type 7 Bit
header.payloadType = 0x7f & byte;
// Sequence Number 16 Bit
header.sequenceNumber = view.getUint16(offset);
offset += 2;
// Timestamp 32 Bit
header.timestamp = view.getUint32(offset);
offset += 4;
// SSRC 32 Bit
header.ssrc = view.getUint32(offset);
offset += 4;
// CSRC 32 Bit
header.csrcs = [];
for (let c = 0; c < header.csrcCount; c++) {
header.csrcs.push(view.getUint32(offset));
offset += 4;
}
// Extensions
header.extensions = [];
header.extensionPaddingBytes = 0;
header.extensionsTotalLength = 0;
if (header.extensionsPresent) {
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | defined by profile | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | header extension |
// | .... |
let addExtension = (id, len) =>
header.extensions.push({
id,
data: new DataView(buffer, offset, len),
});
let extensionId = view.getUint16(offset);
offset += 2;
// len is in 32 bit units, not bytes
header.extensionsTotalLength = view.getUint16(offset) * 4;
offset += 2;
if (extensionId != 0xbede) {
// No rfc5285
addExtension(extensionId, header.extensionsTotalLength);
offset += header.extensionsTotalLength;
} else {
let expectedEnd = offset + header.extensionsTotalLength;
while (offset < expectedEnd) {
// We only support "one-byte" extension headers ATM
// 0
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// | ID | len |
// +-+-+-+-+-+-+-+-+
byte = view.getUint8(offset);
offset++;
// Check for padding which can occur between extensions or at the end
if (byte == 0) {
header.extensionPaddingBytes++;
continue;
}
let id = (byte & 0xf0) >> 4;
// Check for the FORBIDDEN id (15), dun dun dun
if (id == 15) {
// Ignore bytes until until the end of extensions
offset = expectedEnd;
break;
}
// the length of the extention is len + 1
let len = (byte & 0x0f) + 1;
addExtension(id, len);
offset += len;
}
}
}
return { type: "rtp", header, payload: new DataView(buffer, offset) };
};