Files
UnrealEngine/Engine/Source/Runtime/RadAudioCodec/SDK/Src/RadA/rada_decode.cpp
2025-05-18 13:04:45 +08:00

723 lines
28 KiB
C++

// Copyright Epic Games Tools, LLC. All Rights Reserved.
#include "rada_decode.h"
#include "radaudio_decoder.h"
#include <string.h>
struct RadAContainer
{
RadAFileHeader Header;
//
// The memory layout is:
// Container
// Decoder Pointers
// Decoder Memory
// Seek header, if present
// SeekTable, if present
size_t OffsetToSeekHeader;
};
static uint8_t Container_StreamCount(RadAContainer* rada) { return (rada->Header.channels + 1) >> 1; }
static RadAudioDecoder** Container_Decoders(RadAContainer* rada) { return (RadAudioDecoder**)(rada + 1); }
static RadAudioDecoder** Container_Decoders(const RadAContainer* rada) { return (RadAudioDecoder**)(rada + 1); }
static uint8_t* Container_DecoderMemory(RadAContainer* rada)
{
return (uint8_t*)(Container_Decoders(rada) + Container_StreamCount(rada));
}
static RadASeekTableHeader* Container_SeekHeader(RadAContainer* rada)
{
return rada->OffsetToSeekHeader ? (RadASeekTableHeader*)((uint8_t*)rada + rada->OffsetToSeekHeader) : 0;
}
static const RadASeekTableHeader* Container_SeekHeader(const RadAContainer* rada)
{
return rada->OffsetToSeekHeader ? (const RadASeekTableHeader*)((uint8_t*)rada + rada->OffsetToSeekHeader) : 0;
}
static uint8_t* Container_SeekTable(RadAContainer* rada)
{
return rada->OffsetToSeekHeader ? (uint8_t*)(Container_SeekHeader(rada) + 1) : 0;
}
static const uint8_t* Container_SeekTable(const RadAContainer* rada)
{
return rada->OffsetToSeekHeader ? (const uint8_t*)(Container_SeekHeader(rada) + 1) : 0;
}
// If positive: the bytes necessary in order to advance (might take multiple passes to succeed)
// If 0: Success
// If negative: error
const RadAFileHeader* RadAGetFileHeader(const uint8_t* InData, size_t InDataSize)
{
if (InDataSize < sizeof(RadAFileHeader))
return 0;
if (InData == 0)
return 0;
const RadAFileHeader* FileHeader = (const RadAFileHeader*)InData;
// Check file type...
if (FileHeader->tag != 'RADA')
return 0;
// Check version
if (FileHeader->version > 1)
return 0;
if (FileHeader->channels > RADA_MAX_CHANNELS)
return 0;
return FileHeader;
}
const RadASeekTableHeader* RadAGetSeekTableHeader(const uint8_t* InData, size_t InDataSize)
{
const RadAFileHeader* FileHeader = RadAGetFileHeader(InData, InDataSize);
if (FileHeader == 0 || FileHeader->seek_table_entry_count == 0)
return 0;
if (InDataSize < (sizeof(RadAFileHeader) + sizeof(RadASeekTableHeader)))
return 0;
return (const RadASeekTableHeader*)(FileHeader + 1);
}
static size_t align8(size_t n) { return (n + 7) & ~7; }
size_t RadAStripSeekTable(uint8_t* InData, size_t InDataSize)
{
RadAFileHeader* FileHeader = (RadAFileHeader*)RadAGetFileHeader(InData, InDataSize);
if (FileHeader == 0 || InDataSize != FileHeader->file_size) // we must have the whole file.
return 0;
if (FileHeader->seek_table_entry_count == 0)
return InDataSize;
// Layout is:
// FileHeader
// SeekHeader
// StreamHeaders
// SeekTable
// EncodedBlocks
//
// want:
//
// FileHeader
// StreamHeaders
// EncodedBlocks
//
// So we move:
// StreamHeaders -> after FileHeader
// EncodedBlocks -> after StreamHeaders
//
size_t OffsetToData = RadAGetBytesToOpen(FileHeader);
size_t BytesToRemove = RadAGetSeekTableSizeOnDisk(FileHeader) + sizeof(RadASeekTableHeader);
size_t EncodedDataSize = InDataSize - OffsetToData;
// StreamHeaders -> after FileHeader
memmove(InData + sizeof(RadAFileHeader), InData + sizeof(RadAFileHeader) + sizeof(RadASeekTableHeader), FileHeader->rada_header_bytes);
// EncodedBlocks -> after StreamHeaders.
memmove(InData + sizeof(RadAFileHeader) + FileHeader->rada_header_bytes, InData + OffsetToData, EncodedDataSize);
// Update size.
FileHeader->seek_table_entry_count = 0;
FileHeader->file_size -= BytesToRemove;
return InDataSize - BytesToRemove;
}
int32_t RadAGetMemoryNeededToOpen(const uint8_t* InData, size_t InDataSize, uint32_t* OutMemoryRequired)
{
if (OutMemoryRequired == 0)
return -1;
if (InDataSize < sizeof(RadAFileHeader))
return sizeof(RadAFileHeader);
// We need to ask the codec how much memory they need, which means we need the
// headers.
// we need memory for:
// -> The actual codec memory
// -> The pointers to our codec memory.
// -> The container structure
// -> The seek table + header.
const RadAFileHeader* FileHeader = (const RadAFileHeader*)InData;
uint8_t StreamCount = (FileHeader->channels + 1) >> 1;
const RadASeekTableHeader* SeekHeader = FileHeader->seek_table_entry_count ? (const RadASeekTableHeader*)(FileHeader + 1) : 0;
// Stream headers are after the seek header if it exists, otherwise the file header.
const uint8_t* StreamHeaders = SeekHeader ? (const uint8_t*)(SeekHeader + 1) : (const uint8_t*)(FileHeader + 1);
// Check if we have enough of InData to determine the memory requirements. We need enough data to get to
// the stream headers, plus the actual size of the stream headers.
size_t BytesToStreamHeaders = StreamHeaders - InData;
if (BytesToStreamHeaders + FileHeader->rada_header_bytes > InDataSize) // can't overflow - this is just a bunch of sizeofs() and an uint16.
return sizeof(RadAFileHeader) + FileHeader->rada_header_bytes;
size_t RemainingHeaderBytes = FileHeader->rada_header_bytes;
size_t CodecMemoryRequired = 0;
for (uint8_t StreamIndex = 0; StreamIndex < StreamCount; StreamIndex++)
{
if (RemainingHeaderBytes < 64) // min required to parse header
return -1;
CodecMemoryRequired += align8(RadAudioDecoderMemoryRequired((uint8_t*)StreamHeaders, RemainingHeaderBytes));
RadAudioInfo Info = {};
size_t HeaderSizeBytes = RadAudioDecoderGetInfoHeader((uint8_t*)StreamHeaders, RemainingHeaderBytes, &Info);
if (HeaderSizeBytes == 0 ||
HeaderSizeBytes > RemainingHeaderBytes)
return -1;
RemainingHeaderBytes -= HeaderSizeBytes;
StreamHeaders += HeaderSizeBytes;
}
size_t TotalMemoryRequired = sizeof(RadAContainer) + CodecMemoryRequired + sizeof(RadAudioDecoder*) * StreamCount;
if (SeekHeader)
TotalMemoryRequired += sizeof(RadASeekTableHeader) + RadAGetSeekTableSizeOnDisk(FileHeader);
if (TotalMemoryRequired >= ~0U)
return -1;
*OutMemoryRequired = (uint32_t)TotalMemoryRequired;
return 0;
}
int32_t RadAOpenDecoder(const uint8_t* InData, size_t InDataSize, RadAContainer* InContainerPtr, size_t InContainerBytes)
{
if (InContainerBytes < sizeof(RadAContainer))
return 0;
RadAContainer* Container = (RadAContainer*)InContainerPtr;
// We should have enough data to open at this point.
const RadAFileHeader* FileHeader = RadAGetFileHeader(InData, InDataSize);
if (FileHeader == 0)
return 0;
Container->Header = *FileHeader;
if (InDataSize < RadAGetBytesToOpen(&Container->Header))
return 0; // they didn't listen to us.
// The stream headers are right after the file/seek header
uint32_t StreamHeaderBytes = Container->Header.rada_header_bytes;
// sanity the header bytes and read them.
uint8_t StreamCount = Container_StreamCount(Container);
if (StreamHeaderBytes > (uint32_t)(RADAUDIO_STREAM_HEADER_SIZE * StreamCount))
return 0;
uint32_t MemoryRequired = 0;
if (RadAGetMemoryNeededToOpen(InData, InDataSize, &MemoryRequired) != 0 ||
MemoryRequired > InContainerBytes)
{
return 0;
}
uint8_t* ContainerEnd = (uint8_t*)InContainerPtr + InContainerBytes;
RadAudioDecoder** Decomps = Container_Decoders(Container);
uint8_t* StreamMemory = Container_DecoderMemory(Container);
if (StreamMemory + StreamHeaderBytes >= ContainerEnd)
return 0;
uint32_t RemainingHeaderBytes = StreamHeaderBytes;
const uint8_t* HeaderBytes = InData + sizeof(RadAFileHeader) + (Container->Header.seek_table_entry_count ? sizeof(RadASeekTableHeader) : 0);
for (uint8_t stream = 0; stream < StreamCount; stream++)
{
if (RemainingHeaderBytes < 64)
return 0;
size_t codec_bytes = RadAudioDecoderMemoryRequired((uint8_t*)HeaderBytes, RemainingHeaderBytes);
if (StreamMemory + codec_bytes > ContainerEnd)
return 0;
size_t header_bytes_this_stream = 0;
Decomps[stream] = RadAudioDecoderOpen((uint8_t*)HeaderBytes, RemainingHeaderBytes, StreamMemory, codec_bytes, &header_bytes_this_stream);
if (Decomps[stream] == 0 || header_bytes_this_stream > RemainingHeaderBytes)
return 0;
HeaderBytes += header_bytes_this_stream;
RemainingHeaderBytes -= (uint32_t)header_bytes_this_stream;
StreamMemory += align8(codec_bytes);
}
const RadASeekTableHeader* SeekHeader = RadAGetSeekTableHeader(InData, InDataSize);
if (SeekHeader)
{
// Copy over the seek table structures.
Container->OffsetToSeekHeader = (StreamMemory - (uint8_t*)Container);
*Container_SeekHeader(Container) = *SeekHeader;
memcpy(Container_SeekTable(Container), InData + RadAGetOffsetToSeekTable(FileHeader), RadAGetSeekTableSizeOnDisk(FileHeader));
}
if (RemainingHeaderBytes != 0)
return 0;
return 1;
}
static bool BitContainerExtract(const uint8_t* bit_data, size_t bit_data_len_words, uint64_t bit_position, uint32_t bit_count, uint64_t& out_bits)
{
// extract the bits.
uint64_t base_bit = bit_position;
uint64_t base_word = base_bit / 64;
uint64_t base_offset = base_bit - base_word * 64;
if (base_word >= bit_data_len_words)
return false;
uint64_t output = 0;
uint64_t bits_avail = 64 - base_offset;
uint64_t bits_from_first = bit_count;
if (bits_from_first > bits_avail)
bits_from_first = bits_avail;
uint64_t bits_to_clear = 64 - (base_offset + bits_from_first);
uint64_t read_bits;
memcpy(&read_bits, bit_data + base_word*8, 8);
output = (read_bits << bits_to_clear) >> (bits_to_clear + base_offset);
if (bits_from_first != bit_count)
{
// need some from the next word
base_word++;
if (base_word >= bit_data_len_words)
return false;
base_offset = 0;
bits_to_clear = 64 - (bit_count - bits_from_first);
memcpy(&read_bits, bit_data + base_word*8, 8);
output |= ((read_bits << bits_to_clear) >> bits_to_clear) << bits_from_first;
}
out_bits = output;
return true;
}
struct SeekTableEnumerationStateInternal
{
uint64_t consumed_qwords;
uint16_t seek_table_position;
uint8_t done;
};
static_assert(sizeof(SeekTableEnumerationStateInternal) <= sizeof(SeekTableEnumerationState), "seek table enum state size mismatch");
// In order to avoid requiring a temp allocation for the entire seek table,
// we set it up so it can be streamed in chunks. The seek table isn't big, but it's big enough
// that we don't want to toss it on the stack as a temp.
RadASeekTableReturn RadADecodeSeekTable(const RadAFileHeader* InFileHeader, const RadASeekTableHeader* InSeekHeader, uint8_t* InData, size_t InDataLenBytes, bool InSeekTableIs64Bits, SeekTableEnumerationState* InOutEnumerationState, uint8_t* OutSeekTableSamples, uint8_t* OutSeekTableBytes, size_t* OutConsumed)
{
if (InOutEnumerationState == 0 ||
OutSeekTableBytes == 0 ||
OutSeekTableSamples == 0 ||
InData == 0 ||
InDataLenBytes == 0 ||
InFileHeader == 0 ||
InSeekHeader == 0 ||
OutConsumed == 0)
return RadASeekTableReturn::InvalidParam;
// If they passed the entire file (or some other larger containing buffer) then we might not
// actually get even qword count, however we know that we can only consume qwords.
size_t DataLenWords = InDataLenBytes / 8;
SeekTableEnumerationStateInternal EnumState;
memcpy(&EnumState, InOutEnumerationState, sizeof(SeekTableEnumerationState));
if (EnumState.done)
{
*OutConsumed = 0;
return RadASeekTableReturn::Done;
}
const uint8_t sample_bits = InFileHeader->bits_for_seek_table_samples;
const uint8_t byte_bits = InFileHeader->bits_for_seek_table_bytes;
const uint8_t sample_shift_bits = InFileHeader->shift_bits_for_seek_table_samples;
uint32_t offset_to_data = RadAGetBytesToOpen(InFileHeader);
int64_t avg_index = InFileHeader->seek_table_entry_count/2;
for (; EnumState.seek_table_position < InFileHeader->seek_table_entry_count; EnumState.seek_table_position++)
{
// we need to offset our request by the amount we have streamed out.
uint64_t request_bit_position = (sample_bits + byte_bits) * EnumState.seek_table_position;
request_bit_position -= EnumState.consumed_qwords * 64;
uint64_t sample_biased_error = 0;
uint64_t byte_biased_error = 0;
if (BitContainerExtract(InData, DataLenWords, request_bit_position, sample_bits, sample_biased_error) == false ||
BitContainerExtract(InData, DataLenWords, request_bit_position + sample_bits, byte_bits, byte_biased_error) == false)
{
// We can get rid of everything up to our base that we need. This might be zero
// if they didn't give us enough data to do _anything_.
uint64_t base_need_qword = request_bit_position / 64;
if (base_need_qword)
base_need_qword--;
*OutConsumed = base_need_qword * 8;
EnumState.consumed_qwords += base_need_qword;
memcpy(InOutEnumerationState, &EnumState, sizeof(SeekTableEnumerationState));
return RadASeekTableReturn::NeedsMoreData;
}
int64_t sample_predicted = InSeekHeader->sample_intercept + ((int64_t)EnumState.seek_table_position - avg_index) * InSeekHeader->sample_line_slope[0] / InSeekHeader->sample_line_slope[1];
int64_t sample_unbiased_error = sample_biased_error + InSeekHeader->sample_bias;
uint64_t sample_result = (sample_predicted + sample_unbiased_error) << sample_shift_bits;
int64_t byte_predicted = InSeekHeader->byte_intercept + ((int64_t)EnumState.seek_table_position - avg_index) * InSeekHeader->byte_line_slope[0] / InSeekHeader->byte_line_slope[1];
int64_t byte_unbiased_error = byte_biased_error + InSeekHeader->byte_bias;
uint64_t byte_result = (byte_predicted + byte_unbiased_error) + offset_to_data;
if (InSeekTableIs64Bits)
{
memcpy(OutSeekTableSamples + sizeof(uint64_t) * EnumState.seek_table_position, &sample_result, sizeof(uint64_t));
memcpy(OutSeekTableBytes + sizeof(uint64_t) * EnumState.seek_table_position, &byte_result, sizeof(uint64_t));
}
else
{
if (sample_result > ~0U ||
byte_result > ~0U)
return RadASeekTableReturn::Needs64Bits;
uint32_t sample_32 = (uint32_t)sample_result;
uint32_t byte_32 = (uint32_t)byte_result;
memcpy(OutSeekTableSamples + sizeof(uint32_t) * EnumState.seek_table_position, &sample_32, sizeof(uint32_t));
memcpy(OutSeekTableBytes + sizeof(uint32_t) * EnumState.seek_table_position, &byte_32, sizeof(uint32_t));
}
}
EnumState.done = 1;
memcpy(InOutEnumerationState, &EnumState, sizeof(SeekTableEnumerationState));
{
uint64_t request_bit_position = ((sample_bits + byte_bits) * EnumState.seek_table_position + 63) & ~63;
request_bit_position -= EnumState.consumed_qwords * 64;
uint64_t last_success_qword = (request_bit_position) / 64;
*OutConsumed = last_success_qword * 8;
}
return RadASeekTableReturn::Done;
}
static uint64_t RadAEvaluateSeekTableSamplePosition(const RadAFileHeader* InFileHeader, const RadASeekTableHeader* InSeekHeader, const uint8_t* InSeekTable, size_t InSeekTableQWords, uint32_t InIndex)
{
int32_t avg_index = InFileHeader->seek_table_entry_count / 2;
uint64_t request_bit_position = (InFileHeader->bits_for_seek_table_samples + InFileHeader->bits_for_seek_table_bytes) * InIndex;
uint64_t sample_biased_error = 0;
BitContainerExtract(InSeekTable, InSeekTableQWords, request_bit_position, InFileHeader->bits_for_seek_table_samples, sample_biased_error);
int64_t sample_predicted = InSeekHeader->sample_intercept + ((int64_t)InIndex - avg_index) * InSeekHeader->sample_line_slope[0] / InSeekHeader->sample_line_slope[1];
int64_t sample_unbiased_error = sample_biased_error + InSeekHeader->sample_bias;
uint64_t sample_result = (sample_predicted + sample_unbiased_error) << InFileHeader->shift_bits_for_seek_table_samples;
return sample_result;
}
static uint64_t RadAEvaluateSeekTableBytePosition(const RadAFileHeader* InFileHeader, const RadASeekTableHeader* InSeekHeader, const uint8_t* InSeekTable, size_t InSeekTableQWords, uint32_t InIndex)
{
int32_t avg_index = InFileHeader->seek_table_entry_count / 2;
uint64_t request_bit_position = (InFileHeader->bits_for_seek_table_samples + InFileHeader->bits_for_seek_table_bytes) * InIndex;
uint64_t biased_error = 0;
BitContainerExtract(InSeekTable, InSeekTableQWords, request_bit_position + InFileHeader->bits_for_seek_table_samples, InFileHeader->bits_for_seek_table_bytes, biased_error);
int64_t predicted = InSeekHeader->byte_intercept + ((int64_t)InIndex - avg_index) * InSeekHeader->byte_line_slope[0] / InSeekHeader->byte_line_slope[1];
int64_t unbiased_error = biased_error + InSeekHeader->byte_bias;
uint64_t result = (predicted + unbiased_error);
return result;
}
static size_t RadASeekTableLookupInternal(const RadAFileHeader* InFileHeader, const RadASeekTableHeader* InSeekHeader, const uint8_t* InSeekTableData, size_t InSeekTableSizeBytes, uint64_t InTargetFrame, size_t* OutFrameAtLocation, size_t* OutFrameBlockSize)
{
if (InTargetFrame >= InFileHeader->frame_count)
InTargetFrame = InFileHeader->frame_count - 1;
size_t seek_table_qwords = InSeekTableSizeBytes / 8;
// binary search
{
uint32_t block_index = 0;
uint32_t count = InFileHeader->seek_table_entry_count;
while (count > 0)
{
uint32_t examine = block_index;
uint32_t step = count / 2;
examine += step;
uint64_t sample_for_index = RadAEvaluateSeekTableSamplePosition(InFileHeader, InSeekHeader, InSeekTableData, seek_table_qwords, examine);
if (!(InTargetFrame < sample_for_index)) // upper_bound
//if (sample_for_index < InTargetFrame) // lower_bound
{
block_index = examine + 1;
count -= step + 1;
}
else
count = step;
}
// We did an upper_bound search which means our result is strictly greater than our
// search term - so this must be the block _after_ the one we want - so evaluate
// the one before us and return it.
if (block_index)
block_index--;
if (OutFrameAtLocation)
*OutFrameAtLocation = RadAEvaluateSeekTableSamplePosition(InFileHeader, InSeekHeader, InSeekTableData, seek_table_qwords, block_index);;
uint64_t byte_result = RadAEvaluateSeekTableBytePosition(InFileHeader, InSeekHeader, InSeekTableData, seek_table_qwords, block_index);
if (OutFrameBlockSize)
{
// We are somewhere in this seek block, but we don't know exactly where, data wise. Since we want to _guarantee_ that the frame can
// be reached, the only option is to report the entire seek table entry.
//
size_t next_entry_bytes = 0;
if ((block_index + 1) < InFileHeader->seek_table_entry_count)
next_entry_bytes = RadAEvaluateSeekTableBytePosition(InFileHeader, InSeekHeader, InSeekTableData, seek_table_qwords, block_index + 1);
else
next_entry_bytes = InFileHeader->file_size - RadAGetBytesToOpen(InFileHeader); // we are doing math in seek table space...
*OutFrameBlockSize = next_entry_bytes - byte_result;
}
return byte_result + RadAGetBytesToOpen(InFileHeader);
}
}
size_t RadASeekTableLookup(const RadAContainer* InContainer, uint64_t InTargetFrame, size_t* OutFrameAtLocation, size_t* OutFrameBlockSize)
{
if (InContainer == 0 ||
InContainer->OffsetToSeekHeader == 0)
return 0;
const RadAFileHeader* FileHeader = &InContainer->Header;
if (InTargetFrame >= FileHeader->frame_count)
InTargetFrame = FileHeader->frame_count - 1;
const RadASeekTableHeader* SeekHeader = Container_SeekHeader(InContainer);
const uint8_t* SeekData = Container_SeekTable(InContainer);
return RadASeekTableLookupInternal(FileHeader, SeekHeader, SeekData, RadAGetSeekTableSizeOnDisk(FileHeader), InTargetFrame, OutFrameAtLocation, OutFrameBlockSize);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
size_t RadADirectSeekTableLookup(const uint8_t* InFileData, size_t InFileDataLenBytes, uint64_t InTargetFrame, size_t* OutFrameAtLocation, size_t* OutFrameBlockSize)
{
// seek table lookup without opening the file.
// We need the seek table, which means we definitely need all the header.
const RadAFileHeader* FileHeader = RadAGetFileHeader(InFileData, InFileDataLenBytes);
if (FileHeader == 0)
return 0;
const RadASeekTableHeader* SeekHeader = RadAGetSeekTableHeader(InFileData, InFileDataLenBytes);
if (SeekHeader == 0)
return 0;
uint32_t bytes_to_seek_table = RadAGetOffsetToSeekTable(FileHeader);
uint32_t seek_table_len = RadAGetSeekTableSizeOnDisk(FileHeader);
uint32_t bytes_to_evaluate = bytes_to_seek_table + seek_table_len;
if (InFileDataLenBytes < bytes_to_evaluate)
return 0; // need the seek table in memory.
const uint8_t* seek_table_bytes = InFileData + bytes_to_seek_table;
return RadASeekTableLookupInternal(FileHeader, SeekHeader, seek_table_bytes, seek_table_len, InTargetFrame, OutFrameAtLocation, OutFrameBlockSize);
}
// the most we'll ever need is:
// 6 - rada chunk header to get size
// 1 - sync
// 2 - multistream bytes
// = 9
constexpr uint32_t input_reservoir_min_size = 9;
// out_input_reservoir_needed is only valid when returning INPUT_BUFFER_VALID
RadAExamineBlockResult RadAExamineBlock(const RadAContainer* rada, const uint8_t* input_reservoir, size_t input_reservoir_len, uint32_t* out_input_reservoir_needed)
{
size_t original_input_reservoir_len = input_reservoir_len;
if (input_reservoir_len < input_reservoir_min_size)
{
if (out_input_reservoir_needed)
*out_input_reservoir_needed = input_reservoir_min_size;
return RadAExamineBlockResult::Incomplete; // no space for header.
}
// Check for file header
uint32_t header_check;
memcpy(&header_check, input_reservoir, 4);
if (header_check == 'RADA')
{
// We are at the beginning of the file - we have 8 bytes so we know we have
// access to the amount we need in the header.
constexpr size_t offset_to_bytes_for_first_decode = offsetof(RadAFileHeader, bytes_for_first_decode);
static_assert(offset_to_bytes_for_first_decode == 4, "incorrect offset in RadAFileHeader!");
uint32_t bytes_for_first_decode;
memcpy(&bytes_for_first_decode, input_reservoir + offset_to_bytes_for_first_decode, 4);
if (out_input_reservoir_needed)
*out_input_reservoir_needed = bytes_for_first_decode;
if (input_reservoir_len < bytes_for_first_decode)
return RadAExamineBlockResult::Incomplete;
return RadAExamineBlockResult::Valid;
}
if (input_reservoir[0] != RADA_SYNC_BYTE)
return RadAExamineBlockResult::Invalid;
input_reservoir++;
input_reservoir_len--;
uint16_t multi_stream_bytes = 0;
if (rada->Header.channels > 2)
{
// multistream - we have 2 bytes of extra size info
memcpy(&multi_stream_bytes, input_reservoir, 2);
input_reservoir += 2;
input_reservoir_len -= 2;
}
// get the first stream's chunk length
int chunk_length_result = RadAudioDecoderGetChunkLength(Container_Decoders(rada)[0], input_reservoir, input_reservoir_len);
if (chunk_length_result < 0)
{
if (chunk_length_result == RADAUDIO_DECODER_INCOMPLETE_DATA)
{
// this should only happen when we don't get the function enough data.. which should never happen.
// so we sanity this by asking for double the min.
if (out_input_reservoir_needed)
*out_input_reservoir_needed = input_reservoir_min_size * 2;
return RadAExamineBlockResult::Incomplete;
}
return RadAExamineBlockResult::Invalid;
}
uint32_t total_frame_size = multi_stream_bytes + chunk_length_result;
if (total_frame_size > rada->Header.max_block_size)
return RadAExamineBlockResult::Invalid;
if (out_input_reservoir_needed)
*out_input_reservoir_needed = (uint32_t)(total_frame_size + (original_input_reservoir_len - input_reservoir_len));
if (input_reservoir_len < total_frame_size)
return RadAExamineBlockResult::Incomplete;
if (input_reservoir_len > total_frame_size)
{
// if we have more data, check for the next sync.
if (input_reservoir[total_frame_size] != RADA_SYNC_BYTE)
return RadAExamineBlockResult::Invalid;
}
return RadAExamineBlockResult::Valid;
}
void RadANotifySeek(RadAContainer* rada)
{
uint8_t stream_count = Container_StreamCount(rada);
for (uint8_t stream_index = 0; stream_index < stream_count; stream_index++)
{
RadAudioDecoderDidSeek(Container_Decoders(rada)[stream_index]);
}
}
#define RadADecodeBlock_Error -2
#define RadADecodeBlock_Done -1
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int16_t RadADecodeBlock(RadAContainer* rada,
const uint8_t* input_reservoir, size_t input_reservoir_len,
float* output_reservoir, size_t output_reservoir_stride_in_floats,
size_t* consumed_bytes)
{
//
// This function assumes that the input reseroivr passed decoder checks and is valid
// with enough space.
//
const uint8_t* input_buffer = input_reservoir;
input_buffer++; // sync bits
input_reservoir_len--;
uint8_t stream_count = Container_StreamCount(rada);
if (stream_count > 1)
{
input_buffer+=2; // multistream size
input_reservoir_len-=2;
}
//
// Get the decompression results.
//
float* current_output_reservoir = output_reservoir;
int16_t decoded_samples = 0;
for (uint8_t stream_index = 0; stream_index < stream_count; stream_index++)
{
float* channel_outputs[2];
channel_outputs[0] = current_output_reservoir;
current_output_reservoir += output_reservoir_stride_in_floats;
channel_outputs[1] = current_output_reservoir;
current_output_reservoir += output_reservoir_stride_in_floats;
int chunk_length_result = RadAudioDecoderGetChunkLength(Container_Decoders(rada)[stream_index], input_buffer, input_reservoir_len);
if ((uint32_t)chunk_length_result > input_reservoir_len)
return RadADecodeBlock_Error;
size_t codec_consumed_bytes = 0;
int got_samples = RadAudioDecoderDecodeChunk(Container_Decoders(rada)[stream_index], input_buffer, input_reservoir_len, &codec_consumed_bytes, channel_outputs, 1024);
if (got_samples == RADAUDIO_DECODER_AT_EOF)
{
*consumed_bytes = 0;
return RadADecodeBlock_Done;
}
if (got_samples < 0)
return RadADecodeBlock_Error;
if (decoded_samples && got_samples != decoded_samples)
return RadADecodeBlock_Error;
if (decoded_samples > 1024) // max block size is 1024.
return RadADecodeBlock_Error;
decoded_samples = (int16_t)got_samples;
if (input_reservoir_len < codec_consumed_bytes)
return RadADecodeBlock_Error;
input_buffer += codec_consumed_bytes;
input_reservoir_len -= codec_consumed_bytes;
}
*consumed_bytes = input_buffer - input_reservoir;
return decoded_samples;
}
#ifdef RADAUDIO_DECODER_HAS_INTERLEAVING
void RadAInterleave( int16_t * output, float const ** inputs, uint32_t input_count, uint32_t samples )
{
RadAudioInterleave( output, inputs, input_count, samples );
}
#endif