Skip to main content

mp4_track/
error.rs

1use crate::{FourCc, TrackId};
2use std::fmt;
3
4/// Errors produced by parsing, sample reads, and writing.
5#[non_exhaustive]
6#[derive(Debug)]
7pub enum Error {
8    /// An input or output operation failed.
9    Io(std::io::Error),
10    /// A header or fixed field ended early.
11    Truncated {
12        /// Absolute byte offset.
13        at: u64,
14        /// Required byte count.
15        needed: u64,
16    },
17    /// A box size is smaller than its header.
18    BoxSizeTooSmall {
19        /// Box code.
20        kind: FourCc,
21        /// Declared size.
22        size: u64,
23        /// Absolute box offset.
24        at: u64,
25    },
26    /// A top-level box extends beyond the stream.
27    BoxExceedsFile {
28        /// Box code.
29        kind: FourCc,
30        /// Absolute box offset.
31        at: u64,
32        /// Declared box end.
33        declared_end: u64,
34        /// Stream length.
35        file_len: u64,
36    },
37    /// A child box extends beyond its parent.
38    ChildExceedsParent {
39        /// Parent box code.
40        parent: FourCc,
41        /// Child box code.
42        child: FourCc,
43        /// Absolute child offset.
44        at: u64,
45    },
46    /// A nested box uses a zero size.
47    ZeroSizeNested {
48        /// Box code.
49        kind: FourCc,
50        /// Absolute box offset.
51        at: u64,
52    },
53    /// Structural nesting exceeds the configured limit.
54    NestingTooDeep {
55        /// Observed depth.
56        depth: u8,
57        /// Absolute box offset.
58        at: u64,
59    },
60    /// A required box is missing.
61    MissingBox {
62        /// Missing box code.
63        kind: FourCc,
64        /// Expected parent.
65        parent: Option<FourCc>,
66    },
67    /// A box that must be unique appears more than once.
68    DuplicateBox {
69        /// Repeated box code.
70        kind: FourCc,
71        /// Enclosing box code.
72        parent: FourCc,
73    },
74    /// A known box has invalid fields.
75    Malformed {
76        /// Box code.
77        kind: FourCc,
78        /// Stable failure description.
79        reason: &'static str,
80    },
81    /// A sample table is inconsistent.
82    InvalidSampleTable {
83        /// Affected track.
84        track: TrackId,
85        /// Stable failure description.
86        reason: &'static str,
87    },
88    /// A movie or media timescale is zero.
89    InvalidTimescale {
90        /// Affected track, or `None` for the movie.
91        track: Option<TrackId>,
92    },
93    /// A sample points beyond the stream.
94    SampleOutOfFile {
95        /// Affected track.
96        track: TrackId,
97        /// Zero-based sample index.
98        index: u32,
99        /// Absolute sample offset.
100        offset: u64,
101        /// Sample size.
102        size: u32,
103        /// Stream length.
104        file_len: u64,
105    },
106    /// A configured resource limit was exceeded.
107    LimitExceeded {
108        /// Limit name.
109        limit: &'static str,
110        /// Observed value.
111        value: u64,
112        /// Configured maximum.
113        max: u64,
114    },
115    /// The file uses an unsupported feature.
116    Unsupported(&'static str),
117    /// No track has the requested identifier.
118    NoSuchTrack(TrackId),
119    /// The requested sample index is absent.
120    NoSuchSample {
121        /// Requested track.
122        track: TrackId,
123        /// Zero-based sample index.
124        index: u32,
125    },
126    /// The box-layer dependency rejected data.
127    Atom(mp4_atom::Error),
128    /// A writer method was called in an invalid state.
129    WriterState(&'static str),
130}
131
132impl fmt::Display for Error {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self {
135            Self::Io(error) => write!(f, "I/O error: {error}"),
136            Self::Truncated { at, needed } => write!(f, "truncated data at {at}, need {needed} bytes"),
137            Self::BoxSizeTooSmall { kind, size, at } => write!(f, "box {kind} at {at} has invalid size {size}"),
138            Self::BoxExceedsFile { kind, at, declared_end, file_len } => {
139                write!(f, "box {kind} at {at} ends at {declared_end}, past file length {file_len}")
140            }
141            Self::ChildExceedsParent { parent, child, at } => write!(f, "box {child} at {at} extends beyond parent {parent}"),
142            Self::ZeroSizeNested { kind, at } => write!(f, "nested box {kind} at {at} has size zero"),
143            Self::NestingTooDeep { depth, at } => write!(f, "box nesting depth {depth} exceeds the limit at {at}"),
144            Self::MissingBox { kind, parent: Some(parent) } => write!(f, "missing {kind} box in {parent}"),
145            Self::MissingBox { kind, parent: None } => write!(f, "missing top-level {kind} box"),
146            Self::DuplicateBox { kind, parent } => write!(f, "duplicate {kind} box in {parent}"),
147            Self::Malformed { kind, reason } => write!(f, "malformed {kind} box: {reason}"),
148            Self::InvalidSampleTable { track, reason } => write!(f, "invalid sample table for track {}: {reason}", track.0),
149            Self::InvalidTimescale { track: Some(track) } => write!(f, "track {} has a zero timescale", track.0),
150            Self::InvalidTimescale { track: None } => f.write_str("movie has a zero timescale"),
151            Self::SampleOutOfFile { track, index, offset, size, file_len } => write!(
152                f,
153                "sample {index} from track {} at {offset} with size {size} exceeds file length {file_len}",
154                track.0
155            ),
156            Self::LimitExceeded { limit, value, max } => write!(f, "{limit} value {value} exceeds limit {max}"),
157            Self::Unsupported(feature) => write!(f, "unsupported MP4 feature: {feature}"),
158            Self::NoSuchTrack(track) => write!(f, "track {} does not exist", track.0),
159            Self::NoSuchSample { track, index } => write!(f, "sample {index} does not exist in track {}", track.0),
160            Self::Atom(error) => write!(f, "atom decode error: {error}"),
161            Self::WriterState(reason) => write!(f, "invalid writer state: {reason}"),
162        }
163    }
164}
165
166impl std::error::Error for Error {
167    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
168        match self {
169            Self::Io(error) => Some(error),
170            Self::Atom(error) => Some(error),
171            _ => None,
172        }
173    }
174}
175
176impl From<std::io::Error> for Error {
177    fn from(value: std::io::Error) -> Self {
178        Self::Io(value)
179    }
180}
181
182impl From<mp4_atom::Error> for Error {
183    fn from(value: mp4_atom::Error) -> Self {
184        Self::Atom(value)
185    }
186}