1use crate::parse::{
2 children, one, validate_tree, validate_typed_atom, BoxHeader, Cursor, FTYP, MDAT, MOOF, MOOV,
3};
4use crate::*;
5use std::collections::BTreeMap;
6use std::io::{Read, Seek, SeekFrom};
7use std::time::Duration;
8
9const MVHD: FourCc = FourCc(*b"mvhd");
10const TRAK: FourCc = FourCc(*b"trak");
11const TKHD: FourCc = FourCc(*b"tkhd");
12const MDIA: FourCc = FourCc(*b"mdia");
13const MDHD: FourCc = FourCc(*b"mdhd");
14const HDLR: FourCc = FourCc(*b"hdlr");
15const MINF: FourCc = FourCc(*b"minf");
16const STBL: FourCc = FourCc(*b"stbl");
17const STSD: FourCc = FourCc(*b"stsd");
18const STTS: FourCc = FourCc(*b"stts");
19const CTTS: FourCc = FourCc(*b"ctts");
20const STSS: FourCc = FourCc(*b"stss");
21const STSC: FourCc = FourCc(*b"stsc");
22const STSZ: FourCc = FourCc(*b"stsz");
23const STZ2: FourCc = FourCc(*b"stz2");
24const STCO: FourCc = FourCc(*b"stco");
25const CO64: FourCc = FourCc(*b"co64");
26const EDTS: FourCc = FourCc(*b"edts");
27const ELST: FourCc = FourCc(*b"elst");
28const MVEX: FourCc = FourCc(*b"mvex");
29const TREX: FourCc = FourCc(*b"trex");
30const EMSG: FourCc = FourCc(*b"emsg");
31const UDTA: FourCc = FourCc(*b"udta");
32const DINF: FourCc = FourCc(*b"dinf");
33
34pub struct Mp4Reader<R: Read + Seek> {
36 reader: R,
37 file_len: u64,
38 brands: Brands,
39 movie_timescale: u32,
40 movie_duration: u64,
41 tracks: Vec<Track>,
42 samples: Vec<Vec<SampleInfo>>,
43 fragments: Vec<Fragment>,
44 has_mvex: bool,
45 events: Vec<Event>,
46 metadata: Metadata,
47 warnings: Vec<Warning>,
48 limits: Limits,
49}
50
51impl<R: Read + Seek> Mp4Reader<R> {
52 pub fn open(reader: R) -> Result<Self, Error> {
54 Self::open_with(reader, Limits::default())
55 }
56
57 pub fn open_with(mut reader: R, limits: Limits) -> Result<Self, Error> {
59 let file_len = reader.seek(SeekFrom::End(0))?;
60 let mut at = 0u64;
61 let mut brands = Brands::default();
62 let mut movie: Option<ParsedMovie> = None;
63 let mut moofs = Vec::new();
64 let mut events = Vec::new();
65 let mut warnings = Vec::new();
66
67 while at < file_len {
68 let remaining = file_len.saturating_sub(at);
69 if remaining < 8 {
70 if movie.is_some() {
71 warnings.push(Warning::TrailingBytes {
72 at,
73 count: remaining,
74 });
75 break;
76 }
77 return Err(Error::Truncated { at, needed: 8 });
78 }
79 let step = read_top_header(&mut reader, at, file_len).and_then(|header| {
80 let end = header
81 .start
82 .checked_add(header.size)
83 .ok_or(Error::BoxExceedsFile {
84 kind: header.kind,
85 at,
86 declared_end: u64::MAX,
87 file_len,
88 })?;
89 Ok((header, end))
90 });
91 let (header, mut end) = match step {
92 Ok(value) => value,
93 Err(Error::Io(error)) => return Err(Error::Io(error)),
94 Err(_) if movie.is_some() => {
97 warnings.push(Warning::TrailingBytes {
98 at,
99 count: remaining,
100 });
101 break;
102 }
103 Err(error) => return Err(error),
104 };
105 if end > file_len {
106 if header.kind == MDAT && movie.is_some() {
107 end = file_len;
108 } else if movie.is_some() {
109 warnings.push(Warning::TrailingBytes {
110 at,
111 count: remaining,
112 });
113 break;
114 } else {
115 return Err(Error::BoxExceedsFile {
116 kind: header.kind,
117 at,
118 declared_end: end,
119 file_len,
120 });
121 }
122 }
123 let body_len =
124 header
125 .size
126 .checked_sub(header.header_len)
127 .ok_or(Error::BoxSizeTooSmall {
128 kind: header.kind,
129 size: header.size,
130 at,
131 })?;
132 match header.kind {
133 FTYP => {
134 let body = read_body(&mut reader, &header, body_len, u64::from(u32::MAX))?;
135 brands = parse_ftyp(&body)?;
136 validate_typed_atom(FTYP, &body)?;
137 }
138 MOOV => {
139 if movie.is_some() {
140 return Err(Error::DuplicateBox {
141 kind: MOOV,
142 parent: FourCc(*b"root"),
143 });
144 }
145 if header.size > limits.max_moov_bytes {
146 return Err(Error::LimitExceeded {
147 limit: "max_moov_bytes",
148 value: header.size,
149 max: limits.max_moov_bytes,
150 });
151 }
152 let body = read_body(&mut reader, &header, body_len, limits.max_moov_bytes)?;
153 let body_at =
154 header
155 .start
156 .checked_add(header.header_len)
157 .ok_or(Error::Malformed {
158 kind: MOOV,
159 reason: "movie body offset overflow",
160 })?;
161 validate_tree(&body, MOOV, body_at, limits)?;
162 let parsed = parse_movie(&body, body_at, limits, &mut warnings)?;
163 validate_typed_atom(MOOV, &body)?;
164 movie = Some(parsed);
165 }
166 MOOF => {
167 let count = u32::try_from(moofs.len()).unwrap_or(u32::MAX);
168 if count >= limits.max_fragments {
169 return Err(Error::LimitExceeded {
170 limit: "max_fragments",
171 value: u64::from(count).saturating_add(1),
172 max: u64::from(limits.max_fragments),
173 });
174 }
175 if header.size > limits.max_moof_bytes {
176 return Err(Error::LimitExceeded {
177 limit: "max_moof_bytes",
178 value: header.size,
179 max: limits.max_moof_bytes,
180 });
181 }
182 let body = read_body(&mut reader, &header, body_len, limits.max_moof_bytes)?;
183 let body_at =
184 header
185 .start
186 .checked_add(header.header_len)
187 .ok_or(Error::Malformed {
188 kind: MOOF,
189 reason: "fragment body offset overflow",
190 })?;
191 validate_tree(&body, MOOF, body_at, limits)?;
192 moofs.push((header.start, body_at, body));
193 }
194 EMSG => {
195 let body = read_body(&mut reader, &header, body_len, limits.max_moof_bytes)?;
196 let event = parse_event(&body)?;
197 validate_typed_atom(EMSG, &body)?;
198 events.push(event);
199 }
200 kind if [
201 MDAT,
202 FourCc(*b"free"),
203 FourCc(*b"skip"),
204 FourCc(*b"sidx"),
205 FourCc(*b"styp"),
206 FourCc(*b"mfra"),
207 FourCc(*b"uuid"),
208 ]
209 .contains(&kind) => {}
210 kind => warnings.push(Warning::UnknownBox { kind, parent: None }),
211 }
212 at = end;
213 if header.size_zero {
214 break;
215 }
216 }
217
218 let mut parsed = movie.ok_or(Error::MissingBox {
219 kind: MOOV,
220 parent: None,
221 })?;
222 let mut fragments = Vec::new();
223 let mut running_dts: BTreeMap<TrackId, u64> = BTreeMap::new();
224 for (fragment_index, (moof_at, body_at, body)) in moofs.iter().enumerate() {
225 let mut fragment = parse_fragment(
226 body,
227 *body_at,
228 *moof_at,
229 u32::try_from(fragment_index).map_err(|_| Error::LimitExceeded {
230 limit: "max_fragments",
231 value: u64::try_from(fragment_index).unwrap_or(u64::MAX),
232 max: u64::from(limits.max_fragments),
233 })?,
234 &parsed.trex,
235 limits,
236 &mut running_dts,
237 &mut warnings,
238 )?;
239 validate_typed_atom(MOOF, body)?;
240 for run in &mut fragment.runs {
241 let position = parsed
242 .tracks
243 .iter()
244 .position(|track| track.id == run.track)
245 .ok_or(Error::NoSuchTrack(run.track))?;
246 let list = parsed
247 .samples
248 .get_mut(position)
249 .ok_or(Error::NoSuchTrack(run.track))?;
250 let total = list.len().saturating_add(run.samples.len());
251 if u64::try_from(total).unwrap_or(u64::MAX) > u64::from(limits.max_sample_count) {
252 return Err(Error::LimitExceeded {
253 limit: "max_sample_count",
254 value: u64::try_from(total).unwrap_or(u64::MAX),
255 max: u64::from(limits.max_sample_count),
256 });
257 }
258 let base_index = u32::try_from(list.len()).map_err(|_| Error::LimitExceeded {
259 limit: "sample_count",
260 value: u64::try_from(list.len()).unwrap_or(u64::MAX),
261 max: u64::from(u32::MAX),
262 })?;
263 for (offset, sample) in run.samples.iter_mut().enumerate() {
264 sample.index = base_index
265 .checked_add(u32::try_from(offset).map_err(|_| Error::LimitExceeded {
266 limit: "sample_count",
267 value: u64::try_from(offset).unwrap_or(u64::MAX),
268 max: u64::from(u32::MAX),
269 })?)
270 .ok_or(Error::LimitExceeded {
271 limit: "sample_count",
272 value: u64::from(u32::MAX).saturating_add(1),
273 max: u64::from(u32::MAX),
274 })?;
275 }
276 list.extend_from_slice(&run.samples);
277 }
278 fragments.push(fragment);
279 }
280 Ok(Self {
281 reader,
282 file_len,
283 brands,
284 movie_timescale: parsed.movie_timescale,
285 movie_duration: parsed.movie_duration,
286 tracks: parsed.tracks,
287 samples: parsed.samples,
288 fragments,
289 has_mvex: parsed.mvex_present,
290 events,
291 metadata: parsed.metadata,
292 warnings,
293 limits,
294 })
295 }
296
297 pub fn brands(&self) -> &Brands {
299 &self.brands
300 }
301
302 pub fn movie_timescale(&self) -> u32 {
304 self.movie_timescale
305 }
306
307 pub fn movie_duration(&self) -> u64 {
309 self.movie_duration
310 }
311
312 pub fn duration(&self) -> Duration {
314 let scale = u64::from(self.movie_timescale);
315 let seconds = self.movie_duration.checked_div(scale).unwrap_or_default();
316 let remainder = self.movie_duration.checked_rem(scale).unwrap_or_default();
317 let nanos = remainder
318 .saturating_mul(1_000_000_000)
319 .checked_div(scale)
320 .unwrap_or_default();
321 Duration::new(seconds, u32::try_from(nanos).unwrap_or(999_999_999))
322 }
323
324 pub fn is_fragmented(&self) -> bool {
326 self.has_mvex
327 || !self.fragments.is_empty()
328 || self
329 .tracks
330 .iter()
331 .any(|track| track.chunk_offsets == ChunkOffsetKind::None)
332 }
333
334 pub fn tracks(&self) -> &[Track] {
336 &self.tracks
337 }
338
339 pub fn track(&self, id: TrackId) -> Option<&Track> {
341 self.tracks.iter().find(|track| track.id == id)
342 }
343
344 pub fn samples(&self, id: TrackId) -> Result<SampleIter<'_>, Error> {
346 let position = self.track_position(id)?;
347 let list = self.samples.get(position).ok_or(Error::NoSuchTrack(id))?;
348 Ok(SampleIter { inner: list.iter() })
349 }
350
351 pub fn sample_info(&self, id: TrackId, index: u32) -> Result<SampleInfo, Error> {
353 let position = self.track_position(id)?;
354 self.samples
355 .get(position)
356 .and_then(|list| list.get(usize::try_from(index).ok()?))
357 .copied()
358 .ok_or(Error::NoSuchSample { track: id, index })
359 }
360
361 pub fn read_sample(&mut self, id: TrackId, index: u32) -> Result<Sample, Error> {
363 let info = self.sample_info(id, index)?;
364 let mut data = Vec::new();
365 self.read_sample_data(id, info, &mut data)?;
366 Ok(Sample { info, data })
367 }
368
369 pub fn read_sample_into(
371 &mut self,
372 id: TrackId,
373 index: u32,
374 buf: &mut Vec<u8>,
375 ) -> Result<SampleInfo, Error> {
376 let info = self.sample_info(id, index)?;
377 self.read_sample_data(id, info, buf)?;
378 Ok(info)
379 }
380
381 pub fn fragments(&self) -> &[Fragment] {
383 &self.fragments
384 }
385
386 pub fn events(&self) -> &[Event] {
388 &self.events
389 }
390
391 pub fn metadata(&self) -> &Metadata {
393 &self.metadata
394 }
395
396 pub fn warnings(&self) -> &[Warning] {
398 &self.warnings
399 }
400
401 pub fn into_inner(self) -> R {
403 self.reader
404 }
405
406 fn track_position(&self, id: TrackId) -> Result<usize, Error> {
407 self.tracks
408 .iter()
409 .position(|track| track.id == id)
410 .ok_or(Error::NoSuchTrack(id))
411 }
412
413 fn read_sample_data(
414 &mut self,
415 id: TrackId,
416 info: SampleInfo,
417 buf: &mut Vec<u8>,
418 ) -> Result<(), Error> {
419 let end = info
420 .offset
421 .checked_add(u64::from(info.size))
422 .ok_or(Error::SampleOutOfFile {
423 track: id,
424 index: info.index,
425 offset: info.offset,
426 size: info.size,
427 file_len: self.file_len,
428 })?;
429 if end > self.file_len {
430 return Err(Error::SampleOutOfFile {
431 track: id,
432 index: info.index,
433 offset: info.offset,
434 size: info.size,
435 file_len: self.file_len,
436 });
437 }
438 if info.size > self.limits.max_sample_bytes {
439 return Err(Error::LimitExceeded {
440 limit: "max_sample_bytes",
441 value: u64::from(info.size),
442 max: u64::from(self.limits.max_sample_bytes),
443 });
444 }
445 let len = usize::try_from(info.size).map_err(|_| Error::LimitExceeded {
446 limit: "max_sample_bytes",
447 value: u64::from(info.size),
448 max: u64::from(self.limits.max_sample_bytes),
449 })?;
450 buf.clear();
451 buf.resize(len, 0);
452 self.reader.seek(SeekFrom::Start(info.offset))?;
453 self.reader.read_exact(buf)?;
454 Ok(())
455 }
456}
457
458pub struct SampleIter<'a> {
460 inner: std::slice::Iter<'a, SampleInfo>,
461}
462
463impl Iterator for SampleIter<'_> {
464 type Item = SampleInfo;
465
466 fn next(&mut self) -> Option<Self::Item> {
467 self.inner.next().copied()
468 }
469
470 fn size_hint(&self) -> (usize, Option<usize>) {
471 self.inner.size_hint()
472 }
473}
474
475impl DoubleEndedIterator for SampleIter<'_> {
476 fn next_back(&mut self) -> Option<Self::Item> {
477 self.inner.next_back().copied()
478 }
479}
480
481impl ExactSizeIterator for SampleIter<'_> {}
482impl std::iter::FusedIterator for SampleIter<'_> {}
483
484struct TopHeader {
485 kind: FourCc,
486 start: u64,
487 size: u64,
488 header_len: u64,
489 size_zero: bool,
490}
491
492fn read_top_header<R: Read + Seek>(
493 reader: &mut R,
494 at: u64,
495 file_len: u64,
496) -> Result<TopHeader, Error> {
497 reader.seek(SeekFrom::Start(at))?;
498 let mut fixed = [0u8; 8];
499 reader.read_exact(&mut fixed).map_err(|error| {
500 if error.kind() == std::io::ErrorKind::UnexpectedEof {
501 Error::Truncated { at, needed: 8 }
502 } else {
503 Error::Io(error)
504 }
505 })?;
506 let size32 = u32::from_be_bytes(
507 fixed
508 .get(..4)
509 .and_then(|bytes| bytes.try_into().ok())
510 .unwrap_or([0; 4]),
511 );
512 let kind = FourCc(
513 fixed
514 .get(4..8)
515 .and_then(|bytes| bytes.try_into().ok())
516 .unwrap_or(*b" "),
517 );
518 let (size, mut header_len, size_zero) = match size32 {
519 0 => (file_len.saturating_sub(at), 8u64, true),
520 1 => {
521 let mut large = [0u8; 8];
522 reader.read_exact(&mut large).map_err(|error| {
523 if error.kind() == std::io::ErrorKind::UnexpectedEof {
524 Error::Truncated { at, needed: 16 }
525 } else {
526 Error::Io(error)
527 }
528 })?;
529 (u64::from_be_bytes(large), 16u64, false)
530 }
531 value => (u64::from(value), 8u64, false),
532 };
533 if kind == FourCc(*b"uuid") {
534 header_len = header_len
535 .checked_add(16)
536 .ok_or(Error::BoxSizeTooSmall { kind, size, at })?;
537 }
538 if size < header_len {
539 return Err(Error::BoxSizeTooSmall { kind, size, at });
540 }
541 Ok(TopHeader {
542 kind,
543 start: at,
544 size,
545 header_len,
546 size_zero,
547 })
548}
549
550fn read_body<R: Read + Seek>(
551 reader: &mut R,
552 header: &TopHeader,
553 body_len: u64,
554 max: u64,
555) -> Result<Vec<u8>, Error> {
556 if body_len > max {
557 return Err(Error::LimitExceeded {
558 limit: "box_body_bytes",
559 value: body_len,
560 max,
561 });
562 }
563 let len = usize::try_from(body_len).map_err(|_| Error::LimitExceeded {
564 limit: "box_body_bytes",
565 value: body_len,
566 max,
567 })?;
568 let body_at = header
569 .start
570 .checked_add(header.header_len)
571 .ok_or(Error::Malformed {
572 kind: header.kind,
573 reason: "body offset overflow",
574 })?;
575 reader.seek(SeekFrom::Start(body_at))?;
576 let mut body = vec![0u8; len];
577 reader.read_exact(&mut body).map_err(|error| {
578 if error.kind() == std::io::ErrorKind::UnexpectedEof {
579 Error::Truncated {
580 at: body_at,
581 needed: body_len,
582 }
583 } else {
584 Error::Io(error)
585 }
586 })?;
587 Ok(body)
588}
589
590fn parse_ftyp(body: &[u8]) -> Result<Brands, Error> {
591 let mut cursor = Cursor::new(body, FTYP);
592 let major = cursor.fourcc()?;
593 let minor_version = cursor.u32()?;
594 if cursor.remaining() % 4 != 0 {
595 return Err(Error::Malformed {
596 kind: FTYP,
597 reason: "compatible brands are not four-byte aligned",
598 });
599 }
600 let mut compatible = Vec::with_capacity(cursor.remaining() / 4);
601 while cursor.remaining() != 0 {
602 compatible.push(cursor.fourcc()?);
603 }
604 Ok(Brands {
605 major,
606 minor_version,
607 compatible,
608 })
609}
610
611struct ParsedMovie {
612 movie_timescale: u32,
613 movie_duration: u64,
614 tracks: Vec<Track>,
615 samples: Vec<Vec<SampleInfo>>,
616 trex: BTreeMap<TrackId, Trex>,
617 mvex_present: bool,
618 metadata: Metadata,
619}
620
621fn warn_unknown_children(
622 boxes: &[(BoxHeader, &[u8])],
623 parent: FourCc,
624 known: &[FourCc],
625 warnings: &mut Vec<Warning>,
626) {
627 for (header, _) in boxes {
628 if known.contains(&header.kind)
629 || header.kind == FourCc(*b"free")
630 || header.kind == FourCc(*b"skip")
631 {
632 continue;
633 }
634 warnings.push(Warning::UnknownBox {
635 kind: header.kind,
636 parent: Some(parent),
637 });
638 }
639}
640
641#[derive(Clone, Copy)]
642struct Trex {
643 description_index: u32,
644 duration: u32,
645 size: u32,
646 flags: u32,
647}
648
649fn parse_movie(
650 body: &[u8],
651 absolute_base: u64,
652 limits: Limits,
653 warnings: &mut Vec<Warning>,
654) -> Result<ParsedMovie, Error> {
655 let boxes = children(body, MOOV, absolute_base)?;
656 warn_unknown_children(&boxes, MOOV, &[MVHD, TRAK, MVEX, UDTA], warnings);
657 let mvhd = one(&boxes, MVHD, MOOV, true)?.ok_or(Error::MissingBox {
658 kind: MVHD,
659 parent: Some(MOOV),
660 })?;
661 let (movie_timescale, movie_duration) = parse_mvhd(mvhd)?;
662 if movie_timescale == 0 {
663 return Err(Error::InvalidTimescale { track: None });
664 }
665 let mut tracks = Vec::new();
666 let mut samples = Vec::new();
667 for (_, trak) in boxes.iter().filter(|(header, _)| header.kind == TRAK) {
668 let (track, track_samples) = parse_track(trak, absolute_base, limits, warnings)?;
669 if tracks
670 .iter()
671 .any(|existing: &Track| existing.id == track.id)
672 {
673 return Err(Error::Malformed {
674 kind: TKHD,
675 reason: "duplicate track id",
676 });
677 }
678 tracks.push(track);
679 samples.push(track_samples);
680 }
681 let mut trex = BTreeMap::new();
682 let mvex_box = one(&boxes, MVEX, MOOV, false)?;
683 let mvex_present = mvex_box.is_some();
684 if let Some(mvex) = mvex_box {
685 for (_, body) in children(mvex, MVEX, absolute_base)?
686 .iter()
687 .filter(|(header, _)| header.kind == TREX)
688 {
689 let (id, value) = parse_trex(body)?;
690 if trex.insert(id, value).is_some() {
691 return Err(Error::DuplicateBox {
692 kind: TREX,
693 parent: MVEX,
694 });
695 }
696 }
697 }
698 let metadata = parse_metadata(&boxes, absolute_base)?;
699 Ok(ParsedMovie {
700 movie_timescale,
701 movie_duration,
702 tracks,
703 samples,
704 trex,
705 mvex_present,
706 metadata,
707 })
708}
709
710fn parse_mvhd(body: &[u8]) -> Result<(u32, u64), Error> {
711 let mut cursor = Cursor::new(body, MVHD);
712 let (version, _) = cursor.full_box()?;
713 match version {
714 0 => {
715 cursor.skip(8)?;
716 Ok((cursor.u32()?, u64::from(cursor.u32()?)))
717 }
718 1 => {
719 cursor.skip(16)?;
720 Ok((cursor.u32()?, cursor.u64()?))
721 }
722 _ => Err(Error::Malformed {
723 kind: MVHD,
724 reason: "unsupported version",
725 }),
726 }
727}
728
729fn parse_track(
730 body: &[u8],
731 absolute_base: u64,
732 limits: Limits,
733 warnings: &mut Vec<Warning>,
734) -> Result<(Track, Vec<SampleInfo>), Error> {
735 let boxes = children(body, TRAK, absolute_base)?;
736 warn_unknown_children(&boxes, TRAK, &[TKHD, EDTS, MDIA, UDTA], warnings);
737 let tkhd_body = one(&boxes, TKHD, TRAK, true)?.ok_or(Error::MissingBox {
738 kind: TKHD,
739 parent: Some(TRAK),
740 })?;
741 let tkhd = parse_tkhd(tkhd_body)?;
742 let mdia = one(&boxes, MDIA, TRAK, true)?.ok_or(Error::MissingBox {
743 kind: MDIA,
744 parent: Some(TRAK),
745 })?;
746 let mdia_boxes = children(mdia, MDIA, absolute_base)?;
747 warn_unknown_children(&mdia_boxes, MDIA, &[MDHD, HDLR, MINF], warnings);
748 let mdhd_body = one(&mdia_boxes, MDHD, MDIA, true)?.ok_or(Error::MissingBox {
749 kind: MDHD,
750 parent: Some(MDIA),
751 })?;
752 let mdhd = parse_mdhd(mdhd_body, tkhd.id)?;
753 let hdlr_body = one(&mdia_boxes, HDLR, MDIA, true)?.ok_or(Error::MissingBox {
754 kind: HDLR,
755 parent: Some(MDIA),
756 })?;
757 let (handler, handler_name) = parse_hdlr(hdlr_body)?;
758 let minf = one(&mdia_boxes, MINF, MDIA, true)?.ok_or(Error::MissingBox {
759 kind: MINF,
760 parent: Some(MDIA),
761 })?;
762 let minf_boxes = children(minf, MINF, absolute_base)?;
763 warn_unknown_children(
764 &minf_boxes,
765 MINF,
766 &[
767 FourCc(*b"vmhd"),
768 FourCc(*b"smhd"),
769 FourCc(*b"nmhd"),
770 FourCc(*b"sthd"),
771 FourCc(*b"hmhd"),
772 FourCc(*b"gmhd"),
773 DINF,
774 STBL,
775 ],
776 warnings,
777 );
778 let stbl = one(&minf_boxes, STBL, MINF, true)?.ok_or(Error::MissingBox {
779 kind: STBL,
780 parent: Some(MINF),
781 })?;
782 let edit_list = if let Some(edts) = one(&boxes, EDTS, TRAK, false)? {
783 let edts_boxes = children(edts, EDTS, absolute_base)?;
784 warn_unknown_children(&edts_boxes, EDTS, &[ELST], warnings);
785 match one(&edts_boxes, ELST, EDTS, false)? {
786 Some(elst) => parse_elst(elst)?,
787 None => Vec::new(),
788 }
789 } else {
790 Vec::new()
791 };
792 let parsed = parse_sample_table(
793 stbl,
794 absolute_base,
795 tkhd.id,
796 mdhd.timescale,
797 limits,
798 warnings,
799 )?;
800 let kind = if handler == FourCc(*b"vide") {
801 TrackKind::Video
802 } else if handler == FourCc(*b"soun") {
803 TrackKind::Audio
804 } else if handler == FourCc(*b"subt") {
805 TrackKind::Subtitle
806 } else {
807 TrackKind::Other(handler)
808 };
809 let duration = parsed.samples.iter().try_fold(0u64, |sum, sample| {
810 sum.checked_add(u64::from(sample.duration))
811 .ok_or(Error::InvalidSampleTable {
812 track: tkhd.id,
813 reason: "sample duration sum overflow",
814 })
815 })?;
816 Ok((
817 Track {
818 id: tkhd.id,
819 kind,
820 handler_name,
821 timescale: mdhd.timescale,
822 duration,
823 language: mdhd.language,
824 enabled: tkhd.enabled,
825 width: tkhd.width,
826 height: tkhd.height,
827 codec: parsed.codec,
828 edit_list,
829 sample_count: u32::try_from(parsed.samples.len()).map_err(|_| {
830 Error::InvalidSampleTable {
831 track: tkhd.id,
832 reason: "sample count exceeds u32",
833 }
834 })?,
835 chunk_offsets: parsed.chunk_kind,
836 sync_sample_count: parsed.sync_count,
837 },
838 parsed.samples,
839 ))
840}
841
842struct TkhdFields {
843 id: TrackId,
844 enabled: bool,
845 width: f64,
846 height: f64,
847}
848
849fn parse_tkhd(body: &[u8]) -> Result<TkhdFields, Error> {
850 let mut cursor = Cursor::new(body, TKHD);
851 let (version, flags) = cursor.full_box()?;
852 let id = match version {
853 0 => {
854 cursor.skip(8)?;
855 let id = cursor.u32()?;
856 cursor.skip(4)?;
857 cursor.skip(4)?;
858 id
859 }
860 1 => {
861 cursor.skip(16)?;
862 let id = cursor.u32()?;
863 cursor.skip(4)?;
864 cursor.skip(8)?;
865 id
866 }
867 _ => {
868 return Err(Error::Malformed {
869 kind: TKHD,
870 reason: "unsupported version",
871 })
872 }
873 };
874 cursor.skip(8 + 2 + 2 + 2 + 2 + 36)?;
875 let width = f64::from(cursor.u32()?) / 65_536.0;
876 let height = f64::from(cursor.u32()?) / 65_536.0;
877 Ok(TkhdFields {
878 id: TrackId(id),
879 enabled: flags & 1 != 0,
880 width,
881 height,
882 })
883}
884
885struct MdhdFields {
886 timescale: u32,
887 language: String,
888}
889
890fn parse_mdhd(body: &[u8], track: TrackId) -> Result<MdhdFields, Error> {
891 let mut cursor = Cursor::new(body, MDHD);
892 let (version, _) = cursor.full_box()?;
893 let timescale = match version {
894 0 => {
895 cursor.skip(8)?;
896 let value = cursor.u32()?;
897 cursor.skip(4)?;
898 value
899 }
900 1 => {
901 cursor.skip(16)?;
902 let value = cursor.u32()?;
903 cursor.skip(8)?;
904 value
905 }
906 _ => {
907 return Err(Error::Malformed {
908 kind: MDHD,
909 reason: "unsupported version",
910 })
911 }
912 };
913 if timescale == 0 {
914 return Err(Error::InvalidTimescale { track: Some(track) });
915 }
916 let packed = cursor.u16()?;
917 let mut language = String::with_capacity(3);
918 for shift in [10u16, 5, 0] {
919 let code = u8::try_from((packed >> shift) & 0x1f).unwrap_or_default();
920 language.push(char::from(code.saturating_add(0x60)));
921 }
922 Ok(MdhdFields {
923 timescale,
924 language,
925 })
926}
927
928fn parse_hdlr(body: &[u8]) -> Result<(FourCc, String), Error> {
929 let mut cursor = Cursor::new(body, HDLR);
930 cursor.full_box()?;
931 cursor.skip(4)?;
932 let handler = cursor.fourcc()?;
933 cursor.skip(12)?;
934 let raw = cursor.rest();
935 let name_bytes = raw.strip_suffix(&[0]).unwrap_or(raw);
936 let name = String::from_utf8(name_bytes.to_vec()).map_err(|_| Error::Malformed {
937 kind: HDLR,
938 reason: "handler name is not UTF-8",
939 })?;
940 Ok((handler, name))
941}
942
943fn parse_elst(body: &[u8]) -> Result<Vec<Edit>, Error> {
944 let mut cursor = Cursor::new(body, ELST);
945 let (version, _) = cursor.full_box()?;
946 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
947 kind: ELST,
948 reason: "entry count is too large",
949 })?;
950 let width = if version == 1 {
951 20usize
952 } else if version == 0 {
953 12usize
954 } else {
955 return Err(Error::Malformed {
956 kind: ELST,
957 reason: "unsupported version",
958 });
959 };
960 if count > cursor.remaining().checked_div(width).unwrap_or_default() {
961 return Err(Error::Malformed {
962 kind: ELST,
963 reason: "entry count exceeds box body",
964 });
965 }
966 let mut edits = Vec::with_capacity(count);
967 for _ in 0..count {
968 let (segment_duration, media_time) = if version == 1 {
969 (cursor.u64()?, cursor.i64()?)
970 } else {
971 (u64::from(cursor.u32()?), i64::from(cursor.i32()?))
972 };
973 let rate_integer = cursor.i16()?;
974 let rate_fraction = cursor.u16()?;
975 let media_rate = f32::from(rate_integer) + f32::from(rate_fraction) / 65_536.0;
976 let media_time = if media_time == -1 {
977 None
978 } else {
979 Some(u64::try_from(media_time).map_err(|_| Error::Malformed {
980 kind: ELST,
981 reason: "negative media time",
982 })?)
983 };
984 edits.push(Edit {
985 segment_duration,
986 media_time,
987 media_rate,
988 });
989 }
990 Ok(edits)
991}
992
993struct ParsedTable {
994 codec: Codec,
995 samples: Vec<SampleInfo>,
996 chunk_kind: ChunkOffsetKind,
997 sync_count: Option<u32>,
998}
999
1000#[derive(Clone, Copy)]
1001struct StscEntry {
1002 first_chunk: u32,
1003 samples_per_chunk: u32,
1004 description_index: u32,
1005}
1006
1007fn parse_sample_table(
1008 body: &[u8],
1009 absolute_base: u64,
1010 track: TrackId,
1011 timescale: u32,
1012 limits: Limits,
1013 warnings: &mut Vec<Warning>,
1014) -> Result<ParsedTable, Error> {
1015 let boxes = children(body, STBL, absolute_base)?;
1016 warn_unknown_children(
1017 &boxes,
1018 STBL,
1019 &[STSD, STTS, CTTS, STSS, STSC, STSZ, STZ2, STCO, CO64],
1020 warnings,
1021 );
1022 if boxes.iter().any(|(header, _)| header.kind == STZ2) {
1023 return Err(Error::Unsupported("compact sample sizes"));
1024 }
1025 let stsd = one(&boxes, STSD, STBL, true)?.ok_or(Error::MissingBox {
1026 kind: STSD,
1027 parent: Some(STBL),
1028 })?;
1029 let codecs = parse_stsd(stsd, track, timescale)?;
1030 if codecs.is_empty() {
1031 return Err(Error::InvalidSampleTable {
1032 track,
1033 reason: "sample description table is empty",
1034 });
1035 }
1036 let codec = codecs.first().cloned().ok_or(Error::InvalidSampleTable {
1037 track,
1038 reason: "sample description table is empty",
1039 })?;
1040 let stts = one(&boxes, STTS, STBL, true)?.ok_or(Error::MissingBox {
1041 kind: STTS,
1042 parent: Some(STBL),
1043 })?;
1044 let timings = parse_stts(stts)?;
1045 let sizes = parse_stsz(
1046 one(&boxes, STSZ, STBL, true)?.ok_or(Error::MissingBox {
1047 kind: STSZ,
1048 parent: Some(STBL),
1049 })?,
1050 limits,
1051 )?;
1052 let ctts = match one(&boxes, CTTS, STBL, false)? {
1053 Some(value) => Some(parse_ctts(value)?),
1054 None => None,
1055 };
1056 let stsc = parse_stsc(one(&boxes, STSC, STBL, true)?.ok_or(Error::MissingBox {
1057 kind: STSC,
1058 parent: Some(STBL),
1059 })?)?;
1060 let stco = one(&boxes, STCO, STBL, false)?;
1061 let co64 = one(&boxes, CO64, STBL, false)?;
1062 if stco.is_some() && co64.is_some() {
1063 return Err(Error::InvalidSampleTable {
1064 track,
1065 reason: "both stco and co64 are present",
1066 });
1067 }
1068 let (offsets, mut chunk_kind) = match (stco, co64) {
1069 (Some(value), None) => (parse_offsets(value, STCO)?, ChunkOffsetKind::Stco),
1070 (None, Some(value)) => (parse_offsets(value, CO64)?, ChunkOffsetKind::Co64),
1071 (None, None) if sizes.is_empty() => (Vec::new(), ChunkOffsetKind::None),
1072 _ => {
1073 return Err(Error::MissingBox {
1074 kind: STCO,
1075 parent: Some(STBL),
1076 })
1077 }
1078 };
1079 if sizes.is_empty() && offsets.is_empty() {
1080 chunk_kind = ChunkOffsetKind::None;
1081 }
1082 let sync = match one(&boxes, STSS, STBL, false)? {
1083 Some(value) => Some(parse_stss(value, sizes.len(), track)?),
1084 None => None,
1085 };
1086 if u64::try_from(sizes.len()).unwrap_or(u64::MAX) > u64::from(limits.max_sample_count) {
1087 return Err(Error::LimitExceeded {
1088 limit: "max_sample_count",
1089 value: u64::try_from(sizes.len()).unwrap_or(u64::MAX),
1090 max: u64::from(limits.max_sample_count),
1091 });
1092 }
1093 let samples = expand_samples(
1094 track,
1095 &sizes,
1096 &timings,
1097 ctts.as_deref(),
1098 &stsc,
1099 &offsets,
1100 sync.as_deref(),
1101 codecs.len(),
1102 )?;
1103 Ok(ParsedTable {
1104 codec,
1105 samples,
1106 chunk_kind,
1107 sync_count: sync
1108 .as_ref()
1109 .map(|items| u32::try_from(items.len()).unwrap_or(u32::MAX)),
1110 })
1111}
1112
1113fn parse_stsd(body: &[u8], track: TrackId, timescale: u32) -> Result<Vec<Codec>, Error> {
1114 let mut cursor = Cursor::new(body, STSD);
1115 let (version, _) = cursor.full_box()?;
1116 if version != 0 {
1117 return Err(Error::Malformed {
1118 kind: STSD,
1119 reason: "unsupported version",
1120 });
1121 }
1122 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
1123 kind: STSD,
1124 reason: "entry count is too large",
1125 })?;
1126 if count > cursor.remaining() / 8 {
1127 return Err(Error::Malformed {
1128 kind: STSD,
1129 reason: "entry count exceeds box body",
1130 });
1131 }
1132 let mut codecs = Vec::with_capacity(count);
1133 for _ in 0..count {
1134 let size = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
1135 kind: STSD,
1136 reason: "sample entry is too large",
1137 })?;
1138 let entry = cursor.fourcc()?;
1139 if size < 8 {
1140 return Err(Error::Malformed {
1141 kind: entry,
1142 reason: "sample entry size is smaller than its header",
1143 });
1144 }
1145 let body_len = size.checked_sub(8).ok_or(Error::Malformed {
1146 kind: entry,
1147 reason: "sample entry size underflow",
1148 })?;
1149 let raw = cursor.take(body_len)?.to_vec();
1150 codecs.push(parse_codec(entry, &raw, timescale)?);
1151 }
1152 if cursor.remaining() != 0 {
1153 return Err(Error::InvalidSampleTable {
1154 track,
1155 reason: "trailing bytes in sample description table",
1156 });
1157 }
1158 Ok(codecs)
1159}
1160
1161fn sidecar_boxes(boxes: &[(BoxHeader, &[u8])]) -> Vec<SidecarBox> {
1162 boxes
1163 .iter()
1164 .filter(|(header, _)| {
1165 [
1166 FourCc(*b"pasp"),
1167 FourCc(*b"btrt"),
1168 FourCc(*b"fiel"),
1169 FourCc(*b"colr"),
1170 ]
1171 .contains(&header.kind)
1172 })
1173 .map(|(header, body)| SidecarBox {
1174 kind: header.kind,
1175 payload: body.to_vec(),
1176 })
1177 .collect()
1178}
1179
1180fn parse_codec(entry: FourCc, raw: &[u8], timescale: u32) -> Result<Codec, Error> {
1181 if entry == FourCc(*b"avc1") || entry == FourCc(*b"avc3") {
1182 let children_bytes = raw.get(78..).ok_or(Error::Malformed {
1183 kind: entry,
1184 reason: "visual sample entry is truncated",
1185 })?;
1186 let boxes = children(children_bytes, entry, 0)?;
1187 let payload = one(&boxes, FourCc(*b"avcC"), entry, true)?.ok_or(Error::MissingBox {
1188 kind: FourCc(*b"avcC"),
1189 parent: Some(entry),
1190 })?;
1191 let mut config = parse_avcc(payload)?;
1192 config.sidecars = sidecar_boxes(&boxes);
1193 Ok(Codec::H264 { entry, config })
1194 } else if entry == FourCc(*b"hvc1") || entry == FourCc(*b"hev1") {
1195 let children_bytes = raw.get(78..).ok_or(Error::Malformed {
1196 kind: entry,
1197 reason: "visual sample entry is truncated",
1198 })?;
1199 let boxes = children(children_bytes, entry, 0)?;
1200 let payload = one(&boxes, FourCc(*b"hvcC"), entry, true)?.ok_or(Error::MissingBox {
1201 kind: FourCc(*b"hvcC"),
1202 parent: Some(entry),
1203 })?;
1204 let mut config = parse_hvcc(payload)?;
1205 config.sidecars = sidecar_boxes(&boxes);
1206 Ok(Codec::H265 { entry, config })
1207 } else if entry == FourCc(*b"mp4a") {
1208 let version = raw
1209 .get(8..10)
1210 .and_then(|value| value.try_into().ok())
1211 .map(u16::from_be_bytes)
1212 .ok_or(Error::Malformed {
1213 kind: entry,
1214 reason: "audio sample entry is truncated",
1215 })?;
1216 let fixed = match version {
1217 0 => 28usize,
1218 1 => 44usize,
1219 _ => return Err(Error::Unsupported("QuickTime audio sample entry version")),
1220 };
1221 let channels = raw
1222 .get(16..18)
1223 .and_then(|value| value.try_into().ok())
1224 .map(u16::from_be_bytes)
1225 .ok_or(Error::Malformed {
1226 kind: entry,
1227 reason: "audio channel count is missing",
1228 })?;
1229 let sample_rate_fixed = raw
1230 .get(24..28)
1231 .and_then(|value| value.try_into().ok())
1232 .map(u32::from_be_bytes)
1233 .ok_or(Error::Malformed {
1234 kind: entry,
1235 reason: "audio sample rate is missing",
1236 })?;
1237 let child_bytes = raw.get(fixed..).ok_or(Error::Malformed {
1238 kind: entry,
1239 reason: "audio sample entry is truncated",
1240 })?;
1241 let boxes = children(child_bytes, entry, 0)?;
1242 let esds = one(&boxes, FourCc(*b"esds"), entry, true)?.ok_or(Error::MissingBox {
1243 kind: FourCc(*b"esds"),
1244 parent: Some(entry),
1245 })?;
1246 let (object_type, audio_specific_config) = parse_esds(esds)?;
1247 let sample_rate = if sample_rate_fixed >> 16 == 0 {
1250 timescale
1251 } else {
1252 sample_rate_fixed >> 16
1253 };
1254 Ok(Codec::Aac {
1255 config: AacConfig {
1256 raw_esds: esds.to_vec(),
1257 object_type,
1258 audio_specific_config,
1259 channels,
1260 sample_rate,
1261 sidecars: sidecar_boxes(&boxes),
1262 },
1263 })
1264 } else if entry == FourCc(*b"Opus") {
1265 let child_bytes = raw.get(28..).ok_or(Error::Malformed {
1266 kind: entry,
1267 reason: "audio sample entry is truncated",
1268 })?;
1269 let boxes = children(child_bytes, entry, 0)?;
1270 let dops = one(&boxes, FourCc(*b"dOps"), entry, true)?.ok_or(Error::MissingBox {
1271 kind: FourCc(*b"dOps"),
1272 parent: Some(entry),
1273 })?;
1274 let mut cursor = Cursor::new(dops, FourCc(*b"dOps"));
1275 let version = cursor.u8()?;
1276 if version != 0 {
1277 return Err(Error::Malformed {
1278 kind: FourCc(*b"dOps"),
1279 reason: "unsupported version",
1280 });
1281 }
1282 let channels = cursor.u8()?;
1283 let pre_skip = cursor.u16()?;
1284 let input_sample_rate = cursor.u32()?;
1285 let output_gain = cursor.i16()?;
1286 let mapping_family = cursor.u8()?;
1287 Ok(Codec::Opus {
1288 config: OpusConfig {
1289 raw: dops.to_vec(),
1290 channels,
1291 pre_skip,
1292 input_sample_rate,
1293 output_gain,
1294 mapping_family,
1295 sidecars: sidecar_boxes(&boxes),
1296 },
1297 })
1298 } else {
1299 Ok(Codec::Other {
1300 entry,
1301 raw: raw.to_vec(),
1302 })
1303 }
1304}
1305
1306fn parse_avcc(data: &[u8]) -> Result<AvcConfig, Error> {
1307 let mut cursor = Cursor::new(data, FourCc(*b"avcC"));
1308 let version = cursor.u8()?;
1309 if version != 1 {
1310 return Err(Error::Malformed {
1311 kind: FourCc(*b"avcC"),
1312 reason: "unsupported version",
1313 });
1314 }
1315 let profile = cursor.u8()?;
1316 let profile_compat = cursor.u8()?;
1317 let level = cursor.u8()?;
1318 let nal_length_size = (cursor.u8()? & 3).saturating_add(1);
1319 let sps_count = usize::from(cursor.u8()? & 0x1f);
1320 let mut sps = Vec::with_capacity(sps_count);
1321 for _ in 0..sps_count {
1322 let len = usize::from(cursor.u16()?);
1323 sps.push(cursor.take(len)?.to_vec());
1324 }
1325 let pps_count = usize::from(cursor.u8()?);
1326 let mut pps = Vec::with_capacity(pps_count);
1327 for _ in 0..pps_count {
1328 let len = usize::from(cursor.u16()?);
1329 pps.push(cursor.take(len)?.to_vec());
1330 }
1331 Ok(AvcConfig {
1332 raw: data.to_vec(),
1333 profile,
1334 profile_compat,
1335 level,
1336 nal_length_size,
1337 sps,
1338 pps,
1339 sidecars: Vec::new(),
1340 })
1341}
1342
1343fn parse_hvcc(data: &[u8]) -> Result<HevcConfig, Error> {
1344 let mut cursor = Cursor::new(data, FourCc(*b"hvcC"));
1345 if cursor.u8()? != 1 {
1346 return Err(Error::Malformed {
1347 kind: FourCc(*b"hvcC"),
1348 reason: "unsupported version",
1349 });
1350 }
1351 cursor.skip(20)?;
1352 let nal_length_size = (cursor.u8()? & 3).saturating_add(1);
1353 let array_count = usize::from(cursor.u8()?);
1354 let mut arrays = Vec::with_capacity(array_count);
1355 for _ in 0..array_count {
1356 let nal_type = cursor.u8()? & 0x3f;
1357 let count = usize::from(cursor.u16()?);
1358 if count > cursor.remaining() / 2 {
1359 return Err(Error::Malformed {
1360 kind: FourCc(*b"hvcC"),
1361 reason: "NAL count exceeds configuration payload",
1362 });
1363 }
1364 let mut nals = Vec::with_capacity(count);
1365 for _ in 0..count {
1366 let len = usize::from(cursor.u16()?);
1367 nals.push(cursor.take(len)?.to_vec());
1368 }
1369 arrays.push(NalArray { nal_type, nals });
1370 }
1371 Ok(HevcConfig {
1372 raw: data.to_vec(),
1373 nal_length_size,
1374 arrays,
1375 sidecars: Vec::new(),
1376 })
1377}
1378
1379fn descriptor_len(data: &[u8], start: usize) -> Option<(usize, usize)> {
1380 let mut value = 0usize;
1381 let mut used = 0usize;
1382 for byte in data.get(start..)?.iter().take(4) {
1383 value = value
1384 .checked_shl(7)?
1385 .checked_add(usize::from(byte & 0x7f))?;
1386 used = used.checked_add(1)?;
1387 if byte & 0x80 == 0 {
1388 return Some((value, used));
1389 }
1390 }
1391 None
1392}
1393
1394fn read_descriptor(data: &[u8], at: usize) -> Result<(u8, &[u8], usize), Error> {
1395 let malformed = |reason: &'static str| Error::Malformed {
1396 kind: FourCc(*b"esds"),
1397 reason,
1398 };
1399 let tag = *data.get(at).ok_or(malformed("descriptor tag is missing"))?;
1400 let len_at = at
1401 .checked_add(1)
1402 .ok_or(malformed("descriptor offset overflow"))?;
1403 let (len, width) =
1404 descriptor_len(data, len_at).ok_or(malformed("descriptor length is invalid"))?;
1405 let body_at = len_at
1406 .checked_add(width)
1407 .ok_or(malformed("descriptor offset overflow"))?;
1408 let end = body_at
1409 .checked_add(len)
1410 .ok_or(malformed("descriptor size overflow"))?;
1411 let body = data
1412 .get(body_at..end)
1413 .ok_or(malformed("descriptor exceeds its container"))?;
1414 Ok((tag, body, end))
1415}
1416
1417fn parse_esds(data: &[u8]) -> Result<(u8, Vec<u8>), Error> {
1418 let malformed = |reason: &'static str| Error::Malformed {
1419 kind: FourCc(*b"esds"),
1420 reason,
1421 };
1422 let payload = data.get(4..).ok_or(malformed("missing full-box fields"))?;
1423 let (es_tag, es_body, _) = read_descriptor(payload, 0)?;
1424 if es_tag != 0x03 {
1425 return Err(malformed("elementary stream descriptor is missing"));
1426 }
1427 let mut header = Cursor::new(es_body, FourCc(*b"esds"));
1428 header.skip(2)?;
1429 let stream_flags = header.u8()?;
1430 if stream_flags & 0x80 != 0 {
1431 header.skip(2)?;
1432 }
1433 if stream_flags & 0x40 != 0 {
1434 let url_length = usize::from(header.u8()?);
1435 header.skip(url_length)?;
1436 }
1437 if stream_flags & 0x20 != 0 {
1438 header.skip(2)?;
1439 }
1440 let mut at = header.position();
1441 let mut decoder_config = None;
1442 while at < es_body.len() {
1443 let (tag, body, next) = read_descriptor(es_body, at)?;
1444 if tag == 0x04 {
1445 decoder_config = Some(body);
1446 break;
1447 }
1448 at = next;
1449 }
1450 let decoder_config =
1451 decoder_config.ok_or(malformed("decoder configuration descriptor is missing"))?;
1452 let object_type = *decoder_config
1453 .first()
1454 .ok_or(malformed("decoder configuration descriptor is empty"))?;
1455 let mut at = 13usize;
1458 let mut specific_info = None;
1459 while at < decoder_config.len() {
1460 let (tag, body, next) = read_descriptor(decoder_config, at)?;
1461 if tag == 0x05 {
1462 specific_info = Some(body.to_vec());
1463 break;
1464 }
1465 at = next;
1466 }
1467 Ok((
1468 object_type,
1469 specific_info.ok_or(malformed("audio specific configuration is missing"))?,
1470 ))
1471}
1472
1473fn parse_counted_pairs(body: &[u8], kind: FourCc, signed: bool) -> Result<Vec<(u32, i64)>, Error> {
1474 let mut cursor = Cursor::new(body, kind);
1475 let (version, _) = cursor.full_box()?;
1476 if (!signed && version != 0) || (signed && version > 1) {
1477 return Err(Error::Malformed {
1478 kind,
1479 reason: "unsupported version",
1480 });
1481 }
1482 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
1483 kind,
1484 reason: "entry count is too large",
1485 })?;
1486 if count > cursor.remaining() / 8 {
1487 return Err(Error::Malformed {
1488 kind,
1489 reason: "entry count exceeds box body",
1490 });
1491 }
1492 let mut entries = Vec::with_capacity(count);
1493 for _ in 0..count {
1494 let sample_count = cursor.u32()?;
1495 let value = if signed && version == 1 {
1496 i64::from(cursor.i32()?)
1497 } else {
1498 i64::from(cursor.u32()?)
1499 };
1500 entries.push((sample_count, value));
1501 }
1502 Ok(entries)
1503}
1504
1505fn parse_stts(body: &[u8]) -> Result<Vec<(u32, u32)>, Error> {
1506 parse_counted_pairs(body, STTS, false)?
1507 .into_iter()
1508 .map(|(count, delta)| {
1509 Ok((
1510 count,
1511 u32::try_from(delta).map_err(|_| Error::Malformed {
1512 kind: STTS,
1513 reason: "sample delta exceeds u32",
1514 })?,
1515 ))
1516 })
1517 .collect()
1518}
1519
1520fn parse_ctts(body: &[u8]) -> Result<Vec<(u32, i64)>, Error> {
1521 parse_counted_pairs(body, CTTS, true)
1522}
1523
1524fn parse_stsz(body: &[u8], limits: Limits) -> Result<Vec<u32>, Error> {
1525 let mut cursor = Cursor::new(body, STSZ);
1526 let (version, _) = cursor.full_box()?;
1527 if version != 0 {
1528 return Err(Error::Malformed {
1529 kind: STSZ,
1530 reason: "unsupported version",
1531 });
1532 }
1533 let fixed = cursor.u32()?;
1534 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
1535 kind: STSZ,
1536 reason: "sample count is too large",
1537 })?;
1538 if fixed == 0 {
1539 if count > cursor.remaining() / 4 {
1540 return Err(Error::Malformed {
1541 kind: STSZ,
1542 reason: "sample count exceeds box body",
1543 });
1544 }
1545 check_sample_count(count, limits)?;
1546 let mut sizes = Vec::with_capacity(count);
1547 for _ in 0..count {
1548 sizes.push(cursor.u32()?);
1549 }
1550 Ok(sizes)
1551 } else {
1552 check_sample_count(count, limits)?;
1553 Ok(vec![fixed; count])
1554 }
1555}
1556
1557fn check_sample_count(count: usize, limits: Limits) -> Result<(), Error> {
1558 if u64::try_from(count).unwrap_or(u64::MAX) > u64::from(limits.max_sample_count) {
1559 return Err(Error::LimitExceeded {
1560 limit: "max_sample_count",
1561 value: u64::try_from(count).unwrap_or(u64::MAX),
1562 max: u64::from(limits.max_sample_count),
1563 });
1564 }
1565 Ok(())
1566}
1567
1568fn parse_stsc(body: &[u8]) -> Result<Vec<StscEntry>, Error> {
1569 let mut cursor = Cursor::new(body, STSC);
1570 let (version, _) = cursor.full_box()?;
1571 if version != 0 {
1572 return Err(Error::Malformed {
1573 kind: STSC,
1574 reason: "unsupported version",
1575 });
1576 }
1577 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
1578 kind: STSC,
1579 reason: "entry count is too large",
1580 })?;
1581 if count > cursor.remaining() / 12 {
1582 return Err(Error::Malformed {
1583 kind: STSC,
1584 reason: "entry count exceeds box body",
1585 });
1586 }
1587 let mut entries = Vec::with_capacity(count);
1588 for _ in 0..count {
1589 entries.push(StscEntry {
1590 first_chunk: cursor.u32()?,
1591 samples_per_chunk: cursor.u32()?,
1592 description_index: cursor.u32()?,
1593 });
1594 }
1595 Ok(entries)
1596}
1597
1598fn parse_offsets(body: &[u8], kind: FourCc) -> Result<Vec<u64>, Error> {
1599 let mut cursor = Cursor::new(body, kind);
1600 let (version, _) = cursor.full_box()?;
1601 if version != 0 {
1602 return Err(Error::Malformed {
1603 kind,
1604 reason: "unsupported version",
1605 });
1606 }
1607 let width = if kind == STCO { 4usize } else { 8usize };
1608 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
1609 kind,
1610 reason: "entry count is too large",
1611 })?;
1612 if count > cursor.remaining().checked_div(width).unwrap_or_default() {
1613 return Err(Error::Malformed {
1614 kind,
1615 reason: "entry count exceeds box body",
1616 });
1617 }
1618 let mut values = Vec::with_capacity(count);
1619 for _ in 0..count {
1620 values.push(if kind == STCO {
1621 u64::from(cursor.u32()?)
1622 } else {
1623 cursor.u64()?
1624 });
1625 }
1626 Ok(values)
1627}
1628
1629fn parse_stss(body: &[u8], sample_count: usize, track: TrackId) -> Result<Vec<u32>, Error> {
1630 let mut cursor = Cursor::new(body, STSS);
1631 let (version, _) = cursor.full_box()?;
1632 if version != 0 {
1633 return Err(Error::Malformed {
1634 kind: STSS,
1635 reason: "unsupported version",
1636 });
1637 }
1638 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
1639 kind: STSS,
1640 reason: "entry count is too large",
1641 })?;
1642 if count > cursor.remaining() / 4 {
1643 return Err(Error::Malformed {
1644 kind: STSS,
1645 reason: "entry count exceeds box body",
1646 });
1647 }
1648 let max = u32::try_from(sample_count).unwrap_or(u32::MAX);
1649 let mut previous = 0u32;
1650 let mut values = Vec::with_capacity(count);
1651 for _ in 0..count {
1652 let value = cursor.u32()?;
1653 if value == 0 || value > max || value <= previous {
1654 return Err(Error::InvalidSampleTable {
1655 track,
1656 reason: "sync samples are not ascending within range",
1657 });
1658 }
1659 previous = value;
1660 values.push(value);
1661 }
1662 Ok(values)
1663}
1664
1665#[allow(clippy::too_many_arguments)]
1666fn expand_samples(
1667 track: TrackId,
1668 sizes: &[u32],
1669 timings: &[(u32, u32)],
1670 compositions: Option<&[(u32, i64)]>,
1671 mappings: &[StscEntry],
1672 chunks: &[u64],
1673 sync: Option<&[u32]>,
1674 description_count: usize,
1675) -> Result<Vec<SampleInfo>, Error> {
1676 let sample_count = sizes.len();
1677 let timing_count = timings.iter().try_fold(0u64, |sum, (count, _)| {
1678 sum.checked_add(u64::from(*count))
1679 .ok_or(Error::InvalidSampleTable {
1680 track,
1681 reason: "time table count overflow",
1682 })
1683 })?;
1684 if timing_count != u64::try_from(sample_count).unwrap_or(u64::MAX) {
1685 return Err(Error::InvalidSampleTable {
1686 track,
1687 reason: "time table count differs from sample count",
1688 });
1689 }
1690 if let Some(values) = compositions {
1691 let count = values.iter().try_fold(0u64, |sum, (entry_count, _)| {
1692 sum.checked_add(u64::from(*entry_count))
1693 .ok_or(Error::InvalidSampleTable {
1694 track,
1695 reason: "composition table count overflow",
1696 })
1697 })?;
1698 if count != u64::try_from(sample_count).unwrap_or(u64::MAX) {
1699 return Err(Error::InvalidSampleTable {
1700 track,
1701 reason: "composition table count differs from sample count",
1702 });
1703 }
1704 }
1705 if sample_count == 0 && chunks.is_empty() {
1706 return Ok(Vec::new());
1707 }
1708 if mappings.is_empty() || mappings.first().map(|entry| entry.first_chunk) != Some(1) {
1709 return Err(Error::InvalidSampleTable {
1710 track,
1711 reason: "sample-to-chunk table must start at chunk one",
1712 });
1713 }
1714 for pair in mappings.windows(2) {
1715 let first = pair.first().copied().ok_or(Error::InvalidSampleTable {
1716 track,
1717 reason: "sample-to-chunk entry is missing",
1718 })?;
1719 let second = pair.get(1).copied().ok_or(Error::InvalidSampleTable {
1720 track,
1721 reason: "sample-to-chunk entry is missing",
1722 })?;
1723 if second.first_chunk <= first.first_chunk {
1724 return Err(Error::InvalidSampleTable {
1725 track,
1726 reason: "sample-to-chunk starts are not strictly increasing",
1727 });
1728 }
1729 }
1730 let mut durations = Vec::with_capacity(sample_count);
1731 let mut dts_values = Vec::with_capacity(sample_count);
1732 let mut dts = 0u64;
1733 for (count, delta) in timings {
1734 for _ in 0..*count {
1735 dts_values.push(dts);
1736 durations.push(*delta);
1737 dts = dts
1738 .checked_add(u64::from(*delta))
1739 .ok_or(Error::InvalidSampleTable {
1740 track,
1741 reason: "decode time overflow",
1742 })?;
1743 }
1744 }
1745 let mut cts_values = Vec::with_capacity(sample_count);
1746 if let Some(values) = compositions {
1747 for (count, offset) in values {
1748 for _ in 0..*count {
1749 cts_values.push(*offset);
1750 }
1751 }
1752 } else {
1753 cts_values.resize(sample_count, 0);
1754 }
1755 let mut samples = Vec::with_capacity(sample_count);
1756 let mut sample_index = 0usize;
1757 let mut mapping_index = 0usize;
1758 for (chunk_index, chunk_offset) in chunks.iter().copied().enumerate() {
1759 let chunk_number = u32::try_from(chunk_index)
1760 .unwrap_or(u32::MAX)
1761 .saturating_add(1);
1762 while let Some(next) = mappings.get(mapping_index.saturating_add(1)) {
1763 if next.first_chunk > chunk_number {
1764 break;
1765 }
1766 mapping_index = mapping_index.saturating_add(1);
1767 }
1768 let mapping = mappings
1769 .get(mapping_index)
1770 .copied()
1771 .ok_or(Error::InvalidSampleTable {
1772 track,
1773 reason: "sample-to-chunk mapping is missing",
1774 })?;
1775 if mapping.samples_per_chunk == 0 {
1776 return Err(Error::InvalidSampleTable {
1777 track,
1778 reason: "samples per chunk is zero",
1779 });
1780 }
1781 if mapping.description_index == 0
1782 || usize::try_from(mapping.description_index).unwrap_or(usize::MAX) > description_count
1783 {
1784 return Err(Error::InvalidSampleTable {
1785 track,
1786 reason: "sample description index is out of range",
1787 });
1788 }
1789 let mut within = 0u64;
1790 for _ in 0..mapping.samples_per_chunk {
1791 let size = sizes
1792 .get(sample_index)
1793 .copied()
1794 .ok_or(Error::InvalidSampleTable {
1795 track,
1796 reason: "chunk mapping produces too many samples",
1797 })?;
1798 let offset = chunk_offset
1799 .checked_add(within)
1800 .ok_or(Error::InvalidSampleTable {
1801 track,
1802 reason: "sample offset overflow",
1803 })?;
1804 within = within
1805 .checked_add(u64::from(size))
1806 .ok_or(Error::InvalidSampleTable {
1807 track,
1808 reason: "sample offset overflow",
1809 })?;
1810 let dts = dts_values
1811 .get(sample_index)
1812 .copied()
1813 .ok_or(Error::InvalidSampleTable {
1814 track,
1815 reason: "decode time is missing",
1816 })?;
1817 let cts_offset =
1818 cts_values
1819 .get(sample_index)
1820 .copied()
1821 .ok_or(Error::InvalidSampleTable {
1822 track,
1823 reason: "composition offset is missing",
1824 })?;
1825 let pts = i64::try_from(dts)
1826 .ok()
1827 .and_then(|value| value.checked_add(cts_offset))
1828 .ok_or(Error::InvalidSampleTable {
1829 track,
1830 reason: "presentation time overflow",
1831 })?;
1832 let number = u32::try_from(sample_index)
1833 .unwrap_or(u32::MAX)
1834 .saturating_add(1);
1835 let is_sync = sync
1836 .map(|values| values.binary_search(&number).is_ok())
1837 .unwrap_or(true);
1838 samples.push(SampleInfo {
1839 index: u32::try_from(sample_index).map_err(|_| Error::InvalidSampleTable {
1840 track,
1841 reason: "sample index exceeds u32",
1842 })?,
1843 offset,
1844 size,
1845 dts,
1846 cts_offset,
1847 pts,
1848 duration: durations.get(sample_index).copied().ok_or(
1849 Error::InvalidSampleTable {
1850 track,
1851 reason: "sample duration is missing",
1852 },
1853 )?,
1854 is_sync,
1855 chunk: u32::try_from(chunk_index).map_err(|_| Error::InvalidSampleTable {
1856 track,
1857 reason: "chunk index exceeds u32",
1858 })?,
1859 description_index: mapping.description_index,
1860 });
1861 sample_index = sample_index
1862 .checked_add(1)
1863 .ok_or(Error::InvalidSampleTable {
1864 track,
1865 reason: "sample index overflow",
1866 })?;
1867 }
1868 }
1869 if sample_index != sample_count {
1870 return Err(Error::InvalidSampleTable {
1871 track,
1872 reason: "chunk mapping does not produce the sample count",
1873 });
1874 }
1875 Ok(samples)
1876}
1877
1878fn parse_trex(body: &[u8]) -> Result<(TrackId, Trex), Error> {
1879 let mut cursor = Cursor::new(body, TREX);
1880 let (version, _) = cursor.full_box()?;
1881 if version != 0 {
1882 return Err(Error::Malformed {
1883 kind: TREX,
1884 reason: "unsupported version",
1885 });
1886 }
1887 let track = TrackId(cursor.u32()?);
1888 Ok((
1889 track,
1890 Trex {
1891 description_index: cursor.u32()?,
1892 duration: cursor.u32()?,
1893 size: cursor.u32()?,
1894 flags: cursor.u32()?,
1895 },
1896 ))
1897}
1898
1899#[derive(Clone, Copy)]
1900struct Tfhd {
1901 track: TrackId,
1902 base_data_offset: Option<u64>,
1903 description_index: Option<u32>,
1904 duration: Option<u32>,
1905 size: Option<u32>,
1906 flags: Option<u32>,
1907 default_base_is_moof: bool,
1908}
1909
1910fn parse_tfhd(body: &[u8]) -> Result<Tfhd, Error> {
1911 let kind = FourCc(*b"tfhd");
1912 let mut cursor = Cursor::new(body, kind);
1913 let (version, flags) = cursor.full_box()?;
1914 if version != 0 {
1915 return Err(Error::Malformed {
1916 kind,
1917 reason: "unsupported version",
1918 });
1919 }
1920 let track = TrackId(cursor.u32()?);
1921 Ok(Tfhd {
1922 track,
1923 base_data_offset: if flags & 0x1 != 0 {
1924 Some(cursor.u64()?)
1925 } else {
1926 None
1927 },
1928 description_index: if flags & 0x2 != 0 {
1929 Some(cursor.u32()?)
1930 } else {
1931 None
1932 },
1933 duration: if flags & 0x8 != 0 {
1934 Some(cursor.u32()?)
1935 } else {
1936 None
1937 },
1938 size: if flags & 0x10 != 0 {
1939 Some(cursor.u32()?)
1940 } else {
1941 None
1942 },
1943 flags: if flags & 0x20 != 0 {
1944 Some(cursor.u32()?)
1945 } else {
1946 None
1947 },
1948 default_base_is_moof: flags & 0x020000 != 0,
1949 })
1950}
1951
1952#[allow(clippy::too_many_arguments)]
1953fn parse_fragment(
1954 body: &[u8],
1955 absolute_base: u64,
1956 moof_offset: u64,
1957 fragment_index: u32,
1958 defaults: &BTreeMap<TrackId, Trex>,
1959 limits: Limits,
1960 running_dts: &mut BTreeMap<TrackId, u64>,
1961 warnings: &mut Vec<Warning>,
1962) -> Result<Fragment, Error> {
1963 let boxes = children(body, MOOF, absolute_base)?;
1964 warn_unknown_children(
1965 &boxes,
1966 MOOF,
1967 &[FourCc(*b"mfhd"), FourCc(*b"traf")],
1968 warnings,
1969 );
1970 let mfhd_kind = FourCc(*b"mfhd");
1971 let mfhd = one(&boxes, mfhd_kind, MOOF, true)?.ok_or(Error::MissingBox {
1972 kind: mfhd_kind,
1973 parent: Some(MOOF),
1974 })?;
1975 let mut mfhd_cursor = Cursor::new(mfhd, mfhd_kind);
1976 let (version, _) = mfhd_cursor.full_box()?;
1977 if version != 0 {
1978 return Err(Error::Malformed {
1979 kind: mfhd_kind,
1980 reason: "unsupported version",
1981 });
1982 }
1983 let sequence_number = mfhd_cursor.u32()?;
1984 let traf_kind = FourCc(*b"traf");
1985 let mut runs = Vec::new();
1986 for (_, traf) in boxes.iter().filter(|(header, _)| header.kind == traf_kind) {
1987 let traf_boxes = children(traf, traf_kind, absolute_base)?;
1988 warn_unknown_children(
1989 &traf_boxes,
1990 traf_kind,
1991 &[FourCc(*b"tfhd"), FourCc(*b"tfdt"), FourCc(*b"trun")],
1992 warnings,
1993 );
1994 let tfhd_kind = FourCc(*b"tfhd");
1995 let tfhd = parse_tfhd(one(&traf_boxes, tfhd_kind, traf_kind, true)?.ok_or(
1996 Error::MissingBox {
1997 kind: tfhd_kind,
1998 parent: Some(traf_kind),
1999 },
2000 )?)?;
2001 let trex = defaults.get(&tfhd.track).copied().ok_or(Error::Malformed {
2002 kind: tfhd_kind,
2003 reason: "track has no fragment defaults",
2004 })?;
2005 let base = if let Some(value) = tfhd.base_data_offset {
2006 value
2007 } else if tfhd.default_base_is_moof {
2008 moof_offset
2009 } else {
2010 return Err(Error::Unsupported("implicit fragment base offsets"));
2011 };
2012 let tfdt_kind = FourCc(*b"tfdt");
2013 let mut dts = if let Some(tfdt) = one(&traf_boxes, tfdt_kind, traf_kind, false)? {
2014 let mut cursor = Cursor::new(tfdt, tfdt_kind);
2015 let (version, _) = cursor.full_box()?;
2016 match version {
2017 0 => u64::from(cursor.u32()?),
2018 1 => cursor.u64()?,
2019 _ => {
2020 return Err(Error::Malformed {
2021 kind: tfdt_kind,
2022 reason: "unsupported version",
2023 })
2024 }
2025 }
2026 } else {
2027 running_dts.get(&tfhd.track).copied().unwrap_or(0)
2028 };
2029 let trun_kind = FourCc(*b"trun");
2030 let mut prior_run_end: Option<u64> = None;
2031 for (_, trun) in traf_boxes
2032 .iter()
2033 .filter(|(header, _)| header.kind == trun_kind)
2034 {
2035 let mut cursor = Cursor::new(trun, trun_kind);
2036 let (version, flags) = cursor.full_box()?;
2037 if version > 1 {
2038 return Err(Error::Malformed {
2039 kind: trun_kind,
2040 reason: "unsupported version",
2041 });
2042 }
2043 let count = usize::try_from(cursor.u32()?).map_err(|_| Error::Malformed {
2044 kind: trun_kind,
2045 reason: "sample count is too large",
2046 })?;
2047 let fields = ((flags & 0x100 != 0) as usize)
2048 .saturating_add((flags & 0x200 != 0) as usize)
2049 .saturating_add((flags & 0x400 != 0) as usize)
2050 .saturating_add((flags & 0x800 != 0) as usize);
2051 let minimum = count
2052 .checked_mul(fields.saturating_mul(4))
2053 .ok_or(Error::Malformed {
2054 kind: trun_kind,
2055 reason: "sample field size overflow",
2056 })?;
2057 if minimum > cursor.remaining() {
2058 return Err(Error::Malformed {
2059 kind: trun_kind,
2060 reason: "sample count exceeds box body",
2061 });
2062 }
2063 check_sample_count(count, limits)?;
2064 let data_offset = if flags & 0x1 != 0 {
2065 Some(cursor.i32()?)
2066 } else {
2067 None
2068 };
2069 let first_flags = if flags & 0x4 != 0 {
2070 Some(cursor.u32()?)
2071 } else {
2072 None
2073 };
2074 let run_start = if let Some(offset) = data_offset {
2075 if offset >= 0 {
2076 base.checked_add(u64::try_from(offset).unwrap_or_default())
2077 } else {
2078 base.checked_sub(u64::from(offset.unsigned_abs()))
2079 }
2080 .ok_or(Error::Malformed {
2081 kind: trun_kind,
2082 reason: "data offset overflow",
2083 })?
2084 } else {
2085 prior_run_end.unwrap_or(base)
2086 };
2087 let mut offset = run_start;
2088 let mut samples = Vec::with_capacity(count);
2089 for index in 0..count {
2090 let duration = if flags & 0x100 != 0 {
2091 cursor.u32()?
2092 } else {
2093 tfhd.duration.unwrap_or(trex.duration)
2094 };
2095 let size = if flags & 0x200 != 0 {
2096 cursor.u32()?
2097 } else {
2098 tfhd.size.unwrap_or(trex.size)
2099 };
2100 let sample_flags = if flags & 0x400 != 0 {
2101 cursor.u32()?
2102 } else if index == 0 {
2103 first_flags.unwrap_or(tfhd.flags.unwrap_or(trex.flags))
2104 } else {
2105 tfhd.flags.unwrap_or(trex.flags)
2106 };
2107 let cts_offset = if flags & 0x800 != 0 {
2108 if version == 1 {
2109 i64::from(cursor.i32()?)
2110 } else {
2111 i64::from(cursor.u32()?)
2112 }
2113 } else {
2114 0
2115 };
2116 let pts = i64::try_from(dts)
2117 .ok()
2118 .and_then(|value| value.checked_add(cts_offset))
2119 .ok_or(Error::Malformed {
2120 kind: trun_kind,
2121 reason: "presentation time overflow",
2122 })?;
2123 samples.push(SampleInfo {
2124 index: u32::try_from(index).map_err(|_| Error::Malformed {
2125 kind: trun_kind,
2126 reason: "sample index exceeds u32",
2127 })?,
2128 offset,
2129 size,
2130 dts,
2131 cts_offset,
2132 pts,
2133 duration,
2134 is_sync: sample_flags & 0x1_0000 == 0,
2135 chunk: fragment_index,
2136 description_index: tfhd.description_index.unwrap_or(trex.description_index),
2137 });
2138 offset = offset
2139 .checked_add(u64::from(size))
2140 .ok_or(Error::Malformed {
2141 kind: trun_kind,
2142 reason: "sample offset overflow",
2143 })?;
2144 dts = dts
2145 .checked_add(u64::from(duration))
2146 .ok_or(Error::Malformed {
2147 kind: trun_kind,
2148 reason: "decode time overflow",
2149 })?;
2150 }
2151 prior_run_end = Some(offset);
2152 runs.push(TrackRun {
2153 track: tfhd.track,
2154 samples,
2155 });
2156 }
2157 running_dts.insert(tfhd.track, dts);
2158 }
2159 Ok(Fragment {
2160 sequence_number,
2161 moof_offset,
2162 runs,
2163 })
2164}
2165
2166fn take_c_string(data: &[u8], position: &mut usize, kind: FourCc) -> Result<String, Error> {
2167 let tail = data.get(*position..).ok_or(Error::Malformed {
2168 kind,
2169 reason: "string offset is outside box",
2170 })?;
2171 let end = tail
2172 .iter()
2173 .position(|byte| *byte == 0)
2174 .ok_or(Error::Malformed {
2175 kind,
2176 reason: "unterminated string",
2177 })?;
2178 let bytes = tail.get(..end).ok_or(Error::Malformed {
2179 kind,
2180 reason: "string is outside box",
2181 })?;
2182 *position = position
2183 .checked_add(end.saturating_add(1))
2184 .ok_or(Error::Malformed {
2185 kind,
2186 reason: "string offset overflow",
2187 })?;
2188 String::from_utf8(bytes.to_vec()).map_err(|_| Error::Malformed {
2189 kind,
2190 reason: "string is not UTF-8",
2191 })
2192}
2193
2194fn parse_event(body: &[u8]) -> Result<Event, Error> {
2195 let mut cursor = Cursor::new(body, EMSG);
2196 let (version, _) = cursor.full_box()?;
2197 match version {
2198 0 => {
2199 let mut position = cursor.position();
2200 let scheme_id_uri = take_c_string(body, &mut position, EMSG)?;
2201 let value = take_c_string(body, &mut position, EMSG)?;
2202 let mut rest = Cursor::new(
2203 body.get(position..).ok_or(Error::Malformed {
2204 kind: EMSG,
2205 reason: "event fields are truncated",
2206 })?,
2207 EMSG,
2208 );
2209 let timescale = rest.u32()?;
2210 let presentation_time = u64::from(rest.u32()?);
2211 let event_duration = rest.u32()?;
2212 let id = rest.u32()?;
2213 Ok(Event {
2214 scheme_id_uri,
2215 value,
2216 timescale,
2217 presentation_time,
2218 event_duration,
2219 id,
2220 message_data: rest.rest().to_vec(),
2221 })
2222 }
2223 1 => {
2224 let timescale = cursor.u32()?;
2225 let presentation_time = cursor.u64()?;
2226 let event_duration = cursor.u32()?;
2227 let id = cursor.u32()?;
2228 let mut position = cursor.position();
2229 let scheme_id_uri = take_c_string(body, &mut position, EMSG)?;
2230 let value = take_c_string(body, &mut position, EMSG)?;
2231 let message_data = body.get(position..).unwrap_or_default().to_vec();
2232 Ok(Event {
2233 scheme_id_uri,
2234 value,
2235 timescale,
2236 presentation_time,
2237 event_duration,
2238 id,
2239 message_data,
2240 })
2241 }
2242 _ => Err(Error::Malformed {
2243 kind: EMSG,
2244 reason: "unsupported version",
2245 }),
2246 }
2247}
2248
2249fn parse_metadata(boxes: &[(BoxHeader, &[u8])], absolute_base: u64) -> Result<Metadata, Error> {
2250 let udta_kind = FourCc(*b"udta");
2251 let meta_kind = FourCc(*b"meta");
2252 let ilst_kind = FourCc(*b"ilst");
2253 let Some(udta) = one(boxes, udta_kind, MOOV, false)? else {
2254 return Ok(Metadata::default());
2255 };
2256 let udta_boxes = children(udta, udta_kind, absolute_base)?;
2257 let Some(meta) = one(&udta_boxes, meta_kind, udta_kind, false)? else {
2258 return Ok(Metadata::default());
2259 };
2260 let nested = meta.get(4..).ok_or(Error::Malformed {
2261 kind: meta_kind,
2262 reason: "missing full-box fields",
2263 })?;
2264 let meta_boxes = children(nested, meta_kind, absolute_base)?;
2265 let Some(ilst) = one(&meta_boxes, ilst_kind, meta_kind, false)? else {
2266 return Ok(Metadata::default());
2267 };
2268 let mut metadata = Metadata::default();
2269 for (item_header, item_body) in children(ilst, ilst_kind, absolute_base)? {
2270 let item_boxes = children(item_body, item_header.kind, absolute_base)?;
2271 let data_kind = FourCc(*b"data");
2272 let Some(data) = one(&item_boxes, data_kind, item_header.kind, false)? else {
2273 continue;
2274 };
2275 let payload = data.get(8..).ok_or(Error::Malformed {
2276 kind: data_kind,
2277 reason: "metadata value is truncated",
2278 })?;
2279 let key = match item_header.kind.0 {
2280 [0xa9, b'n', b'a', b'm'] => MetaKey::Title,
2281 [0xa9, b'A', b'R', b'T'] => MetaKey::Artist,
2282 [0xa9, b'a', b'l', b'b'] => MetaKey::Album,
2283 [b'a', b'A', b'R', b'T'] => MetaKey::AlbumArtist,
2284 [0xa9, b'w', b'r', b't'] => MetaKey::Composer,
2285 [0xa9, b'g', b'e', b'n'] => MetaKey::Genre,
2286 [0xa9, b'c', b'm', b't'] => MetaKey::Comment,
2287 [0xa9, b'd', b'a', b'y'] => MetaKey::Date,
2288 _ => MetaKey::Other(item_header.kind),
2289 };
2290 let value = match String::from_utf8(payload.to_vec()) {
2291 Ok(text) => MetaValue::Text(text),
2292 Err(_) => MetaValue::Binary(payload.to_vec()),
2293 };
2294 metadata.entries.push((key, value));
2295 }
2296 Ok(metadata)
2297}