use std::io::{Read, Seek, SeekFrom};
use crate::budgets::{BudgetExceeded, BudgetKind, pcm_bytes};
use crate::engine::adts::AdtsHeader;
use crate::error::{AacError, Result};
use crate::isomp4::sniff_is_isobmff;
use crate::options::{ChannelMode, DEFAULT_MAX_INPUT_BYTES, DecodeOptions};
use crate::stream::{
decode_read_streaming, decode_seek_streaming, decode_streaming, decode_streaming_mono_into,
};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct DecodedAac {
pub sample_rate: u32,
pub channels: Vec<Vec<f32>>,
pub core_rate: u32,
pub layout: crate::Layout,
pub priming: Option<u64>,
pub remainder: Option<u64>,
}
impl DecodedAac {
pub(crate) fn from_info(info: crate::StreamInfo, channels: Vec<Vec<f32>>) -> Self {
Self {
sample_rate: info.sample_rate,
channels,
core_rate: info.core_rate,
layout: info.layout,
priming: info.priming,
remainder: info.remainder,
}
}
}
#[inline]
pub fn decode(data: &[u8]) -> Result<DecodedAac> {
decode_with(data, &DecodeOptions::speech())
}
#[inline]
pub fn decode_bytes(data: &[u8]) -> Result<DecodedAac> {
decode(data)
}
pub fn read(path: impl AsRef<std::path::Path>) -> Result<DecodedAac> {
read_with(path, &DecodeOptions::speech())
}
pub fn read_with(path: impl AsRef<std::path::Path>, opts: &DecodeOptions) -> Result<DecodedAac> {
let path = path.as_ref();
let mut f = std::fs::File::open(path)?;
let mut head = [0u8; 12];
let n = f.read(&mut head)?;
if sniff_is_isobmff(&head[..n]) {
f.seek(SeekFrom::Start(0))?;
return decode_seek_with(f, opts);
}
drop(f);
decode_with(&read_file_capped(path, DEFAULT_MAX_INPUT_BYTES)?, opts)
}
#[inline]
pub fn decode_seek<R: Read + Seek>(reader: R) -> Result<DecodedAac> {
decode_seek_with(reader, &DecodeOptions::speech())
}
pub fn decode_seek_with<R: Read + Seek>(reader: R, opts: &DecodeOptions) -> Result<DecodedAac> {
let mut tracks: Vec<Vec<f32>> = Vec::new();
let info = decode_seek_streaming(reader, opts, |f| {
let n_ch = u32::try_from(f.planar.len()).unwrap_or(u32::MAX);
if tracks.is_empty() {
opts.memory.check_channels(n_ch)?;
tracks.resize_with(f.planar.len(), Vec::new);
}
let next = tracks.first().map(|t| t.len() as u64).unwrap_or(0) + f.samples as u64;
opts.memory.check_output(n_ch, next)?;
for (dst, src) in tracks.iter_mut().zip(f.planar.iter()) {
dst.extend_from_slice(src);
}
Ok(())
})?;
Ok(DecodedAac::from_info(info, tracks))
}
#[inline]
pub fn decode_read<R: Read>(reader: R) -> Result<DecodedAac> {
decode_read_with(reader, &DecodeOptions::speech())
}
pub fn decode_read_with<R: Read>(reader: R, opts: &DecodeOptions) -> Result<DecodedAac> {
let mut tracks: Vec<Vec<f32>> = Vec::new();
let info = decode_read_streaming(reader, opts, |f| {
let n_ch = u32::try_from(f.planar.len()).unwrap_or(u32::MAX);
if tracks.is_empty() {
opts.memory.check_channels(n_ch)?;
tracks.resize_with(f.planar.len(), Vec::new);
}
let next = tracks.first().map(|t| t.len() as u64).unwrap_or(0) + f.samples as u64;
opts.memory.check_output(n_ch, next)?;
for (dst, src) in tracks.iter_mut().zip(f.planar.iter()) {
dst.extend_from_slice(src);
}
Ok(())
})?;
Ok(DecodedAac::from_info(info, tracks))
}
pub(crate) fn read_file_capped(path: impl AsRef<std::path::Path>, limit: u64) -> Result<Vec<u8>> {
let path = path.as_ref();
let meta = std::fs::metadata(path)?;
if meta.len() > limit {
return Err(AacError::too_long(meta.len() as f64 / 40_000.0, 0.0));
}
let file = std::fs::File::open(path)?;
let mut buf = Vec::new();
file.take(limit.saturating_add(1)).read_to_end(&mut buf)?;
if buf.len() as u64 > limit {
return Err(AacError::too_long(buf.len() as f64 / 40_000.0, 0.0));
}
Ok(buf)
}
pub fn decode_with(data: &[u8], opts: &DecodeOptions) -> Result<DecodedAac> {
let est_frames = adts_frame_estimate(data);
if matches!(opts.channel_mode, ChannelMode::Mono) && !sniff_is_isobmff(data) {
let mut track: Vec<f32> = Vec::new();
let info = decode_streaming_mono_into(data, opts, est_frames, &mut track)?;
return Ok(DecodedAac::from_info(info, vec![track]));
}
let mut tracks: Vec<Vec<f32>> = Vec::new();
let info = decode_streaming(data, opts, |f| {
let n_ch = u32::try_from(f.planar.len()).unwrap_or(u32::MAX);
if tracks.is_empty() {
opts.memory.check_channels(n_ch)?;
tracks.resize_with(f.planar.len(), Vec::new);
if let Some(frames) = est_frames {
let per = (frames * f.samples).min(opts.max_frames(f.sample_rate));
if opts.memory.check_output(n_ch, per as u64).is_ok() {
for t in &mut tracks {
t.try_reserve(per).map_err(|_| {
AacError::from(BudgetExceeded {
kind: BudgetKind::Output,
observed: pcm_bytes(n_ch, per as u64).unwrap_or(u64::MAX),
max: opts.memory.effective_output_bytes(),
})
})?;
}
}
}
}
let next = tracks.first().map(|t| t.len() as u64).unwrap_or(0) + f.samples as u64;
opts.memory.check_output(n_ch, next)?;
for (dst, src) in tracks.iter_mut().zip(f.planar.iter()) {
dst.extend_from_slice(src);
}
Ok(())
})?;
Ok(DecodedAac::from_info(info, tracks))
}
fn adts_frame_estimate(data: &[u8]) -> Option<usize> {
let mut pos = 0usize;
let mut frames = 0usize;
while let Ok((hdr, _)) = AdtsHeader::parse(&data[pos..]) {
let len = usize::from(hdr.aac_frame_length);
if len == 0 || pos + len > data.len() {
break;
}
pos += len;
frames += 1;
}
(frames > 0).then_some(frames)
}