use oxideav_codec::Decoder;
use oxideav_core::{
AudioFrame, CodecId, CodecParameters, Error, Frame, Packet, Result, SampleFormat, TimeBase,
};
use crate::{sync, toc};
pub fn make_decoder(params: &CodecParameters) -> Result<Box<dyn Decoder>> {
Ok(Box::new(Ac4Decoder::new(params)))
}
pub struct Ac4Decoder {
codec_id: CodecId,
hint_channels: u16,
hint_sample_rate: u32,
pending: Option<Packet>,
eof: bool,
pub last_info: Option<toc::Ac4FrameInfo>,
}
impl Ac4Decoder {
pub fn new(params: &CodecParameters) -> Self {
Self {
codec_id: params.codec_id.clone(),
hint_channels: params.channels.unwrap_or(2),
hint_sample_rate: params.sample_rate.unwrap_or(48_000),
pending: None,
eof: false,
last_info: None,
}
}
fn extract_raw_frame<'a>(&self, pkt: &'a Packet) -> (&'a [u8], bool) {
if let Some(f) = sync::find_sync_frame(&pkt.data) {
(f.payload, true)
} else {
(pkt.data.as_slice(), false)
}
}
}
impl Decoder for Ac4Decoder {
fn codec_id(&self) -> &CodecId {
&self.codec_id
}
fn send_packet(&mut self, packet: &Packet) -> Result<()> {
if self.pending.is_some() {
return Err(Error::other(
"ac4 decoder: call receive_frame before sending another packet",
));
}
self.pending = Some(packet.clone());
Ok(())
}
fn receive_frame(&mut self) -> Result<Frame> {
let Some(pkt) = self.pending.take() else {
return if self.eof {
Err(Error::Eof)
} else {
Err(Error::NeedMore)
};
};
if pkt.data.is_empty() {
return Ok(Frame::Audio(AudioFrame {
format: SampleFormat::S16,
channels: self.hint_channels,
sample_rate: self.hint_sample_rate,
samples: 0,
pts: pkt.pts,
time_base: TimeBase::new(1, self.hint_sample_rate as i64),
data: vec![Vec::new()],
}));
}
let (raw, _had_sync) = self.extract_raw_frame(&pkt);
let info = toc::parse_ac4_toc(raw)
.map_err(|e| Error::invalid(format!("ac4 decoder: TOC parse failed: {e}")))?;
let channels = if info.channels == 0 {
self.hint_channels
} else {
info.channels
};
let sample_rate = if info.sample_rate == 0 {
self.hint_sample_rate
} else {
info.sample_rate
};
let samples = if info.frame_length == 0 {
if sample_rate == 44_100 {
480
} else {
1024
}
} else {
if sample_rate == 96_000 {
info.frame_length * 2
} else if sample_rate == 192_000 {
info.frame_length * 4
} else {
info.frame_length
}
};
self.last_info = Some(info);
let byte_count = (samples as usize) * (channels as usize) * 2; let data = vec![vec![0u8; byte_count]];
Ok(Frame::Audio(AudioFrame {
format: SampleFormat::S16,
channels,
sample_rate,
samples,
pts: pkt.pts,
time_base: TimeBase::new(1, sample_rate as i64),
data,
}))
}
fn flush(&mut self) -> Result<()> {
self.eof = true;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
fn build_minimal_toc() -> Vec<u8> {
let mut bw = BitWriter::new();
bw.write_u32(2, 2);
bw.write_u32(7, 10);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.write_u32(1, 4);
bw.write_u32(1, 1);
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(0, 3);
bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_u32(0, 2);
bw.write_u32(0, 3);
bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_u32(0b10, 2); bw.write_u32(0, 1); bw.write_u32(0, 1); bw.write_u32(0, 1); bw.write_u32(1, 1); bw.write_u32(0, 2); bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 2);
bw.write_u32(0, 1);
bw.align_to_byte();
bw.finish()
}
#[test]
fn decoder_emits_silence_with_correct_shape() {
let mut bytes = build_minimal_toc();
bytes.extend(vec![0u8; 64]);
let params = CodecParameters::audio(CodecId::new("ac4"));
let mut dec = Ac4Decoder::new(¶ms);
let pkt = Packet::new(0, TimeBase::new(1, 48_000), bytes);
dec.send_packet(&pkt).unwrap();
let Frame::Audio(af) = dec.receive_frame().unwrap() else {
panic!("expected audio");
};
assert_eq!(af.channels, 2);
assert_eq!(af.sample_rate, 48_000);
assert_eq!(af.samples, 1_920);
assert_eq!(af.format, SampleFormat::S16);
assert_eq!(af.data.len(), 1);
assert_eq!(af.data[0].len(), (1_920 * 2 * 2) as usize);
assert!(af.data[0].iter().all(|&b| b == 0));
let info = dec.last_info.as_ref().unwrap();
assert_eq!(info.n_presentations, 1);
assert_eq!(info.n_substreams, 1);
assert_eq!(info.fs_index, 1);
assert_eq!(info.frame_rate_index, 1);
assert_eq!(info.frame_length, 1_920);
assert!(info.b_iframe_global);
}
#[test]
fn decoder_handles_sync_wrapped_packet() {
let raw = build_minimal_toc();
let mut wrapped = vec![0xAC, 0x40];
let fs = raw.len() as u16;
wrapped.extend_from_slice(&fs.to_be_bytes());
wrapped.extend_from_slice(&raw);
let params = CodecParameters::audio(CodecId::new("ac4"));
let mut dec = Ac4Decoder::new(¶ms);
let pkt = Packet::new(0, TimeBase::new(1, 48_000), wrapped);
dec.send_packet(&pkt).unwrap();
let Frame::Audio(af) = dec.receive_frame().unwrap() else {
panic!("expected audio");
};
assert_eq!(af.channels, 2);
assert_eq!(af.samples, 1_920);
}
}