Skip to main content

pito_codecs/mkv/
reader.rs

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}