isobmff/boxes/
metadata.rs

1use std::fmt::Debug;
2use std::io;
3
4use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, U24Be};
5use scuffle_bytes_util::{BitWriter, BytesCow, IoResultExt};
6
7use super::{
8    Brand, DataInformationBox, ExtendedTypeBox, FDItemInformationBox, GroupsListBox, HandlerBox, ProtectionSchemeInfoBox,
9    ScrambleSchemeInfoBox,
10};
11use crate::{BoxHeader, FullBoxHeader, IsoBox, IsoSized, UnknownBox, Utf8String};
12
13/// Meta box
14///
15/// ISO/IEC 14496-12 - 8.11.1
16#[derive(IsoBox, Debug, PartialEq, Eq)]
17#[iso_box(box_type = b"meta", crate_path = crate)]
18pub struct MetaBox<'a> {
19    /// The full box header.
20    pub full_header: FullBoxHeader,
21    /// The contained [`HandlerBox`]. (mandatory)
22    #[iso_box(nested_box)]
23    pub hdlr: HandlerBox,
24    /// The contained [`DataInformationBox`]. (optional)
25    #[iso_box(nested_box(collect))]
26    pub dinf: Option<DataInformationBox<'a>>,
27    /// The contained [`ItemLocationBox`]. (optional)
28    #[iso_box(nested_box(collect))]
29    pub iloc: Option<ItemLocationBox>,
30    /// The contained [`ItemProtectionBox`]. (optional)
31    #[iso_box(nested_box(collect))]
32    pub ipro: Option<ItemProtectionBox<'a>>,
33    /// The contained [`ItemInfoBox`]. (optional)
34    #[iso_box(nested_box(collect))]
35    pub iinf: Option<ItemInfoBox<'a>>,
36    /// The contained [`XmlBox`]. (optional)
37    #[iso_box(nested_box(collect))]
38    pub xml: Option<XmlBox>,
39    /// The contained [`BinaryXmlBox`]. (optional)
40    #[iso_box(nested_box(collect))]
41    pub bxml: Option<BinaryXmlBox<'a>>,
42    /// The contained [`PrimaryItemBox`]. (optional)
43    #[iso_box(nested_box(collect))]
44    pub pitm: Option<PrimaryItemBox>,
45    /// The contained [`FDItemInformationBox`]. (optional)
46    #[iso_box(nested_box(collect))]
47    pub fiin: Option<FDItemInformationBox>,
48    /// The contained [`ItemDataBox`]. (optional)
49    #[iso_box(nested_box(collect))]
50    pub idat: Option<ItemDataBox<'a>>,
51    /// The contained [`ItemReferenceBox`]. (optional)
52    #[iso_box(nested_box(collect))]
53    pub iref: Option<ItemReferenceBox>,
54    /// The contained [`ItemPropertiesBox`]. (optional)
55    #[iso_box(nested_box(collect))]
56    pub iprp: Option<ItemPropertiesBox<'a>>,
57    /// The contained [`GroupsListBox`]. (optional)
58    #[iso_box(nested_box(collect))]
59    pub grpl: Option<GroupsListBox<'a>>,
60    /// A list of unknown boxes that were not recognized during deserialization.
61    #[iso_box(nested_box(collect_unknown))]
62    pub unknown_boxes: Vec<UnknownBox<'a>>,
63}
64
65/// XML box
66///
67/// ISO/IEC 14496-12 - 8.11.2
68#[derive(IsoBox, Debug, PartialEq, Eq)]
69#[iso_box(box_type = b"xml ", crate_path = crate)]
70pub struct XmlBox {
71    /// The full box header.
72    pub full_header: FullBoxHeader,
73    /// A string containing the XML data.
74    pub xml: Utf8String,
75}
76
77/// Binary XML box
78///
79/// ISO/IEC 14496-12 - 8.11.2
80#[derive(IsoBox, Debug, PartialEq, Eq)]
81#[iso_box(box_type = b"bxml", crate_path = crate)]
82pub struct BinaryXmlBox<'a> {
83    /// The full box header.
84    pub full_header: FullBoxHeader,
85    /// Contains the encoded XML data.
86    pub data: BytesCow<'a>,
87}
88
89/// Item location box
90///
91/// ISO/IEC 14496-12 - 8.11.3
92#[derive(IsoBox, Debug, PartialEq, Eq)]
93#[iso_box(box_type = b"iloc", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
94pub struct ItemLocationBox {
95    /// The full box header.
96    pub full_header: FullBoxHeader,
97    /// Taken from the set {0, 4, 8} and indicates the length in bytes of the `offset` field.
98    pub offset_size: u8,
99    /// Taken from the set {0, 4, 8} and indicates the length in bytes of the `length` field.
100    pub length_size: u8,
101    /// Taken from the set {0, 4, 8} and indicates the length in bytes of the `base_offset` field.
102    pub base_offset_size: u8,
103    /// Taken from the set {0, 4, 8} and indicates the length in bytes of the `item_reference_index` field.
104    ///
105    /// If version is not 1 or 2, this field is reserved and does not represent the `index_size`.
106    pub index_size: u8,
107    /// Counts the number of resources in the [`items`](Self::items) array.
108    pub item_count: Option<u32>,
109    /// The items contained in this box.
110    pub items: Vec<ItemLocationBoxItem>,
111}
112
113impl<'a> DeserializeSeed<'a, BoxHeader> for ItemLocationBox {
114    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
115    where
116        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
117    {
118        let full_header = FullBoxHeader::deserialize(&mut reader)?;
119
120        let byte = u8::deserialize(&mut reader)?;
121        let offset_size = byte >> 4;
122
123        if ![0, 4, 8].contains(&offset_size) {
124            return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid offset_size"));
125        }
126
127        let length_size = byte & 0x0F;
128
129        if ![0, 4, 8].contains(&length_size) {
130            return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid length_size"));
131        }
132
133        let byte = u8::deserialize(&mut reader)?;
134        let base_offset_size = byte >> 4;
135
136        if ![0, 4, 8].contains(&base_offset_size) {
137            return Err(std::io::Error::new(
138                std::io::ErrorKind::InvalidData,
139                "Invalid base_offset_size",
140            ));
141        }
142
143        let index_size = byte & 0x0F;
144        if (full_header.version == 1 || full_header.version == 2) && ![0, 4, 8].contains(&index_size) {
145            return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid index_size"));
146        }
147
148        let item_count = if full_header.version < 2 {
149            Some(u16::deserialize(&mut reader)? as u32)
150        } else if full_header.version == 2 {
151            Some(u32::deserialize(&mut reader)?)
152        } else {
153            None
154        };
155
156        let mut items = Vec::with_capacity(item_count.unwrap_or(0) as usize);
157        for _ in 0..item_count.unwrap_or(0) {
158            let item_id = if full_header.version < 2 {
159                Some(u16::deserialize(&mut reader)? as u32)
160            } else if full_header.version == 2 {
161                Some(u32::deserialize(&mut reader)?)
162            } else {
163                None
164            };
165
166            let construction_method = if full_header.version == 1 || full_header.version == 2 {
167                let value = u16::deserialize(&mut reader)?;
168                Some((value & 0b1111) as u8)
169            } else {
170                None
171            };
172
173            let data_reference_index = u16::deserialize(&mut reader)?;
174            let base_offset = reader.try_read(base_offset_size as usize)?.pad_to_u64_be();
175            let extent_count = u16::deserialize(&mut reader)?;
176            let mut extents = Vec::with_capacity(extent_count as usize);
177            for _ in 0..extent_count {
178                let item_reference_index = if (full_header.version == 1 || full_header.version == 2) && index_size > 0 {
179                    Some(reader.try_read(index_size as usize)?.pad_to_u64_be())
180                } else {
181                    None
182                };
183                let extent_offset = reader.try_read(offset_size as usize)?.pad_to_u64_be();
184                let extent_length = reader.try_read(length_size as usize)?.pad_to_u64_be();
185
186                extents.push(ItemLocationBoxExtent {
187                    item_reference_index,
188                    extent_offset,
189                    extent_length,
190                });
191            }
192
193            items.push(ItemLocationBoxItem {
194                item_id,
195                construction_method,
196                data_reference_index,
197                base_offset,
198                extent_count,
199                extents,
200            });
201        }
202
203        Ok(ItemLocationBox {
204            full_header,
205            offset_size,
206            length_size,
207            base_offset_size,
208            index_size,
209            item_count,
210            items,
211        })
212    }
213}
214
215impl Serialize for ItemLocationBox {
216    fn serialize<W>(&self, writer: W) -> std::io::Result<()>
217    where
218        W: std::io::Write,
219    {
220        let mut bit_writer = BitWriter::new(writer);
221
222        self.serialize_box_header(&mut bit_writer)?;
223        self.full_header.serialize(&mut bit_writer)?;
224        bit_writer.write_bits(self.offset_size as u64, 4)?;
225        bit_writer.write_bits(self.length_size as u64, 4)?;
226        bit_writer.write_bits(self.base_offset_size as u64, 4)?;
227        bit_writer.write_bits(self.index_size as u64, 4)?;
228
229        if self.full_header.version < 2 {
230            (self
231                .item_count
232                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_count is required"))? as u16)
233                .serialize(&mut bit_writer)?;
234        } else if self.full_header.version == 2 {
235            self.item_count
236                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_count is required"))?
237                .serialize(&mut bit_writer)?;
238        }
239
240        for item in &self.items {
241            item.serialize(&mut bit_writer, self)?;
242        }
243
244        Ok(())
245    }
246}
247
248impl IsoSized for ItemLocationBox {
249    fn size(&self) -> usize {
250        let mut size = 0;
251
252        size += self.full_header.size();
253        size += 1; // offset_size + length_size
254        size += 1; // base_offset_size + index_size/reserved
255
256        if self.full_header.version < 2 {
257            size += 2; // item_count
258        } else if self.full_header.version == 2 {
259            size += 4; // item_count
260        }
261
262        size += self.items.iter().map(|item| item.size(self)).sum::<usize>();
263
264        Self::add_header_size(size)
265    }
266}
267
268/// Item in the [`ItemLocationBox`].
269#[derive(Debug, PartialEq, Eq)]
270pub struct ItemLocationBoxItem {
271    /// An arbitrary integer 'name' for this resource which can be used to refer to it (e.g. in a URL).
272    pub item_id: Option<u32>,
273    /// Taken from the set 0 (file), 1 (idat) or 2 (item).
274    pub construction_method: Option<u8>,
275    /// Either zero ('this file') or an index, with value 1 indicating the first entry, into
276    /// the data references in the [`DataInformationBox`].
277    pub data_reference_index: u16,
278    /// Provides a base value for offset calculations within the referenced data.
279    /// If `base_offset_size` is 0, `base_offset` takes the value 0, i.e. it is unused.
280    pub base_offset: u64,
281    /// Provides the count of the number of extents into which the resource is fragmented;
282    /// it shall have the value 1 or greater.
283    pub extent_count: u16,
284    /// Extents in this item.
285    pub extents: Vec<ItemLocationBoxExtent>,
286}
287
288impl ItemLocationBoxItem {
289    fn serialize<W>(&self, writer: W, parent: &ItemLocationBox) -> io::Result<()>
290    where
291        W: std::io::Write,
292    {
293        let mut bit_writer = BitWriter::new(writer);
294
295        if parent.full_header.version < 2 {
296            (self
297                .item_id
298                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_id is required"))? as u16)
299                .serialize(&mut bit_writer)?;
300        } else if parent.full_header.version == 2 {
301            self.item_id
302                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_id is required"))?
303                .serialize(&mut bit_writer)?;
304        }
305
306        if parent.full_header.version == 1 || parent.full_header.version == 2 {
307            bit_writer.write_bits(0, 12)?;
308            bit_writer.write_bits(
309                self.construction_method
310                    .ok_or(io::Error::new(io::ErrorKind::InvalidData, "construction_method is required"))?
311                    as u64,
312                4,
313            )?;
314        }
315
316        self.data_reference_index.serialize(&mut bit_writer)?;
317        bit_writer.write_bits(self.base_offset, parent.base_offset_size * 8)?;
318        self.extent_count.serialize(&mut bit_writer)?;
319
320        for extent in &self.extents {
321            extent.serialize(&mut bit_writer, parent)?;
322        }
323
324        Ok(())
325    }
326}
327
328impl ItemLocationBoxItem {
329    /// Calculates the size of this item, depending on the parent box.
330    pub fn size(&self, parent: &ItemLocationBox) -> usize {
331        let mut size = 0;
332
333        if parent.full_header.version < 2 {
334            size += 2; // item_id
335        } else if parent.full_header.version == 2 {
336            size += 4; // item_id
337        }
338        if parent.full_header.version == 1 || parent.full_header.version == 2 {
339            size += 2; // reserved + construction_method
340        }
341        size += 2; // data_reference_index
342        size += parent.base_offset_size as usize; // base_offset
343        size += 2; // extent_count
344        size += self.extents.iter().map(|e| e.size(parent)).sum::<usize>();
345
346        size
347    }
348}
349
350/// Extent in the [`ItemLocationBoxItem`].
351#[derive(Debug, PartialEq, Eq)]
352pub struct ItemLocationBoxExtent {
353    /// Provides an index as defined for the construction method.
354    pub item_reference_index: Option<u64>,
355    /// Provides the absolute offset, in bytes from the data origin of the container, of this extent
356    /// data. If [`offset_size`](ItemLocationBox::offset_size) is 0, `extent_offset` takes the value 0.
357    pub extent_offset: u64,
358    /// Provides the absolute length in bytes of this metadata item extent.
359    /// If [`length_size`](ItemLocationBox::length_size) is 0, `extent_length` takes the value 0.
360    /// If the value is 0, then length of the extent is the length of the entire referenced container.
361    pub extent_length: u64,
362}
363
364impl ItemLocationBoxExtent {
365    fn serialize<W>(&self, writer: W, parent: &ItemLocationBox) -> io::Result<()>
366    where
367        W: std::io::Write,
368    {
369        let mut bit_writer = BitWriter::new(writer);
370
371        if (parent.full_header.version == 1 || parent.full_header.version == 2) && parent.index_size > 0 {
372            bit_writer.write_bits(
373                self.item_reference_index
374                    .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_reference_index is required"))?,
375                parent.index_size * 8,
376            )?;
377        }
378        bit_writer.write_bits(self.extent_offset, parent.offset_size * 8)?;
379        bit_writer.write_bits(self.extent_length, parent.length_size * 8)?;
380
381        Ok(())
382    }
383}
384
385impl ItemLocationBoxExtent {
386    /// Calculates the size of this extent, depending on the parent box.
387    pub fn size(&self, parent: &ItemLocationBox) -> usize {
388        let mut size = 0;
389
390        if (parent.full_header.version == 1 || parent.full_header.version == 2) && parent.index_size > 0 {
391            size += parent.index_size as usize; // item_reference_index
392        }
393        size += parent.offset_size as usize; // extent_offset
394        size += parent.length_size as usize; // extent_length
395
396        size
397    }
398}
399
400/// Primary item box
401///
402/// ISO/IEC 14496-12 - 8.11.4
403#[derive(IsoBox, Debug, PartialEq, Eq)]
404#[iso_box(box_type = b"pitm", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
405pub struct PrimaryItemBox {
406    /// The full box header.
407    pub full_header: FullBoxHeader,
408    /// The identifier of the primary item, which shall be the identifier of an item in the [`MetaBox`]
409    /// containing the [`PrimaryItemBox`]. Version 1 should only be used when large `item_ID` values (exceeding
410    /// 65535) are required or expected to be required.
411    pub item_id: u32,
412}
413
414impl<'a> DeserializeSeed<'a, BoxHeader> for PrimaryItemBox {
415    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
416    where
417        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
418    {
419        let full_header = FullBoxHeader::deserialize(&mut reader)?;
420
421        let item_id = if full_header.version == 0 {
422            u16::deserialize(&mut reader)? as u32
423        } else {
424            u32::deserialize(&mut reader)?
425        };
426
427        Ok(PrimaryItemBox { full_header, item_id })
428    }
429}
430
431impl Serialize for PrimaryItemBox {
432    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
433    where
434        W: std::io::Write,
435    {
436        self.serialize_box_header(&mut writer)?;
437        self.full_header.serialize(&mut writer)?;
438
439        if self.full_header.version == 0 {
440            (self.item_id as u16).serialize(&mut writer)?;
441        } else {
442            self.item_id.serialize(&mut writer)?;
443        }
444
445        Ok(())
446    }
447}
448
449impl IsoSized for PrimaryItemBox {
450    fn size(&self) -> usize {
451        let mut size = self.full_header.size();
452
453        if self.full_header.version == 0 {
454            size += 2; // item_id
455        } else {
456            size += 4; // item_id
457        }
458
459        Self::add_header_size(size)
460    }
461}
462
463/// Item protection box
464///
465/// ISO/IEC 14496-12 - 8.11.5
466#[derive(IsoBox, Debug, PartialEq, Eq)]
467#[iso_box(box_type = b"ipro", crate_path = crate)]
468pub struct ItemProtectionBox<'a> {
469    /// The full box header.
470    pub full_header: FullBoxHeader,
471    /// Number of [`ProtectionSchemeInfoBox`]es in this box.
472    pub protection_count: u16,
473    /// The contained [`ScrambleSchemeInfoBox`]es. (one or more)
474    #[iso_box(nested_box(collect))]
475    pub protection_information: Vec<ProtectionSchemeInfoBox<'a>>,
476}
477
478/// Item information box
479///
480/// ISO/IEC 14496-12 - 8.11.6
481#[derive(IsoBox, Debug, PartialEq, Eq)]
482#[iso_box(box_type = b"iinf", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
483pub struct ItemInfoBox<'a> {
484    /// The full box header.
485    pub full_header: FullBoxHeader,
486    /// Provides a count of the number of entries in the [`item_infos`](Self::item_infos) vec.
487    pub entry_count: u32,
488    /// The entries.
489    #[iso_box(nested_box(collect))]
490    pub item_infos: Vec<ItemInfoEntry<'a>>,
491}
492
493impl<'a> DeserializeSeed<'a, BoxHeader> for ItemInfoBox<'a> {
494    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
495    where
496        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
497    {
498        let full_header = FullBoxHeader::deserialize(&mut reader)?;
499
500        let entry_count = if full_header.version == 0 {
501            u16::deserialize(&mut reader)? as u32
502        } else {
503            u32::deserialize(&mut reader)?
504        };
505
506        let mut item_infos = Vec::with_capacity(entry_count as usize);
507        for _ in 0..entry_count {
508            item_infos.push(ItemInfoEntry::deserialize(&mut reader)?);
509        }
510
511        Ok(ItemInfoBox {
512            full_header,
513            entry_count,
514            item_infos,
515        })
516    }
517}
518
519impl Serialize for ItemInfoBox<'_> {
520    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
521    where
522        W: std::io::Write,
523    {
524        self.serialize_box_header(&mut writer)?;
525        self.full_header.serialize(&mut writer)?;
526
527        if self.full_header.version == 0 {
528            (self.entry_count as u16).serialize(&mut writer)?;
529        } else {
530            self.entry_count.serialize(&mut writer)?;
531        }
532
533        for item_info in &self.item_infos {
534            item_info.serialize(&mut writer)?;
535        }
536
537        Ok(())
538    }
539}
540
541impl IsoSized for ItemInfoBox<'_> {
542    fn size(&self) -> usize {
543        let mut size = self.full_header.size();
544
545        if self.full_header.version == 0 {
546            size += 2; // entry_count
547        } else {
548            size += 4; // entry_count
549        }
550
551        size += self.item_infos.size();
552
553        Self::add_header_size(size)
554    }
555}
556
557/// Item information entry
558///
559/// ISO/IEC 14496-12 - 8.11.6
560#[derive(IsoBox, Debug, PartialEq, Eq)]
561#[iso_box(box_type = b"infe", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
562pub struct ItemInfoEntry<'a> {
563    /// The full box header.
564    pub full_header: FullBoxHeader,
565    /// Contains either 0 for the primary resource (e.g., the XML contained in an XMLBox) or the ID of
566    /// the item for which the following information is defined.
567    pub item_id: Option<u32>,
568    /// Contains either 0 for an unprotected item, or the index, with value 1 indicating
569    /// the first entry, into the ItemProtectionBox defining the protection applied to this item (the first
570    /// box in the ItemProtectionBox has the index 1).
571    pub item_protection_index: u16,
572    /// A 32-bit value, typically 4 printable characters, that is a defined valid item type indicator,
573    /// such as 'mime'.
574    pub item_type: [u8; 4],
575    /// The symbolic name of the item (source file for file delivery transmissions).
576    pub item_name: Utf8String,
577    /// The item information.
578    pub item: Option<ItemInfoEntryItem>,
579    /// A four character code that identifies the extension fields.
580    pub extension_type: Option<[u8; 4]>,
581    /// The extension.
582    pub extension: Option<ItemInfoExtension<'a>>,
583}
584
585/// Info in [`ItemInfoEntry`].
586#[derive(Debug, PartialEq, Eq)]
587pub enum ItemInfoEntryItem {
588    /// MIME type item
589    Mime {
590        /// The MIME type of the item. If the item is content encoded, then the content
591        /// type refers to the item after content decoding.
592        content_type: Utf8String,
593        /// Indicates that the binary file is encoded and needs to be decoded before
594        /// interpreted. The values are as defined for `Content-Encoding` for HTTP/1.1. Some possible values are
595        /// "gzip", "compress" and "deflate". An empty string indicates no content encoding. Note that the item
596        /// is stored after the content encoding has been applied.
597        content_encoding: Utf8String,
598    },
599    /// URI item
600    Uri {
601        /// An absolute URI, that is used as a type indicator.
602        item_uri_type: Utf8String,
603    },
604}
605
606impl<'a> DeserializeSeed<'a, BoxHeader> for ItemInfoEntry<'a> {
607    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
608    where
609        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
610    {
611        let full_header = FullBoxHeader::deserialize(&mut reader)?;
612
613        let item_id = if full_header.version == 0 || full_header.version == 1 || full_header.version == 2 {
614            Some(u16::deserialize(&mut reader)? as u32)
615        } else if full_header.version == 3 {
616            Some(u32::deserialize(&mut reader)?)
617        } else {
618            None
619        };
620        let item_protection_index = u16::deserialize(&mut reader)?;
621        let item_type = if full_header.version == 0 || full_header.version == 1 {
622            *b"mime"
623        } else {
624            <[u8; 4]>::deserialize(&mut reader)?
625        };
626        let item_name = Utf8String::deserialize(&mut reader)?;
627
628        let item = match &item_type {
629            b"mime" => {
630                let content_type = Utf8String::deserialize(&mut reader)?;
631                let content_encoding = Utf8String::deserialize(&mut reader)?;
632
633                Some(ItemInfoEntryItem::Mime {
634                    content_type,
635                    content_encoding,
636                })
637            }
638            b"uri " => {
639                let item_uri_type = Utf8String::deserialize(&mut reader)?;
640                Some(ItemInfoEntryItem::Uri { item_uri_type })
641            }
642            _ => None,
643        };
644
645        let extension_type = if full_header.version == 1 {
646            <[u8; 4]>::deserialize(&mut reader).eof_to_none()?
647        } else {
648            None
649        };
650        let extension = if let Some(extension_type) = extension_type {
651            let extension = ItemInfoExtension::deserialize_seed(&mut reader, extension_type)?;
652            Some(extension)
653        } else {
654            None
655        };
656
657        Ok(Self {
658            full_header,
659            item_id,
660            item_protection_index,
661            item_type,
662            item_name,
663            item,
664            extension_type,
665            extension,
666        })
667    }
668}
669
670impl Serialize for ItemInfoEntry<'_> {
671    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
672    where
673        W: std::io::Write,
674    {
675        self.serialize_box_header(&mut writer)?;
676        self.full_header.serialize(&mut writer)?;
677
678        if self.full_header.version == 0 || self.full_header.version == 1 {
679            (self
680                .item_id
681                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_ID is required"))? as u16)
682                .serialize(&mut writer)?;
683            self.item_protection_index.serialize(&mut writer)?;
684            self.item_name.serialize(&mut writer)?;
685            if let Some(ItemInfoEntryItem::Mime {
686                content_type,
687                content_encoding,
688            }) = self.item.as_ref()
689            {
690                content_type.serialize(&mut writer)?;
691                content_encoding.serialize(&mut writer)?;
692            } else {
693                return Err(io::Error::new(io::ErrorKind::InvalidData, "content_type is required"));
694            }
695        }
696
697        if self.full_header.version == 1 {
698            if let Some(extension_type) = self.extension_type {
699                extension_type.serialize(&mut writer)?;
700            }
701            if let Some(extension) = self.extension.as_ref() {
702                extension.serialize(&mut writer)?;
703            }
704        }
705
706        if self.full_header.version >= 2 {
707            if self.full_header.version == 2 {
708                (self
709                    .item_id
710                    .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_ID is required"))? as u16)
711                    .serialize(&mut writer)?;
712            } else if self.full_header.version == 3 {
713                self.item_id
714                    .ok_or(io::Error::new(io::ErrorKind::InvalidData, "item_ID is required"))?
715                    .serialize(&mut writer)?;
716            }
717
718            self.item_protection_index.serialize(&mut writer)?;
719            self.item_type.serialize(&mut writer)?;
720            self.item_name.serialize(&mut writer)?;
721            match &self.item {
722                Some(ItemInfoEntryItem::Mime {
723                    content_type,
724                    content_encoding,
725                }) => {
726                    content_type.serialize(&mut writer)?;
727                    content_encoding.serialize(&mut writer)?;
728                }
729                Some(ItemInfoEntryItem::Uri { item_uri_type }) => {
730                    item_uri_type.serialize(&mut writer)?;
731                }
732                None => {}
733            }
734        }
735
736        Ok(())
737    }
738}
739
740impl IsoSized for ItemInfoEntry<'_> {
741    fn size(&self) -> usize {
742        let mut size = self.full_header.size();
743
744        if self.full_header.version == 0 || self.full_header.version == 1 {
745            size += 2; // item_id
746            size += 2; // item_protection_index
747            size += self.item_name.size();
748            if let Some(ItemInfoEntryItem::Mime {
749                content_type,
750                content_encoding,
751            }) = &self.item
752            {
753                size += content_type.size();
754                size += content_encoding.size();
755            }
756        }
757        if self.full_header.version == 1 {
758            size += self.extension_type.size();
759            size += self.extension.size();
760        }
761        if self.full_header.version >= 2 {
762            if self.full_header.version == 2 {
763                size += 2; // item_id
764            } else if self.full_header.version == 3 {
765                size += 4; // item_id
766            }
767            size += 2; // item_protection_index
768            size += self.item_type.size();
769            size += self.item_name.size();
770            match &self.item {
771                Some(ItemInfoEntryItem::Mime {
772                    content_type,
773                    content_encoding,
774                }) => {
775                    size += content_type.size();
776                    size += content_encoding.size();
777                }
778                Some(ItemInfoEntryItem::Uri { item_uri_type }) => {
779                    size += item_uri_type.size();
780                }
781                None => {}
782            }
783        }
784
785        Self::add_header_size(size)
786    }
787}
788
789/// [`ItemInfoEntry`] extension.
790#[derive(Debug, PartialEq, Eq)]
791pub enum ItemInfoExtension<'a> {
792    /// "fdel"
793    FDItemInfoExtension {
794        /// Contains the URI of the file as defined in HTTP/1.1 (IETF RFC 2616).
795        current_location: Utf8String,
796        /// Contains an MD5 digest of the file.
797        /// See HTTP/1.1 (IETF RFC 2616) and IETF RFC 1864.
798        current_md5: Utf8String,
799        /// Gives the total length (in bytes) of the (un-encoded) file.
800        content_length: u64,
801        /// Gives the total length (in bytes) of the (encoded) file. Transfer length is equal to
802        /// content length if no content encoding is applied (see above).
803        transfer_length: u64,
804        /// Provides a count of the number of entries in the
805        /// [`group_id`](ItemInfoExtension::FDItemInfoExtension::entry_count) vec.
806        entry_count: u8,
807        /// Indicates a file group to which the file item (source file) belongs. See 3GPP TS 26.346 for
808        /// more details on file groups.
809        group_id: Vec<u32>,
810    },
811    /// Any other extension.
812    Other {
813        /// The four character code that identifies the extension fields.
814        extension_type: [u8; 4],
815        /// Extension fields.
816        data: BytesCow<'a>,
817    },
818}
819
820impl<'a> DeserializeSeed<'a, [u8; 4]> for ItemInfoExtension<'a> {
821    fn deserialize_seed<R>(mut reader: R, seed: [u8; 4]) -> std::io::Result<Self>
822    where
823        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
824    {
825        match &seed {
826            b"fdel" => {
827                let current_location = Utf8String::deserialize(&mut reader)?;
828                let current_md5 = Utf8String::deserialize(&mut reader)?;
829                let content_length = u64::deserialize(&mut reader)?;
830                let transfer_length = u64::deserialize(&mut reader)?;
831                let entry_count = u8::deserialize(&mut reader)?;
832
833                let mut group_id = Vec::with_capacity(entry_count as usize);
834                for _ in 0..entry_count {
835                    group_id.push(u32::deserialize(&mut reader)?);
836                }
837
838                Ok(ItemInfoExtension::FDItemInfoExtension {
839                    current_location,
840                    current_md5,
841                    content_length,
842                    transfer_length,
843                    entry_count,
844                    group_id,
845                })
846            }
847            _ => Ok(ItemInfoExtension::Other {
848                extension_type: seed,
849                data: reader.try_read_to_end()?,
850            }),
851        }
852    }
853}
854
855impl Serialize for ItemInfoExtension<'_> {
856    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
857    where
858        W: std::io::Write,
859    {
860        match self {
861            ItemInfoExtension::FDItemInfoExtension {
862                current_location,
863                current_md5,
864                content_length,
865                transfer_length,
866                entry_count,
867                group_id,
868            } => {
869                current_location.serialize(&mut writer)?;
870                current_md5.serialize(&mut writer)?;
871                content_length.serialize(&mut writer)?;
872                transfer_length.serialize(&mut writer)?;
873                entry_count.serialize(&mut writer)?;
874
875                for id in group_id {
876                    id.serialize(&mut writer)?;
877                }
878
879                Ok(())
880            }
881            ItemInfoExtension::Other { extension_type, data } => {
882                extension_type.serialize(&mut writer)?;
883                data.serialize(&mut writer)?;
884                Ok(())
885            }
886        }
887    }
888}
889
890impl IsoSized for ItemInfoExtension<'_> {
891    fn size(&self) -> usize {
892        match self {
893            ItemInfoExtension::FDItemInfoExtension {
894                current_location,
895                current_md5,
896                content_length,
897                transfer_length,
898                entry_count,
899                group_id,
900            } => {
901                current_location.size()
902                    + current_md5.size()
903                    + content_length.size()
904                    + transfer_length.size()
905                    + entry_count.size()
906                    + group_id.size()
907            }
908            ItemInfoExtension::Other { data, .. } => data.size(),
909        }
910    }
911}
912
913// 8.11.7 is deprecated
914
915// 8.11.8 is deprecated
916
917/// Item data box
918///
919/// ISO/IEC 14496-12 - 8.11.11
920#[derive(IsoBox, PartialEq, Eq)]
921#[iso_box(box_type = b"idat", crate_path = crate)]
922pub struct ItemDataBox<'a> {
923    /// The contained metadata.
924    pub data: BytesCow<'a>,
925}
926
927impl Debug for ItemDataBox<'_> {
928    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
929        f.debug_struct("ItemDataBox").field("data.len", &self.data.len()).finish()
930    }
931}
932
933/// Item reference box
934///
935/// ISO/IEC 14496-12 - 8.11.12
936#[derive(IsoBox, Debug, PartialEq, Eq)]
937#[iso_box(box_type = b"iref", skip_impl(deserialize_seed), crate_path = crate)]
938pub struct ItemReferenceBox {
939    /// The full box header.
940    pub full_header: FullBoxHeader,
941    /// The contained [`SingleItemTypeReferenceBox`]es. (any quantity)
942    #[iso_box(repeated)]
943    pub references: Vec<SingleItemTypeReferenceBox>,
944}
945
946impl<'a> DeserializeSeed<'a, BoxHeader> for ItemReferenceBox {
947    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
948    where
949        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
950    {
951        let full_header = FullBoxHeader::deserialize(&mut reader)?;
952
953        let mut references = Vec::new();
954
955        loop {
956            let Some(header) = BoxHeader::deserialize(&mut reader).eof_to_none()? else {
957                break;
958            };
959
960            let Some(iso_box) =
961                SingleItemTypeReferenceBox::deserialize_seed(&mut reader, (header, full_header.version == 1))
962                    .eof_to_none()?
963            else {
964                break;
965            };
966            references.push(iso_box);
967        }
968
969        Ok(ItemReferenceBox { full_header, references })
970    }
971}
972
973/// Single item type reference box
974///
975/// ISO/IEC 14496-12 - 8.11.12
976#[derive(Debug, PartialEq, Eq)]
977pub struct SingleItemTypeReferenceBox {
978    /// Inidicates whether this is a `SingleItemTypeReferenceBox` or a `SingleItemTypeReferenceBoxLarge`.
979    pub large: bool,
980    /// The box header.
981    pub header: BoxHeader,
982    /// The `item_ID` of the item that refers to other items.
983    pub from_item_id: u32,
984    /// The number of references.
985    pub reference_count: u16,
986    /// The `item_ID` of the item referred to.
987    pub to_item_id: Vec<u32>,
988}
989
990impl<'a> DeserializeSeed<'a, (BoxHeader, bool)> for SingleItemTypeReferenceBox {
991    fn deserialize_seed<R>(mut reader: R, seed: (BoxHeader, bool)) -> std::io::Result<Self>
992    where
993        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
994    {
995        let (header, large) = seed;
996
997        let from_item_id = if !large {
998            u16::deserialize(&mut reader)? as u32
999        } else {
1000            u32::deserialize(&mut reader)?
1001        };
1002        let reference_count = u16::deserialize(&mut reader)?;
1003        let mut to_item_id = Vec::with_capacity(reference_count as usize);
1004        for _ in 0..reference_count {
1005            if !large {
1006                to_item_id.push(u16::deserialize(&mut reader)? as u32);
1007            } else {
1008                to_item_id.push(u32::deserialize(&mut reader)?);
1009            }
1010        }
1011
1012        Ok(SingleItemTypeReferenceBox {
1013            large,
1014            header,
1015            from_item_id,
1016            reference_count,
1017            to_item_id,
1018        })
1019    }
1020}
1021
1022impl Serialize for SingleItemTypeReferenceBox {
1023    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
1024    where
1025        W: std::io::Write,
1026    {
1027        self.header.serialize(&mut writer)?;
1028
1029        if !self.large {
1030            (self.from_item_id as u16).serialize(&mut writer)?;
1031        } else {
1032            self.from_item_id.serialize(&mut writer)?;
1033        }
1034        self.reference_count.serialize(&mut writer)?;
1035        for id in &self.to_item_id {
1036            if !self.large {
1037                (*id as u16).serialize(&mut writer)?;
1038            } else {
1039                id.serialize(&mut writer)?;
1040            }
1041        }
1042
1043        Ok(())
1044    }
1045}
1046
1047impl IsoSized for SingleItemTypeReferenceBox {
1048    fn size(&self) -> usize {
1049        let mut size = self.header.size();
1050
1051        if !self.large {
1052            size += 2; // from_item_id
1053            size += 2; // reference_count
1054            size += self.to_item_id.len() * 2; // to_item_id
1055        } else {
1056            size += 4; // from_item_id
1057            size += 2; // reference_count
1058            size += self.to_item_id.len() * 4; // to_item_id
1059        }
1060
1061        size
1062    }
1063}
1064
1065/// Item properties box
1066///
1067/// ISO/IEC 14496-12 - 8.11.14
1068#[derive(IsoBox, Debug, PartialEq, Eq)]
1069#[iso_box(box_type = b"iprp", crate_path = crate)]
1070pub struct ItemPropertiesBox<'a> {
1071    /// The contained [`ItemPropertyContainerBox`]. (mandatory)
1072    #[iso_box(nested_box)]
1073    pub property_container: ItemPropertyContainerBox<'a>,
1074    /// The contained [`ItemPropertyAssociationBox`]es. (any quantity)
1075    #[iso_box(nested_box(collect))]
1076    pub association: Vec<ItemPropertyAssociationBox>,
1077}
1078
1079/// Item property container box
1080///
1081/// ISO/IEC 14496-12 - 8.11.14
1082#[derive(IsoBox, Debug, PartialEq, Eq)]
1083#[iso_box(box_type = b"ipco", crate_path = crate)]
1084pub struct ItemPropertyContainerBox<'a> {
1085    /// The contained [`ExtendedTypeBox`]. (optional)
1086    #[iso_box(nested_box(collect))]
1087    pub etyp: Option<ExtendedTypeBox<'a>>,
1088    /// The contained [`BrandProperty`]es. (zero or one per item)
1089    #[iso_box(nested_box(collect))]
1090    pub brnd: Vec<BrandProperty>,
1091    /// The contained [`ScrambleSchemeInfoBox`]es. (one or more)
1092    #[iso_box(nested_box(collect))]
1093    pub scrb: Vec<ScrambleSchemeInfoBox<'a>>,
1094    /// Any other sub boxes.
1095    #[iso_box(nested_box(collect_unknown))]
1096    pub boxes: Vec<UnknownBox<'a>>,
1097}
1098
1099/// Item property association box
1100///
1101/// ISO/IEC 14496-12 - 8.11.14
1102#[derive(IsoBox, Debug, PartialEq, Eq)]
1103#[iso_box(box_type = b"ipma", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
1104pub struct ItemPropertyAssociationBox {
1105    /// The full box header.
1106    pub full_header: FullBoxHeader,
1107    /// The number of entries in the [`entries`](Self::entries) vec.
1108    pub entry_count: u32,
1109    /// The contained entries.
1110    pub entries: Vec<ItemPropertyAssociationBoxEntry>,
1111}
1112
1113impl<'a> DeserializeSeed<'a, BoxHeader> for ItemPropertyAssociationBox {
1114    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
1115    where
1116        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
1117    {
1118        let full_header = FullBoxHeader::deserialize(&mut reader)?;
1119        let entry_count = u32::deserialize(&mut reader)?;
1120
1121        let mut entries = Vec::with_capacity(entry_count as usize);
1122        for _ in 0..entry_count {
1123            entries.push(ItemPropertyAssociationBoxEntry::deserialize_seed(&mut reader, &full_header)?);
1124        }
1125
1126        Ok(ItemPropertyAssociationBox {
1127            full_header,
1128            entry_count,
1129            entries,
1130        })
1131    }
1132}
1133
1134impl Serialize for ItemPropertyAssociationBox {
1135    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
1136    where
1137        W: std::io::Write,
1138    {
1139        self.serialize_box_header(&mut writer)?;
1140        self.full_header.serialize(&mut writer)?;
1141        self.entry_count.serialize(&mut writer)?;
1142
1143        for entry in &self.entries {
1144            entry.serialize(&mut writer, &self.full_header)?;
1145        }
1146
1147        Ok(())
1148    }
1149}
1150
1151impl IsoSized for ItemPropertyAssociationBox {
1152    fn size(&self) -> usize {
1153        let mut size = self.full_header.size();
1154
1155        size += 4; // entry_count
1156        size += self.entries.iter().map(|e| e.size(&self.full_header)).sum::<usize>();
1157
1158        Self::add_header_size(size)
1159    }
1160}
1161
1162/// Entry in the [`ItemPropertyAssociationBox`].
1163#[derive(Debug, PartialEq, Eq)]
1164pub struct ItemPropertyAssociationBoxEntry {
1165    /// Identifies the item with which properties are associated.
1166    pub item_id: u32,
1167    /// The number of associations in the [`associations`](Self::associations) vec.
1168    pub association_count: u8,
1169    /// The associations.
1170    pub associations: Vec<ItemPropertyAssociationBoxEntryAssociation>,
1171}
1172
1173impl<'a> DeserializeSeed<'a, &FullBoxHeader> for ItemPropertyAssociationBoxEntry {
1174    fn deserialize_seed<R>(mut reader: R, seed: &FullBoxHeader) -> std::io::Result<Self>
1175    where
1176        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
1177    {
1178        let item_id = if seed.version < 1 {
1179            u16::deserialize(&mut reader)? as u32
1180        } else {
1181            u32::deserialize(&mut reader)?
1182        };
1183
1184        let assocation_count = u8::deserialize(&mut reader)?;
1185        let mut associations = Vec::with_capacity(assocation_count as usize);
1186        for _ in 0..assocation_count {
1187            associations.push(ItemPropertyAssociationBoxEntryAssociation::deserialize_seed(
1188                &mut reader,
1189                seed,
1190            )?);
1191        }
1192
1193        Ok(ItemPropertyAssociationBoxEntry {
1194            item_id,
1195            association_count: assocation_count,
1196            associations,
1197        })
1198    }
1199}
1200
1201impl ItemPropertyAssociationBoxEntry {
1202    fn serialize<W>(&self, mut writer: W, header: &FullBoxHeader) -> io::Result<()>
1203    where
1204        W: std::io::Write,
1205    {
1206        if header.version < 1 {
1207            (self.item_id as u16).serialize(&mut writer)?;
1208        } else {
1209            self.item_id.serialize(&mut writer)?;
1210        }
1211
1212        self.association_count.serialize(&mut writer)?;
1213        for association in &self.associations {
1214            association.serialize(&mut writer, header.flags)?;
1215        }
1216
1217        Ok(())
1218    }
1219}
1220
1221impl ItemPropertyAssociationBoxEntry {
1222    /// Calculates the size of this entry, depending on the parent's box header.
1223    pub fn size(&self, header: &FullBoxHeader) -> usize {
1224        let mut size = 0;
1225
1226        if header.version < 1 {
1227            size += 2; // item_id
1228        } else {
1229            size += 4; // item_id
1230        }
1231        size += 1; // association_count
1232        size += self.associations.iter().map(|a| a.size(header.flags)).sum::<usize>();
1233
1234        size
1235    }
1236}
1237
1238/// Association in the [`ItemPropertyAssociationBoxEntry`].
1239#[derive(Debug, PartialEq, Eq)]
1240pub struct ItemPropertyAssociationBoxEntryAssociation {
1241    /// When set to 1 indicates that the associated property is essential to the item, otherwise it is non-essential.
1242    pub essential: bool,
1243    /// Either 0 indicating that no property is associated (the essential indicator shall also
1244    /// be 0), or is the 1-based index (counting all boxes, including [`FreeSpaceBox`](super::FreeSpaceBox)es)
1245    /// of the associated property box in the [`ItemPropertyContainerBox`] contained in the same [`ItemPropertiesBox`].
1246    pub property_index: u16,
1247}
1248
1249impl<'a> DeserializeSeed<'a, &FullBoxHeader> for ItemPropertyAssociationBoxEntryAssociation {
1250    fn deserialize_seed<R>(mut reader: R, seed: &FullBoxHeader) -> std::io::Result<Self>
1251    where
1252        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
1253    {
1254        let byte = u8::deserialize(&mut reader)?;
1255        let essential = byte >> 7 != 0;
1256        let property_index = (byte & 0b0111_1111) as u16;
1257
1258        if (*seed.flags & 0b1) != 0 {
1259            let low_byte = u8::deserialize(&mut reader)?;
1260
1261            Ok(Self {
1262                essential,
1263                property_index: (property_index << 8) | low_byte as u16,
1264            })
1265        } else {
1266            Ok(Self {
1267                essential,
1268                property_index,
1269            })
1270        }
1271    }
1272}
1273
1274impl ItemPropertyAssociationBoxEntryAssociation {
1275    fn serialize<W>(&self, mut writer: W, flags: U24Be) -> io::Result<()>
1276    where
1277        W: std::io::Write,
1278    {
1279        if (*flags & 0b1) != 0 {
1280            // e iiiiiii
1281            let mut byte = (self.essential as u8) << 7;
1282            byte |= ((self.property_index >> 8) as u8) & 0b0111_1111;
1283            byte.serialize(&mut writer)?;
1284
1285            // iiiiiiii
1286            let low_byte = (self.property_index & 0xFF) as u8;
1287            low_byte.serialize(&mut writer)?;
1288        } else {
1289            // e iiiiiii
1290            let byte = (self.essential as u8) << 7 | (self.property_index & 0b0111_1111) as u8;
1291            byte.serialize(&mut writer)?;
1292        }
1293
1294        Ok(())
1295    }
1296}
1297
1298impl ItemPropertyAssociationBoxEntryAssociation {
1299    /// Calculates the size of this association, depending on the flags.
1300    pub fn size(&self, flags: U24Be) -> usize {
1301        if (*flags & 0b1) != 0 {
1302            2 // e iiiiiii + iiiiiiii
1303        } else {
1304            1 // e iiiiiii
1305        }
1306    }
1307}
1308
1309/// Brand item property
1310///
1311/// ISO/IEC 14496-12 - 8.11.15
1312#[derive(IsoBox, Debug, PartialEq, Eq)]
1313#[iso_box(box_type = b"brnd", crate_path = crate)]
1314pub struct BrandProperty {
1315    /// The "best use" brand of the file which will provide the greatest compatibility.
1316    #[iso_box(from = "[u8; 4]")]
1317    pub major_brand: Brand,
1318    /// Minor version of the major brand.
1319    pub minor_version: u32,
1320    /// A list of compatible brands.
1321    #[iso_box(repeated, from = "[u8; 4]")]
1322    pub compatible_brands: Vec<Brand>,
1323}