Skip to main content

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            if self.property_index > 0x7fff {
60                return Err(Error::TooLarge(Ipma::KIND));
61            }
62            let flag_and_prop_index = if self.essential {
63                0x8000 | self.property_index
64            } else {
65                self.property_index
66            };
67            flag_and_prop_index.encode(buf)
68        } else {
69            let flag_and_prop_index = if self.essential {
70                0x80 | (self.property_index as u8)
71            } else {
72                self.property_index as u8
73            };
74            flag_and_prop_index.encode(buf)
75        }
76    }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Default)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81pub struct PropertyAssociations {
82    pub item_id: u32,
83    pub associations: Vec<PropertyAssociation>,
84}
85
86impl PropertyAssociations {
87    fn encode<B: BufMut>(
88        &self,
89        buf: &mut B,
90        version: IpmaVersion,
91        prop_index_15_bit: bool,
92    ) -> Result<()> {
93        if version == IpmaVersion::V0 {
94            (self.item_id as u16).encode(buf)?;
95        } else {
96            self.item_id.encode(buf)?;
97        }
98        let association_count: u8 = self
99            .associations
100            .len()
101            .try_into()
102            .map_err(|_| Error::TooLarge(Ipma::KIND))?;
103        association_count.encode(buf)?;
104        for association in &self.associations {
105            association.encode(buf, prop_index_15_bit)?;
106        }
107        Ok(())
108    }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Default)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113pub struct Ipma {
114    pub item_properties: Vec<PropertyAssociations>,
115}
116
117ext! {
118    name: Ipma,
119    versions: [0, 1],
120    flags: {
121        prop_index_15_bits = 0,
122    }
123}
124
125impl AtomExt for Ipma {
126    type Ext = IpmaExt;
127
128    const KIND_EXT: FourCC = FourCC::new(b"ipma");
129
130    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext> {
131        let mut version = IpmaVersion::V0;
132        let mut prop_index_15_bit = false;
133        for item_property in &self.item_properties {
134            if item_property.item_id > (u16::MAX as u32) {
135                version = IpmaVersion::V1;
136            }
137            for association in &item_property.associations {
138                if association.property_index > 0x7f {
139                    prop_index_15_bit = true;
140                }
141            }
142        }
143        let entry_count: u32 = self.item_properties.len() as u32;
144        entry_count.encode(buf)?;
145        for item_property in &self.item_properties {
146            item_property.encode(buf, version, prop_index_15_bit)?;
147        }
148        Ok(IpmaExt {
149            version,
150            prop_index_15_bits: prop_index_15_bit,
151        })
152    }
153
154    fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self> {
155        let entry_count = u32::decode(buf)?;
156        let mut item_properties = vec![];
157        for _i in 0..entry_count {
158            let item_id: u32 = if ext.version == IpmaVersion::V0 {
159                u16::decode(buf)? as u32
160            } else {
161                u32::decode(buf)?
162            };
163            let mut associations = vec![];
164            let association_count = u8::decode(buf)?;
165            // The duplicate use of i in the standard is apparently fixed in Ed 8.
166            // See https://github.com/MPEGGroup/FileFormat/issues/86
167            for _j in 0..association_count {
168                if ext.prop_index_15_bits {
169                    let flag_and_prop_index = u16::decode(buf)?;
170                    let essential = (flag_and_prop_index & 0x8000) == 0x8000;
171                    let property_index = flag_and_prop_index & 0x7fff;
172                    associations.push(PropertyAssociation {
173                        essential,
174                        property_index,
175                    });
176                } else {
177                    let flag_and_prop_index = u8::decode(buf)?;
178                    let essential = (flag_and_prop_index & 0x80) == 0x80;
179                    let property_index = (flag_and_prop_index & 0x7f) as u16;
180                    associations.push(PropertyAssociation {
181                        essential,
182                        property_index,
183                    });
184                }
185            }
186            item_properties.push(PropertyAssociations {
187                item_id,
188                associations,
189            });
190        }
191        Ok(Self { item_properties })
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    const IPMA_WITH_15_BIT_INDEX: &[u8] = &[
200        0x00, 0x00, 0x00, 0x15, b'i', b'p', b'm', b'a', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
201        0x01, 0x00, 0x01, 0x01, 0x80, 0x80,
202    ];
203
204    fn ipma(property_index: u16, association_count: usize) -> Ipma {
205        Ipma {
206            item_properties: vec![PropertyAssociations {
207                item_id: 1,
208                associations: vec![
209                    PropertyAssociation {
210                        essential: true,
211                        property_index,
212                    };
213                    association_count
214                ],
215            }],
216        }
217    }
218
219    #[test]
220    fn decode_15_bit_property_index() {
221        let decoded = Ipma::decode(&mut &IPMA_WITH_15_BIT_INDEX[..]).unwrap();
222
223        assert_eq!(decoded, ipma(0x80, 1));
224    }
225
226    #[test]
227    fn encode_15_bit_property_index() {
228        let mut encoded = Vec::new();
229        ipma(0x80, 1).encode(&mut encoded).unwrap();
230
231        assert_eq!(encoded, IPMA_WITH_15_BIT_INDEX);
232    }
233
234    #[test]
235    fn reject_property_index_above_15_bits() {
236        let result = ipma(0x8000, 1).encode(&mut Vec::new());
237
238        assert!(matches!(result, Err(Error::TooLarge(Ipma::KIND))));
239    }
240
241    #[test]
242    fn reject_more_than_255_associations() {
243        let result = ipma(1, 256).encode(&mut Vec::new());
244
245        assert!(matches!(result, Err(Error::TooLarge(Ipma::KIND))));
246    }
247}