UNPKG

rtp.js

Version:

RTP stack for Node.js and browser written in TypeScript

262 lines (261 loc) 9.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CompoundPacket = void 0; const Packet_1 = require("../Packet"); const RtcpPacket_1 = require("./RtcpPacket"); const ReceiverReportPacket_1 = require("./ReceiverReportPacket"); const SenderReportPacket_1 = require("./SenderReportPacket"); const ByePacket_1 = require("./ByePacket"); const SdesPacket_1 = require("./SdesPacket"); const FeedbackPacket_1 = require("./FeedbackPacket"); const NackPacket_1 = require("./NackPacket"); const SrReqPacket_1 = require("./SrReqPacket"); const EcnPacket_1 = require("./EcnPacket"); const PliPacket_1 = require("./PliPacket"); const SliPacket_1 = require("./SliPacket"); const RpsiPacket_1 = require("./RpsiPacket"); const GenericFeedbackPacket_1 = require("./GenericFeedbackPacket"); const XrPacket_1 = require("./XrPacket"); const ExtendedJitterReportsPacket_1 = require("./ExtendedJitterReportsPacket"); const GenericPacket_1 = require("./GenericPacket"); /** * RTCP Compound packet. * * @category RTCP * * @see * - [RFC 3550](https://datatracker.ietf.org/doc/html/rfc3550) */ class CompoundPacket extends Packet_1.Packet { // RTCP packets. #packets = []; /** * @param view - If given it will be parsed. Otherwise an empty RTCP Compound * packet will be created. * * @throws * - If given `view` does not contain a valid RTCP Compound packet. */ constructor(view) { super(view); if (this.view && !(0, RtcpPacket_1.isRtcp)(this.view)) { throw new TypeError('not a RTCP compound packet'); } if (!this.view) { this.view = new DataView(new ArrayBuffer(0)); return; } // Position relative to the DataView byte offset. let pos = 0; // Parse all RTCP packets. while (pos < this.view.byteLength) { const remainingView = new DataView(this.view.buffer, this.view.byteOffset + pos, this.view.byteLength - pos); const packetLength = (0, RtcpPacket_1.getRtcpLength)(remainingView); const packetView = new DataView(this.view.buffer, this.view.byteOffset + pos, packetLength); let packet; switch ((0, RtcpPacket_1.getRtcpPacketType)(remainingView)) { case RtcpPacket_1.RtcpPacketType.RR: { packet = new ReceiverReportPacket_1.ReceiverReportPacket(packetView); break; } case RtcpPacket_1.RtcpPacketType.SR: { packet = new SenderReportPacket_1.SenderReportPacket(packetView); break; } case RtcpPacket_1.RtcpPacketType.BYE: { packet = new ByePacket_1.ByePacket(packetView); break; } case RtcpPacket_1.RtcpPacketType.SDES: { packet = new SdesPacket_1.SdesPacket(packetView); break; } case RtcpPacket_1.RtcpPacketType.RTPFB: { switch ((0, FeedbackPacket_1.getRtcpFeedbackMessageType)(packetView)) { case FeedbackPacket_1.RtpFeedbackMessageType.NACK: { packet = new NackPacket_1.NackPacket(packetView); break; } case FeedbackPacket_1.RtpFeedbackMessageType.SR_REQ: { packet = new SrReqPacket_1.SrReqPacket(packetView); break; } case FeedbackPacket_1.RtpFeedbackMessageType.ECN: { packet = new EcnPacket_1.EcnPacket(packetView); break; } default: { packet = new GenericFeedbackPacket_1.GenericFeedbackPacket(packetView); } } break; } case RtcpPacket_1.RtcpPacketType.PSFB: { switch ((0, FeedbackPacket_1.getRtcpFeedbackMessageType)(packetView)) { case FeedbackPacket_1.PsFeedbackMessageType.PLI: { packet = new PliPacket_1.PliPacket(packetView); break; } case FeedbackPacket_1.PsFeedbackMessageType.SLI: { packet = new SliPacket_1.SliPacket(packetView); break; } case FeedbackPacket_1.PsFeedbackMessageType.RPSI: { packet = new RpsiPacket_1.RpsiPacket(packetView); break; } default: { packet = new GenericFeedbackPacket_1.GenericFeedbackPacket(packetView); } } break; } case RtcpPacket_1.RtcpPacketType.XR: { packet = new XrPacket_1.XrPacket(packetView); break; } case RtcpPacket_1.RtcpPacketType.IJ: { packet = new ExtendedJitterReportsPacket_1.ExtendedJitterReportsPacket(packetView); break; } default: { packet = new GenericPacket_1.GenericPacket(packetView); } } pos += packetLength; this.#packets.push(packet); } // 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 Compound packet info. */ dump() { return { ...super.dump(), packets: this.#packets.map(packet => packet.dump()), }; } /** * @inheritDoc */ getByteLength() { if (!this.needsSerialization()) { return this.view.byteLength; } const packetLength = this.#packets.reduce((sum, packet) => sum + packet.getByteLength(), 0); return packetLength; } /** * Not implemented in RTCP Compound packet. * * @hidden */ getPadding() { return 0; } /** * Not implemented in RTCP Compound packet. * * @hidden */ padTo4Bytes() { throw new Error('method not implemented in RTCP CompoundPacket'); } /** * @inheritDoc */ needsSerialization() { return (super.needsSerialization() || this.#packets.some(packet => packet.needsSerialization())); } /** * @inheritDoc */ serialize(buffer, byteOffset) { const bufferData = this.getSerializationBuffer(buffer, byteOffset); // Create new DataView with new buffer. const view = new DataView(bufferData.buffer, bufferData.byteOffset, bufferData.byteLength); // Position relative to the DataView byte offset. let pos = 0; for (const packet of this.#packets) { packet.serialize(view.buffer, view.byteOffset + pos); pos += packet.getByteLength(); } // 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; this.setSerializationNeeded(false); } /** * @inheritDoc */ clone(buffer, byteOffset, serializationBuffer, serializationByteOffset) { const view = this.cloneInternal(buffer, byteOffset, serializationBuffer, serializationByteOffset); return new CompoundPacket(view); } /** * Return the {@link RtcpPacket} entries in this RTCP Compound packet. * * @remarks * - The returned value is an array of {@link RtcpPacket}, which is an * abstract class. * - By inspecting {@link RtcpPacket.getPacketType} we can cast each packet * to its specific class. * * @example * ```ts * import { packets } from 'rtp.js'; * const { CompoundPacket, RtcpPacketType, SdesPacket } = packets; * * const compoundPacket = new CompoundPacket(view); * * for (const packet of compoundPacket.getPackets()) * { * switch (packet.getPacketType()) * { * case RtcpPacketType.SDES: * { * const sdesPacket = packet as SdesPacket; * * console.log(sdesPacket.getChunks()); * * break; * } * * // etc. * } * } * ``` */ getPackets() { return Array.from(this.#packets); } /** * Set the {@link RtcpPacket} entries in this RTCP Compound packet. * * @remarks * - Serialization is needed after calling this method. */ setPackets(packets) { this.#packets = Array.from(packets); this.setSerializationNeeded(true); } /** * Add a new {@link RtcpPacket} at the end of this RTCP Compound packet. * * @remarks * - Serialization is needed after calling this method. */ addPacket(packet) { this.#packets.push(packet); this.setSerializationNeeded(true); } } exports.CompoundPacket = CompoundPacket;