1use crate::media::{Codec, Error, Packet, Result};
2use crate::mp4::boxes::{Cur, FourCc, boxes, find, parse_box_header};
3use crate::mp4::entry::{CodecConfig, SampleEntry, parse_sample_entry};
4use crate::mp4::source::ByteSource;
5use crate::mp4::{too_large, truncated};
6use std::collections::HashMap;
7use std::fs::File;
8use std::num::NonZeroU64;
9use std::path::Path;
10
11const MAX_SAMPLES: usize = 1 << 26;
12const MAX_EMPTY_SAMPLES: u64 = 1 << 16;
13const MAX_HEADER_BOX: u64 = 1 << 30;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
16pub struct Sample {
17 pub offset: u64,
18 pub size: u32,
19 pub dts: i64,
20 pub pts: i64,
21 pub duration: u32,
22 pub keyframe: bool,
23 pub entry: u32,
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct Edit {
28 pub segment_duration: u64,
29 pub media_time: i64,
30 pub media_rate: i32,
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum TrackKind {
35 Video,
36 Audio,
37 Subtitle,
38 Other,
39}
40
41#[derive(Clone, Debug, PartialEq)]
42pub struct Track {
43 pub id: u32,
44 pub kind: TrackKind,
45 pub handler: FourCc,
46 pub name: String,
47 pub timescale: u32,
48 pub duration: u64,
49 pub language: String,
50 pub width: u32,
51 pub height: u32,
52 pub enabled: bool,
53 pub matrix: [i32; 9],
54 pub entries: Vec<SampleEntry>,
55 pub samples: Vec<Sample>,
56 pub edits: Vec<Edit>,
57 pub edit_offset: i64,
58 sync_index: Vec<u32>,
59 pts_order: Vec<u32>,
60}
61
62impl Track {
63 pub fn entry(&self) -> Option<&SampleEntry> {
64 self.entries.first()
65 }
66
67 pub fn config(&self) -> Option<&CodecConfig> {
68 self.entry().map(|e| &e.codec)
69 }
70
71 pub fn codec(&self) -> Option<Codec> {
72 match self.config()? {
73 CodecConfig::Avc(_) => Some(Codec::H264),
74 CodecConfig::Hevc(_) => Some(Codec::Hevc),
75 CodecConfig::Vp9(_) => Some(Codec::Vp9),
76 CodecConfig::Aac(_) => Some(Codec::Aac),
77 _ => None,
78 }
79 }
80
81 pub fn extradata(&self) -> Option<Vec<u8>> {
82 self.config()?.extradata()
83 }
84
85 pub fn rotation(&self) -> u16 {
86 let [a, b, _, c, d, _, _, _, _] = self.matrix;
87 match (a.signum(), b.signum(), c.signum(), d.signum()) {
88 (0, 1, -1, 0) => 90,
89 (-1, 0, 0, -1) => 180,
90 (0, -1, 1, 0) => 270,
91 _ => 0,
92 }
93 }
94
95 pub fn channels(&self) -> u32 {
96 match self.config() {
97 Some(CodecConfig::Aac(a)) if a.channels() > 0 => a.channels(),
98 _ => self.entry().map_or(0, |e| {
99 e.ac3_channels()
100 .or(e.audio.as_ref().map(|a| a.channels))
101 .unwrap_or(0)
102 }),
103 }
104 }
105
106 pub fn sample_rate(&self) -> u32 {
107 match self.config() {
108 Some(CodecConfig::Aac(a)) if a.sample_rate > 0 => a.sample_rate,
109 _ => self
110 .entry()
111 .and_then(|e| e.audio.as_ref())
112 .map_or(0, |a| a.sample_rate.round() as u32),
113 }
114 }
115
116 pub fn seconds(&self, ticks: i64) -> f64 {
117 if self.timescale == 0 {
118 0.0
119 } else {
120 ticks as f64 / self.timescale as f64
121 }
122 }
123
124 pub fn ticks(&self, seconds: f64) -> i64 {
125 (seconds * self.timescale as f64).round() as i64
126 }
127
128 pub fn duration_seconds(&self) -> f64 {
129 let edited: u64 = self
130 .edits
131 .iter()
132 .filter(|e| e.media_time >= 0)
133 .fold(0u64, |a, e| a.saturating_add(e.segment_duration));
134 let media = self.seconds(self.duration.min(i64::MAX as u64) as i64);
135 if edited == 0 {
136 media
137 } else {
138 media.min(self.seconds(self.media_span()))
139 }
140 }
141
142 fn media_span(&self) -> i64 {
143 let end = self
144 .samples
145 .iter()
146 .map(|s| s.pts.saturating_add(s.duration as i64))
147 .max()
148 .unwrap_or(0);
149 end.saturating_add(self.edit_offset).max(0)
150 }
151
152 pub fn bitrate(&self) -> u32 {
153 let declared = match self.config() {
154 Some(CodecConfig::Aac(a)) if a.avg_bitrate > 0 => a.avg_bitrate,
155 _ => self
156 .entry()
157 .and_then(|e| e.bitrate)
158 .map_or(0, |b| b.avg_bitrate),
159 };
160 if declared > 0 {
161 return declared;
162 }
163 let bytes: u64 = self.samples.iter().map(|s| s.size as u64).sum();
164 let ticks: u64 = self.samples.iter().map(|s| s.duration as u64).sum();
165 if ticks == 0 || self.timescale == 0 {
166 return 0;
167 }
168 (bytes as u128 * 8 * self.timescale as u128 / ticks as u128).min(u32::MAX as u128) as u32
169 }
170
171 pub fn fps(&self) -> f64 {
172 let ticks: u64 = self.samples.iter().map(|s| s.duration as u64).sum();
173 if ticks == 0 || self.timescale == 0 {
174 0.0
175 } else {
176 self.samples.len() as f64 * self.timescale as f64 / ticks as f64
177 }
178 }
179
180 pub fn presentation(&self, index: usize) -> Option<i64> {
181 self.samples
182 .get(index)
183 .map(|s| s.pts.saturating_add(self.edit_offset))
184 }
185
186 pub fn sample_at(&self, presentation: i64) -> Option<usize> {
187 let pts = presentation.saturating_sub(self.edit_offset);
188 if self.pts_order.is_empty() {
189 return None;
190 }
191 let k = self
192 .pts_order
193 .partition_point(|&i| self.samples[i as usize].pts <= pts);
194 Some(self.pts_order[k.saturating_sub(1)] as usize)
195 }
196
197 pub fn keyframe_before(&self, index: usize) -> usize {
198 let k = self.sync_index.partition_point(|&i| i as usize <= index);
199 if k == 0 {
200 0
201 } else {
202 self.sync_index[k - 1] as usize
203 }
204 }
205
206 pub fn keyframes(&self) -> &[u32] {
207 &self.sync_index
208 }
209
210 fn finish_indexes(&mut self) {
211 self.sync_index = self
212 .samples
213 .iter()
214 .enumerate()
215 .filter(|(_, s)| s.keyframe)
216 .map(|(i, _)| i as u32)
217 .collect();
218 let mut order: Vec<u32> = (0..self.samples.len() as u32).collect();
219 order.sort_by_key(|&i| (self.samples[i as usize].pts, i));
220 self.pts_order = order;
221 }
222}
223
224pub struct Reader<S: ByteSource = File> {
225 source: S,
226 len: u64,
227 pub brand: FourCc,
228 pub quicktime: bool,
229 pub timescale: u32,
230 pub duration: u64,
231 pub tracks: Vec<Track>,
232 pub fragmented: bool,
233}
234
235impl Reader<File> {
236 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
237 let path = path.as_ref();
238 File::open(path)
239 .map_err(Error::from)
240 .and_then(Self::new)
241 .map_err(|e| e.in_file(path))
242 }
243}
244
245impl<S: ByteSource> Reader<S> {
246 pub fn new(source: S) -> Result<Self> {
247 open(source)
248 }
249
250 pub fn source(&self) -> &S {
251 &self.source
252 }
253
254 pub fn duration_seconds(&self) -> f64 {
255 let movie = if self.timescale == 0 {
256 0.0
257 } else {
258 self.duration as f64 / self.timescale as f64
259 };
260 let tracks = self
261 .tracks
262 .iter()
263 .map(Track::duration_seconds)
264 .fold(0.0, f64::max);
265 if movie > 0.0 { movie } else { tracks }
266 }
267
268 pub fn video(&self) -> Option<usize> {
269 self.tracks.iter().position(|t| t.kind == TrackKind::Video)
270 }
271
272 pub fn audio(&self) -> Vec<usize> {
273 self.tracks
274 .iter()
275 .enumerate()
276 .filter(|(_, t)| t.kind == TrackKind::Audio)
277 .map(|(i, _)| i)
278 .collect()
279 }
280
281 pub fn read(&self, track: usize, index: usize) -> Result<Vec<u8>> {
282 let mut data = Vec::new();
283 self.read_into(track, index, &mut data)?;
284 Ok(data)
285 }
286
287 pub fn read_into(&self, track: usize, index: usize, data: &mut Vec<u8>) -> Result<()> {
288 let t = self
289 .tracks
290 .get(track)
291 .ok_or_else(|| Error::Invalid(format!("no track {track}")))?;
292 let s = t
293 .samples
294 .get(index)
295 .ok_or_else(|| Error::Invalid(format!("no sample {index} in track {track}")))?;
296 read_range(&self.source, self.len, s.offset, s.size as u64, data)
297 }
298
299 pub fn read_span(&self, offset: u64, data: &mut Vec<u8>) -> Result<()> {
300 read_range(&self.source, self.len, offset, data.len() as u64, data)
301 }
302
303 pub fn packet(&self, track: usize, index: usize) -> Result<Packet> {
304 let data = self.read(track, index)?;
305 let (t, s) = self
306 .tracks
307 .get(track)
308 .and_then(|t| Some((t, *t.samples.get(index)?)))
309 .ok_or_else(|| Error::Invalid(format!("no sample {index} in track {track}")))?;
310 Ok(Packet {
311 data,
312 pts: s.pts.saturating_add(t.edit_offset),
313 dts: s.dts.saturating_add(t.edit_offset),
314 keyframe: s.keyframe,
315 })
316 }
317}
318
319fn times(c: &mut Cur, v: u8) -> Result<(u32, u64)> {
320 c.skip(if v == 1 { 16 } else { 8 })?;
321 let timescale = c.u32()?;
322 let duration = if v == 1 { c.u64()? } else { c.u32()? as u64 };
323 Ok((timescale, duration))
324}
325
326fn read_range<S: ByteSource + ?Sized>(
327 src: &S,
328 len: u64,
329 offset: u64,
330 size: u64,
331 data: &mut Vec<u8>,
332) -> Result<()> {
333 let end = offset
334 .checked_add(size)
335 .ok_or_else(|| truncated("sample"))?;
336 if end > len {
337 return Err(truncated("sample data"));
338 }
339 data.resize(size as usize, 0);
340 src.read_at(offset, data)?;
341 Ok(())
342}
343
344fn parse_name(udta: &[u8]) -> Option<String> {
345 for b in boxes(udta).map_while(|b| b.ok()) {
346 let text = if b.kind == b"name" {
347 b.payload
348 } else if b.kind == [0xA9, b'n', b'a', b'm'] && b.payload.len() >= 4 {
349 let n = u16::from_be_bytes([b.payload[0], b.payload[1]]) as usize;
350 b.payload.get(4..4 + n).unwrap_or(&[])
351 } else {
352 continue;
353 };
354 let text = text.split(|&c| c == 0).next().unwrap_or(&[]);
355 if !text.is_empty() {
356 return Some(String::from_utf8_lossy(text).into_owned());
357 }
358 }
359 None
360}
361
362fn open<S: ByteSource>(src: S) -> Result<Reader<S>> {
363 let len = src.len();
364 let mut offset = 0u64;
365 let mut ftyp: Option<Vec<u8>> = None;
366 let mut moov: Option<(u64, Vec<u8>)> = None;
367 let mut moofs: Vec<(u64, Vec<u8>)> = Vec::new();
368 while len.saturating_sub(offset) >= 8 {
369 let n = (len - offset).min(32) as usize;
370 let mut hb = [0u8; 32];
371 src.read_at(offset, &mut hb[..n])?;
372 let h = parse_box_header(&hb[..n])?;
373 let size = h.size.unwrap_or(len - offset);
374 let end = offset.saturating_add(size).min(len);
375 let body_len = end.saturating_sub(offset + h.header_len);
376 let body = |max: u64| -> Result<Vec<u8>> {
377 if body_len > max {
378 return Err(too_large("header box"));
379 }
380 let mut data = Vec::new();
381 read_range(&src, len, offset + h.header_len, body_len, &mut data)?;
382 Ok(data)
383 };
384 match &h.kind.0 {
385 b"ftyp" if ftyp.is_none() => ftyp = Some(body(1 << 16)?),
386 b"moov" if moov.is_none() => moov = Some((offset, body(MAX_HEADER_BOX)?)),
387 b"moof" => moofs.push((offset, body(MAX_HEADER_BOX)?)),
388 _ => {}
389 }
390 if h.size.is_none() {
391 break;
392 }
393 offset = offset.saturating_add(size);
394 }
395 let (_, moov) = moov.ok_or_else(|| Error::Invalid("no moov box".into()))?;
396 let mut file = Reader {
397 source: src,
398 len,
399 brand: FourCc::default(),
400 quicktime: true,
401 timescale: 0,
402 duration: 0,
403 tracks: Vec::new(),
404 fragmented: false,
405 };
406 if let Some(f) = &ftyp {
407 let mut c = Cur::new(f, "ftyp");
408 file.brand = c.fourcc()?;
409 file.quicktime = file.brand == b"qt ";
410 }
411 let mut trex: HashMap<u32, Trex> = HashMap::new();
412 let mut trak_payloads = Vec::new();
413 for b in boxes(&moov) {
414 let b = b?;
415 match &b.kind.0 {
416 b"cmov" => return Err(Error::Unsupported("compressed movie header (cmov)".into())),
417 b"mvhd" => {
418 let mut c = Cur::new(b.payload, "mvhd");
419 let (v, _) = c.full_header()?;
420 (file.timescale, file.duration) = times(&mut c, v)?;
421 }
422 b"trak" => trak_payloads.push(b.payload),
423 b"mvex" => {
424 file.fragmented = true;
425 for m in boxes(b.payload).map_while(|b| b.ok()) {
426 if m.kind == b"trex" {
427 let mut c = Cur::new(m.payload, "trex");
428 c.full_header()?;
429 let id = c.u32()?;
430 trex.insert(
431 id,
432 Trex {
433 description_index: c.u32()?,
434 duration: c.u32()?,
435 size: c.u32()?,
436 flags: c.u32()?,
437 },
438 );
439 }
440 }
441 }
442 _ => {}
443 }
444 }
445 let mut room = len;
446 for p in trak_payloads {
447 let t = parse_trak(p, file.timescale, file.quicktime, len, &mut room)?;
448 file.tracks.push(t);
449 }
450 let mut next_dts: Vec<i64> = file
451 .tracks
452 .iter()
453 .map(|t| {
454 t.samples
455 .last()
456 .map(|s| s.dts.saturating_add(s.duration as i64))
457 .unwrap_or(0)
458 })
459 .collect();
460 for (moof_off, moof) in &moofs {
461 parse_moof(*moof_off, moof, &mut file.tracks, &trex, &mut next_dts, len)?;
462 }
463 for t in &mut file.tracks {
464 if file.fragmented && !moofs.is_empty() {
465 let d: u64 = t.samples.iter().map(|s| s.duration as u64).sum();
466 t.duration = t.duration.max(d);
467 if t.timescale > 0 && file.timescale > 0 {
468 let md = (t.duration as u128 * file.timescale as u128 / t.timescale as u128) as u64;
469 file.duration = file.duration.max(md);
470 }
471 }
472 t.finish_indexes();
473 }
474 Ok(file)
475}
476
477#[derive(Clone, Copy, Debug, Default)]
478struct Trex {
479 description_index: u32,
480 duration: u32,
481 size: u32,
482 flags: u32,
483}
484
485fn parse_language(code: u16) -> String {
486 if code < 0x400 {
487 return if code == 0 {
488 "eng".into()
489 } else {
490 "und".into()
491 };
492 }
493 let c = |s: u16| (((code >> s) & 0x1F) as u8 + 0x60) as char;
494 let s: String = [c(10), c(5), c(0)].into_iter().collect();
495 if s.chars().all(|ch| ch.is_ascii_lowercase()) {
496 s
497 } else {
498 "und".into()
499 }
500}
501
502fn rescale(v: i128, from: u32, to: u32) -> i64 {
503 if from == 0 {
504 return 0;
505 }
506 (v * to as i128 / from as i128).clamp(i64::MIN as i128, i64::MAX as i128) as i64
507}
508
509fn parse_trak(
510 p: &[u8],
511 movie_timescale: u32,
512 quicktime: bool,
513 file_len: u64,
514 room: &mut u64,
515) -> Result<Track> {
516 let mut t = Track {
517 id: 0,
518 kind: TrackKind::Other,
519 handler: FourCc::default(),
520 name: String::new(),
521 timescale: 0,
522 duration: 0,
523 language: "und".into(),
524 width: 0,
525 height: 0,
526 enabled: true,
527 matrix: [0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x4000_0000],
528 entries: Vec::new(),
529 samples: Vec::new(),
530 edits: Vec::new(),
531 edit_offset: 0,
532 sync_index: Vec::new(),
533 pts_order: Vec::new(),
534 };
535 let mut mdia = None;
536 let mut name = None;
537 for b in boxes(p) {
538 let b = b?;
539 match &b.kind.0 {
540 b"tkhd" => {
541 let mut c = Cur::new(b.payload, "tkhd");
542 let (v, flags) = c.full_header()?;
543 t.enabled = flags & 1 != 0;
544 c.skip(if v == 1 { 16 } else { 8 })?;
545 t.id = c.u32()?;
546 c.skip(if v == 1 { 28 } else { 24 })?;
547 for m in t.matrix.iter_mut() {
548 *m = c.i32()?;
549 }
550 t.width = c.u32()? >> 16;
551 t.height = c.u32()? >> 16;
552 }
553 b"edts" => {
554 if let Some(e) = find(b.payload, b"elst") {
555 let mut c = Cur::new(e, "elst");
556 let (v, _) = c.full_header()?;
557 let n = c.count(if v == 1 { 20 } else { 12 })?;
558 for _ in 0..n {
559 let (segment_duration, media_time) = if v == 1 {
560 (c.u64()?, c.i64()?)
561 } else {
562 (c.u32()? as u64, c.i32()? as i64)
563 };
564 t.edits.push(Edit {
565 segment_duration,
566 media_time,
567 media_rate: c.i32()?,
568 });
569 }
570 }
571 }
572 b"udta" => {
573 if let Some(n) = parse_name(b.payload) {
574 name = Some(n);
575 }
576 }
577 b"mdia" => mdia = Some(b.payload),
578 _ => {}
579 }
580 }
581 let mdia = mdia.ok_or_else(|| Error::Invalid("trak without mdia".into()))?;
582 let mut minf = None;
583 for b in boxes(mdia) {
584 let b = b?;
585 match &b.kind.0 {
586 b"mdhd" => {
587 let mut c = Cur::new(b.payload, "mdhd");
588 let (v, _) = c.full_header()?;
589 (t.timescale, t.duration) = times(&mut c, v)?;
590 t.language = parse_language(c.u16()?);
591 }
592 b"hdlr" => {
593 let mut c = Cur::new(b.payload, "hdlr");
594 c.full_header()?;
595 let _pre = c.u32()?;
596 t.handler = c.fourcc()?;
597 c.skip(12)?;
598 let name = c.rest();
599 let s = if !name.is_empty() && name[0] as usize == name.len() - 1 && name.len() > 1
600 {
601 &name[1..]
602 } else {
603 name.split(|&b| b == 0).next().unwrap_or(&[])
604 };
605 t.name = String::from_utf8_lossy(s).into_owned();
606 }
607 b"minf" => minf = Some(b.payload),
608 _ => {}
609 }
610 }
611 t.kind = match &t.handler.0 {
612 b"vide" => TrackKind::Video,
613 b"soun" => TrackKind::Audio,
614 b"text" | b"sbtl" | b"subt" | b"clcp" => TrackKind::Subtitle,
615 _ => TrackKind::Other,
616 };
617 let mut empty = 0i128;
618 for e in &t.edits {
619 if e.media_time == -1 {
620 empty += e.segment_duration as i128;
621 } else {
622 t.edit_offset =
623 rescale(empty, movie_timescale, t.timescale).saturating_sub(e.media_time);
624 break;
625 }
626 }
627 if let Some(stbl) = minf.and_then(|m| find(m, b"stbl")) {
628 parse_stbl(stbl, &mut t, quicktime, file_len, room)?;
629 }
630 if let Some(n) = name {
631 t.name = n;
632 }
633 Ok(t)
634}
635
636struct SttsCursor<'a> {
637 runs: &'a [(u32, u32)],
638 run: usize,
639 used: u32,
640}
641
642impl SttsCursor<'_> {
643 fn take(&mut self, mut n: u64) -> u64 {
644 let mut total = 0u64;
645 while n > 0 && self.run < self.runs.len() {
646 let (count, delta) = self.runs[self.run];
647 let avail = (count - self.used) as u64;
648 let k = avail.min(n);
649 total = total.saturating_add(k.saturating_mul(delta as u64));
650 n -= k;
651 self.used += k as u32;
652 if self.used >= count {
653 self.run += 1;
654 self.used = 0;
655 }
656 }
657 total
658 }
659 fn next_value(&mut self) -> u32 {
660 while self.run < self.runs.len() && self.runs[self.run].0 == 0 {
661 self.run += 1;
662 }
663 let Some(&(_, d)) = self.runs.get(self.run) else {
664 return 0;
665 };
666 self.take(1);
667 d
668 }
669}
670
671fn chunk_capacity(stsc: &[(u32, u32, u32)], chunks: usize) -> u64 {
672 let end = chunks as u64 + 1;
673 stsc.iter()
674 .enumerate()
675 .map(|(i, &(first, spc, _))| {
676 let next = stsc.get(i + 1).map_or(end, |n| (n.0 as u64).min(end));
677 next.saturating_sub(first as u64).saturating_mul(spc as u64)
678 })
679 .fold(0u64, u64::saturating_add)
680}
681
682fn parse_stbl(
683 stbl: &[u8],
684 t: &mut Track,
685 is_qt: bool,
686 file_len: u64,
687 room: &mut u64,
688) -> Result<()> {
689 let mut stts: Vec<(u32, u32)> = Vec::new();
690 let mut ctts: Vec<(u32, u32)> = Vec::new();
691 let mut stss: Option<Vec<u32>> = None;
692 let mut stsc: Vec<(u32, u32, u32)> = Vec::new();
693 let mut const_size = 0u32;
694 let mut sample_count = 0usize;
695 let mut sizes: Vec<u32> = Vec::new();
696 let mut chunks: Vec<u64> = Vec::new();
697 for b in boxes(stbl) {
698 let b = b?;
699 let mut c = Cur::new(b.payload, "stbl child");
700 match &b.kind.0 {
701 b"stsd" => {
702 c.full_header()?;
703 let n = c.count(8)?;
704 for e in boxes(c.rest()).take(n) {
705 let e = e?;
706 t.entries
707 .push(parse_sample_entry(e.kind, e.payload, t.handler, is_qt)?);
708 }
709 }
710 b"stts" => {
711 c.full_header()?;
712 let n = c.count(8)?;
713 stts = (0..n)
714 .map(|_| Ok((c.u32()?, c.u32()?)))
715 .collect::<Result<_>>()?;
716 }
717 b"ctts" => {
718 c.full_header()?;
719 let n = c.count(8)?;
720 ctts = (0..n)
721 .map(|_| Ok((c.u32()?, c.u32()?)))
722 .collect::<Result<_>>()?;
723 }
724 b"stss" => {
725 c.full_header()?;
726 let n = c.count(4)?;
727 let mut v: Vec<u32> = (0..n).map(|_| c.u32()).collect::<Result<_>>()?;
728 v.sort_unstable();
729 stss = Some(v);
730 }
731 b"stsc" => {
732 c.full_header()?;
733 let n = c.count(12)?;
734 stsc = (0..n)
735 .map(|_| Ok((c.u32()?, c.u32()?, c.u32()?)))
736 .collect::<Result<_>>()?;
737 }
738 b"stsz" => {
739 c.full_header()?;
740 const_size = c.u32()?;
741 let n = c.u32()? as usize;
742 match NonZeroU64::new(const_size as u64) {
743 None => {
744 c.check_count(n, 4)?;
745 sizes = (0..n).map(|_| c.u32()).collect::<Result<_>>()?;
746 sample_count = n;
747 }
748 Some(size) => {
749 sample_count =
750 usize::try_from((n as u64).min(file_len / size)).unwrap_or(usize::MAX);
751 }
752 }
753 }
754 b"stz2" => {
755 c.full_header()?;
756 let field = c.u32()? & 0xFF;
757 let n = c.u32()? as usize;
758 let bits = match field {
759 4 | 8 | 16 => field as usize,
760 _ => return Err(Error::Invalid(format!("stz2 field size {field}"))),
761 };
762 if n.checked_mul(bits)
763 .is_none_or(|b| b.div_ceil(8) > c.remaining())
764 {
765 return Err(truncated("stz2"));
766 }
767 let d = c.rest();
768 sizes = (0..n)
769 .map(|i| match bits {
770 4 => ((d[i / 2] >> if i % 2 == 0 { 4 } else { 0 }) & 0xF) as u32,
771 8 => d[i] as u32,
772 _ => u16::from_be_bytes([d[2 * i], d[2 * i + 1]]) as u32,
773 })
774 .collect();
775 sample_count = n;
776 }
777 b"stco" => {
778 c.full_header()?;
779 let n = c.count(4)?;
780 chunks = (0..n)
781 .map(|_| c.u32().map(|v| v as u64))
782 .collect::<Result<_>>()?;
783 }
784 b"co64" => {
785 c.full_header()?;
786 let n = c.count(8)?;
787 chunks = (0..n).map(|_| c.u64()).collect::<Result<_>>()?;
788 }
789 _ => {}
790 }
791 }
792 if sample_count == 0 || chunks.is_empty() || stsc.is_empty() {
793 return Ok(());
794 }
795 if stsc[0].0 == 0 || stsc.windows(2).any(|w| w[1].0 <= w[0].0) {
796 return Err(Error::Invalid(
797 "stsc first_chunk values are not increasing from 1".into(),
798 ));
799 }
800 let pcm = match t.entries.first().map(|e| &e.codec) {
801 Some(CodecConfig::Pcm(p)) if const_size != 0 => Some(*p),
802 _ => None,
803 };
804 let mut stts_c = SttsCursor {
805 runs: &stts,
806 run: 0,
807 used: 0,
808 };
809 let mut ctts_c = SttsCursor {
810 runs: &ctts,
811 run: 0,
812 used: 0,
813 };
814 let mut samples = Vec::new();
815 let mut sidx = 0usize;
816 let mut dts = 0i64;
817 let mut stsc_i = 0usize;
818 if let Some(p) = pcm {
819 let a = t.entry().and_then(|e| e.audio.clone()).unwrap_or_default();
820 let frame_bytes = |frames: u64| -> u64 {
821 if a.qt_version == 1 && a.bytes_per_frame > 0 && a.samples_per_packet > 0 {
822 frames / a.samples_per_packet as u64 * a.bytes_per_frame as u64
823 } else if const_size > 1 {
824 frames * const_size as u64
825 } else {
826 frames * p.bytes_per_frame() as u64
827 }
828 };
829 if chunks.len() > MAX_SAMPLES {
830 return Err(too_large("chunk count"));
831 }
832 samples.reserve(chunks.len());
833 for (ci, &off) in chunks.iter().enumerate() {
834 while stsc_i + 1 < stsc.len() && stsc[stsc_i + 1].0 as usize <= ci + 1 {
835 stsc_i += 1;
836 }
837 let spc = (stsc[stsc_i].1 as usize).min(sample_count - sidx);
838 if spc == 0 {
839 continue;
840 }
841 let dur = stts_c.take(spc as u64);
842 let size = frame_bytes(spc as u64).min(u32::MAX as u64) as u32;
843 samples.push(Sample {
844 offset: off,
845 size,
846 dts,
847 pts: dts,
848 duration: dur.min(u32::MAX as u64) as u32,
849 keyframe: true,
850 entry: stsc[stsc_i].2.saturating_sub(1),
851 });
852 dts = dts.saturating_add(dur.min(i64::MAX as u64) as i64);
853 sidx += spc;
854 if sidx >= sample_count {
855 break;
856 }
857 }
858 } else {
859 let capacity = chunk_capacity(&stsc, chunks.len());
860 let mut sample_count = sample_count.min(usize::try_from(capacity).unwrap_or(usize::MAX));
861 if let Some(size) = NonZeroU64::new(const_size as u64) {
862 let fit = usize::try_from(*room / size).unwrap_or(usize::MAX);
863 sample_count = sample_count.min(fit);
864 *room = room.saturating_sub((sample_count as u64).saturating_mul(size.get()));
865 }
866 if sample_count > MAX_SAMPLES {
867 return Err(too_large("sample count"));
868 }
869 samples.reserve(sample_count);
870 let mut stss_i = 0usize;
871 'chunks: for (ci, &off) in chunks.iter().enumerate() {
872 while stsc_i + 1 < stsc.len() && stsc[stsc_i + 1].0 as usize <= ci + 1 {
873 stsc_i += 1;
874 }
875 let (_, spc, sdi) = stsc[stsc_i];
876 let mut pos = off;
877 for _ in 0..spc {
878 if sidx >= sample_count {
879 break 'chunks;
880 }
881 let size = if const_size != 0 {
882 const_size
883 } else {
884 *sizes.get(sidx).ok_or_else(|| truncated("stsz"))?
885 };
886 let duration = stts_c.next_value();
887 let cts = ctts_c.next_value() as i32 as i64;
888 let keyframe = match &stss {
889 None => true,
890 Some(v) => {
891 while stss_i < v.len() && (v[stss_i] as usize) < sidx + 1 {
892 stss_i += 1;
893 }
894 stss_i < v.len() && v[stss_i] as usize == sidx + 1
895 }
896 };
897 samples.push(Sample {
898 offset: pos,
899 size,
900 dts,
901 pts: dts.saturating_add(cts),
902 duration,
903 keyframe,
904 entry: sdi.saturating_sub(1),
905 });
906 pos = pos.saturating_add(size as u64);
907 dts = dts.saturating_add(duration as i64);
908 sidx += 1;
909 }
910 }
911 }
912 t.samples = samples;
913 Ok(())
914}
915
916fn parse_moof(
917 moof_off: u64,
918 moof: &[u8],
919 tracks: &mut [Track],
920 trex: &HashMap<u32, Trex>,
921 next_dts: &mut [i64],
922 file_len: u64,
923) -> Result<()> {
924 let mut prev_end: Option<u64> = None;
925 for b in boxes(moof) {
926 let b = b?;
927 if b.kind != b"traf" {
928 continue;
929 }
930 let mut tfhd = None;
931 let mut tfdt = None;
932 let mut truns = Vec::new();
933 for c in boxes(b.payload) {
934 let c = c?;
935 match &c.kind.0 {
936 b"tfhd" => tfhd = Some(c.payload),
937 b"tfdt" => tfdt = Some(c.payload),
938 b"trun" => truns.push(c.payload),
939 _ => {}
940 }
941 }
942 let tfhd = tfhd.ok_or_else(|| Error::Invalid("traf without tfhd".into()))?;
943 let mut c = Cur::new(tfhd, "tfhd");
944 let (_, fl) = c.full_header()?;
945 let id = c.u32()?;
946 let Some(ti) = tracks.iter().position(|t| t.id == id) else {
947 continue;
948 };
949 let d = trex.get(&id).copied().unwrap_or_default();
950 let base = if fl & 1 != 0 {
951 c.u64()?
952 } else if fl & 0x20000 != 0 {
953 moof_off
954 } else {
955 prev_end.unwrap_or(moof_off)
956 };
957 let sdi = if fl & 2 != 0 {
958 c.u32()?
959 } else {
960 d.description_index
961 };
962 let def_dur = if fl & 8 != 0 { c.u32()? } else { d.duration };
963 let def_size = if fl & 0x10 != 0 { c.u32()? } else { d.size };
964 let def_flags = if fl & 0x20 != 0 { c.u32()? } else { d.flags };
965 if let Some(p) = tfdt {
966 let mut c = Cur::new(p, "tfdt");
967 let (v, _) = c.full_header()?;
968 let t = if v == 1 { c.u64()? } else { c.u32()? as u64 };
969 next_dts[ti] = t.min(i64::MAX as u64) as i64;
970 }
971 let mut data_pos = base;
972 for tr in truns {
973 let mut c = Cur::new(tr, "trun");
974 let (v, tf) = c.full_header()?;
975 let per = [0x100u32, 0x200, 0x400, 0x800]
976 .iter()
977 .filter(|&&f| tf & f != 0)
978 .count()
979 * 4;
980 let n = c.u32()? as usize;
981 let total = (tracks[ti].samples.len() as u64).saturating_add(n as u64);
982 if per > 0 {
983 c.check_count(n, per)?;
984 } else if def_size == 0 {
985 if total > (file_len / 64).max(MAX_EMPTY_SAMPLES) {
986 return Err(too_large("trun sample count"));
987 }
988 } else if total.saturating_mul(def_size as u64) > file_len {
989 return Err(too_large("trun sample count"));
990 }
991 if tracks[ti].samples.len() + n > MAX_SAMPLES {
992 return Err(too_large("sample count"));
993 }
994 if tf & 1 != 0 {
995 data_pos = base.wrapping_add_signed(c.i32()? as i64);
996 }
997 let first_flags = if tf & 4 != 0 { Some(c.u32()?) } else { None };
998 let t = &mut tracks[ti];
999 t.samples.reserve(n);
1000 for i in 0..n {
1001 let dur = if tf & 0x100 != 0 { c.u32()? } else { def_dur };
1002 let size = if tf & 0x200 != 0 { c.u32()? } else { def_size };
1003 let flags = if tf & 0x400 != 0 { c.u32()? } else { def_flags };
1004 let flags = if i == 0 {
1005 first_flags.unwrap_or(flags)
1006 } else {
1007 flags
1008 };
1009 let cto = if tf & 0x800 != 0 {
1010 let raw = c.u32()?;
1011 if v == 0 {
1012 raw as i64
1013 } else {
1014 raw as i32 as i64
1015 }
1016 } else {
1017 0
1018 };
1019 let dts = next_dts[ti];
1020 t.samples.push(Sample {
1021 offset: data_pos,
1022 size,
1023 dts,
1024 pts: dts.saturating_add(cto),
1025 duration: dur,
1026 keyframe: flags & 0x1_0000 == 0,
1027 entry: sdi.saturating_sub(1),
1028 });
1029 data_pos = data_pos.saturating_add(size as u64);
1030 next_dts[ti] = dts.saturating_add(dur as i64);
1031 }
1032 }
1033 prev_end = Some(data_pos);
1034 }
1035 Ok(())
1036}