use std::io::Read;
use std::{cmp, mem};
use indexmap::IndexSet;
use log::{debug, info, trace};
use vorbis_bitpack::BitpackReader;
use crate::vorbis::codebook::VorbisCodebook;
use crate::vorbis::{PacketType, ResidueType, VectorLookupType};
use super::{
common_header_validation, ilog, AudioPacketAnalyze, VorbisCommentData,
VorbisIdentificationHeaderData, VorbisOptimizerError
};
type PacketSlice<'pref, 'packet> = &'pref mut &'packet [u8];
#[derive(Default)]
pub(super) struct VorbisSetupData {
pub(super) codebook_configurations: Vec<CodebookConfiguration>,
pub(super) floor_configurations: Vec<Floor1Configuration>,
pub(super) residue_configurations: Vec<ResidueConfiguration>,
pub(super) mapping_configurations: Vec<MappingConfiguration>,
pub(super) modes: Vec<Mode>
}
pub(super) struct SetupHeaderParse {
pub(super) comment_data: VorbisCommentData
}
pub(super) struct ChannelMapping {
pub(super) magnitude_channel: u8,
pub(super) angle_channel: u8
}
pub(super) struct FloorAndResidueMapping {
pub(super) floor_number: u8,
pub(super) residue_number: u8
}
pub(super) struct CodebookConfiguration {
pub(super) codebook: VorbisCodebook,
pub(super) entry_count: u32,
pub(super) dimensions: u16,
pub(super) vector_lookup_type: VectorLookupType,
pub(super) codebook_vector_minimum_value: f64,
pub(super) codebook_vector_delta_value: f64,
pub(super) codebook_vector_multiplicands: Vec<u16>,
pub(super) codebook_vector_value_bits: u8,
pub(super) codebook_vector_sequence_flag: bool
}
pub(super) struct Floor1Configuration {
pub(super) multiplier: u8,
pub(super) range_bits: u8,
pub(super) partition_class_list: Vec<u8>,
pub(super) class_dimensions: Vec<u8>,
pub(super) class_subclasses: Vec<u8>,
pub(super) class_masterbooks: Vec<Option<u8>>,
pub(super) subclass_books: Vec<Vec<Option<u8>>>,
pub(super) x_list: Vec<u16>
}
pub(super) struct ResidueConfiguration {
pub(super) residue_type: ResidueType,
pub(super) begin: u32,
pub(super) end: u32,
pub(super) partition_size: u32,
pub(super) classifications: u8,
pub(super) classbook: u8,
pub(super) books: Vec<[Option<u8>; 8]>
}
pub(super) struct MappingConfiguration {
pub(super) channel_mappings: Vec<ChannelMapping>,
pub(super) mapping_mux: Vec<u8>,
pub(super) floor_and_residue_mappings: Vec<FloorAndResidueMapping>
}
pub(super) struct Mode {
pub(super) big_block: bool,
pub(super) mapping_number: u8
}
impl SetupHeaderParse {
pub(super) fn analyze_packet(
&mut self,
packet: &[u8],
identification_data: &VorbisIdentificationHeaderData
) -> Result<(Option<u16>, Option<AudioPacketAnalyze>), VorbisOptimizerError> {
trace!("Decoding setup header Vorbis packet");
let mut setup_header = common_header_validation(packet, PacketType::SetupHeader)?;
let (codebook_configurations, mut bitpacker, header_length) =
parse_codebook_configurations(&mut setup_header)?;
let time_domain_transform_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!(
"Time domain transforms (unused): {}",
time_domain_transform_count
);
for _ in 0..time_domain_transform_count {
let transform = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if cfg!(debug_assertions) && transform != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
}
let floor_configurations = parse_floor1_configurations(
&mut bitpacker,
header_length,
codebook_configurations.len()
)?;
let residue_configurations =
parse_residue_configurations(&mut bitpacker, header_length, &codebook_configurations)?;
let mapping_configurations = parse_mapping_configurations(
&mut bitpacker,
header_length,
identification_data.channels.get(),
floor_configurations.len(),
residue_configurations.len()
)?;
let modes = parse_modes(&mut bitpacker, header_length, mapping_configurations.len())?;
Ok((
None,
Some(AudioPacketAnalyze {
comment_data: mem::take(&mut self.comment_data),
codec_setup: VorbisSetupData {
residue_configurations,
codebook_configurations,
floor_configurations,
mapping_configurations,
modes
}
})
))
}
}
fn parse_codebook_configurations<'pref, 'packet>(
setup_header: PacketSlice<'pref, 'packet>
) -> Result<
(
Vec<CodebookConfiguration>,
BitpackReader<PacketSlice<'pref, 'packet>>,
usize
),
VorbisOptimizerError
> {
let header_length = setup_header.len() + 7; if setup_header.is_empty() {
return Err(VorbisOptimizerError::TooSmallPacket(header_length));
}
let codebook_count = setup_header
.split_first()
.map(|(codebook_count, setup_header_tail)| {
*setup_header = setup_header_tail;
*codebook_count as u16 + 1
})
.unwrap();
info!("Codebook count: {}", codebook_count);
let mut bitpacker = BitpackReader::new(setup_header);
let mut codebook_configurations = Vec::with_capacity(codebook_count as usize);
for i in 0..codebook_count {
let sync_pattern =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?;
if cfg!(debug_assertions) && sync_pattern != 0x564342 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let codebook_dimensions =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
let codebook_entries =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?;
let codebook_entries_usize = codebook_entries.try_into()?;
let ordered = bitpack_packet_read!(bitpacker, read_flag, header_length)?;
debug!(
"Codebook {}: {} dimensions, {} entries, ordered: {}",
i, codebook_dimensions, codebook_entries, ordered
);
let mut codeword_lengths = vec![0; codebook_entries_usize];
if ordered {
let mut start_entry = 0;
let mut codeword_length = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?
+ 1;
while start_entry < codebook_entries_usize {
if codeword_length > 32 {
return Err(VorbisOptimizerError::TooBigCodewordLength);
}
let entries_with_this_cw_length = usize::try_from(bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut ilog((codebook_entries_usize - start_entry) as i32),
u32
)?)?;
let next_start_entry = start_entry + entries_with_this_cw_length;
if next_start_entry > codebook_entries_usize {
return Err(VorbisOptimizerError::InvalidSetupValue);
}
codeword_lengths[start_entry..next_start_entry].fill(codeword_length);
start_entry = next_start_entry;
codeword_length += 1;
}
} else {
let sparse = bitpack_packet_read!(bitpacker, read_flag, header_length)?;
trace!("Codebook {} is sparse: {}", i, sparse);
for codeword_length in codeword_lengths.iter_mut() {
if !sparse || bitpack_packet_read!(bitpacker, read_flag, header_length)? {
*codeword_length = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?
+ 1;
}
}
}
let lookup_type = VectorLookupType::try_from(
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?
)?;
let codebook_vector_minimum_value;
let codebook_vector_delta_value;
let codebook_vector_value_bits;
let codebook_vector_sequence_flag;
let mut codebook_vector_multiplicands;
match lookup_type {
VectorLookupType::NoLookup => {
debug!("Codebook {} is not used for vector decoding", i);
codebook_vector_minimum_value = 0.0;
codebook_vector_delta_value = 0.0;
codebook_vector_value_bits = 0;
codebook_vector_sequence_flag = false;
codebook_vector_multiplicands = Vec::new();
}
_ => {
codebook_vector_minimum_value =
bitpack_packet_read!(bitpacker, read_float32, header_length)?;
codebook_vector_delta_value =
bitpack_packet_read!(bitpacker, read_float32, header_length)?;
codebook_vector_value_bits = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?
+ 1;
codebook_vector_sequence_flag =
bitpack_packet_read!(bitpacker, read_flag, header_length)?;
let codebook_lookup_value_count =
if lookup_type == VectorLookupType::ImplicitlyPopulated {
lookup1_values(codebook_entries, codebook_dimensions) as u64
} else {
codebook_entries as u64 * codebook_dimensions as u64
};
debug!(
"Codebook {} vector lookup: type {} ({:?}), minimum value {}, delta value {}, \
value bits {}, value count {}",
i,
lookup_type as u8,
lookup_type,
codebook_vector_minimum_value,
codebook_vector_delta_value,
codebook_vector_value_bits,
codebook_lookup_value_count
);
codebook_vector_multiplicands =
Vec::with_capacity(cmp::min(codebook_lookup_value_count.try_into()?, 65535));
for _ in 0..codebook_lookup_value_count {
codebook_vector_multiplicands.push(bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut codebook_vector_value_bits,
u16
)?);
}
}
}
codebook_configurations.push(CodebookConfiguration {
codebook: VorbisCodebook::new(i, &codeword_lengths)?,
entry_count: codebook_entries,
vector_lookup_type: lookup_type,
codebook_vector_minimum_value,
codebook_vector_delta_value,
codebook_vector_multiplicands,
codebook_vector_value_bits,
codebook_vector_sequence_flag,
dimensions: codebook_dimensions
});
}
Ok((codebook_configurations, bitpacker, header_length))
}
fn parse_floor1_configurations<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
codebook_count: usize
) -> Result<Vec<Floor1Configuration>, VorbisOptimizerError> {
let floor_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Floor configurations count: {}", floor_count);
let mut floor_configurations = Vec::with_capacity(floor_count as usize);
for i in 0..floor_count {
let floor_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if floor_type != 1 {
return Err(VorbisOptimizerError::UnsupportedFloorType(floor_type));
}
let partitions =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?;
let mut partition_class_list = Vec::with_capacity(partitions as usize);
let mut maximum_class = -1;
for _ in 0..partitions {
let class =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?;
partition_class_list.push(class);
maximum_class = cmp::max(class as i8, maximum_class);
}
debug!(
"Floor {}: type {}, {} partitions, {} classes",
i,
floor_type,
partitions,
maximum_class + 1
);
let mut class_dimensions = Vec::with_capacity((maximum_class + 1) as usize);
let mut class_subclasses = Vec::with_capacity((maximum_class + 1) as usize);
let mut class_masterbooks = Vec::with_capacity((maximum_class + 1) as usize);
let mut subclass_books = Vec::with_capacity((maximum_class + 1) as usize * 8);
let mut maximum_class_dimension = 1;
for _ in 0..=maximum_class {
let class_dimension = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 3, u8)?
+ 1;
class_dimensions.push(class_dimension);
maximum_class_dimension = cmp::max(class_dimension, maximum_class_dimension);
let current_subclass =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 2, u8)?;
class_subclasses.push(current_subclass);
if current_subclass != 0 {
let codebook_number = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if codebook_number as usize >= codebook_count {
return Err(VorbisOptimizerError::InvalidCodebookNumber(codebook_number));
}
debug!(
"Floor {}, subclass {} codebook: {}",
i, current_subclass, codebook_number
);
class_masterbooks.push(Some(codebook_number));
} else {
debug!("Floor {}, subclass {} has no codebook", i, current_subclass);
class_masterbooks.push(None);
}
let current_subclass_books_count = 1 << current_subclass;
let mut current_subclass_books = Vec::with_capacity(current_subclass_books_count);
for _ in 0..current_subclass_books_count {
let codebook_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?
.checked_sub(1);
if let Some(codebook_number) = codebook_number
.iter()
.filter(|n| **n as usize >= codebook_count)
.last()
{
return Err(VorbisOptimizerError::InvalidCodebookNumber(
*codebook_number
));
}
debug!(
"Floor {} vector partition codebook: {:?}",
i, codebook_number
);
current_subclass_books.push(codebook_number);
}
subclass_books.push(current_subclass_books);
}
let multiplier =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 2, u8)? + 1;
let range_bits =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?;
let mut x_list =
IndexSet::with_capacity(partition_class_list.len() * maximum_class_dimension as usize);
for current_class in partition_class_list.iter().copied().map(|c| c as usize) {
for _ in 0..class_dimensions[current_class] {
if !x_list.insert(bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut range_bits,
u16
)?) {
return Err(VorbisOptimizerError::RepeatedFloor1Point(i));
}
}
}
if x_list.len() > 65 {
return Err(VorbisOptimizerError::TooManyFloor1Points(i));
}
floor_configurations.push(Floor1Configuration {
multiplier,
range_bits,
partition_class_list,
class_dimensions,
class_subclasses,
class_masterbooks,
subclass_books,
x_list: x_list.into_iter().collect()
});
}
Ok(floor_configurations)
}
fn parse_residue_configurations<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
codebook_configurations: &[CodebookConfiguration]
) -> Result<Vec<ResidueConfiguration>, VorbisOptimizerError> {
let residue_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Residue count: {}", residue_count);
let mut residue_configurations = Vec::with_capacity(residue_count as usize);
for i in 0..residue_count {
let residue_type = ResidueType::try_from(
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?
)?;
let residue_begin =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?;
let residue_end = cmp::max(
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?,
residue_begin
);
let residue_partition_size = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?
+ 1;
let residue_classifications =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
let residue_classbook =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
debug!(
"Residue {}: type {} ({:?}), begin {}, end {}, partition size {}, classifications {}, classbook {}",
i,
residue_type as u8,
residue_type,
residue_begin,
residue_end,
residue_partition_size,
residue_classifications,
residue_classbook
);
#[allow(clippy::blocks_in_if_conditions)]
if codebook_configurations
.get(residue_classbook as usize)
.filter(|classbook_configuration| {
let minimum_entry_count = (residue_classifications as u32)
.saturating_pow(classbook_configuration.dimensions as u32);
minimum_entry_count <= classbook_configuration.entry_count
})
.is_none()
{
return Err(VorbisOptimizerError::InvalidCodebookNumber(
residue_classbook
));
}
let mut residue_cascade = Vec::with_capacity(residue_classifications as usize);
for _ in 0..residue_classifications {
let low_bits =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 3, u8)?;
let high_bits = if bitpack_packet_read!(bitpacker, read_flag, header_length)? {
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?
} else {
0
};
residue_cascade.push((high_bits << 3) | low_bits);
}
let mut residue_books = Vec::with_capacity(residue_classifications as usize);
for (i, residue_cascade) in residue_cascade.iter().copied().enumerate() {
residue_books.push([None; 8]);
for j in 0..8 {
if residue_cascade & (1 << j) != 0 {
let residue_book = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if codebook_configurations.get(residue_book as usize).is_none() {
return Err(VorbisOptimizerError::InvalidCodebookNumber(residue_book));
}
trace!(
"Read residue book {} for classification {}",
residue_book,
i
);
residue_books[i][j] = Some(residue_book);
} else {
}
}
}
residue_configurations.push(ResidueConfiguration {
residue_type,
begin: residue_begin,
end: residue_end,
partition_size: residue_partition_size,
classifications: residue_classifications,
classbook: residue_classbook,
books: residue_books
});
}
Ok(residue_configurations)
}
fn parse_mapping_configurations<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
audio_channels: u8,
floor_count: usize,
residue_count: usize
) -> Result<Vec<MappingConfiguration>, VorbisOptimizerError> {
let mapping_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Mapping count: {}", mapping_count);
let mut mapping_configurations = Vec::with_capacity(mapping_count as usize);
for i in 0..mapping_count {
let mapping_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if mapping_type != 0 {
return Err(VorbisOptimizerError::ReservedMappingType(mapping_type));
}
let mapping_submap_count = if bitpack_packet_read!(bitpacker, read_flag, header_length)? {
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)? + 1
} else {
1
};
debug!(
"Mapping {} floor and residue submap count: {}",
i, mapping_submap_count
);
let mut channel_mappings;
if bitpack_packet_read!(bitpacker, read_flag, header_length)? {
let mapping_coupling_steps = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u16)?
+ 1;
let mapping_component_bits = ilog(audio_channels as i32 - 1);
channel_mappings = Vec::with_capacity(mapping_coupling_steps as usize);
for j in 0..mapping_coupling_steps {
let magnitude_channel = bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut mapping_component_bits,
u8
)?;
let angle_channel = bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut mapping_component_bits,
u8
)?;
if magnitude_channel == angle_channel
|| magnitude_channel >= audio_channels
|| angle_channel >= audio_channels
{
return Err(VorbisOptimizerError::InvalidChannelMapping {
magnitude_channel,
angle_channel,
audio_channels
});
}
trace!(
"Mapping {}, channel mapping {}: \
magnitude channel {}, angle channel {}",
i,
j,
magnitude_channel,
angle_channel
);
channel_mappings.push(ChannelMapping {
magnitude_channel,
angle_channel
});
}
} else {
trace!("Not using channel mappings for mapping {}", i);
channel_mappings = vec![];
}
let reserved_field =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 2, u8)?;
if cfg!(debug_assertions) && reserved_field != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let mut mapping_mux;
if mapping_submap_count > 1 {
mapping_mux = Vec::with_capacity(audio_channels as usize);
for _ in 0..audio_channels {
let mux_submap = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?;
if mux_submap >= mapping_submap_count {
return Err(VorbisOptimizerError::InvalidChannelMultiplexing {
mux_submap,
mapping_submap_count
});
}
mapping_mux.push(mux_submap);
}
} else {
mapping_mux = vec![0; audio_channels as usize];
}
trace!(
"Read mapping {} channel floor and residue submapping settings: {:?}",
i,
mapping_mux
);
let mut floor_and_residue_mappings = Vec::with_capacity(mapping_submap_count as usize);
for j in 0..mapping_submap_count {
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
let floor_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if floor_number as usize >= floor_count {
return Err(VorbisOptimizerError::InvalidFloorNumber(floor_number));
}
let residue_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if residue_number as usize >= residue_count {
return Err(VorbisOptimizerError::InvalidResidueNumber(residue_number));
}
info!(
"Mapping {}, submap {}: floor {}, residue {}",
i, j, floor_number, residue_number
);
floor_and_residue_mappings.push(FloorAndResidueMapping {
floor_number,
residue_number
});
}
mapping_configurations.push(MappingConfiguration {
channel_mappings,
mapping_mux,
floor_and_residue_mappings
});
}
Ok(mapping_configurations)
}
fn parse_modes<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
mapping_count: usize
) -> Result<Vec<Mode>, VorbisOptimizerError> {
let mode_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Mode count: {}", mode_count);
let mut modes = Vec::with_capacity(mode_count as usize);
for i in 0..mode_count {
let big_block = bitpack_packet_read!(bitpacker, read_flag, header_length)?;
let window_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if cfg!(debug_assertions) && window_type != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let time_transform_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if cfg!(debug_assertions) && time_transform_type != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let mapping_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if mapping_number as usize >= mapping_count {
return Err(VorbisOptimizerError::InvalidMappingNumber(mapping_number));
}
debug!(
"Mode {}: uses big block size: {}, mapping number {}",
i, big_block, mapping_number
);
modes.push(Mode {
big_block,
mapping_number
});
}
if cfg!(debug_assertions) && !bitpack_packet_read!(bitpacker, read_flag, header_length)? {
return Err(VorbisOptimizerError::InvalidPattern);
}
Ok(modes)
}
fn lookup1_values(codebook_entries: u32, codebook_dimensions: u16) -> u32 {
if codebook_dimensions == 0 {
u32::MAX
} else {
(codebook_entries as f32).powf(1.0 / codebook_dimensions as f32) as u32
}
}
#[cfg(test)]
mod tests {
use super::lookup1_values;
#[test]
fn lookup1_values_works() {
assert_eq!(lookup1_values(100, 5), 2);
assert_eq!(lookup1_values(1, 5), 1);
assert_eq!(lookup1_values(0, u16::MAX), 0);
assert_eq!(lookup1_values(0xFFFFFF, 0), u32::MAX);
assert_eq!(lookup1_values(0xFFFFFF, u16::MAX), 1);
}
}