in src/redundantaudioencoder/RedundantAudioEncoder.ts [294:433]
private splitEncodings(
primaryTimestamp: number,
frame: ArrayBuffer,
getFecInfo: boolean = false,
primarySequenceNumber: number = undefined
): RedundantAudioEncoder.Encoding[] | null {
// process RED headers (according to RFC 2198)
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |F| block PT | timestamp offset | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// last header
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |0| Block PT |
// +-+-+-+-+-+-+-+-+
const payload = new DataView(frame);
let payloadSizeBytes = payload.byteLength;
let totalPayloadSizeBytes = 0;
let totalHeaderSizeBytes = 0;
let primaryPayloadSizeBytes = 0;
let payloadOffset = 0;
let gotLastBlock = false;
const encodings = new Array<RedundantAudioEncoder.Encoding>();
const redundantEncodingBlockLengths = new Array();
const redundantEncodingTimestamps = new Array();
while (payloadSizeBytes > 0) {
gotLastBlock = (payload.getUint8(payloadOffset) & 0x80) === 0;
if (gotLastBlock) {
// Bits 1 through 7 are payload type
const payloadType = payload.getUint8(payloadOffset) & 0x7f;
// Unexpected payload type. This is a bad packet.
if (payloadType !== this.opusPayloadType) {
return null;
}
totalPayloadSizeBytes += this.redLastHeaderSizeBytes;
totalHeaderSizeBytes += this.redLastHeaderSizeBytes;
// Accumulated block lengths are equal to or larger than the buffer, which means there is no primary block. This
// is a bad packet.
if (totalPayloadSizeBytes >= payload.byteLength) {
return null;
}
primaryPayloadSizeBytes = payload.byteLength - totalPayloadSizeBytes;
break;
} else {
if (payloadSizeBytes < this.redHeaderSizeBytes) {
return null;
}
// Bits 22 through 31 are payload length
const blockLength =
((payload.getUint8(payloadOffset + 2) & 0x03) << 8) + payload.getUint8(payloadOffset + 3);
redundantEncodingBlockLengths.push(blockLength);
const timestampOffset = payload.getUint16(payloadOffset + 1) >> 2;
const timestamp = primaryTimestamp - timestampOffset;
redundantEncodingTimestamps.push(timestamp);
totalPayloadSizeBytes += blockLength + this.redHeaderSizeBytes;
totalHeaderSizeBytes += this.redHeaderSizeBytes;
payloadOffset += this.redHeaderSizeBytes;
payloadSizeBytes -= this.redHeaderSizeBytes;
}
}
// The last block was never found. The packet we received
// does not have a good RED payload.
if (!gotLastBlock) {
// Note that sequence numbers only exist for
// incoming audio frames.
if (primarySequenceNumber !== undefined) {
// This could be a possible padding packet used
// for BWE with a good sequence number.
// Create a dummy encoding to make sure loss values
// are calculated correctly by consuming sequence number.
// Note that for the receive side, we process packets only
// for loss/recovery calculations and forward the original
// packet without changing it even in the error case.
encodings.push({
payload: frame,
isRedundant: false,
seq: primarySequenceNumber,
});
return encodings;
}
// This is a bad packet.
return null;
}
let redundantPayloadOffset = totalHeaderSizeBytes;
for (let i = 0; i < redundantEncodingTimestamps.length; i++) {
const redundantPayloadBuffer = new ArrayBuffer(redundantEncodingBlockLengths[i]);
const redundantPayloadArray = new Uint8Array(redundantPayloadBuffer);
redundantPayloadArray.set(
new Uint8Array(payload.buffer, redundantPayloadOffset, redundantEncodingBlockLengths[i]),
0
);
const encoding: RedundantAudioEncoder.Encoding = {
timestamp: redundantEncodingTimestamps[i],
payload: redundantPayloadBuffer,
isRedundant: true,
};
if (getFecInfo) {
encoding.hasFec = this.opusPacketHasFec(
new DataView(redundantPayloadBuffer),
redundantPayloadBuffer.byteLength
);
}
encodings.push(encoding);
redundantPayloadOffset += redundantEncodingBlockLengths[i];
}
const primaryPayloadOffset = payload.byteLength - primaryPayloadSizeBytes;
const primaryPayloadBuffer = new ArrayBuffer(primaryPayloadSizeBytes);
const primaryArray = new Uint8Array(primaryPayloadBuffer);
primaryArray.set(
new Uint8Array(payload.buffer, primaryPayloadOffset, primaryPayloadSizeBytes),
0
);
const encoding: RedundantAudioEncoder.Encoding = {
timestamp: primaryTimestamp,
payload: primaryPayloadBuffer,
isRedundant: false,
seq: primarySequenceNumber,
};
if (getFecInfo) {
encoding.hasFec = this.opusPacketHasFec(
new DataView(primaryPayloadBuffer),
primaryPayloadBuffer.byteLength
);
}
encodings.push(encoding);
return encodings;
}