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