use anyhow::{Result, bail};
use crate::process::extract::Frame;
use crate::process::{MAX_PRESENTATIONS, PresentationMap};
use crate::structs::access_unit::AccessUnit;
use crate::structs::restart_header::Guards;
use crate::utils::bitstream_io::BsIoSliceReader;
use crate::utils::crc::{
CRC_MAJOR_SYNC_INFO_ALG, CRC_RESTART_BLOCK_HEADER_ALG, CRC_SUBSTREAM_ALG, Crc8, Crc16,
};
use crate::utils::diagnostic::{
Diagnostic, DiagnosticMode, DiagnosticSink, Location, Rule, bit_position,
};
use crate::utils::errors::ParseError;
use crate::utils::fifo::{ACCUMULATORS, FifoDepthState, FifoPeak, SUBSTREAMS};
pub use crate::utils::perf::ParserPerfStats;
use crate::utils::timing::HiresOutputTimingState;
pub const RESTART_GAP_HISTORY: usize = 4;
#[derive(Default)]
pub struct Parser {
state: ParserState,
resyncing: bool,
}
impl Parser {
pub fn parse(&mut self, frame: &Frame) -> Result<AccessUnit> {
self.parse_inner(frame).0
}
fn parse_inner(&mut self, frame: &Frame) -> (Result<AccessUnit>, Option<u64>) {
self.state.perf = ParserPerfStats::default();
self.state.au_index = frame.index;
self.state.au_offset = frame.offset;
let reader = &mut BsIoSliceReader::from_slice(frame.as_ref());
let access_unit = AccessUnit::read(&mut self.state, reader);
let bit_offset = match access_unit {
Ok(_) => None,
Err(_) => bit_position(reader),
};
(access_unit, bit_offset)
}
pub fn parse_recovering(&mut self, frame: &Frame) -> Option<AccessUnit> {
if self.resyncing && !frame.is_major_sync() {
self.state.au_index = frame.index;
self.state.au_offset = frame.offset;
return None;
}
let recorded = self.state.diagnostics.len();
match self.parse_inner(frame) {
(Ok(access_unit), _) => {
self.resyncing = false;
Some(access_unit)
}
(Err(error), bit_offset) => {
self.record_parse_failure(recorded, error, bit_offset);
self.reset_for_next_major_sync();
self.resyncing = true;
None
}
}
}
fn record_parse_failure(
&mut self,
recorded: usize,
error: anyhow::Error,
bit_offset: Option<u64>,
) {
if !self.state.is_collecting() {
return;
}
if self.state.diagnostics.len() > recorded
&& let Some(diagnostic) = self.state.diagnostics.last_mut()
&& diagnostic.source.is_none()
{
diagnostic.source = Some(error);
return;
}
let diagnostic = Diagnostic {
rule: error.rule_id(),
severity: log::Level::Error,
location: self.state.location(bit_offset),
message: error.to_string(),
source: Some(error),
};
self.state.diagnostics.push(diagnostic);
}
pub fn set_diagnostic_mode(&mut self, mode: DiagnosticMode) {
self.state.diagnostic_mode = mode;
}
pub fn diagnostic_mode(&self) -> DiagnosticMode {
self.state.diagnostic_mode
}
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.state.diagnostics
}
pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
std::mem::take(&mut self.state.diagnostics)
}
pub fn set_required_presentations(
&mut self,
required_presentations: &[bool; MAX_PRESENTATIONS],
) {
self.state.required_presentations = *required_presentations;
if let Some(presentation_map) = &self.state.presentation_map {
self.state.substream_mask =
presentation_map.substream_mask_by_required_presentations(required_presentations);
}
}
pub fn hires_output_timing(&self) -> Option<usize> {
self.state.hires_output_timing
}
pub fn last_parse_stats(&self) -> ParserPerfStats {
self.state.perf
}
pub fn branches(&self) -> &[Branch] {
&self.state.branches
}
pub fn take_branches(&mut self) -> Vec<Branch> {
std::mem::take(&mut self.state.branches)
}
pub fn invalid_branches(&self) -> usize {
self.state.branches.iter().filter(|b| !b.is_valid()).count()
}
pub fn substream_state(&self, i: usize) -> Option<&ParserSubstreamState> {
self.state.substream_i_state(i).ok()
}
pub fn set_fail_level(&mut self, level: log::Level) {
self.state.fail_level = level;
}
pub fn set_allow_seamless_branch(&mut self, allow: bool) {
self.state.allow_seamless_branch = allow;
}
pub fn set_check_fifo(&mut self, check: bool) {
self.state.check_fifo = check;
}
pub fn fifo_depth_peaks(&self) -> [usize; ACCUMULATORS] {
self.state.fifo_depth.peaks()
}
pub fn fifo_depth_records(&self) -> [FifoPeak; ACCUMULATORS] {
self.state.fifo_depth.peak_records()
}
pub fn fifo_substream_peaks(&self) -> [usize; SUBSTREAMS] {
self.state.fifo_depth.substream_peaks()
}
pub fn max_data_rate(&self) -> usize {
self.state.max_data_rate
}
pub fn max_data_rate_au(&self) -> usize {
self.state.max_data_rate_au_index
}
pub fn max_fifo_latency(&self) -> usize {
self.state.max_latency
}
pub fn max_access_unit_size(&self) -> usize {
self.state.max_access_unit_size
}
pub fn total_access_unit_bytes(&self) -> usize {
self.state.total_access_unit_length << 1
}
pub fn reset_for_next_major_sync(&mut self) {
self.state.reset_for_next_major_sync();
}
}
#[derive(Clone, Copy, Debug)]
pub struct ParserSubstreamState {
pub crc_present: bool,
pub substream_end_ptr: u16,
pub drc_active: bool,
pub drc_gain_update: i16,
pub drc_time_update: u8,
pub drc_count: usize,
pub heavy_drc_active: bool,
pub heavy_drc_present: bool,
pub heavy_drc_gain_update: i16,
pub heavy_drc_time_update: u8,
pub heavy_drc_count: usize,
pub restart: ParserRestartState,
pub hires_output_timing: bool,
pub hires_output_timing_state: HiresOutputTimingState,
pub latency: usize,
pub prev_latency: usize,
pub output_timing_history: [usize; 128],
pub substream_size_history: [usize; 128],
pub history_index: usize,
}
#[derive(Clone, Copy, Debug)]
pub struct ParserRestartState {
pub block_index: usize,
pub restart_sync_word: u16,
pub min_chan: usize,
pub max_chan: usize,
pub max_matrix_chan: usize,
pub max_shift: i8,
pub max_lsbs: u32,
pub error_protect: bool,
pub guards: Guards,
pub block_size: usize,
pub primitive_matrices: usize,
pub matrix_ch: [u8; 16],
pub frac_bits: [u8; 16],
pub lsb_bypass_used: [bool; 16],
pub cf_mask: [u16; 16],
pub delta_bits: [u8; 16],
pub lsb_bypass_bit_count: [u8; 16],
pub huff_offset: [i32; 16],
pub huff_type: [usize; 16],
pub huff_lsbs: [u32; 16],
pub output_shift: [i8; 16],
pub quantiser_step_size: [u32; 16],
}
impl Default for ParserSubstreamState {
fn default() -> Self {
Self {
crc_present: false,
substream_end_ptr: 0,
drc_active: false,
drc_gain_update: 0,
drc_time_update: 0,
drc_count: 0,
hires_output_timing: false,
heavy_drc_active: false,
heavy_drc_present: false,
heavy_drc_gain_update: 0,
heavy_drc_time_update: 0,
heavy_drc_count: 0,
restart: ParserRestartState::default(),
hires_output_timing_state: HiresOutputTimingState::default(),
latency: 0,
prev_latency: 0,
output_timing_history: [0; 128],
substream_size_history: [0; 128],
history_index: 0,
}
}
}
impl Default for ParserRestartState {
fn default() -> Self {
Self {
block_index: 0,
restart_sync_word: 0,
min_chan: 0,
max_chan: 0,
max_matrix_chan: 0,
max_shift: 0,
max_lsbs: 0,
error_protect: false,
guards: Guards::default(),
block_size: 8,
primitive_matrices: 0,
matrix_ch: [0; 16],
frac_bits: [0; 16],
lsb_bypass_used: [false; 16],
cf_mask: [0; 16],
delta_bits: [0; 16],
lsb_bypass_bit_count: [0; 16],
huff_offset: [0; 16],
huff_type: [0; 16],
huff_lsbs: [24; 16],
output_shift: [0; 16],
quantiser_step_size: [0; 16],
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct BranchConditions {
pub advance_step: bool,
pub fifo_duration: bool,
pub within_75ms: bool,
pub data_rate: bool,
}
impl BranchConditions {
pub const fn is_valid(&self) -> bool {
self.advance_step && self.fifo_duration && self.within_75ms && self.data_rate
}
pub fn failed(&self) -> Vec<&'static str> {
[
(self.advance_step, "advance step"),
(self.fifo_duration, "FIFO duration"),
(self.within_75ms, "75 ms limit"),
(self.data_rate, "peak data rate"),
]
.into_iter()
.filter_map(|(met, name)| (!met).then_some(name))
.collect()
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Branch {
pub au_index: usize,
pub byte_offset: u64,
pub sample: u64,
pub advance: usize,
pub conditions: BranchConditions,
}
impl Branch {
pub const fn is_valid(&self) -> bool {
self.conditions.is_valid()
}
}
#[derive(Debug)]
#[repr(C)]
pub struct ParserState {
pub fail_level: log::Level,
pub allow_seamless_branch: bool,
pub check_fifo: bool,
pub diagnostic_mode: DiagnosticMode,
pub diagnostics: Vec<Diagnostic>,
pub au_index: u64,
pub au_offset: u64,
pub restart_gap: [usize; RESTART_GAP_HISTORY],
pub last_major_sync_index: usize,
pub au_counter: usize,
pub is_major_sync: bool,
pub fifo_depth: FifoDepthState,
pub fifo_output_clock: Option<usize>,
pub has_parsed_au: bool,
pub segment_start: bool,
pub au_start_pos: usize,
pub access_unit_length: usize,
pub prev_access_unit_length: usize,
pub total_access_unit_length: usize,
pub au_end_pos_bit: usize,
pub max_data_rate: usize,
pub max_data_rate_au_index: usize,
pub max_latency: usize,
pub max_access_unit_size: usize,
pub advance: usize,
pub prev_advance: usize,
pub fifo_duration: usize,
pub prev_fifo_duration: usize,
pub input_timing: usize,
pub first_input_timing: usize,
pub prev_input_timing: usize,
pub wrapped_input_timing: usize,
pub output_timing: usize,
pub au_output_timing: Option<(usize, u16)>,
pub first_output_timing: usize,
pub output_timing_deviation: usize,
pub hires_output_timing: Option<usize>,
pub unwrapped_input_timing: usize,
pub prev_unwrapped_input_timing: usize,
pub first_unwrapped_input_timing: usize,
pub input_timing_jump: bool,
pub output_timing_jump: bool,
pub peak_data_rate_jump: bool,
pub has_valid_branch: bool,
pub has_substream_info_changed: bool,
pub branches: Vec<Branch>,
pub perf: ParserPerfStats,
pub variable_rate: bool,
pub peak_data_rate: usize,
pub prev_peak_data_rate: usize,
pub audio_sampling_frequency_1: u32,
pub samples_per_au: usize,
pub format_sync: u32,
pub flags: u16,
pub presentation_map: Option<PresentationMap>,
pub required_presentations: [bool; MAX_PRESENTATIONS],
pub substreams: Option<usize>,
pub extended_substream_info: u8,
pub substream_info: u8,
pub has_parsed_substream: bool,
pub substream_segment_start_pos: u64,
pub substream_index: usize,
pub substream_mask: u8,
pub substream_state: [ParserSubstreamState; MAX_PRESENTATIONS],
pub crc_restart_block_header: Crc8,
pub crc_substream: Crc8,
pub crc_major_sync_info: Crc16,
pub bypassed_lsb: [[i32; 16]; 160],
pub sample_buffer: [[i32; 16]; 160],
}
impl Default for ParserState {
fn default() -> Self {
Self {
fail_level: log::Level::Error,
allow_seamless_branch: true,
check_fifo: true,
diagnostic_mode: DiagnosticMode::default(),
diagnostics: Vec::new(),
au_index: 0,
au_offset: 0,
restart_gap: [0, 8, 8, 8],
last_major_sync_index: 0,
au_counter: 0,
is_major_sync: false,
fifo_depth: FifoDepthState::default(),
fifo_output_clock: None,
segment_start: false,
has_parsed_au: false,
au_start_pos: 0,
access_unit_length: 0,
prev_access_unit_length: 0,
total_access_unit_length: 0,
au_end_pos_bit: 0,
max_data_rate: 0,
max_data_rate_au_index: 0,
max_latency: 0,
max_access_unit_size: 0,
advance: 0,
prev_advance: 0,
fifo_duration: 0,
prev_fifo_duration: 0,
input_timing: 0,
first_input_timing: 0,
prev_input_timing: 0,
wrapped_input_timing: 0,
output_timing: 0,
au_output_timing: None,
first_output_timing: 0,
output_timing_deviation: 0,
hires_output_timing: None,
unwrapped_input_timing: 0,
prev_unwrapped_input_timing: 0,
first_unwrapped_input_timing: 0,
input_timing_jump: false,
output_timing_jump: false,
peak_data_rate_jump: false,
has_valid_branch: false,
has_substream_info_changed: false,
branches: Vec::new(),
perf: ParserPerfStats::default(),
variable_rate: false,
peak_data_rate: 0,
prev_peak_data_rate: 0,
audio_sampling_frequency_1: 0,
samples_per_au: 0,
format_sync: 0,
flags: 0,
presentation_map: None,
required_presentations: [true; MAX_PRESENTATIONS],
substreams: None,
extended_substream_info: 0,
substream_info: 0,
has_parsed_substream: false,
substream_segment_start_pos: 0,
substream_index: 0,
substream_mask: 0,
substream_state: [ParserSubstreamState::default(); MAX_PRESENTATIONS],
crc_restart_block_header: Crc8::new(&CRC_RESTART_BLOCK_HEADER_ALG),
crc_substream: Crc8::new(&CRC_SUBSTREAM_ALG),
crc_major_sync_info: Crc16::new(&CRC_MAJOR_SYNC_INFO_ALG),
bypassed_lsb: [[0; 16]; 160],
sample_buffer: [[0; 16]; 160],
}
}
}
impl DiagnosticSink for ParserState {
fn fail_level(&self) -> log::Level {
self.fail_level
}
fn diagnostic_mode(&self) -> DiagnosticMode {
self.diagnostic_mode
}
fn location(&self, bit_offset: Option<u64>) -> Location {
Location {
au_index: self.au_index,
au_offset: self.au_offset,
bit_offset,
}
}
fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
self.diagnostics.push(diagnostic);
}
}
impl ParserState {
pub fn record_branch(&mut self, advance: usize, conditions: BranchConditions) {
if let Some(branch) = self.branches.last_mut()
&& branch.au_index == self.au_counter
{
let met = branch.conditions;
branch.conditions = BranchConditions {
advance_step: met.advance_step && conditions.advance_step,
fifo_duration: met.fifo_duration && conditions.fifo_duration,
within_75ms: met.within_75ms && conditions.within_75ms,
data_rate: met.data_rate && conditions.data_rate,
};
return;
}
self.branches.push(Branch {
au_index: self.au_counter,
byte_offset: self.au_offset,
sample: self.au_counter as u64 * self.samples_per_au as u64,
advance,
conditions,
});
}
pub fn reset_for_next_major_sync(&mut self) {
let diagnostics = std::mem::take(&mut self.diagnostics);
*self = Self {
fail_level: self.fail_level,
allow_seamless_branch: self.allow_seamless_branch,
check_fifo: self.check_fifo,
required_presentations: self.required_presentations,
branches: self.branches.clone(),
diagnostic_mode: self.diagnostic_mode,
diagnostics,
au_index: self.au_index,
au_offset: self.au_offset,
..Default::default()
};
}
pub fn expected_au_end_pos(&self) -> usize {
self.au_start_pos + (self.access_unit_length << 4)
}
pub fn substream_state_mut(&mut self) -> Result<&mut ParserSubstreamState> {
self.substream_i_state_mut(self.substream_index)
}
pub fn substream_state(&self) -> Result<&ParserSubstreamState> {
self.substream_i_state(self.substream_index)
}
pub fn substream_i_state_mut(&mut self, i: usize) -> Result<&mut ParserSubstreamState> {
self.check_substream(i)?;
Ok(&mut self.substream_state[i])
}
pub fn substream_i_state(&self, i: usize) -> Result<&ParserSubstreamState> {
self.check_substream(i)?;
Ok(&self.substream_state[i])
}
pub fn has_jump(&self) -> bool {
self.peak_data_rate_jump || self.input_timing_jump || self.output_timing_jump
}
pub fn reset_parser_substream_state(&mut self) {
self.substream_state[self.substream_index].restart = ParserRestartState::default();
}
pub fn restart_stream_for_branch(&mut self, output_timing: usize) {
self.output_timing_deviation = 0;
self.unwrapped_input_timing = self.input_timing;
self.prev_unwrapped_input_timing = 0;
self.first_input_timing = self.input_timing;
self.first_unwrapped_input_timing = self.unwrapped_input_timing;
let mut output_timing = output_timing;
if output_timing < self.input_timing {
output_timing += 0x10000;
}
self.output_timing = output_timing;
self.first_output_timing = output_timing;
self.fifo_output_clock = None;
for ss_state in &mut self.substream_state {
ss_state.history_index = 0;
}
self.fifo_depth.restart();
self.segment_start = true;
}
pub fn reset_for_branch(&mut self) {
for ss_state in &mut self.substream_state {
ss_state.hires_output_timing_state.reset_for_branch()
}
}
fn check_substream(&self, i: usize) -> Result<()> {
let Some(substreams) = self.substreams else {
bail!(ParseError::NoSubstream);
};
if substreams <= i {
bail!(ParseError::InvalidSubstreamIndex(i + 1, substreams));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{Branch, Parser};
use log::Level;
#[test]
fn taking_the_branches_empties_the_list() {
let mut parser = Parser::default();
parser.state.branches.push(Branch::default());
parser.state.branches.push(Branch::default());
assert_eq!(parser.take_branches().len(), 2);
assert!(parser.branches().is_empty(), "the list is left empty");
assert!(
parser.take_branches().is_empty(),
"taking twice is harmless"
);
}
#[test]
fn the_fifo_depth_model_can_be_turned_off() {
let mut parser = Parser::default();
assert!(parser.state.check_fifo, "on by default");
parser.set_check_fifo(false);
assert!(!parser.state.check_fifo);
}
#[test]
fn reset_preserves_configuration() {
let mut parser = Parser::default();
parser.set_fail_level(Level::Warn);
parser.set_required_presentations(&[true, false, true, false]);
parser.set_allow_seamless_branch(true);
parser.set_check_fifo(false);
parser.state.has_parsed_au = true;
parser.state.au_counter = 42;
parser.state.input_timing = 1234;
parser.reset_for_next_major_sync();
assert_eq!(parser.state.fail_level, Level::Warn);
assert_eq!(
parser.state.required_presentations,
[true, false, true, false]
);
assert!(parser.state.allow_seamless_branch);
assert!(!parser.state.check_fifo);
assert!(!parser.state.has_parsed_au);
assert_eq!(parser.state.au_counter, 0);
assert_eq!(parser.state.input_timing, 0);
}
fn first_au(data: &[u8]) -> Option<crate::structs::access_unit::AccessUnit> {
let mut extractor = crate::process::extract::Extractor::default();
extractor.push_bytes(data);
let frame = extractor.by_ref().next()?.ok()?;
Parser::default().parse(&frame).ok()
}
#[test]
fn fbb_stream_parses() {
let au = first_au(crate::process::EXAMPLE_DATA_FBB).expect("an access unit");
let ms = au.major_sync_info.as_ref().expect("a major sync");
assert_eq!(ms.format_sync, crate::structs::sync::MAJOR_SYNC_FBB);
assert_eq!(ms.format_info.sampling_frequency_1().unwrap(), 48000);
}
#[test]
fn fbb_channel_meaning_is_read_with_the_fbb_layout() {
let au = first_au(crate::process::EXAMPLE_DATA_FBB).expect("an access unit");
let ms = au.major_sync_info.as_ref().expect("a major sync");
assert!(ms.channel_meaning.fba().is_none(), "not the FBA layout");
let cm = ms.channel_meaning.fbb().expect("the FBB layout");
assert_eq!(cm.fs, 10);
assert_eq!(cm.wordwidth, 24);
assert_eq!(cm.channel_occupancy, 0x3F);
assert_eq!(cm.mlp_multi_channel_type, 0);
assert_eq!(cm.speaker_layout, 0);
assert_eq!(cm.copy_protection, 0);
assert_eq!(cm.level_control, 0x8080);
assert!(!cm.hdcd_process);
assert_eq!(cm.reserved2, 0);
assert_eq!(cm.source_format, 0);
assert_eq!(cm.summary_info, 0);
assert!(ms.channel_meaning.extra_channel_meaning().is_none());
}
#[test]
fn fba_stream_still_parses() {
let au = first_au(crate::process::EXAMPLE_DATA).expect("an access unit");
let ms = au.major_sync_info.as_ref().expect("a major sync");
assert_eq!(ms.format_sync, crate::structs::sync::MAJOR_SYNC_FBA);
}
#[test]
fn fbb_unextractable_stream_extracts() {
let au = first_au(crate::process::EXAMPLE_DATA_FBB_UNEXTRACTABLE).expect("an access unit");
let ms = au.major_sync_info.as_ref().expect("a major sync");
assert_eq!(ms.format_sync, crate::structs::sync::MAJOR_SYNC_FBB);
assert_eq!(ms.substream_info, 0x05);
assert_eq!(ms.channel_meaning.fbb().expect("the FBB layout").fs, 10);
}
#[test]
fn fbb_sync_interval_limit_is_stricter_than_fba() {
use crate::utils::errors::AccessUnitError;
assert_eq!(
AccessUnitError::FbbSyncTooFar.to_string(),
"FBB stream major syncs must occur at intervals not exceeding 32 access units"
);
assert_eq!(
AccessUnitError::FbaSyncTooFar.to_string(),
"FBA stream major syncs must occur at intervals not exceeding 128 access units"
);
}
}