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