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