private opusPacketParseImpl()

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;
  }