use crate::engine::asc::AudioSpecificConfig;
use crate::error::{AacError, MalformedKind, Result, UnsupportedFeature};
use crate::isomp4::frag::{moov_is_fragmented, parse_init};
use crate::options::ChannelMode;
use super::{Decoder, Frame};
#[path = "fmp4_state.rs"]
mod state;
pub(super) use state::Fmp4Push;
use state::{Phase, checked_end, fence_box, peek_box};
impl Decoder {
pub(super) fn pump_fmp4<F>(
&mut self,
buf: &[u8],
on_frame: &mut F,
final_flush: bool,
) -> Result<()>
where
F: FnMut(Frame<'_>) -> Result<()>,
{
self.pump_fmp4_run(buf, on_frame)?;
if final_flush {
let done = match self.fmp4.phase {
Phase::Drain => true,
Phase::Init | Phase::Boxes => self.pos == buf.len(),
Phase::Skip(_) | Phase::Moof { .. } | Phase::Mdat => false,
};
if !done {
return Err(AacError::truncated_at(Some(
self.abs_base + self.pos as u64,
)));
}
}
Ok(())
}
fn pump_fmp4_run<F>(&mut self, buf: &[u8], on_frame: &mut F) -> Result<()>
where
F: FnMut(Frame<'_>) -> Result<()>,
{
loop {
match self.fmp4.phase {
Phase::Drain => {
self.pos = buf.len();
return Ok(());
}
Phase::Mdat => {
if !self.pump_fmp4_mdat(buf, on_frame)? {
return Ok(()); }
}
Phase::Skip(end) => {
let cur = self.abs_base + self.pos as u64;
if end < cur {
return Err(AacError::Malformed(MalformedKind::Syntax));
}
let avail_end = self.abs_base + buf.len() as u64;
self.pos += (end.min(avail_end) - cur) as usize;
if avail_end < end {
return Ok(());
}
self.fmp4.phase = if self.fmp4.init.is_some() {
Phase::Boxes
} else {
Phase::Init
};
}
Phase::Moof { start, end, next } => {
if !self.pump_fmp4_moof(buf, start, end, next)? {
return Ok(()); }
}
Phase::Init | Phase::Boxes => {
let pos = self.pos;
let Some((size, _head, typ)) = peek_box(buf, pos)? else {
return Ok(());
};
let box_start = self.abs_base + pos as u64;
let box_end = checked_end(box_start, size)?;
match &typ {
b"moov" if matches!(self.fmp4.phase, Phase::Init) => {
fence_box(&self.opts.memory, size)?;
if size > (buf.len() - pos) as u64 {
return Ok(()); }
let full = &buf[pos..pos + size as usize];
if !moov_is_fragmented(full) {
return Err(AacError::Unsupported(UnsupportedFeature::M4aPush));
}
self.install_fmp4_init(full)?;
self.pos += size as usize;
self.fmp4.phase = Phase::Boxes;
}
b"moov" => {
return Err(AacError::Unsupported(UnsupportedFeature::FragmentedMp4(
"a second init segment is not supported",
)));
}
b"moof" => {
if matches!(self.fmp4.phase, Phase::Init) {
return Err(AacError::Malformed(MalformedKind::Syntax));
}
fence_box(&self.opts.memory, size)?;
if size > (buf.len() - pos) as u64 {
return Ok(()); }
self.fmp4.phase = Phase::Moof {
start: box_start,
end: box_end,
next: box_end,
};
}
_ => self.fmp4.phase = Phase::Skip(box_end),
}
}
}
}
}
fn pump_fmp4_moof(&mut self, buf: &[u8], start: u64, end: u64, next: u64) -> Result<bool> {
let moof_start = usize::try_from(start - self.abs_base)
.map_err(|_| AacError::Malformed(MalformedKind::Syntax))?;
let moof_end = usize::try_from(end - self.abs_base)
.map_err(|_| AacError::Malformed(MalformedKind::Syntax))?;
let moof_head = match peek_box(buf, moof_start)? {
Some((_, head, _)) => head as usize,
None => return Ok(false),
};
let at = usize::try_from(next - self.abs_base)
.map_err(|_| AacError::Malformed(MalformedKind::Syntax))?;
let Some((size, head, typ)) = peek_box(buf, at)? else {
return Ok(false);
};
match &typ {
b"mdat" => {
let content_start = checked_end(next, head)?;
let content_end = checked_end(next, size)?;
if content_start < end {
return Err(AacError::Malformed(MalformedKind::Syntax));
}
let init = self
.fmp4
.init
.as_ref()
.ok_or(AacError::Malformed(MalformedKind::Syntax))?;
self.fmp4.res.add_moof_push(
&buf[moof_start + moof_head..moof_end],
start,
init,
(content_start, content_end),
&mut self.fmp4.frag,
&self.opts.memory,
)?;
self.fmp4.frag_idx = 0;
self.fmp4.mdat_end = content_end;
self.pos = usize::try_from(content_start - self.abs_base)
.map_err(|_| AacError::Malformed(MalformedKind::Syntax))?;
self.fmp4.phase = Phase::Mdat;
Ok(true)
}
b"moof" | b"moov" => Err(AacError::Malformed(MalformedKind::Syntax)),
_ => {
if ((buf.len() - at) as u64) < size {
return Ok(false);
}
self.fmp4.phase = Phase::Moof {
start,
end,
next: checked_end(next, size)?,
};
Ok(true)
}
}
}
fn pump_fmp4_mdat<F>(&mut self, buf: &[u8], on_frame: &mut F) -> Result<bool>
where
F: FnMut(Frame<'_>) -> Result<()>,
{
loop {
let Some(&(off, size)) = self.fmp4.frag.get(self.fmp4.frag_idx) else {
let tail = self.fmp4.mdat_end;
self.fmp4.frag.clear(); self.fmp4.phase = Phase::Skip(tail);
return Ok(true);
};
let cur = self.abs_base + self.pos as u64;
if off < cur {
return Err(AacError::Malformed(MalformedKind::Syntax));
}
let end = off + u64::from(size);
if end > self.abs_base + buf.len() as u64 {
return Ok(false); }
self.pos += usize::try_from(off - cur)
.map_err(|_| AacError::Malformed(MalformedKind::Syntax))?;
let payload = &buf[self.pos..self.pos + size as usize];
self.decode_fmp4_au(payload, on_frame)?;
self.pos += size as usize;
self.fmp4.frag_idx += 1;
if matches!(self.fmp4.phase, Phase::Drain) {
return Ok(true);
}
}
}
fn decode_fmp4_au<F>(&mut self, payload: &[u8], on_frame: &mut F) -> Result<()>
where
F: FnMut(Frame<'_>) -> Result<()>,
{
self.opts.memory.check_declared_au(payload.len() as u64)?;
let (aot, fs, sr, ch) = self
.fmp4
.asc
.ok_or(AacError::Malformed(MalformedKind::Syntax))?;
let mono = matches!(self.opts.channel_mode, ChannelMode::Mono);
let rate = if mono {
self.mono_scratch.clear();
self.dec
.decode_raw_mono_f32(aot, fs, sr, ch, 1, payload, &mut self.mono_scratch)
.map_err(AacError::from)?
} else {
self.dec
.decode_frame_scaled(aot, fs, sr, ch, payload)
.map_err(AacError::from)?
};
self.aac_frames += 1; let (n_ch, n) = if mono {
(1, self.mono_scratch.len())
} else {
let planes = self.dec.frame_planes();
(planes.len(), planes.first().map_or(0, Vec::len))
};
if n_ch == 0 {
return Ok(()); }
self.tally_decoded(rate, n_ch, n)?;
let f = &mut self.fmp4;
if f.skip_left >= n {
f.skip_left -= n; return Ok(());
}
let start = f.skip_left;
f.skip_left = 0;
let mut take = n - start;
if let Some(left) = f.play_left.as_mut() {
if *left == 0 {
f.phase = Phase::Drain; return Ok(());
}
take = take.min(*left);
*left -= take;
}
if take == 0 {
return Ok(());
}
self.samples_out += take as u64;
self.emitted += 1;
let end = start + take;
let mut slots: [&[f32]; crate::layout::MAX_PLANES] = [&[]; crate::layout::MAX_PLANES];
let planar: &[&[f32]] = if mono {
slots[0] = &self.mono_scratch[start..end];
&slots[..1]
} else {
let planes = self.dec.frame_planes();
let n = planes.len().min(crate::layout::MAX_PLANES);
for (i, p) in planes.iter().enumerate().take(n) {
slots[i] = &p[start..end];
}
&slots[..n]
};
on_frame(Frame {
sample_rate: rate,
samples: take,
planar,
meta: self.dec.last_meta(),
})
}
fn install_fmp4_init(&mut self, moov: &[u8]) -> Result<()> {
let init = parse_init(moov, &self.opts.memory)?;
let (asc, _) = AudioSpecificConfig::parse(&init.asc).map_err(AacError::from)?;
let out = asc.output_sample_rate;
if out == 0 || out > self.opts.max_sample_rate {
return Err(AacError::sample_rate(out, self.opts.max_sample_rate));
}
if let Some(pce) = asc.pce.clone() {
self.dec.set_config_pce(pce);
}
self.dec
.set_he_config(asc.sbr_present, asc.ps_present, asc.output_sample_rate);
self.dec.set_frame_samples(asc.frame_samples);
let (skip, play) = init.edit_window(out)?;
self.fmp4.asc = Some((
asc.aot,
asc.sampling_frequency_index,
asc.sample_rate,
asc.channel_configuration,
));
self.fmp4.skip_left = skip;
self.fmp4.skip0 = skip;
self.fmp4.play_left = play;
self.fmp4.init = Some(init);
Ok(())
}
pub(super) fn fmp4_trim(&self) -> (Option<u64>, Option<u64>) {
let has_elst = self.fmp4.init.as_ref().is_some_and(|i| i.has_elst);
(
has_elst.then_some(self.fmp4.skip0 as u64),
has_elst.then_some(
self.fmp4
.res
.total()
.saturating_sub(self.fmp4.skip0 as u64)
.saturating_sub(self.samples_out),
),
)
}
}