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