1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::decoder::*;
16 use crate::internal_utils::stream::*;
17 use crate::parser::mp4box::*;
18 use crate::*;
19 
20 use std::collections::BTreeMap;
21 
22 #[derive(Debug, Default)]
23 pub struct Item {
24     pub id: u32,
25     pub item_type: String,
26     pub size: usize,
27     pub width: u32,
28     pub height: u32,
29     pub content_type: String,
30     pub properties: Vec<ItemProperty>,
31     pub extents: Vec<Extent>,
32     pub thumbnail_for_id: u32,
33     pub aux_for_id: u32,
34     pub desc_for_id: u32,
35     pub dimg_for_id: u32,
36     pub dimg_index: u32,
37     pub prem_by_id: u32,
38     pub has_unsupported_essential_property: bool,
39     pub progressive: bool,
40     pub idat: Vec<u8>,
41     pub grid_item_ids: Vec<u32>,
42     pub data_buffer: Option<Vec<u8>>,
43     pub is_made_up: bool, // Placeholder grid alpha item if true.
44 }
45 
46 macro_rules! find_property {
47     ($properties:expr, $property_name:ident) => {
48         $properties.iter().find_map(|p| match p {
49             ItemProperty::$property_name(value) => Some(value),
50             _ => None,
51         })
52     };
53 }
54 
55 impl Item {
stream<'a>(&'a mut self, io: &'a mut GenericIO) -> AvifResult<IStream<'a>>56     pub fn stream<'a>(&'a mut self, io: &'a mut GenericIO) -> AvifResult<IStream<'a>> {
57         if !self.idat.is_empty() {
58             match self.extents.len() {
59                 0 => return Err(AvifError::UnknownError("no extent".into())),
60                 1 => {
61                     let idat = self.idat.as_slice();
62                     let offset = usize_from_u64(self.extents[0].offset)?;
63                     let range = offset..checked_add!(offset, self.size)?;
64                     check_slice_range(idat.len(), &range)?;
65                     return Ok(IStream::create(&idat[range]));
66                 }
67                 _ => {
68                     return Err(AvifError::UnknownError(
69                         "idat with multiple extents is not supported".into(),
70                     ));
71                 }
72             }
73         }
74 
75         let io_data = match self.extents.len() {
76             0 => return Err(AvifError::UnknownError("no extent".into())),
77             1 => io.read_exact(self.extents[0].offset, self.size)?,
78             _ => {
79                 if self.data_buffer.is_none() {
80                     // Decoder::prepare_sample() will merge the extents the same way but only for
81                     // image items. It may be necessary here for Exif/XMP metadata for example.
82                     let mut data_buffer: Vec<u8> = create_vec_exact(self.size)?;
83                     for extent in &self.extents {
84                         data_buffer.extend_from_slice(io.read_exact(extent.offset, extent.size)?);
85                     }
86                     self.data_buffer = Some(data_buffer);
87                 }
88                 self.data_buffer.as_ref().unwrap().as_slice()
89             }
90         };
91         Ok(IStream::create(io_data))
92     }
93 
read_and_parse( &mut self, io: &mut GenericIO, grid: &mut Grid, size_limit: u32, dimension_limit: u32, ) -> AvifResult<()>94     pub fn read_and_parse(
95         &mut self,
96         io: &mut GenericIO,
97         grid: &mut Grid,
98         size_limit: u32,
99         dimension_limit: u32,
100     ) -> AvifResult<()> {
101         if self.item_type != "grid" {
102             return Ok(());
103         }
104         let mut stream = self.stream(io)?;
105         // unsigned int(8) version = 0;
106         let version = stream.read_u8()?;
107         if version != 0 {
108             return Err(AvifError::InvalidImageGrid(
109                 "unsupported version for grid".into(),
110             ));
111         }
112         // unsigned int(8) flags;
113         let flags = stream.read_u8()?;
114         // unsigned int(8) rows_minus_one;
115         grid.rows = stream.read_u8()? as u32 + 1;
116         // unsigned int(8) columns_minus_one;
117         grid.columns = stream.read_u8()? as u32 + 1;
118         if (flags & 1) == 1 {
119             // unsigned int(32) output_width;
120             grid.width = stream.read_u32()?;
121             // unsigned int(32) output_height;
122             grid.height = stream.read_u32()?;
123         } else {
124             // unsigned int(16) output_width;
125             grid.width = stream.read_u16()? as u32;
126             // unsigned int(16) output_height;
127             grid.height = stream.read_u16()? as u32;
128         }
129         if grid.width == 0 || grid.height == 0 {
130             return Err(AvifError::InvalidImageGrid(
131                 "invalid dimensions in grid box".into(),
132             ));
133         }
134         if !check_limits(grid.width, grid.height, size_limit, dimension_limit) {
135             return Err(AvifError::InvalidImageGrid(
136                 "grid dimensions too large".into(),
137             ));
138         }
139         if stream.has_bytes_left()? {
140             return Err(AvifError::InvalidImageGrid(
141                 "found unknown extra bytes in the grid box".into(),
142             ));
143         }
144         Ok(())
145     }
146 
operating_point(&self) -> u8147     pub fn operating_point(&self) -> u8 {
148         match find_property!(self.properties, OperatingPointSelector) {
149             Some(operating_point_selector) => *operating_point_selector,
150             _ => 0, // default operating point.
151         }
152     }
153 
harvest_ispe( &mut self, alpha_ispe_required: bool, size_limit: u32, dimension_limit: u32, ) -> AvifResult<()>154     pub fn harvest_ispe(
155         &mut self,
156         alpha_ispe_required: bool,
157         size_limit: u32,
158         dimension_limit: u32,
159     ) -> AvifResult<()> {
160         if self.should_skip() {
161             return Ok(());
162         }
163 
164         match find_property!(self.properties, ImageSpatialExtents) {
165             Some(image_spatial_extents) => {
166                 self.width = image_spatial_extents.width;
167                 self.height = image_spatial_extents.height;
168                 if self.width == 0 || self.height == 0 {
169                     return Err(AvifError::BmffParseFailed(
170                         "item id has invalid size.".into(),
171                     ));
172                 }
173                 if !check_limits(
174                     image_spatial_extents.width,
175                     image_spatial_extents.height,
176                     size_limit,
177                     dimension_limit,
178                 ) {
179                     return Err(AvifError::BmffParseFailed(
180                         "item dimensions too large".into(),
181                     ));
182                 }
183             }
184             None => {
185                 // No ispe was found.
186                 if self.is_auxiliary_alpha() {
187                     if alpha_ispe_required {
188                         return Err(AvifError::BmffParseFailed(
189                             "alpha auxiliary image item is missing mandatory ispe".into(),
190                         ));
191                     }
192                 } else {
193                     return Err(AvifError::BmffParseFailed(
194                         "item is missing mandatory ispe property".into(),
195                     ));
196                 }
197             }
198         }
199         Ok(())
200     }
201 
validate_properties(&self, items: &Items, pixi_required: bool) -> AvifResult<()>202     pub fn validate_properties(&self, items: &Items, pixi_required: bool) -> AvifResult<()> {
203         let codec_config = self
204             .codec_config()
205             .ok_or(AvifError::BmffParseFailed("missing av1C property".into()))?;
206         if self.item_type == "grid" {
207             for grid_item_id in &self.grid_item_ids {
208                 let grid_item = items.get(grid_item_id).unwrap();
209                 let grid_codec_config = grid_item.codec_config().ok_or(
210                     AvifError::BmffParseFailed("missing codec config property".into()),
211                 )?;
212                 if codec_config != grid_codec_config {
213                     return Err(AvifError::BmffParseFailed(
214                         "codec config of grid items do not match".into(),
215                     ));
216                 }
217             }
218         }
219         match self.pixi() {
220             Some(pixi) => {
221                 for depth in &pixi.plane_depths {
222                     if *depth != codec_config.depth() {
223                         return Err(AvifError::BmffParseFailed(
224                             "pixi depth does not match codec config depth".into(),
225                         ));
226                     }
227                 }
228             }
229             None => {
230                 if pixi_required {
231                     return Err(AvifError::BmffParseFailed("missing pixi property".into()));
232                 }
233             }
234         }
235         Ok(())
236     }
237 
codec_config(&self) -> Option<&CodecConfiguration>238     pub fn codec_config(&self) -> Option<&CodecConfiguration> {
239         find_property!(self.properties, CodecConfiguration)
240     }
241 
pixi(&self) -> Option<&PixelInformation>242     pub fn pixi(&self) -> Option<&PixelInformation> {
243         find_property!(self.properties, PixelInformation)
244     }
245 
a1lx(&self) -> Option<&[usize; 3]>246     pub fn a1lx(&self) -> Option<&[usize; 3]> {
247         find_property!(self.properties, AV1LayeredImageIndexing)
248     }
249 
lsel(&self) -> Option<&u16>250     pub fn lsel(&self) -> Option<&u16> {
251         find_property!(self.properties, LayerSelector)
252     }
253 
clli(&self) -> Option<&ContentLightLevelInformation>254     pub fn clli(&self) -> Option<&ContentLightLevelInformation> {
255         find_property!(self.properties, ContentLightLevelInformation)
256     }
257 
is_auxiliary_alpha(&self) -> bool258     pub fn is_auxiliary_alpha(&self) -> bool {
259         matches!(find_property!(self.properties, AuxiliaryType),
260                  Some(aux_type) if aux_type == "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha" ||
261                                    aux_type == "urn:mpeg:hevc:2015:auxid:1")
262     }
263 
is_image_codec_item(&self) -> bool264     pub fn is_image_codec_item(&self) -> bool {
265         [
266             "av01",
267             #[cfg(feature = "heic")]
268             "hvc1",
269         ]
270         .contains(&self.item_type.as_str())
271     }
272 
is_image_item(&self) -> bool273     pub fn is_image_item(&self) -> bool {
274         self.is_image_codec_item() || self.item_type == "grid"
275     }
276 
should_skip(&self) -> bool277     pub fn should_skip(&self) -> bool {
278         // The item has no payload in idat or mdat. It cannot be a coded image item, a
279         // non-identity derived image item, or Exif/XMP metadata.
280         self.size == 0
281             // An essential property isn't supported by libavif. Ignore the whole item.
282             || self.has_unsupported_essential_property
283             // Probably Exif/XMP or some other data.
284             || !self.is_image_item()
285             // libavif does not support thumbnails.
286             || self.thumbnail_for_id != 0
287     }
288 
is_metadata(&self, item_type: &str, color_id: Option<u32>) -> bool289     fn is_metadata(&self, item_type: &str, color_id: Option<u32>) -> bool {
290         self.size != 0
291             && !self.has_unsupported_essential_property
292             && (color_id.is_none() || self.desc_for_id == color_id.unwrap())
293             && self.item_type == *item_type
294     }
295 
is_exif(&self, color_id: Option<u32>) -> bool296     pub fn is_exif(&self, color_id: Option<u32>) -> bool {
297         self.is_metadata("Exif", color_id)
298     }
299 
is_xmp(&self, color_id: Option<u32>) -> bool300     pub fn is_xmp(&self, color_id: Option<u32>) -> bool {
301         self.is_metadata("mime", color_id) && self.content_type == "application/rdf+xml"
302     }
303 
is_tmap(&self) -> bool304     pub fn is_tmap(&self) -> bool {
305         self.is_metadata("tmap", None) && self.thumbnail_for_id == 0
306     }
307 
max_extent(&self, sample: &DecodeSample) -> AvifResult<Extent>308     pub fn max_extent(&self, sample: &DecodeSample) -> AvifResult<Extent> {
309         if !self.idat.is_empty() {
310             return Ok(Extent::default());
311         }
312         if sample.size == 0 {
313             return Err(AvifError::TruncatedData);
314         }
315         let mut remaining_offset = sample.offset;
316         let mut min_offset = u64::MAX;
317         let mut max_offset = 0;
318         if self.extents.is_empty() {
319             return Err(AvifError::TruncatedData);
320         } else if self.extents.len() == 1 {
321             min_offset = sample.offset;
322             max_offset = checked_add!(sample.offset, u64_from_usize(sample.size)?)?;
323         } else {
324             let mut remaining_size = sample.size;
325             for extent in &self.extents {
326                 let mut start_offset = extent.offset;
327                 let mut size = extent.size;
328                 let sizeu64 = u64_from_usize(size)?;
329                 if remaining_offset != 0 {
330                     if remaining_offset >= sizeu64 {
331                         remaining_offset -= sizeu64;
332                         continue;
333                     } else {
334                         checked_incr!(start_offset, remaining_offset);
335                         checked_decr!(size, usize_from_u64(remaining_offset)?);
336                         remaining_offset = 0;
337                     }
338                 }
339                 // TODO(yguyon): Add comment to explain why it is fine to clip the extent size.
340                 let used_extent_size = std::cmp::min(size, remaining_size);
341                 let end_offset = checked_add!(start_offset, u64_from_usize(used_extent_size)?)?;
342                 min_offset = std::cmp::min(min_offset, start_offset);
343                 max_offset = std::cmp::max(max_offset, end_offset);
344                 remaining_size -= used_extent_size;
345                 if remaining_size == 0 {
346                     break;
347                 }
348             }
349             if remaining_size != 0 {
350                 return Err(AvifError::TruncatedData);
351             }
352         }
353         Ok(Extent {
354             offset: min_offset,
355             size: usize_from_u64(checked_sub!(max_offset, min_offset)?)?,
356         })
357     }
358 }
359 
360 pub type Items = BTreeMap<u32, Item>;
361 
insert_item_if_not_exists(id: u32, items: &mut Items)362 fn insert_item_if_not_exists(id: u32, items: &mut Items) {
363     if items.contains_key(&id) {
364         return;
365     }
366     items.insert(
367         id,
368         Item {
369             id,
370             ..Item::default()
371         },
372     );
373 }
374 
construct_items(meta: &MetaBox) -> AvifResult<Items>375 pub fn construct_items(meta: &MetaBox) -> AvifResult<Items> {
376     let mut items: Items = BTreeMap::new();
377     for iinf in &meta.iinf {
378         items.insert(
379             iinf.item_id,
380             Item {
381                 id: iinf.item_id,
382                 item_type: iinf.item_type.clone(),
383                 content_type: iinf.content_type.clone(),
384                 ..Item::default()
385             },
386         );
387     }
388     for iloc in &meta.iloc.items {
389         insert_item_if_not_exists(iloc.item_id, &mut items);
390         let item = items.get_mut(&iloc.item_id).unwrap();
391         if !item.extents.is_empty() {
392             return Err(AvifError::BmffParseFailed(
393                 "item already has extents".into(),
394             ));
395         }
396         if iloc.construction_method == 1 {
397             item.idat.clone_from(&meta.idat);
398         }
399         for extent in &iloc.extents {
400             item.extents.push(Extent {
401                 offset: checked_add!(iloc.base_offset, extent.offset)?,
402                 size: extent.size,
403             });
404             checked_incr!(item.size, extent.size);
405         }
406     }
407     let mut ipma_seen: HashSet<u32> = HashSet::with_hasher(NonRandomHasherState);
408     for association in &meta.iprp.associations {
409         if association.associations.is_empty() {
410             continue;
411         }
412         if ipma_seen.contains(&association.item_id) {
413             return Err(AvifError::BmffParseFailed(
414                 "item has duplicate ipma entry".into(),
415             ));
416         }
417         ipma_seen.insert(association.item_id);
418 
419         insert_item_if_not_exists(association.item_id, &mut items);
420         let item = items.get_mut(&association.item_id).unwrap();
421         for (property_index_ref, essential_ref) in &association.associations {
422             let property_index: usize = *property_index_ref as usize;
423             let essential = *essential_ref;
424             if property_index == 0 {
425                 // Not associated with any item.
426                 continue;
427             }
428             // property_index is 1-based.
429             if property_index > meta.iprp.properties.len() {
430                 return Err(AvifError::BmffParseFailed(
431                     "invalid property_index in ipma".into(),
432                 ));
433             }
434 
435             match (&meta.iprp.properties[property_index - 1], essential) {
436                 (ItemProperty::Unknown(_), true) => item.has_unsupported_essential_property = true,
437                 (ItemProperty::AV1LayeredImageIndexing(_), true) => {
438                     return Err(AvifError::BmffParseFailed(
439                         "invalid essential property".into(),
440                     ));
441                 }
442                 (
443                     ItemProperty::OperatingPointSelector(_) | ItemProperty::LayerSelector(_),
444                     false,
445                 ) => {
446                     return Err(AvifError::BmffParseFailed(
447                         "required essential property not marked as essential".into(),
448                     ));
449                 }
450                 (property, _) => item.properties.push(property.clone()),
451             }
452         }
453     }
454 
455     for reference in &meta.iref {
456         insert_item_if_not_exists(reference.from_item_id, &mut items);
457         let item = items.get_mut(&reference.from_item_id).unwrap();
458         match reference.reference_type.as_str() {
459             "thmb" => item.thumbnail_for_id = reference.to_item_id,
460             "auxl" => item.aux_for_id = reference.to_item_id,
461             "cdsc" => item.desc_for_id = reference.to_item_id,
462             "prem" => item.prem_by_id = reference.to_item_id,
463             "dimg" => {
464                 // derived images refer in the opposite direction.
465                 insert_item_if_not_exists(reference.to_item_id, &mut items);
466                 let dimg_item = items.get_mut(&reference.to_item_id).unwrap();
467                 if dimg_item.dimg_for_id != 0 {
468                     return Err(if dimg_item.dimg_for_id == reference.from_item_id {
469                         // Section 8.11.12.1 of ISO/IEC 14496-12:
470                         //   The items linked to are then represented by an array of to_item_IDs;
471                         //   within a given array, a given value shall occur at most once.
472                         AvifError::BmffParseFailed(format!(
473                             "multiple dimg references for item ID {}",
474                             dimg_item.dimg_for_id
475                         ))
476                     } else {
477                         AvifError::NotImplemented
478                     });
479                 }
480                 dimg_item.dimg_for_id = reference.from_item_id;
481                 dimg_item.dimg_index = reference.index;
482             }
483             _ => {
484                 // unknown reference type, ignore.
485             }
486         }
487     }
488     Ok(items)
489 }
490