export default class RedundantAudioEncoder { private readonly maxRedPacketSizeBytes; private readonly maxAudioPayloadSizeBytes; private readonly maxRedTimestampOffset; private readonly redHeaderSizeBytes; private readonly redLastHeaderSizeBytes; private readonly redPacketizationTime; private readonly redPacketDistance; private readonly maxRedEncodings; private readonly redMaxRecoveryDistance; private readonly maxEncodingHistorySize; private redPayloadType; private opusPayloadType; private numRedundantEncodings; private encodingHistory; private redundancyEnabled; static shouldLog: boolean; static shouldReportStats: boolean; constructor(); /** * Creates an instance of RedundantAudioEncoder and sets up callbacks. */ static initializeWorker(): void; /** * Post logs to the main thread */ static log(msg: string): void; /** * Returns the number of encodings based on packetLoss value. This is used by `DefaultTransceiverController` to * determine when to alert the encoder to update the number of encodings. It also determines if we need to * turn off red in cases of very high packet loss to avoid congestion collapse. */ static getNumRedundantEncodingsForPacketLoss(packetLoss: number): [number, boolean]; /** * Sets up a passthrough (no-op) transform for the given streams. */ setupPassthroughTransform(readable: ReadableStream, writable: WritableStream): void; /** * Sets up the transform stream and pipes the outgoing encoded audio frames through the transform function. */ setupSenderTransform(readable: ReadableStream, writable: WritableStream): void; /** * Sets up the transform stream and pipes the received encoded audio frames through the transform function. */ setupReceiverTransform(readable: ReadableStream, writable: WritableStream): void; /** * Set the RED payload type ideally obtained from local offer. */ setRedPayloadType(payloadType: number): void; /** * Set the opus payload type ideally obtained from local offer. */ setOpusPayloadType(payloadType: number): void; /** * Set the number of redundant encodings */ setNumRedundantEncodings(numRedundantEncodings: number): void; /** * Enable or disable redundancy in response to * high packet loss event. */ setRedundancyEnabled(enabled: boolean): void; /** * Helper function to only enqueue audio frames if they do not exceed the audio payload byte limit imposed by * Chromium-based browsers. Chromium will throw an error (https://crbug.com/1248479) if an audio payload larger than * 1000 bytes is enqueued. Any controller that attempts to enqueue an audio payload larger than 1000 bytes will * encounter this error and will permanently stop sending or receiving audio. */ private enqueueAudioFrameIfPayloadSizeIsValid; /** * Receives encoded frames and modifies as needed before sending to transport. */ private senderTransform; /** * Get the primary payload from encoding */ private getPrimaryPayload; /** * Split up the encoding received into primary and redundant encodings * These will be ordered oldest to newest which is the same ordering * in the RTP red payload. */ private splitEncodings; /** * Create a new encoding with current primary payload and the older payloads of choice. */ private encode; /** * Update the encoding history with the latest primary encoding */ private updateEncodingHistory; private primaryPacketLog; private redRecoveryLog; private fecRecoveryLog; private newestSequenceNumber; private totalAudioPacketsExpected; private totalAudioPacketsLost; private totalAudioPacketsRecoveredRed; private totalAudioPacketsRecoveredFec; private lastLossReportTimestamp; private readonly lossReportInterval; private readonly maxOutOfOrderPacketDistance; /** * Initialize packet logs and metric values. */ private initializePacketLogs; /** * Receives encoded frames from the server * and adds the timestamps to a packet log * to calculate an approximate recovery metric. */ private receivePacketLogTransform; /** * Adds a timestamp to the primary packet log. * This also updates totalAudioPacketsLost and totalAudioPacketsExpected by looking * at the difference between timestamps. * * @param encoding : The encoding to be analyzed * @returns false if sequence number was greater than max out of order distance * true otherwise */ private updateLossStats; /** * Adds a timestamp to the red recovery log if it is not present in * the primary packet log and if it's not too old. * * @param encoding : The encoding to be analyzed */ private updateRedStats; /** * Adds a timestamp to the fec recovery log if it is not present in * the primary packet log and red recovery log and if it is not too old. * * @param encoding : The encoding to be analyzed */ private updateFecStats; /** * Reports loss metrics to DefaultTransceiverController * * @param timestamp : Timestamp of most recent primary packet */ private maybeReportLossStats; /** * Adds a timestamp to a packet log * * @param packetLog : The packetlog to add the timestamp to * @param timestamp : The timestamp that should be added */ private addTimestamp; /** * Checks if a timestamp is in a packetlog * * @param packetLog : The packetlog to search * @param timestamp : The timestamp to search for * @returns true if timestamp is present, false otherwise */ private hasTimestamp; /** * Removes a timestamp from a packet log * * @param packetLog : The packetlog from which the timestamp should be removed * @param timestamp : The timestamp to be removed * @returns true if timestamp was present in the log and removed, false otherwise */ private removeTimestamp; /** * Removes a timestamp from red and fec recovery windows. * * @param timestamp : The timestamp to be removed */ private removeFromRecoveryWindows; /** * Converts the supplied argument to 32-bit unsigned integer */ private uint32WrapAround; /** * Converts the supplied argument to 16-bit signed integer */ private int16; /** * Below are Opus helper methods and constants. */ private readonly OPUS_BAD_ARG; private readonly OPUS_INVALID_PACKET; private readonly OPUS_MAX_OPUS_FRAMES; private readonly OPUS_MAX_FRAME_SIZE_BYTES; /** * Determines if an Opus packet is in CELT-only mode. * * @param packet Opus packet. * @returns `true` if the packet is in CELT-only mode. */ private opusPacketIsCeltOnly; /** * Gets the number of samples per frame from an Opus packet. * * @param packet Opus packet. This must contain at least one byte of data. * @param sampleRateHz 32-bit integer sampling rate in Hz. This must be a multiple of 400 or inaccurate results will * be returned. * @returns Number of samples per frame. */ private opusPacketGetSamplesPerFrame; /** * Gets the number of SILK frames per Opus frame. * * @param packet Opus packet. * @returns Number of SILK frames per Opus frame. */ private opusNumSilkFrames; /** * Gets the number of channels from an Opus packet. * * @param packet Opus packet. * @returns Number of channels. */ private opusPacketGetNumChannels; /** * Determine the size (in bytes) of an Opus frame. * * @param packet Opus packet. * @param byteOffset Offset (from the start of the packet) to the byte containing the size information. * @param remainingBytes Remaining number of bytes to parse from the Opus packet. * @param sizeBytes Variable to store the parsed frame size (in bytes). * @returns Number of bytes that were parsed to determine the frame size. */ private opusParseSize; /** * Parse binary data containing an Opus packet into one or more Opus frames. * * @param data Binary data containing an Opus packet to be parsed. The data should begin with the first byte (i.e the * TOC byte) of an Opus packet. Note that the size of the data does not have to equal the size of the * contained Opus packet. * @param lenBytes Size of the data (in bytes). * @param selfDelimited Indicates if the Opus packet is self-delimiting * (https://www.rfc-editor.org/rfc/rfc6716#appendix-B). * @param tocByte Optional variable to store the TOC (table of contents) byte. * @param frameOffsets Optional variable to store the offsets (from the start of the data) to the first bytes of each * Opus frame. * @param frameSizes Required variable to store the sizes (in bytes) of each Opus frame. * @param payloadOffset Optional variable to store the offset (from the start of the data) to the first byte of the * payload. * @param packetLenBytes Optional variable to store the length of the Opus packet (in bytes). * @returns Number of Opus frames. */ private opusPacketParseImpl; /** * Parse a single undelimited Opus packet into one or more Opus frames. * * @param packet Opus packet to be parsed. * @param lenBytes Size of the packet (in bytes). * @param tocByte Optional variable to store the TOC (table of contents) byte. * @param frameOffsets Optional variable to store the offsets (from the start of the packet) to the first bytes of * each Opus frame. * @param frameSizes Required variable to store the sizes (in bytes) of each Opus frame. * @param payloadOffset Optional variable to store the offset (from the start of the packet) to the first byte of the * payload. * @returns Number of Opus frames. */ private opusPacketParse; /** * This function returns the SILK VAD (voice activity detection) information encoded in the Opus packet. For CELT-only * packets that do not have VAD information, it returns -1. * * @param packet Opus packet. * @param lenBytes Size of the packet (in bytes). * @returns 0: no frame had the VAD flag set. * 1: at least one frame had the VAD flag set. * -1: VAD status could not be determined. */ private opusPacketHasVoiceActivity; /** * This method is based on Definition of the Opus Audio Codec * (https://tools.ietf.org/html/rfc6716). Basically, this method is based on * parsing the LP layer of an Opus packet, particularly the LBRR flag. * * @param packet Opus packet. * @param lenBytes Size of the packet (in bytes). * @returns true: packet has fec encoding about previous packet. * false: no fec encoding present. */ private opusPacketHasFec; }