private encode()

in src/redundantaudioencoder/RedundantAudioEncoder.ts [438:537]


  private encode(primaryTimestamp: number, primaryPayload: ArrayBuffer): ArrayBuffer | null {
    const primaryPayloadSize = primaryPayload.byteLength;

    // Payload size needs to be valid.
    if (
      primaryPayloadSize === 0 ||
      primaryPayloadSize >= this.maxRedPacketSizeBytes ||
      primaryPayloadSize >= this.maxAudioPayloadSizeBytes
    ) {
      return null;
    }

    const numRedundantEncodings = this.numRedundantEncodings;
    let headerSizeBytes = this.redLastHeaderSizeBytes;
    let payloadSizeBytes = primaryPayloadSize;
    let bytesAvailable = this.maxAudioPayloadSizeBytes - primaryPayloadSize - headerSizeBytes;
    const redundantEncodingTimestamps: Array<number> = new Array();
    const redundantEncodingPayloads: Array<ArrayBuffer> = new Array();

    // If redundancy is disabled then only send the primary payload
    if (this.redundancyEnabled) {
      // Determine how much redundancy we can fit into our packet
      let redundantTimestamp = this.uint32WrapAround(
        primaryTimestamp - this.redPacketizationTime * this.redPacketDistance
      );
      for (let i = 0; i < numRedundantEncodings; i++) {
        // Do not add redundant encodings that are beyond the maximum timestamp offset.
        if (
          this.uint32WrapAround(primaryTimestamp - redundantTimestamp) >= this.maxRedTimestampOffset
        ) {
          break;
        }

        let findTimestamp = redundantTimestamp;
        let encoding = this.encodingHistory.find(e => e.timestamp === findTimestamp);

        if (!encoding) {
          // If not found or not important then look for the previous packet.
          // The current packet may have included FEC for the previous, so just
          // use the previous packet instead provided that it has voice activity.
          findTimestamp = this.uint32WrapAround(redundantTimestamp - this.redPacketizationTime);
          encoding = this.encodingHistory.find(e => e.timestamp === findTimestamp);
        }

        if (encoding) {
          const redundantEncodingSizeBytes = encoding.payload.byteLength;

          // Only add redundancy if there are enough bytes available.
          if (bytesAvailable < this.redHeaderSizeBytes + redundantEncodingSizeBytes) break;

          bytesAvailable -= this.redHeaderSizeBytes + redundantEncodingSizeBytes;
          headerSizeBytes += this.redHeaderSizeBytes;
          payloadSizeBytes += redundantEncodingSizeBytes;
          redundantEncodingTimestamps.unshift(encoding.timestamp);
          redundantEncodingPayloads.unshift(encoding.payload);
        }
        redundantTimestamp -= this.redPacketizationTime * this.redPacketDistance;
        redundantTimestamp = this.uint32WrapAround(redundantTimestamp);
      }
    }

    const redPayloadBuffer = new ArrayBuffer(headerSizeBytes + payloadSizeBytes);
    const redPayloadView = new DataView(redPayloadBuffer);

    // Add redundant encoding header(s) to new buffer
    let redPayloadOffset = 0;
    for (let i = 0; i < redundantEncodingTimestamps.length; i++) {
      const timestampDelta = primaryTimestamp - redundantEncodingTimestamps[i];
      redPayloadView.setUint8(redPayloadOffset, this.opusPayloadType | 0x80);
      redPayloadView.setUint16(
        redPayloadOffset + 1,
        (timestampDelta << 2) | (redundantEncodingPayloads[i].byteLength >> 8)
      );
      redPayloadView.setUint8(redPayloadOffset + 3, redundantEncodingPayloads[i].byteLength & 0xff);
      redPayloadOffset += this.redHeaderSizeBytes;
    }

    // Add primary encoding header to new buffer
    redPayloadView.setUint8(redPayloadOffset, this.opusPayloadType);
    redPayloadOffset += this.redLastHeaderSizeBytes;

    // Add redundant payload(s) to new buffer
    const redPayloadArray = new Uint8Array(redPayloadBuffer);
    for (let i = 0; i < redundantEncodingPayloads.length; i++) {
      redPayloadArray.set(new Uint8Array(redundantEncodingPayloads[i]), redPayloadOffset);
      redPayloadOffset += redundantEncodingPayloads[i].byteLength;
    }

    // Add primary payload to new buffer
    redPayloadArray.set(new Uint8Array(primaryPayload), redPayloadOffset);
    redPayloadOffset += primaryPayload.byteLength;

    /* istanbul ignore next */
    // Sanity check that we got the expected total payload size.
    if (redPayloadOffset !== headerSizeBytes + payloadSizeBytes) return null;

    this.updateEncodingHistory(primaryTimestamp, primaryPayload);

    return redPayloadBuffer;
  }