1#![deny(unsafe_code)]
2#![allow(clippy::missing_safety_doc)]
3use arrayvec::ArrayVec;
12use log::{debug, warn};
13
14use bitreader::BitReader;
15use byteorder::ReadBytesExt;
16use fallible_collections::{TryClone, TryReserveError};
17use std::borrow::Cow;
18use std::convert::{TryFrom, TryInto as _};
19
20use std::io::{Read, Take};
21use std::num::NonZeroU32;
22use std::ops::{Range, RangeFrom};
23
24mod obu;
25
26mod boxes;
27use crate::boxes::{BoxType, FourCC};
28
29#[cfg(feature = "eager")]
31pub mod c_api;
32
33pub use enough::{Stop, StopReason, Unstoppable};
34
35trait ToU64 {
41 fn to_u64(self) -> u64;
42}
43
44impl ToU64 for usize {
48 fn to_u64(self) -> u64 {
49 const _: () = assert!(std::mem::size_of::<usize>() <= std::mem::size_of::<u64>());
50 self.try_into().ok().unwrap()
51 }
52}
53
54pub(crate) trait ToUsize {
57 fn to_usize(self) -> usize;
58}
59
60macro_rules! impl_to_usize_from {
64 ( $from_type:ty ) => {
65 impl ToUsize for $from_type {
66 fn to_usize(self) -> usize {
67 const _: () = assert!(std::mem::size_of::<$from_type>() <= std::mem::size_of::<usize>());
68 self.try_into().ok().unwrap()
69 }
70 }
71 };
72}
73
74impl_to_usize_from!(u8);
75impl_to_usize_from!(u16);
76impl_to_usize_from!(u32);
77
78trait Offset {
80 fn offset(&self) -> u64;
81}
82
83struct OffsetReader<'a, T> {
85 reader: &'a mut T,
86 offset: u64,
87}
88
89impl<'a, T> OffsetReader<'a, T> {
90 fn new(reader: &'a mut T) -> Self {
91 Self { reader, offset: 0 }
92 }
93}
94
95impl<T> Offset for OffsetReader<'_, T> {
96 fn offset(&self) -> u64 {
97 self.offset
98 }
99}
100
101impl<T: Read> Read for OffsetReader<'_, T> {
102 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
103 let bytes_read = self.reader.read(buf)?;
104 self.offset = self
105 .offset
106 .checked_add(bytes_read.to_u64())
107 .ok_or(Error::Unsupported("total bytes read too large for offset type"))?;
108 Ok(bytes_read)
109 }
110}
111
112#[doc(hidden)]
113pub type TryVec<T> = fallible_collections::TryVec<T>;
114type TryString = fallible_collections::TryVec<u8>;
115
116#[allow(dead_code)]
118struct Vec;
119#[allow(dead_code)]
120struct Box;
121#[allow(dead_code)]
122struct HashMap;
123#[allow(dead_code)]
124struct String;
125
126#[derive(Debug)]
131pub enum Error {
132 InvalidData(&'static str),
134 Unsupported(&'static str),
136 UnexpectedEOF,
138 Io(std::io::Error),
140 NoMoov,
142 OutOfMemory,
144 ResourceLimitExceeded(&'static str),
146 Stopped(enough::StopReason),
148}
149
150impl std::fmt::Display for Error {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 let msg = match self {
153 Self::InvalidData(s) | Self::Unsupported(s) | Self::ResourceLimitExceeded(s) => s,
154 Self::UnexpectedEOF => "EOF",
155 Self::Io(err) => return err.fmt(f),
156 Self::NoMoov => "Missing Moov box",
157 Self::OutOfMemory => "OOM",
158 Self::Stopped(reason) => return write!(f, "Stopped: {}", reason),
159 };
160 f.write_str(msg)
161 }
162}
163
164impl std::error::Error for Error {}
165
166impl From<bitreader::BitReaderError> for Error {
167 #[cold]
168 #[cfg_attr(debug_assertions, track_caller)]
169 fn from(err: bitreader::BitReaderError) -> Self {
170 log::warn!("bitreader: {err}");
171 debug_assert!(!matches!(err, bitreader::BitReaderError::TooManyBitsForType { .. })); Self::InvalidData("truncated bits")
173 }
174}
175
176impl From<std::io::Error> for Error {
177 fn from(err: std::io::Error) -> Self {
178 match err.kind() {
179 std::io::ErrorKind::UnexpectedEof => Self::UnexpectedEOF,
180 _ => Self::Io(err),
181 }
182 }
183}
184
185impl From<std::string::FromUtf8Error> for Error {
186 fn from(_: std::string::FromUtf8Error) -> Self {
187 Self::InvalidData("invalid utf8")
188 }
189}
190
191impl From<std::num::TryFromIntError> for Error {
192 fn from(_: std::num::TryFromIntError) -> Self {
193 Self::Unsupported("integer conversion failed")
194 }
195}
196
197impl From<Error> for std::io::Error {
198 fn from(err: Error) -> Self {
199 let kind = match err {
200 Error::InvalidData(_) => std::io::ErrorKind::InvalidData,
201 Error::UnexpectedEOF => std::io::ErrorKind::UnexpectedEof,
202 Error::Io(io_err) => return io_err,
203 _ => std::io::ErrorKind::Other,
204 };
205 Self::new(kind, err)
206 }
207}
208
209impl From<TryReserveError> for Error {
210 fn from(_: TryReserveError) -> Self {
211 Self::OutOfMemory
212 }
213}
214
215impl From<enough::StopReason> for Error {
216 fn from(reason: enough::StopReason) -> Self {
217 Self::Stopped(reason)
218 }
219}
220
221pub type Result<T, E = Error> = std::result::Result<T, E>;
223
224#[derive(Debug, Clone, Copy)]
233struct BoxHeader {
234 name: BoxType,
236 size: u64,
238 offset: u64,
240 #[allow(unused)]
242 uuid: Option<[u8; 16]>,
243}
244
245impl BoxHeader {
246 const MIN_SIZE: u64 = 8;
248 const MIN_LARGE_SIZE: u64 = 16;
250}
251
252#[derive(Debug)]
254#[allow(unused)]
255struct FileTypeBox {
256 major_brand: FourCC,
257 minor_version: u32,
258 compatible_brands: TryVec<FourCC>,
259}
260
261#[derive(Debug)]
263#[allow(unused)]
264struct HandlerBox {
265 handler_type: FourCC,
266}
267
268#[derive(Debug, Clone, PartialEq, Eq)]
273pub struct AV1Config {
274 pub profile: u8,
276 pub level: u8,
278 pub tier: u8,
280 pub bit_depth: u8,
282 pub monochrome: bool,
284 pub chroma_subsampling_x: u8,
286 pub chroma_subsampling_y: u8,
288 pub chroma_sample_position: u8,
290}
291
292#[derive(Debug, Clone, PartialEq, Eq)]
297pub enum ColorInformation {
298 Nclx {
300 color_primaries: u16,
302 transfer_characteristics: u16,
304 matrix_coefficients: u16,
306 full_range: bool,
308 },
309 IccProfile(std::vec::Vec<u8>),
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq)]
318pub struct ImageRotation {
319 pub angle: u16,
321}
322
323#[derive(Debug, Clone, Copy, PartialEq, Eq)]
328pub struct ImageMirror {
329 pub axis: u8,
332}
333
334#[derive(Debug, Clone, Copy, PartialEq, Eq)]
340pub struct CleanAperture {
341 pub width_n: u32,
343 pub width_d: u32,
345 pub height_n: u32,
347 pub height_d: u32,
349 pub horiz_off_n: i32,
351 pub horiz_off_d: u32,
353 pub vert_off_n: i32,
355 pub vert_off_d: u32,
357}
358
359#[derive(Debug, Clone, Copy, PartialEq, Eq)]
364pub struct PixelAspectRatio {
365 pub h_spacing: u32,
367 pub v_spacing: u32,
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
376pub struct ContentLightLevel {
377 pub max_content_light_level: u16,
379 pub max_pic_average_light_level: u16,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
388pub struct MasteringDisplayColourVolume {
389 pub primaries: [(u16, u16); 3],
392 pub white_point: (u16, u16),
394 pub max_luminance: u32,
396 pub min_luminance: u32,
398}
399
400#[derive(Debug, Clone, Copy, PartialEq, Eq)]
406pub struct ContentColourVolume {
407 pub primaries: Option<[(i32, i32); 3]>,
410 pub min_luminance: Option<u32>,
412 pub max_luminance: Option<u32>,
414 pub avg_luminance: Option<u32>,
416}
417
418#[derive(Debug, Clone, Copy, PartialEq, Eq)]
423pub struct AmbientViewingEnvironment {
424 pub ambient_illuminance: u32,
426 pub ambient_light_x: u16,
428 pub ambient_light_y: u16,
430}
431
432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
437pub struct OperatingPointSelector {
438 pub op_index: u8,
440}
441
442#[derive(Debug, Clone, Copy, PartialEq, Eq)]
447pub struct LayerSelector {
448 pub layer_id: u16,
450}
451
452#[derive(Debug, Clone, Copy, PartialEq, Eq)]
458pub struct AV1LayeredImageIndexing {
459 pub layer_sizes: [u32; 3],
462}
463
464#[derive(Debug, Clone, Copy)]
468#[derive(Default)]
469pub struct ParseOptions {
470 pub lenient: bool,
478}
479
480#[derive(Debug, Clone)]
506pub struct DecodeConfig {
507 pub peak_memory_limit: Option<u64>,
510
511 pub total_megapixels_limit: Option<u32>,
514
515 pub max_animation_frames: Option<u32>,
518
519 pub max_grid_tiles: Option<u32>,
522
523 pub lenient: bool,
526}
527
528impl Default for DecodeConfig {
529 fn default() -> Self {
530 Self {
531 peak_memory_limit: Some(1_000_000_000),
532 total_megapixels_limit: Some(512),
533 max_animation_frames: Some(10_000),
534 max_grid_tiles: Some(1_000),
535 lenient: false,
536 }
537 }
538}
539
540impl DecodeConfig {
541 pub fn unlimited() -> Self {
545 Self {
546 peak_memory_limit: None,
547 total_megapixels_limit: None,
548 max_animation_frames: None,
549 max_grid_tiles: None,
550 lenient: false,
551 }
552 }
553
554 pub fn with_peak_memory_limit(mut self, bytes: u64) -> Self {
556 self.peak_memory_limit = Some(bytes);
557 self
558 }
559
560 pub fn with_total_megapixels_limit(mut self, megapixels: u32) -> Self {
562 self.total_megapixels_limit = Some(megapixels);
563 self
564 }
565
566 pub fn with_max_animation_frames(mut self, frames: u32) -> Self {
568 self.max_animation_frames = Some(frames);
569 self
570 }
571
572 pub fn with_max_grid_tiles(mut self, tiles: u32) -> Self {
574 self.max_grid_tiles = Some(tiles);
575 self
576 }
577
578 pub fn lenient(mut self, lenient: bool) -> Self {
580 self.lenient = lenient;
581 self
582 }
583}
584
585#[derive(Debug, Clone, PartialEq)]
587pub struct GridConfig {
605 pub rows: u8,
607 pub columns: u8,
609 pub output_width: u32,
611 pub output_height: u32,
613}
614
615#[cfg(feature = "eager")]
617#[deprecated(since = "1.5.0", note = "Use `AvifParser::frame()` which returns `FrameRef` instead")]
618#[derive(Debug)]
619pub struct AnimationFrame {
620 pub data: TryVec<u8>,
622 pub duration_ms: u32,
624}
625
626#[cfg(feature = "eager")]
628#[deprecated(since = "1.5.0", note = "Use `AvifParser::animation_info()` and `AvifParser::frames()` instead")]
629#[derive(Debug)]
630#[allow(deprecated)]
631pub struct AnimationConfig {
632 pub loop_count: u32,
634 pub frames: TryVec<AnimationFrame>,
636}
637
638#[derive(Debug)]
641struct MovieHeader {
642 _timescale: u32,
643 _duration: u64,
644}
645
646#[derive(Debug)]
647struct MediaHeader {
648 timescale: u32,
649 _duration: u64,
650}
651
652#[derive(Debug)]
653struct TimeToSampleEntry {
654 sample_count: u32,
655 sample_delta: u32,
656}
657
658#[derive(Debug)]
659struct SampleToChunkEntry {
660 first_chunk: u32,
661 samples_per_chunk: u32,
662 _sample_description_index: u32,
663}
664
665#[derive(Debug)]
666struct SampleTable {
667 time_to_sample: TryVec<TimeToSampleEntry>,
668 sample_to_chunk: TryVec<SampleToChunkEntry>,
669 sample_sizes: TryVec<u32>,
670 chunk_offsets: TryVec<u64>,
671}
672
673#[cfg(feature = "eager")]
674#[deprecated(since = "1.5.0", note = "Use `AvifParser` for zero-copy parsing instead")]
675#[derive(Debug, Default)]
676#[allow(deprecated)]
677pub struct AvifData {
678 pub primary_item: TryVec<u8>,
682 pub alpha_item: Option<TryVec<u8>>,
686 pub premultiplied_alpha: bool,
690
691 pub grid_config: Option<GridConfig>,
715
716 pub grid_tiles: TryVec<TryVec<u8>>,
724
725 pub animation: Option<AnimationConfig>,
729
730 pub av1_config: Option<AV1Config>,
732
733 pub color_info: Option<ColorInformation>,
735
736 pub rotation: Option<ImageRotation>,
738
739 pub mirror: Option<ImageMirror>,
741
742 pub clean_aperture: Option<CleanAperture>,
744
745 pub pixel_aspect_ratio: Option<PixelAspectRatio>,
747
748 pub content_light_level: Option<ContentLightLevel>,
750
751 pub mastering_display: Option<MasteringDisplayColourVolume>,
753
754 pub content_colour_volume: Option<ContentColourVolume>,
756
757 pub ambient_viewing: Option<AmbientViewingEnvironment>,
759
760 pub operating_point: Option<OperatingPointSelector>,
762
763 pub layer_selector: Option<LayerSelector>,
765
766 pub layered_image_indexing: Option<AV1LayeredImageIndexing>,
768
769 pub major_brand: [u8; 4],
771
772 pub compatible_brands: std::vec::Vec<[u8; 4]>,
774}
775
776#[cfg(feature = "eager")]
797#[allow(deprecated)]
798impl AvifData {
799 #[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader()` instead")]
800 pub fn from_reader<R: Read>(reader: &mut R) -> Result<Self> {
801 read_avif(reader)
802 }
803
804 pub fn primary_item_metadata(&self) -> Result<AV1Metadata> {
806 AV1Metadata::parse_av1_bitstream(&self.primary_item)
807 }
808
809 pub fn alpha_item_metadata(&self) -> Result<Option<AV1Metadata>> {
811 self.alpha_item.as_deref().map(AV1Metadata::parse_av1_bitstream).transpose()
812 }
813}
814
815#[non_exhaustive]
819#[derive(Debug, Clone)]
820pub struct AV1Metadata {
821 pub still_picture: bool,
823 pub max_frame_width: NonZeroU32,
824 pub max_frame_height: NonZeroU32,
825 pub bit_depth: u8,
827 pub seq_profile: u8,
829 pub chroma_subsampling: (bool, bool),
831 pub monochrome: bool,
832}
833
834impl AV1Metadata {
835 #[inline(never)]
840 pub fn parse_av1_bitstream(obu_bitstream: &[u8]) -> Result<Self> {
841 let h = obu::parse_obu(obu_bitstream)?;
842 Ok(Self {
843 still_picture: h.still_picture,
844 max_frame_width: h.max_frame_width,
845 max_frame_height: h.max_frame_height,
846 bit_depth: h.color.bit_depth,
847 seq_profile: h.seq_profile,
848 chroma_subsampling: h.color.chroma_subsampling,
849 monochrome: h.color.monochrome,
850 })
851 }
852}
853
854pub struct FrameRef<'a> {
859 pub data: Cow<'a, [u8]>,
860 pub duration_ms: u32,
861}
862
863struct MdatBounds {
865 offset: u64,
866 length: u64,
867}
868
869struct ItemExtents {
871 construction_method: ConstructionMethod,
872 extents: TryVec<ExtentRange>,
873}
874
875pub struct AvifParser<'data> {
901 raw: Cow<'data, [u8]>,
902 mdat_bounds: TryVec<MdatBounds>,
903 idat: Option<TryVec<u8>>,
904 primary: ItemExtents,
905 alpha: Option<ItemExtents>,
906 grid_config: Option<GridConfig>,
907 tiles: TryVec<ItemExtents>,
908 animation_data: Option<AnimationParserData>,
909 premultiplied_alpha: bool,
910 av1_config: Option<AV1Config>,
911 color_info: Option<ColorInformation>,
912 rotation: Option<ImageRotation>,
913 mirror: Option<ImageMirror>,
914 clean_aperture: Option<CleanAperture>,
915 pixel_aspect_ratio: Option<PixelAspectRatio>,
916 content_light_level: Option<ContentLightLevel>,
917 mastering_display: Option<MasteringDisplayColourVolume>,
918 content_colour_volume: Option<ContentColourVolume>,
919 ambient_viewing: Option<AmbientViewingEnvironment>,
920 operating_point: Option<OperatingPointSelector>,
921 layer_selector: Option<LayerSelector>,
922 layered_image_indexing: Option<AV1LayeredImageIndexing>,
923 major_brand: [u8; 4],
924 compatible_brands: std::vec::Vec<[u8; 4]>,
925}
926
927struct AnimationParserData {
928 media_timescale: u32,
929 sample_table: SampleTable,
930 loop_count: u32,
931}
932
933#[derive(Debug, Clone, Copy)]
935pub struct AnimationInfo {
936 pub frame_count: usize,
937 pub loop_count: u32,
938}
939
940struct ParsedStructure {
942 meta: AvifInternalMeta,
943 mdat_bounds: TryVec<MdatBounds>,
944 animation_data: Option<(u32, SampleTable, u32)>,
945 major_brand: [u8; 4],
946 compatible_brands: std::vec::Vec<[u8; 4]>,
947}
948
949impl<'data> AvifParser<'data> {
950 pub fn from_bytes(data: &'data [u8]) -> Result<Self> {
959 Self::from_bytes_with_config(data, &DecodeConfig::unlimited(), &Unstoppable)
960 }
961
962 pub fn from_bytes_with_config(
964 data: &'data [u8],
965 config: &DecodeConfig,
966 stop: &dyn Stop,
967 ) -> Result<Self> {
968 let parsed = Self::parse_raw(data, config, stop)?;
969 Self::build(Cow::Borrowed(data), parsed, config)
970 }
971
972 pub fn from_owned(data: std::vec::Vec<u8>) -> Result<AvifParser<'static>> {
977 AvifParser::from_owned_with_config(data, &DecodeConfig::unlimited(), &Unstoppable)
978 }
979
980 pub fn from_owned_with_config(
982 data: std::vec::Vec<u8>,
983 config: &DecodeConfig,
984 stop: &dyn Stop,
985 ) -> Result<AvifParser<'static>> {
986 let parsed = AvifParser::parse_raw(&data, config, stop)?;
987 AvifParser::build(Cow::Owned(data), parsed, config)
988 }
989
990 pub fn from_reader<R: Read>(reader: &mut R) -> Result<AvifParser<'static>> {
992 AvifParser::from_reader_with_config(reader, &DecodeConfig::unlimited(), &Unstoppable)
993 }
994
995 pub fn from_reader_with_config<R: Read>(
997 reader: &mut R,
998 config: &DecodeConfig,
999 stop: &dyn Stop,
1000 ) -> Result<AvifParser<'static>> {
1001 let mut buf = std::vec::Vec::new();
1002 reader.read_to_end(&mut buf)?;
1003 AvifParser::from_owned_with_config(buf, config, stop)
1004 }
1005
1006 fn parse_raw(data: &[u8], config: &DecodeConfig, stop: &dyn Stop) -> Result<ParsedStructure> {
1013 let parse_opts = ParseOptions { lenient: config.lenient };
1014 let mut cursor = std::io::Cursor::new(data);
1015 let mut f = OffsetReader::new(&mut cursor);
1016 let mut iter = BoxIter::new(&mut f);
1017
1018 let (major_brand, compatible_brands) = if let Some(mut b) = iter.next_box()? {
1020 if b.head.name == BoxType::FileTypeBox {
1021 let ftyp = read_ftyp(&mut b)?;
1022 if ftyp.major_brand != b"avif" && ftyp.major_brand != b"avis" {
1023 return Err(Error::InvalidData("ftyp must be 'avif' or 'avis'"));
1024 }
1025 let major = ftyp.major_brand.value;
1026 let compat = ftyp.compatible_brands.iter().map(|b| b.value).collect();
1027 (major, compat)
1028 } else {
1029 return Err(Error::InvalidData("'ftyp' box must occur first"));
1030 }
1031 } else {
1032 return Err(Error::InvalidData("'ftyp' box must occur first"));
1033 };
1034
1035 let mut meta = None;
1036 let mut mdat_bounds = TryVec::new();
1037 let mut animation_data: Option<(u32, SampleTable, u32)> = None;
1038
1039 while let Some(mut b) = iter.next_box()? {
1040 stop.check()?;
1041
1042 match b.head.name {
1043 BoxType::MetadataBox => {
1044 if meta.is_some() {
1045 return Err(Error::InvalidData(
1046 "There should be zero or one meta boxes per ISO 14496-12:2015 § 8.11.1.1",
1047 ));
1048 }
1049 meta = Some(read_avif_meta(&mut b, &parse_opts)?);
1050 }
1051 BoxType::MovieBox => {
1052 if let Some((media_timescale, sample_table)) = read_moov(&mut b)? {
1053 animation_data = Some((media_timescale, sample_table, 0));
1054 }
1055 }
1056 BoxType::MediaDataBox => {
1057 if b.bytes_left() > 0 {
1058 let offset = b.offset();
1059 let length = b.bytes_left();
1060 mdat_bounds.push(MdatBounds { offset, length })?;
1061 }
1062 skip_box_content(&mut b)?;
1064 }
1065 _ => skip_box_content(&mut b)?,
1066 }
1067
1068 check_parser_state(&b.head, &b.content)?;
1069 }
1070
1071 let meta = meta.ok_or(Error::InvalidData("missing meta"))?;
1072
1073 Ok(ParsedStructure { meta, mdat_bounds, animation_data, major_brand, compatible_brands })
1074 }
1075
1076 fn build(raw: Cow<'data, [u8]>, parsed: ParsedStructure, config: &DecodeConfig) -> Result<Self> {
1078 let tracker = ResourceTracker::new(config);
1079 let meta = parsed.meta;
1080
1081 let primary = Self::get_item_extents(&meta, meta.primary_item_id)?;
1083
1084 let alpha_item_id = meta
1086 .item_references
1087 .iter()
1088 .filter(|iref| {
1089 iref.to_item_id == meta.primary_item_id
1090 && iref.from_item_id != meta.primary_item_id
1091 && iref.item_type == b"auxl"
1092 })
1093 .map(|iref| iref.from_item_id)
1094 .find(|&item_id| {
1095 meta.properties.iter().any(|prop| {
1096 prop.item_id == item_id
1097 && match &prop.property {
1098 ItemProperty::AuxiliaryType(urn) => {
1099 urn.type_subtype().0 == b"urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
1100 }
1101 _ => false,
1102 }
1103 })
1104 });
1105
1106 let alpha = alpha_item_id
1107 .map(|id| Self::get_item_extents(&meta, id))
1108 .transpose()?;
1109
1110 let premultiplied_alpha = alpha_item_id.is_some_and(|alpha_id| {
1112 meta.item_references.iter().any(|iref| {
1113 iref.from_item_id == meta.primary_item_id
1114 && iref.to_item_id == alpha_id
1115 && iref.item_type == b"prem"
1116 })
1117 });
1118
1119 let is_grid = meta
1121 .item_infos
1122 .iter()
1123 .find(|x| x.item_id == meta.primary_item_id)
1124 .is_some_and(|info| info.item_type == b"grid");
1125
1126 let (grid_config, tiles) = if is_grid {
1128 let mut tiles_with_index: TryVec<(u32, u16)> = TryVec::new();
1129 for iref in meta.item_references.iter() {
1130 if iref.from_item_id == meta.primary_item_id && iref.item_type == b"dimg" {
1131 tiles_with_index.push((iref.to_item_id, iref.reference_index))?;
1132 }
1133 }
1134
1135 tracker.validate_grid_tiles(tiles_with_index.len() as u32)?;
1136 tiles_with_index.sort_by_key(|&(_, idx)| idx);
1137
1138 let mut tile_extents = TryVec::new();
1139 for (tile_id, _) in tiles_with_index.iter() {
1140 tile_extents.push(Self::get_item_extents(&meta, *tile_id)?)?;
1141 }
1142
1143 let mut tile_ids = TryVec::new();
1144 for (tile_id, _) in tiles_with_index.iter() {
1145 tile_ids.push(*tile_id)?;
1146 }
1147
1148 let grid_config = Self::calculate_grid_config(&meta, &tile_ids)?;
1149
1150 for (tile_id, _) in tiles_with_index.iter() {
1152 for prop in meta.properties.iter() {
1153 if prop.item_id == *tile_id {
1154 match &prop.property {
1155 ItemProperty::Rotation(_)
1156 | ItemProperty::Mirror(_)
1157 | ItemProperty::CleanAperture(_) => {
1158 warn!("grid tile {} has a transformative property (irot/imir/clap), violating AVIF spec", tile_id);
1159 }
1160 _ => {}
1161 }
1162 }
1163 }
1164 }
1165
1166 (Some(grid_config), tile_extents)
1167 } else {
1168 (None, TryVec::new())
1169 };
1170
1171 macro_rules! find_prop {
1173 ($variant:ident) => {
1174 meta.properties.iter().find_map(|p| {
1175 if p.item_id == meta.primary_item_id {
1176 match &p.property {
1177 ItemProperty::$variant(c) => Some(c.clone()),
1178 _ => None,
1179 }
1180 } else {
1181 None
1182 }
1183 })
1184 };
1185 }
1186
1187 let av1_config = find_prop!(AV1Config);
1188 let color_info = find_prop!(ColorInformation);
1189 let rotation = find_prop!(Rotation);
1190 let mirror = find_prop!(Mirror);
1191 let clean_aperture = find_prop!(CleanAperture);
1192 let pixel_aspect_ratio = find_prop!(PixelAspectRatio);
1193 let content_light_level = find_prop!(ContentLightLevel);
1194 let mastering_display = find_prop!(MasteringDisplayColourVolume);
1195 let content_colour_volume = find_prop!(ContentColourVolume);
1196 let ambient_viewing = find_prop!(AmbientViewingEnvironment);
1197 let operating_point = find_prop!(OperatingPointSelector);
1198 let layer_selector = find_prop!(LayerSelector);
1199 let layered_image_indexing = find_prop!(AV1LayeredImageIndexing);
1200
1201 let animation_data = if let Some((media_timescale, sample_table, loop_count)) = parsed.animation_data {
1203 tracker.validate_animation_frames(sample_table.sample_sizes.len() as u32)?;
1204 Some(AnimationParserData { media_timescale, sample_table, loop_count })
1205 } else {
1206 None
1207 };
1208
1209 let idat = if let Some(ref idat_data) = meta.idat {
1211 let mut cloned = TryVec::new();
1212 cloned.extend_from_slice(idat_data)?;
1213 Some(cloned)
1214 } else {
1215 None
1216 };
1217
1218 Ok(Self {
1219 raw,
1220 mdat_bounds: parsed.mdat_bounds,
1221 idat,
1222 primary,
1223 alpha,
1224 grid_config,
1225 tiles,
1226 animation_data,
1227 premultiplied_alpha,
1228 av1_config,
1229 color_info,
1230 rotation,
1231 mirror,
1232 clean_aperture,
1233 pixel_aspect_ratio,
1234 content_light_level,
1235 mastering_display,
1236 content_colour_volume,
1237 ambient_viewing,
1238 operating_point,
1239 layer_selector,
1240 layered_image_indexing,
1241 major_brand: parsed.major_brand,
1242 compatible_brands: parsed.compatible_brands,
1243 })
1244 }
1245
1246 fn get_item_extents(meta: &AvifInternalMeta, item_id: u32) -> Result<ItemExtents> {
1252 let item = meta
1253 .iloc_items
1254 .iter()
1255 .find(|item| item.item_id == item_id)
1256 .ok_or(Error::InvalidData("item not found in iloc"))?;
1257
1258 let mut extents = TryVec::new();
1259 for extent in &item.extents {
1260 extents.push(extent.extent_range.clone())?;
1261 }
1262 Ok(ItemExtents {
1263 construction_method: item.construction_method,
1264 extents,
1265 })
1266 }
1267
1268 fn resolve_item(&self, item: &ItemExtents) -> Result<Cow<'_, [u8]>> {
1271 match item.construction_method {
1272 ConstructionMethod::Idat => self.resolve_idat_extents(&item.extents),
1273 ConstructionMethod::File => self.resolve_file_extents(&item.extents),
1274 ConstructionMethod::Item => Err(Error::Unsupported("construction_method 'item' not supported")),
1275 }
1276 }
1277
1278 fn resolve_file_extents(&self, extents: &[ExtentRange]) -> Result<Cow<'_, [u8]>> {
1280 let raw = self.raw.as_ref();
1281
1282 if extents.len() == 1 {
1284 let extent = &extents[0];
1285 let (start, end) = self.extent_byte_range(extent)?;
1286 let slice = raw.get(start..end).ok_or(Error::InvalidData("extent out of bounds in raw buffer"))?;
1287 return Ok(Cow::Borrowed(slice));
1288 }
1289
1290 let mut data = TryVec::new();
1292 for extent in extents {
1293 let (start, end) = self.extent_byte_range(extent)?;
1294 let slice = raw.get(start..end).ok_or(Error::InvalidData("extent out of bounds in raw buffer"))?;
1295 data.extend_from_slice(slice)?;
1296 }
1297 Ok(Cow::Owned(data.to_vec()))
1298 }
1299
1300 fn extent_byte_range(&self, extent: &ExtentRange) -> Result<(usize, usize)> {
1302 let file_offset = extent.start();
1303 let start = usize::try_from(file_offset)?;
1304
1305 match extent {
1306 ExtentRange::WithLength(range) => {
1307 let len = range.end.checked_sub(range.start)
1308 .ok_or(Error::InvalidData("extent range start > end"))?;
1309 let end = start.checked_add(usize::try_from(len)?)
1310 .ok_or(Error::InvalidData("extent end overflow"))?;
1311 Ok((start, end))
1312 }
1313 ExtentRange::ToEnd(_) => {
1314 for mdat in &self.mdat_bounds {
1316 if file_offset >= mdat.offset && file_offset < mdat.offset + mdat.length {
1317 let end = usize::try_from(mdat.offset + mdat.length)?;
1318 return Ok((start, end));
1319 }
1320 }
1321 Ok((start, self.raw.len()))
1323 }
1324 }
1325 }
1326
1327 fn resolve_idat_extents(&self, extents: &[ExtentRange]) -> Result<Cow<'_, [u8]>> {
1329 let idat_data = self.idat.as_ref()
1330 .ok_or(Error::InvalidData("idat box missing but construction_method is Idat"))?;
1331
1332 if extents.len() == 1 {
1333 let extent = &extents[0];
1334 let start = usize::try_from(extent.start())?;
1335 let slice = match extent {
1336 ExtentRange::WithLength(range) => {
1337 let len = usize::try_from(range.end - range.start)?;
1338 idat_data.get(start..start + len)
1339 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1340 }
1341 ExtentRange::ToEnd(_) => {
1342 idat_data.get(start..)
1343 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1344 }
1345 };
1346 return Ok(Cow::Borrowed(slice));
1347 }
1348
1349 let mut data = TryVec::new();
1351 for extent in extents {
1352 let start = usize::try_from(extent.start())?;
1353 let slice = match extent {
1354 ExtentRange::WithLength(range) => {
1355 let len = usize::try_from(range.end - range.start)?;
1356 idat_data.get(start..start + len)
1357 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1358 }
1359 ExtentRange::ToEnd(_) => {
1360 idat_data.get(start..)
1361 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1362 }
1363 };
1364 data.extend_from_slice(slice)?;
1365 }
1366 Ok(Cow::Owned(data.to_vec()))
1367 }
1368
1369 fn resolve_frame(&self, index: usize) -> Result<FrameRef<'_>> {
1371 let anim = self.animation_data.as_ref()
1372 .ok_or(Error::InvalidData("not an animated AVIF"))?;
1373
1374 if index >= anim.sample_table.sample_sizes.len() {
1375 return Err(Error::InvalidData("frame index out of bounds"));
1376 }
1377
1378 let duration_ms = self.calculate_frame_duration(&anim.sample_table, anim.media_timescale, index)?;
1379 let (offset, size) = self.calculate_sample_location(&anim.sample_table, index)?;
1380
1381 let start = usize::try_from(offset)?;
1382 let end = start.checked_add(size as usize)
1383 .ok_or(Error::InvalidData("frame end overflow"))?;
1384
1385 let raw = self.raw.as_ref();
1386 let slice = raw.get(start..end)
1387 .ok_or(Error::InvalidData("frame not found in raw buffer"))?;
1388
1389 Ok(FrameRef {
1390 data: Cow::Borrowed(slice),
1391 duration_ms,
1392 })
1393 }
1394
1395 fn calculate_grid_config(meta: &AvifInternalMeta, tile_ids: &[u32]) -> Result<GridConfig> {
1397 for prop in &meta.properties {
1399 if prop.item_id == meta.primary_item_id
1400 && let ItemProperty::ImageGrid(grid) = &prop.property {
1401 return Ok(grid.clone());
1402 }
1403 }
1404
1405 let grid_dims = meta
1407 .properties
1408 .iter()
1409 .find(|p| p.item_id == meta.primary_item_id)
1410 .and_then(|p| match &p.property {
1411 ItemProperty::ImageSpatialExtents(e) => Some(e),
1412 _ => None,
1413 });
1414
1415 let tile_dims = tile_ids.first().and_then(|&tile_id| {
1416 meta.properties
1417 .iter()
1418 .find(|p| p.item_id == tile_id)
1419 .and_then(|p| match &p.property {
1420 ItemProperty::ImageSpatialExtents(e) => Some(e),
1421 _ => None,
1422 })
1423 });
1424
1425 if let (Some(grid), Some(tile)) = (grid_dims, tile_dims)
1426 && tile.width != 0
1427 && tile.height != 0
1428 && grid.width % tile.width == 0
1429 && grid.height % tile.height == 0
1430 {
1431 let columns = grid.width / tile.width;
1432 let rows = grid.height / tile.height;
1433
1434 if columns <= 255 && rows <= 255 {
1435 return Ok(GridConfig {
1436 rows: rows as u8,
1437 columns: columns as u8,
1438 output_width: grid.width,
1439 output_height: grid.height,
1440 });
1441 }
1442 }
1443
1444 let tile_count = tile_ids.len();
1445 Ok(GridConfig {
1446 rows: tile_count.min(255) as u8,
1447 columns: 1,
1448 output_width: 0,
1449 output_height: 0,
1450 })
1451 }
1452
1453 fn calculate_frame_duration(
1455 &self,
1456 st: &SampleTable,
1457 timescale: u32,
1458 index: usize,
1459 ) -> Result<u32> {
1460 let mut current_sample = 0;
1461 for entry in &st.time_to_sample {
1462 if current_sample + entry.sample_count as usize > index {
1463 let duration_ms = if timescale > 0 {
1464 ((entry.sample_delta as u64) * 1000) / (timescale as u64)
1465 } else {
1466 0
1467 };
1468 return Ok(duration_ms as u32);
1469 }
1470 current_sample += entry.sample_count as usize;
1471 }
1472 Ok(0)
1473 }
1474
1475 fn calculate_sample_location(&self, st: &SampleTable, index: usize) -> Result<(u64, u32)> {
1477 let sample_size = *st
1478 .sample_sizes
1479 .get(index)
1480 .ok_or(Error::InvalidData("sample index out of bounds"))?;
1481
1482 let mut current_sample = 0;
1483 for (chunk_map_idx, entry) in st.sample_to_chunk.iter().enumerate() {
1484 let next_first_chunk = st
1485 .sample_to_chunk
1486 .get(chunk_map_idx + 1)
1487 .map(|e| e.first_chunk)
1488 .unwrap_or(u32::MAX);
1489
1490 for chunk_idx in entry.first_chunk..next_first_chunk {
1491 if chunk_idx == 0 || (chunk_idx as usize) > st.chunk_offsets.len() {
1492 break;
1493 }
1494
1495 let chunk_offset = st.chunk_offsets[(chunk_idx - 1) as usize];
1496
1497 for sample_in_chunk in 0..entry.samples_per_chunk {
1498 if current_sample == index {
1499 let mut offset_in_chunk = 0u64;
1500 for s in 0..sample_in_chunk {
1501 let prev_idx = current_sample.saturating_sub((sample_in_chunk - s) as usize);
1502 if let Some(&prev_size) = st.sample_sizes.get(prev_idx) {
1503 offset_in_chunk += prev_size as u64;
1504 }
1505 }
1506
1507 return Ok((chunk_offset + offset_in_chunk, sample_size));
1508 }
1509 current_sample += 1;
1510 }
1511 }
1512 }
1513
1514 Err(Error::InvalidData("sample not found in chunk table"))
1515 }
1516
1517 pub fn primary_data(&self) -> Result<Cow<'_, [u8]>> {
1525 self.resolve_item(&self.primary)
1526 }
1527
1528 pub fn alpha_data(&self) -> Option<Result<Cow<'_, [u8]>>> {
1530 self.alpha.as_ref().map(|item| self.resolve_item(item))
1531 }
1532
1533 pub fn tile_data(&self, index: usize) -> Result<Cow<'_, [u8]>> {
1535 let item = self.tiles.get(index)
1536 .ok_or(Error::InvalidData("tile index out of bounds"))?;
1537 self.resolve_item(item)
1538 }
1539
1540 pub fn frame(&self, index: usize) -> Result<FrameRef<'_>> {
1542 self.resolve_frame(index)
1543 }
1544
1545 pub fn frames(&self) -> FrameIterator<'_> {
1547 let count = self
1548 .animation_info()
1549 .map(|info| info.frame_count)
1550 .unwrap_or(0);
1551 FrameIterator { parser: self, index: 0, count }
1552 }
1553
1554 pub fn animation_info(&self) -> Option<AnimationInfo> {
1560 self.animation_data.as_ref().map(|data| AnimationInfo {
1561 frame_count: data.sample_table.sample_sizes.len(),
1562 loop_count: data.loop_count,
1563 })
1564 }
1565
1566 pub fn grid_config(&self) -> Option<&GridConfig> {
1568 self.grid_config.as_ref()
1569 }
1570
1571 pub fn grid_tile_count(&self) -> usize {
1573 self.tiles.len()
1574 }
1575
1576 pub fn premultiplied_alpha(&self) -> bool {
1578 self.premultiplied_alpha
1579 }
1580
1581 pub fn av1_config(&self) -> Option<&AV1Config> {
1585 self.av1_config.as_ref()
1586 }
1587
1588 pub fn color_info(&self) -> Option<&ColorInformation> {
1594 self.color_info.as_ref()
1595 }
1596
1597 pub fn rotation(&self) -> Option<&ImageRotation> {
1599 self.rotation.as_ref()
1600 }
1601
1602 pub fn mirror(&self) -> Option<&ImageMirror> {
1604 self.mirror.as_ref()
1605 }
1606
1607 pub fn clean_aperture(&self) -> Option<&CleanAperture> {
1609 self.clean_aperture.as_ref()
1610 }
1611
1612 pub fn pixel_aspect_ratio(&self) -> Option<&PixelAspectRatio> {
1614 self.pixel_aspect_ratio.as_ref()
1615 }
1616
1617 pub fn content_light_level(&self) -> Option<&ContentLightLevel> {
1619 self.content_light_level.as_ref()
1620 }
1621
1622 pub fn mastering_display(&self) -> Option<&MasteringDisplayColourVolume> {
1624 self.mastering_display.as_ref()
1625 }
1626
1627 pub fn content_colour_volume(&self) -> Option<&ContentColourVolume> {
1629 self.content_colour_volume.as_ref()
1630 }
1631
1632 pub fn ambient_viewing(&self) -> Option<&AmbientViewingEnvironment> {
1634 self.ambient_viewing.as_ref()
1635 }
1636
1637 pub fn operating_point(&self) -> Option<&OperatingPointSelector> {
1639 self.operating_point.as_ref()
1640 }
1641
1642 pub fn layer_selector(&self) -> Option<&LayerSelector> {
1644 self.layer_selector.as_ref()
1645 }
1646
1647 pub fn layered_image_indexing(&self) -> Option<&AV1LayeredImageIndexing> {
1649 self.layered_image_indexing.as_ref()
1650 }
1651
1652 pub fn major_brand(&self) -> &[u8; 4] {
1654 &self.major_brand
1655 }
1656
1657 pub fn compatible_brands(&self) -> &[[u8; 4]] {
1659 &self.compatible_brands
1660 }
1661
1662 pub fn primary_metadata(&self) -> Result<AV1Metadata> {
1664 let data = self.primary_data()?;
1665 AV1Metadata::parse_av1_bitstream(&data)
1666 }
1667
1668 pub fn alpha_metadata(&self) -> Option<Result<AV1Metadata>> {
1670 self.alpha.as_ref().map(|item| {
1671 let data = self.resolve_item(item)?;
1672 AV1Metadata::parse_av1_bitstream(&data)
1673 })
1674 }
1675
1676 #[cfg(feature = "eager")]
1685 #[deprecated(since = "1.5.0", note = "Use AvifParser methods directly instead of converting to AvifData")]
1686 #[allow(deprecated)]
1687 pub fn to_avif_data(&self) -> Result<AvifData> {
1688 let primary_data = self.primary_data()?;
1689 let mut primary_item = TryVec::new();
1690 primary_item.extend_from_slice(&primary_data)?;
1691
1692 let alpha_item = match self.alpha_data() {
1693 Some(Ok(data)) => {
1694 let mut v = TryVec::new();
1695 v.extend_from_slice(&data)?;
1696 Some(v)
1697 }
1698 Some(Err(e)) => return Err(e),
1699 None => None,
1700 };
1701
1702 let mut grid_tiles = TryVec::new();
1703 for i in 0..self.grid_tile_count() {
1704 let data = self.tile_data(i)?;
1705 let mut v = TryVec::new();
1706 v.extend_from_slice(&data)?;
1707 grid_tiles.push(v)?;
1708 }
1709
1710 let animation = if let Some(info) = self.animation_info() {
1711 let mut frames = TryVec::new();
1712 for i in 0..info.frame_count {
1713 let frame_ref = self.frame(i)?;
1714 let mut data = TryVec::new();
1715 data.extend_from_slice(&frame_ref.data)?;
1716 frames.push(AnimationFrame { data, duration_ms: frame_ref.duration_ms })?;
1717 }
1718 Some(AnimationConfig {
1719 loop_count: info.loop_count,
1720 frames,
1721 })
1722 } else {
1723 None
1724 };
1725
1726 Ok(AvifData {
1727 primary_item,
1728 alpha_item,
1729 premultiplied_alpha: self.premultiplied_alpha,
1730 grid_config: self.grid_config.clone(),
1731 grid_tiles,
1732 animation,
1733 av1_config: self.av1_config.clone(),
1734 color_info: self.color_info.clone(),
1735 rotation: self.rotation,
1736 mirror: self.mirror,
1737 clean_aperture: self.clean_aperture,
1738 pixel_aspect_ratio: self.pixel_aspect_ratio,
1739 content_light_level: self.content_light_level,
1740 mastering_display: self.mastering_display,
1741 content_colour_volume: self.content_colour_volume,
1742 ambient_viewing: self.ambient_viewing,
1743 operating_point: self.operating_point,
1744 layer_selector: self.layer_selector,
1745 layered_image_indexing: self.layered_image_indexing,
1746 major_brand: self.major_brand,
1747 compatible_brands: self.compatible_brands.clone(),
1748 })
1749 }
1750}
1751
1752pub struct FrameIterator<'a> {
1756 parser: &'a AvifParser<'a>,
1757 index: usize,
1758 count: usize,
1759}
1760
1761impl<'a> Iterator for FrameIterator<'a> {
1762 type Item = Result<FrameRef<'a>>;
1763
1764 fn next(&mut self) -> Option<Self::Item> {
1765 if self.index >= self.count {
1766 return None;
1767 }
1768 let result = self.parser.frame(self.index);
1769 self.index += 1;
1770 Some(result)
1771 }
1772
1773 fn size_hint(&self) -> (usize, Option<usize>) {
1774 let remaining = self.count.saturating_sub(self.index);
1775 (remaining, Some(remaining))
1776 }
1777}
1778
1779impl ExactSizeIterator for FrameIterator<'_> {
1780 fn len(&self) -> usize {
1781 self.count.saturating_sub(self.index)
1782 }
1783}
1784
1785struct AvifInternalMeta {
1786 item_references: TryVec<SingleItemTypeReferenceBox>,
1787 properties: TryVec<AssociatedProperty>,
1788 primary_item_id: u32,
1789 iloc_items: TryVec<ItemLocationBoxItem>,
1790 item_infos: TryVec<ItemInfoEntry>,
1791 idat: Option<TryVec<u8>>,
1792}
1793
1794#[cfg(feature = "eager")]
1797struct MediaDataBox {
1798 offset: u64,
1800 data: TryVec<u8>,
1801}
1802
1803#[cfg(feature = "eager")]
1804impl MediaDataBox {
1805 fn contains_extent(&self, extent: &ExtentRange) -> bool {
1809 if self.offset <= extent.start() {
1810 let start_offset = extent.start() - self.offset;
1811 start_offset < self.data.len().to_u64()
1812 } else {
1813 false
1814 }
1815 }
1816
1817 fn matches_extent(&self, extent: &ExtentRange) -> bool {
1819 if self.offset == extent.start() {
1820 match extent {
1821 ExtentRange::WithLength(range) => {
1822 if let Some(end) = self.offset.checked_add(self.data.len().to_u64()) {
1823 end == range.end
1824 } else {
1825 false
1826 }
1827 },
1828 ExtentRange::ToEnd(_) => true,
1829 }
1830 } else {
1831 false
1832 }
1833 }
1834
1835 fn read_extent(&self, extent: &ExtentRange, buf: &mut TryVec<u8>) -> Result<()> {
1838 let start_offset = extent
1839 .start()
1840 .checked_sub(self.offset)
1841 .ok_or(Error::InvalidData("mdat does not contain extent"))?;
1842 let slice = match extent {
1843 ExtentRange::WithLength(range) => {
1844 let range_len = range
1845 .end
1846 .checked_sub(range.start)
1847 .ok_or(Error::InvalidData("range start > end"))?;
1848 let end = start_offset
1849 .checked_add(range_len)
1850 .ok_or(Error::InvalidData("extent end overflow"))?;
1851 self.data.get(start_offset.try_into()?..end.try_into()?)
1852 },
1853 ExtentRange::ToEnd(_) => self.data.get(start_offset.try_into()?..),
1854 };
1855 let slice = slice.ok_or(Error::InvalidData("extent crosses box boundary"))?;
1856 buf.extend_from_slice(slice)?;
1857 Ok(())
1858 }
1859
1860}
1861
1862#[derive(Debug)]
1866struct ItemInfoEntry {
1867 item_id: u32,
1868 item_type: FourCC,
1869}
1870
1871#[derive(Debug)]
1873struct SingleItemTypeReferenceBox {
1874 item_type: FourCC,
1875 from_item_id: u32,
1876 to_item_id: u32,
1877 reference_index: u16,
1880}
1881
1882#[derive(Debug)]
1885enum IlocFieldSize {
1886 Zero,
1887 Four,
1888 Eight,
1889}
1890
1891impl IlocFieldSize {
1892 const fn to_bits(&self) -> u8 {
1893 match self {
1894 Self::Zero => 0,
1895 Self::Four => 32,
1896 Self::Eight => 64,
1897 }
1898 }
1899}
1900
1901impl TryFrom<u8> for IlocFieldSize {
1902 type Error = Error;
1903
1904 fn try_from(value: u8) -> Result<Self> {
1905 match value {
1906 0 => Ok(Self::Zero),
1907 4 => Ok(Self::Four),
1908 8 => Ok(Self::Eight),
1909 _ => Err(Error::InvalidData("value must be in the set {0, 4, 8}")),
1910 }
1911 }
1912}
1913
1914#[derive(PartialEq)]
1915enum IlocVersion {
1916 Zero,
1917 One,
1918 Two,
1919}
1920
1921impl TryFrom<u8> for IlocVersion {
1922 type Error = Error;
1923
1924 fn try_from(value: u8) -> Result<Self> {
1925 match value {
1926 0 => Ok(Self::Zero),
1927 1 => Ok(Self::One),
1928 2 => Ok(Self::Two),
1929 _ => Err(Error::Unsupported("unsupported version in 'iloc' box")),
1930 }
1931 }
1932}
1933
1934#[derive(Debug)]
1939struct ItemLocationBoxItem {
1940 item_id: u32,
1941 construction_method: ConstructionMethod,
1942 extents: TryVec<ItemLocationBoxExtent>,
1944}
1945
1946#[derive(Clone, Copy, Debug, PartialEq)]
1947enum ConstructionMethod {
1948 File,
1949 Idat,
1950 #[allow(dead_code)] Item,
1952}
1953
1954#[derive(Clone, Debug)]
1957struct ItemLocationBoxExtent {
1958 extent_range: ExtentRange,
1959}
1960
1961#[derive(Clone, Debug)]
1962enum ExtentRange {
1963 WithLength(Range<u64>),
1964 ToEnd(RangeFrom<u64>),
1965}
1966
1967impl ExtentRange {
1968 const fn start(&self) -> u64 {
1969 match self {
1970 Self::WithLength(r) => r.start,
1971 Self::ToEnd(r) => r.start,
1972 }
1973 }
1974}
1975
1976struct BMFFBox<'a, T> {
1978 head: BoxHeader,
1979 content: Take<&'a mut T>,
1980}
1981
1982impl<T: Read> BMFFBox<'_, T> {
1983 fn read_into_try_vec(&mut self) -> std::io::Result<TryVec<u8>> {
1984 let limit = self.content.limit();
1985 let mut vec = if limit >= u64::MAX - BoxHeader::MIN_LARGE_SIZE {
1989 std::vec::Vec::new()
1991 } else {
1992 let mut v = std::vec::Vec::new();
1993 v.try_reserve_exact(limit as usize)
1994 .map_err(|_| std::io::ErrorKind::OutOfMemory)?;
1995 v
1996 };
1997 self.content.read_to_end(&mut vec)?; Ok(vec.into())
1999 }
2000}
2001
2002#[test]
2003fn box_read_to_end() {
2004 let tmp = &mut b"1234567890".as_slice();
2005 let mut src = BMFFBox {
2006 head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None },
2007 content: <_ as Read>::take(tmp, 5),
2008 };
2009 let buf = src.read_into_try_vec().unwrap();
2010 assert_eq!(buf.len(), 5);
2011 assert_eq!(buf, b"12345".as_ref());
2012}
2013
2014#[test]
2015fn box_read_to_end_oom() {
2016 let tmp = &mut b"1234567890".as_slice();
2017 let mut src = BMFFBox {
2018 head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None },
2019 content: <_ as Read>::take(tmp, u64::MAX / 2),
2021 };
2022 assert!(src.read_into_try_vec().is_err());
2023}
2024
2025struct BoxIter<'a, T> {
2026 src: &'a mut T,
2027}
2028
2029impl<T: Read> BoxIter<'_, T> {
2030 fn new(src: &mut T) -> BoxIter<'_, T> {
2031 BoxIter { src }
2032 }
2033
2034 fn next_box(&mut self) -> Result<Option<BMFFBox<'_, T>>> {
2035 let r = read_box_header(self.src);
2036 match r {
2037 Ok(h) => Ok(Some(BMFFBox {
2038 head: h,
2039 content: self.src.take(h.size - h.offset),
2040 })),
2041 Err(Error::UnexpectedEOF) => Ok(None),
2042 Err(e) => Err(e),
2043 }
2044 }
2045}
2046
2047impl<T: Read> Read for BMFFBox<'_, T> {
2048 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2049 self.content.read(buf)
2050 }
2051}
2052
2053impl<T: Offset> Offset for BMFFBox<'_, T> {
2054 fn offset(&self) -> u64 {
2055 self.content.get_ref().offset()
2056 }
2057}
2058
2059impl<T: Read> BMFFBox<'_, T> {
2060 fn bytes_left(&self) -> u64 {
2061 self.content.limit()
2062 }
2063
2064 const fn get_header(&self) -> &BoxHeader {
2065 &self.head
2066 }
2067
2068 fn box_iter(&mut self) -> BoxIter<'_, Self> {
2069 BoxIter::new(self)
2070 }
2071}
2072
2073impl<T> Drop for BMFFBox<'_, T> {
2074 fn drop(&mut self) {
2075 if self.content.limit() > 0 {
2076 let name: FourCC = From::from(self.head.name);
2077 debug!("Dropping {} bytes in '{}'", self.content.limit(), name);
2078 }
2079 }
2080}
2081
2082fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
2091 let size32 = be_u32(src)?;
2092 let name = BoxType::from(be_u32(src)?);
2093 let size = match size32 {
2094 0 => {
2096 u64::MAX
2098 },
2099 1 => {
2100 let size64 = be_u64(src)?;
2101 if size64 < BoxHeader::MIN_LARGE_SIZE {
2102 return Err(Error::InvalidData("malformed wide size"));
2103 }
2104 size64
2105 },
2106 _ => {
2107 if u64::from(size32) < BoxHeader::MIN_SIZE {
2108 return Err(Error::InvalidData("malformed size"));
2109 }
2110 u64::from(size32)
2111 },
2112 };
2113 let mut offset = match size32 {
2114 1 => BoxHeader::MIN_LARGE_SIZE,
2115 _ => BoxHeader::MIN_SIZE,
2116 };
2117 let uuid = if name == BoxType::UuidBox {
2118 if size >= offset + 16 {
2119 let mut buffer = [0u8; 16];
2120 let count = src.read(&mut buffer)?;
2121 offset += count.to_u64();
2122 if count == 16 {
2123 Some(buffer)
2124 } else {
2125 debug!("malformed uuid (short read), skipping");
2126 None
2127 }
2128 } else {
2129 debug!("malformed uuid, skipping");
2130 None
2131 }
2132 } else {
2133 None
2134 };
2135 assert!(offset <= size);
2136 Ok(BoxHeader { name, size, offset, uuid })
2137}
2138
2139fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> {
2141 let version = src.read_u8()?;
2142 let flags_a = src.read_u8()?;
2143 let flags_b = src.read_u8()?;
2144 let flags_c = src.read_u8()?;
2145 Ok((
2146 version,
2147 u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c),
2148 ))
2149}
2150
2151fn read_fullbox_version_no_flags<T: ReadBytesExt>(src: &mut T, options: &ParseOptions) -> Result<u8> {
2153 let (version, flags) = read_fullbox_extra(src)?;
2154
2155 if flags != 0 && !options.lenient {
2156 return Err(Error::Unsupported("expected flags to be 0"));
2157 }
2158
2159 Ok(version)
2160}
2161
2162fn skip_box_content<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<()> {
2164 let to_skip = {
2166 let header = src.get_header();
2167 debug!("{header:?} (skipped)");
2168 header
2169 .size
2170 .checked_sub(header.offset)
2171 .ok_or(Error::InvalidData("header offset > size"))?
2172 };
2173 assert_eq!(to_skip, src.bytes_left());
2174 skip(src, to_skip)
2175}
2176
2177fn skip_box_remain<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<()> {
2179 let remain = {
2180 let header = src.get_header();
2181 let len = src.bytes_left();
2182 debug!("remain {len} (skipped) in {header:?}");
2183 len
2184 };
2185 skip(src, remain)
2186}
2187
2188struct ResourceTracker<'a> {
2189 config: &'a DecodeConfig,
2190 #[cfg(feature = "eager")]
2191 current_memory: u64,
2192 #[cfg(feature = "eager")]
2193 peak_memory: u64,
2194}
2195
2196impl<'a> ResourceTracker<'a> {
2197 fn new(config: &'a DecodeConfig) -> Self {
2198 Self {
2199 config,
2200 #[cfg(feature = "eager")]
2201 current_memory: 0,
2202 #[cfg(feature = "eager")]
2203 peak_memory: 0,
2204 }
2205 }
2206
2207 #[cfg(feature = "eager")]
2208 fn reserve(&mut self, bytes: u64) -> Result<()> {
2209 self.current_memory = self.current_memory.saturating_add(bytes);
2210 self.peak_memory = self.peak_memory.max(self.current_memory);
2211
2212 if let Some(limit) = self.config.peak_memory_limit
2213 && self.peak_memory > limit {
2214 return Err(Error::ResourceLimitExceeded("peak memory limit exceeded"));
2215 }
2216
2217 Ok(())
2218 }
2219
2220 #[cfg(feature = "eager")]
2221 fn release(&mut self, bytes: u64) {
2222 self.current_memory = self.current_memory.saturating_sub(bytes);
2223 }
2224
2225 #[cfg(feature = "eager")]
2226 fn validate_total_megapixels(&self, width: u32, height: u32) -> Result<()> {
2227 if let Some(limit) = self.config.total_megapixels_limit {
2228 let megapixels = (width as u64)
2229 .checked_mul(height as u64)
2230 .ok_or(Error::InvalidData("dimension overflow"))?
2231 / 1_000_000;
2232
2233 if megapixels > limit as u64 {
2234 return Err(Error::ResourceLimitExceeded("total megapixels limit exceeded"));
2235 }
2236 }
2237
2238 Ok(())
2239 }
2240
2241 fn validate_animation_frames(&self, count: u32) -> Result<()> {
2242 if let Some(limit) = self.config.max_animation_frames
2243 && count > limit {
2244 return Err(Error::ResourceLimitExceeded("animation frame count limit exceeded"));
2245 }
2246
2247 Ok(())
2248 }
2249
2250 fn validate_grid_tiles(&self, count: u32) -> Result<()> {
2251 if let Some(limit) = self.config.max_grid_tiles
2252 && count > limit {
2253 return Err(Error::ResourceLimitExceeded("grid tile count limit exceeded"));
2254 }
2255
2256 Ok(())
2257 }
2258}
2259
2260#[cfg(feature = "eager")]
2271#[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader_with_config()` instead")]
2272#[allow(deprecated)]
2273pub fn read_avif_with_config<T: Read>(
2274 f: &mut T,
2275 config: &DecodeConfig,
2276 stop: &dyn Stop,
2277) -> Result<AvifData> {
2278 let mut tracker = ResourceTracker::new(config);
2279 let mut f = OffsetReader::new(f);
2280
2281 let mut iter = BoxIter::new(&mut f);
2282
2283 let (major_brand, compatible_brands) = if let Some(mut b) = iter.next_box()? {
2285 if b.head.name == BoxType::FileTypeBox {
2286 let ftyp = read_ftyp(&mut b)?;
2287 if ftyp.major_brand != b"avif" && ftyp.major_brand != b"avis" {
2289 warn!("major_brand: {}", ftyp.major_brand);
2290 return Err(Error::InvalidData("ftyp must be 'avif' or 'avis'"));
2291 }
2292 let major = ftyp.major_brand.value;
2293 let compat = ftyp.compatible_brands.iter().map(|b| b.value).collect();
2294 (major, compat)
2295 } else {
2296 return Err(Error::InvalidData("'ftyp' box must occur first"));
2297 }
2298 } else {
2299 return Err(Error::InvalidData("'ftyp' box must occur first"));
2300 };
2301
2302 let mut meta = None;
2303 let mut mdats = TryVec::new();
2304 let mut animation_data: Option<(u32, SampleTable)> = None;
2305
2306 let parse_opts = ParseOptions { lenient: config.lenient };
2307
2308 while let Some(mut b) = iter.next_box()? {
2309 stop.check()?;
2310
2311 match b.head.name {
2312 BoxType::MetadataBox => {
2313 if meta.is_some() {
2314 return Err(Error::InvalidData("There should be zero or one meta boxes per ISO 14496-12:2015 § 8.11.1.1"));
2315 }
2316 meta = Some(read_avif_meta(&mut b, &parse_opts)?);
2317 },
2318 BoxType::MovieBox => {
2319 animation_data = read_moov(&mut b)?;
2320 },
2321 BoxType::MediaDataBox => {
2322 if b.bytes_left() > 0 {
2323 let offset = b.offset();
2324 let size = b.bytes_left();
2325 tracker.reserve(size)?;
2326 let data = b.read_into_try_vec()?;
2327 tracker.release(size);
2328 mdats.push(MediaDataBox { offset, data })?;
2329 }
2330 },
2331 _ => skip_box_content(&mut b)?,
2332 }
2333
2334 check_parser_state(&b.head, &b.content)?;
2335 }
2336
2337 let meta = meta.ok_or(Error::InvalidData("missing meta"))?;
2338
2339 let is_grid = meta
2341 .item_infos
2342 .iter()
2343 .find(|x| x.item_id == meta.primary_item_id)
2344 .is_some_and(|info| {
2345 let is_g = info.item_type == b"grid";
2346 if is_g {
2347 log::debug!("Grid image detected: primary_item_id={}", meta.primary_item_id);
2348 }
2349 is_g
2350 });
2351
2352 let mut grid_config = if is_grid {
2354 meta.properties
2355 .iter()
2356 .find(|prop| {
2357 prop.item_id == meta.primary_item_id
2358 && matches!(prop.property, ItemProperty::ImageGrid(_))
2359 })
2360 .and_then(|prop| match &prop.property {
2361 ItemProperty::ImageGrid(config) => {
2362 log::debug!("Grid: found explicit ImageGrid property: {:?}", config);
2363 Some(config.clone())
2364 },
2365 _ => None,
2366 })
2367 } else {
2368 None
2369 };
2370
2371 let tile_item_ids: TryVec<u32> = if is_grid {
2373 let mut tiles_with_index: TryVec<(u32, u16)> = TryVec::new();
2375 for iref in meta.item_references.iter() {
2376 if iref.from_item_id == meta.primary_item_id && iref.item_type == b"dimg" {
2378 tiles_with_index.push((iref.to_item_id, iref.reference_index))?;
2379 }
2380 }
2381
2382 tracker.validate_grid_tiles(tiles_with_index.len() as u32)?;
2384
2385 tiles_with_index.sort_by_key(|&(_, idx)| idx);
2387
2388 let mut ids = TryVec::new();
2390 for (tile_id, _) in tiles_with_index.iter() {
2391 ids.push(*tile_id)?;
2392 }
2393
2394 if grid_config.is_none() && !ids.is_empty() {
2398 let grid_dims = meta.properties.iter()
2400 .find(|p| p.item_id == meta.primary_item_id)
2401 .and_then(|p| match &p.property {
2402 ItemProperty::ImageSpatialExtents(e) => Some(e),
2403 _ => None,
2404 });
2405
2406 let tile_dims = ids.first().and_then(|&tile_id| {
2407 meta.properties.iter()
2408 .find(|p| p.item_id == tile_id)
2409 .and_then(|p| match &p.property {
2410 ItemProperty::ImageSpatialExtents(e) => Some(e),
2411 _ => None,
2412 })
2413 });
2414
2415 if let (Some(grid), Some(tile)) = (grid_dims, tile_dims) {
2416 tracker.validate_total_megapixels(grid.width, grid.height)?;
2418
2419 if tile.width == 0 || tile.height == 0 {
2421 log::warn!("Grid: tile has zero dimensions, using fallback");
2422 } else if grid.width % tile.width == 0 && grid.height % tile.height == 0 {
2423 let columns = grid.width / tile.width;
2425 let rows = grid.height / tile.height;
2426
2427 if columns > 255 || rows > 255 {
2429 log::warn!("Grid: calculated dimensions {}×{} exceed 255, using fallback", rows, columns);
2430 } else {
2431 log::debug!("Grid: calculated {}×{} layout from ispe dimensions", rows, columns);
2432 grid_config = Some(GridConfig {
2433 rows: rows as u8,
2434 columns: columns as u8,
2435 output_width: grid.width,
2436 output_height: grid.height,
2437 });
2438 }
2439 } else {
2440 log::warn!("Grid: dimension mismatch - grid {}×{} not evenly divisible by tile {}×{}, using fallback",
2441 grid.width, grid.height, tile.width, tile.height);
2442 }
2443 }
2444
2445 if grid_config.is_none() {
2447 log::debug!("Grid: using fallback {}×1 layout inference", ids.len());
2448 grid_config = Some(GridConfig {
2449 rows: ids.len() as u8, columns: 1, output_width: 0, output_height: 0, });
2454 }
2455 }
2456
2457 ids
2458 } else {
2459 TryVec::new()
2460 };
2461
2462 let alpha_item_id = meta
2463 .item_references
2464 .iter()
2465 .filter(|iref| {
2467 iref.to_item_id == meta.primary_item_id
2468 && iref.from_item_id != meta.primary_item_id
2469 && iref.item_type == b"auxl"
2470 })
2471 .map(|iref| iref.from_item_id)
2472 .find(|&item_id| {
2474 meta.properties.iter().any(|prop| {
2475 prop.item_id == item_id
2476 && match &prop.property {
2477 ItemProperty::AuxiliaryType(urn) => {
2478 urn.type_subtype().0 == b"urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
2479 }
2480 _ => false,
2481 }
2482 })
2483 });
2484
2485 macro_rules! find_prop {
2487 ($variant:ident) => {
2488 meta.properties.iter().find_map(|p| {
2489 if p.item_id == meta.primary_item_id {
2490 match &p.property {
2491 ItemProperty::$variant(c) => Some(c.clone()),
2492 _ => None,
2493 }
2494 } else {
2495 None
2496 }
2497 })
2498 };
2499 }
2500
2501 let av1_config = find_prop!(AV1Config);
2502 let color_info = find_prop!(ColorInformation);
2503 let rotation = find_prop!(Rotation);
2504 let mirror = find_prop!(Mirror);
2505 let clean_aperture = find_prop!(CleanAperture);
2506 let pixel_aspect_ratio = find_prop!(PixelAspectRatio);
2507 let content_light_level = find_prop!(ContentLightLevel);
2508 let mastering_display = find_prop!(MasteringDisplayColourVolume);
2509 let content_colour_volume = find_prop!(ContentColourVolume);
2510 let ambient_viewing = find_prop!(AmbientViewingEnvironment);
2511 let operating_point = find_prop!(OperatingPointSelector);
2512 let layer_selector = find_prop!(LayerSelector);
2513 let layered_image_indexing = find_prop!(AV1LayeredImageIndexing);
2514
2515 let mut context = AvifData {
2516 premultiplied_alpha: alpha_item_id.is_some_and(|alpha_item_id| {
2517 meta.item_references.iter().any(|iref| {
2518 iref.from_item_id == meta.primary_item_id
2519 && iref.to_item_id == alpha_item_id
2520 && iref.item_type == b"prem"
2521 })
2522 }),
2523 av1_config,
2524 color_info,
2525 rotation,
2526 mirror,
2527 clean_aperture,
2528 pixel_aspect_ratio,
2529 content_light_level,
2530 mastering_display,
2531 content_colour_volume,
2532 ambient_viewing,
2533 operating_point,
2534 layer_selector,
2535 layered_image_indexing,
2536 major_brand,
2537 compatible_brands,
2538 ..Default::default()
2539 };
2540
2541 let mut extract_item_data = |loc: &ItemLocationBoxItem, buf: &mut TryVec<u8>| -> Result<()> {
2543 match loc.construction_method {
2544 ConstructionMethod::File => {
2545 for extent in loc.extents.iter() {
2546 let mut found = false;
2547 for mdat in mdats.iter_mut() {
2548 if mdat.matches_extent(&extent.extent_range) {
2549 buf.append(&mut mdat.data)?;
2550 found = true;
2551 break;
2552 } else if mdat.contains_extent(&extent.extent_range) {
2553 mdat.read_extent(&extent.extent_range, buf)?;
2554 found = true;
2555 break;
2556 }
2557 }
2558 if !found {
2559 return Err(Error::InvalidData("iloc contains an extent that is not in mdat"));
2560 }
2561 }
2562 Ok(())
2563 },
2564 ConstructionMethod::Idat => {
2565 let idat_data = meta.idat.as_ref().ok_or(Error::InvalidData("idat box missing but construction_method is Idat"))?;
2566 for extent in loc.extents.iter() {
2567 match &extent.extent_range {
2568 ExtentRange::WithLength(range) => {
2569 let start = usize::try_from(range.start).map_err(|_| Error::InvalidData("extent start too large"))?;
2570 let end = usize::try_from(range.end).map_err(|_| Error::InvalidData("extent end too large"))?;
2571 if end > idat_data.len() {
2572 return Err(Error::InvalidData("extent exceeds idat size"));
2573 }
2574 buf.extend_from_slice(&idat_data[start..end]).map_err(|_| Error::OutOfMemory)?;
2575 },
2576 ExtentRange::ToEnd(range) => {
2577 let start = usize::try_from(range.start).map_err(|_| Error::InvalidData("extent start too large"))?;
2578 if start >= idat_data.len() {
2579 return Err(Error::InvalidData("extent start exceeds idat size"));
2580 }
2581 buf.extend_from_slice(&idat_data[start..]).map_err(|_| Error::OutOfMemory)?;
2582 },
2583 }
2584 }
2585 Ok(())
2586 },
2587 ConstructionMethod::Item => {
2588 Err(Error::Unsupported("construction_method 'item' not supported"))
2589 },
2590 }
2591 };
2592
2593 if is_grid {
2596 for (idx, &tile_id) in tile_item_ids.iter().enumerate() {
2598 if idx % 16 == 0 {
2599 stop.check()?;
2600 }
2601
2602 let mut tile_data = TryVec::new();
2603
2604 if let Some(loc) = meta.iloc_items.iter().find(|loc| loc.item_id == tile_id) {
2605 extract_item_data(loc, &mut tile_data)?;
2606 } else {
2607 return Err(Error::InvalidData("grid tile not found in iloc"));
2608 }
2609
2610 context.grid_tiles.push(tile_data)?;
2611 }
2612
2613 context.grid_config = grid_config;
2615 } else {
2616 for loc in meta.iloc_items.iter() {
2618 let item_data = if loc.item_id == meta.primary_item_id {
2619 &mut context.primary_item
2620 } else if Some(loc.item_id) == alpha_item_id {
2621 context.alpha_item.get_or_insert_with(TryVec::new)
2622 } else {
2623 continue;
2624 };
2625
2626 extract_item_data(loc, item_data)?;
2627 }
2628 }
2629
2630 if let Some((media_timescale, sample_table)) = animation_data {
2632 let frame_count = sample_table.sample_sizes.len() as u32;
2633 tracker.validate_animation_frames(frame_count)?;
2634
2635 log::debug!("Animation: extracting frames (media_timescale={})", media_timescale);
2636 match extract_animation_frames(&sample_table, media_timescale, &mut mdats) {
2637 Ok(frames) => {
2638 if !frames.is_empty() {
2639 log::debug!("Animation: extracted {} frames", frames.len());
2640 context.animation = Some(AnimationConfig {
2641 loop_count: 0, frames,
2643 });
2644 }
2645 }
2646 Err(e) => {
2647 log::warn!("Animation: failed to extract frames: {}", e);
2648 }
2649 }
2650 }
2651
2652 Ok(context)
2653}
2654
2655#[cfg(feature = "eager")]
2664#[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader_with_config()` with `DecodeConfig::lenient()` instead")]
2665#[allow(deprecated)]
2666pub fn read_avif_with_options<T: Read>(f: &mut T, options: &ParseOptions) -> Result<AvifData> {
2667 let config = DecodeConfig::unlimited().lenient(options.lenient);
2668 read_avif_with_config(f, &config, &Unstoppable)
2669}
2670
2671#[cfg(feature = "eager")]
2679#[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader()` instead")]
2680#[allow(deprecated)]
2681pub fn read_avif<T: Read>(f: &mut T) -> Result<AvifData> {
2682 read_avif_with_options(f, &ParseOptions::default())
2683}
2684
2685fn read_avif_meta<T: Read + Offset>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<AvifInternalMeta> {
2690 let version = read_fullbox_version_no_flags(src, options)?;
2691
2692 if version != 0 {
2693 return Err(Error::Unsupported("unsupported meta version"));
2694 }
2695
2696 let mut primary_item_id = None;
2697 let mut item_infos = None;
2698 let mut iloc_items = None;
2699 let mut item_references = TryVec::new();
2700 let mut properties = TryVec::new();
2701 let mut idat = None;
2702
2703 let mut iter = src.box_iter();
2704 while let Some(mut b) = iter.next_box()? {
2705 match b.head.name {
2706 BoxType::ItemInfoBox => {
2707 if item_infos.is_some() {
2708 return Err(Error::InvalidData("There should be zero or one iinf boxes per ISO 14496-12:2015 § 8.11.6.1"));
2709 }
2710 item_infos = Some(read_iinf(&mut b, options)?);
2711 },
2712 BoxType::ItemLocationBox => {
2713 if iloc_items.is_some() {
2714 return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.3.1"));
2715 }
2716 iloc_items = Some(read_iloc(&mut b, options)?);
2717 },
2718 BoxType::PrimaryItemBox => {
2719 if primary_item_id.is_some() {
2720 return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.4.1"));
2721 }
2722 primary_item_id = Some(read_pitm(&mut b, options)?);
2723 },
2724 BoxType::ImageReferenceBox => {
2725 item_references.append(&mut read_iref(&mut b, options)?)?;
2726 },
2727 BoxType::ImagePropertiesBox => {
2728 properties = read_iprp(&mut b, options)?;
2729 },
2730 BoxType::ItemDataBox => {
2731 if idat.is_some() {
2732 return Err(Error::InvalidData("There should be zero or one idat boxes"));
2733 }
2734 idat = Some(b.read_into_try_vec()?);
2735 },
2736 BoxType::HandlerBox => {
2737 let hdlr = read_hdlr(&mut b)?;
2738 if hdlr.handler_type != b"pict" {
2739 warn!("hdlr handler_type: {}", hdlr.handler_type);
2740 return Err(Error::InvalidData("meta handler_type must be 'pict' for AVIF"));
2741 }
2742 },
2743 _ => skip_box_content(&mut b)?,
2744 }
2745
2746 check_parser_state(&b.head, &b.content)?;
2747 }
2748
2749 let primary_item_id = primary_item_id.ok_or(Error::InvalidData("Required pitm box not present in meta box"))?;
2750
2751 let item_infos = item_infos.ok_or(Error::InvalidData("iinf missing"))?;
2752
2753 if let Some(item_info) = item_infos.iter().find(|x| x.item_id == primary_item_id) {
2754 if item_info.item_type != b"av01" && item_info.item_type != b"grid" {
2756 warn!("primary_item_id type: {}", item_info.item_type);
2757 return Err(Error::InvalidData("primary_item_id type is not av01 or grid"));
2758 }
2759 } else {
2760 return Err(Error::InvalidData("primary_item_id not present in iinf box"));
2761 }
2762
2763 Ok(AvifInternalMeta {
2764 properties,
2765 item_references,
2766 primary_item_id,
2767 iloc_items: iloc_items.ok_or(Error::InvalidData("iloc missing"))?,
2768 item_infos,
2769 idat,
2770 })
2771}
2772
2773fn read_hdlr<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<HandlerBox> {
2776 let (_version, _flags) = read_fullbox_extra(src)?;
2777 skip(src, 4)?;
2779 let handler_type = be_u32(src)?;
2781 skip_box_remain(src)?;
2783 Ok(HandlerBox {
2784 handler_type: FourCC::from(handler_type),
2785 })
2786}
2787
2788fn read_pitm<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<u32> {
2791 let version = read_fullbox_version_no_flags(src, options)?;
2792
2793 let item_id = match version {
2794 0 => be_u16(src)?.into(),
2795 1 => be_u32(src)?,
2796 _ => return Err(Error::Unsupported("unsupported pitm version")),
2797 };
2798
2799 Ok(item_id)
2800}
2801
2802fn read_iinf<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<ItemInfoEntry>> {
2805 let version = read_fullbox_version_no_flags(src, options)?;
2806
2807 match version {
2808 0 | 1 => (),
2809 _ => return Err(Error::Unsupported("unsupported iinf version")),
2810 }
2811
2812 let entry_count = if version == 0 {
2813 be_u16(src)?.to_usize()
2814 } else {
2815 be_u32(src)?.to_usize()
2816 };
2817 let mut item_infos = TryVec::with_capacity(entry_count)?;
2818
2819 let mut iter = src.box_iter();
2820 while let Some(mut b) = iter.next_box()? {
2821 if b.head.name != BoxType::ItemInfoEntry {
2822 return Err(Error::InvalidData("iinf box should contain only infe boxes"));
2823 }
2824
2825 item_infos.push(read_infe(&mut b)?)?;
2826
2827 check_parser_state(&b.head, &b.content)?;
2828 }
2829
2830 Ok(item_infos)
2831}
2832
2833fn read_infe<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ItemInfoEntry> {
2836 let (version, _) = read_fullbox_extra(src)?;
2839
2840 let item_id = match version {
2842 2 => be_u16(src)?.into(),
2843 3 => be_u32(src)?,
2844 _ => return Err(Error::Unsupported("unsupported version in 'infe' box")),
2845 };
2846
2847 let item_protection_index = be_u16(src)?;
2848
2849 if item_protection_index != 0 {
2850 return Err(Error::Unsupported("protected items (infe.item_protection_index != 0) are not supported"));
2851 }
2852
2853 let item_type = FourCC::from(be_u32(src)?);
2854 debug!("infe item_id {item_id} item_type: {item_type}");
2855
2856 skip_box_remain(src)?;
2858
2859 Ok(ItemInfoEntry { item_id, item_type })
2860}
2861
2862fn read_iref<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<SingleItemTypeReferenceBox>> {
2863 let mut item_references = TryVec::new();
2864 let version = read_fullbox_version_no_flags(src, options)?;
2865 if version > 1 {
2866 return Err(Error::Unsupported("iref version"));
2867 }
2868
2869 let mut iter = src.box_iter();
2870 while let Some(mut b) = iter.next_box()? {
2871 let from_item_id = if version == 0 {
2872 be_u16(&mut b)?.into()
2873 } else {
2874 be_u32(&mut b)?
2875 };
2876 let reference_count = be_u16(&mut b)?;
2877 for reference_index in 0..reference_count {
2878 let to_item_id = if version == 0 {
2879 be_u16(&mut b)?.into()
2880 } else {
2881 be_u32(&mut b)?
2882 };
2883 if from_item_id == to_item_id {
2884 return Err(Error::InvalidData("from_item_id and to_item_id must be different"));
2885 }
2886 item_references.push(SingleItemTypeReferenceBox {
2887 item_type: b.head.name.into(),
2888 from_item_id,
2889 to_item_id,
2890 reference_index,
2891 })?;
2892 }
2893 check_parser_state(&b.head, &b.content)?;
2894 }
2895 Ok(item_references)
2896}
2897
2898fn read_iprp<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<AssociatedProperty>> {
2899 let mut iter = src.box_iter();
2900 let mut properties = TryVec::new();
2901 let mut associations = TryVec::new();
2902
2903 while let Some(mut b) = iter.next_box()? {
2904 match b.head.name {
2905 BoxType::ItemPropertyContainerBox => {
2906 properties = read_ipco(&mut b, options)?;
2907 },
2908 BoxType::ItemPropertyAssociationBox => {
2909 associations = read_ipma(&mut b)?;
2910 },
2911 _ => return Err(Error::InvalidData("unexpected ipco child")),
2912 }
2913 }
2914
2915 let mut associated = TryVec::new();
2916 for a in associations {
2917 let index = match a.property_index {
2918 0 => continue,
2919 x => x as usize - 1,
2920 };
2921 if let Some(prop) = properties.get(index)
2922 && *prop != ItemProperty::Unsupported {
2923 associated.push(AssociatedProperty {
2924 item_id: a.item_id,
2925 property: prop.try_clone()?,
2926 })?;
2927 }
2928 }
2929 Ok(associated)
2930}
2931
2932#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2934pub(crate) struct ImageSpatialExtents {
2935 pub(crate) width: u32,
2936 pub(crate) height: u32,
2937}
2938
2939#[derive(Debug, PartialEq)]
2940pub(crate) enum ItemProperty {
2941 Channels(ArrayVec<u8, 16>),
2942 AuxiliaryType(AuxiliaryTypeProperty),
2943 ImageSpatialExtents(ImageSpatialExtents),
2944 ImageGrid(GridConfig),
2945 AV1Config(AV1Config),
2946 ColorInformation(ColorInformation),
2947 Rotation(ImageRotation),
2948 Mirror(ImageMirror),
2949 CleanAperture(CleanAperture),
2950 PixelAspectRatio(PixelAspectRatio),
2951 ContentLightLevel(ContentLightLevel),
2952 MasteringDisplayColourVolume(MasteringDisplayColourVolume),
2953 ContentColourVolume(ContentColourVolume),
2954 AmbientViewingEnvironment(AmbientViewingEnvironment),
2955 OperatingPointSelector(OperatingPointSelector),
2956 LayerSelector(LayerSelector),
2957 AV1LayeredImageIndexing(AV1LayeredImageIndexing),
2958 Unsupported,
2959}
2960
2961impl TryClone for ItemProperty {
2962 fn try_clone(&self) -> Result<Self, TryReserveError> {
2963 Ok(match self {
2964 Self::Channels(val) => Self::Channels(val.clone()),
2965 Self::AuxiliaryType(val) => Self::AuxiliaryType(val.try_clone()?),
2966 Self::ImageSpatialExtents(val) => Self::ImageSpatialExtents(*val),
2967 Self::ImageGrid(val) => Self::ImageGrid(val.clone()),
2968 Self::AV1Config(val) => Self::AV1Config(val.clone()),
2969 Self::ColorInformation(val) => Self::ColorInformation(val.clone()),
2970 Self::Rotation(val) => Self::Rotation(*val),
2971 Self::Mirror(val) => Self::Mirror(*val),
2972 Self::CleanAperture(val) => Self::CleanAperture(*val),
2973 Self::PixelAspectRatio(val) => Self::PixelAspectRatio(*val),
2974 Self::ContentLightLevel(val) => Self::ContentLightLevel(*val),
2975 Self::MasteringDisplayColourVolume(val) => Self::MasteringDisplayColourVolume(*val),
2976 Self::ContentColourVolume(val) => Self::ContentColourVolume(*val),
2977 Self::AmbientViewingEnvironment(val) => Self::AmbientViewingEnvironment(*val),
2978 Self::OperatingPointSelector(val) => Self::OperatingPointSelector(*val),
2979 Self::LayerSelector(val) => Self::LayerSelector(*val),
2980 Self::AV1LayeredImageIndexing(val) => Self::AV1LayeredImageIndexing(*val),
2981 Self::Unsupported => Self::Unsupported,
2982 })
2983 }
2984}
2985
2986struct Association {
2987 item_id: u32,
2988 #[allow(unused)]
2989 essential: bool,
2990 property_index: u16,
2991}
2992
2993pub(crate) struct AssociatedProperty {
2994 pub item_id: u32,
2995 pub property: ItemProperty,
2996}
2997
2998fn read_ipma<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<Association>> {
2999 let (version, flags) = read_fullbox_extra(src)?;
3000
3001 let mut associations = TryVec::new();
3002
3003 let entry_count = be_u32(src)?;
3004 for _ in 0..entry_count {
3005 let item_id = if version == 0 {
3006 be_u16(src)?.into()
3007 } else {
3008 be_u32(src)?
3009 };
3010 let association_count = src.read_u8()?;
3011 for _ in 0..association_count {
3012 let num_association_bytes = if flags & 1 == 1 { 2 } else { 1 };
3013 let association = &mut [0; 2][..num_association_bytes];
3014 src.read_exact(association)?;
3015 let mut association = BitReader::new(association);
3016 let essential = association.read_bool()?;
3017 let property_index = association.read_u16(association.remaining().try_into()?)?;
3018 associations.push(Association {
3019 item_id,
3020 essential,
3021 property_index,
3022 })?;
3023 }
3024 }
3025 Ok(associations)
3026}
3027
3028fn read_ipco<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<ItemProperty>> {
3029 let mut properties = TryVec::new();
3030
3031 let mut iter = src.box_iter();
3032 while let Some(mut b) = iter.next_box()? {
3033 let prop = match b.head.name {
3035 BoxType::PixelInformationBox => ItemProperty::Channels(read_pixi(&mut b, options)?),
3036 BoxType::AuxiliaryTypeProperty => ItemProperty::AuxiliaryType(read_auxc(&mut b, options)?),
3037 BoxType::ImageSpatialExtentsBox => ItemProperty::ImageSpatialExtents(read_ispe(&mut b, options)?),
3038 BoxType::ImageGridBox => ItemProperty::ImageGrid(read_grid(&mut b, options)?),
3039 BoxType::AV1CodecConfigurationBox => ItemProperty::AV1Config(read_av1c(&mut b)?),
3040 BoxType::ColorInformationBox => {
3041 match read_colr(&mut b) {
3042 Ok(colr) => ItemProperty::ColorInformation(colr),
3043 Err(_) => ItemProperty::Unsupported,
3044 }
3045 },
3046 BoxType::ImageRotationBox => ItemProperty::Rotation(read_irot(&mut b)?),
3047 BoxType::ImageMirrorBox => ItemProperty::Mirror(read_imir(&mut b)?),
3048 BoxType::CleanApertureBox => ItemProperty::CleanAperture(read_clap(&mut b)?),
3049 BoxType::PixelAspectRatioBox => ItemProperty::PixelAspectRatio(read_pasp(&mut b)?),
3050 BoxType::ContentLightLevelBox => ItemProperty::ContentLightLevel(read_clli(&mut b)?),
3051 BoxType::MasteringDisplayColourVolumeBox => ItemProperty::MasteringDisplayColourVolume(read_mdcv(&mut b)?),
3052 BoxType::ContentColourVolumeBox => ItemProperty::ContentColourVolume(read_cclv(&mut b)?),
3053 BoxType::AmbientViewingEnvironmentBox => ItemProperty::AmbientViewingEnvironment(read_amve(&mut b)?),
3054 BoxType::OperatingPointSelectorBox => ItemProperty::OperatingPointSelector(read_a1op(&mut b)?),
3055 BoxType::LayerSelectorBox => ItemProperty::LayerSelector(read_lsel(&mut b)?),
3056 BoxType::AV1LayeredImageIndexingBox => ItemProperty::AV1LayeredImageIndexing(read_a1lx(&mut b)?),
3057 _ => {
3058 skip_box_remain(&mut b)?;
3059 ItemProperty::Unsupported
3060 },
3061 };
3062 properties.push(prop)?;
3063 }
3064 Ok(properties)
3065}
3066
3067fn read_pixi<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<ArrayVec<u8, 16>> {
3068 let version = read_fullbox_version_no_flags(src, options)?;
3069 if version != 0 {
3070 return Err(Error::Unsupported("pixi version"));
3071 }
3072
3073 let num_channels = usize::from(src.read_u8()?);
3074 let mut channels = ArrayVec::new();
3075 channels.extend((0..num_channels.min(channels.capacity())).map(|_| 0));
3076 debug_assert_eq!(num_channels, channels.len());
3077 src.read_exact(&mut channels).map_err(|_| Error::InvalidData("invalid num_channels"))?;
3078
3079 if options.lenient && src.bytes_left() > 0 {
3081 skip(src, src.bytes_left())?;
3082 }
3083
3084 check_parser_state(&src.head, &src.content)?;
3085 Ok(channels)
3086}
3087
3088#[derive(Debug, PartialEq)]
3089struct AuxiliaryTypeProperty {
3090 aux_data: TryString,
3091}
3092
3093impl AuxiliaryTypeProperty {
3094 #[must_use]
3095 fn type_subtype(&self) -> (&[u8], &[u8]) {
3096 let split = self.aux_data.iter().position(|&b| b == b'\0')
3097 .map(|pos| self.aux_data.split_at(pos));
3098 if let Some((aux_type, rest)) = split {
3099 (aux_type, &rest[1..])
3100 } else {
3101 (&self.aux_data, &[])
3102 }
3103 }
3104}
3105
3106impl TryClone for AuxiliaryTypeProperty {
3107 fn try_clone(&self) -> Result<Self, TryReserveError> {
3108 Ok(Self {
3109 aux_data: self.aux_data.try_clone()?,
3110 })
3111 }
3112}
3113
3114fn read_auxc<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<AuxiliaryTypeProperty> {
3115 let version = read_fullbox_version_no_flags(src, options)?;
3116 if version != 0 {
3117 return Err(Error::Unsupported("auxC version"));
3118 }
3119
3120 let aux_data = src.read_into_try_vec()?;
3121
3122 Ok(AuxiliaryTypeProperty { aux_data })
3123}
3124
3125fn read_av1c<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<AV1Config> {
3128 let byte0 = src.read_u8()?;
3130 let marker = byte0 >> 7;
3131 let version = byte0 & 0x7F;
3132
3133 if marker != 1 {
3134 return Err(Error::InvalidData("av1C marker must be 1"));
3135 }
3136 if version != 1 {
3137 return Err(Error::Unsupported("av1C version must be 1"));
3138 }
3139
3140 let byte1 = src.read_u8()?;
3141 let profile = byte1 >> 5;
3142 let level = byte1 & 0x1F;
3143
3144 let byte2 = src.read_u8()?;
3145 let tier = byte2 >> 7;
3146 let high_bitdepth = (byte2 >> 6) & 1;
3147 let twelve_bit = (byte2 >> 5) & 1;
3148 let monochrome = (byte2 >> 4) & 1 != 0;
3149 let chroma_subsampling_x = (byte2 >> 3) & 1;
3150 let chroma_subsampling_y = (byte2 >> 2) & 1;
3151 let chroma_sample_position = byte2 & 0x03;
3152
3153 let byte3 = src.read_u8()?;
3154 let _ = byte3;
3157
3158 let bit_depth = if high_bitdepth != 0 {
3159 if twelve_bit != 0 { 12 } else { 10 }
3160 } else {
3161 8
3162 };
3163
3164 skip_box_remain(src)?;
3166
3167 Ok(AV1Config {
3168 profile,
3169 level,
3170 tier,
3171 bit_depth,
3172 monochrome,
3173 chroma_subsampling_x,
3174 chroma_subsampling_y,
3175 chroma_sample_position,
3176 })
3177}
3178
3179fn read_colr<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ColorInformation> {
3182 let colour_type = be_u32(src)?;
3184
3185 match &colour_type.to_be_bytes() {
3186 b"nclx" => {
3187 let color_primaries = be_u16(src)?;
3188 let transfer_characteristics = be_u16(src)?;
3189 let matrix_coefficients = be_u16(src)?;
3190 let full_range_byte = src.read_u8()?;
3191 let full_range = (full_range_byte >> 7) != 0;
3192 skip_box_remain(src)?;
3194 Ok(ColorInformation::Nclx {
3195 color_primaries,
3196 transfer_characteristics,
3197 matrix_coefficients,
3198 full_range,
3199 })
3200 }
3201 b"rICC" | b"prof" => {
3202 let icc_data = src.read_into_try_vec()?;
3203 Ok(ColorInformation::IccProfile(icc_data.to_vec()))
3204 }
3205 _ => {
3206 skip_box_remain(src)?;
3207 Err(Error::Unsupported("unsupported colr colour_type"))
3208 }
3209 }
3210}
3211
3212fn read_irot<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ImageRotation> {
3215 let byte = src.read_u8()?;
3216 let angle_code = byte & 0x03;
3217 let angle = match angle_code {
3218 0 => 0,
3219 1 => 90,
3220 2 => 180,
3221 3 => 270,
3222 _ => unreachable!(),
3223 };
3224 skip_box_remain(src)?;
3225 Ok(ImageRotation { angle })
3226}
3227
3228fn read_imir<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ImageMirror> {
3231 let byte = src.read_u8()?;
3232 let axis = byte & 0x01;
3233 skip_box_remain(src)?;
3234 Ok(ImageMirror { axis })
3235}
3236
3237fn read_clap<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<CleanAperture> {
3240 let width_n = be_u32(src)?;
3241 let width_d = be_u32(src)?;
3242 let height_n = be_u32(src)?;
3243 let height_d = be_u32(src)?;
3244 let horiz_off_n = be_i32(src)?;
3245 let horiz_off_d = be_u32(src)?;
3246 let vert_off_n = be_i32(src)?;
3247 let vert_off_d = be_u32(src)?;
3248 if width_d == 0 || height_d == 0 || horiz_off_d == 0 || vert_off_d == 0 {
3250 return Err(Error::InvalidData("clap denominator cannot be zero"));
3251 }
3252 skip_box_remain(src)?;
3253 Ok(CleanAperture {
3254 width_n, width_d,
3255 height_n, height_d,
3256 horiz_off_n, horiz_off_d,
3257 vert_off_n, vert_off_d,
3258 })
3259}
3260
3261fn read_pasp<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<PixelAspectRatio> {
3264 let h_spacing = be_u32(src)?;
3265 let v_spacing = be_u32(src)?;
3266 skip_box_remain(src)?;
3267 Ok(PixelAspectRatio { h_spacing, v_spacing })
3268}
3269
3270fn read_clli<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ContentLightLevel> {
3273 let max_content_light_level = be_u16(src)?;
3274 let max_pic_average_light_level = be_u16(src)?;
3275 skip_box_remain(src)?;
3276 Ok(ContentLightLevel {
3277 max_content_light_level,
3278 max_pic_average_light_level,
3279 })
3280}
3281
3282fn read_mdcv<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<MasteringDisplayColourVolume> {
3285 let primaries = [
3287 (be_u16(src)?, be_u16(src)?),
3288 (be_u16(src)?, be_u16(src)?),
3289 (be_u16(src)?, be_u16(src)?),
3290 ];
3291 let white_point = (be_u16(src)?, be_u16(src)?);
3292 let max_luminance = be_u32(src)?;
3293 let min_luminance = be_u32(src)?;
3294 skip_box_remain(src)?;
3295 Ok(MasteringDisplayColourVolume {
3296 primaries,
3297 white_point,
3298 max_luminance,
3299 min_luminance,
3300 })
3301}
3302
3303fn read_cclv<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ContentColourVolume> {
3306 let flags = src.read_u8()?;
3307 let primaries_present = flags & 0x20 != 0;
3308 let min_lum_present = flags & 0x10 != 0;
3309 let max_lum_present = flags & 0x08 != 0;
3310 let avg_lum_present = flags & 0x04 != 0;
3311
3312 let primaries = if primaries_present {
3313 Some([
3314 (be_i32(src)?, be_i32(src)?),
3315 (be_i32(src)?, be_i32(src)?),
3316 (be_i32(src)?, be_i32(src)?),
3317 ])
3318 } else {
3319 None
3320 };
3321
3322 let min_luminance = if min_lum_present { Some(be_u32(src)?) } else { None };
3323 let max_luminance = if max_lum_present { Some(be_u32(src)?) } else { None };
3324 let avg_luminance = if avg_lum_present { Some(be_u32(src)?) } else { None };
3325
3326 skip_box_remain(src)?;
3327 Ok(ContentColourVolume {
3328 primaries,
3329 min_luminance,
3330 max_luminance,
3331 avg_luminance,
3332 })
3333}
3334
3335fn read_amve<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<AmbientViewingEnvironment> {
3338 let ambient_illuminance = be_u32(src)?;
3339 let ambient_light_x = be_u16(src)?;
3340 let ambient_light_y = be_u16(src)?;
3341 skip_box_remain(src)?;
3342 Ok(AmbientViewingEnvironment {
3343 ambient_illuminance,
3344 ambient_light_x,
3345 ambient_light_y,
3346 })
3347}
3348
3349fn read_a1op<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<OperatingPointSelector> {
3352 let op_index = src.read_u8()?;
3353 if op_index > 31 {
3354 return Err(Error::InvalidData("a1op op_index must be 0..31"));
3355 }
3356 skip_box_remain(src)?;
3357 Ok(OperatingPointSelector { op_index })
3358}
3359
3360fn read_lsel<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<LayerSelector> {
3363 let layer_id = be_u16(src)?;
3364 skip_box_remain(src)?;
3365 Ok(LayerSelector { layer_id })
3366}
3367
3368fn read_a1lx<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<AV1LayeredImageIndexing> {
3371 let flags = src.read_u8()?;
3372 let large_size = flags & 0x01 != 0;
3373 let layer_sizes = if large_size {
3374 [be_u32(src)?, be_u32(src)?, be_u32(src)?]
3375 } else {
3376 [u32::from(be_u16(src)?), u32::from(be_u16(src)?), u32::from(be_u16(src)?)]
3377 };
3378 skip_box_remain(src)?;
3379 Ok(AV1LayeredImageIndexing { layer_sizes })
3380}
3381
3382fn read_ispe<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<ImageSpatialExtents> {
3385 let _version = read_fullbox_version_no_flags(src, options)?;
3386 let width = be_u32(src)?;
3389 let height = be_u32(src)?;
3390
3391 if width == 0 || height == 0 {
3393 return Err(Error::InvalidData("ispe dimensions cannot be zero"));
3394 }
3395
3396 Ok(ImageSpatialExtents { width, height })
3397}
3398
3399fn read_mvhd<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<MovieHeader> {
3402 let version = src.read_u8()?;
3403 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
3404
3405 let (timescale, duration) = if version == 1 {
3406 let _creation_time = be_u64(src)?;
3407 let _modification_time = be_u64(src)?;
3408 let timescale = be_u32(src)?;
3409 let duration = be_u64(src)?;
3410 (timescale, duration)
3411 } else {
3412 let _creation_time = be_u32(src)?;
3413 let _modification_time = be_u32(src)?;
3414 let timescale = be_u32(src)?;
3415 let duration = be_u32(src)?;
3416 (timescale, duration as u64)
3417 };
3418
3419 skip_box_remain(src)?;
3421
3422 Ok(MovieHeader { _timescale: timescale, _duration: duration })
3423}
3424
3425fn read_mdhd<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<MediaHeader> {
3428 let version = src.read_u8()?;
3429 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
3430
3431 let (timescale, duration) = if version == 1 {
3432 let _creation_time = be_u64(src)?;
3433 let _modification_time = be_u64(src)?;
3434 let timescale = be_u32(src)?;
3435 let duration = be_u64(src)?;
3436 (timescale, duration)
3437 } else {
3438 let _creation_time = be_u32(src)?;
3439 let _modification_time = be_u32(src)?;
3440 let timescale = be_u32(src)?;
3441 let duration = be_u32(src)?;
3442 (timescale, duration as u64)
3443 };
3444
3445 skip_box_remain(src)?;
3447
3448 Ok(MediaHeader { timescale, _duration: duration })
3449}
3450
3451fn read_stts<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<TimeToSampleEntry>> {
3454 let _version = src.read_u8()?;
3455 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
3456 let entry_count = be_u32(src)?;
3457
3458 let mut entries = TryVec::new();
3459 for _ in 0..entry_count {
3460 entries.push(TimeToSampleEntry {
3461 sample_count: be_u32(src)?,
3462 sample_delta: be_u32(src)?,
3463 })?;
3464 }
3465
3466 Ok(entries)
3467}
3468
3469fn read_stsc<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<SampleToChunkEntry>> {
3472 let _version = src.read_u8()?;
3473 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
3474 let entry_count = be_u32(src)?;
3475
3476 let mut entries = TryVec::new();
3477 for _ in 0..entry_count {
3478 entries.push(SampleToChunkEntry {
3479 first_chunk: be_u32(src)?,
3480 samples_per_chunk: be_u32(src)?,
3481 _sample_description_index: be_u32(src)?,
3482 })?;
3483 }
3484
3485 Ok(entries)
3486}
3487
3488fn read_stsz<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<u32>> {
3491 let _version = src.read_u8()?;
3492 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
3493 let sample_size = be_u32(src)?;
3494 let sample_count = be_u32(src)?;
3495
3496 let mut sizes = TryVec::new();
3497 if sample_size == 0 {
3498 for _ in 0..sample_count {
3500 sizes.push(be_u32(src)?)?;
3501 }
3502 } else {
3503 for _ in 0..sample_count {
3505 sizes.push(sample_size)?;
3506 }
3507 }
3508
3509 Ok(sizes)
3510}
3511
3512fn read_chunk_offsets<T: Read>(src: &mut BMFFBox<'_, T>, is_64bit: bool) -> Result<TryVec<u64>> {
3515 let _version = src.read_u8()?;
3516 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
3517 let entry_count = be_u32(src)?;
3518
3519 let mut offsets = TryVec::new();
3520 for _ in 0..entry_count {
3521 let offset = if is_64bit {
3522 be_u64(src)?
3523 } else {
3524 be_u32(src)? as u64
3525 };
3526 offsets.push(offset)?;
3527 }
3528
3529 Ok(offsets)
3530}
3531
3532fn read_stbl<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<SampleTable> {
3535 let mut time_to_sample = TryVec::new();
3536 let mut sample_to_chunk = TryVec::new();
3537 let mut sample_sizes = TryVec::new();
3538 let mut chunk_offsets = TryVec::new();
3539
3540 let mut iter = src.box_iter();
3541 while let Some(mut b) = iter.next_box()? {
3542 match b.head.name {
3543 BoxType::TimeToSampleBox => {
3544 time_to_sample = read_stts(&mut b)?;
3545 }
3546 BoxType::SampleToChunkBox => {
3547 sample_to_chunk = read_stsc(&mut b)?;
3548 }
3549 BoxType::SampleSizeBox => {
3550 sample_sizes = read_stsz(&mut b)?;
3551 }
3552 BoxType::ChunkOffsetBox => {
3553 chunk_offsets = read_chunk_offsets(&mut b, false)?;
3554 }
3555 BoxType::ChunkLargeOffsetBox => {
3556 chunk_offsets = read_chunk_offsets(&mut b, true)?;
3557 }
3558 _ => {
3559 skip_box_remain(&mut b)?;
3560 }
3561 }
3562 }
3563
3564 Ok(SampleTable {
3565 time_to_sample,
3566 sample_to_chunk,
3567 sample_sizes,
3568 chunk_offsets,
3569 })
3570}
3571
3572fn read_moov<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<Option<(u32, SampleTable)>> {
3575 let mut media_timescale: Option<u32> = None;
3576 let mut sample_table: Option<SampleTable> = None;
3577
3578 let mut iter = src.box_iter();
3579 while let Some(mut b) = iter.next_box()? {
3580 match b.head.name {
3581 BoxType::MovieHeaderBox => {
3582 let _mvhd = read_mvhd(&mut b)?;
3583 }
3584 BoxType::TrackBox => {
3585 if media_timescale.is_none() {
3588 if let Some((timescale, stbl)) = read_trak(&mut b)? {
3589 media_timescale = Some(timescale);
3590 sample_table = Some(stbl);
3591 }
3592 } else {
3593 skip_box_remain(&mut b)?;
3594 }
3595 }
3596 _ => {
3597 skip_box_remain(&mut b)?;
3598 }
3599 }
3600 }
3601
3602 if let (Some(timescale), Some(stbl)) = (media_timescale, sample_table) {
3603 Ok(Some((timescale, stbl)))
3604 } else {
3605 Ok(None)
3606 }
3607}
3608
3609fn read_trak<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<Option<(u32, SampleTable)>> {
3612 let mut iter = src.box_iter();
3613 while let Some(mut b) = iter.next_box()? {
3614 if b.head.name == BoxType::MediaBox {
3615 return read_mdia(&mut b);
3616 } else {
3617 skip_box_remain(&mut b)?;
3618 }
3619 }
3620 Ok(None)
3621}
3622
3623fn read_mdia<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<Option<(u32, SampleTable)>> {
3625 let mut media_timescale = 1000; let mut sample_table: Option<SampleTable> = None;
3627
3628 let mut iter = src.box_iter();
3629 while let Some(mut b) = iter.next_box()? {
3630 match b.head.name {
3631 BoxType::MediaHeaderBox => {
3632 let mdhd = read_mdhd(&mut b)?;
3633 media_timescale = mdhd.timescale;
3634 }
3635 BoxType::MediaInformationBox => {
3636 sample_table = read_minf(&mut b)?;
3637 }
3638 _ => {
3639 skip_box_remain(&mut b)?;
3640 }
3641 }
3642 }
3643
3644 if let Some(stbl) = sample_table {
3645 Ok(Some((media_timescale, stbl)))
3646 } else {
3647 Ok(None)
3648 }
3649}
3650
3651fn read_minf<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<Option<SampleTable>> {
3653 let mut iter = src.box_iter();
3654 while let Some(mut b) = iter.next_box()? {
3655 if b.head.name == BoxType::SampleTableBox {
3656 return Ok(Some(read_stbl(&mut b)?));
3657 } else {
3658 skip_box_remain(&mut b)?;
3659 }
3660 }
3661 Ok(None)
3662}
3663
3664#[cfg(feature = "eager")]
3666#[allow(deprecated)]
3667fn extract_animation_frames(
3668 sample_table: &SampleTable,
3669 media_timescale: u32,
3670 mdats: &mut [MediaDataBox],
3671) -> Result<TryVec<AnimationFrame>> {
3672 let mut frames = TryVec::new();
3673
3674 let mut sample_to_chunk_map = TryVec::new();
3676 for (i, entry) in sample_table.sample_to_chunk.iter().enumerate() {
3677 let next_first_chunk = sample_table
3678 .sample_to_chunk
3679 .get(i + 1)
3680 .map(|e| e.first_chunk)
3681 .unwrap_or(u32::MAX);
3682
3683 for chunk_idx in entry.first_chunk..next_first_chunk {
3684 if chunk_idx > sample_table.chunk_offsets.len() as u32 {
3685 break;
3686 }
3687 sample_to_chunk_map.push((chunk_idx, entry.samples_per_chunk))?;
3688 }
3689 }
3690
3691 let mut frame_durations = TryVec::new();
3693 for entry in &sample_table.time_to_sample {
3694 for _ in 0..entry.sample_count {
3695 let duration_ms = if media_timescale > 0 {
3697 ((entry.sample_delta as u64) * 1000) / (media_timescale as u64)
3698 } else {
3699 0
3700 };
3701 frame_durations.push(duration_ms as u32)?;
3702 }
3703 }
3704
3705 let sample_count = sample_table.sample_sizes.len();
3707 let mut current_sample = 0;
3708
3709 for (chunk_idx_1based, samples_in_chunk) in &sample_to_chunk_map {
3710 let chunk_idx = (*chunk_idx_1based as usize).saturating_sub(1);
3711 if chunk_idx >= sample_table.chunk_offsets.len() {
3712 continue;
3713 }
3714
3715 let chunk_offset = sample_table.chunk_offsets[chunk_idx];
3716
3717 for sample_in_chunk in 0..*samples_in_chunk {
3718 if current_sample >= sample_count {
3719 break;
3720 }
3721
3722 let sample_size = sample_table.sample_sizes[current_sample];
3723 let duration_ms = frame_durations.get(current_sample).copied().unwrap_or(0);
3724
3725 let mut offset_in_chunk = 0u64;
3727 for s in 0..sample_in_chunk {
3728 let prev_sample = current_sample.saturating_sub((sample_in_chunk - s) as usize);
3729 if prev_sample < sample_count {
3730 offset_in_chunk += sample_table.sample_sizes[prev_sample] as u64;
3731 }
3732 }
3733
3734 let sample_offset = chunk_offset + offset_in_chunk;
3735
3736 let mut frame_data = TryVec::new();
3738 let mut found = false;
3739
3740 for mdat in mdats.iter_mut() {
3741 let range = ExtentRange::WithLength(Range {
3742 start: sample_offset,
3743 end: sample_offset + sample_size as u64,
3744 });
3745
3746 if mdat.contains_extent(&range) {
3747 mdat.read_extent(&range, &mut frame_data)?;
3748 found = true;
3749 break;
3750 }
3751 }
3752
3753 if !found {
3754 log::warn!("Animation frame {} not found in mdat", current_sample);
3755 }
3756
3757 frames.push(AnimationFrame {
3758 data: frame_data,
3759 duration_ms,
3760 })?;
3761
3762 current_sample += 1;
3763 }
3764 }
3765
3766 Ok(frames)
3767}
3768
3769fn read_grid<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<GridConfig> {
3772 let version = read_fullbox_version_no_flags(src, options)?;
3773 if version > 0 {
3774 return Err(Error::Unsupported("grid version > 0"));
3775 }
3776
3777 let flags_byte = src.read_u8()?;
3778 let rows = src.read_u8()?;
3779 let columns = src.read_u8()?;
3780
3781 let (output_width, output_height) = if flags_byte & 1 == 0 {
3783 (u32::from(be_u16(src)?), u32::from(be_u16(src)?))
3785 } else {
3786 (be_u32(src)?, be_u32(src)?)
3788 };
3789
3790 Ok(GridConfig {
3791 rows,
3792 columns,
3793 output_width,
3794 output_height,
3795 })
3796}
3797
3798fn read_iloc<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<ItemLocationBoxItem>> {
3801 let version: IlocVersion = read_fullbox_version_no_flags(src, options)?.try_into()?;
3802
3803 let iloc = src.read_into_try_vec()?;
3804 let mut iloc = BitReader::new(&iloc);
3805
3806 let offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
3807 let length_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
3808 let base_offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
3809
3810 let index_size: Option<IlocFieldSize> = match version {
3811 IlocVersion::One | IlocVersion::Two => Some(iloc.read_u8(4)?.try_into()?),
3812 IlocVersion::Zero => {
3813 let _reserved = iloc.read_u8(4)?;
3814 None
3815 },
3816 };
3817
3818 let item_count = match version {
3819 IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?,
3820 IlocVersion::Two => iloc.read_u32(32)?,
3821 };
3822
3823 let mut items = TryVec::with_capacity(item_count.to_usize())?;
3824
3825 for _ in 0..item_count {
3826 let item_id = match version {
3827 IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?,
3828 IlocVersion::Two => iloc.read_u32(32)?,
3829 };
3830
3831 let construction_method = match version {
3837 IlocVersion::Zero => ConstructionMethod::File,
3838 IlocVersion::One | IlocVersion::Two => {
3839 let _reserved = iloc.read_u16(12)?;
3840 match iloc.read_u16(4)? {
3841 0 => ConstructionMethod::File,
3842 1 => ConstructionMethod::Idat,
3843 2 => return Err(Error::Unsupported("construction_method 'item_offset' is not supported")),
3844 _ => return Err(Error::InvalidData("construction_method is taken from the set 0, 1 or 2 per ISO 14496-12:2015 § 8.11.3.3")),
3845 }
3846 },
3847 };
3848
3849 let data_reference_index = iloc.read_u16(16)?;
3850
3851 if data_reference_index != 0 {
3852 return Err(Error::Unsupported("external file references (iloc.data_reference_index != 0) are not supported"));
3853 }
3854
3855 let base_offset = iloc.read_u64(base_offset_size.to_bits())?;
3856 let extent_count = iloc.read_u16(16)?;
3857
3858 if extent_count < 1 {
3859 return Err(Error::InvalidData("extent_count must have a value 1 or greater per ISO 14496-12:2015 § 8.11.3.3"));
3860 }
3861
3862 let mut extents = TryVec::with_capacity(extent_count.to_usize())?;
3863
3864 for _ in 0..extent_count {
3865 let _extent_index = match &index_size {
3867 None | Some(IlocFieldSize::Zero) => None,
3868 Some(index_size) => {
3869 debug_assert!(version == IlocVersion::One || version == IlocVersion::Two);
3870 Some(iloc.read_u64(index_size.to_bits())?)
3871 },
3872 };
3873
3874 let extent_offset = iloc.read_u64(offset_size.to_bits())?;
3879 let extent_length = iloc.read_u64(length_size.to_bits())?;
3880
3881 let start = base_offset
3884 .checked_add(extent_offset)
3885 .ok_or(Error::InvalidData("offset calculation overflow"))?;
3886 let extent_range = if extent_length == 0 {
3887 ExtentRange::ToEnd(RangeFrom { start })
3888 } else {
3889 let end = start
3890 .checked_add(extent_length)
3891 .ok_or(Error::InvalidData("end calculation overflow"))?;
3892 ExtentRange::WithLength(Range { start, end })
3893 };
3894
3895 extents.push(ItemLocationBoxExtent { extent_range })?;
3896 }
3897
3898 items.push(ItemLocationBoxItem { item_id, construction_method, extents })?;
3899 }
3900
3901 if iloc.remaining() == 0 {
3902 Ok(items)
3903 } else {
3904 Err(Error::InvalidData("invalid iloc size"))
3905 }
3906}
3907
3908fn read_ftyp<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<FileTypeBox> {
3911 let major = be_u32(src)?;
3912 let minor = be_u32(src)?;
3913 let bytes_left = src.bytes_left();
3914 if !bytes_left.is_multiple_of(4) {
3915 return Err(Error::InvalidData("invalid ftyp size"));
3916 }
3917 let brand_count = bytes_left / 4;
3919 let mut brands = TryVec::with_capacity(brand_count.try_into()?)?;
3920 for _ in 0..brand_count {
3921 brands.push(be_u32(src)?.into())?;
3922 }
3923 Ok(FileTypeBox {
3924 major_brand: From::from(major),
3925 minor_version: minor,
3926 compatible_brands: brands,
3927 })
3928}
3929
3930#[cfg_attr(debug_assertions, track_caller)]
3931fn check_parser_state<T>(header: &BoxHeader, left: &Take<T>) -> Result<(), Error> {
3932 let limit = left.limit();
3933 if limit == 0 || header.size == u64::MAX {
3935 Ok(())
3936 } else {
3937 debug_assert_eq!(0, limit, "bad parser state bytes left");
3938 Err(Error::InvalidData("unread box content or bad parser sync"))
3939 }
3940}
3941
3942fn skip<T: Read>(src: &mut T, bytes: u64) -> Result<()> {
3944 std::io::copy(&mut src.take(bytes), &mut std::io::sink())?;
3945 Ok(())
3946}
3947
3948fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> {
3949 src.read_u16::<byteorder::BigEndian>().map_err(From::from)
3950}
3951
3952fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
3953 src.read_u32::<byteorder::BigEndian>().map_err(From::from)
3954}
3955
3956fn be_i32<T: ReadBytesExt>(src: &mut T) -> Result<i32> {
3957 src.read_i32::<byteorder::BigEndian>().map_err(From::from)
3958}
3959
3960fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
3961 src.read_u64::<byteorder::BigEndian>().map_err(From::from)
3962}