131 lines
4.9 KiB
JavaScript
131 lines
4.9 KiB
JavaScript
/* 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
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"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
|
|
// https://tools.ietf.org/html/rfc3550#section-5.1
|
|
// 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) {
|
|
// https://tools.ietf.org/html/rfc3550#section-5.3.1
|
|
// 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;
|
|
// Check for https://tools.ietf.org/html/rfc5285
|
|
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
|
|
// https://tools.ietf.org/html/rfc5285#section-4.2
|
|
// 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) };
|
|
};
|