• 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::*;
17 
18 use std::num::NonZero;
19 
20 #[derive(Debug, Default)]
21 pub struct DecodeSample {
22     pub item_id: u32, // 1-based. 0 if it comes from a track.
23     pub offset: u64,
24     pub size: usize,
25     pub spatial_id: u8,
26     pub sync: bool,
27 }
28 
29 impl DecodeSample {
partial_data<'a>( &'a self, io: &'a mut Box<impl decoder::IO + ?Sized>, buffer: &'a Option<Vec<u8>>, size: usize, ) -> AvifResult<&'a [u8]>30     pub(crate) fn partial_data<'a>(
31         &'a self,
32         io: &'a mut Box<impl decoder::IO + ?Sized>,
33         buffer: &'a Option<Vec<u8>>,
34         size: usize,
35     ) -> AvifResult<&'a [u8]> {
36         match buffer {
37             Some(x) => {
38                 let start_offset = usize_from_u64(self.offset)?;
39                 let end_offset = checked_add!(start_offset, size)?;
40                 let range = start_offset..end_offset;
41                 check_slice_range(x.len(), &range)?;
42                 Ok(&x[range])
43             }
44             None => {
45                 let data = io.read(self.offset, size)?;
46                 if data.len() != size {
47                     Err(AvifError::TruncatedData)
48                 } else {
49                     Ok(data)
50                 }
51             }
52         }
53     }
54 
data<'a>( &'a self, io: &'a mut Box<impl decoder::IO + ?Sized>, buffer: &'a Option<Vec<u8>>, ) -> AvifResult<&'a [u8]>55     pub(crate) fn data<'a>(
56         &'a self,
57         io: &'a mut Box<impl decoder::IO + ?Sized>,
58         buffer: &'a Option<Vec<u8>>,
59     ) -> AvifResult<&'a [u8]> {
60         self.partial_data(io, buffer, self.size)
61     }
62 }
63 
64 #[derive(Debug, Default)]
65 pub struct DecodeInput {
66     pub samples: Vec<DecodeSample>,
67     pub all_layers: bool,
68     pub category: Category,
69 }
70 
71 #[derive(Debug, Default)]
72 pub struct Overlay {
73     pub canvas_fill_value: [u16; 4],
74     pub width: u32,
75     pub height: u32,
76     pub horizontal_offsets: Vec<i32>,
77     pub vertical_offsets: Vec<i32>,
78 }
79 
80 #[derive(Debug, Default)]
81 pub(crate) struct TileInfo {
82     pub tile_count: u32,
83     pub decoded_tile_count: u32,
84     pub grid: Grid,
85     pub overlay: Overlay,
86 }
87 
88 impl TileInfo {
is_grid(&self) -> bool89     pub(crate) fn is_grid(&self) -> bool {
90         self.grid.rows > 0 && self.grid.columns > 0
91     }
92 
is_overlay(&self) -> bool93     pub(crate) fn is_overlay(&self) -> bool {
94         !self.overlay.horizontal_offsets.is_empty() && !self.overlay.vertical_offsets.is_empty()
95     }
96 
is_derived_image(&self) -> bool97     pub(crate) fn is_derived_image(&self) -> bool {
98         self.is_grid() || self.is_overlay()
99     }
100 
grid_tile_count(&self) -> AvifResult<u32>101     pub(crate) fn grid_tile_count(&self) -> AvifResult<u32> {
102         if self.is_grid() {
103             checked_mul!(self.grid.rows, self.grid.columns)
104         } else {
105             Ok(1)
106         }
107     }
108 
decoded_row_count(&self, image_height: u32, tile_height: u32) -> u32109     pub(crate) fn decoded_row_count(&self, image_height: u32, tile_height: u32) -> u32 {
110         if self.decoded_tile_count == 0 {
111             return 0;
112         }
113         if self.decoded_tile_count == self.tile_count || !self.is_grid() {
114             return image_height;
115         }
116         std::cmp::min(
117             (self.decoded_tile_count / self.grid.columns) * tile_height,
118             image_height,
119         )
120     }
121 
is_fully_decoded(&self) -> bool122     pub(crate) fn is_fully_decoded(&self) -> bool {
123         self.tile_count == self.decoded_tile_count
124     }
125 }
126 
127 #[derive(Default)]
128 pub struct Tile {
129     pub width: u32,
130     pub height: u32,
131     pub operating_point: u8,
132     pub image: Image,
133     pub input: DecodeInput,
134     pub codec_index: usize,
135     pub codec_config: CodecConfiguration,
136 }
137 
138 impl Tile {
create_from_item( item: &mut Item, allow_progressive: bool, image_count_limit: Option<NonZero<u32>>, size_hint: u64, ) -> AvifResult<Tile>139     pub(crate) fn create_from_item(
140         item: &mut Item,
141         allow_progressive: bool,
142         image_count_limit: Option<NonZero<u32>>,
143         size_hint: u64,
144     ) -> AvifResult<Tile> {
145         if size_hint != 0 && item.size as u64 > size_hint {
146             return Err(AvifError::BmffParseFailed("exceeded size_hint".into()));
147         }
148         let mut tile = Tile {
149             width: item.width,
150             height: item.height,
151             operating_point: item.operating_point(),
152             image: Image::default(),
153             codec_config: item
154                 .codec_config()
155                 .ok_or(AvifError::BmffParseFailed("missing av1C property".into()))?
156                 .clone(),
157             ..Tile::default()
158         };
159         let mut layer_sizes: [usize; MAX_AV1_LAYER_COUNT] = [0; MAX_AV1_LAYER_COUNT];
160         let mut layer_count: usize = 0;
161         let a1lx = item.a1lx();
162         let has_a1lx = a1lx.is_some();
163         if let Some(a1lx) = a1lx {
164             let mut remaining_size: usize = item.size;
165             for i in 0usize..3 {
166                 layer_count += 1;
167                 if a1lx[i] > 0 {
168                     // >= instead of > because there must be room for the last layer
169                     if a1lx[i] >= remaining_size {
170                         return Err(AvifError::BmffParseFailed(format!(
171                             "a1lx layer index [{i}] does not fit in item size"
172                         )));
173                     }
174                     layer_sizes[i] = a1lx[i];
175                     remaining_size -= a1lx[i];
176                 } else {
177                     layer_sizes[i] = remaining_size;
178                     remaining_size = 0;
179                     break;
180                 }
181             }
182             if remaining_size > 0 {
183                 assert!(layer_count == 3);
184                 layer_count += 1;
185                 layer_sizes[3] = remaining_size;
186             }
187         }
188         let lsel;
189         let has_lsel;
190         match item.lsel() {
191             Some(x) => {
192                 lsel = *x;
193                 has_lsel = true;
194             }
195             None => {
196                 lsel = 0;
197                 has_lsel = false;
198             }
199         }
200         // Progressive images offer layers via the a1lxProp, but don't specify a layer selection with
201         // lsel.
202         item.progressive = has_a1lx && (!has_lsel || lsel == 0xFFFF);
203         let base_item_offset = if item.extents.len() == 1 { item.extents[0].offset } else { 0 };
204         if has_lsel && lsel != 0xFFFF {
205             // Layer selection. This requires that the underlying AV1 codec decodes all layers, and
206             // then only returns the requested layer as a single frame. To the user of libavif,
207             // this appears to be a single frame.
208             tile.input.all_layers = true;
209             let mut sample_size: usize = 0;
210             let layer_id = usize_from_u16(lsel)?;
211             if layer_count > 0 {
212                 // Optimization: If we're selecting a layer that doesn't require the entire image's
213                 // payload (hinted via the a1lx box).
214                 if layer_id >= layer_count {
215                     return Err(AvifError::InvalidImageGrid(
216                         "lsel layer index not found in a1lx.".into(),
217                     ));
218                 }
219                 let layer_id_plus_1 = layer_id + 1;
220                 for layer_size in layer_sizes.iter().take(layer_id_plus_1) {
221                     checked_incr!(sample_size, *layer_size);
222                 }
223             } else {
224                 // This layer payload subsection is not known. Use the whole payload.
225                 sample_size = item.size;
226             }
227             let sample = DecodeSample {
228                 item_id: item.id,
229                 offset: base_item_offset,
230                 size: sample_size,
231                 spatial_id: lsel as u8,
232                 sync: true,
233             };
234             tile.input.samples.push(sample);
235         } else if item.progressive && allow_progressive {
236             // Progressive image. Decode all layers and expose them all to the
237             // user.
238             if let Some(limit) = image_count_limit {
239                 if layer_count as u32 > limit.get() {
240                     return Err(AvifError::BmffParseFailed(
241                         "exceeded image_count_limit (progressive)".into(),
242                     ));
243                 }
244             }
245             tile.input.all_layers = true;
246             let mut offset = 0;
247             for (i, layer_size) in layer_sizes.iter().take(layer_count).enumerate() {
248                 let sample = DecodeSample {
249                     item_id: item.id,
250                     offset: checked_add!(base_item_offset, offset)?,
251                     size: *layer_size,
252                     spatial_id: 0xff,
253                     sync: i == 0, // Assume all layers depend on the first layer.
254                 };
255                 tile.input.samples.push(sample);
256                 offset = checked_add!(offset, *layer_size as u64)?;
257             }
258         } else {
259             // Typical case: Use the entire item's payload for a single frame output
260             let sample = DecodeSample {
261                 item_id: item.id,
262                 offset: base_item_offset,
263                 size: item.size,
264                 // Legal spatial_id values are [0,1,2,3], so this serves as a sentinel value for
265                 // "do not filter by spatial_id"
266                 spatial_id: 0xff,
267                 sync: true,
268             };
269             tile.input.samples.push(sample);
270         }
271         Ok(tile)
272     }
273 
create_from_track( track: &Track, image_count_limit: Option<NonZero<u32>>, size_hint: u64, category: Category, ) -> AvifResult<Tile>274     pub(crate) fn create_from_track(
275         track: &Track,
276         image_count_limit: Option<NonZero<u32>>,
277         size_hint: u64,
278         category: Category,
279     ) -> AvifResult<Tile> {
280         let properties = track
281             .get_properties()
282             .ok_or(AvifError::BmffParseFailed("".into()))?;
283         let codec_config = find_property!(properties, CodecConfiguration)
284             .ok_or(AvifError::BmffParseFailed("".into()))?
285             .clone();
286         let mut tile = Tile {
287             width: track.width,
288             height: track.height,
289             operating_point: 0, // No way to set operating point via tracks
290             input: DecodeInput {
291                 category,
292                 ..DecodeInput::default()
293             },
294             codec_config,
295             ..Tile::default()
296         };
297         let sample_table = &track.sample_table.unwrap_ref();
298 
299         if let Some(limit) = image_count_limit {
300             let mut limit = limit.get();
301             for (chunk_index, _chunk_offset) in sample_table.chunk_offsets.iter().enumerate() {
302                 // Figure out how many samples are in this chunk.
303                 let sample_count = sample_table.get_sample_count_of_chunk(chunk_index as u32);
304                 if sample_count == 0 {
305                     return Err(AvifError::BmffParseFailed(
306                         "chunk with 0 samples found".into(),
307                     ));
308                 }
309                 if sample_count > limit {
310                     return Err(AvifError::BmffParseFailed(
311                         "exceeded image_count_limit".into(),
312                     ));
313                 }
314                 limit -= sample_count;
315             }
316         }
317 
318         let mut sample_size_index: usize = 0;
319         for (chunk_index, chunk_offset) in sample_table.chunk_offsets.iter().enumerate() {
320             // Figure out how many samples are in this chunk.
321             let sample_count = sample_table.get_sample_count_of_chunk(chunk_index as u32);
322             if sample_count == 0 {
323                 return Err(AvifError::BmffParseFailed(
324                     "chunk with 0 samples found".into(),
325                 ));
326             }
327 
328             let mut sample_offset = *chunk_offset;
329             for _ in 0..sample_count {
330                 let sample_size = sample_table.sample_size(sample_size_index)?;
331                 let sample_size_hint = checked_add!(sample_offset, sample_size as u64)?;
332                 if size_hint != 0 && sample_size_hint > size_hint {
333                     return Err(AvifError::BmffParseFailed("exceeded size_hint".into()));
334                 }
335                 let sample = DecodeSample {
336                     item_id: 0,
337                     offset: sample_offset,
338                     size: sample_size,
339                     // Legal spatial_id values are [0,1,2,3], so this serves as a sentinel value for "do
340                     // not filter by spatial_id"
341                     spatial_id: 0xff,
342                     // Assume first sample is always sync (in case stss box was missing).
343                     sync: tile.input.samples.is_empty(),
344                 };
345                 tile.input.samples.push(sample);
346                 checked_incr!(sample_offset, sample_size as u64);
347                 checked_incr!(sample_size_index, 1);
348             }
349         }
350         for sync_sample_number in &sample_table.sync_samples {
351             let index = usize_from_u32(*sync_sample_number)?;
352             // sample_table.sync_samples is 1-based.
353             if index == 0 || index > tile.input.samples.len() {
354                 return Err(AvifError::BmffParseFailed(format!(
355                     "invalid sync sample number {}",
356                     index
357                 )));
358             }
359             tile.input.samples[index - 1].sync = true;
360         }
361         Ok(tile)
362     }
363 
max_sample_size(&self) -> usize364     pub(crate) fn max_sample_size(&self) -> usize {
365         match self.input.samples.iter().max_by_key(|sample| sample.size) {
366             Some(sample) => sample.size,
367             None => 0,
368         }
369     }
370 }
371