1use crate::*;
2
3ext! {
7 name: Iinf,
8 versions: [0, 1],
9 flags: {}
10}
11
12ext! {
13 name: ItemInfoEntry,
14 versions: [0, 1, 2, 3],
15 flags: {item_not_in_presentation = 0,}
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct ItemInfoEntry {
21 pub item_id: u32,
22 pub item_protection_index: u16,
23 pub item_type: Option<FourCC>,
24 pub item_name: String,
25 pub content_type: Option<String>,
26 pub content_encoding: Option<String>,
27 pub item_uri_type: Option<String>,
28 pub item_not_in_presentation: bool,
29}
30
31impl AtomExt for ItemInfoEntry {
32 const KIND_EXT: FourCC = FourCC::new(b"infe");
33
34 type Ext = ItemInfoEntryExt;
35
36 fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext> {
37 let version: ItemInfoEntryVersion = if self.item_id > u16::MAX as u32 {
39 ItemInfoEntryVersion::V3
40 } else {
41 if self.item_type.is_some() {
43 ItemInfoEntryVersion::V2
44 } else {
45 ItemInfoEntryVersion::V0
46 }
47 };
48 if (version == ItemInfoEntryVersion::V0) || (version == ItemInfoEntryVersion::V1) {
49 (self.item_id as u16).encode(buf)?;
50 self.item_protection_index.encode(buf)?;
51 self.item_name.as_str().encode(buf)?;
52 self.content_type.clone().unwrap().as_str().encode(buf)?;
53 self.content_encoding
54 .clone()
55 .unwrap_or("".to_string())
56 .as_str()
57 .encode(buf)?;
58 if version == ItemInfoEntryVersion::V1 {
59 unimplemented!("infe extensions are not yet supported");
60 }
61 } else {
62 if version == ItemInfoEntryVersion::V2 {
63 (self.item_id as u16).encode(buf)?;
64 } else {
65 self.item_id.encode(buf)?;
66 }
67 self.item_protection_index.encode(buf)?;
68 Some(self.item_type).encode(buf)?;
69 self.item_name.as_str().encode(buf)?;
70 if self.item_type == Some(FourCC::new(b"mime")) {
71 self.content_type.clone().unwrap().as_str().encode(buf)?;
72 self.content_encoding
73 .clone()
74 .unwrap_or("".to_string())
75 .as_str()
76 .encode(buf)?;
77 } else if self.item_type == Some(FourCC::new(b"uri ")) {
78 let item_uri_type = self.item_uri_type.as_ref().ok_or(Error::MissingContent(
79 "item_uri_type required with 'uri ' item_type",
80 ))?;
81 item_uri_type.as_str().encode(buf)?;
82 }
83 }
84 Ok(ItemInfoEntryExt {
85 version,
86 item_not_in_presentation: self.item_not_in_presentation,
87 })
88 }
89
90 fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self> {
91 let item_id: u32;
92 let item_protection_index;
93 let mut item_type = None;
94 let item_name;
95 let mut content_type = None;
96 let mut content_encoding = None;
97 let mut item_uri_type = None;
98 if (ext.version == ItemInfoEntryVersion::V0) || (ext.version == ItemInfoEntryVersion::V1) {
99 item_id = u16::decode(buf)? as u32;
100 item_protection_index = u16::decode(buf)?;
101 item_name = String::decode(buf)?;
102 content_type = Some(String::decode(buf)?);
103 content_encoding = Some(String::decode(buf)?);
104 if ext.version == ItemInfoEntryVersion::V1 {
105 unimplemented!("infe extensions are not yet supported");
106 }
107 } else {
108 if ext.version == ItemInfoEntryVersion::V2 {
109 item_id = u16::decode(buf)? as u32;
110 } else {
111 item_id = u32::decode(buf)?;
112 }
113 item_protection_index = u16::decode(buf)?;
114 item_type = Some(FourCC::decode(buf)?);
115 item_name = String::decode(buf)?;
116 if item_type == Some(FourCC::new(b"mime")) {
117 content_type = Some(String::decode(buf)?);
118 content_encoding = Some(String::decode(buf)?);
119 } else if item_type == Some(FourCC::new(b"uri ")) {
120 item_uri_type = Some(String::decode(buf)?);
121 }
122 }
123 Ok(ItemInfoEntry {
124 item_id,
125 item_protection_index,
126 item_type,
127 item_name,
128 content_type,
129 content_encoding,
130 item_uri_type,
131 item_not_in_presentation: ext.item_not_in_presentation,
132 })
133 }
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
138pub struct Iinf {
139 pub item_infos: Vec<ItemInfoEntry>,
140}
141
142impl AtomExt for Iinf {
143 type Ext = IinfExt;
144
145 const KIND_EXT: FourCC = FourCC::new(b"iinf");
146
147 fn decode_body_ext<B: Buf>(buf: &mut B, ext: IinfExt) -> Result<Self> {
148 let mut item_infos = vec![];
149 let entry_count = if ext.version == IinfVersion::V0 {
150 u16::decode(buf)? as usize
151 } else {
152 u32::decode(buf)? as usize
153 };
154 for _ in 0..entry_count {
155 item_infos.push(ItemInfoEntry::decode(buf)?);
156 }
157 Ok(Iinf { item_infos })
158 }
159
160 fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<IinfExt> {
161 let version;
162 if self.item_infos.len() > u16::MAX as usize {
163 version = IinfVersion::V1;
164 (self.item_infos.len() as u32).encode(buf)?
165 } else {
166 version = IinfVersion::V0;
167 (self.item_infos.len() as u16).encode(buf)?
168 }
169 for item_info in &self.item_infos {
170 item_info.encode(buf)?;
171 }
172
173 Ok(IinfExt { version })
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 const ENCODED_IINF_LIBAVIF_MIME: &[u8] = &[
182 0, 0, 0, 65, 105, 105, 110, 102, 0, 0, 0, 0, 0, 1, 0, 0, 0, 51, 105, 110, 102, 101, 2, 0,
183 0, 0, 0, 1, 0, 0, 109, 105, 109, 101, 73, 116, 101, 109, 0, 99, 111, 110, 116, 101, 110,
184 116, 45, 116, 121, 112, 101, 0, 117, 110, 107, 110, 111, 119, 110, 47, 109, 105, 109, 101,
185 0,
186 ];
187
188 #[test]
189 fn test_iinf_libavif_decode_mime() {
190 let buf: &mut std::io::Cursor<&&[u8]> =
191 &mut std::io::Cursor::new(&ENCODED_IINF_LIBAVIF_MIME);
192
193 let iinf: Iinf = Iinf {
194 item_infos: vec![ItemInfoEntry {
195 item_id: 1,
196 item_protection_index: 0,
197 item_type: Some(FourCC::new(b"mime")),
198 item_name: "Item".to_string(),
199 content_type: Some("content-type".to_string()),
200 content_encoding: Some("unknown/mime".to_string()),
201 item_uri_type: None,
202 item_not_in_presentation: false,
203 }],
204 };
205 let decoded = Iinf::decode(buf).unwrap();
206 assert_eq!(decoded, iinf);
207 }
208
209 #[test]
210 fn test_iinf_avif_encode_mime() {
211 let iinf: Iinf = Iinf {
212 item_infos: vec![ItemInfoEntry {
213 item_id: 1,
214 item_protection_index: 0,
215 item_type: Some(FourCC::new(b"mime")),
216 item_name: "Item".to_string(),
217 content_type: Some("content-type".to_string()),
218 content_encoding: Some("unknown/mime".to_string()),
219 item_uri_type: None,
220 item_not_in_presentation: false,
221 }],
222 };
223 let mut buf = Vec::new();
224 iinf.encode(&mut buf).unwrap();
225
226 assert_eq!(buf.as_slice(), ENCODED_IINF_LIBAVIF_MIME);
227 }
228
229 const ENCODED_IINF_LIBAVIF_URI: &[u8] = &[
230 0, 0, 0, 50, 105, 105, 110, 102, 0, 0, 0, 0, 0, 1, 0, 0, 0, 36, 105, 110, 102, 101, 2, 0,
231 0, 0, 0, 1, 0, 0, 117, 114, 105, 32, 73, 116, 101, 109, 0, 117, 114, 105, 58, 47, 47, 116,
232 101, 115, 116, 0,
233 ];
234
235 #[test]
236 fn test_iinf_libavif_decode_uri() {
237 let buf: &mut std::io::Cursor<&&[u8]> =
238 &mut std::io::Cursor::new(&ENCODED_IINF_LIBAVIF_URI);
239
240 let iinf: Iinf = Iinf {
241 item_infos: vec![ItemInfoEntry {
242 item_id: 1,
243 item_protection_index: 0,
244 item_type: Some(FourCC::new(b"uri ")),
245 item_name: "Item".to_string(),
246 content_type: None,
247 content_encoding: None,
248 item_uri_type: Some("uri://test".to_string()),
249 item_not_in_presentation: false,
250 }],
251 };
252 let decoded = Iinf::decode(buf).unwrap();
253 assert_eq!(decoded, iinf);
254 }
255
256 #[test]
257 fn test_iinf_avif_encode_uri() {
258 let iinf: Iinf = Iinf {
259 item_infos: vec![ItemInfoEntry {
260 item_id: 1,
261 item_protection_index: 0,
262 item_type: Some(FourCC::new(b"uri ")),
263 item_name: "Item".to_string(),
264 content_type: None,
265 content_encoding: None,
266 item_uri_type: Some("uri://test".to_string()),
267 item_not_in_presentation: false,
268 }],
269 };
270 let mut buf = Vec::new();
271 iinf.encode(&mut buf).unwrap();
272
273 assert_eq!(buf.as_slice(), ENCODED_IINF_LIBAVIF_URI);
274 }
275
276 #[test]
277 fn test_iinf_avif_encode_uri_invalid() {
278 let iinf: Iinf = Iinf {
279 item_infos: vec![ItemInfoEntry {
280 item_id: 1,
281 item_protection_index: 0,
282 item_type: Some(FourCC::new(b"uri ")),
283 item_name: "Item".to_string(),
284 content_type: None,
285 content_encoding: None,
286 item_uri_type: None, item_not_in_presentation: false,
288 }],
289 };
290 let mut buf = Vec::new();
291 assert!(matches!(
292 iinf.encode(&mut buf),
293 Err(Error::MissingContent(_))
294 ));
295 }
296}