use crate::budgets::{BudgetExceeded, BudgetKind};
use crate::engine::error::Error as EngineError;
use std::fmt;
use std::io;
#[path = "error_feat.rs"]
mod error_feat;
pub type Result<T> = std::result::Result<T, AacError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum UnsupportedFeature {
AudioObjectType(u8),
FrameLength960,
HeFrame960,
ChannelConfiguration(u8),
SampleRateIndex(u8),
LatmVersionA,
LatmFrameLengthType,
Extension(u8),
M4aPush,
EncodeM4aStreaming,
EncodeRawOneShot,
EncodeHeRate(u32),
RawAccessUnit,
AscChange,
FragmentedMp4(&'static str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum MalformedKind {
AdtsSync,
AdtsLayer,
AdtsFrameLength,
AdtsCrc,
Huffman,
Ics,
Section,
Spectrum,
Filterbank,
Codebook,
LatmCrc,
LatmConfig,
LatmMux,
Sbr,
Ps,
Syntax,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LifecycleState {
Failed,
Finished,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PcmReject {
NonFinite,
Amplitude,
Empty,
PlaneLength,
ChannelCount,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum AacError {
Io(io::Error),
NotAac,
UnsupportedSampleRate { rate: u32, max: u32 },
TooLong { observed_secs: f64, max_secs: f64 },
InvalidLimits(String),
Limit {
kind: BudgetKind,
observed: u64,
max: u64,
},
Unsupported(UnsupportedFeature),
Truncated { at: Option<u64> },
NeedMore { have: u64, need: u64 },
Malformed(MalformedKind),
Lifecycle { state: LifecycleState },
InvalidPcm(PcmReject),
Format(String),
Decode(String),
Encode(String),
}
impl AacError {
#[inline]
pub fn format(msg: impl Into<String>) -> Self {
Self::Format(msg.into())
}
#[inline]
pub fn decode(msg: impl Into<String>) -> Self {
Self::Decode(msg.into())
}
#[inline]
pub fn encode(msg: impl Into<String>) -> Self {
Self::Encode(msg.into())
}
#[inline]
pub fn too_long(observed_secs: f64, max_secs: f64) -> Self {
Self::TooLong {
observed_secs,
max_secs,
}
}
#[inline]
pub fn sample_rate(rate: u32, max: u32) -> Self {
Self::UnsupportedSampleRate { rate, max }
}
#[inline]
pub fn invalid_limits(msg: impl Into<String>) -> Self {
Self::InvalidLimits(msg.into())
}
#[inline]
pub fn limit(kind: BudgetKind, observed: u64, max: u64) -> Self {
Self::Limit {
kind,
observed,
max,
}
}
#[inline]
pub fn truncated_at(at: Option<u64>) -> Self {
Self::Truncated { at }
}
#[inline]
pub fn with_truncated_at(self, at: u64) -> Self {
match self {
Self::Truncated { at: None } => Self::Truncated { at: Some(at) },
other => other,
}
}
pub fn is_format_class(&self) -> bool {
matches!(self, Self::NotAac | Self::Format(_))
}
}
impl fmt::Display for MalformedKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::AdtsSync => "aac: ADTS sync not found",
Self::AdtsLayer => "aac: ADTS layer must be 0",
Self::AdtsFrameLength => "aac: ADTS frame length too small",
Self::AdtsCrc => "aac: ADTS CRC mismatch",
Self::Huffman => "aac: Huffman codeword invalid",
Self::Ics => "aac: invalid ics_info",
Self::Section => "aac: section_data overrun",
Self::Spectrum => "aac: spectral geometry invalid",
Self::Filterbank => "aac: filterbank length mismatch",
Self::Codebook => "aac: invalid codebook",
Self::LatmCrc => "aac: LATM CRC mismatch",
Self::LatmConfig => "aac: LATM config out of range",
Self::LatmMux => "aac: LATM missing StreamMuxConfig",
Self::Sbr => "aac: SBR payload invalid",
Self::Ps => "aac: PS data invalid",
Self::Syntax => "aac: malformed syntax",
};
f.write_str(s)
}
}
impl fmt::Display for AacError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "{e}"),
Self::NotAac => write!(f, "Unsupported audio format"),
Self::UnsupportedSampleRate { rate, max } => {
write!(f, "Unsupported sample rate: {rate}Hz (max {max}Hz)")
}
Self::TooLong {
observed_secs,
max_secs,
} => write!(
f,
"Audio file too long ({observed_secs:.0}s). Maximum supported: {max_secs:.0}s."
),
Self::InvalidLimits(msg) => write!(f, "invalid decode limits: {msg}"),
Self::Limit {
kind,
observed,
max,
} => write!(f, "decode budget exceeded ({kind}: {observed} > {max})"),
Self::Unsupported(feat) => write!(f, "{feat}"),
Self::Truncated { at } => match at {
Some(n) => write!(f, "aac: unexpected end of bitstream at byte {n}"),
None => write!(f, "aac: unexpected end of bitstream"),
},
Self::NeedMore { have, need } => {
write!(f, "aac: need more bytes ({have} of {need})")
}
Self::Malformed(kind) => write!(f, "{kind}"),
Self::Lifecycle { state } => match state {
LifecycleState::Failed => write!(f, "stream failed; call reset()"),
LifecycleState::Finished => write!(f, "stream already finished; call reset()"),
},
Self::InvalidPcm(PcmReject::NonFinite) => write!(f, "encode: non-finite sample"),
Self::InvalidPcm(PcmReject::Amplitude) => {
write!(f, "encode: sample amplitude exceeds ±1")
}
Self::InvalidPcm(PcmReject::Empty) => write!(f, "encode: empty input"),
Self::InvalidPcm(PcmReject::PlaneLength) => {
write!(f, "encode: channel planes differ in length")
}
Self::InvalidPcm(PcmReject::ChannelCount) => {
write!(f, "encode: channel plane count mismatch")
}
Self::Format(msg) | Self::Decode(msg) | Self::Encode(msg) => write!(f, "{msg}"),
}
}
}
impl std::error::Error for AacError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
_ => None,
}
}
}
impl From<io::Error> for AacError {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl From<BudgetExceeded> for AacError {
fn from(e: BudgetExceeded) -> Self {
Self::Limit {
kind: e.kind,
observed: e.observed,
max: e.max,
}
}
}
impl From<EngineError> for AacError {
fn from(value: EngineError) -> Self {
use MalformedKind as M;
use UnsupportedFeature as U;
match value {
EngineError::UnexpectedEnd => Self::Truncated { at: None },
EngineError::AdtsSyncNotFound => Self::Malformed(M::AdtsSync),
EngineError::AdtsLayerNonZero => Self::Malformed(M::AdtsLayer),
EngineError::AdtsReservedSampleRateIndex => Self::Unsupported(U::SampleRateIndex(0xFF)),
EngineError::AdtsFrameLengthTooSmall => Self::Malformed(M::AdtsFrameLength),
EngineError::AdtsCrcMismatch => Self::Malformed(M::AdtsCrc),
EngineError::UnsupportedAot(a) => Self::Unsupported(U::AudioObjectType(a)),
EngineError::UnsupportedFrameLength => Self::Unsupported(U::FrameLength960),
EngineError::UnsupportedHeFrame960 => Self::Unsupported(U::HeFrame960),
EngineError::UnsupportedChannelConfiguration(c) => {
Self::Unsupported(U::ChannelConfiguration(c))
}
EngineError::UnsupportedSampleRateIndex(i) => Self::Unsupported(U::SampleRateIndex(i)),
EngineError::IcsInfoInvalid => Self::Malformed(M::Ics),
EngineError::SectionDataOverrun => Self::Malformed(M::Section),
EngineError::InvalidCodebook(_) => Self::Malformed(M::Codebook),
EngineError::HuffmanInvalid => Self::Malformed(M::Huffman),
EngineError::SpectrumInvalid => Self::Malformed(M::Spectrum),
EngineError::FilterbankInvalid => Self::Malformed(M::Filterbank),
EngineError::LoasSyncInvalid => Self::Malformed(M::LatmMux),
EngineError::LatmAudioMuxVersionAReserved => Self::Unsupported(U::LatmVersionA),
EngineError::LatmConfigOutOfRange => Self::Malformed(M::LatmConfig),
EngineError::LatmCrcMismatch => Self::Malformed(M::LatmCrc),
EngineError::LatmNoPreviousMuxConfig => Self::Malformed(M::LatmMux),
EngineError::LatmUnsupportedFrameLengthType => {
Self::Unsupported(U::LatmFrameLengthType)
}
EngineError::SbrQmfInvalid
| EngineError::SbrFreqBandInvalid
| EngineError::SbrGridInvalid
| EngineError::SbrHuffInvalid
| EngineError::ExtensionPayloadInvalid => Self::Malformed(M::Sbr),
EngineError::PsDataInvalid => Self::Malformed(M::Ps),
EngineError::UnsupportedExtensionSbr(t) | EngineError::UnsupportedExtensionType(t) => {
Self::Unsupported(U::Extension(t))
}
EngineError::UnsupportedCce => Self::Unsupported(U::Extension(0)),
EngineError::Format(msg) => Self::Decode(format!("aac: {msg}")),
}
}
}