UNPKG

rtp.js

Version:

RTP stack for Node.js and browser written in TypeScript

227 lines (226 loc) 7.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DrleExtendedReport = void 0; const ExtendedReport_1 = require("./ExtendedReport"); const helpers_1 = require("../../../utils/helpers"); const bitOps_1 = require("../../../utils/bitOps"); // Common header + SSRC of source + begin seq + end seq. const DRLE_EXTENDED_REPORT_MIN_LENGTH = ExtendedReport_1.COMMON_HEADER_LENGTH + 8; /** * Duplicate RLE Extended Report. * * ```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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | BT=2 | rsvd. | T | block length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | SSRC of source | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | begin_seq | end_seq | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | chunk 1 | chunk 2 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * : ... : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | chunk n-1 | chunk n | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * ``` * * @category RTCP Extended Reports * * @see * - [RFC 3611 section 4.2](https://datatracker.ietf.org/doc/html/rfc3611#section-4.2) */ class DrleExtendedReport extends ExtendedReport_1.ExtendedReport { // Chunks (2 bytes numbers, unparsed). #chunks = []; /** * @param view - If given it will be parsed. Otherwise an empty Duplicate RLE * Extended Report will be created. */ constructor(view) { super(ExtendedReport_1.ExtendedReportType.DRLE, view); if (!this.view) { this.view = new DataView(new ArrayBuffer(DRLE_EXTENDED_REPORT_MIN_LENGTH)); // Write report type. this.writeCommonHeader(); return; } if (this.view.byteLength < DRLE_EXTENDED_REPORT_MIN_LENGTH) { throw new TypeError('wrong byte length for a Duplicate RLE Extended Report'); } // Position relative to the DataView byte offset. let pos = 0; // Move to chunks. pos += DRLE_EXTENDED_REPORT_MIN_LENGTH; while (pos < this.view.byteLength) { const chunk = this.view.getUint16(pos); if (chunk === 0) { break; } this.#chunks.push(chunk); pos += 2; } } /** * Dump Duplicate RLE Extended Report info. */ dump() { return { ...super.dump(), thinning: this.getThinning(), ssrc: this.getSsrc(), beginSeq: this.getBeginSeq(), endSeq: this.getEndSeq(), chunks: this.getChunks(), }; } /** * @inheritDoc */ getByteLength() { if (!this.needsSerialization()) { return this.view.byteLength; } // Common header + SSRC + begin seq + end seq. let reportLength = DRLE_EXTENDED_REPORT_MIN_LENGTH; // Add chunks. reportLength += this.#chunks.length * 2; // The list of chunks must terminate in terminating null chunks, which // basically means padding them to 4 bytes. reportLength = (0, helpers_1.padTo4Bytes)(reportLength); return reportLength; } /** * @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 the fixed header fields after the common header. pos += ExtendedReport_1.COMMON_HEADER_LENGTH; // Copy the rest of the fixed fields into the new buffer. uint8Array.set(new Uint8Array(this.view.buffer, this.view.byteOffset + pos, DRLE_EXTENDED_REPORT_MIN_LENGTH - ExtendedReport_1.COMMON_HEADER_LENGTH), pos); // Move to chunks. pos += DRLE_EXTENDED_REPORT_MIN_LENGTH - ExtendedReport_1.COMMON_HEADER_LENGTH; // Copy chunks. for (const chunk of this.#chunks) { view.setUint16(pos, chunk); pos += 2; } // NOTE: Must pad the content to 4 bytes. pos = (0, helpers_1.padTo4Bytes)(pos); if (pos !== view.byteLength) { throw new RangeError(`filled length (${pos} bytes) does not match 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 DrleExtendedReport(view); } /** * Get thinning. */ getThinning() { return (0, bitOps_1.readBitsInDataView)({ view: this.view, pos: 1, mask: 0x0f }); } /** * Set thinning. */ setThinning(thinning) { (0, bitOps_1.writeBitsInDataView)({ view: this.view, pos: 1, mask: 0x0f, value: thinning, }); this.setSerializationNeeded(true); } /** * Get SSRC of source. */ getSsrc() { return this.view.getUint32(4); } /** * Set SSRC of source. */ setSsrc(ssrc) { this.view.setUint32(4, ssrc); this.setSerializationNeeded(true); } /** * Get begin sequence number. */ getBeginSeq() { return this.view.getUint16(8); } /** * Set begin sequence number. */ setBeginSeq(seq) { this.view.setUint16(8, seq); this.setSerializationNeeded(true); } /** * Get end sequence number. */ getEndSeq() { return this.view.getUint16(10); } /** * Set end sequence number. */ setEndSeq(seq) { this.view.setUint16(10, seq); this.setSerializationNeeded(true); } /** * Get chunks. * * @remarks * - Chunks are given as a list of 2 byte integers. * - Use {@link parseExtendedReportChunk} to parse them. */ getChunks() { return Array.from(this.#chunks); } /** * Set chunks. * * @remarks * - Chunks must be given as a list of 2 byte integers. * - Use {@link createExtendedReportRunLengthChunk} or * {@link createExtendedReportBitVectorChunk} to create them. */ setChunks(chunks) { this.#chunks = Array.from(chunks); this.setSerializationNeeded(true); } /** * Add chunk. * * @remarks * - Chunk must be given as 2 byte integer. * - Use {@link createExtendedReportRunLengthChunk} or * {@link createExtendedReportBitVectorChunk} to create it. * - Given chunk cannot be a terminating null chunk (0 number). */ addChunk(chunk) { if (chunk === 0) { throw new TypeError('cannot add terminating null chunks'); } this.#chunks.push(chunk); this.setSerializationNeeded(true); } } exports.DrleExtendedReport = DrleExtendedReport;