mp4_track/types.rs
1use std::fmt;
2
3/// A four-byte ISO box or sample-entry code.
4#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
5pub struct FourCc(pub [u8; 4]);
6
7impl FourCc {
8 /// Creates a code from its four bytes.
9 pub const fn new(bytes: [u8; 4]) -> Self {
10 Self(bytes)
11 }
12
13 /// Returns the code bytes.
14 pub const fn into_bytes(self) -> [u8; 4] {
15 self.0
16 }
17
18 /// Returns printable text, replacing invalid bytes.
19 pub fn as_lossy_str(&self) -> String {
20 String::from_utf8_lossy(&self.0).into_owned()
21 }
22}
23
24impl Default for FourCc {
25 fn default() -> Self {
26 Self(*b" ")
27 }
28}
29
30impl From<[u8; 4]> for FourCc {
31 fn from(value: [u8; 4]) -> Self {
32 Self(value)
33 }
34}
35
36impl TryFrom<&str> for FourCc {
37 type Error = &'static str;
38
39 fn try_from(value: &str) -> Result<Self, Self::Error> {
40 let bytes: [u8; 4] = value
41 .as_bytes()
42 .try_into()
43 .map_err(|_| "a fourcc must contain four bytes")?;
44 Ok(Self(bytes))
45 }
46}
47
48impl fmt::Display for FourCc {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 for byte in self.0 {
51 if byte.is_ascii_graphic() || byte == b' ' {
52 write!(f, "{}", char::from(byte))?;
53 } else {
54 write!(f, "\\x{byte:02x}")?;
55 }
56 }
57 Ok(())
58 }
59}
60
61impl fmt::Debug for FourCc {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(f, "FourCc({self})")
64 }
65}
66
67/// A track identifier stored in track and fragment headers.
68#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
69pub struct TrackId(pub u32);
70
71/// File-type brands from the `ftyp` box.
72#[derive(Clone, Debug, PartialEq, Eq, Default)]
73pub struct Brands {
74 /// Primary file brand.
75 pub major: FourCc,
76 /// Brand minor version.
77 pub minor_version: u32,
78 /// Compatible brands in stored order.
79 pub compatible: Vec<FourCc>,
80}
81
82/// The media role of a track.
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum TrackKind {
85 /// Video media.
86 Video,
87 /// Audio media.
88 Audio,
89 /// Timed text or subtitle media.
90 Subtitle,
91 /// Another handler type.
92 Other(FourCc),
93}
94
95/// Codec configuration from a sample description.
96#[derive(Clone, Debug, PartialEq)]
97pub enum Codec {
98 /// AVC configuration for an `avc1` or `avc3` entry.
99 H264 {
100 /// Stored sample-entry code.
101 entry: FourCc,
102 /// Decoder configuration.
103 config: AvcConfig,
104 },
105 /// HEVC configuration for an `hvc1` or `hev1` entry.
106 H265 {
107 /// Stored sample-entry code.
108 entry: FourCc,
109 /// Decoder configuration.
110 config: HevcConfig,
111 },
112 /// AAC configuration from an `mp4a` entry.
113 Aac {
114 /// Decoder configuration.
115 config: AacConfig,
116 },
117 /// Opus configuration from an `Opus` entry.
118 Opus {
119 /// Decoder configuration.
120 config: OpusConfig,
121 },
122 /// An unrecognized sample entry whose body is preserved.
123 Other {
124 /// Stored sample-entry code.
125 entry: FourCc,
126 /// Complete sample-entry body after the size and code fields.
127 raw: Vec<u8>,
128 },
129}
130
131/// A preserved sample-entry child box.
132#[derive(Clone, Debug, PartialEq, Eq)]
133pub struct SidecarBox {
134 /// Box code.
135 pub kind: FourCc,
136 /// Box body bytes.
137 pub payload: Vec<u8>,
138}
139
140/// AVC decoder configuration.
141#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct AvcConfig {
143 /// Complete `avcC` payload.
144 pub raw: Vec<u8>,
145 /// AVC profile indication.
146 pub profile: u8,
147 /// Profile compatibility flags.
148 pub profile_compat: u8,
149 /// AVC level indication.
150 pub level: u8,
151 /// NAL unit length field width in bytes.
152 pub nal_length_size: u8,
153 /// Sequence parameter sets.
154 pub sps: Vec<Vec<u8>>,
155 /// Picture parameter sets.
156 pub pps: Vec<Vec<u8>>,
157 /// Preserved `pasp`, `btrt`, `fiel`, and `colr` boxes in stored order.
158 pub sidecars: Vec<SidecarBox>,
159}
160
161/// One HEVC NAL array.
162#[derive(Clone, Debug, PartialEq, Eq)]
163pub struct NalArray {
164 /// NAL unit type.
165 pub nal_type: u8,
166 /// NAL units in stored order.
167 pub nals: Vec<Vec<u8>>,
168}
169
170/// HEVC decoder configuration.
171#[derive(Clone, Debug, PartialEq, Eq)]
172pub struct HevcConfig {
173 /// Complete `hvcC` payload.
174 pub raw: Vec<u8>,
175 /// NAL unit length field width in bytes.
176 pub nal_length_size: u8,
177 /// Parameter-set arrays in stored order.
178 pub arrays: Vec<NalArray>,
179 /// Preserved `pasp`, `btrt`, `fiel`, and `colr` boxes in stored order.
180 pub sidecars: Vec<SidecarBox>,
181}
182
183/// AAC decoder configuration.
184#[derive(Clone, Debug, PartialEq, Eq)]
185pub struct AacConfig {
186 /// Complete `esds` payload, including full-box fields.
187 pub raw_esds: Vec<u8>,
188 /// MPEG-4 object type indication.
189 pub object_type: u8,
190 /// Decoder-specific AudioSpecificConfig bytes.
191 pub audio_specific_config: Vec<u8>,
192 /// Channel count from the audio sample entry.
193 pub channels: u16,
194 /// Sample rate from the audio sample entry, or the media timescale when
195 /// the entry stores no rate.
196 pub sample_rate: u32,
197 /// Preserved `pasp`, `btrt`, `fiel`, and `colr` boxes in stored order.
198 pub sidecars: Vec<SidecarBox>,
199}
200
201/// Opus decoder configuration.
202#[derive(Clone, Debug, PartialEq, Eq)]
203pub struct OpusConfig {
204 /// Complete `dOps` payload.
205 pub raw: Vec<u8>,
206 /// Output channel count.
207 pub channels: u8,
208 /// Decoder pre-skip in samples.
209 pub pre_skip: u16,
210 /// Input sample rate from the identification header.
211 pub input_sample_rate: u32,
212 /// Output gain in Q7.8 decibels.
213 pub output_gain: i16,
214 /// Channel mapping family.
215 pub mapping_family: u8,
216 /// Preserved `pasp`, `btrt`, `fiel`, and `colr` boxes in stored order.
217 pub sidecars: Vec<SidecarBox>,
218}
219
220/// One movie edit-list entry.
221#[derive(Clone, Debug, PartialEq)]
222pub struct Edit {
223 /// Segment duration in movie timescale units.
224 pub segment_duration: u64,
225 /// Media start time, or `None` for an empty edit.
226 pub media_time: Option<u64>,
227 /// Playback rate stored as 16.16 fixed point.
228 pub media_rate: f32,
229}
230
231/// The box used for chunk offsets.
232#[derive(Clone, Copy, Debug, PartialEq, Eq)]
233pub enum ChunkOffsetKind {
234 /// 32-bit offsets.
235 Stco,
236 /// 64-bit offsets.
237 Co64,
238 /// No progressive chunk offsets.
239 None,
240}
241
242/// Parsed track metadata.
243#[derive(Clone, Debug, PartialEq)]
244pub struct Track {
245 /// Track identifier.
246 pub id: TrackId,
247 /// Handler role.
248 pub kind: TrackKind,
249 /// Handler display name.
250 pub handler_name: String,
251 /// Media timescale.
252 pub timescale: u32,
253 /// Sum of sample durations in media units.
254 pub duration: u64,
255 /// ISO 639-2/T language code.
256 pub language: String,
257 /// Whether the track is enabled.
258 pub enabled: bool,
259 /// Display width.
260 pub width: f64,
261 /// Display height.
262 pub height: f64,
263 /// Primary sample description.
264 pub codec: Codec,
265 /// Movie edit list.
266 pub edit_list: Vec<Edit>,
267 /// Number of samples.
268 pub sample_count: u32,
269 /// Stored chunk-offset width.
270 pub chunk_offsets: ChunkOffsetKind,
271 /// Stored sync-sample count, or `None` when every sample is sync.
272 pub sync_sample_count: Option<u32>,
273}
274
275impl Track {
276 /// Returns the media time of the single non-empty edit.
277 pub fn presentation_offset(&self) -> Option<u64> {
278 let mut non_empty = self
279 .edit_list
280 .iter()
281 .filter(|edit| edit.media_time.is_some());
282 let first = non_empty.next()?;
283 if non_empty.next().is_some() {
284 return None;
285 }
286 first.media_time
287 }
288}
289
290/// Location and timing for one sample.
291#[derive(Clone, Copy, Debug, PartialEq, Eq)]
292pub struct SampleInfo {
293 /// Zero-based track sample index.
294 pub index: u32,
295 /// Absolute byte offset.
296 pub offset: u64,
297 /// Sample size in bytes.
298 pub size: u32,
299 /// Decode time in media units.
300 pub dts: u64,
301 /// Signed composition-time offset.
302 pub cts_offset: i64,
303 /// Presentation time before edit-list mapping.
304 pub pts: i64,
305 /// Sample duration in media units.
306 pub duration: u32,
307 /// Whether this is a random-access sample.
308 pub is_sync: bool,
309 /// Zero-based chunk index, or fragment index for fragmented input.
310 pub chunk: u32,
311 /// One-based sample-description index.
312 pub description_index: u32,
313}
314
315/// Sample bytes and parsed metadata.
316#[derive(Clone, Debug, PartialEq, Eq)]
317pub struct Sample {
318 /// Parsed sample metadata.
319 pub info: SampleInfo,
320 /// Encoded sample bytes.
321 pub data: Vec<u8>,
322}
323
324/// One parsed movie fragment.
325#[derive(Clone, Debug, PartialEq, Eq)]
326pub struct Fragment {
327 /// Fragment sequence number.
328 pub sequence_number: u32,
329 /// Absolute `moof` offset.
330 pub moof_offset: u64,
331 /// Track runs in stored order.
332 pub runs: Vec<TrackRun>,
333}
334
335/// Samples from one fragment track run.
336#[derive(Clone, Debug, PartialEq, Eq)]
337pub struct TrackRun {
338 /// Referenced track.
339 pub track: TrackId,
340 /// Expanded samples.
341 pub samples: Vec<SampleInfo>,
342}
343
344/// A top-level event message.
345#[derive(Clone, Debug, PartialEq, Eq)]
346pub struct Event {
347 /// Event scheme identifier URI.
348 pub scheme_id_uri: String,
349 /// Scheme-specific value.
350 pub value: String,
351 /// Event timescale.
352 pub timescale: u32,
353 /// Presentation time.
354 pub presentation_time: u64,
355 /// Event duration.
356 pub event_duration: u32,
357 /// Event identifier.
358 pub id: u32,
359 /// Scheme-specific message bytes.
360 pub message_data: Vec<u8>,
361}
362
363/// Parsed user metadata.
364#[derive(Clone, Debug, PartialEq, Eq, Default)]
365pub struct Metadata {
366 /// Metadata entries in stored order.
367 pub entries: Vec<(MetaKey, MetaValue)>,
368}
369
370/// A recognized metadata key.
371#[derive(Clone, Debug, PartialEq, Eq)]
372pub enum MetaKey {
373 /// Title.
374 Title,
375 /// Artist.
376 Artist,
377 /// Album.
378 Album,
379 /// Album artist.
380 AlbumArtist,
381 /// Composer.
382 Composer,
383 /// Genre.
384 Genre,
385 /// Comment.
386 Comment,
387 /// Release date.
388 Date,
389 /// Another four-byte key.
390 Other(FourCc),
391}
392
393/// A metadata value.
394#[derive(Clone, Debug, PartialEq, Eq)]
395pub enum MetaValue {
396 /// UTF-8 text.
397 Text(String),
398 /// Uninterpreted bytes.
399 Binary(Vec<u8>),
400 /// Big-endian integer.
401 Integer(i64),
402}
403
404/// A tolerated file oddity.
405#[derive(Clone, Debug, PartialEq, Eq)]
406pub enum Warning {
407 /// Bytes remain after the last complete top-level box.
408 TrailingBytes {
409 /// Offset where trailing data begins.
410 at: u64,
411 /// Number of trailing bytes.
412 count: u64,
413 },
414 /// An unknown box was skipped.
415 UnknownBox {
416 /// Unknown box code.
417 kind: FourCc,
418 /// Enclosing box code, when known.
419 parent: Option<FourCc>,
420 },
421}
422
423/// Resource bounds applied while opening and reading.
424#[non_exhaustive]
425#[derive(Clone, Copy, Debug, PartialEq, Eq)]
426pub struct Limits {
427 /// Maximum accepted movie box size. Defaults to 256 MiB.
428 pub max_moov_bytes: u64,
429 /// Maximum accepted fragment box size. Defaults to 64 MiB.
430 pub max_moof_bytes: u64,
431 /// Maximum bytes read for one sample. Defaults to 64 MiB.
432 pub max_sample_bytes: u32,
433 /// Maximum fragment count. Defaults to one million.
434 pub max_fragments: u32,
435 /// Maximum expanded sample count for one track. Defaults to 500,000.
436 pub max_sample_count: u32,
437 /// Maximum structural nesting. Defaults to 32.
438 pub max_depth: u8,
439}
440
441impl Default for Limits {
442 fn default() -> Self {
443 Self {
444 max_moov_bytes: 256 * 1024 * 1024,
445 max_moof_bytes: 64 * 1024 * 1024,
446 max_sample_bytes: 64 * 1024 * 1024,
447 max_fragments: 1_000_000,
448 max_sample_count: 500_000,
449 max_depth: 32,
450 }
451 }
452}
453
454/// Writer selection for chunk-offset boxes.
455#[derive(Clone, Copy, Debug, PartialEq, Eq)]
456pub enum ChunkOffsetChoice {
457 /// Select the narrowest valid offset box.
458 Auto,
459 /// Always write 64-bit offsets.
460 Co64,
461}
462
463/// File-level writer settings.
464#[derive(Clone, Debug, PartialEq, Eq)]
465pub struct WriterConfig {
466 /// Movie timescale. Defaults to 1000.
467 pub movie_timescale: u32,
468 /// Primary output brand.
469 pub major_brand: FourCc,
470 /// Compatible output brands.
471 pub compatible_brands: Vec<FourCc>,
472 /// Chunk-offset width selection.
473 pub chunk_offsets: ChunkOffsetChoice,
474}
475
476impl Default for WriterConfig {
477 fn default() -> Self {
478 Self {
479 movie_timescale: 1000,
480 major_brand: FourCc(*b"isom"),
481 compatible_brands: vec![FourCc(*b"isom"), FourCc(*b"iso2"), FourCc(*b"mp41")],
482 chunk_offsets: ChunkOffsetChoice::Auto,
483 }
484 }
485}
486
487/// Track settings supplied to the writer.
488#[derive(Clone, Debug, PartialEq)]
489pub struct TrackConfig {
490 /// Handler role.
491 pub kind: TrackKind,
492 /// Media timescale.
493 pub timescale: u32,
494 /// ISO 639-2/T language code.
495 pub language: String,
496 /// Handler display name.
497 pub handler_name: String,
498 /// Sample description.
499 pub codec: Codec,
500 /// Display width.
501 pub width: f64,
502 /// Display height.
503 pub height: f64,
504 /// Movie edit list.
505 pub edit_list: Vec<Edit>,
506}
507
508/// One sample supplied to the writer.
509#[derive(Clone, Copy, Debug, PartialEq, Eq)]
510pub struct SampleInput<'a> {
511 /// Encoded sample bytes.
512 pub data: &'a [u8],
513 /// Decode duration in media units.
514 pub duration: u32,
515 /// Signed composition-time offset.
516 pub cts_offset: i32,
517 /// Whether this is a random-access sample.
518 pub is_sync: bool,
519}