1mod idat;
2mod iinf;
3mod iloc;
4mod ilst;
5mod iprp;
6mod iref;
7mod pitm;
8mod properties;
9
10pub use idat::*;
11pub use iinf::*;
12pub use iloc::*;
13pub use ilst::*;
14pub use iprp::*;
15pub use iref::*;
16pub use pitm::*;
17pub use properties::*;
18
19use crate::*;
20
21#[derive(Debug, Clone, PartialEq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Meta {
29 pub hdlr: Hdlr,
30 pub items: Vec<Any>,
31}
32
33impl Eq for Meta {}
34
35macro_rules! meta_atom {
36 ($($atom:ident),*,) => {
37 pub trait MetaAtom: AnyAtom {}
39
40 $(impl MetaAtom for $atom {})*
41 };
42}
43
44meta_atom! {
45 Pitm,
46 Dinf,
47 Iloc,
48 Iinf,
49 Iprp,
50 Iref,
51 Idat,
52 Ilst,
53}
54
55impl Meta {
57 pub fn get<T: MetaAtom>(&self) -> Option<&T> {
59 self.items.iter().find_map(T::from_any_ref)
60 }
61
62 pub fn get_mut<T: MetaAtom>(&mut self) -> Option<&mut T> {
64 self.items.iter_mut().find_map(T::from_any_mut)
65 }
66
67 pub fn push<T: MetaAtom>(&mut self, atom: T) {
69 self.items.push(atom.into_any());
70 }
71
72 pub fn remove<T: MetaAtom>(&mut self) -> Option<T> {
76 let pos = self.items.iter().position(|a| T::from_any_ref(a).is_some());
77 if let Some(pos) = pos {
78 Some(T::from_any(self.items.remove(pos)).unwrap())
79 } else {
80 None
81 }
82 }
83}
84
85impl Atom for Meta {
86 const KIND: FourCC = FourCC::new(b"meta");
87 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
88 if buf.remaining() < 8 {
93 return Err(Error::OutOfBounds);
94 }
95
96 if buf.slice(8)[4..8] == *b"hdlr".as_ref() {
97 tracing::trace!("meta box without fullbox header");
99 } else {
100 let _version_and_flags = u32::decode(buf)?; }
103
104 let hdlr = Hdlr::decode(buf)?;
105 let mut items = Vec::new();
106 while let Some(atom) = Any::decode_maybe(buf)? {
107 items.push(atom);
108 }
109
110 Ok(Self { hdlr, items })
111 }
112
113 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
114 0u32.encode(buf)?; self.hdlr.encode(buf)?;
116 for atom in &self.items {
117 atom.encode(buf)?;
118 }
119 Ok(())
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_meta_empty() {
129 let expected = Meta {
130 hdlr: Hdlr {
131 handler: b"fake".into(),
132 name: "".into(),
133 },
134 items: Vec::new(),
135 };
136 let mut buf = Vec::new();
137 expected.encode(&mut buf).unwrap();
138
139 let mut buf = buf.as_ref();
140 let output = Meta::decode(&mut buf).unwrap();
141 assert_eq!(output, expected);
142 }
143
144 #[test]
145 fn test_meta_mdir() {
146 let mut expected = Meta {
147 hdlr: Hdlr {
148 handler: b"mdir".into(),
149 name: "".into(),
150 },
151 items: Vec::new(),
152 };
153
154 expected.push(Pitm { item_id: 3 });
155 expected.push(Dinf {
156 dref: Dref {
157 urls: vec![Url {
158 location: "".into(),
159 }],
160 },
161 });
162 expected.push(Iloc {
163 item_locations: vec![ItemLocation {
164 item_id: 3,
165 construction_method: 0,
166 data_reference_index: 0,
167 base_offset: 0,
168 extents: vec![ItemLocationExtent {
169 item_reference_index: 0,
170 offset: 200,
171 length: 100,
172 }],
173 }],
174 });
175 expected.push(Iinf { item_infos: vec![] });
176 expected.push(Iprp {
177 ipco: Ipco { properties: vec![] },
178 ipma: vec![Ipma {
179 item_properties: vec![
180 PropertyAssociations {
181 item_id: 1,
182 associations: vec![
183 PropertyAssociation {
184 essential: true,
185 property_index: 1,
186 },
187 PropertyAssociation {
188 essential: false,
189 property_index: 2,
190 },
191 PropertyAssociation {
192 essential: false,
193 property_index: 3,
194 },
195 PropertyAssociation {
196 essential: false,
197 property_index: 5,
198 },
199 PropertyAssociation {
200 essential: true,
201 property_index: 4,
202 },
203 ],
204 },
205 PropertyAssociations {
206 item_id: 2,
207 associations: vec![
208 PropertyAssociation {
209 essential: true,
210 property_index: 6,
211 },
212 PropertyAssociation {
213 essential: false,
214 property_index: 3,
215 },
216 PropertyAssociation {
217 essential: false,
218 property_index: 7,
219 },
220 PropertyAssociation {
221 essential: true,
222 property_index: 8,
223 },
224 PropertyAssociation {
225 essential: true,
226 property_index: 4,
227 },
228 ],
229 },
230 ],
231 }],
232 });
233 expected.push(Iref {
234 references: vec![Reference {
235 reference_type: b"cdsc".into(),
236 from_item_id: 2,
237 to_item_ids: vec![1, 3],
238 }],
239 });
240 expected.push(Idat {
241 data: vec![0x01, 0xFF, 0xFE, 0x03],
242 });
243 expected.push(Ilst::default());
244
245 let mut buf = Vec::new();
246 expected.encode(&mut buf).unwrap();
247
248 let mut buf = buf.as_ref();
249 let output = Meta::decode(&mut buf).unwrap();
250 assert_eq!(output, expected);
251 }
252
253 #[test]
254 fn test_meta_apple_quicktime() {
255 let mut buf = Vec::new();
261
262 buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); buf.extend_from_slice(b"meta");
265
266 let hdlr = Hdlr {
268 handler: b"mdir".into(),
269 name: "Apple".into(),
270 };
271
272 let mut hdlr_buf = Vec::new();
274 hdlr.encode(&mut hdlr_buf).unwrap();
275 buf.extend_from_slice(&hdlr_buf);
276
277 let ilst = Ilst::default();
279 let mut ilst_buf = Vec::new();
280 ilst.encode(&mut ilst_buf).unwrap();
281 buf.extend_from_slice(&ilst_buf);
282
283 let size = buf.len() as u32;
285 buf[0..4].copy_from_slice(&size.to_be_bytes());
286
287 let mut cursor = std::io::Cursor::new(&buf[8..]);
289
290 let decoded = Meta::decode_body(&mut cursor).expect("failed to decode Apple meta box");
292
293 assert_eq!(decoded.hdlr.handler, FourCC::new(b"mdir"));
295 assert_eq!(decoded.hdlr.name, "Apple");
296 assert_eq!(decoded.items.len(), 1);
297 assert!(decoded.get::<Ilst>().is_some());
298 }
299
300 #[test]
301 fn test_meta_apple_with_ilst() {
302 let mut buf = Vec::new();
304
305 buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); buf.extend_from_slice(b"meta");
308
309 let hdlr = Hdlr {
311 handler: b"mdir".into(),
312 name: "".into(),
313 };
314 let mut hdlr_buf = Vec::new();
315 hdlr.encode(&mut hdlr_buf).unwrap();
316 buf.extend_from_slice(&hdlr_buf);
317
318 let ilst = Ilst {
320 name: Some(Name("Test Song".into())),
321 year: Some(Year("2025".into())),
322 ..Default::default()
323 };
324
325 let mut ilst_buf = Vec::new();
326 ilst.encode(&mut ilst_buf).unwrap();
327 buf.extend_from_slice(&ilst_buf);
328
329 let size = buf.len() as u32;
331 buf[0..4].copy_from_slice(&size.to_be_bytes());
332
333 let mut cursor = std::io::Cursor::new(&buf[8..]);
335 let decoded =
336 Meta::decode_body(&mut cursor).expect("failed to decode Apple meta with ilst");
337
338 assert_eq!(decoded.hdlr.handler, FourCC::new(b"mdir"));
340 let decoded_ilst = decoded.get::<Ilst>().expect("ilst not found");
341 assert_eq!(decoded_ilst.name.as_ref().unwrap().0, "Test Song");
342 assert_eq!(decoded_ilst.year.as_ref().unwrap().0, "2025");
343 }
344
345 #[test]
346 fn test_meta_iso_vs_apple_roundtrip() {
347 let meta = Meta {
351 hdlr: Hdlr {
352 handler: b"mdir".into(),
353 name: "Handler".into(),
354 },
355 items: vec![],
356 };
357
358 let mut encoded = Vec::new();
360 meta.encode(&mut encoded).unwrap();
361
362 let mut cursor = std::io::Cursor::new(&encoded);
364 let decoded = Meta::decode(&mut cursor).expect("failed to decode ISO format");
365 assert_eq!(decoded, meta);
366
367 let mut apple_format = Vec::new();
369 apple_format.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); apple_format.extend_from_slice(b"meta");
371
372 let mut hdlr_buf = Vec::new();
373 meta.hdlr.encode(&mut hdlr_buf).unwrap();
374 apple_format.extend_from_slice(&hdlr_buf);
375
376 let size = apple_format.len() as u32;
377 apple_format[0..4].copy_from_slice(&size.to_be_bytes());
378
379 let mut cursor = std::io::Cursor::new(&apple_format);
381 let decoded_apple = Meta::decode(&mut cursor).expect("failed to decode Apple format");
382 assert_eq!(decoded_apple, meta);
383 }
384}