mp4_atom/meta/
iloc.rs

1use crate::*;
2
3// ItemInformationBox. ISO/IEC 14496-12:2022 Section 8.11.3
4// This is used to work out where the items are
5
6ext! {
7    name: Iloc,
8    versions: [0, 1, 2],
9    flags: {}
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct ItemLocationExtent {
15    pub item_reference_index: u64,
16    pub offset: u64,
17    pub length: u64,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct ItemLocation {
23    pub item_id: u32,
24    pub construction_method: u8, // enum?
25    pub data_reference_index: u16,
26    pub base_offset: u64,
27    pub extents: Vec<ItemLocationExtent>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct Iloc {
33    pub item_locations: Vec<ItemLocation>,
34}
35
36impl AtomExt for Iloc {
37    type Ext = IlocExt;
38
39    const KIND_EXT: FourCC = FourCC::new(b"iloc");
40
41    fn decode_body_ext<B: Buf>(buf: &mut B, ext: IlocExt) -> Result<Self> {
42        let sizes0 = u8::decode(buf)?;
43        let offset_size = sizes0 >> 4;
44        let length_size = sizes0 & 0x0F;
45        let sizes1 = u8::decode(buf)?;
46        let base_offset_size = sizes1 >> 4;
47        let index_size: u8 = if ext.version == IlocVersion::V1 || ext.version == IlocVersion::V2 {
48            sizes1 & 0x0F
49        } else {
50            0
51        };
52
53        let item_count = if ext.version == IlocVersion::V0 || ext.version == IlocVersion::V1 {
54            u16::decode(buf)? as usize
55        } else {
56            u32::decode(buf)? as usize
57        };
58        let mut item_locations = vec![];
59        for _i in 0..item_count {
60            let item_id = if ext.version == IlocVersion::V0 || ext.version == IlocVersion::V1 {
61                u16::decode(buf)? as u32
62            } else {
63                u32::decode(buf)?
64            };
65            let construction_method: u8 =
66                if ext.version == IlocVersion::V1 || ext.version == IlocVersion::V2 {
67                    let construction_method_packed = u16::decode(buf)?;
68                    (construction_method_packed & 0x0f) as u8
69                } else {
70                    0
71                };
72            let data_reference_index = u16::decode(buf)?;
73            let base_offset = match base_offset_size {
74                0 => 0u64,
75                4 => u32::decode(buf)? as u64,
76                8 => u64::decode(buf)?,
77                _ => panic!("iloc base_offset_size must be in [0,4,8]"),
78            };
79            let extent_count = u16::decode(buf)?;
80            let mut extents = vec![];
81            for _j in 0..extent_count {
82                let item_reference_index: u64 =
83                    if ext.version == IlocVersion::V1 || ext.version == IlocVersion::V2 {
84                        match index_size {
85                            0 => 0,
86                            4 => u32::decode(buf)? as u64,
87                            8 => u64::decode(buf)?,
88                            _ => panic!("iloc index_size must be in [0,4,8]"),
89                        }
90                    } else {
91                        0
92                    };
93                let extent_offset = match offset_size {
94                    0 => 0u64,
95                    4 => u32::decode(buf)? as u64,
96                    8 => u64::decode(buf)?,
97                    _ => panic!("iloc offset_size must be in [0,4,8]"),
98                };
99                let extent_length = match length_size {
100                    0 => 0u64,
101                    4 => u32::decode(buf)? as u64,
102                    8 => u64::decode(buf)?,
103                    _ => panic!("iloc length_size must be in [0,4,8]"),
104                };
105                extents.push(ItemLocationExtent {
106                    item_reference_index,
107                    offset: extent_offset,
108                    length: extent_length,
109                });
110            }
111            item_locations.push(ItemLocation {
112                item_id,
113                construction_method,
114                data_reference_index,
115                base_offset,
116                extents,
117            })
118        }
119        Ok(Iloc { item_locations })
120    }
121
122    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<IlocExt> {
123        let mut base_offset_size = 0u8;
124        // TODO: work out which version and sizes we really need for this instance.
125        for item_location in &self.item_locations {
126            if item_location.base_offset > 0 {
127                if item_location.base_offset > u32::MAX as u64 {
128                    base_offset_size = 8u8;
129                } else if base_offset_size != 8 {
130                    base_offset_size = 4u8;
131                }
132            }
133        }
134        let version = IlocVersion::V0;
135        let offset_size = 4u8;
136        let length_size = 4u8;
137
138        let index_size = 0u8;
139        let size0 = (offset_size << 4) | length_size;
140        let size1 = (base_offset_size << 4) | index_size;
141        size0.encode(buf)?;
142        size1.encode(buf)?;
143        if version == IlocVersion::V0 || version == IlocVersion::V1 {
144            (self.item_locations.len() as u16).encode(buf)?;
145        } else {
146            (self.item_locations.len() as u32).encode(buf)?;
147        }
148        for item_location in &self.item_locations {
149            if version == IlocVersion::V0 || version == IlocVersion::V1 {
150                (item_location.item_id as u16).encode(buf)?;
151            } else {
152                item_location.item_id.encode(buf)?;
153            }
154            if version == IlocVersion::V1 || version == IlocVersion::V2 {
155                (item_location.construction_method as u16).encode(buf)?
156            }
157            item_location.data_reference_index.encode(buf)?;
158            match base_offset_size {
159                0 => {}
160                4 => (item_location.base_offset as u32).encode(buf)?,
161                8 => item_location.base_offset.encode(buf)?,
162                _ => unreachable!("iloc base_offset_size must be in [0,4,8]"),
163            }
164            (item_location.extents.len() as u16).encode(buf)?;
165            for extent in &item_location.extents {
166                match index_size {
167                    0 => {}
168                    4 => (extent.item_reference_index as u32).encode(buf)?,
169                    8 => extent.item_reference_index.encode(buf)?,
170                    _ => unreachable!("iloc index_size must be in [0,4,8]"),
171                }
172                match offset_size {
173                    0 => {}
174                    4 => (extent.offset as u32).encode(buf)?,
175                    8 => extent.offset.encode(buf)?,
176                    _ => unreachable!("iloc offset_size must be in [0,4,8]"),
177                }
178                match length_size {
179                    0 => {}
180                    4 => (extent.length as u32).encode(buf)?,
181                    8 => extent.length.encode(buf)?,
182                    _ => unreachable!("iloc length_size must be in [0,4,8]"),
183                }
184            }
185        }
186        Ok(IlocExt {
187            version: IlocVersion::V0,
188        })
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    const ENCODED_ILOC_LIBAVIF: &[u8] = &[
197        0x00, 0x00, 0x00, 0x1e, 0x69, 0x6c, 0x6f, 0x63, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
198        0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x1a,
199    ];
200
201    #[test]
202    fn test_iloc_libavif_decode() {
203        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_ILOC_LIBAVIF);
204
205        let iloc: Iloc = Iloc {
206            item_locations: vec![ItemLocation {
207                item_id: 1,
208                construction_method: 0,
209                data_reference_index: 0,
210                base_offset: 0,
211                extents: vec![ItemLocationExtent {
212                    item_reference_index: 0,
213                    offset: 312,
214                    length: 26,
215                }],
216            }],
217        };
218        let decoded = Iloc::decode(buf).unwrap();
219        assert_eq!(decoded, iloc);
220    }
221
222    #[test]
223    fn test_iloc_avif_encode() {
224        let iloc: Iloc = Iloc {
225            item_locations: vec![ItemLocation {
226                item_id: 1,
227                construction_method: 0,
228                data_reference_index: 0,
229                base_offset: 0,
230                extents: vec![ItemLocationExtent {
231                    item_reference_index: 0,
232                    offset: 312,
233                    length: 26,
234                }],
235            }],
236        };
237        let mut buf = Vec::new();
238        iloc.encode(&mut buf).unwrap();
239
240        assert_eq!(buf.as_slice(), ENCODED_ILOC_LIBAVIF);
241    }
242}