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::internal_utils::*; 16 use crate::parser::mp4box::ItemProperty; 17 use crate::parser::mp4box::MetaBox; 18 use crate::*; 19 20 use std::num::NonZero; 21 22 #[derive(Clone, Copy, Debug, PartialEq)] 23 pub enum RepetitionCount { 24 Unknown, 25 Infinite, 26 Finite(i32), 27 } 28 29 impl Default for RepetitionCount { default() -> Self30 fn default() -> Self { 31 Self::Finite(0) 32 } 33 } 34 35 #[derive(Debug, Default)] 36 pub struct Track { 37 pub id: u32, 38 pub aux_for_id: Option<u32>, 39 pub prem_by_id: Option<u32>, 40 pub media_timescale: u32, 41 pub media_duration: u64, 42 pub track_duration: u64, 43 pub segment_duration: u64, 44 pub is_repeating: bool, 45 pub width: u32, 46 pub height: u32, 47 pub sample_table: Option<SampleTable>, 48 pub elst_seen: bool, 49 pub meta: Option<MetaBox>, 50 pub handler_type: String, 51 } 52 53 impl Track { check_limits( &self, size_limit: Option<NonZero<u32>>, dimension_limit: Option<NonZero<u32>>, ) -> bool54 pub(crate) fn check_limits( 55 &self, 56 size_limit: Option<NonZero<u32>>, 57 dimension_limit: Option<NonZero<u32>>, 58 ) -> bool { 59 check_limits(self.width, self.height, size_limit, dimension_limit) 60 } 61 has_av1_samples(&self) -> bool62 fn has_av1_samples(&self) -> bool { 63 if let Some(sample_table) = &self.sample_table { 64 self.id != 0 && !sample_table.chunk_offsets.is_empty() && sample_table.has_av1_sample() 65 } else { 66 false 67 } 68 } is_video_handler(&self) -> bool69 pub(crate) fn is_video_handler(&self) -> bool { 70 // Handler types known to be associated with video content. 71 self.handler_type == "pict" || self.handler_type == "vide" || self.handler_type == "auxv" 72 } is_aux(&self, primary_track_id: u32) -> bool73 pub(crate) fn is_aux(&self, primary_track_id: u32) -> bool { 74 // Do not check the track's handler_type. It should be "auxv" according to 75 // HEIF (ISO/IEC 23008-12:2022), Section 7.5.3.1, but old versions of libavif used to write 76 // "pict" instead. 77 self.has_av1_samples() && self.aux_for_id == Some(primary_track_id) 78 } is_color(&self) -> bool79 pub(crate) fn is_color(&self) -> bool { 80 // Do not check the track's handler_type. It should be "pict" according to 81 // HEIF (ISO/IEC 23008-12:2022), Section 7 but some existing files might be using "vide". 82 self.has_av1_samples() && self.aux_for_id.is_none() 83 } 84 is_auxiliary_alpha(&self) -> bool85 pub(crate) fn is_auxiliary_alpha(&self) -> bool { 86 if let Some(properties) = self.get_properties() { 87 if let Some(aux_type) = &find_property!(properties, AuxiliaryType) { 88 return is_auxiliary_type_alpha(aux_type); 89 } 90 } 91 true // Assume alpha if no type is present 92 } 93 get_properties(&self) -> Option<&Vec<ItemProperty>>94 pub(crate) fn get_properties(&self) -> Option<&Vec<ItemProperty>> { 95 self.sample_table.as_ref()?.get_properties() 96 } 97 repetition_count(&self) -> AvifResult<RepetitionCount>98 pub(crate) fn repetition_count(&self) -> AvifResult<RepetitionCount> { 99 if !self.elst_seen { 100 return Ok(RepetitionCount::Unknown); 101 } 102 if self.is_repeating { 103 if self.track_duration == u64::MAX { 104 // If isRepeating is true and the track duration is unknown/indefinite, then set the 105 // repetition count to infinite(Section 9.6.1 of ISO/IEC 23008-12 Part 12). 106 return Ok(RepetitionCount::Infinite); 107 } else { 108 // Section 9.6.1. of ISO/IEC 23008-12 Part 12: 1, the entire edit list is repeated a 109 // sufficient number of times to equal the track duration. 110 // 111 // Since libavif uses repetitionCount (which is 0-based), we subtract the value by 1 112 // to derive the number of repetitions. 113 assert!(self.segment_duration != 0); 114 // We specifically check for trackDuration == 0 here and not when it is actually 115 // read in order to accept files which inadvertently has a trackDuration of 0 116 // without any edit lists. 117 if self.track_duration == 0 { 118 return Err(AvifError::BmffParseFailed( 119 "invalid track duration 0".into(), 120 )); 121 } 122 let repetition_count: u64 = self.track_duration.div_ceil(self.segment_duration) - 1; 123 return match i32::try_from(repetition_count) { 124 Ok(value) => Ok(RepetitionCount::Finite(value)), 125 Err(_) => Ok(RepetitionCount::Infinite), 126 }; 127 } 128 } 129 Ok(RepetitionCount::Finite(0)) 130 } 131 image_timing(&self, image_index: u32) -> AvifResult<ImageTiming>132 pub(crate) fn image_timing(&self, image_index: u32) -> AvifResult<ImageTiming> { 133 let sample_table = self.sample_table.unwrap_ref(); 134 let mut image_timing = ImageTiming { 135 timescale: self.media_timescale as u64, 136 pts_in_timescales: 0, 137 ..ImageTiming::default() 138 }; 139 for i in 0..image_index as usize { 140 checked_incr!( 141 image_timing.pts_in_timescales, 142 sample_table.image_delta(i)? as u64 143 ); 144 } 145 image_timing.duration_in_timescales = 146 sample_table.image_delta(image_index as usize)? as u64; 147 if image_timing.timescale > 0 { 148 image_timing.pts = 149 image_timing.pts_in_timescales as f64 / image_timing.timescale as f64; 150 image_timing.duration = 151 image_timing.duration_in_timescales as f64 / image_timing.timescale as f64; 152 } else { 153 image_timing.pts = 0.0; 154 image_timing.duration = 0.0; 155 } 156 Ok(image_timing) 157 } 158 } 159 160 #[derive(Debug)] 161 pub struct TimeToSample { 162 pub sample_count: u32, 163 pub sample_delta: u32, 164 } 165 166 // Section 8.7.4.3 of ISO/IEC 14496-12. 167 #[derive(Debug)] 168 pub struct SampleToChunk { 169 pub first_chunk: u32, // 1-based 170 pub samples_per_chunk: u32, 171 pub sample_description_index: u32, // 1-based 172 } 173 174 #[derive(Debug, Default)] 175 pub struct SampleDescription { 176 pub format: String, 177 pub properties: Vec<ItemProperty>, 178 } 179 180 impl SampleDescription { is_supported_format(&self) -> bool181 pub(crate) fn is_supported_format(&self) -> bool { 182 [ 183 "av01", 184 #[cfg(feature = "heic")] 185 "hvc1", 186 ] 187 .contains(&self.format.as_str()) 188 } 189 } 190 191 #[derive(Debug)] 192 pub enum SampleSize { 193 FixedSize(u32), 194 Sizes(Vec<u32>), 195 } 196 197 impl Default for SampleSize { default() -> Self198 fn default() -> Self { 199 Self::FixedSize(0) 200 } 201 } 202 203 #[derive(Debug, Default)] 204 pub struct SampleTable { 205 pub chunk_offsets: Vec<u64>, 206 pub sample_to_chunk: Vec<SampleToChunk>, 207 pub sample_size: SampleSize, 208 pub sync_samples: Vec<u32>, 209 pub time_to_sample: Vec<TimeToSample>, 210 pub sample_descriptions: Vec<SampleDescription>, 211 } 212 213 impl SampleTable { has_av1_sample(&self) -> bool214 pub(crate) fn has_av1_sample(&self) -> bool { 215 self.sample_descriptions 216 .iter() 217 .any(|x| x.is_supported_format()) 218 } 219 220 // returns the number of samples in the chunk. get_sample_count_of_chunk(&self, chunk_index: u32) -> u32221 pub(crate) fn get_sample_count_of_chunk(&self, chunk_index: u32) -> u32 { 222 for entry in self.sample_to_chunk.iter().rev() { 223 if entry.first_chunk <= chunk_index + 1 { 224 return entry.samples_per_chunk; 225 } 226 } 227 0 228 } 229 get_properties(&self) -> Option<&Vec<ItemProperty>>230 pub(crate) fn get_properties(&self) -> Option<&Vec<ItemProperty>> { 231 Some( 232 &self 233 .sample_descriptions 234 .iter() 235 .find(|x| x.is_supported_format())? 236 .properties, 237 ) 238 } 239 sample_size(&self, index: usize) -> AvifResult<usize>240 pub(crate) fn sample_size(&self, index: usize) -> AvifResult<usize> { 241 usize_from_u32(match &self.sample_size { 242 SampleSize::FixedSize(size) => *size, 243 SampleSize::Sizes(sizes) => { 244 if index >= sizes.len() { 245 return Err(AvifError::BmffParseFailed( 246 "not enough sampel sizes in the table".into(), 247 )); 248 } 249 sizes[index] 250 } 251 }) 252 } 253 image_delta(&self, index: usize) -> AvifResult<u32>254 pub(crate) fn image_delta(&self, index: usize) -> AvifResult<u32> { 255 let mut max_index: u32 = 0; 256 for (i, time_to_sample) in self.time_to_sample.iter().enumerate() { 257 checked_incr!(max_index, time_to_sample.sample_count); 258 if index < max_index as usize || i == self.time_to_sample.len() - 1 { 259 return Ok(time_to_sample.sample_delta); 260 } 261 } 262 Ok(1) 263 } 264 } 265 266 /// cbindgen:rename-all=CamelCase 267 #[repr(C)] 268 #[derive(Clone, Copy, Debug, Default)] 269 pub struct ImageTiming { 270 pub timescale: u64, 271 pub pts: f64, 272 pub pts_in_timescales: u64, 273 pub duration: f64, 274 pub duration_in_timescales: u64, 275 } 276