• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use std::num::NonZero;
22 
23 #[derive(Debug, Default)]
24 pub struct Item {
25     pub id: u32,
26     pub item_type: String,
27     pub size: usize,
28     pub width: u32,
29     pub height: u32,
30     pub content_type: String,
31     pub properties: Vec<ItemProperty>,
32     pub extents: Vec<Extent>,
33     pub thumbnail_for_id: u32,
34     pub aux_for_id: u32,
35     pub desc_for_id: u32,
36     pub dimg_for_id: u32,
37     pub dimg_index: u32,
38     pub prem_by_id: u32,
39     pub has_unsupported_essential_property: bool,
40     pub progressive: bool,
41     pub idat: Vec<u8>,
42     // Item ids of source items of a derived image item, in the same order as
43     // they appear in the `dimg` box. E.g. item ids for the cells of a grid
44     // item, or for the layers of an overlay item.
45     pub source_item_ids: Vec<u32>,
46     pub data_buffer: Option<Vec<u8>>,
47     pub is_made_up: bool, // Placeholder grid alpha item if true.
48 }
49 
50 macro_rules! find_property {
51     ($properties:expr, $property_name:ident) => {
52         $properties.iter().find_map(|p| match p {
53             ItemProperty::$property_name(value) => Some(value),
54             _ => None,
55         })
56     };
57 }
58 
59 impl Item {
stream<'a>(&'a mut self, io: &'a mut GenericIO) -> AvifResult<IStream<'a>>60     pub(crate) fn stream<'a>(&'a mut self, io: &'a mut GenericIO) -> AvifResult<IStream<'a>> {
61         if !self.idat.is_empty() {
62             match self.extents.len() {
63                 0 => return Err(AvifError::UnknownError("no extent".into())),
64                 1 => {
65                     let idat = self.idat.as_slice();
66                     let offset = usize_from_u64(self.extents[0].offset)?;
67                     let range = offset..checked_add!(offset, self.size)?;
68                     check_slice_range(idat.len(), &range)?;
69                     return Ok(IStream::create(&idat[range]));
70                 }
71                 _ => {
72                     return Err(AvifError::UnknownError(
73                         "idat with multiple extents is not supported".into(),
74                     ));
75                 }
76             }
77         }
78 
79         let io_data = match self.extents.len() {
80             0 => return Err(AvifError::UnknownError("no extent".into())),
81             1 => io.read_exact(self.extents[0].offset, self.size)?,
82             _ => {
83                 if self.data_buffer.is_none() {
84                     // Decoder::prepare_sample() will merge the extents the same way but only for
85                     // image items. It may be necessary here for Exif/XMP metadata for example.
86                     let mut data_buffer: Vec<u8> = create_vec_exact(self.size)?;
87                     for extent in &self.extents {
88                         data_buffer.extend_from_slice(io.read_exact(extent.offset, extent.size)?);
89                     }
90                     self.data_buffer = Some(data_buffer);
91                 }
92                 self.data_buffer.as_ref().unwrap().as_slice()
93             }
94         };
95         Ok(IStream::create(io_data))
96     }
97 
validate_derived_image_dimensions( width: u32, height: u32, size_limit: Option<NonZero<u32>>, dimension_limit: Option<NonZero<u32>>, ) -> AvifResult<()>98     fn validate_derived_image_dimensions(
99         width: u32,
100         height: u32,
101         size_limit: Option<NonZero<u32>>,
102         dimension_limit: Option<NonZero<u32>>,
103     ) -> AvifResult<()> {
104         if width == 0 || height == 0 || !check_limits(width, height, size_limit, dimension_limit) {
105             return Err(AvifError::InvalidImageGrid(
106                 "invalid derived image dimensions".into(),
107             ));
108         }
109         Ok(())
110     }
111 
read_and_parse( &mut self, io: &mut GenericIO, grid: &mut Grid, overlay: &mut Overlay, size_limit: Option<NonZero<u32>>, dimension_limit: Option<NonZero<u32>>, ) -> AvifResult<()>112     pub(crate) fn read_and_parse(
113         &mut self,
114         io: &mut GenericIO,
115         grid: &mut Grid,
116         overlay: &mut Overlay,
117         size_limit: Option<NonZero<u32>>,
118         dimension_limit: Option<NonZero<u32>>,
119     ) -> AvifResult<()> {
120         if self.is_grid_item() {
121             let mut stream = self.stream(io)?;
122             // unsigned int(8) version = 0;
123             let version = stream.read_u8()?;
124             if version != 0 {
125                 return Err(AvifError::InvalidImageGrid(
126                     "unsupported version for grid".into(),
127                 ));
128             }
129             // unsigned int(8) flags;
130             let flags = stream.read_u8()?;
131             // unsigned int(8) rows_minus_one;
132             grid.rows = stream.read_u8()? as u32 + 1;
133             // unsigned int(8) columns_minus_one;
134             grid.columns = stream.read_u8()? as u32 + 1;
135             if (flags & 1) == 1 {
136                 // unsigned int(32) output_width;
137                 grid.width = stream.read_u32()?;
138                 // unsigned int(32) output_height;
139                 grid.height = stream.read_u32()?;
140             } else {
141                 // unsigned int(16) output_width;
142                 grid.width = stream.read_u16()? as u32;
143                 // unsigned int(16) output_height;
144                 grid.height = stream.read_u16()? as u32;
145             }
146             Self::validate_derived_image_dimensions(
147                 grid.width,
148                 grid.height,
149                 size_limit,
150                 dimension_limit,
151             )?;
152             if stream.has_bytes_left()? {
153                 return Err(AvifError::InvalidImageGrid(
154                     "found unknown extra bytes in the grid box".into(),
155                 ));
156             }
157         } else if self.is_overlay_item() {
158             let reference_count = self.source_item_ids.len();
159             let mut stream = self.stream(io)?;
160             // unsigned int(8) version = 0;
161             let version = stream.read_u8()?;
162             if version != 0 {
163                 return Err(AvifError::InvalidImageGrid(format!(
164                     "unsupported version {version} for iovl"
165                 )));
166             }
167             // unsigned int(8) flags;
168             let flags = stream.read_u8()?;
169             for j in 0..4 {
170                 // unsigned int(16) canvas_fill_value;
171                 overlay.canvas_fill_value[j] = stream.read_u16()?;
172             }
173             if (flags & 1) == 1 {
174                 // unsigned int(32) output_width;
175                 overlay.width = stream.read_u32()?;
176                 // unsigned int(32) output_height;
177                 overlay.height = stream.read_u32()?;
178             } else {
179                 // unsigned int(16) output_width;
180                 overlay.width = stream.read_u16()? as u32;
181                 // unsigned int(16) output_height;
182                 overlay.height = stream.read_u16()? as u32;
183             }
184             Self::validate_derived_image_dimensions(
185                 overlay.width,
186                 overlay.height,
187                 size_limit,
188                 dimension_limit,
189             )?;
190             for _ in 0..reference_count {
191                 if (flags & 1) == 1 {
192                     // unsigned int(32) horizontal_offset;
193                     overlay.horizontal_offsets.push(stream.read_i32()?);
194                     // unsigned int(32) vertical_offset;
195                     overlay.vertical_offsets.push(stream.read_i32()?);
196                 } else {
197                     // unsigned int(16) horizontal_offset;
198                     overlay.horizontal_offsets.push(stream.read_i16()? as i32);
199                     // unsigned int(16) vertical_offset;
200                     overlay.vertical_offsets.push(stream.read_i16()? as i32);
201                 }
202             }
203             if stream.has_bytes_left()? {
204                 return Err(AvifError::InvalidImageGrid(
205                     "found unknown extra bytes in the iovl box".into(),
206                 ));
207             }
208         }
209         Ok(())
210     }
211 
operating_point(&self) -> u8212     pub(crate) fn operating_point(&self) -> u8 {
213         match find_property!(self.properties, OperatingPointSelector) {
214             Some(operating_point_selector) => *operating_point_selector,
215             _ => 0, // default operating point.
216         }
217     }
218 
harvest_ispe( &mut self, alpha_ispe_required: bool, size_limit: Option<NonZero<u32>>, dimension_limit: Option<NonZero<u32>>, ) -> AvifResult<()>219     pub(crate) fn harvest_ispe(
220         &mut self,
221         alpha_ispe_required: bool,
222         size_limit: Option<NonZero<u32>>,
223         dimension_limit: Option<NonZero<u32>>,
224     ) -> AvifResult<()> {
225         if self.should_skip() {
226             return Ok(());
227         }
228 
229         match find_property!(self.properties, ImageSpatialExtents) {
230             Some(image_spatial_extents) => {
231                 self.width = image_spatial_extents.width;
232                 self.height = image_spatial_extents.height;
233                 if self.width == 0 || self.height == 0 {
234                     return Err(AvifError::BmffParseFailed(
235                         "item id has invalid size.".into(),
236                     ));
237                 }
238                 if !check_limits(
239                     image_spatial_extents.width,
240                     image_spatial_extents.height,
241                     size_limit,
242                     dimension_limit,
243                 ) {
244                     return Err(AvifError::BmffParseFailed(
245                         "item dimensions too large".into(),
246                     ));
247                 }
248             }
249             None => {
250                 // No ispe was found.
251                 if self.is_auxiliary_alpha() {
252                     if alpha_ispe_required {
253                         return Err(AvifError::BmffParseFailed(
254                             "alpha auxiliary image item is missing mandatory ispe".into(),
255                         ));
256                     }
257                 } else {
258                     return Err(AvifError::BmffParseFailed(
259                         "item is missing mandatory ispe property".into(),
260                     ));
261                 }
262             }
263         }
264         Ok(())
265     }
266 
validate_properties(&self, items: &Items, pixi_required: bool) -> AvifResult<()>267     pub(crate) fn validate_properties(&self, items: &Items, pixi_required: bool) -> AvifResult<()> {
268         let codec_config = self
269             .codec_config()
270             .ok_or(AvifError::BmffParseFailed("missing av1C property".into()))?;
271         if self.is_derived_image_item() {
272             for derived_item_id in &self.source_item_ids {
273                 let derived_item = items.get(derived_item_id).unwrap();
274                 let derived_codec_config =
275                     derived_item
276                         .codec_config()
277                         .ok_or(AvifError::BmffParseFailed(
278                             "missing codec config property".into(),
279                         ))?;
280                 if codec_config != derived_codec_config {
281                     return Err(AvifError::BmffParseFailed(
282                         "codec config of derived items do not match".into(),
283                     ));
284                 }
285             }
286         }
287         match self.pixi() {
288             Some(pixi) => {
289                 for depth in &pixi.plane_depths {
290                     if *depth != codec_config.depth() {
291                         return Err(AvifError::BmffParseFailed(
292                             "pixi depth does not match codec config depth".into(),
293                         ));
294                     }
295                 }
296             }
297             None => {
298                 if pixi_required {
299                     return Err(AvifError::BmffParseFailed("missing pixi property".into()));
300                 }
301             }
302         }
303         Ok(())
304     }
305 
codec_config(&self) -> Option<&CodecConfiguration>306     pub(crate) fn codec_config(&self) -> Option<&CodecConfiguration> {
307         find_property!(self.properties, CodecConfiguration)
308     }
309 
pixi(&self) -> Option<&PixelInformation>310     pub(crate) fn pixi(&self) -> Option<&PixelInformation> {
311         find_property!(self.properties, PixelInformation)
312     }
313 
a1lx(&self) -> Option<&[usize; 3]>314     pub(crate) fn a1lx(&self) -> Option<&[usize; 3]> {
315         find_property!(self.properties, AV1LayeredImageIndexing)
316     }
317 
lsel(&self) -> Option<&u16>318     pub(crate) fn lsel(&self) -> Option<&u16> {
319         find_property!(self.properties, LayerSelector)
320     }
321 
clli(&self) -> Option<&ContentLightLevelInformation>322     pub(crate) fn clli(&self) -> Option<&ContentLightLevelInformation> {
323         find_property!(self.properties, ContentLightLevelInformation)
324     }
325 
is_auxiliary_alpha(&self) -> bool326     pub(crate) fn is_auxiliary_alpha(&self) -> bool {
327         matches!(find_property!(&self.properties, AuxiliaryType),
328                  Some(aux_type) if is_auxiliary_type_alpha(aux_type))
329     }
330 
is_image_codec_item(&self) -> bool331     pub(crate) fn is_image_codec_item(&self) -> bool {
332         [
333             "av01",
334             #[cfg(feature = "heic")]
335             "hvc1",
336         ]
337         .contains(&self.item_type.as_str())
338     }
339 
is_grid_item(&self) -> bool340     pub(crate) fn is_grid_item(&self) -> bool {
341         self.item_type == "grid"
342     }
343 
is_overlay_item(&self) -> bool344     pub(crate) fn is_overlay_item(&self) -> bool {
345         self.item_type == "iovl"
346     }
347 
is_derived_image_item(&self) -> bool348     pub(crate) fn is_derived_image_item(&self) -> bool {
349         self.is_grid_item() || self.is_overlay_item() || self.is_tmap()
350     }
351 
is_image_item(&self) -> bool352     pub(crate) fn is_image_item(&self) -> bool {
353         // Adding || self.is_tmap() here would cause differences with libavif.
354         self.is_image_codec_item() || self.is_grid_item() || self.is_overlay_item()
355     }
356 
should_skip(&self) -> bool357     pub(crate) fn should_skip(&self) -> bool {
358         // The item has no payload in idat or mdat. It cannot be a coded image item, a
359         // non-identity derived image item, or Exif/XMP metadata.
360         self.size == 0
361             // An essential property isn't supported by libavif. Ignore the whole item.
362             || self.has_unsupported_essential_property
363             // Probably Exif/XMP or some other data.
364             || !self.is_image_item()
365             // libavif does not support thumbnails.
366             || self.thumbnail_for_id != 0
367     }
368 
is_metadata(&self, item_type: &str, color_id: Option<u32>) -> bool369     fn is_metadata(&self, item_type: &str, color_id: Option<u32>) -> bool {
370         self.size != 0
371             && !self.has_unsupported_essential_property
372             && (color_id.is_none() || self.desc_for_id == color_id.unwrap())
373             && self.item_type == *item_type
374     }
375 
is_exif(&self, color_id: Option<u32>) -> bool376     pub(crate) fn is_exif(&self, color_id: Option<u32>) -> bool {
377         self.is_metadata("Exif", color_id)
378     }
379 
is_xmp(&self, color_id: Option<u32>) -> bool380     pub(crate) fn is_xmp(&self, color_id: Option<u32>) -> bool {
381         self.is_metadata("mime", color_id) && self.content_type == "application/rdf+xml"
382     }
383 
is_tmap(&self) -> bool384     pub(crate) fn is_tmap(&self) -> bool {
385         self.is_metadata("tmap", None) && self.thumbnail_for_id == 0
386     }
387 
max_extent(&self, sample: &DecodeSample) -> AvifResult<Extent>388     pub(crate) fn max_extent(&self, sample: &DecodeSample) -> AvifResult<Extent> {
389         if !self.idat.is_empty() {
390             return Ok(Extent::default());
391         }
392         if sample.size == 0 {
393             return Err(AvifError::TruncatedData);
394         }
395         let mut remaining_offset = sample.offset;
396         let mut min_offset = u64::MAX;
397         let mut max_offset = 0;
398         if self.extents.is_empty() {
399             return Err(AvifError::TruncatedData);
400         } else if self.extents.len() == 1 {
401             min_offset = sample.offset;
402             max_offset = checked_add!(sample.offset, u64_from_usize(sample.size)?)?;
403         } else {
404             let mut remaining_size = sample.size;
405             for extent in &self.extents {
406                 let mut start_offset = extent.offset;
407                 let mut size = extent.size;
408                 let sizeu64 = u64_from_usize(size)?;
409                 if remaining_offset != 0 {
410                     if remaining_offset >= sizeu64 {
411                         remaining_offset -= sizeu64;
412                         continue;
413                     } else {
414                         checked_incr!(start_offset, remaining_offset);
415                         checked_decr!(size, usize_from_u64(remaining_offset)?);
416                         remaining_offset = 0;
417                     }
418                 }
419                 // TODO(yguyon): Add comment to explain why it is fine to clip the extent size.
420                 let used_extent_size = std::cmp::min(size, remaining_size);
421                 let end_offset = checked_add!(start_offset, u64_from_usize(used_extent_size)?)?;
422                 min_offset = std::cmp::min(min_offset, start_offset);
423                 max_offset = std::cmp::max(max_offset, end_offset);
424                 remaining_size -= used_extent_size;
425                 if remaining_size == 0 {
426                     break;
427                 }
428             }
429             if remaining_size != 0 {
430                 return Err(AvifError::TruncatedData);
431             }
432         }
433         Ok(Extent {
434             offset: min_offset,
435             size: usize_from_u64(checked_sub!(max_offset, min_offset)?)?,
436         })
437     }
438 }
439 
440 pub type Items = BTreeMap<u32, Item>;
441 
insert_item_if_not_exists(id: u32, items: &mut Items)442 fn insert_item_if_not_exists(id: u32, items: &mut Items) {
443     if items.contains_key(&id) {
444         return;
445     }
446     items.insert(
447         id,
448         Item {
449             id,
450             ..Item::default()
451         },
452     );
453 }
454 
construct_items(meta: &MetaBox) -> AvifResult<Items>455 pub(crate) fn construct_items(meta: &MetaBox) -> AvifResult<Items> {
456     let mut items: Items = BTreeMap::new();
457     for iinf in &meta.iinf {
458         items.insert(
459             iinf.item_id,
460             Item {
461                 id: iinf.item_id,
462                 item_type: iinf.item_type.clone(),
463                 content_type: iinf.content_type.clone(),
464                 ..Item::default()
465             },
466         );
467     }
468     for iloc in &meta.iloc.items {
469         insert_item_if_not_exists(iloc.item_id, &mut items);
470         let item = items.get_mut(&iloc.item_id).unwrap();
471         if !item.extents.is_empty() {
472             return Err(AvifError::BmffParseFailed(
473                 "item already has extents".into(),
474             ));
475         }
476         if iloc.construction_method == 1 {
477             item.idat.clone_from(&meta.idat);
478         }
479         for extent in &iloc.extents {
480             item.extents.push(Extent {
481                 offset: checked_add!(iloc.base_offset, extent.offset)?,
482                 size: extent.size,
483             });
484             checked_incr!(item.size, extent.size);
485         }
486     }
487     let mut ipma_seen: HashSet<u32> = HashSet::with_hasher(NonRandomHasherState);
488     for association in &meta.iprp.associations {
489         if association.associations.is_empty() {
490             continue;
491         }
492         if ipma_seen.contains(&association.item_id) {
493             return Err(AvifError::BmffParseFailed(
494                 "item has duplicate ipma entry".into(),
495             ));
496         }
497         ipma_seen.insert(association.item_id);
498 
499         insert_item_if_not_exists(association.item_id, &mut items);
500         let item = items.get_mut(&association.item_id).unwrap();
501         for (property_index_ref, essential_ref) in &association.associations {
502             let property_index: usize = *property_index_ref as usize;
503             let essential = *essential_ref;
504             if property_index == 0 {
505                 if essential {
506                     return Err(AvifError::BmffParseFailed(format!(
507                         "item id {} contains an illegal essential property index 0",
508                         { item.id }
509                     )));
510                 }
511                 continue;
512             }
513             // property_index is 1-based.
514             if property_index > meta.iprp.properties.len() {
515                 return Err(AvifError::BmffParseFailed(
516                     "invalid property_index in ipma".into(),
517                 ));
518             }
519 
520             match (&meta.iprp.properties[property_index - 1], essential) {
521                 (ItemProperty::Unknown(_), true) => item.has_unsupported_essential_property = true,
522                 (ItemProperty::AV1LayeredImageIndexing(_), true) => {
523                     return Err(AvifError::BmffParseFailed(
524                         "invalid essential property".into(),
525                     ));
526                 }
527                 (
528                     ItemProperty::OperatingPointSelector(_)
529                     | ItemProperty::LayerSelector(_)
530                     // MIAF 2019/Amd. 2:2021: Section 7.3.9:
531                     //   All transformative properties associated with coded and derived images
532                     //   shall be marked as essential.
533                     | ItemProperty::CleanAperture(_)
534                     | ItemProperty::ImageRotation(_)
535                     | ItemProperty::ImageMirror(_),
536                     false,
537                 ) => {
538                     return Err(AvifError::BmffParseFailed(
539                         "required essential property not marked as essential".into(),
540                     ));
541                 }
542                 (property, _) => item.properties.push(property.clone()),
543             }
544         }
545     }
546 
547     for reference in &meta.iref {
548         insert_item_if_not_exists(reference.from_item_id, &mut items);
549         let item = items.get_mut(&reference.from_item_id).unwrap();
550         match reference.reference_type.as_str() {
551             "thmb" => item.thumbnail_for_id = reference.to_item_id,
552             "auxl" => item.aux_for_id = reference.to_item_id,
553             "cdsc" => item.desc_for_id = reference.to_item_id,
554             "prem" => item.prem_by_id = reference.to_item_id,
555             "dimg" => {
556                 // derived images refer in the opposite direction.
557                 insert_item_if_not_exists(reference.to_item_id, &mut items);
558                 let dimg_item = items.get_mut(&reference.to_item_id).unwrap();
559                 if dimg_item.dimg_for_id != 0 {
560                     return Err(if dimg_item.dimg_for_id == reference.from_item_id {
561                         // Section 8.11.12.1 of ISO/IEC 14496-12:
562                         //   The items linked to are then represented by an array of to_item_IDs;
563                         //   within a given array, a given value shall occur at most once.
564                         AvifError::BmffParseFailed(format!(
565                             "multiple dimg references for item ID {}",
566                             dimg_item.dimg_for_id
567                         ))
568                     } else {
569                         AvifError::NotImplemented
570                     });
571                 }
572                 dimg_item.dimg_for_id = reference.from_item_id;
573                 dimg_item.dimg_index = reference.index;
574             }
575             _ => {
576                 // unknown reference type, ignore.
577             }
578         }
579     }
580     Ok(items)
581 }
582