Skip to main content

mp4_atom/meta/
iref.rs

1use crate::*;
2
3// ItemReferenceBox. ISO/IEC 14496-12:2022 Section 8.11.12
4// This is used to work out how the items related to each other
5
6ext! {
7    name: Iref,
8    versions: [0, 1],
9    flags: {}
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Reference {
15    pub reference_type: FourCC,
16    pub from_item_id: u32,
17    pub to_item_ids: Vec<u32>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct Iref {
23    pub references: Vec<Reference>,
24}
25
26impl AtomExt for Iref {
27    type Ext = IrefExt;
28
29    const KIND_EXT: FourCC = FourCC::new(b"iref");
30
31    fn decode_body_ext<B: Buf>(buf: &mut B, ext: IrefExt) -> Result<Self> {
32        let mut references = vec![];
33        while buf.has_remaining() {
34            if buf.remaining() < 4 {
35                return Err(Error::InvalidSize);
36            }
37            let box_len = u32::decode(buf)? as usize;
38            let body_len = box_len.checked_sub(4).ok_or(Error::InvalidSize)?;
39            if body_len > buf.remaining() {
40                return Err(Error::InvalidSize);
41            }
42
43            let mut body = buf.slice(body_len);
44            if ext.version == IrefVersion::V0 {
45                let reference_type = FourCC::decode(&mut body)?;
46                let from_item_id: u32 = u16::decode(&mut body)?.into();
47                let reference_count: u16 = u16::decode(&mut body)?;
48                let mut to_item_ids: Vec<u32> = vec![];
49                for _ in 0..reference_count {
50                    let to_item_id: u32 = u16::decode(&mut body)?.into();
51                    to_item_ids.push(to_item_id);
52                }
53                let reference = Reference {
54                    reference_type,
55                    from_item_id,
56                    to_item_ids,
57                };
58                references.push(reference);
59            } else {
60                let reference_type = FourCC::decode(&mut body)?;
61                let from_item_id: u32 = u32::decode(&mut body)?;
62                let reference_count: u16 = u16::decode(&mut body)?;
63                let mut to_item_ids: Vec<u32> = vec![];
64                for _ in 0..reference_count {
65                    let to_item_id: u32 = u32::decode(&mut body)?;
66                    to_item_ids.push(to_item_id);
67                }
68                let reference = Reference {
69                    reference_type,
70                    from_item_id,
71                    to_item_ids,
72                };
73                references.push(reference);
74            }
75
76            if body.has_remaining() {
77                return Err(Error::InvalidSize);
78            }
79            buf.advance(body_len);
80        }
81        Ok(Iref { references })
82    }
83
84    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<IrefExt> {
85        let mut version = IrefVersion::V0;
86        'reference: for reference in &self.references {
87            if reference.from_item_id > (u16::MAX as u32) {
88                version = IrefVersion::V1;
89                break 'reference;
90            }
91            for id in &reference.to_item_ids {
92                if *id > (u16::MAX as u32) {
93                    version = IrefVersion::V1;
94                    break 'reference;
95                }
96            }
97        }
98        if version == IrefVersion::V0 {
99            for reference in &self.references {
100                let size = (4 + 4 + 2 + 2 + 2 * reference.to_item_ids.len()) as u32;
101                size.encode(buf)?;
102                reference.reference_type.encode(buf)?;
103                (reference.from_item_id as u16).encode(buf)?;
104                (reference.to_item_ids.len() as u16).encode(buf)?;
105                for id in &reference.to_item_ids {
106                    (*id as u16).encode(buf)?;
107                }
108            }
109        } else {
110            for reference in &self.references {
111                let size = (4 + 4 + 4 + 2 + 4 * reference.to_item_ids.len()) as u32;
112                size.encode(buf)?;
113                reference.reference_type.encode(buf)?;
114                reference.from_item_id.encode(buf)?;
115                (reference.to_item_ids.len() as u16).encode(buf)?;
116                for id in &reference.to_item_ids {
117                    id.encode(buf)?;
118                }
119            }
120        }
121        Ok(IrefExt { version })
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn oversized_nested_reference_returns_error() {
131        let body: &[u8] = &[
132            0x00, 0x00, 0x00, 0x20, // nested box length exceeds the iref body
133            b'd', b'i', b'm', b'g', // reference_type
134            0x00, 0x01, // from_item_id
135            0x00, 0x00, // reference_count
136        ];
137
138        assert!(matches!(
139            Iref::decode_body_ext(
140                &mut std::io::Cursor::new(body),
141                IrefExt {
142                    version: IrefVersion::V0,
143                },
144            ),
145            Err(Error::InvalidSize)
146        ));
147    }
148
149    #[test]
150    fn nested_reference_smaller_than_header_returns_error() {
151        let body: &[u8] = &[0x00, 0x00, 0x00, 0x03];
152
153        assert!(matches!(
154            Iref::decode_body_ext(
155                &mut std::io::Cursor::new(body),
156                IrefExt {
157                    version: IrefVersion::V0,
158                },
159            ),
160            Err(Error::InvalidSize)
161        ));
162    }
163}