rtp.js
Version:
RTP stack for Node.js and browser written in TypeScript
281 lines (280 loc) • 9.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RtcpPacket = exports.RtcpPacketType = exports.COMMON_HEADER_LENGTH = void 0;
exports.isRtcp = isRtcp;
exports.getRtcpPacketType = getRtcpPacketType;
exports.getRtcpLength = getRtcpLength;
exports.packetTypeToString = packetTypeToString;
const Packet_1 = require("../Packet");
const helpers_1 = require("../../utils/helpers");
const bitOps_1 = require("../../utils/bitOps");
/**
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* header |V=2|P| SC | PT | length |
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*/
exports.COMMON_HEADER_LENGTH = 4;
/**
* RTCP packet types.
*
* @category RTCP
*/
var RtcpPacketType;
(function (RtcpPacketType) {
/**
* Extended Jitter Reports packet.
*/
RtcpPacketType[RtcpPacketType["IJ"] = 195] = "IJ";
/**
* RTCP Sender Report packet.
*/
RtcpPacketType[RtcpPacketType["SR"] = 200] = "SR";
/**
* RTCP Receiver Report packet.
*/
RtcpPacketType[RtcpPacketType["RR"] = 201] = "RR";
/**
* RTCP Sender Report packet.
*/
RtcpPacketType[RtcpPacketType["SDES"] = 202] = "SDES";
/**
* RTCP BYE packet.
*/
RtcpPacketType[RtcpPacketType["BYE"] = 203] = "BYE";
/**
* RTCP APP packet.
*/
RtcpPacketType[RtcpPacketType["APP"] = 204] = "APP";
/**
* RTCP Transport Layer Feedback packet.
*/
RtcpPacketType[RtcpPacketType["RTPFB"] = 205] = "RTPFB";
/**
* RTCP Payload Specific Feedback packet.
*/
RtcpPacketType[RtcpPacketType["PSFB"] = 206] = "PSFB";
/**
* RTCP Extended Report packet.
*/
RtcpPacketType[RtcpPacketType["XR"] = 207] = "XR";
})(RtcpPacketType || (exports.RtcpPacketType = RtcpPacketType = {}));
/**
* Whether the given buffer view could be a valid RTCP packet or not.
*
* @category RTCP
*/
function isRtcp(view) {
const firstByte = view.getUint8(0);
const secondByte = view.getUint8(1);
return (view.byteLength >= exports.COMMON_HEADER_LENGTH &&
// DOC: https://tools.ietf.org/html/draft-ietf-avtcore-rfc5764-mux-fixes
firstByte > 127 &&
firstByte < 192 &&
// RTCP Version must be 2.
firstByte >> 6 === Packet_1.RTP_VERSION &&
// RTCP packet types defined by IANA:
// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-4
// RFC 5761 (RTCP-mux) states this range for secure RTCP/RTP detection.
secondByte >= 192 &&
secondByte <= 223);
}
/**
* Get the RTCP packet type.
*
* @hidden
*/
function getRtcpPacketType(view) {
return view.getUint8(1);
}
/**
* Read the RTCP header length value and compute its size in bytes (including
* first octet).
*
* @hidden
*/
function getRtcpLength(view) {
// As per RFC 3550, this is the length of this RTCP packet in 32-bit words
// minus one, including the header and any padding.
const length = view.getUint16(2);
const byteLength = (length + 1) * 4;
return byteLength;
}
/**
* Writes given length (in bytes) in the RTCP header length field.
*
* @hidden
*/
function setRtcpLength(view, byteLength) {
// RTCP packet byte length must be multiple of 4.
if (byteLength % 4 !== 0) {
throw new RangeError(`RTCP packet byte length must be multiple of 4 but given byte length is ${byteLength} bytes`);
}
const length = byteLength / 4 - 1;
view.setUint16(2, length);
}
/**
* @hidden
*/
function packetTypeToString(packetType) {
switch (packetType) {
case RtcpPacketType.SR: {
return 'Sender Report';
}
case RtcpPacketType.RR: {
return 'Receiver Report';
}
case RtcpPacketType.SDES: {
return 'SDES';
}
case RtcpPacketType.BYE: {
return 'BYE';
}
case RtcpPacketType.APP: {
return 'APP';
}
case RtcpPacketType.RTPFB: {
return 'RTP Feedback';
}
case RtcpPacketType.PSFB: {
return 'PS Feedback';
}
case RtcpPacketType.XR: {
return 'Extended Report';
}
case RtcpPacketType.IJ: {
return 'Extended Jitter Reports';
}
default: {
(0, helpers_1.assertUnreachable)(packetType);
}
}
}
/**
* RTCP packet. Parent class of all RTCP packets.
*
* ```text
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* header |V=2|P| SC | PT | length |
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
* ```
*
* @category RTCP
*
* @see
* - [RFC 3550 section 6.1](https://datatracker.ietf.org/doc/html/rfc3550#section-6.1)
*/
class RtcpPacket extends Packet_1.Packet {
// RTCP packet type.
#packetType;
constructor(packetType, view) {
super(view);
this.#packetType = packetType;
if (this.view) {
if (!isRtcp(this.view)) {
throw new TypeError('not a RTCP packet');
}
else if (this.getPacketType() !== this.#packetType) {
throw new TypeError(`given buffer view is not a RTCP ${packetTypeToString(this.#packetType)} packet`);
}
// RTCP packet byte length must be multiple of 4.
else if (this.view.byteLength % 4 !== 0) {
throw new RangeError(`RTCP packet byte length must be multiple of 4 but given buffer view is ${this.view.byteLength} bytes`);
}
else if (getRtcpLength(this.view) !== this.view.byteLength) {
throw new RangeError(`length in the RTCP header (${getRtcpLength(this.view)} bytes) does not match buffer view length (${this.view.byteLength} bytes)`);
}
// Get padding.
if (this.hasPaddingBit()) {
this.padding = this.view.getUint8(this.view.byteLength - 1);
}
}
}
/**
* Base RTCP packet dump.
*
* @remarks
* - Read the info dump type of each RTCP packet instead.
*/
dump() {
return {
...super.dump(),
packetType: this.getPacketType(),
count: this.getCount(),
};
}
/**
* Get the RTCP packet type.
*/
getPacketType() {
return this.view.getUint8(1);
}
/**
* Get the RTCP header count value.
*
* @remarks
* - Some RTCP packets do not use this byte (the second one in the common
* RTCP header) for counting chunks or items.
*/
getCount() {
return (0, bitOps_1.readBitsInDataView)({ view: this.view, pos: 0, mask: 0b00011111 });
}
writeCommonHeader() {
this.setVersion();
this.setPacketType();
// Update the length field in the RTCP header.
setRtcpLength(this.view, this.view.byteLength);
}
/**
* Set the RTCP header count value.
*
* @privateRemarks
* - This method is not public since users should not manipulate this field
* directly.
* - Also, there is no `count` field in all RTCP packets. For instance, XR
* and APP packets do not have it.
*/
setCount(count) {
(0, bitOps_1.writeBitsInDataView)({
view: this.view,
pos: 0,
mask: 0b00011111,
value: count,
});
}
/**
* Serialize base RTCP packet into a new buffer.
*/
serializeBase(buffer, byteOffset) {
const bufferData = this.getSerializationBuffer(buffer, byteOffset);
// Create new DataView with new buffer.
const view = new DataView(bufferData.buffer, bufferData.byteOffset, bufferData.byteLength);
const uint8Array = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
// Copy the common header into the new buffer.
uint8Array.set(new Uint8Array(this.view.buffer, this.view.byteOffset, exports.COMMON_HEADER_LENGTH), 0);
// Update the length field in the RTCP header.
setRtcpLength(view, view.byteLength);
// Write padding.
if (this.padding > 0) {
if (this.padding > 255) {
throw new TypeError(`padding (${this.padding} bytes) cannot be higher than 255`);
}
view.setUint8(view.byteLength - 1, this.padding);
}
return view;
}
/**
* Set the RTCP packet type.
*
* @privateRemarks
* - This method is not public since users should not manipulate this field
* directly.
*/
setPacketType() {
this.view.setUint8(1, this.#packetType);
}
}
exports.RtcpPacket = RtcpPacket;