in src/redundantaudioencoder/RedundantAudioEncoder.ts [1100:1340]
private opusPacketParseImpl(
data: DataView,
lenBytes: number,
selfDelimited: boolean,
tocByte: [number],
frameOffsets: Array<[number]>,
frameSizes: Array<[number]>,
payloadOffset: [number],
packetLenBytes: [number]
): number {
if (!frameSizes || lenBytes < 0) return this.OPUS_BAD_ARG;
if (lenBytes === 0) return this.OPUS_INVALID_PACKET;
// The number of Opus frames in the packet.
let numFrames: number;
// Intermediate storage for the number of bytes parsed to determine the size of a frame.
let numBytesParsed: number;
// The number of the padding bytes (excluding the padding count bytes) in the packet.
let paddingBytes = 0;
// Indicates whether CBR (constant bitrate) framing is used.
let cbr = false;
// The TOC (table of contents) byte (https://www.rfc-editor.org/rfc/rfc6716#section-3.1).
const toc = data.getUint8(0);
// Store the TOC byte.
if (tocByte) tocByte[0] = toc;
// The remaining number of bytes to parse from the packet. Note that the TOC byte has already been parsed, hence the
// minus 1.
let remainingBytes = lenBytes - 1;
// This keeps track of where we are in the packet. This starts at 1 since the TOC byte has already been read.
let byteOffset = 1;
// The size of the last Opus frame in bytes.
let lastSizeBytes = remainingBytes;
// Read the `c` bits (i.e. code bits) from the TOC byte.
switch (toc & 0x3) {
// A code 0 packet (https://www.rfc-editor.org/rfc/rfc6716#section-3.2.2) has one frame.
case 0:
numFrames = 1;
break;
// A code 1 packet (https://www.rfc-editor.org/rfc/rfc6716#section-3.2.3) has two CBR (constant bitrate) frames.
case 1:
numFrames = 2;
cbr = true;
if (!selfDelimited) {
// Undelimited code 1 packets must be an even number of data bytes, otherwise the packet is invalid.
if (remainingBytes & 0x1) return this.OPUS_INVALID_PACKET;
// The sizes of both frames are equal (i.e. half of the number of data bytes).
lastSizeBytes = remainingBytes / 2;
// If `lastSizeBytes` is too large, we will catch it later.
frameSizes[0][0] = lastSizeBytes;
}
break;
// A code 2 packet (https://www.rfc-editor.org/rfc/rfc6716#section-3.2.4) has two VBR (variable bitrate) frames.
case 2:
numFrames = 2;
numBytesParsed = this.opusParseSize(data, byteOffset, remainingBytes, frameSizes[0]);
remainingBytes -= numBytesParsed;
// The parsed size of the first frame cannot be larger than the number of remaining bytes in the packet.
if (frameSizes[0][0] < 0 || frameSizes[0][0] > remainingBytes) {
return this.OPUS_INVALID_PACKET;
}
byteOffset += numBytesParsed;
// The size of the second frame is the remaining number of bytes after the first frame.
lastSizeBytes = remainingBytes - frameSizes[0][0];
break;
// A code 3 packet (https://www.rfc-editor.org/rfc/rfc6716#section-3.2.5) has multiple CBR/VBR frames (from 0 to
// 120 ms).
default:
// Code 3 packets must have at least 2 bytes (i.e. at least 1 byte after the TOC byte).
if (remainingBytes < 1) return this.OPUS_INVALID_PACKET;
// Frame count byte format:
// 0
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |v|p| M |
// +-+-+-+-+-+-+-+-+
//
// Read the frame count byte, which immediately follows the TOC byte.
const frameCountByte = data.getUint8(byteOffset++);
--remainingBytes;
// Read the 'M' bits of the frame count byte, which encode the number of frames.
numFrames = frameCountByte & 0x3f;
// The number of frames in a code 3 packet must not be 0.
if (numFrames <= 0) return this.OPUS_INVALID_PACKET;
const samplesPerFrame = this.opusPacketGetSamplesPerFrame(data, 48000);
// A single frame can have at most 2880 samples, which happens in the case where 60ms of 48kHz audio is encoded
// per frame. A code 3 packet cannot contain more than 120ms of audio, so the total number of samples cannot
// exceed 2880 * 2 = 5760.
if (samplesPerFrame * numFrames > 5760) return this.OPUS_INVALID_PACKET;
// Parse padding bytes if the 'p' bit is 1.
if (frameCountByte & 0x40) {
let paddingCountByte: number;
let numPaddingBytes: number;
// Remove padding bytes (including padding count bytes) from the remaining byte count.
do {
// Sanity check that there are enough bytes to parse and remove the padding.
if (remainingBytes <= 0) return this.OPUS_INVALID_PACKET;
// Get the next padding count byte.
paddingCountByte = data.getUint8(byteOffset++);
--remainingBytes;
// If the padding count byte has a value in the range 0...254, then the total size of the padding is the
// value in the padding count byte.
//
// If the padding count byte has value 255, then the total size of the padding is 254 plus the value in the
// next padding count byte. Therefore, keep reading padding count bytes while the value is 255.
numPaddingBytes = paddingCountByte === 255 ? 254 : paddingCountByte;
remainingBytes -= numPaddingBytes;
paddingBytes += numPaddingBytes;
} while (paddingCountByte === 255);
}
// Sanity check that the remaining number of bytes is not negative after removing the padding.
if (remainingBytes < 0) return this.OPUS_INVALID_PACKET;
// Read the 'v' bit (i.e. VBR bit).
cbr = !(frameCountByte & 0x80);
// VBR case
if (!cbr) {
lastSizeBytes = remainingBytes;
// Let M be the number of frames. There will be M - 1 frame length indicators (which can be 1 or 2 bytes)
// corresponding to the lengths of frames 0 to M - 2. The size of the last frame (i.e. frame M - 1) is the
// number of data bytes after the end of frame M - 2 and before the start of the padding bytes.
for (let i = 0; i < numFrames - 1; ++i) {
numBytesParsed = this.opusParseSize(data, byteOffset, remainingBytes, frameSizes[i]);
remainingBytes -= numBytesParsed;
// The remaining number of data bytes must be enough to contain each frame.
if (frameSizes[i][0] < 0 || frameSizes[i][0] > remainingBytes) {
return this.OPUS_INVALID_PACKET;
}
byteOffset += numBytesParsed;
lastSizeBytes -= numBytesParsed + frameSizes[i][0];
}
// Sanity check that the size of the last frame is not negative.
if (lastSizeBytes < 0) return this.OPUS_INVALID_PACKET;
}
// CBR case
else if (!selfDelimited) {
// The size of each frame is the number of data bytes divided by the number of frames.
lastSizeBytes = Math.trunc(remainingBytes / numFrames);
// The number of data bytes must be a non-negative integer multiple of the number of frames.
if (lastSizeBytes * numFrames !== remainingBytes) return this.OPUS_INVALID_PACKET;
// All frames have equal size in the undelimited CBR case.
for (let i = 0; i < numFrames - 1; ++i) {
frameSizes[i][0] = lastSizeBytes;
}
}
}
// Self-delimited framing uses an extra 1 or 2 bytes, immediately preceding the data bytes, to indicate either the
// size of the last frame (for code 0, code 2, and VBR code 3 packets) or the size of all the frames (for code 1 and
// CBR code 3 packets). See https://www.rfc-editor.org/rfc/rfc6716#appendix-B.
if (selfDelimited) {
// The extra frame size byte(s) will always indicate the size of the last frame.
numBytesParsed = this.opusParseSize(
data,
byteOffset,
remainingBytes,
frameSizes[numFrames - 1]
);
remainingBytes -= numBytesParsed;
// There must be enough data bytes for the last frame.
if (frameSizes[numFrames - 1][0] < 0 || frameSizes[numFrames - 1][0] > remainingBytes) {
return this.OPUS_INVALID_PACKET;
}
byteOffset += numBytesParsed;
// For CBR packets, the sizes of all the frames are equal.
if (cbr) {
// There must be enough data bytes for all the frames.
if (frameSizes[numFrames - 1][0] * numFrames > remainingBytes) {
return this.OPUS_INVALID_PACKET;
}
for (let i = 0; i < numFrames - 1; ++i) {
frameSizes[i][0] = frameSizes[numFrames - 1][0];
}
}
// At this point, `lastSizeBytes` contains the size of the last frame plus the size of the extra frame size
// byte(s), so sanity check that `lastSizeBytes` is the upper bound for the size of the last frame.
else if (!(numBytesParsed + frameSizes[numFrames - 1][0] <= lastSizeBytes)) {
return this.OPUS_INVALID_PACKET;
}
}
// Undelimited case
else {
// Because the size of the last packet is not encoded explicitly, it is possible that the size of the last packet
// (or of all the packets, for the CBR case) is larger than maximum frame size.
if (lastSizeBytes > this.OPUS_MAX_FRAME_SIZE_BYTES) return this.OPUS_INVALID_PACKET;
frameSizes[numFrames - 1][0] = lastSizeBytes;
}
// Store the offset to the start of the payload.
if (payloadOffset) payloadOffset[0] = byteOffset;
// Store the offsets to the start of each frame.
for (let i = 0; i < numFrames; ++i) {
if (frameOffsets) frameOffsets[i][0] = byteOffset;
byteOffset += frameSizes[i][0];
}
// Store the length of the Opus packet.
if (packetLenBytes) packetLenBytes[0] = byteOffset + paddingBytes;
return numFrames;
}