rtp.js
Version:
RTP stack for Node.js and browser written in TypeScript
152 lines (151 loc) • 5.71 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenericPacket = void 0;
const RtcpPacket_1 = require("./RtcpPacket");
/**
* RTCP generic packet.
*
* ```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 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* body | ... |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* : ... :
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ```
*
* @category RTCP
*
* @see
* - [RFC 3550](https://datatracker.ietf.org/doc/html/rfc3550)
*/
class GenericPacket extends RtcpPacket_1.RtcpPacket {
// Buffer view holding the packet body.
#bodyView;
/**
* @param view - If given it will be parsed. Otherwise an empty RTCP generic
* packet will be created.
* @param packetType - If `view` is not given, this parameter must be given.
*
* @throws
* - If given `view` does not contain a valid RTCP generic packet.
*/
constructor(view, packetType) {
super(view ? (0, RtcpPacket_1.getRtcpPacketType)(view) : packetType, view);
if (!view && !packetType) {
throw new TypeError('view or packetType must be given');
}
if (!this.view) {
this.view = new DataView(new ArrayBuffer(RtcpPacket_1.COMMON_HEADER_LENGTH));
// Write version and packet type.
this.writeCommonHeader();
// Set empty body.
this.#bodyView = new DataView(this.view.buffer, this.view.byteOffset + RtcpPacket_1.COMMON_HEADER_LENGTH, 0);
return;
}
// Position relative to the DataView byte offset.
let pos = 0;
// Move to body.
pos += RtcpPacket_1.COMMON_HEADER_LENGTH;
// Get body.
const bodyLength = this.view.byteLength - pos - this.padding;
this.#bodyView = new DataView(this.view.buffer, this.view.byteOffset + pos, bodyLength);
pos += bodyLength + this.padding;
// Ensure that view length and parsed length match.
if (pos !== this.view.byteLength) {
throw new RangeError(`parsed length (${pos} bytes) does not match view length (${this.view.byteLength} bytes)`);
}
}
/**
* Dump RTCP generic packet info.
*/
dump() {
return {
...super.dump(),
bodyLength: this.getBody().byteLength,
};
}
/**
* @inheritDoc
*/
getByteLength() {
if (!this.needsSerialization()) {
return this.view.byteLength;
}
const packetLength = RtcpPacket_1.COMMON_HEADER_LENGTH + this.#bodyView.byteLength + this.padding;
return packetLength;
}
/**
* @inheritDoc
*/
serialize(buffer, byteOffset) {
const view = this.serializeBase(buffer, byteOffset);
const uint8Array = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
// Position relative to the DataView byte offset.
let pos = 0;
// Move to body.
pos += RtcpPacket_1.COMMON_HEADER_LENGTH;
// Copy the body into the new buffer.
uint8Array.set(new Uint8Array(this.#bodyView.buffer, this.#bodyView.byteOffset, this.#bodyView.byteLength), pos);
// Create new body DataView.
const bodyView = new DataView(view.buffer, view.byteOffset + pos, this.#bodyView.byteLength);
pos += bodyView.byteLength;
pos += this.padding;
// Assert that current position is equal than new buffer length.
if (pos !== view.byteLength) {
throw new RangeError(`filled length (${pos} bytes) is different than the available buffer size (${view.byteLength} bytes)`);
}
// Update DataView.
this.view = view;
// Update body DataView.
this.#bodyView = bodyView;
this.setSerializationNeeded(false);
}
/**
* @inheritDoc
*/
clone(buffer, byteOffset, serializationBuffer, serializationByteOffset) {
const view = this.cloneInternal(buffer, byteOffset, serializationBuffer, serializationByteOffset);
return new GenericPacket(view);
}
/**
* Set the RTCP header count value.
*
* @remarks
* - This field (the 5 less significant bits in the first byte of the common
* RTCP header) can be used for other custom purpose in case the packet
* needs it for something else.
*
* @privateRemarks
* - This method is made public for this class since the user is free to add
* whatever body to this packet, and hence the user may want to also
* manipulate this field.
*/
setCount(count) {
super.setCount(count);
}
/**
* Get the packet body.
*/
getBody() {
return this.#bodyView;
}
/**
* Set the packet body.
*
* @remarks
* - Serialization is needed after calling this method.
*/
setBody(view) {
this.#bodyView = view;
// We must set the flag first because padTo4Bytes() will call getByteLength()
// which needs that flag set in order to compute new length.
this.setSerializationNeeded(true);
// Ensure body is padded to 4 bytes.
this.padTo4Bytes();
}
}
exports.GenericPacket = GenericPacket;