isobmff/boxes/
file_organization.rs

1//! File organization boxes defined in ISO/IEC 14496-12 - 4
2
3use nutype_enum::nutype_enum;
4
5use crate::{IsoBox, IsoSized, UnknownBox};
6
7nutype_enum! {
8    /// A four character code, registered with ISO, that identifies a precise specification.
9    ///
10    /// See: https://mp4ra.org/registered-types/brands
11    pub enum Brand([u8; 4]) {
12        /// MPEG-4 version 1
13        Mp41 = *b"mp41",
14        /// E.2 'isom' brand
15        IsoM = *b"isom",
16        /// E.3 'avc1' brand
17        Avc1 = *b"avc1",
18        /// E.4 'iso2' brand
19        Iso2 = *b"iso2",
20        /// E.5 'mp71' brand
21        Mp71 = *b"mp71",
22        /// E.6 'iso3' brand
23        Iso3 = *b"iso3",
24        /// E.7 'iso4' brand
25        Iso4 = *b"iso4",
26        /// E.8 'iso5' brand
27        Iso5 = *b"iso5",
28        /// E.9 'iso6' brand
29        Iso6 = *b"iso6",
30        /// E.10 'iso7' brand
31        Iso7 = *b"iso7",
32        /// E.11 'iso8' brand
33        Iso8 = *b"iso8",
34        /// E.12 'iso9' brand
35        Iso9 = *b"iso9",
36        /// E.13 'isoa' brand
37        IsoA = *b"isoa",
38        /// E.14 'isob' brand
39        IsoB = *b"isob",
40        /// E.15 'relo' brand
41        Relo = *b"relo",
42        /// E.16 'isoc' brand
43        IsoC = *b"isoc",
44        /// E.17 'comp' brand
45        Comp = *b"comp",
46        /// E.18 'unif' brand
47        Unif = *b"unif",
48    }
49}
50
51impl IsoSized for Brand {
52    fn size(&self) -> usize {
53        4
54    }
55}
56
57/// File-type box
58///
59/// ISO/IEC 14496-12 - 4.3
60#[derive(IsoBox, Debug, PartialEq, Eq)]
61#[iso_box(box_type = b"ftyp", crate_path = crate)]
62pub struct FileTypeBox {
63    /// The "best use" brand of the file which will provide the greatest compatibility.
64    #[iso_box(from = "[u8; 4]")]
65    pub major_brand: Brand,
66    /// Minor version of the major brand.
67    pub minor_version: u32,
68    /// A list of compatible brands.
69    #[iso_box(repeated, from = "[u8; 4]")]
70    pub compatible_brands: Vec<Brand>,
71}
72
73/// Type combination box
74///
75/// ISO/IEC 14496-12 - 4.4
76#[derive(IsoBox, Debug, PartialEq, Eq)]
77#[iso_box(box_type = b"tyco", crate_path = crate)]
78pub struct TypeCombinationBox {
79    /// A list of compatible brands.
80    #[iso_box(repeated, from = "[u8; 4]")]
81    pub compatible_brands: Vec<Brand>,
82}
83
84/// Extended type box
85///
86/// ISO/IEC 14496-12 - 4.4
87#[derive(IsoBox, Debug, PartialEq, Eq)]
88#[iso_box(box_type = b"etyp", crate_path = crate)]
89pub struct ExtendedTypeBox<'a> {
90    /// A list of [`TypeCombinationBox`]es.
91    #[iso_box(nested_box(collect))]
92    pub compatible_combinations: Vec<TypeCombinationBox>,
93    /// A list of unknown boxes that were encountered while parsing the box.
94    #[iso_box(nested_box(collect_unknown))]
95    pub unknown_boxes: Vec<UnknownBox<'a>>,
96}
97
98#[cfg(test)]
99#[cfg_attr(all(test, coverage_nightly), coverage(off))]
100mod tests {
101    use scuffle_bytes_util::zero_copy::{Deserialize, Slice};
102
103    use crate::boxes::{Brand, ExtendedTypeBox, TypeCombinationBox};
104
105    #[test]
106    fn demux_tyco() {
107        #[rustfmt::skip]
108        let data = [
109            0x00, 0x00, 0x00, 0x0C, // size
110            b't', b'y', b'c', b'o', // type
111            b'i', b's', b'o', b'6', // data
112            0x01,
113        ];
114
115        let mdat = TypeCombinationBox::deserialize(Slice::from(&data[..])).unwrap();
116        assert_eq!(mdat.compatible_brands.len(), 1);
117        assert_eq!(mdat.compatible_brands[0], Brand::Iso6);
118    }
119
120    #[test]
121    fn demux_etyp() {
122        #[rustfmt::skip]
123        let data = [
124            0x00, 0x00, 0x00, 44, // size
125            b'e', b't', b'y', b'p', // type
126
127            0x00, 0x00, 0x00, 12, // tyco size
128            b't', b'y', b'c', b'o', // tyco type
129            b'i', b's', b'o', b'm', // data
130
131            0x00, 0x00, 0x00, 12, // tyco size
132            b't', b'y', b'c', b'o', // tyco type
133            b'i', b's', b'o', b'6', // data
134
135            0x00, 0x00, 0x00, 12, // unknown size
136            b'u', b'n', b'k', b'n', // unknown type
137            0x42, 0x00, 0x42, 0x00, // data
138        ];
139
140        let mdat = ExtendedTypeBox::deserialize(Slice::from(&data[..])).unwrap();
141
142        assert_eq!(mdat.compatible_combinations.len(), 2);
143
144        assert_eq!(mdat.compatible_combinations[0].compatible_brands.len(), 1);
145        assert_eq!(mdat.compatible_combinations[0].compatible_brands[0], Brand::IsoM);
146
147        assert_eq!(mdat.compatible_combinations[1].compatible_brands.len(), 1);
148        assert_eq!(mdat.compatible_combinations[1].compatible_brands[0], Brand::Iso6);
149
150        assert_eq!(mdat.unknown_boxes.len(), 1);
151        assert_eq!(mdat.unknown_boxes[0].data.len(), 4);
152        assert_eq!(mdat.unknown_boxes[0].data.as_bytes()[0], 0x42);
153        assert_eq!(mdat.unknown_boxes[0].data.as_bytes()[1], 0x00);
154        assert_eq!(mdat.unknown_boxes[0].data.as_bytes()[2], 0x42);
155        assert_eq!(mdat.unknown_boxes[0].data.as_bytes()[3], 0x00);
156    }
157}