1use crate::{FourCc, TrackId};
2use std::fmt;
3
4#[non_exhaustive]
6#[derive(Debug)]
7pub enum Error {
8 Io(std::io::Error),
10 Truncated {
12 at: u64,
14 needed: u64,
16 },
17 BoxSizeTooSmall {
19 kind: FourCc,
21 size: u64,
23 at: u64,
25 },
26 BoxExceedsFile {
28 kind: FourCc,
30 at: u64,
32 declared_end: u64,
34 file_len: u64,
36 },
37 ChildExceedsParent {
39 parent: FourCc,
41 child: FourCc,
43 at: u64,
45 },
46 ZeroSizeNested {
48 kind: FourCc,
50 at: u64,
52 },
53 NestingTooDeep {
55 depth: u8,
57 at: u64,
59 },
60 MissingBox {
62 kind: FourCc,
64 parent: Option<FourCc>,
66 },
67 DuplicateBox {
69 kind: FourCc,
71 parent: FourCc,
73 },
74 Malformed {
76 kind: FourCc,
78 reason: &'static str,
80 },
81 InvalidSampleTable {
83 track: TrackId,
85 reason: &'static str,
87 },
88 InvalidTimescale {
90 track: Option<TrackId>,
92 },
93 SampleOutOfFile {
95 track: TrackId,
97 index: u32,
99 offset: u64,
101 size: u32,
103 file_len: u64,
105 },
106 LimitExceeded {
108 limit: &'static str,
110 value: u64,
112 max: u64,
114 },
115 Unsupported(&'static str),
117 NoSuchTrack(TrackId),
119 NoSuchSample {
121 track: TrackId,
123 index: u32,
125 },
126 Atom(mp4_atom::Error),
128 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}