1use std::collections::HashSet;
2use std::fs::File;
3use std::path::Path;
4use std::sync::OnceLock;
5
6use super::ebml::{self, Children, Lacing, Size, parse_lacing, read_id, read_size};
7use super::ids::*;
8use super::parse::{self, Cluster, CuePoint, Info, SeekEntry};
9use super::track::{Sample, Track};
10use crate::media::{Error, Packet, Result};
11use crate::mp4::{ByteSource, TrackKind};
12
13const WIN: usize = 64 * 1024;
14const MAX_WIN: usize = 4 << 20;
15const MAX_META: u64 = 256 << 20;
16const MAX_SAMPLES: usize = 1 << 26;
17const MAX_WARNINGS: usize = 256;
18
19fn warn(warnings: &mut Vec<String>, line: impl FnOnce() -> String) {
20 match warnings.len().cmp(&MAX_WARNINGS) {
21 std::cmp::Ordering::Less => warnings.push(line()),
22 std::cmp::Ordering::Equal => warnings.push("further damage not listed".into()),
23 std::cmp::Ordering::Greater => {}
24 }
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub struct Options {
29 pub index: bool,
30}
31
32impl Default for Options {
33 fn default() -> Self {
34 Self { index: true }
35 }
36}
37
38#[derive(Default, Clone, Debug)]
39pub(crate) struct Cache {
40 buf: Vec<u8>,
41 start: u64,
42 win: usize,
43 scanned: Option<Scanned>,
44}
45
46#[derive(Clone, Copy, Debug)]
47struct Scanned {
48 from: u64,
49 to: u64,
50 limit: u64,
51 found: bool,
52}
53
54pub(crate) struct Io<'a, S: ByteSource + ?Sized> {
55 src: &'a S,
56 pub(crate) len: u64,
57 cache: &'a mut Cache,
58}
59
60#[derive(Clone, Copy, Debug)]
61pub(crate) struct Hdr {
62 pub(crate) id: u32,
63 pub(crate) pos: u64,
64 pub(crate) size: Size,
65 hlen: usize,
66}
67
68impl Hdr {
69 pub(crate) fn data_start(&self) -> u64 {
70 self.pos + self.hlen as u64
71 }
72
73 pub(crate) fn end(&self, limit: u64) -> u64 {
74 match self.size {
75 Size::Known(n) => self.data_start().saturating_add(n).min(limit),
76 Size::Unknown => limit,
77 }
78 }
79
80 pub(crate) fn overruns(&self, limit: u64) -> bool {
81 matches!(self.size, Size::Known(n) if self.data_start().saturating_add(n) > limit)
82 }
83}
84
85fn eof() -> Error {
86 Error::Invalid("truncated Matroska data".into())
87}
88
89impl<'a, S: ByteSource + ?Sized> Io<'a, S> {
90 pub(crate) fn new(src: &'a S, cache: &'a mut Cache) -> Self {
91 let len = src.len();
92 Self { src, len, cache }
93 }
94
95 pub(crate) fn with_len(src: &'a S, len: u64, cache: &'a mut Cache) -> Self {
96 Self { src, len, cache }
97 }
98
99 fn cached(&self, pos: u64, end: u64) -> Option<std::ops::Range<usize>> {
100 let c = &*self.cache;
101 let from = usize::try_from(pos.checked_sub(c.start)?).ok()?;
102 let to = usize::try_from(end.checked_sub(c.start)?).ok()?;
103 (from <= to && to <= c.buf.len()).then_some(from..to)
104 }
105
106 fn refill(&mut self, pos: u64) -> Result<()> {
107 let c = &*self.cache;
108 let end = c.start.saturating_add(c.buf.len() as u64);
109 let ahead = !c.buf.is_empty() && pos >= c.start && pos <= end.saturating_add(c.win as u64);
110 let win = if ahead {
111 c.win.saturating_mul(2).clamp(WIN, MAX_WIN)
112 } else {
113 WIN
114 };
115 let fill = self.len.saturating_sub(pos).min(win as u64) as usize;
116 let mut buf = std::mem::take(&mut self.cache.buf);
117 buf.resize(fill, 0);
118 let read = self.src.read_at(pos, &mut buf);
119 if read.is_err() {
120 buf.clear();
121 }
122 self.cache.buf = buf;
123 self.cache.start = pos;
124 self.cache.win = win;
125 Ok(read?)
126 }
127
128 pub(crate) fn peek(&mut self, pos: u64, n: usize) -> Result<&[u8]> {
129 let n = n.min(WIN);
130 if pos >= self.len {
131 return Ok(&[]);
132 }
133 let want_end = pos.saturating_add(n as u64).min(self.len);
134 let range = match self.cached(pos, want_end) {
135 Some(r) => r,
136 None => {
137 self.refill(pos)?;
138 self.cached(pos, want_end).ok_or_else(eof)?
139 }
140 };
141 self.cache.buf.get(range).ok_or_else(eof)
142 }
143
144 pub(crate) fn read_into(&mut self, pos: u64, n: u64, out: &mut Vec<u8>) -> Result<()> {
145 let end = pos
146 .checked_add(n)
147 .filter(|&e| e <= self.len)
148 .ok_or_else(eof)?;
149 if let Some(r) = self.cached(pos, end) {
150 out.extend_from_slice(self.cache.buf.get(r).unwrap_or_default());
151 return Ok(());
152 }
153 let n = usize::try_from(n).map_err(|_| eof())?;
154 if n <= WIN / 2 {
155 let b = self.peek(pos, n)?;
156 out.extend_from_slice(b);
157 return Ok(());
158 }
159 let at = out.len();
160 out.resize(at.checked_add(n).ok_or_else(eof)?, 0);
161 self.src.read_at(pos, &mut out[at..])?;
162 Ok(())
163 }
164
165 fn read_vec(&mut self, pos: u64, n: u64) -> Result<Vec<u8>> {
166 let mut v = Vec::new();
167 self.read_into(pos, n, &mut v)?;
168 Ok(v)
169 }
170
171 pub(crate) fn header(&mut self, pos: u64, limit: u64) -> Result<Option<Hdr>> {
172 if pos >= limit {
173 return Ok(None);
174 }
175 let b = self.peek(pos, 12)?;
176 let Some((id, il)) = read_id(b) else {
177 return Ok(None);
178 };
179 let Some((size, sl)) = read_size(&b[il..]) else {
180 return Ok(None);
181 };
182 let hlen = il + sl;
183 if pos + hlen as u64 > limit {
184 return Ok(None);
185 }
186 Ok(Some(Hdr {
187 id,
188 pos,
189 size,
190 hlen,
191 }))
192 }
193
194 pub(crate) fn uint(&mut self, h: &Hdr, end: u64) -> Result<u64> {
195 let n = end.saturating_sub(h.data_start()).min(8) as usize;
196 Ok(ebml::uint(self.peek(h.data_start(), n)?))
197 }
198
199 pub(crate) fn resync(&mut self, from: u64, limit: u64) -> Result<Option<u64>> {
200 let memo = self
201 .cache
202 .scanned
203 .filter(|m| m.limit == limit && m.to >= from);
204 let last = limit.saturating_sub(3);
205 let mut pos = from;
206 loop {
207 if let Some(m) = memo
208 && m.from <= pos
209 {
210 return Ok(self.remember(from.min(m.from), m.to, limit, m.found));
211 }
212 let stop = memo.map_or(last, |m| m.from.min(last));
213 if pos >= stop {
214 return Ok(self.remember(from, limit, limit, false));
215 }
216 let hit = {
217 let chunk = self.peek(pos, WIN)?;
218 let n = chunk.len().saturating_sub(3);
219 if n == 0 {
220 return Ok(None);
221 }
222 let n = n.min(usize::try_from(stop - pos).unwrap_or(usize::MAX));
223 let found = chunk.windows(4).take(n).enumerate().find_map(|(i, w)| {
224 let &[a, b, c, d] = w else {
225 return None;
226 };
227 if a & 0xF0 != 0x10 {
228 return None;
229 }
230 let id = u32::from_be_bytes([a, b, c, d]);
231 (is_top_level(id) && id != EBML && id != SEGMENT).then_some((i, id))
232 });
233 match found {
234 Some((i, id)) => Ok((pos + i as u64, id)),
235 None => Err(pos + n as u64),
236 }
237 };
238 match hit {
239 Ok((p, id)) => {
240 if self.plausible(p, id, limit)? {
241 return Ok(self.remember(from, p, limit, true));
242 }
243 pos = p + 1;
244 }
245 Err(next) => pos = next,
246 }
247 }
248 }
249
250 fn remember(&mut self, from: u64, to: u64, limit: u64, found: bool) -> Option<u64> {
251 self.cache.scanned = Some(Scanned {
252 from,
253 to,
254 limit,
255 found,
256 });
257 found.then_some(to)
258 }
259
260 fn plausible(&mut self, p: u64, id: u32, limit: u64) -> Result<bool> {
261 let Some(h) = self.header(p, limit)? else {
262 return Ok(false);
263 };
264 if h.id != id || h.overruns(limit) {
265 return Ok(false);
266 }
267 if h.size == Size::Unknown && id != CLUSTER {
268 return Ok(false);
269 }
270 let mut cp = h.data_start();
271 let end = h.end(limit);
272 for _ in 0..3 {
273 let Some(c) = self.header(cp, end)? else {
274 return Ok(false);
275 };
276 if c.overruns(end) {
277 return Ok(false);
278 }
279 if id != CLUSTER {
280 return Ok(true);
281 }
282 match c.id {
283 TIMESTAMP => return Ok(true),
284 CRC32 | VOID => cp = c.end(end),
285 _ => return Ok(false),
286 }
287 }
288 Ok(false)
289 }
290
291 pub(crate) fn unknown_end(&mut self, data_start: u64, limit: u64) -> Result<u64> {
292 let mut pos = data_start;
293 while pos < limit {
294 let Some(h) = self.header(pos, limit)? else {
295 return Ok(pos);
296 };
297 if is_top_level(h.id) || h.size == Size::Unknown {
298 return Ok(pos);
299 }
300 pos = h.end(limit);
301 }
302 Ok(limit)
303 }
304}
305
306#[derive(Clone, Copy, Debug)]
307pub(crate) struct BlockRef {
308 pub(crate) track: usize,
309 pub(crate) offset: u64,
310 pub(crate) ts: i64,
311 pub(crate) duration: Option<u64>,
312 pub(crate) keyframe: bool,
313}
314
315#[derive(Clone, Copy, Debug)]
316pub(crate) struct FrameRef {
317 pub(crate) track: usize,
318 pub(crate) offset: u64,
319 pub(crate) size: u32,
320 pub(crate) pts: i64,
321 pub(crate) duration: u32,
322 pub(crate) keyframe: bool,
323 pub(crate) block: u64,
324 pub(crate) lace: u16,
325}
326
327pub(crate) fn track_index(tracks: &[Track], number: u64) -> Option<usize> {
328 tracks.iter().position(|t| t.number == number)
329}
330
331fn parse_block<S: ByteSource + ?Sized>(
332 io: &mut Io<'_, S>,
333 data_start: u64,
334 len: u64,
335 tracks: &[Track],
336 frames: &mut Vec<(u64, u32)>,
337) -> Result<Option<(usize, i16, u8)>> {
338 frames.clear();
339 let head = io.peek(data_start, 16.min(len as usize))?;
340 let Some((number, tl, _)) = ebml::read_vint(head) else {
341 return Ok(None);
342 };
343 if head.len() < tl + 3 {
344 return Ok(None);
345 }
346 let rel = i16::from_be_bytes([head[tl], head[tl + 1]]);
347 let flags = head[tl + 2];
348 let Some(track) = track_index(tracks, number) else {
349 return Ok(None);
350 };
351 let hdr = (tl + 3) as u64;
352 let lacing = Lacing::from_flags(flags);
353 if lacing == Lacing::None {
354 frames.push((data_start + hdr, (len - hdr) as u32));
355 } else {
356 let body = io.read_vec(data_start + hdr, len - hdr)?;
357 match parse_lacing(lacing, &body) {
358 Ok(f) => frames.extend(
359 f.into_iter()
360 .map(|(o, l)| (data_start + hdr + o as u64, l as u32)),
361 ),
362 Err(_) => return Ok(None),
363 }
364 }
365 Ok(Some((track, rel, flags)))
366}
367
368pub(crate) fn parse_block_elem<S: ByteSource + ?Sized>(
369 io: &mut Io<'_, S>,
370 h: &Hdr,
371 end: u64,
372 cluster_ts: i64,
373 tracks: &[Track],
374 frames: &mut Vec<(u64, u32)>,
375) -> Result<Option<BlockRef>> {
376 let ds = h.data_start();
377 match h.id {
378 SIMPLE_BLOCK => {
379 let Some((track, rel, flags)) = parse_block(io, ds, end - ds, tracks, frames)? else {
380 return Ok(None);
381 };
382 Ok(Some(BlockRef {
383 track,
384 offset: h.pos,
385 ts: cluster_ts.saturating_add(rel as i64),
386 duration: None,
387 keyframe: flags & 0x80 != 0,
388 }))
389 }
390 BLOCK_GROUP => {
391 let mut pos = ds;
392 let mut block = None;
393 let (mut duration, mut has_ref) = (None, false);
394 while pos < end {
395 let Some(c) = io.header(pos, end)? else {
396 break;
397 };
398 let ce = c.end(end);
399 match c.id {
400 BLOCK => {
401 block =
402 parse_block(io, c.data_start(), ce - c.data_start(), tracks, frames)?
403 }
404 BLOCK_DURATION => duration = Some(io.uint(&c, ce)?),
405 REFERENCE_BLOCK => has_ref = true,
406 _ => {}
407 }
408 pos = ce;
409 }
410 let Some((track, rel, _)) = block else {
411 return Ok(None);
412 };
413 Ok(Some(BlockRef {
414 track,
415 offset: h.pos,
416 ts: cluster_ts.saturating_add(rel as i64),
417 duration,
418 keyframe: !has_ref,
419 }))
420 }
421 _ => Ok(None),
422 }
423}
424
425pub(crate) fn expand(
426 b: &BlockRef,
427 frames: &[(u64, u32)],
428 t: &Track,
429 scale: u64,
430 skip: u16,
431 mut out: impl FnMut(FrameRef),
432) {
433 let n = frames.len() as u64;
434 let block_ns: Option<u128> = match (b.duration, t.default_duration) {
435 (Some(d), _) => Some(d as u128 * scale as u128),
436 (None, Some(dd)) => Some(dd as u128 * n as u128),
437 _ => None,
438 };
439 let scale = scale as i128;
440 let ticks = |ns: i128| -> i64 {
441 (if ns >= 0 {
442 (ns + scale / 2) / scale
443 } else {
444 (ns - scale / 2) / scale
445 })
446 .clamp(i64::MIN as i128, i64::MAX as i128) as i64
447 };
448 let delay = ticks(t.codec_delay as i128);
449 let base = b.ts as i128 * scale;
450 for (i, &(offset, size)) in frames.iter().enumerate().skip(skip as usize) {
451 let (start, end) = match block_ns {
452 Some(total) => (
453 (total * i as u128 / n as u128) as i128,
454 Some((total * (i as u128 + 1) / n as u128) as i128),
455 ),
456 None => (0, None),
457 };
458 let duration = end.map_or(0, |e| {
459 ticks(e)
460 .saturating_sub(ticks(start))
461 .clamp(0, u32::MAX as i64)
462 });
463 out(FrameRef {
464 track: b.track,
465 offset,
466 size,
467 pts: ticks(base + start).saturating_sub(delay),
468 duration: duration as u32,
469 keyframe: b.keyframe,
470 block: b.offset,
471 lace: i as u16,
472 });
473 }
474}
475
476pub(crate) fn scan_cluster<S: ByteSource + ?Sized>(
477 io: &mut Io<'_, S>,
478 h: &Hdr,
479 limit: u64,
480 tracks: &[Track],
481 warnings: &mut Vec<String>,
482 frames: &mut Vec<(u64, u32)>,
483 mut each: impl FnMut(&BlockRef, &[(u64, u32)]),
484) -> Result<Cluster> {
485 let ds = h.data_start();
486 let known_end = match h.size {
487 Size::Known(_) => Some(h.end(limit)),
488 Size::Unknown => None,
489 };
490 let lim = known_end.unwrap_or(limit);
491 let mut ts: i64 = 0;
492 let mut pos = ds;
493 let end = loop {
494 if pos >= lim {
495 break lim;
496 }
497 let Some(c) = io.header(pos, lim)? else {
498 warn(warnings, || {
499 format!("damaged cluster at {}: undecodable element at {pos}", h.pos)
500 });
501 break io.resync(pos + 1, limit)?.unwrap_or(limit);
502 };
503 if known_end.is_none() && is_top_level(c.id) {
504 break pos;
505 }
506 if c.size == Size::Unknown || c.overruns(lim) {
507 warn(warnings, || {
508 format!(
509 "damaged cluster at {}: element 0x{:X} at {pos} overruns",
510 h.pos, c.id
511 )
512 });
513 if c.overruns(lim)
514 && matches!(c.id, SIMPLE_BLOCK | BLOCK_GROUP)
515 && io.len <= c.data_start() + c.size.known().unwrap_or(0)
516 {
517 break lim;
518 }
519 break io.resync(pos + 1, limit)?.unwrap_or(limit);
520 }
521 let ce = c.end(lim);
522 match c.id {
523 TIMESTAMP => ts = io.uint(&c, ce)? as i64,
524 SIMPLE_BLOCK | BLOCK_GROUP => {
525 if let Some(b) = parse_block_elem(io, &c, ce, ts, tracks, frames)? {
526 each(&b, frames);
527 }
528 }
529 _ => {}
530 }
531 pos = ce;
532 };
533 Ok(Cluster {
534 offset: h.pos,
535 data_start: ds,
536 end,
537 timestamp: ts.max(0) as u64,
538 })
539}
540
541pub struct Reader<S: ByteSource = File> {
542 source: S,
543 len: u64,
544 pub doc_type: String,
545 pub info: Info,
546 pub tracks: Vec<Track>,
547 pub cues: Vec<CuePoint>,
548 pub clusters: Vec<Cluster>,
549 pub warnings: Vec<String>,
550 pub indexed: bool,
551 pub(crate) segment_start: u64,
552 pub(crate) segment_end: u64,
553 pub(crate) first_cluster: Option<u64>,
554 full: OnceLock<std::result::Result<Box<Reader<S>>, Error>>,
555}
556
557struct Head {
558 doc_type: String,
559 info: Info,
560 tracks: Vec<Track>,
561 cues: Vec<CuePoint>,
562 clusters: Vec<Cluster>,
563 warnings: Vec<String>,
564 segment_start: u64,
565 segment_end: u64,
566 first_cluster: Option<u64>,
567 seek_head: Vec<SeekEntry>,
568}
569
570impl Reader<File> {
571 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
572 let path = path.as_ref();
573 File::open(path)
574 .map_err(Error::from)
575 .and_then(Self::new)
576 .map_err(|e| e.in_file(path))
577 }
578
579 pub fn open_with(path: impl AsRef<Path>, options: Options) -> Result<Self> {
580 let path = path.as_ref();
581 File::open(path)
582 .map_err(Error::from)
583 .and_then(|f| Self::with_options(f, options))
584 .map_err(|e| e.in_file(path))
585 }
586}
587
588impl<S: ByteSource> Reader<S> {
589 pub fn new(source: S) -> Result<Self> {
590 Self::with_options(source, Options::default())
591 }
592
593 pub fn with_options(source: S, options: Options) -> Result<Self> {
594 Self::open_inner(source, options, false)
595 }
596
597 fn open_inner(source: S, options: Options, video_only: bool) -> Result<Self> {
598 let mut cache = Cache::default();
599 let mut io = Io::new(&source, &mut cache);
600 let len = io.len;
601 let start = {
602 let b = io.peek(0, WIN)?;
603 b.windows(4)
604 .position(|w| w == [0x1A, 0x45, 0xDF, 0xA3])
605 .ok_or_else(|| Error::Invalid("not a Matroska file: no EBML header".into()))?
606 as u64
607 };
608 let eh = io
609 .header(start, len)?
610 .ok_or_else(|| Error::Invalid("not a Matroska file: bad EBML header".into()))?;
611 let hdr_end = eh.end(len);
612 let hdr = io.read_vec(eh.data_start(), hdr_end - eh.data_start())?;
613 let mut doc_type = "matroska".to_string();
614 for e in Children::new(&hdr) {
615 match e.id {
616 DOC_TYPE => doc_type = ebml::string(e.data),
617 EBML_MAX_ID_LENGTH if ebml::uint(e.data) > 4 => {
618 return Err(Error::Unsupported("EBMLMaxIDLength above 4".into()));
619 }
620 EBML_MAX_SIZE_LENGTH if ebml::uint(e.data) > 8 => {
621 return Err(Error::Unsupported("EBMLMaxSizeLength above 8".into()));
622 }
623 _ => {}
624 }
625 }
626 if doc_type != "matroska" && doc_type != "webm" {
627 return Err(Error::Invalid(format!(
628 "not a Matroska file: DocType {doc_type:?}"
629 )));
630 }
631 let mut pos = hdr_end;
632 let seg = loop {
633 let h = io
634 .header(pos, len)?
635 .ok_or_else(|| Error::Invalid("no Matroska Segment".into()))?;
636 if h.id == SEGMENT {
637 break h;
638 }
639 if h.size == Size::Unknown {
640 return Err(Error::Invalid(
641 "unknown-size element before the Segment".into(),
642 ));
643 }
644 pos = h.end(len);
645 };
646 let mut head = Head {
647 doc_type,
648 info: Info::default(),
649 tracks: Vec::new(),
650 cues: Vec::new(),
651 clusters: Vec::new(),
652 warnings: Vec::new(),
653 segment_start: seg.data_start(),
654 segment_end: seg.end(len),
655 first_cluster: None,
656 seek_head: Vec::new(),
657 };
658 if seg.overruns(len) {
659 head.warnings
660 .push("Segment extends past the end of the file".into());
661 }
662 let mut w = Walker {
663 index: options.index,
664 video_only,
665 parsed: HashSet::new(),
666 frames: Vec::new(),
667 room: len / 2,
668 };
669 w.walk(&mut io, &mut head)?;
670 for t in &mut head.tracks {
671 fill_durations(t);
672 t.finish_indexes();
673 }
674 Ok(Reader {
675 source,
676 len,
677 doc_type: head.doc_type,
678 info: head.info,
679 tracks: head.tracks,
680 cues: head.cues,
681 clusters: head.clusters,
682 warnings: head.warnings,
683 indexed: options.index,
684 segment_start: head.segment_start,
685 segment_end: head.segment_end,
686 first_cluster: head.first_cluster,
687 full: OnceLock::new(),
688 })
689 }
690
691 pub fn source(&self) -> &S {
692 &self.source
693 }
694
695 pub fn is_webm(&self) -> bool {
696 self.doc_type == "webm"
697 }
698
699 pub fn duration_seconds(&self) -> f64 {
700 if let Some(d) = self.info.duration.filter(|&d| d > 0.0) {
701 return d * self.info.timestamp_scale as f64 / 1e9;
702 }
703 self.tracks
704 .iter()
705 .map(Track::duration_seconds)
706 .fold(0.0, f64::max)
707 }
708
709 pub fn video(&self) -> Option<usize> {
710 let mut it = self
711 .tracks
712 .iter()
713 .enumerate()
714 .filter(|(_, t)| t.kind == TrackKind::Video);
715 let first = it.clone().next().map(|x| x.0);
716 it.find(|(_, t)| t.default).map(|x| x.0).or(first)
717 }
718
719 pub fn audio(&self) -> Vec<usize> {
720 self.tracks
721 .iter()
722 .enumerate()
723 .filter(|(_, t)| t.kind == TrackKind::Audio)
724 .map(|(i, _)| i)
725 .collect()
726 }
727
728 pub fn read(&self, track: usize, index: usize) -> Result<Vec<u8>> {
729 let mut data = Vec::new();
730 self.read_into(track, index, &mut data)?;
731 Ok(data)
732 }
733
734 pub fn read_into(&self, track: usize, index: usize, data: &mut Vec<u8>) -> Result<()> {
735 let t = self
736 .tracks
737 .get(track)
738 .ok_or_else(|| Error::Invalid(format!("no track {track}")))?;
739 let s = t
740 .samples
741 .get(index)
742 .ok_or_else(|| Error::Invalid(format!("no sample {index} in track {track}")))?;
743 read_frame(&self.source, self.len, t, s.offset, s.size, data)
744 }
745
746 pub fn packet(&self, track: usize, index: usize) -> Result<Packet> {
747 let data = self.read(track, index)?;
748 let s = self
749 .tracks
750 .get(track)
751 .and_then(|t| t.samples.get(index))
752 .copied()
753 .ok_or_else(|| Error::Invalid(format!("no sample {index} in track {track}")))?;
754 Ok(Packet {
755 data,
756 pts: s.pts,
757 dts: s.pts,
758 keyframe: s.keyframe,
759 })
760 }
761
762 pub(crate) fn len(&self) -> u64 {
763 self.len
764 }
765}
766
767impl Reader<File> {
768 #[cfg_attr(
769 not(all(
770 feature = "h264",
771 feature = "hevc",
772 feature = "aac",
773 feature = "gpu",
774 feature = "filter",
775 feature = "vp9"
776 )),
777 allow(dead_code)
778 )]
779 pub(crate) fn full(&self) -> Result<&Reader<File>> {
780 if self.indexed {
781 return Ok(self);
782 }
783 let built = self.full.get_or_init(|| {
784 self.source
785 .try_clone()
786 .map_err(Error::from)
787 .and_then(|f| Self::open_inner(f, Options::default(), true))
788 .map(Box::new)
789 });
790 match built {
791 Ok(r) => Ok(r),
792 Err(e) => Err(again(e)),
793 }
794 }
795}
796
797fn again(e: &Error) -> Error {
798 match e {
799 Error::Unsupported(s) => Error::Unsupported(s.clone()),
800 Error::Invalid(s) => Error::Invalid(s.clone()),
801 Error::Backend(s) => Error::Backend(s.clone()),
802 Error::Io(e) => Error::Io(std::io::Error::new(e.kind(), e.to_string())),
803 Error::Cancelled => Error::Cancelled,
804 }
805}
806
807pub(crate) fn read_frame<S: ByteSource + ?Sized>(
808 src: &S,
809 len: u64,
810 t: &Track,
811 offset: u64,
812 size: u32,
813 data: &mut Vec<u8>,
814) -> Result<()> {
815 if !t.frames_readable() {
816 return Err(Error::Unsupported(format!(
817 "compressed or encrypted frames in track {}",
818 t.number
819 )));
820 }
821 if offset.checked_add(size as u64).is_none_or(|e| e > len) {
822 return Err(eof());
823 }
824 let prefix = t.stripped_header().unwrap_or(&[]);
825 data.clear();
826 data.reserve(prefix.len() + size as usize);
827 data.extend_from_slice(prefix);
828 data.resize(prefix.len() + size as usize, 0);
829 src.read_at(offset, &mut data[prefix.len()..])?;
830 Ok(())
831}
832
833pub(crate) fn read_frame_io<S: ByteSource + ?Sized>(
834 io: &mut Io<'_, S>,
835 t: &Track,
836 offset: u64,
837 size: u32,
838 data: &mut Vec<u8>,
839) -> Result<()> {
840 if !t.frames_readable() {
841 return Err(Error::Unsupported(format!(
842 "compressed or encrypted frames in track {}",
843 t.number
844 )));
845 }
846 data.clear();
847 data.extend_from_slice(t.stripped_header().unwrap_or(&[]));
848 io.read_into(offset, size as u64, data)
849}
850
851fn fill_durations(t: &mut Track) {
852 if t.samples.iter().all(|s| s.duration > 0) {
853 return;
854 }
855 let mut order: Vec<usize> = (0..t.samples.len()).collect();
856 order.sort_by_key(|&i| (t.samples[i].pts, i));
857 let mut last = 0u32;
858 for w in 0..order.len() {
859 let i = order[w];
860 if t.samples[i].duration > 0 {
861 last = t.samples[i].duration;
862 continue;
863 }
864 let d = match order.get(w + 1) {
865 Some(&j) => t.samples[j]
866 .pts
867 .saturating_sub(t.samples[i].pts)
868 .clamp(0, u32::MAX as i64) as u32,
869 None => last,
870 };
871 t.samples[i].duration = d;
872 if d > 0 {
873 last = d;
874 }
875 }
876}
877
878struct Walker {
879 index: bool,
880 video_only: bool,
881 parsed: HashSet<u64>,
882 frames: Vec<(u64, u32)>,
883 room: u64,
884}
885
886impl Walker {
887 fn walk<S: ByteSource + ?Sized>(&mut self, io: &mut Io<'_, S>, r: &mut Head) -> Result<()> {
888 let seg_end = r.segment_end;
889 let mut pos = r.segment_start;
890 while pos < seg_end {
891 let Some(h) = io.header(pos, seg_end)? else {
892 match io.resync(pos + 1, seg_end)? {
893 Some(p) => {
894 warn(&mut r.warnings, || {
895 format!("undecodable element at {pos}; resynced at {p}")
896 });
897 pos = p;
898 continue;
899 }
900 None => break,
901 }
902 };
903 match h.id {
904 CLUSTER => {
905 if r.first_cluster.is_none() {
906 r.first_cluster = Some(h.pos);
907 self.follow_seek_head(io, r, &[INFO, TRACKS])?;
908 if !self.index {
909 break;
910 }
911 }
912 let cluster = r.clusters.len() as u32;
913 let scale = r.info.timestamp_scale;
914 let mut samples: Vec<Vec<Sample>> = r
915 .tracks
916 .iter_mut()
917 .map(|t| std::mem::take(&mut t.samples))
918 .collect();
919 let tracks = &r.tracks;
920 let video_only = self.video_only;
921 let room = &mut self.room;
922 let mut full = false;
923 let ci = scan_cluster(
924 io,
925 &h,
926 seg_end,
927 tracks,
928 &mut r.warnings,
929 &mut self.frames,
930 |b, frames| {
931 if full || video_only && tracks[b.track].kind != TrackKind::Video {
932 return;
933 }
934 expand(b, frames, &tracks[b.track], scale, 0, |f| {
935 let Some(list) = samples.get_mut(f.track) else {
936 return;
937 };
938 if *room == 0 || list.len() >= MAX_SAMPLES {
939 full = true;
940 return;
941 }
942 *room -= 1;
943 list.push(Sample {
944 offset: f.offset,
945 size: f.size,
946 pts: f.pts,
947 duration: f.duration,
948 keyframe: f.keyframe,
949 cluster,
950 block: f.block,
951 lace: f.lace,
952 })
953 })
954 },
955 )?;
956 for (t, s) in r.tracks.iter_mut().zip(samples) {
957 t.samples = s;
958 }
959 if full {
960 return Err(Error::Invalid(
961 "more frames than a Matroska file of this size can index".into(),
962 ));
963 }
964 r.clusters.push(ci);
965 pos = ci.end.max(h.data_start());
966 }
967 SEEK_HEAD | INFO | TRACKS | CUES | CHAPTERS | TAGS | ATTACHMENTS => {
968 pos = self.level1(io, r, &h, seg_end)?;
969 }
970 EBML | SEGMENT => break,
971 _ => {
972 if h.size == Size::Unknown {
973 pos = io.unknown_end(h.data_start(), seg_end)?;
974 } else if h.overruns(seg_end) && h.id != VOID {
975 match io.resync(pos + 1, seg_end)? {
976 Some(p) => {
977 warn(&mut r.warnings, || {
978 format!(
979 "oversized element 0x{:X} at {pos}; resynced at {p}",
980 h.id
981 )
982 });
983 pos = p;
984 }
985 None => break,
986 }
987 } else {
988 pos = h.end(seg_end);
989 }
990 }
991 }
992 }
993 self.follow_seek_head(io, r, &[INFO, TRACKS, CUES, SEEK_HEAD])?;
994 self.follow_seek_head(io, r, &[CUES])?;
995 r.cues.sort_by_key(|c| c.time);
996 Ok(())
997 }
998
999 fn follow_seek_head<S: ByteSource + ?Sized>(
1000 &mut self,
1001 io: &mut Io<'_, S>,
1002 r: &mut Head,
1003 ids: &[u32],
1004 ) -> Result<()> {
1005 let entries = r.seek_head.clone();
1006 for e in entries {
1007 if !ids.contains(&e.id) {
1008 continue;
1009 }
1010 let Some(abs) = r.segment_start.checked_add(e.position) else {
1011 continue;
1012 };
1013 if self.parsed.contains(&abs) || abs >= r.segment_end {
1014 continue;
1015 }
1016 match io.header(abs, r.segment_end)? {
1017 Some(h) if h.id == e.id => {
1018 self.level1(io, r, &h, r.segment_end)?;
1019 }
1020 _ => warn(&mut r.warnings, || {
1021 format!(
1022 "SeekHead entry 0x{:X} -> {abs} does not point at that element",
1023 e.id
1024 )
1025 }),
1026 }
1027 }
1028 Ok(())
1029 }
1030
1031 fn level1<S: ByteSource + ?Sized>(
1032 &mut self,
1033 io: &mut Io<'_, S>,
1034 r: &mut Head,
1035 h: &Hdr,
1036 limit: u64,
1037 ) -> Result<u64> {
1038 let end = if h.size == Size::Unknown {
1039 io.unknown_end(h.data_start(), limit)?
1040 } else {
1041 h.end(limit)
1042 };
1043 if h.overruns(limit) {
1044 warn(&mut r.warnings, || {
1045 format!("element 0x{:X} at {} truncated", h.id, h.pos)
1046 });
1047 }
1048 if !self.parsed.insert(h.pos) || !matches!(h.id, SEEK_HEAD | INFO | TRACKS | CUES) {
1049 return Ok(end);
1050 }
1051 let n = end - h.data_start();
1052 if n > MAX_META {
1053 warn(&mut r.warnings, || {
1054 format!(
1055 "element 0x{:X} at {} too large ({n} bytes); skipped",
1056 h.id, h.pos
1057 )
1058 });
1059 return Ok(end);
1060 }
1061 let data = io.read_vec(h.data_start(), n)?;
1062 match h.id {
1063 SEEK_HEAD => r.seek_head.extend(parse::seek_head(&data)),
1064 INFO => r.info = parse::info(&data),
1065 TRACKS if r.tracks.is_empty() => {
1066 r.tracks = parse::tracks(&data, r.info.timestamp_scale);
1067 }
1068 CUES => parse::cues(&data, &mut r.cues),
1069 _ => {}
1070 }
1071 Ok(end)
1072 }
1073}