| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use crate::internal_utils::*; |
| use crate::parser::mp4box::ItemProperty; |
| use crate::parser::mp4box::MetaBox; |
| use crate::*; |
| |
| use std::num::NonZero; |
| |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum RepetitionCount { |
| Unknown, |
| Infinite, |
| Finite(i32), |
| } |
| |
| impl Default for RepetitionCount { |
| fn default() -> Self { |
| Self::Finite(0) |
| } |
| } |
| |
| #[derive(Debug, Default)] |
| pub struct Track { |
| pub id: u32, |
| pub aux_for_id: Option<u32>, |
| pub prem_by_id: Option<u32>, |
| pub media_timescale: u32, |
| pub media_duration: u64, |
| pub track_duration: u64, |
| pub segment_duration: u64, |
| pub is_repeating: bool, |
| pub width: u32, |
| pub height: u32, |
| pub sample_table: Option<SampleTable>, |
| pub elst_seen: bool, |
| pub meta: Option<MetaBox>, |
| pub handler_type: String, |
| } |
| |
| impl Track { |
| pub(crate) fn check_limits( |
| &self, |
| size_limit: Option<NonZero<u32>>, |
| dimension_limit: Option<NonZero<u32>>, |
| ) -> bool { |
| check_limits(self.width, self.height, size_limit, dimension_limit) |
| } |
| |
| fn has_av1_samples(&self) -> bool { |
| if let Some(sample_table) = &self.sample_table { |
| self.id != 0 && !sample_table.chunk_offsets.is_empty() && sample_table.has_av1_sample() |
| } else { |
| false |
| } |
| } |
| pub(crate) fn is_video_handler(&self) -> bool { |
| // Handler types known to be associated with video content. |
| self.handler_type == "pict" || self.handler_type == "vide" || self.handler_type == "auxv" |
| } |
| pub(crate) fn is_aux(&self, primary_track_id: u32) -> bool { |
| // Do not check the track's handler_type. It should be "auxv" according to |
| // HEIF (ISO/IEC 23008-12:2022), Section 7.5.3.1, but old versions of libavif used to write |
| // "pict" instead. |
| self.has_av1_samples() && self.aux_for_id == Some(primary_track_id) |
| } |
| pub(crate) fn is_color(&self) -> bool { |
| // Do not check the track's handler_type. It should be "pict" according to |
| // HEIF (ISO/IEC 23008-12:2022), Section 7 but some existing files might be using "vide". |
| self.has_av1_samples() && self.aux_for_id.is_none() |
| } |
| |
| pub(crate) fn is_auxiliary_alpha(&self) -> bool { |
| if let Some(properties) = self.get_properties() { |
| if let Some(aux_type) = &find_property!(properties, AuxiliaryType) { |
| return is_auxiliary_type_alpha(aux_type); |
| } |
| } |
| true // Assume alpha if no type is present |
| } |
| |
| pub(crate) fn get_properties(&self) -> Option<&Vec<ItemProperty>> { |
| self.sample_table.as_ref()?.get_properties() |
| } |
| |
| pub(crate) fn repetition_count(&self) -> AvifResult<RepetitionCount> { |
| if !self.elst_seen { |
| return Ok(RepetitionCount::Unknown); |
| } |
| if self.is_repeating { |
| if self.track_duration == u64::MAX { |
| // If isRepeating is true and the track duration is unknown/indefinite, then set the |
| // repetition count to infinite(Section 9.6.1 of ISO/IEC 23008-12 Part 12). |
| return Ok(RepetitionCount::Infinite); |
| } else { |
| // Section 9.6.1. of ISO/IEC 23008-12 Part 12: 1, the entire edit list is repeated a |
| // sufficient number of times to equal the track duration. |
| // |
| // Since libavif uses repetitionCount (which is 0-based), we subtract the value by 1 |
| // to derive the number of repetitions. |
| assert!(self.segment_duration != 0); |
| // We specifically check for trackDuration == 0 here and not when it is actually |
| // read in order to accept files which inadvertently has a trackDuration of 0 |
| // without any edit lists. |
| if self.track_duration == 0 { |
| return Err(AvifError::BmffParseFailed( |
| "invalid track duration 0".into(), |
| )); |
| } |
| let repetition_count: u64 = self.track_duration.div_ceil(self.segment_duration) - 1; |
| return match i32::try_from(repetition_count) { |
| Ok(value) => Ok(RepetitionCount::Finite(value)), |
| Err(_) => Ok(RepetitionCount::Infinite), |
| }; |
| } |
| } |
| Ok(RepetitionCount::Finite(0)) |
| } |
| |
| pub(crate) fn image_timing(&self, image_index: u32) -> AvifResult<ImageTiming> { |
| let sample_table = self.sample_table.unwrap_ref(); |
| let mut image_timing = ImageTiming { |
| timescale: self.media_timescale as u64, |
| pts_in_timescales: 0, |
| ..ImageTiming::default() |
| }; |
| for i in 0..image_index as usize { |
| checked_incr!( |
| image_timing.pts_in_timescales, |
| sample_table.image_delta(i)? as u64 |
| ); |
| } |
| image_timing.duration_in_timescales = |
| sample_table.image_delta(image_index as usize)? as u64; |
| if image_timing.timescale > 0 { |
| image_timing.pts = |
| image_timing.pts_in_timescales as f64 / image_timing.timescale as f64; |
| image_timing.duration = |
| image_timing.duration_in_timescales as f64 / image_timing.timescale as f64; |
| } else { |
| image_timing.pts = 0.0; |
| image_timing.duration = 0.0; |
| } |
| Ok(image_timing) |
| } |
| } |
| |
| #[derive(Debug)] |
| pub struct TimeToSample { |
| pub sample_count: u32, |
| pub sample_delta: u32, |
| } |
| |
| // Section 8.7.4.3 of ISO/IEC 14496-12. |
| #[derive(Debug)] |
| pub struct SampleToChunk { |
| pub first_chunk: u32, // 1-based |
| pub samples_per_chunk: u32, |
| pub sample_description_index: u32, // 1-based |
| } |
| |
| #[derive(Debug, Default)] |
| pub struct SampleDescription { |
| pub format: String, |
| pub properties: Vec<ItemProperty>, |
| } |
| |
| impl SampleDescription { |
| pub(crate) fn is_supported_format(&self) -> bool { |
| [ |
| "av01", |
| #[cfg(feature = "heic")] |
| "hvc1", |
| ] |
| .contains(&self.format.as_str()) |
| } |
| } |
| |
| #[derive(Debug)] |
| pub enum SampleSize { |
| FixedSize(u32), |
| Sizes(Vec<u32>), |
| } |
| |
| impl Default for SampleSize { |
| fn default() -> Self { |
| Self::FixedSize(0) |
| } |
| } |
| |
| #[derive(Debug, Default)] |
| pub struct SampleTable { |
| pub chunk_offsets: Vec<u64>, |
| pub sample_to_chunk: Vec<SampleToChunk>, |
| pub sample_size: SampleSize, |
| pub sync_samples: Vec<u32>, |
| pub time_to_sample: Vec<TimeToSample>, |
| pub sample_descriptions: Vec<SampleDescription>, |
| } |
| |
| impl SampleTable { |
| pub(crate) fn has_av1_sample(&self) -> bool { |
| self.sample_descriptions |
| .iter() |
| .any(|x| x.is_supported_format()) |
| } |
| |
| // returns the number of samples in the chunk. |
| pub(crate) fn get_sample_count_of_chunk(&self, chunk_index: u32) -> u32 { |
| for entry in self.sample_to_chunk.iter().rev() { |
| if entry.first_chunk <= chunk_index + 1 { |
| return entry.samples_per_chunk; |
| } |
| } |
| 0 |
| } |
| |
| pub(crate) fn get_properties(&self) -> Option<&Vec<ItemProperty>> { |
| Some( |
| &self |
| .sample_descriptions |
| .iter() |
| .find(|x| x.is_supported_format())? |
| .properties, |
| ) |
| } |
| |
| pub(crate) fn sample_size(&self, index: usize) -> AvifResult<usize> { |
| usize_from_u32(match &self.sample_size { |
| SampleSize::FixedSize(size) => *size, |
| SampleSize::Sizes(sizes) => { |
| if index >= sizes.len() { |
| return Err(AvifError::BmffParseFailed( |
| "not enough sampel sizes in the table".into(), |
| )); |
| } |
| sizes[index] |
| } |
| }) |
| } |
| |
| pub(crate) fn image_delta(&self, index: usize) -> AvifResult<u32> { |
| let mut max_index: u32 = 0; |
| for (i, time_to_sample) in self.time_to_sample.iter().enumerate() { |
| checked_incr!(max_index, time_to_sample.sample_count); |
| if index < max_index as usize || i == self.time_to_sample.len() - 1 { |
| return Ok(time_to_sample.sample_delta); |
| } |
| } |
| Ok(1) |
| } |
| } |
| |
| /// cbindgen:rename-all=CamelCase |
| #[repr(C)] |
| #[derive(Clone, Copy, Debug, Default)] |
| pub struct ImageTiming { |
| pub timescale: u64, |
| pub pts: f64, |
| pub pts_in_timescales: u64, |
| pub duration: f64, |
| pub duration_in_timescales: u64, |
| } |