mp4_edit/atom/leaf/
tref.rs1use std::fmt;
2
3use crate::{atom::FourCC, parser::ParseAtomData, writer::SerializeAtom, ParseError};
4
5pub const TREF: FourCC = FourCC::new(b"tref");
6
7#[derive(Debug, Clone)]
9pub struct TrackReference {
10 pub reference_type: FourCC,
12 pub track_ids: Vec<u32>,
14}
15
16impl TrackReference {
17 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#[derive(Default, Debug, Clone)]
41pub struct TrackReferenceAtom {
42 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]
163 fn test_tref_roundtrip() {
164 test_atom_roundtrip::<TrackReferenceAtom>(TREF);
165 }
166}