Skip to main content

mp4_edit/atom/leaf/
tref.rs

1use std::fmt;
2
3use crate::{atom::FourCC, parser::ParseAtomData, writer::SerializeAtom, ParseError};
4
5pub const TREF: FourCC = FourCC::new(b"tref");
6
7/// A single track reference entry containing the reference type and target track IDs
8#[derive(Debug, Clone)]
9pub struct TrackReference {
10    /// The type of reference (e.g., "hint", "chap", "subt")
11    pub reference_type: FourCC,
12    /// List of track IDs that this reference points to
13    pub track_ids: Vec<u32>,
14}
15
16impl TrackReference {
17    /// Check if this reference is of a specific type
18    pub fn is_type(&self, ref_type: &[u8; 4]) -> bool {
19        self.reference_type == ref_type
20    }
21}
22
23impl fmt::Display for TrackReference {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        write!(
26            f,
27            "{} -> [{}]",
28            self.reference_type,
29            self.track_ids
30                .iter()
31                .map(ToString::to_string)
32                .collect::<Vec<_>>()
33                .join(", ")
34        )
35    }
36}
37
38/// Track Reference Atom (tref) - ISO/IEC 14496-12
39/// Contains references from this track to other tracks
40#[derive(Default, Debug, Clone)]
41pub struct TrackReferenceAtom {
42    /// List of track references
43    pub references: Vec<TrackReference>,
44}
45
46impl TrackReferenceAtom {
47    pub fn new(references: impl Into<Vec<TrackReference>>) -> Self {
48        Self {
49            references: references.into(),
50        }
51    }
52
53    pub fn replace_references(&mut self, references: impl Into<Vec<TrackReference>>) -> &mut Self {
54        self.references = references.into();
55        self
56    }
57}
58
59impl fmt::Display for TrackReferenceAtom {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        if self.references.is_empty() {
62            write!(f, "TrackReferenceAtom {{ no references }}")
63        } else {
64            write!(f, "TrackReferenceAtom {{")?;
65            for (i, reference) in self.references.iter().enumerate() {
66                if i > 0 {
67                    write!(f, ", ")?;
68                }
69                write!(f, " {reference}")?;
70            }
71            write!(f, " }}")
72        }
73    }
74}
75
76impl ParseAtomData for TrackReferenceAtom {
77    fn parse_atom_data(atom_type: FourCC, input: &[u8]) -> Result<Self, ParseError> {
78        crate::atom::util::parser::assert_atom_type!(atom_type, TREF);
79        use crate::atom::util::parser::stream;
80        use winnow::Parser;
81        Ok(parser::parse_tref_data.parse(stream(input))?)
82    }
83}
84
85impl SerializeAtom for TrackReferenceAtom {
86    fn atom_type(&self) -> FourCC {
87        TREF
88    }
89
90    fn into_body_bytes(self) -> Vec<u8> {
91        serializer::serialize_tref_atom(self)
92    }
93}
94
95mod serializer {
96    use crate::atom::util::serializer::{prepend_size_inclusive, SizeU32};
97
98    use super::TrackReferenceAtom;
99
100    pub fn serialize_tref_atom(tref: TrackReferenceAtom) -> Vec<u8> {
101        tref.references
102            .into_iter()
103            .flat_map(|reference| {
104                prepend_size_inclusive::<SizeU32, _>(move || {
105                    let mut data = Vec::new();
106
107                    data.extend(reference.reference_type.into_bytes());
108                    for track_id in reference.track_ids {
109                        data.extend(track_id.to_be_bytes());
110                    }
111
112                    data
113                })
114            })
115            .collect()
116    }
117}
118
119mod parser {
120    use winnow::{
121        binary::be_u32,
122        combinator::{repeat, seq, trace},
123        error::StrContext,
124        ModalResult, Parser,
125    };
126
127    use super::{TrackReference, TrackReferenceAtom};
128    use crate::atom::util::parser::{combinators::inclusive_length_and_then, fourcc, Stream};
129
130    pub fn parse_tref_data(input: &mut Stream<'_>) -> ModalResult<TrackReferenceAtom> {
131        trace(
132            "tref",
133            seq!(TrackReferenceAtom {
134                references: repeat(0.., inclusive_length_and_then(be_u32, reference))
135                    .context(StrContext::Label("references")),
136            })
137            .context(StrContext::Label("tref")),
138        )
139        .parse_next(input)
140    }
141
142    fn reference(input: &mut Stream<'_>) -> ModalResult<TrackReference> {
143        trace(
144            "reference",
145            seq!(TrackReference {
146                reference_type: fourcc.context(StrContext::Label("reference_type")),
147                track_ids: repeat(0.., be_u32.context(StrContext::Label("track_id")))
148                    .context(StrContext::Label("track_ids")),
149            })
150            .context(StrContext::Label("reference")),
151        )
152        .parse_next(input)
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use crate::atom::test_utils::test_atom_roundtrip;
160
161    /// Test round-trip for all available tref test data files
162    #[test]
163    fn test_tref_roundtrip() {
164        test_atom_roundtrip::<TrackReferenceAtom>(TREF);
165    }
166}