mp4_atom/meta/
iprp.rs

1use crate::*;
2
3// ItemPropertiesBox. ISO/IEC 14496-12:2022 Section 8.11.14
4// This is used to work out what the items mean
5
6#[derive(Debug, Clone, PartialEq, Default)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Iprp {
9    pub ipco: Ipco,
10    pub ipma: Vec<Ipma>,
11}
12
13impl Atom for Iprp {
14    const KIND: FourCC = FourCC::new(b"iprp");
15
16    nested! {
17        required: [ Ipco ],
18        optional: [ ],
19        multiple: [ Ipma ],
20    }
21}
22
23#[derive(Debug, Clone, PartialEq, Default)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Ipco {
26    // Its a container, but properties (boxes) can repeat and the exact order matters
27    pub properties: Vec<crate::Any>,
28}
29
30impl Atom for Ipco {
31    const KIND: FourCC = FourCC::new(b"ipco");
32
33    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
34        let mut props = vec![];
35        while let Some(prop) = crate::Any::decode_maybe(buf)? {
36            props.push(prop);
37        }
38        Ok(Self { properties: props })
39    }
40
41    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
42        for property in &self.properties {
43            property.encode(buf)?
44        }
45        Ok(())
46    }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Default)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51pub struct PropertyAssociation {
52    pub essential: bool,
53    pub property_index: u16,
54}
55
56impl PropertyAssociation {
57    fn encode<B: BufMut>(&self, buf: &mut B, prop_index_15_bit: bool) -> Result<()> {
58        if prop_index_15_bit {
59            let flag_and_prop_index = if self.essential {
60                0x8000 | self.property_index
61            } else {
62                self.property_index
63            };
64            flag_and_prop_index.encode(buf)
65        } else {
66            let flag_and_prop_index = if self.essential {
67                0x80 | (self.property_index as u8)
68            } else {
69                self.property_index as u8
70            };
71            flag_and_prop_index.encode(buf)
72        }
73    }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Default)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
78pub struct PropertyAssociations {
79    pub item_id: u32,
80    pub associations: Vec<PropertyAssociation>,
81}
82
83impl PropertyAssociations {
84    fn encode<B: BufMut>(
85        &self,
86        buf: &mut B,
87        version: IpmaVersion,
88        prop_index_15_bit: bool,
89    ) -> Result<()> {
90        if version == IpmaVersion::V0 {
91            (self.item_id as u16).encode(buf)?;
92        } else {
93            self.item_id.encode(buf)?;
94        }
95        let association_count: u8 = self.associations.len() as u8;
96        association_count.encode(buf)?;
97        for association in &self.associations {
98            association.encode(buf, prop_index_15_bit)?;
99        }
100        Ok(())
101    }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Default)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub struct Ipma {
107    pub item_properties: Vec<PropertyAssociations>,
108}
109
110ext! {
111    name: Ipma,
112    versions: [0, 1],
113    flags: {
114        prop_index_15_bits = 1,
115    }
116}
117
118impl AtomExt for Ipma {
119    type Ext = IpmaExt;
120
121    const KIND_EXT: FourCC = FourCC::new(b"ipma");
122
123    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext> {
124        let mut version = IpmaVersion::V0;
125        let mut prop_index_15_bit = false;
126        for item_property in &self.item_properties {
127            if item_property.item_id > (u16::MAX as u32) {
128                version = IpmaVersion::V1;
129            }
130            for association in &item_property.associations {
131                if association.property_index > 0x7f {
132                    prop_index_15_bit = true;
133                }
134            }
135        }
136        let entry_count: u32 = self.item_properties.len() as u32;
137        entry_count.encode(buf)?;
138        for item_property in &self.item_properties {
139            item_property.encode(buf, version, prop_index_15_bit)?;
140        }
141        Ok(IpmaExt {
142            version,
143            prop_index_15_bits: prop_index_15_bit,
144        })
145    }
146
147    fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self> {
148        let entry_count = u32::decode(buf)?;
149        let mut item_properties = vec![];
150        for _i in 0..entry_count {
151            let item_id: u32 = if ext.version == IpmaVersion::V0 {
152                u16::decode(buf)? as u32
153            } else {
154                u32::decode(buf)?
155            };
156            let mut associations = vec![];
157            let association_count = u8::decode(buf)?;
158            // The duplicate use of i in the standard is apparently fixed in Ed 8.
159            // See https://github.com/MPEGGroup/FileFormat/issues/86
160            for _j in 0..association_count {
161                if ext.prop_index_15_bits {
162                    let flag_and_prop_index = u16::decode(buf)?;
163                    let essential = (flag_and_prop_index & 0x8000) == 0x8000;
164                    let property_index = flag_and_prop_index & 0x7fff;
165                    associations.push(PropertyAssociation {
166                        essential,
167                        property_index,
168                    });
169                } else {
170                    let flag_and_prop_index = u8::decode(buf)?;
171                    let essential = (flag_and_prop_index & 0x80) == 0x80;
172                    let property_index = (flag_and_prop_index & 0x7f) as u16;
173                    associations.push(PropertyAssociation {
174                        essential,
175                        property_index,
176                    });
177                }
178            }
179            item_properties.push(PropertyAssociations {
180                item_id,
181                associations,
182            });
183        }
184        Ok(Self { item_properties })
185    }
186}