• 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::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