1#![deny(unsafe_code)]
2#![allow(clippy::missing_safety_doc)]
3use arrayvec::ArrayVec;
19use log::{debug, warn};
20
21use bitreader::BitReader;
22use byteorder::ReadBytesExt;
23use fallible_collections::{TryClone, TryReserveError};
24use std::borrow::Cow;
25use std::convert::{TryFrom, TryInto as _};
26
27use std::io::{Read, Take};
28use std::num::NonZeroU32;
29use std::ops::{Range, RangeFrom};
30
31mod obu;
32
33mod boxes;
34use crate::boxes::{BoxType, FourCC};
35
36#[cfg(feature = "c_api")]
38pub mod c_api;
39
40pub use enough::{Stop, StopReason, Unstoppable};
41
42trait ToU64 {
48 fn to_u64(self) -> u64;
49}
50
51impl ToU64 for usize {
53 fn to_u64(self) -> u64 {
54 const _: () = assert!(std::mem::size_of::<usize>() <= std::mem::size_of::<u64>());
55 self as u64
56 }
57}
58
59pub(crate) trait ToUsize {
62 fn to_usize(self) -> usize;
63}
64
65macro_rules! impl_to_usize_from {
67 ( $from_type:ty ) => {
68 impl ToUsize for $from_type {
69 fn to_usize(self) -> usize {
70 const _: () = assert!(std::mem::size_of::<$from_type>() <= std::mem::size_of::<usize>());
71 self as usize
72 }
73 }
74 };
75}
76
77impl_to_usize_from!(u8);
78impl_to_usize_from!(u16);
79impl_to_usize_from!(u32);
80
81trait Offset {
83 fn offset(&self) -> u64;
84}
85
86struct OffsetReader<'a, T> {
88 reader: &'a mut T,
89 offset: u64,
90}
91
92impl<'a, T> OffsetReader<'a, T> {
93 fn new(reader: &'a mut T) -> Self {
94 Self { reader, offset: 0 }
95 }
96}
97
98impl<T> Offset for OffsetReader<'_, T> {
99 fn offset(&self) -> u64 {
100 self.offset
101 }
102}
103
104impl<T: Read> Read for OffsetReader<'_, T> {
105 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
106 let bytes_read = self.reader.read(buf)?;
107 self.offset = self
108 .offset
109 .checked_add(bytes_read.to_u64())
110 .ok_or(Error::Unsupported("total bytes read too large for offset type"))?;
111 Ok(bytes_read)
112 }
113}
114
115pub(crate) type TryVec<T> = fallible_collections::TryVec<T>;
116pub(crate) type TryString = fallible_collections::TryVec<u8>;
117
118#[allow(dead_code)]
120struct Vec;
121#[allow(dead_code)]
122struct Box;
123#[allow(dead_code)]
124struct HashMap;
125#[allow(dead_code)]
126struct String;
127
128#[derive(Debug)]
133pub enum Error {
134 InvalidData(&'static str),
136 Unsupported(&'static str),
138 UnexpectedEOF,
140 Io(std::io::Error),
142 NoMoov,
144 OutOfMemory,
146 ResourceLimitExceeded(&'static str),
148 Stopped(enough::StopReason),
150}
151
152impl std::fmt::Display for Error {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 let msg = match self {
155 Self::InvalidData(s) | Self::Unsupported(s) | Self::ResourceLimitExceeded(s) => s,
156 Self::UnexpectedEOF => "EOF",
157 Self::Io(err) => return err.fmt(f),
158 Self::NoMoov => "Missing Moov box",
159 Self::OutOfMemory => "OOM",
160 Self::Stopped(reason) => return write!(f, "Stopped: {}", reason),
161 };
162 f.write_str(msg)
163 }
164}
165
166impl std::error::Error for Error {}
167
168impl From<bitreader::BitReaderError> for Error {
169 #[cold]
170 #[cfg_attr(debug_assertions, track_caller)]
171 fn from(err: bitreader::BitReaderError) -> Self {
172 log::warn!("bitreader: {err}");
173 debug_assert!(!matches!(err, bitreader::BitReaderError::TooManyBitsForType { .. })); Self::InvalidData("truncated bits")
175 }
176}
177
178impl From<std::io::Error> for Error {
179 fn from(err: std::io::Error) -> Self {
180 match err.kind() {
181 std::io::ErrorKind::UnexpectedEof => Self::UnexpectedEOF,
182 _ => Self::Io(err),
183 }
184 }
185}
186
187impl From<std::string::FromUtf8Error> for Error {
188 fn from(_: std::string::FromUtf8Error) -> Self {
189 Self::InvalidData("invalid utf8")
190 }
191}
192
193impl From<std::num::TryFromIntError> for Error {
194 fn from(_: std::num::TryFromIntError) -> Self {
195 Self::Unsupported("integer conversion failed")
196 }
197}
198
199impl From<Error> for std::io::Error {
200 fn from(err: Error) -> Self {
201 let kind = match err {
202 Error::InvalidData(_) => std::io::ErrorKind::InvalidData,
203 Error::UnexpectedEOF => std::io::ErrorKind::UnexpectedEof,
204 Error::Io(io_err) => return io_err,
205 _ => std::io::ErrorKind::Other,
206 };
207 Self::new(kind, err)
208 }
209}
210
211impl From<TryReserveError> for Error {
212 fn from(_: TryReserveError) -> Self {
213 Self::OutOfMemory
214 }
215}
216
217impl From<enough::StopReason> for Error {
218 fn from(reason: enough::StopReason) -> Self {
219 Self::Stopped(reason)
220 }
221}
222
223pub type Result<T, E = Error> = std::result::Result<T, E>;
225
226#[derive(Debug, Clone, Copy)]
235struct BoxHeader {
236 name: BoxType,
238 size: u64,
240 offset: u64,
242 #[allow(unused)]
244 uuid: Option<[u8; 16]>,
245}
246
247impl BoxHeader {
248 const MIN_SIZE: u64 = 8;
250 const MIN_LARGE_SIZE: u64 = 16;
252}
253
254#[derive(Debug)]
256#[allow(unused)]
257struct FileTypeBox {
258 major_brand: FourCC,
259 minor_version: u32,
260 compatible_brands: TryVec<FourCC>,
261}
262
263#[derive(Debug)]
265#[allow(unused)]
266struct HandlerBox {
267 handler_type: FourCC,
268}
269
270#[derive(Debug, Clone, PartialEq, Eq)]
275pub struct AV1Config {
276 pub profile: u8,
278 pub level: u8,
280 pub tier: u8,
282 pub bit_depth: u8,
284 pub monochrome: bool,
286 pub chroma_subsampling_x: u8,
288 pub chroma_subsampling_y: u8,
290 pub chroma_sample_position: u8,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq)]
299pub enum ColorInformation {
300 Nclx {
302 color_primaries: u16,
304 transfer_characteristics: u16,
306 matrix_coefficients: u16,
308 full_range: bool,
310 },
311 IccProfile(std::vec::Vec<u8>),
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
320pub struct ImageRotation {
321 pub angle: u16,
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330pub struct ImageMirror {
331 pub axis: u8,
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
342pub struct CleanAperture {
343 pub width_n: u32,
345 pub width_d: u32,
347 pub height_n: u32,
349 pub height_d: u32,
351 pub horiz_off_n: i32,
353 pub horiz_off_d: u32,
355 pub vert_off_n: i32,
357 pub vert_off_d: u32,
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq)]
366pub struct PixelAspectRatio {
367 pub h_spacing: u32,
369 pub v_spacing: u32,
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq)]
378pub struct ContentLightLevel {
379 pub max_content_light_level: u16,
381 pub max_pic_average_light_level: u16,
383}
384
385#[derive(Debug, Clone, Copy, PartialEq, Eq)]
390pub struct MasteringDisplayColourVolume {
391 pub primaries: [(u16, u16); 3],
394 pub white_point: (u16, u16),
396 pub max_luminance: u32,
398 pub min_luminance: u32,
400}
401
402#[derive(Debug, Clone, Copy, PartialEq, Eq)]
408pub struct ContentColourVolume {
409 pub primaries: Option<[(i32, i32); 3]>,
412 pub min_luminance: Option<u32>,
414 pub max_luminance: Option<u32>,
416 pub avg_luminance: Option<u32>,
418}
419
420#[derive(Debug, Clone, Copy, PartialEq, Eq)]
425pub struct AmbientViewingEnvironment {
426 pub ambient_illuminance: u32,
428 pub ambient_light_x: u16,
430 pub ambient_light_y: u16,
432}
433
434#[derive(Debug, Clone, Copy, PartialEq, Eq)]
439pub struct GainMapChannel {
440 pub gain_map_min_n: i32,
442 pub gain_map_min_d: u32,
444 pub gain_map_max_n: i32,
446 pub gain_map_max_d: u32,
448 pub gamma_n: u32,
450 pub gamma_d: u32,
452 pub base_offset_n: i32,
454 pub base_offset_d: u32,
456 pub alternate_offset_n: i32,
458 pub alternate_offset_d: u32,
460}
461
462#[derive(Debug, Clone, PartialEq, Eq)]
470pub struct GainMapMetadata {
471 pub is_multichannel: bool,
474 pub use_base_colour_space: bool,
477 pub base_hdr_headroom_n: u32,
479 pub base_hdr_headroom_d: u32,
481 pub alternate_hdr_headroom_n: u32,
483 pub alternate_hdr_headroom_d: u32,
485 pub channels: [GainMapChannel; 3],
488}
489
490#[derive(Debug, Clone, Copy, PartialEq, Eq)]
495pub struct OperatingPointSelector {
496 pub op_index: u8,
498}
499
500#[derive(Debug, Clone, Copy, PartialEq, Eq)]
505pub struct LayerSelector {
506 pub layer_id: u16,
508}
509
510#[derive(Debug, Clone, Copy, PartialEq, Eq)]
516pub struct AV1LayeredImageIndexing {
517 pub layer_sizes: [u32; 3],
520}
521
522#[derive(Debug, Clone, Copy)]
526#[derive(Default)]
527pub struct ParseOptions {
528 pub lenient: bool,
536}
537
538#[derive(Debug, Clone)]
564pub struct DecodeConfig {
565 pub peak_memory_limit: Option<u64>,
568
569 pub total_megapixels_limit: Option<u32>,
572
573 pub max_animation_frames: Option<u32>,
576
577 pub max_grid_tiles: Option<u32>,
580
581 pub lenient: bool,
584}
585
586impl Default for DecodeConfig {
587 fn default() -> Self {
588 Self {
589 peak_memory_limit: Some(1_000_000_000),
590 total_megapixels_limit: Some(512),
591 max_animation_frames: Some(10_000),
592 max_grid_tiles: Some(1_000),
593 lenient: false,
594 }
595 }
596}
597
598impl DecodeConfig {
599 pub fn unlimited() -> Self {
603 Self {
604 peak_memory_limit: None,
605 total_megapixels_limit: None,
606 max_animation_frames: None,
607 max_grid_tiles: None,
608 lenient: false,
609 }
610 }
611
612 pub fn with_peak_memory_limit(mut self, bytes: u64) -> Self {
614 self.peak_memory_limit = Some(bytes);
615 self
616 }
617
618 pub fn with_total_megapixels_limit(mut self, megapixels: u32) -> Self {
620 self.total_megapixels_limit = Some(megapixels);
621 self
622 }
623
624 pub fn with_max_animation_frames(mut self, frames: u32) -> Self {
626 self.max_animation_frames = Some(frames);
627 self
628 }
629
630 pub fn with_max_grid_tiles(mut self, tiles: u32) -> Self {
632 self.max_grid_tiles = Some(tiles);
633 self
634 }
635
636 pub fn lenient(mut self, lenient: bool) -> Self {
638 self.lenient = lenient;
639 self
640 }
641}
642
643#[derive(Debug, Clone, PartialEq)]
645pub struct GridConfig {
663 pub rows: u8,
665 pub columns: u8,
667 pub output_width: u32,
669 pub output_height: u32,
671}
672
673#[cfg(feature = "eager")]
675#[deprecated(since = "1.5.0", note = "Use `AvifParser::frame()` which returns `FrameRef` instead")]
676#[derive(Debug)]
677pub struct AnimationFrame {
678 pub data: TryVec<u8>,
680 pub duration_ms: u32,
682}
683
684#[cfg(feature = "eager")]
686#[deprecated(since = "1.5.0", note = "Use `AvifParser::animation_info()` and `AvifParser::frames()` instead")]
687#[derive(Debug)]
688#[allow(deprecated)]
689pub struct AnimationConfig {
690 pub loop_count: u32,
692 pub frames: TryVec<AnimationFrame>,
694}
695
696#[derive(Debug)]
699struct MovieHeader {
700 _timescale: u32,
701 _duration: u64,
702}
703
704#[derive(Debug)]
705struct MediaHeader {
706 timescale: u32,
707 _duration: u64,
708}
709
710#[derive(Debug)]
711struct TimeToSampleEntry {
712 sample_count: u32,
713 sample_delta: u32,
714}
715
716#[derive(Debug)]
717struct SampleToChunkEntry {
718 first_chunk: u32,
719 samples_per_chunk: u32,
720 _sample_description_index: u32,
721}
722
723#[derive(Debug)]
724struct SampleTable {
725 time_to_sample: TryVec<TimeToSampleEntry>,
726 sample_sizes: TryVec<u32>,
727 sample_offsets: TryVec<u64>,
730}
731
732#[derive(Debug)]
734struct TrackReference {
735 reference_type: FourCC,
736 track_ids: TryVec<u32>,
737}
738
739#[derive(Debug, Clone, Default)]
741struct TrackCodecConfig {
742 av1_config: Option<AV1Config>,
743 color_info: Option<ColorInformation>,
744}
745
746#[derive(Debug)]
748struct ParsedTrack {
749 track_id: u32,
750 handler_type: FourCC,
751 media_timescale: u32,
752 sample_table: SampleTable,
753 references: TryVec<TrackReference>,
754 loop_count: u32,
755 codec_config: TrackCodecConfig,
756}
757
758struct ParsedAnimationData {
760 color_timescale: u32,
761 color_sample_table: SampleTable,
762 alpha_timescale: Option<u32>,
763 alpha_sample_table: Option<SampleTable>,
764 loop_count: u32,
765 color_codec_config: TrackCodecConfig,
766}
767
768#[cfg(feature = "eager")]
769#[deprecated(since = "1.5.0", note = "Use `AvifParser` for zero-copy parsing instead")]
770#[derive(Debug, Default)]
771#[allow(deprecated)]
772pub struct AvifData {
773 pub primary_item: TryVec<u8>,
777 pub alpha_item: Option<TryVec<u8>>,
781 pub premultiplied_alpha: bool,
785
786 pub grid_config: Option<GridConfig>,
810
811 pub grid_tiles: TryVec<TryVec<u8>>,
819
820 pub animation: Option<AnimationConfig>,
824
825 pub av1_config: Option<AV1Config>,
827
828 pub color_info: Option<ColorInformation>,
830
831 pub rotation: Option<ImageRotation>,
833
834 pub mirror: Option<ImageMirror>,
836
837 pub clean_aperture: Option<CleanAperture>,
839
840 pub pixel_aspect_ratio: Option<PixelAspectRatio>,
842
843 pub content_light_level: Option<ContentLightLevel>,
845
846 pub mastering_display: Option<MasteringDisplayColourVolume>,
848
849 pub content_colour_volume: Option<ContentColourVolume>,
851
852 pub ambient_viewing: Option<AmbientViewingEnvironment>,
854
855 pub operating_point: Option<OperatingPointSelector>,
857
858 pub layer_selector: Option<LayerSelector>,
860
861 pub layered_image_indexing: Option<AV1LayeredImageIndexing>,
863
864 pub exif: Option<TryVec<u8>>,
868
869 pub xmp: Option<TryVec<u8>>,
873
874 pub gain_map_metadata: Option<GainMapMetadata>,
876
877 pub gain_map_item: Option<TryVec<u8>>,
879
880 pub gain_map_color_info: Option<ColorInformation>,
882
883 pub major_brand: [u8; 4],
885
886 pub compatible_brands: std::vec::Vec<[u8; 4]>,
888}
889
890#[cfg(feature = "eager")]
911#[allow(deprecated)]
912impl AvifData {
913 #[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader()` instead")]
914 pub fn from_reader<R: Read>(reader: &mut R) -> Result<Self> {
915 read_avif(reader)
916 }
917
918 pub fn primary_item_metadata(&self) -> Result<AV1Metadata> {
920 AV1Metadata::parse_av1_bitstream(&self.primary_item)
921 }
922
923 pub fn alpha_item_metadata(&self) -> Result<Option<AV1Metadata>> {
925 self.alpha_item.as_deref().map(AV1Metadata::parse_av1_bitstream).transpose()
926 }
927}
928
929#[derive(Debug, Clone, Copy, PartialEq, Eq)]
935pub struct ChromaSubsampling {
936 pub horizontal: bool,
938 pub vertical: bool,
940}
941
942impl ChromaSubsampling {
943 pub const NONE: Self = Self { horizontal: false, vertical: false };
945 pub const YUV420: Self = Self { horizontal: true, vertical: true };
947 pub const YUV422: Self = Self { horizontal: true, vertical: false };
949}
950
951impl From<(bool, bool)> for ChromaSubsampling {
952 fn from((h, v): (bool, bool)) -> Self {
953 Self { horizontal: h, vertical: v }
954 }
955}
956
957impl From<ChromaSubsampling> for (bool, bool) {
958 fn from(cs: ChromaSubsampling) -> Self {
959 (cs.horizontal, cs.vertical)
960 }
961}
962
963#[non_exhaustive]
967#[derive(Debug, Clone)]
968pub struct AV1Metadata {
969 pub still_picture: bool,
971 pub max_frame_width: NonZeroU32,
972 pub max_frame_height: NonZeroU32,
973 pub bit_depth: u8,
975 pub seq_profile: u8,
977 pub chroma_subsampling: ChromaSubsampling,
980 pub monochrome: bool,
981}
982
983impl AV1Metadata {
984 #[inline(never)]
989 pub fn parse_av1_bitstream(obu_bitstream: &[u8]) -> Result<Self> {
990 let h = obu::parse_obu(obu_bitstream)?;
991 Ok(Self {
992 still_picture: h.still_picture,
993 max_frame_width: h.max_frame_width,
994 max_frame_height: h.max_frame_height,
995 bit_depth: h.color.bit_depth,
996 seq_profile: h.seq_profile,
997 chroma_subsampling: h.color.chroma_subsampling,
998 monochrome: h.color.monochrome,
999 })
1000 }
1001}
1002
1003pub struct FrameRef<'a> {
1008 pub data: Cow<'a, [u8]>,
1009 pub alpha_data: Option<Cow<'a, [u8]>>,
1011 pub duration_ms: u32,
1012}
1013
1014struct MdatBounds {
1016 offset: u64,
1017 length: u64,
1018}
1019
1020struct ItemExtents {
1022 construction_method: ConstructionMethod,
1023 extents: TryVec<ExtentRange>,
1024}
1025
1026pub struct AvifParser<'data> {
1052 raw: Cow<'data, [u8]>,
1053 mdat_bounds: TryVec<MdatBounds>,
1054 idat: Option<TryVec<u8>>,
1055 primary: ItemExtents,
1056 alpha: Option<ItemExtents>,
1057 grid_config: Option<GridConfig>,
1058 tiles: TryVec<ItemExtents>,
1059 animation_data: Option<AnimationParserData>,
1060 premultiplied_alpha: bool,
1061 av1_config: Option<AV1Config>,
1062 color_info: Option<ColorInformation>,
1063 rotation: Option<ImageRotation>,
1064 mirror: Option<ImageMirror>,
1065 clean_aperture: Option<CleanAperture>,
1066 pixel_aspect_ratio: Option<PixelAspectRatio>,
1067 content_light_level: Option<ContentLightLevel>,
1068 mastering_display: Option<MasteringDisplayColourVolume>,
1069 content_colour_volume: Option<ContentColourVolume>,
1070 ambient_viewing: Option<AmbientViewingEnvironment>,
1071 operating_point: Option<OperatingPointSelector>,
1072 layer_selector: Option<LayerSelector>,
1073 layered_image_indexing: Option<AV1LayeredImageIndexing>,
1074 exif_item: Option<ItemExtents>,
1075 xmp_item: Option<ItemExtents>,
1076 gain_map_metadata: Option<GainMapMetadata>,
1077 gain_map: Option<ItemExtents>,
1078 gain_map_color_info: Option<ColorInformation>,
1079 major_brand: [u8; 4],
1080 compatible_brands: std::vec::Vec<[u8; 4]>,
1081}
1082
1083struct AnimationParserData {
1084 media_timescale: u32,
1085 sample_table: SampleTable,
1086 alpha_media_timescale: Option<u32>,
1087 alpha_sample_table: Option<SampleTable>,
1088 loop_count: u32,
1089 codec_config: TrackCodecConfig,
1090}
1091
1092#[derive(Debug, Clone, Copy)]
1094pub struct AnimationInfo {
1095 pub frame_count: usize,
1096 pub loop_count: u32,
1097 pub has_alpha: bool,
1099 pub timescale: u32,
1101}
1102
1103struct ParsedStructure {
1105 meta: Option<AvifInternalMeta>,
1107 mdat_bounds: TryVec<MdatBounds>,
1108 animation_data: Option<ParsedAnimationData>,
1109 major_brand: [u8; 4],
1110 compatible_brands: std::vec::Vec<[u8; 4]>,
1111}
1112
1113impl<'data> AvifParser<'data> {
1114 pub fn from_bytes(data: &'data [u8]) -> Result<Self> {
1123 Self::from_bytes_with_config(data, &DecodeConfig::unlimited(), &Unstoppable)
1124 }
1125
1126 pub fn from_bytes_with_config(
1128 data: &'data [u8],
1129 config: &DecodeConfig,
1130 stop: &dyn Stop,
1131 ) -> Result<Self> {
1132 let parsed = Self::parse_raw(data, config, stop)?;
1133 Self::build(Cow::Borrowed(data), parsed, config)
1134 }
1135
1136 pub fn from_owned(data: std::vec::Vec<u8>) -> Result<AvifParser<'static>> {
1141 AvifParser::from_owned_with_config(data, &DecodeConfig::unlimited(), &Unstoppable)
1142 }
1143
1144 pub fn from_owned_with_config(
1146 data: std::vec::Vec<u8>,
1147 config: &DecodeConfig,
1148 stop: &dyn Stop,
1149 ) -> Result<AvifParser<'static>> {
1150 let parsed = AvifParser::parse_raw(&data, config, stop)?;
1151 AvifParser::build(Cow::Owned(data), parsed, config)
1152 }
1153
1154 pub fn from_reader<R: Read>(reader: &mut R) -> Result<AvifParser<'static>> {
1156 AvifParser::from_reader_with_config(reader, &DecodeConfig::unlimited(), &Unstoppable)
1157 }
1158
1159 pub fn from_reader_with_config<R: Read>(
1161 reader: &mut R,
1162 config: &DecodeConfig,
1163 stop: &dyn Stop,
1164 ) -> Result<AvifParser<'static>> {
1165 let mut buf = std::vec::Vec::new();
1166 reader.read_to_end(&mut buf)?;
1167 AvifParser::from_owned_with_config(buf, config, stop)
1168 }
1169
1170 fn parse_raw(data: &[u8], config: &DecodeConfig, stop: &dyn Stop) -> Result<ParsedStructure> {
1177 let parse_opts = ParseOptions { lenient: config.lenient };
1178 let mut cursor = std::io::Cursor::new(data);
1179 let mut f = OffsetReader::new(&mut cursor);
1180 let mut iter = BoxIter::new(&mut f);
1181
1182 let (major_brand, compatible_brands) = if let Some(mut b) = iter.next_box()? {
1184 if b.head.name == BoxType::FileTypeBox {
1185 let ftyp = read_ftyp(&mut b)?;
1186 if ftyp.major_brand != b"avif" && ftyp.major_brand != b"avis" {
1187 return Err(Error::InvalidData("ftyp must be 'avif' or 'avis'"));
1188 }
1189 let major = ftyp.major_brand.value;
1190 let compat = ftyp.compatible_brands.iter().map(|b| b.value).collect();
1191 (major, compat)
1192 } else {
1193 return Err(Error::InvalidData("'ftyp' box must occur first"));
1194 }
1195 } else {
1196 return Err(Error::InvalidData("'ftyp' box must occur first"));
1197 };
1198
1199 let mut meta = None;
1200 let mut mdat_bounds = TryVec::new();
1201 let mut animation_data: Option<ParsedAnimationData> = None;
1202
1203 while let Some(mut b) = iter.next_box()? {
1204 stop.check()?;
1205
1206 match b.head.name {
1207 BoxType::MetadataBox => {
1208 if meta.is_some() {
1209 return Err(Error::InvalidData(
1210 "There should be zero or one meta boxes per ISO 14496-12:2015 § 8.11.1.1",
1211 ));
1212 }
1213 meta = Some(read_avif_meta(&mut b, &parse_opts)?);
1214 }
1215 BoxType::MovieBox => {
1216 let tracks = read_moov(&mut b)?;
1217 if !tracks.is_empty() {
1218 animation_data = Some(associate_tracks(tracks)?);
1219 }
1220 }
1221 BoxType::MediaDataBox => {
1222 if b.bytes_left() > 0 {
1223 let offset = b.offset();
1224 let length = b.bytes_left();
1225 mdat_bounds.push(MdatBounds { offset, length })?;
1226 }
1227 skip_box_content(&mut b)?;
1229 }
1230 _ => skip_box_content(&mut b)?,
1231 }
1232
1233 check_parser_state(&b.head, &b.content)?;
1234 }
1235
1236 if meta.is_none() && animation_data.is_none() {
1239 return Err(Error::InvalidData("missing meta"));
1240 }
1241
1242 Ok(ParsedStructure { meta, mdat_bounds, animation_data, major_brand, compatible_brands })
1243 }
1244
1245 fn build(raw: Cow<'data, [u8]>, parsed: ParsedStructure, config: &DecodeConfig) -> Result<Self> {
1247 let tracker = ResourceTracker::new(config);
1248
1249 let animation_data = if let Some(anim) = parsed.animation_data {
1251 tracker.validate_animation_frames(anim.color_sample_table.sample_sizes.len() as u32)?;
1252 Some(AnimationParserData {
1253 media_timescale: anim.color_timescale,
1254 sample_table: anim.color_sample_table,
1255 alpha_media_timescale: anim.alpha_timescale,
1256 alpha_sample_table: anim.alpha_sample_table,
1257 loop_count: anim.loop_count,
1258 codec_config: anim.color_codec_config,
1259 })
1260 } else {
1261 None
1262 };
1263
1264 let Some(meta) = parsed.meta else {
1267 let track_config = animation_data.as_ref()
1268 .map(|a| a.codec_config.clone())
1269 .unwrap_or_default();
1270 return Ok(Self {
1271 raw,
1272 mdat_bounds: parsed.mdat_bounds,
1273 idat: None,
1274 primary: ItemExtents { construction_method: ConstructionMethod::File, extents: TryVec::new() },
1275 alpha: None,
1276 grid_config: None,
1277 tiles: TryVec::new(),
1278 animation_data,
1279 premultiplied_alpha: false,
1280 av1_config: track_config.av1_config,
1281 color_info: track_config.color_info,
1282 rotation: None,
1283 mirror: None,
1284 clean_aperture: None,
1285 pixel_aspect_ratio: None,
1286 content_light_level: None,
1287 mastering_display: None,
1288 content_colour_volume: None,
1289 ambient_viewing: None,
1290 operating_point: None,
1291 layer_selector: None,
1292 layered_image_indexing: None,
1293 exif_item: None,
1294 xmp_item: None,
1295 gain_map_metadata: None,
1296 gain_map: None,
1297 gain_map_color_info: None,
1298 major_brand: parsed.major_brand,
1299 compatible_brands: parsed.compatible_brands,
1300 });
1301 };
1302
1303 let primary = Self::get_item_extents(&meta, meta.primary_item_id)?;
1305
1306 let alpha_item_id = meta
1308 .item_references
1309 .iter()
1310 .filter(|iref| {
1311 iref.to_item_id == meta.primary_item_id
1312 && iref.from_item_id != meta.primary_item_id
1313 && iref.item_type == b"auxl"
1314 })
1315 .map(|iref| iref.from_item_id)
1316 .find(|&item_id| {
1317 meta.properties.iter().any(|prop| {
1318 prop.item_id == item_id
1319 && match &prop.property {
1320 ItemProperty::AuxiliaryType(urn) => {
1321 urn.type_subtype().0 == b"urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
1322 }
1323 _ => false,
1324 }
1325 })
1326 });
1327
1328 let alpha = alpha_item_id
1329 .map(|id| Self::get_item_extents(&meta, id))
1330 .transpose()?;
1331
1332 let premultiplied_alpha = alpha_item_id.is_some_and(|alpha_id| {
1334 meta.item_references.iter().any(|iref| {
1335 iref.from_item_id == meta.primary_item_id
1336 && iref.to_item_id == alpha_id
1337 && iref.item_type == b"prem"
1338 })
1339 });
1340
1341 let mut exif_item = None;
1343 let mut xmp_item = None;
1344 for iref in meta.item_references.iter() {
1345 if iref.to_item_id != meta.primary_item_id || iref.item_type != b"cdsc" {
1346 continue;
1347 }
1348 let desc_item_id = iref.from_item_id;
1349 let Some(info) = meta.item_infos.iter().find(|i| i.item_id == desc_item_id) else {
1350 continue;
1351 };
1352 if info.item_type == b"Exif" && exif_item.is_none() {
1353 exif_item = Some(Self::get_item_extents(&meta, desc_item_id)?);
1354 } else if info.item_type == b"mime" && xmp_item.is_none() {
1355 xmp_item = Some(Self::get_item_extents(&meta, desc_item_id)?);
1356 }
1357 }
1358
1359 let is_grid = meta
1361 .item_infos
1362 .iter()
1363 .find(|x| x.item_id == meta.primary_item_id)
1364 .is_some_and(|info| info.item_type == b"grid");
1365
1366 let (grid_config, tiles) = if is_grid {
1368 let mut tiles_with_index: TryVec<(u32, u16)> = TryVec::new();
1369 for iref in meta.item_references.iter() {
1370 if iref.from_item_id == meta.primary_item_id && iref.item_type == b"dimg" {
1371 tiles_with_index.push((iref.to_item_id, iref.reference_index))?;
1372 }
1373 }
1374
1375 tracker.validate_grid_tiles(tiles_with_index.len() as u32)?;
1376 tiles_with_index.sort_by_key(|&(_, idx)| idx);
1377
1378 let mut tile_extents = TryVec::new();
1379 for (tile_id, _) in tiles_with_index.iter() {
1380 tile_extents.push(Self::get_item_extents(&meta, *tile_id)?)?;
1381 }
1382
1383 let mut tile_ids = TryVec::new();
1384 for (tile_id, _) in tiles_with_index.iter() {
1385 tile_ids.push(*tile_id)?;
1386 }
1387
1388 let grid_config = Self::calculate_grid_config(&meta, &tile_ids)?;
1389
1390 for (tile_id, _) in tiles_with_index.iter() {
1392 for prop in meta.properties.iter() {
1393 if prop.item_id == *tile_id {
1394 match &prop.property {
1395 ItemProperty::Rotation(_)
1396 | ItemProperty::Mirror(_)
1397 | ItemProperty::CleanAperture(_) => {
1398 warn!("grid tile {} has a transformative property (irot/imir/clap), violating AVIF spec", tile_id);
1399 }
1400 _ => {}
1401 }
1402 }
1403 }
1404 }
1405
1406 (Some(grid_config), tile_extents)
1407 } else {
1408 (None, TryVec::new())
1409 };
1410
1411 let (gain_map_metadata, gain_map, gain_map_color_info) = {
1413 let tmap_item = meta.item_infos.iter()
1414 .find(|info| info.item_type == b"tmap");
1415
1416 if let Some(tmap_info) = tmap_item {
1417 let tmap_id = tmap_info.item_id;
1418
1419 let mut inputs: TryVec<(u32, u16)> = TryVec::new();
1421 for iref in meta.item_references.iter() {
1422 if iref.from_item_id == tmap_id && iref.item_type == b"dimg" {
1423 inputs.push((iref.to_item_id, iref.reference_index))?;
1424 }
1425 }
1426 inputs.sort_by_key(|&(_, idx)| idx);
1427
1428 if inputs.len() >= 2 {
1429 let base_item_id = inputs[0].0;
1430 let gmap_item_id = inputs[1].0;
1431
1432 if base_item_id == meta.primary_item_id {
1433 let tmap_extents = Self::get_item_extents(&meta, tmap_id)?;
1435 let tmap_data = Self::resolve_extents_from_raw(
1436 raw.as_ref(), &parsed.mdat_bounds, &tmap_extents,
1437 )?;
1438 let metadata = parse_tone_map_image(&tmap_data)?;
1439
1440 let gmap_extents = Self::get_item_extents(&meta, gmap_item_id)?;
1442
1443 let alt_color = meta.properties.iter().find_map(|p| {
1445 if p.item_id == tmap_id {
1446 match &p.property {
1447 ItemProperty::ColorInformation(c) => Some(c.clone()),
1448 _ => None,
1449 }
1450 } else {
1451 None
1452 }
1453 });
1454
1455 (Some(metadata), Some(gmap_extents), alt_color)
1456 } else {
1457 (None, None, None)
1458 }
1459 } else {
1460 (None, None, None)
1461 }
1462 } else {
1463 (None, None, None)
1464 }
1465 };
1466
1467 macro_rules! find_prop {
1469 ($variant:ident) => {
1470 meta.properties.iter().find_map(|p| {
1471 if p.item_id == meta.primary_item_id {
1472 match &p.property {
1473 ItemProperty::$variant(c) => Some(c.clone()),
1474 _ => None,
1475 }
1476 } else {
1477 None
1478 }
1479 })
1480 };
1481 }
1482
1483 let track_config = animation_data.as_ref().map(|a| &a.codec_config);
1484 let av1_config = find_prop!(AV1Config)
1485 .or_else(|| track_config.and_then(|c| c.av1_config.clone()));
1486 let color_info = find_prop!(ColorInformation)
1487 .or_else(|| track_config.and_then(|c| c.color_info.clone()));
1488 let rotation = find_prop!(Rotation);
1489 let mirror = find_prop!(Mirror);
1490 let clean_aperture = find_prop!(CleanAperture);
1491 let pixel_aspect_ratio = find_prop!(PixelAspectRatio);
1492 let content_light_level = find_prop!(ContentLightLevel);
1493 let mastering_display = find_prop!(MasteringDisplayColourVolume);
1494 let content_colour_volume = find_prop!(ContentColourVolume);
1495 let ambient_viewing = find_prop!(AmbientViewingEnvironment);
1496 let operating_point = find_prop!(OperatingPointSelector);
1497 let layer_selector = find_prop!(LayerSelector);
1498 let layered_image_indexing = find_prop!(AV1LayeredImageIndexing);
1499
1500 let idat = if let Some(ref idat_data) = meta.idat {
1502 let mut cloned = TryVec::new();
1503 cloned.extend_from_slice(idat_data)?;
1504 Some(cloned)
1505 } else {
1506 None
1507 };
1508
1509 Ok(Self {
1510 raw,
1511 mdat_bounds: parsed.mdat_bounds,
1512 idat,
1513 primary,
1514 alpha,
1515 grid_config,
1516 tiles,
1517 animation_data,
1518 premultiplied_alpha,
1519 av1_config,
1520 color_info,
1521 rotation,
1522 mirror,
1523 clean_aperture,
1524 pixel_aspect_ratio,
1525 content_light_level,
1526 mastering_display,
1527 content_colour_volume,
1528 ambient_viewing,
1529 operating_point,
1530 layer_selector,
1531 layered_image_indexing,
1532 exif_item,
1533 xmp_item,
1534 gain_map_metadata,
1535 gain_map,
1536 gain_map_color_info,
1537 major_brand: parsed.major_brand,
1538 compatible_brands: parsed.compatible_brands,
1539 })
1540 }
1541
1542 fn get_item_extents(meta: &AvifInternalMeta, item_id: u32) -> Result<ItemExtents> {
1548 let item = meta
1549 .iloc_items
1550 .iter()
1551 .find(|item| item.item_id == item_id)
1552 .ok_or(Error::InvalidData("item not found in iloc"))?;
1553
1554 let mut extents = TryVec::new();
1555 for extent in &item.extents {
1556 extents.push(extent.extent_range.clone())?;
1557 }
1558 Ok(ItemExtents {
1559 construction_method: item.construction_method,
1560 extents,
1561 })
1562 }
1563
1564 fn resolve_extents_from_raw(
1567 raw: &[u8],
1568 mdat_bounds: &[MdatBounds],
1569 item: &ItemExtents,
1570 ) -> Result<std::vec::Vec<u8>> {
1571 if item.construction_method != ConstructionMethod::File {
1572 return Err(Error::Unsupported("tmap item must use file construction method"));
1573 }
1574 let mut data = std::vec::Vec::new();
1575 for extent in &item.extents {
1576 let file_offset = extent.start();
1577 let start = usize::try_from(file_offset)?;
1578 let end = match extent {
1579 ExtentRange::WithLength(range) => {
1580 let len = range.end.checked_sub(range.start)
1581 .ok_or(Error::InvalidData("extent range start > end"))?;
1582 start.checked_add(usize::try_from(len)?)
1583 .ok_or(Error::InvalidData("extent end overflow"))?
1584 }
1585 ExtentRange::ToEnd(_) => {
1586 let mut found_end = raw.len();
1588 for mdat in mdat_bounds {
1589 if file_offset >= mdat.offset && file_offset < mdat.offset + mdat.length {
1590 found_end = usize::try_from(mdat.offset + mdat.length)?;
1591 break;
1592 }
1593 }
1594 found_end
1595 }
1596 };
1597 let slice = raw.get(start..end)
1598 .ok_or(Error::InvalidData("tmap extent out of bounds"))?;
1599 data.extend_from_slice(slice);
1600 }
1601 Ok(data)
1602 }
1603
1604 fn resolve_item(&self, item: &ItemExtents) -> Result<Cow<'_, [u8]>> {
1607 match item.construction_method {
1608 ConstructionMethod::Idat => self.resolve_idat_extents(&item.extents),
1609 ConstructionMethod::File => self.resolve_file_extents(&item.extents),
1610 ConstructionMethod::Item => Err(Error::Unsupported("construction_method 'item' not supported")),
1611 }
1612 }
1613
1614 fn resolve_file_extents(&self, extents: &[ExtentRange]) -> Result<Cow<'_, [u8]>> {
1616 let raw = self.raw.as_ref();
1617
1618 if extents.len() == 1 {
1620 let extent = &extents[0];
1621 let (start, end) = self.extent_byte_range(extent)?;
1622 let slice = raw.get(start..end).ok_or(Error::InvalidData("extent out of bounds in raw buffer"))?;
1623 return Ok(Cow::Borrowed(slice));
1624 }
1625
1626 let mut data = TryVec::new();
1628 for extent in extents {
1629 let (start, end) = self.extent_byte_range(extent)?;
1630 let slice = raw.get(start..end).ok_or(Error::InvalidData("extent out of bounds in raw buffer"))?;
1631 data.extend_from_slice(slice)?;
1632 }
1633 Ok(Cow::Owned(data.into_iter().collect()))
1634 }
1635
1636 fn extent_byte_range(&self, extent: &ExtentRange) -> Result<(usize, usize)> {
1638 let file_offset = extent.start();
1639 let start = usize::try_from(file_offset)?;
1640
1641 match extent {
1642 ExtentRange::WithLength(range) => {
1643 let len = range.end.checked_sub(range.start)
1644 .ok_or(Error::InvalidData("extent range start > end"))?;
1645 let end = start.checked_add(usize::try_from(len)?)
1646 .ok_or(Error::InvalidData("extent end overflow"))?;
1647 Ok((start, end))
1648 }
1649 ExtentRange::ToEnd(_) => {
1650 for mdat in &self.mdat_bounds {
1652 if file_offset >= mdat.offset && file_offset < mdat.offset + mdat.length {
1653 let end = usize::try_from(mdat.offset + mdat.length)?;
1654 return Ok((start, end));
1655 }
1656 }
1657 Ok((start, self.raw.len()))
1659 }
1660 }
1661 }
1662
1663 fn resolve_idat_extents(&self, extents: &[ExtentRange]) -> Result<Cow<'_, [u8]>> {
1665 let idat_data = self.idat.as_ref()
1666 .ok_or(Error::InvalidData("idat box missing but construction_method is Idat"))?;
1667
1668 if extents.len() == 1 {
1669 let extent = &extents[0];
1670 let start = usize::try_from(extent.start())?;
1671 let slice = match extent {
1672 ExtentRange::WithLength(range) => {
1673 let len = usize::try_from(range.end - range.start)?;
1674 idat_data.get(start..start + len)
1675 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1676 }
1677 ExtentRange::ToEnd(_) => {
1678 idat_data.get(start..)
1679 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1680 }
1681 };
1682 return Ok(Cow::Borrowed(slice));
1683 }
1684
1685 let mut data = TryVec::new();
1687 for extent in extents {
1688 let start = usize::try_from(extent.start())?;
1689 let slice = match extent {
1690 ExtentRange::WithLength(range) => {
1691 let len = usize::try_from(range.end - range.start)?;
1692 idat_data.get(start..start + len)
1693 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1694 }
1695 ExtentRange::ToEnd(_) => {
1696 idat_data.get(start..)
1697 .ok_or(Error::InvalidData("idat extent out of bounds"))?
1698 }
1699 };
1700 data.extend_from_slice(slice)?;
1701 }
1702 Ok(Cow::Owned(data.into_iter().collect()))
1703 }
1704
1705 fn resolve_frame(&self, index: usize) -> Result<FrameRef<'_>> {
1707 let anim = self.animation_data.as_ref()
1708 .ok_or(Error::InvalidData("not an animated AVIF"))?;
1709
1710 if index >= anim.sample_table.sample_sizes.len() {
1711 return Err(Error::InvalidData("frame index out of bounds"));
1712 }
1713
1714 let duration_ms = self.calculate_frame_duration(&anim.sample_table, anim.media_timescale, index)?;
1715 let (offset, size) = self.calculate_sample_location(&anim.sample_table, index)?;
1716
1717 let start = usize::try_from(offset)?;
1718 let end = start.checked_add(size as usize)
1719 .ok_or(Error::InvalidData("frame end overflow"))?;
1720
1721 let raw = self.raw.as_ref();
1722 let slice = raw.get(start..end)
1723 .ok_or(Error::InvalidData("frame not found in raw buffer"))?;
1724
1725 let alpha_data = if let Some(ref alpha_st) = anim.alpha_sample_table {
1727 let alpha_timescale = anim.alpha_media_timescale.unwrap_or(anim.media_timescale);
1728 if index < alpha_st.sample_sizes.len() {
1729 let (a_offset, a_size) = self.calculate_sample_location(alpha_st, index)?;
1730 let a_start = usize::try_from(a_offset)?;
1731 let a_end = a_start.checked_add(a_size as usize)
1732 .ok_or(Error::InvalidData("alpha frame end overflow"))?;
1733 let a_slice = raw.get(a_start..a_end)
1734 .ok_or(Error::InvalidData("alpha frame not found in raw buffer"))?;
1735 let _ = alpha_timescale; Some(Cow::Borrowed(a_slice))
1737 } else {
1738 warn!("alpha track has fewer frames than color track (index {})", index);
1739 None
1740 }
1741 } else {
1742 None
1743 };
1744
1745 Ok(FrameRef {
1746 data: Cow::Borrowed(slice),
1747 alpha_data,
1748 duration_ms,
1749 })
1750 }
1751
1752 fn calculate_grid_config(meta: &AvifInternalMeta, tile_ids: &[u32]) -> Result<GridConfig> {
1754 for prop in &meta.properties {
1756 if prop.item_id == meta.primary_item_id
1757 && let ItemProperty::ImageGrid(grid) = &prop.property {
1758 return Ok(grid.clone());
1759 }
1760 }
1761
1762 let grid_dims = meta
1764 .properties
1765 .iter()
1766 .find(|p| p.item_id == meta.primary_item_id)
1767 .and_then(|p| match &p.property {
1768 ItemProperty::ImageSpatialExtents(e) => Some(e),
1769 _ => None,
1770 });
1771
1772 let tile_dims = tile_ids.first().and_then(|&tile_id| {
1773 meta.properties
1774 .iter()
1775 .find(|p| p.item_id == tile_id)
1776 .and_then(|p| match &p.property {
1777 ItemProperty::ImageSpatialExtents(e) => Some(e),
1778 _ => None,
1779 })
1780 });
1781
1782 if let (Some(grid), Some(tile)) = (grid_dims, tile_dims)
1783 && tile.width != 0
1784 && tile.height != 0
1785 && grid.width % tile.width == 0
1786 && grid.height % tile.height == 0
1787 {
1788 let columns = grid.width / tile.width;
1789 let rows = grid.height / tile.height;
1790
1791 if columns <= 255 && rows <= 255 {
1792 return Ok(GridConfig {
1793 rows: rows as u8,
1794 columns: columns as u8,
1795 output_width: grid.width,
1796 output_height: grid.height,
1797 });
1798 }
1799 }
1800
1801 let tile_count = tile_ids.len();
1802 Ok(GridConfig {
1803 rows: tile_count.min(255) as u8,
1804 columns: 1,
1805 output_width: 0,
1806 output_height: 0,
1807 })
1808 }
1809
1810 fn calculate_frame_duration(
1812 &self,
1813 st: &SampleTable,
1814 timescale: u32,
1815 index: usize,
1816 ) -> Result<u32> {
1817 let mut current_sample = 0;
1818 for entry in &st.time_to_sample {
1819 if current_sample + entry.sample_count as usize > index {
1820 let duration_ms = if timescale > 0 {
1821 ((entry.sample_delta as u64) * 1000) / (timescale as u64)
1822 } else {
1823 0
1824 };
1825 return Ok(duration_ms as u32);
1826 }
1827 current_sample += entry.sample_count as usize;
1828 }
1829 Ok(0)
1830 }
1831
1832 fn calculate_sample_location(&self, st: &SampleTable, index: usize) -> Result<(u64, u32)> {
1834 let offset = *st
1835 .sample_offsets
1836 .get(index)
1837 .ok_or(Error::InvalidData("sample index out of bounds"))?;
1838 let size = *st
1839 .sample_sizes
1840 .get(index)
1841 .ok_or(Error::InvalidData("sample index out of bounds"))?;
1842 Ok((offset, size))
1843 }
1844
1845 pub fn primary_data(&self) -> Result<Cow<'_, [u8]>> {
1853 self.resolve_item(&self.primary)
1854 }
1855
1856 pub fn alpha_data(&self) -> Option<Result<Cow<'_, [u8]>>> {
1858 self.alpha.as_ref().map(|item| self.resolve_item(item))
1859 }
1860
1861 pub fn tile_data(&self, index: usize) -> Result<Cow<'_, [u8]>> {
1863 let item = self.tiles.get(index)
1864 .ok_or(Error::InvalidData("tile index out of bounds"))?;
1865 self.resolve_item(item)
1866 }
1867
1868 pub fn frame(&self, index: usize) -> Result<FrameRef<'_>> {
1870 self.resolve_frame(index)
1871 }
1872
1873 pub fn frames(&self) -> FrameIterator<'_> {
1875 let count = self
1876 .animation_info()
1877 .map(|info| info.frame_count)
1878 .unwrap_or(0);
1879 FrameIterator { parser: self, index: 0, count }
1880 }
1881
1882 pub fn animation_info(&self) -> Option<AnimationInfo> {
1888 self.animation_data.as_ref().map(|data| AnimationInfo {
1889 frame_count: data.sample_table.sample_sizes.len(),
1890 loop_count: data.loop_count,
1891 has_alpha: data.alpha_sample_table.is_some(),
1892 timescale: data.media_timescale,
1893 })
1894 }
1895
1896 pub fn grid_config(&self) -> Option<&GridConfig> {
1898 self.grid_config.as_ref()
1899 }
1900
1901 pub fn grid_tile_count(&self) -> usize {
1903 self.tiles.len()
1904 }
1905
1906 pub fn premultiplied_alpha(&self) -> bool {
1908 self.premultiplied_alpha
1909 }
1910
1911 pub fn av1_config(&self) -> Option<&AV1Config> {
1915 self.av1_config.as_ref()
1916 }
1917
1918 pub fn color_info(&self) -> Option<&ColorInformation> {
1924 self.color_info.as_ref()
1925 }
1926
1927 pub fn rotation(&self) -> Option<&ImageRotation> {
1929 self.rotation.as_ref()
1930 }
1931
1932 pub fn mirror(&self) -> Option<&ImageMirror> {
1934 self.mirror.as_ref()
1935 }
1936
1937 pub fn clean_aperture(&self) -> Option<&CleanAperture> {
1939 self.clean_aperture.as_ref()
1940 }
1941
1942 pub fn pixel_aspect_ratio(&self) -> Option<&PixelAspectRatio> {
1944 self.pixel_aspect_ratio.as_ref()
1945 }
1946
1947 pub fn content_light_level(&self) -> Option<&ContentLightLevel> {
1949 self.content_light_level.as_ref()
1950 }
1951
1952 pub fn mastering_display(&self) -> Option<&MasteringDisplayColourVolume> {
1954 self.mastering_display.as_ref()
1955 }
1956
1957 pub fn content_colour_volume(&self) -> Option<&ContentColourVolume> {
1959 self.content_colour_volume.as_ref()
1960 }
1961
1962 pub fn ambient_viewing(&self) -> Option<&AmbientViewingEnvironment> {
1964 self.ambient_viewing.as_ref()
1965 }
1966
1967 pub fn operating_point(&self) -> Option<&OperatingPointSelector> {
1969 self.operating_point.as_ref()
1970 }
1971
1972 pub fn layer_selector(&self) -> Option<&LayerSelector> {
1974 self.layer_selector.as_ref()
1975 }
1976
1977 pub fn layered_image_indexing(&self) -> Option<&AV1LayeredImageIndexing> {
1979 self.layered_image_indexing.as_ref()
1980 }
1981
1982 pub fn exif(&self) -> Option<Result<Cow<'_, [u8]>>> {
1986 self.exif_item.as_ref().map(|item| {
1987 let raw = self.resolve_item(item)?;
1988 if raw.len() <= 4 {
1990 return Err(Error::InvalidData("EXIF item too short"));
1991 }
1992 let offset = u32::from_be_bytes([raw[0], raw[1], raw[2], raw[3]]) as usize;
1993 let start = 4 + offset;
1994 if start >= raw.len() {
1995 return Err(Error::InvalidData("EXIF offset exceeds item size"));
1996 }
1997 match raw {
1998 Cow::Borrowed(slice) => Ok(Cow::Borrowed(&slice[start..])),
1999 Cow::Owned(vec) => Ok(Cow::Owned(vec[start..].to_vec())),
2000 }
2001 })
2002 }
2003
2004 pub fn xmp(&self) -> Option<Result<Cow<'_, [u8]>>> {
2008 self.xmp_item.as_ref().map(|item| self.resolve_item(item))
2009 }
2010
2011 pub fn gain_map_metadata(&self) -> Option<&GainMapMetadata> {
2016 self.gain_map_metadata.as_ref()
2017 }
2018
2019 pub fn gain_map_data(&self) -> Option<Result<Cow<'_, [u8]>>> {
2021 self.gain_map.as_ref().map(|item| self.resolve_item(item))
2022 }
2023
2024 pub fn gain_map_color_info(&self) -> Option<&ColorInformation> {
2029 self.gain_map_color_info.as_ref()
2030 }
2031
2032 pub fn major_brand(&self) -> &[u8; 4] {
2034 &self.major_brand
2035 }
2036
2037 pub fn compatible_brands(&self) -> &[[u8; 4]] {
2039 &self.compatible_brands
2040 }
2041
2042 pub fn primary_metadata(&self) -> Result<AV1Metadata> {
2044 let data = self.primary_data()?;
2045 AV1Metadata::parse_av1_bitstream(&data)
2046 }
2047
2048 pub fn alpha_metadata(&self) -> Option<Result<AV1Metadata>> {
2050 self.alpha.as_ref().map(|item| {
2051 let data = self.resolve_item(item)?;
2052 AV1Metadata::parse_av1_bitstream(&data)
2053 })
2054 }
2055
2056 #[cfg(feature = "eager")]
2065 #[deprecated(since = "1.5.0", note = "Use AvifParser methods directly instead of converting to AvifData")]
2066 #[allow(deprecated)]
2067 pub fn to_avif_data(&self) -> Result<AvifData> {
2068 let primary_data = self.primary_data()?;
2069 let mut primary_item = TryVec::new();
2070 primary_item.extend_from_slice(&primary_data)?;
2071
2072 let alpha_item = match self.alpha_data() {
2073 Some(Ok(data)) => {
2074 let mut v = TryVec::new();
2075 v.extend_from_slice(&data)?;
2076 Some(v)
2077 }
2078 Some(Err(e)) => return Err(e),
2079 None => None,
2080 };
2081
2082 let mut grid_tiles = TryVec::new();
2083 for i in 0..self.grid_tile_count() {
2084 let data = self.tile_data(i)?;
2085 let mut v = TryVec::new();
2086 v.extend_from_slice(&data)?;
2087 grid_tiles.push(v)?;
2088 }
2089
2090 let animation = if let Some(info) = self.animation_info() {
2091 let mut frames = TryVec::new();
2092 for i in 0..info.frame_count {
2093 let frame_ref = self.frame(i)?;
2094 let mut data = TryVec::new();
2095 data.extend_from_slice(&frame_ref.data)?;
2096 frames.push(AnimationFrame { data, duration_ms: frame_ref.duration_ms })?;
2097 }
2098 Some(AnimationConfig {
2099 loop_count: info.loop_count,
2100 frames,
2101 })
2102 } else {
2103 None
2104 };
2105
2106 Ok(AvifData {
2107 primary_item,
2108 alpha_item,
2109 premultiplied_alpha: self.premultiplied_alpha,
2110 grid_config: self.grid_config.clone(),
2111 grid_tiles,
2112 animation,
2113 av1_config: self.av1_config.clone(),
2114 color_info: self.color_info.clone(),
2115 rotation: self.rotation,
2116 mirror: self.mirror,
2117 clean_aperture: self.clean_aperture,
2118 pixel_aspect_ratio: self.pixel_aspect_ratio,
2119 content_light_level: self.content_light_level,
2120 mastering_display: self.mastering_display,
2121 content_colour_volume: self.content_colour_volume,
2122 ambient_viewing: self.ambient_viewing,
2123 operating_point: self.operating_point,
2124 layer_selector: self.layer_selector,
2125 layered_image_indexing: self.layered_image_indexing,
2126 exif: self.exif().and_then(|r| r.ok()).map(|c| {
2127 let mut v = TryVec::new();
2128 let _ = v.extend_from_slice(&c);
2129 v
2130 }),
2131 xmp: self.xmp().and_then(|r| r.ok()).map(|c| {
2132 let mut v = TryVec::new();
2133 let _ = v.extend_from_slice(&c);
2134 v
2135 }),
2136 gain_map_metadata: self.gain_map_metadata.clone(),
2137 gain_map_item: self.gain_map_data().and_then(|r| r.ok()).map(|c| {
2138 let mut v = TryVec::new();
2139 let _ = v.extend_from_slice(&c);
2140 v
2141 }),
2142 gain_map_color_info: self.gain_map_color_info.clone(),
2143 major_brand: self.major_brand,
2144 compatible_brands: self.compatible_brands.clone(),
2145 })
2146 }
2147}
2148
2149pub struct FrameIterator<'a> {
2153 parser: &'a AvifParser<'a>,
2154 index: usize,
2155 count: usize,
2156}
2157
2158impl<'a> Iterator for FrameIterator<'a> {
2159 type Item = Result<FrameRef<'a>>;
2160
2161 fn next(&mut self) -> Option<Self::Item> {
2162 if self.index >= self.count {
2163 return None;
2164 }
2165 let result = self.parser.frame(self.index);
2166 self.index += 1;
2167 Some(result)
2168 }
2169
2170 fn size_hint(&self) -> (usize, Option<usize>) {
2171 let remaining = self.count.saturating_sub(self.index);
2172 (remaining, Some(remaining))
2173 }
2174}
2175
2176impl ExactSizeIterator for FrameIterator<'_> {
2177 fn len(&self) -> usize {
2178 self.count.saturating_sub(self.index)
2179 }
2180}
2181
2182struct AvifInternalMeta {
2183 item_references: TryVec<SingleItemTypeReferenceBox>,
2184 properties: TryVec<AssociatedProperty>,
2185 primary_item_id: u32,
2186 iloc_items: TryVec<ItemLocationBoxItem>,
2187 item_infos: TryVec<ItemInfoEntry>,
2188 idat: Option<TryVec<u8>>,
2189 #[allow(dead_code)] entity_groups: TryVec<EntityGroup>,
2191}
2192
2193#[cfg(feature = "eager")]
2196struct MediaDataBox {
2197 offset: u64,
2199 data: TryVec<u8>,
2200}
2201
2202#[cfg(feature = "eager")]
2203impl MediaDataBox {
2204 fn contains_extent(&self, extent: &ExtentRange) -> bool {
2208 if self.offset <= extent.start() {
2209 let start_offset = extent.start() - self.offset;
2210 start_offset < self.data.len().to_u64()
2211 } else {
2212 false
2213 }
2214 }
2215
2216 fn matches_extent(&self, extent: &ExtentRange) -> bool {
2218 if self.offset == extent.start() {
2219 match extent {
2220 ExtentRange::WithLength(range) => {
2221 if let Some(end) = self.offset.checked_add(self.data.len().to_u64()) {
2222 end == range.end
2223 } else {
2224 false
2225 }
2226 },
2227 ExtentRange::ToEnd(_) => true,
2228 }
2229 } else {
2230 false
2231 }
2232 }
2233
2234 fn read_extent(&self, extent: &ExtentRange, buf: &mut TryVec<u8>) -> Result<()> {
2237 let start_offset = extent
2238 .start()
2239 .checked_sub(self.offset)
2240 .ok_or(Error::InvalidData("mdat does not contain extent"))?;
2241 let slice = match extent {
2242 ExtentRange::WithLength(range) => {
2243 let range_len = range
2244 .end
2245 .checked_sub(range.start)
2246 .ok_or(Error::InvalidData("range start > end"))?;
2247 let end = start_offset
2248 .checked_add(range_len)
2249 .ok_or(Error::InvalidData("extent end overflow"))?;
2250 self.data.get(start_offset.try_into()?..end.try_into()?)
2251 },
2252 ExtentRange::ToEnd(_) => self.data.get(start_offset.try_into()?..),
2253 };
2254 let slice = slice.ok_or(Error::InvalidData("extent crosses box boundary"))?;
2255 buf.extend_from_slice(slice)?;
2256 Ok(())
2257 }
2258
2259}
2260
2261#[derive(Debug)]
2265struct ItemInfoEntry {
2266 item_id: u32,
2267 item_type: FourCC,
2268}
2269
2270#[derive(Debug)]
2272struct SingleItemTypeReferenceBox {
2273 item_type: FourCC,
2274 from_item_id: u32,
2275 to_item_id: u32,
2276 reference_index: u16,
2279}
2280
2281#[derive(Debug)]
2284enum IlocFieldSize {
2285 Zero,
2286 Four,
2287 Eight,
2288}
2289
2290impl IlocFieldSize {
2291 const fn to_bits(&self) -> u8 {
2292 match self {
2293 Self::Zero => 0,
2294 Self::Four => 32,
2295 Self::Eight => 64,
2296 }
2297 }
2298}
2299
2300impl TryFrom<u8> for IlocFieldSize {
2301 type Error = Error;
2302
2303 fn try_from(value: u8) -> Result<Self> {
2304 match value {
2305 0 => Ok(Self::Zero),
2306 4 => Ok(Self::Four),
2307 8 => Ok(Self::Eight),
2308 _ => Err(Error::InvalidData("value must be in the set {0, 4, 8}")),
2309 }
2310 }
2311}
2312
2313#[derive(PartialEq)]
2314enum IlocVersion {
2315 Zero,
2316 One,
2317 Two,
2318}
2319
2320impl TryFrom<u8> for IlocVersion {
2321 type Error = Error;
2322
2323 fn try_from(value: u8) -> Result<Self> {
2324 match value {
2325 0 => Ok(Self::Zero),
2326 1 => Ok(Self::One),
2327 2 => Ok(Self::Two),
2328 _ => Err(Error::Unsupported("unsupported version in 'iloc' box")),
2329 }
2330 }
2331}
2332
2333#[derive(Debug)]
2338struct ItemLocationBoxItem {
2339 item_id: u32,
2340 construction_method: ConstructionMethod,
2341 extents: TryVec<ItemLocationBoxExtent>,
2343}
2344
2345#[derive(Clone, Copy, Debug, PartialEq)]
2346enum ConstructionMethod {
2347 File,
2348 Idat,
2349 #[allow(dead_code)] Item,
2351}
2352
2353#[derive(Clone, Debug)]
2356struct ItemLocationBoxExtent {
2357 extent_range: ExtentRange,
2358}
2359
2360#[derive(Clone, Debug)]
2361enum ExtentRange {
2362 WithLength(Range<u64>),
2363 ToEnd(RangeFrom<u64>),
2364}
2365
2366impl ExtentRange {
2367 const fn start(&self) -> u64 {
2368 match self {
2369 Self::WithLength(r) => r.start,
2370 Self::ToEnd(r) => r.start,
2371 }
2372 }
2373}
2374
2375struct BMFFBox<'a, T> {
2377 head: BoxHeader,
2378 content: Take<&'a mut T>,
2379}
2380
2381impl<T: Read> BMFFBox<'_, T> {
2382 fn read_into_try_vec(&mut self) -> std::io::Result<TryVec<u8>> {
2383 let limit = self.content.limit();
2384 let mut vec = if limit >= u64::MAX - BoxHeader::MIN_LARGE_SIZE {
2388 std::vec::Vec::new()
2390 } else {
2391 let mut v = std::vec::Vec::new();
2392 v.try_reserve_exact(limit as usize)
2393 .map_err(|_| std::io::ErrorKind::OutOfMemory)?;
2394 v
2395 };
2396 self.content.read_to_end(&mut vec)?; Ok(vec.into())
2398 }
2399}
2400
2401#[test]
2402fn box_read_to_end() {
2403 let tmp = &mut b"1234567890".as_slice();
2404 let mut src = BMFFBox {
2405 head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None },
2406 content: <_ as Read>::take(tmp, 5),
2407 };
2408 let buf = src.read_into_try_vec().unwrap();
2409 assert_eq!(buf.len(), 5);
2410 assert_eq!(buf, b"12345".as_ref());
2411}
2412
2413#[test]
2414fn box_read_to_end_oom() {
2415 let tmp = &mut b"1234567890".as_slice();
2416 let mut src = BMFFBox {
2417 head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None },
2418 content: <_ as Read>::take(tmp, u64::MAX / 2),
2420 };
2421 assert!(src.read_into_try_vec().is_err());
2422}
2423
2424struct BoxIter<'a, T> {
2425 src: &'a mut T,
2426}
2427
2428impl<T: Read> BoxIter<'_, T> {
2429 fn new(src: &mut T) -> BoxIter<'_, T> {
2430 BoxIter { src }
2431 }
2432
2433 fn next_box(&mut self) -> Result<Option<BMFFBox<'_, T>>> {
2434 let r = read_box_header(self.src);
2435 match r {
2436 Ok(h) => Ok(Some(BMFFBox {
2437 head: h,
2438 content: self.src.take(h.size - h.offset),
2439 })),
2440 Err(Error::UnexpectedEOF) => Ok(None),
2441 Err(e) => Err(e),
2442 }
2443 }
2444}
2445
2446impl<T: Read> Read for BMFFBox<'_, T> {
2447 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2448 self.content.read(buf)
2449 }
2450}
2451
2452impl<T: Offset> Offset for BMFFBox<'_, T> {
2453 fn offset(&self) -> u64 {
2454 self.content.get_ref().offset()
2455 }
2456}
2457
2458impl<T: Read> BMFFBox<'_, T> {
2459 fn bytes_left(&self) -> u64 {
2460 self.content.limit()
2461 }
2462
2463 const fn get_header(&self) -> &BoxHeader {
2464 &self.head
2465 }
2466
2467 fn box_iter(&mut self) -> BoxIter<'_, Self> {
2468 BoxIter::new(self)
2469 }
2470}
2471
2472impl<T> Drop for BMFFBox<'_, T> {
2473 fn drop(&mut self) {
2474 if self.content.limit() > 0 {
2475 let name: FourCC = From::from(self.head.name);
2476 debug!("Dropping {} bytes in '{}'", self.content.limit(), name);
2477 }
2478 }
2479}
2480
2481fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
2490 let size32 = be_u32(src)?;
2491 let name = BoxType::from(be_u32(src)?);
2492 let size = match size32 {
2493 0 => {
2495 u64::MAX
2497 },
2498 1 => {
2499 let size64 = be_u64(src)?;
2500 if size64 < BoxHeader::MIN_LARGE_SIZE {
2501 return Err(Error::InvalidData("malformed wide size"));
2502 }
2503 size64
2504 },
2505 _ => {
2506 if u64::from(size32) < BoxHeader::MIN_SIZE {
2507 return Err(Error::InvalidData("malformed size"));
2508 }
2509 u64::from(size32)
2510 },
2511 };
2512 let mut offset = match size32 {
2513 1 => BoxHeader::MIN_LARGE_SIZE,
2514 _ => BoxHeader::MIN_SIZE,
2515 };
2516 let uuid = if name == BoxType::UuidBox {
2517 if size >= offset + 16 {
2518 let mut buffer = [0u8; 16];
2519 let count = src.read(&mut buffer)?;
2520 offset += count.to_u64();
2521 if count == 16 {
2522 Some(buffer)
2523 } else {
2524 debug!("malformed uuid (short read), skipping");
2525 None
2526 }
2527 } else {
2528 debug!("malformed uuid, skipping");
2529 None
2530 }
2531 } else {
2532 None
2533 };
2534 if offset > size {
2535 return Err(Error::InvalidData("box header offset exceeds size"));
2536 }
2537 Ok(BoxHeader { name, size, offset, uuid })
2538}
2539
2540fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> {
2542 let version = src.read_u8()?;
2543 let flags_a = src.read_u8()?;
2544 let flags_b = src.read_u8()?;
2545 let flags_c = src.read_u8()?;
2546 Ok((
2547 version,
2548 u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c),
2549 ))
2550}
2551
2552fn read_fullbox_version_no_flags<T: ReadBytesExt>(src: &mut T, options: &ParseOptions) -> Result<u8> {
2554 let (version, flags) = read_fullbox_extra(src)?;
2555
2556 if flags != 0 && !options.lenient {
2557 return Err(Error::Unsupported("expected flags to be 0"));
2558 }
2559
2560 Ok(version)
2561}
2562
2563fn skip_box_content<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<()> {
2565 let to_skip = {
2567 let header = src.get_header();
2568 debug!("{header:?} (skipped)");
2569 header
2570 .size
2571 .checked_sub(header.offset)
2572 .ok_or(Error::InvalidData("header offset > size"))?
2573 };
2574 assert_eq!(to_skip, src.bytes_left());
2575 skip(src, to_skip)
2576}
2577
2578fn skip_box_remain<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<()> {
2580 let remain = {
2581 let header = src.get_header();
2582 let len = src.bytes_left();
2583 debug!("remain {len} (skipped) in {header:?}");
2584 len
2585 };
2586 skip(src, remain)
2587}
2588
2589struct ResourceTracker<'a> {
2590 config: &'a DecodeConfig,
2591 #[cfg(feature = "eager")]
2592 current_memory: u64,
2593 #[cfg(feature = "eager")]
2594 peak_memory: u64,
2595}
2596
2597impl<'a> ResourceTracker<'a> {
2598 fn new(config: &'a DecodeConfig) -> Self {
2599 Self {
2600 config,
2601 #[cfg(feature = "eager")]
2602 current_memory: 0,
2603 #[cfg(feature = "eager")]
2604 peak_memory: 0,
2605 }
2606 }
2607
2608 #[cfg(feature = "eager")]
2609 fn reserve(&mut self, bytes: u64) -> Result<()> {
2610 self.current_memory = self.current_memory.saturating_add(bytes);
2611 self.peak_memory = self.peak_memory.max(self.current_memory);
2612
2613 if let Some(limit) = self.config.peak_memory_limit
2614 && self.peak_memory > limit {
2615 return Err(Error::ResourceLimitExceeded("peak memory limit exceeded"));
2616 }
2617
2618 Ok(())
2619 }
2620
2621 #[cfg(feature = "eager")]
2622 fn release(&mut self, bytes: u64) {
2623 self.current_memory = self.current_memory.saturating_sub(bytes);
2624 }
2625
2626 #[cfg(feature = "eager")]
2627 fn validate_total_megapixels(&self, width: u32, height: u32) -> Result<()> {
2628 if let Some(limit) = self.config.total_megapixels_limit {
2629 let megapixels = (width as u64)
2630 .checked_mul(height as u64)
2631 .ok_or(Error::InvalidData("dimension overflow"))?
2632 / 1_000_000;
2633
2634 if megapixels > limit as u64 {
2635 return Err(Error::ResourceLimitExceeded("total megapixels limit exceeded"));
2636 }
2637 }
2638
2639 Ok(())
2640 }
2641
2642 fn validate_animation_frames(&self, count: u32) -> Result<()> {
2643 if let Some(limit) = self.config.max_animation_frames
2644 && count > limit {
2645 return Err(Error::ResourceLimitExceeded("animation frame count limit exceeded"));
2646 }
2647
2648 Ok(())
2649 }
2650
2651 fn validate_grid_tiles(&self, count: u32) -> Result<()> {
2652 if let Some(limit) = self.config.max_grid_tiles
2653 && count > limit {
2654 return Err(Error::ResourceLimitExceeded("grid tile count limit exceeded"));
2655 }
2656
2657 Ok(())
2658 }
2659}
2660
2661#[cfg(feature = "eager")]
2672#[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader_with_config()` instead")]
2673#[allow(deprecated)]
2674pub fn read_avif_with_config<T: Read>(
2675 f: &mut T,
2676 config: &DecodeConfig,
2677 stop: &dyn Stop,
2678) -> Result<AvifData> {
2679 let mut tracker = ResourceTracker::new(config);
2680 let mut f = OffsetReader::new(f);
2681
2682 let mut iter = BoxIter::new(&mut f);
2683
2684 let (major_brand, compatible_brands) = if let Some(mut b) = iter.next_box()? {
2686 if b.head.name == BoxType::FileTypeBox {
2687 let ftyp = read_ftyp(&mut b)?;
2688 if ftyp.major_brand != b"avif" && ftyp.major_brand != b"avis" {
2690 warn!("major_brand: {}", ftyp.major_brand);
2691 return Err(Error::InvalidData("ftyp must be 'avif' or 'avis'"));
2692 }
2693 let major = ftyp.major_brand.value;
2694 let compat = ftyp.compatible_brands.iter().map(|b| b.value).collect();
2695 (major, compat)
2696 } else {
2697 return Err(Error::InvalidData("'ftyp' box must occur first"));
2698 }
2699 } else {
2700 return Err(Error::InvalidData("'ftyp' box must occur first"));
2701 };
2702
2703 let mut meta = None;
2704 let mut mdats = TryVec::new();
2705 let mut animation_data: Option<ParsedAnimationData> = None;
2706
2707 let parse_opts = ParseOptions { lenient: config.lenient };
2708
2709 while let Some(mut b) = iter.next_box()? {
2710 stop.check()?;
2711
2712 match b.head.name {
2713 BoxType::MetadataBox => {
2714 if meta.is_some() {
2715 return Err(Error::InvalidData("There should be zero or one meta boxes per ISO 14496-12:2015 § 8.11.1.1"));
2716 }
2717 meta = Some(read_avif_meta(&mut b, &parse_opts)?);
2718 },
2719 BoxType::MovieBox => {
2720 let tracks = read_moov(&mut b)?;
2721 if !tracks.is_empty() {
2722 animation_data = Some(associate_tracks(tracks)?);
2723 }
2724 },
2725 BoxType::MediaDataBox => {
2726 if b.bytes_left() > 0 {
2727 let offset = b.offset();
2728 let size = b.bytes_left();
2729 tracker.reserve(size)?;
2730 let data = b.read_into_try_vec()?;
2731 tracker.release(size);
2732 mdats.push(MediaDataBox { offset, data })?;
2733 }
2734 },
2735 _ => skip_box_content(&mut b)?,
2736 }
2737
2738 check_parser_state(&b.head, &b.content)?;
2739 }
2740
2741 if meta.is_none() && animation_data.is_none() {
2743 return Err(Error::InvalidData("missing meta"));
2744 }
2745 let Some(meta) = meta else {
2746 return Ok(AvifData {
2748 ..Default::default()
2749 });
2750 };
2751
2752 let is_grid = meta
2754 .item_infos
2755 .iter()
2756 .find(|x| x.item_id == meta.primary_item_id)
2757 .is_some_and(|info| {
2758 let is_g = info.item_type == b"grid";
2759 if is_g {
2760 log::debug!("Grid image detected: primary_item_id={}", meta.primary_item_id);
2761 }
2762 is_g
2763 });
2764
2765 let mut grid_config = if is_grid {
2767 meta.properties
2768 .iter()
2769 .find(|prop| {
2770 prop.item_id == meta.primary_item_id
2771 && matches!(prop.property, ItemProperty::ImageGrid(_))
2772 })
2773 .and_then(|prop| match &prop.property {
2774 ItemProperty::ImageGrid(config) => {
2775 log::debug!("Grid: found explicit ImageGrid property: {:?}", config);
2776 Some(config.clone())
2777 },
2778 _ => None,
2779 })
2780 } else {
2781 None
2782 };
2783
2784 let tile_item_ids: TryVec<u32> = if is_grid {
2786 let mut tiles_with_index: TryVec<(u32, u16)> = TryVec::new();
2788 for iref in meta.item_references.iter() {
2789 if iref.from_item_id == meta.primary_item_id && iref.item_type == b"dimg" {
2791 tiles_with_index.push((iref.to_item_id, iref.reference_index))?;
2792 }
2793 }
2794
2795 tracker.validate_grid_tiles(tiles_with_index.len() as u32)?;
2797
2798 tiles_with_index.sort_by_key(|&(_, idx)| idx);
2800
2801 let mut ids = TryVec::new();
2803 for (tile_id, _) in tiles_with_index.iter() {
2804 ids.push(*tile_id)?;
2805 }
2806
2807 if grid_config.is_none() && !ids.is_empty() {
2811 let grid_dims = meta.properties.iter()
2813 .find(|p| p.item_id == meta.primary_item_id)
2814 .and_then(|p| match &p.property {
2815 ItemProperty::ImageSpatialExtents(e) => Some(e),
2816 _ => None,
2817 });
2818
2819 let tile_dims = ids.first().and_then(|&tile_id| {
2820 meta.properties.iter()
2821 .find(|p| p.item_id == tile_id)
2822 .and_then(|p| match &p.property {
2823 ItemProperty::ImageSpatialExtents(e) => Some(e),
2824 _ => None,
2825 })
2826 });
2827
2828 if let (Some(grid), Some(tile)) = (grid_dims, tile_dims) {
2829 tracker.validate_total_megapixels(grid.width, grid.height)?;
2831
2832 if tile.width == 0 || tile.height == 0 {
2834 log::warn!("Grid: tile has zero dimensions, using fallback");
2835 } else if grid.width % tile.width == 0 && grid.height % tile.height == 0 {
2836 let columns = grid.width / tile.width;
2838 let rows = grid.height / tile.height;
2839
2840 if columns > 255 || rows > 255 {
2842 log::warn!("Grid: calculated dimensions {}×{} exceed 255, using fallback", rows, columns);
2843 } else {
2844 log::debug!("Grid: calculated {}×{} layout from ispe dimensions", rows, columns);
2845 grid_config = Some(GridConfig {
2846 rows: rows as u8,
2847 columns: columns as u8,
2848 output_width: grid.width,
2849 output_height: grid.height,
2850 });
2851 }
2852 } else {
2853 log::warn!("Grid: dimension mismatch - grid {}×{} not evenly divisible by tile {}×{}, using fallback",
2854 grid.width, grid.height, tile.width, tile.height);
2855 }
2856 }
2857
2858 if grid_config.is_none() {
2860 log::debug!("Grid: using fallback {}×1 layout inference", ids.len());
2861 grid_config = Some(GridConfig {
2862 rows: ids.len() as u8, columns: 1, output_width: 0, output_height: 0, });
2867 }
2868 }
2869
2870 ids
2871 } else {
2872 TryVec::new()
2873 };
2874
2875 let alpha_item_id = meta
2876 .item_references
2877 .iter()
2878 .filter(|iref| {
2880 iref.to_item_id == meta.primary_item_id
2881 && iref.from_item_id != meta.primary_item_id
2882 && iref.item_type == b"auxl"
2883 })
2884 .map(|iref| iref.from_item_id)
2885 .find(|&item_id| {
2887 meta.properties.iter().any(|prop| {
2888 prop.item_id == item_id
2889 && match &prop.property {
2890 ItemProperty::AuxiliaryType(urn) => {
2891 urn.type_subtype().0 == b"urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
2892 }
2893 _ => false,
2894 }
2895 })
2896 });
2897
2898 macro_rules! find_prop {
2900 ($variant:ident) => {
2901 meta.properties.iter().find_map(|p| {
2902 if p.item_id == meta.primary_item_id {
2903 match &p.property {
2904 ItemProperty::$variant(c) => Some(c.clone()),
2905 _ => None,
2906 }
2907 } else {
2908 None
2909 }
2910 })
2911 };
2912 }
2913
2914 let av1_config = find_prop!(AV1Config);
2915 let color_info = find_prop!(ColorInformation);
2916 let rotation = find_prop!(Rotation);
2917 let mirror = find_prop!(Mirror);
2918 let clean_aperture = find_prop!(CleanAperture);
2919 let pixel_aspect_ratio = find_prop!(PixelAspectRatio);
2920 let content_light_level = find_prop!(ContentLightLevel);
2921 let mastering_display = find_prop!(MasteringDisplayColourVolume);
2922 let content_colour_volume = find_prop!(ContentColourVolume);
2923 let ambient_viewing = find_prop!(AmbientViewingEnvironment);
2924 let operating_point = find_prop!(OperatingPointSelector);
2925 let layer_selector = find_prop!(LayerSelector);
2926 let layered_image_indexing = find_prop!(AV1LayeredImageIndexing);
2927
2928 let mut context = AvifData {
2929 premultiplied_alpha: alpha_item_id.is_some_and(|alpha_item_id| {
2930 meta.item_references.iter().any(|iref| {
2931 iref.from_item_id == meta.primary_item_id
2932 && iref.to_item_id == alpha_item_id
2933 && iref.item_type == b"prem"
2934 })
2935 }),
2936 av1_config,
2937 color_info,
2938 rotation,
2939 mirror,
2940 clean_aperture,
2941 pixel_aspect_ratio,
2942 content_light_level,
2943 mastering_display,
2944 content_colour_volume,
2945 ambient_viewing,
2946 operating_point,
2947 layer_selector,
2948 layered_image_indexing,
2949 major_brand,
2950 compatible_brands,
2951 ..Default::default()
2952 };
2953
2954 let mut extract_item_data = |loc: &ItemLocationBoxItem, buf: &mut TryVec<u8>| -> Result<()> {
2956 match loc.construction_method {
2957 ConstructionMethod::File => {
2958 for extent in loc.extents.iter() {
2959 let mut found = false;
2960 for mdat in mdats.iter_mut() {
2961 if mdat.matches_extent(&extent.extent_range) {
2962 buf.append(&mut mdat.data)?;
2963 found = true;
2964 break;
2965 } else if mdat.contains_extent(&extent.extent_range) {
2966 mdat.read_extent(&extent.extent_range, buf)?;
2967 found = true;
2968 break;
2969 }
2970 }
2971 if !found {
2972 return Err(Error::InvalidData("iloc contains an extent that is not in mdat"));
2973 }
2974 }
2975 Ok(())
2976 },
2977 ConstructionMethod::Idat => {
2978 let idat_data = meta.idat.as_ref().ok_or(Error::InvalidData("idat box missing but construction_method is Idat"))?;
2979 for extent in loc.extents.iter() {
2980 match &extent.extent_range {
2981 ExtentRange::WithLength(range) => {
2982 let start = usize::try_from(range.start).map_err(|_| Error::InvalidData("extent start too large"))?;
2983 let end = usize::try_from(range.end).map_err(|_| Error::InvalidData("extent end too large"))?;
2984 if end > idat_data.len() {
2985 return Err(Error::InvalidData("extent exceeds idat size"));
2986 }
2987 buf.extend_from_slice(&idat_data[start..end]).map_err(|_| Error::OutOfMemory)?;
2988 },
2989 ExtentRange::ToEnd(range) => {
2990 let start = usize::try_from(range.start).map_err(|_| Error::InvalidData("extent start too large"))?;
2991 if start >= idat_data.len() {
2992 return Err(Error::InvalidData("extent start exceeds idat size"));
2993 }
2994 buf.extend_from_slice(&idat_data[start..]).map_err(|_| Error::OutOfMemory)?;
2995 },
2996 }
2997 }
2998 Ok(())
2999 },
3000 ConstructionMethod::Item => {
3001 Err(Error::Unsupported("construction_method 'item' not supported"))
3002 },
3003 }
3004 };
3005
3006 if is_grid {
3009 for (idx, &tile_id) in tile_item_ids.iter().enumerate() {
3011 if idx % 16 == 0 {
3012 stop.check()?;
3013 }
3014
3015 let mut tile_data = TryVec::new();
3016
3017 if let Some(loc) = meta.iloc_items.iter().find(|loc| loc.item_id == tile_id) {
3018 extract_item_data(loc, &mut tile_data)?;
3019 } else {
3020 return Err(Error::InvalidData("grid tile not found in iloc"));
3021 }
3022
3023 context.grid_tiles.push(tile_data)?;
3024 }
3025
3026 context.grid_config = grid_config;
3028 } else {
3029 for loc in meta.iloc_items.iter() {
3031 let item_data = if loc.item_id == meta.primary_item_id {
3032 &mut context.primary_item
3033 } else if Some(loc.item_id) == alpha_item_id {
3034 context.alpha_item.get_or_insert_with(TryVec::new)
3035 } else {
3036 continue;
3037 };
3038
3039 extract_item_data(loc, item_data)?;
3040 }
3041 }
3042
3043 for iref in meta.item_references.iter() {
3045 if iref.to_item_id != meta.primary_item_id || iref.item_type != b"cdsc" {
3046 continue;
3047 }
3048 let desc_item_id = iref.from_item_id;
3049 let Some(info) = meta.item_infos.iter().find(|i| i.item_id == desc_item_id) else {
3050 continue;
3051 };
3052 if info.item_type == b"Exif" {
3053 if let Some(loc) = meta.iloc_items.iter().find(|l| l.item_id == desc_item_id) {
3054 let mut raw = TryVec::new();
3055 extract_item_data(loc, &mut raw)?;
3056 if raw.len() > 4 {
3058 let offset = u32::from_be_bytes([raw[0], raw[1], raw[2], raw[3]]) as usize;
3059 let start = 4 + offset;
3060 if start < raw.len() {
3061 let mut exif = TryVec::new();
3062 exif.extend_from_slice(&raw[start..])?;
3063 context.exif = Some(exif);
3064 }
3065 }
3066 }
3067 } else if info.item_type == b"mime"
3068 && let Some(loc) = meta.iloc_items.iter().find(|l| l.item_id == desc_item_id)
3069 {
3070 let mut xmp = TryVec::new();
3071 extract_item_data(loc, &mut xmp)?;
3072 context.xmp = Some(xmp);
3073 }
3074 }
3075
3076 if let Some(tmap_info) = meta.item_infos.iter().find(|info| info.item_type == b"tmap") {
3078 let tmap_id = tmap_info.item_id;
3079
3080 let mut inputs: TryVec<(u32, u16)> = TryVec::new();
3081 for iref in meta.item_references.iter() {
3082 if iref.from_item_id == tmap_id && iref.item_type == b"dimg" {
3083 inputs.push((iref.to_item_id, iref.reference_index))?;
3084 }
3085 }
3086 inputs.sort_by_key(|&(_, idx)| idx);
3087
3088 if inputs.len() >= 2 && inputs[0].0 == meta.primary_item_id {
3089 let gmap_item_id = inputs[1].0;
3090
3091 if let Some(loc) = meta.iloc_items.iter().find(|l| l.item_id == tmap_id) {
3093 let mut tmap_data = TryVec::new();
3094 extract_item_data(loc, &mut tmap_data)?;
3095 if let Ok(metadata) = parse_tone_map_image(&tmap_data) {
3096 context.gain_map_metadata = Some(metadata);
3097 }
3098 }
3099
3100 if let Some(loc) = meta.iloc_items.iter().find(|l| l.item_id == gmap_item_id) {
3102 let mut gmap_data = TryVec::new();
3103 extract_item_data(loc, &mut gmap_data)?;
3104 context.gain_map_item = Some(gmap_data);
3105 }
3106
3107 context.gain_map_color_info = meta.properties.iter().find_map(|p| {
3109 if p.item_id == tmap_id {
3110 match &p.property {
3111 ItemProperty::ColorInformation(c) => Some(c.clone()),
3112 _ => None,
3113 }
3114 } else {
3115 None
3116 }
3117 });
3118 }
3119 }
3120
3121 if let Some(anim) = animation_data {
3123 let frame_count = anim.color_sample_table.sample_sizes.len() as u32;
3124 tracker.validate_animation_frames(frame_count)?;
3125
3126 log::debug!("Animation: extracting frames (media_timescale={})", anim.color_timescale);
3127 match extract_animation_frames(&anim.color_sample_table, anim.color_timescale, &mut mdats) {
3128 Ok(frames) => {
3129 if !frames.is_empty() {
3130 log::debug!("Animation: extracted {} frames", frames.len());
3131 context.animation = Some(AnimationConfig {
3132 loop_count: anim.loop_count,
3133 frames,
3134 });
3135 }
3136 }
3137 Err(e) => {
3138 log::warn!("Animation: failed to extract frames: {}", e);
3139 }
3140 }
3141 }
3142
3143 Ok(context)
3144}
3145
3146#[cfg(feature = "eager")]
3155#[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader_with_config()` with `DecodeConfig::lenient()` instead")]
3156#[allow(deprecated)]
3157pub fn read_avif_with_options<T: Read>(f: &mut T, options: &ParseOptions) -> Result<AvifData> {
3158 let config = DecodeConfig::unlimited().lenient(options.lenient);
3159 read_avif_with_config(f, &config, &Unstoppable)
3160}
3161
3162#[cfg(feature = "eager")]
3170#[deprecated(since = "1.5.0", note = "Use `AvifParser::from_reader()` instead")]
3171#[allow(deprecated)]
3172pub fn read_avif<T: Read>(f: &mut T) -> Result<AvifData> {
3173 read_avif_with_options(f, &ParseOptions::default())
3174}
3175
3176#[allow(dead_code)] struct EntityGroup {
3181 group_type: FourCC,
3182 group_id: u32,
3183 entity_ids: TryVec<u32>,
3184}
3185
3186fn read_grpl<T: Read + Offset>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<EntityGroup>> {
3191 let mut groups = TryVec::new();
3192 let mut iter = src.box_iter();
3193 while let Some(mut b) = iter.next_box()? {
3194 let group_type = FourCC::from(u32::from(b.head.name));
3195 let _version = b.read_u8()?;
3197 let mut flags_buf = [0u8; 3];
3198 b.read_exact(&mut flags_buf)?;
3199
3200 let group_id = be_u32(&mut b)?;
3201 let num_entities = be_u32(&mut b)?;
3202
3203 let mut entity_ids = TryVec::new();
3204 for _ in 0..num_entities {
3205 entity_ids.push(be_u32(&mut b)?)?;
3206 }
3207
3208 groups.push(EntityGroup {
3209 group_type,
3210 group_id,
3211 entity_ids,
3212 })?;
3213
3214 skip_box_remain(&mut b)?;
3215 check_parser_state(&b.head, &b.content)?;
3216 }
3217 Ok(groups)
3218}
3219
3220fn parse_tone_map_image(data: &[u8]) -> Result<GainMapMetadata> {
3224 let mut cursor = std::io::Cursor::new(data);
3225
3226 let version = cursor.read_u8()?;
3228 if version != 0 {
3229 return Err(Error::Unsupported("tmap version"));
3230 }
3231
3232 let minimum_version = be_u16(&mut cursor)?;
3234 if minimum_version > 0 {
3235 return Err(Error::Unsupported("tmap minimum version"));
3236 }
3237
3238 let writer_version = be_u16(&mut cursor)?;
3240 if writer_version < minimum_version {
3241 return Err(Error::InvalidData("tmap writer_version < minimum_version"));
3242 }
3243
3244 let flags = cursor.read_u8()?;
3246 let is_multichannel = (flags & 0x80) != 0;
3247 let use_base_colour_space = (flags & 0x40) != 0;
3248
3249 let base_hdr_headroom_n = be_u32(&mut cursor)?;
3251 let base_hdr_headroom_d = be_u32(&mut cursor)?;
3252 let alternate_hdr_headroom_n = be_u32(&mut cursor)?;
3253 let alternate_hdr_headroom_d = be_u32(&mut cursor)?;
3254
3255 let channel_count = if is_multichannel { 3 } else { 1 };
3256 let mut channels = [GainMapChannel {
3257 gain_map_min_n: 0, gain_map_min_d: 0,
3258 gain_map_max_n: 0, gain_map_max_d: 0,
3259 gamma_n: 0, gamma_d: 0,
3260 base_offset_n: 0, base_offset_d: 0,
3261 alternate_offset_n: 0, alternate_offset_d: 0,
3262 }; 3];
3263
3264 for ch in channels.iter_mut().take(channel_count) {
3265 ch.gain_map_min_n = be_i32(&mut cursor)?;
3266 ch.gain_map_min_d = be_u32(&mut cursor)?;
3267 ch.gain_map_max_n = be_i32(&mut cursor)?;
3268 ch.gain_map_max_d = be_u32(&mut cursor)?;
3269 ch.gamma_n = be_u32(&mut cursor)?;
3270 ch.gamma_d = be_u32(&mut cursor)?;
3271 ch.base_offset_n = be_i32(&mut cursor)?;
3272 ch.base_offset_d = be_u32(&mut cursor)?;
3273 ch.alternate_offset_n = be_i32(&mut cursor)?;
3274 ch.alternate_offset_d = be_u32(&mut cursor)?;
3275 }
3276
3277 if !is_multichannel {
3279 channels[1] = channels[0];
3280 channels[2] = channels[0];
3281 }
3282
3283 Ok(GainMapMetadata {
3284 is_multichannel,
3285 use_base_colour_space,
3286 base_hdr_headroom_n,
3287 base_hdr_headroom_d,
3288 alternate_hdr_headroom_n,
3289 alternate_hdr_headroom_d,
3290 channels,
3291 })
3292}
3293
3294fn read_avif_meta<T: Read + Offset>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<AvifInternalMeta> {
3299 let version = read_fullbox_version_no_flags(src, options)?;
3300
3301 if version != 0 {
3302 return Err(Error::Unsupported("unsupported meta version"));
3303 }
3304
3305 let mut primary_item_id = None;
3306 let mut item_infos = None;
3307 let mut iloc_items = None;
3308 let mut item_references = TryVec::new();
3309 let mut properties = TryVec::new();
3310 let mut idat = None;
3311 let mut entity_groups = TryVec::new();
3312
3313 let mut iter = src.box_iter();
3314 while let Some(mut b) = iter.next_box()? {
3315 match b.head.name {
3316 BoxType::ItemInfoBox => {
3317 if item_infos.is_some() {
3318 return Err(Error::InvalidData("There should be zero or one iinf boxes per ISO 14496-12:2015 § 8.11.6.1"));
3319 }
3320 item_infos = Some(read_iinf(&mut b, options)?);
3321 },
3322 BoxType::ItemLocationBox => {
3323 if iloc_items.is_some() {
3324 return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.3.1"));
3325 }
3326 iloc_items = Some(read_iloc(&mut b, options)?);
3327 },
3328 BoxType::PrimaryItemBox => {
3329 if primary_item_id.is_some() {
3330 return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.4.1"));
3331 }
3332 primary_item_id = Some(read_pitm(&mut b, options)?);
3333 },
3334 BoxType::ImageReferenceBox => {
3335 item_references.append(&mut read_iref(&mut b, options)?)?;
3336 },
3337 BoxType::ImagePropertiesBox => {
3338 properties = read_iprp(&mut b, options)?;
3339 },
3340 BoxType::ItemDataBox => {
3341 if idat.is_some() {
3342 return Err(Error::InvalidData("There should be zero or one idat boxes"));
3343 }
3344 idat = Some(b.read_into_try_vec()?);
3345 },
3346 BoxType::GroupsListBox => {
3347 entity_groups.append(&mut read_grpl(&mut b)?)?;
3348 },
3349 BoxType::HandlerBox => {
3350 let hdlr = read_hdlr(&mut b)?;
3351 if hdlr.handler_type != b"pict" {
3352 warn!("hdlr handler_type: {}", hdlr.handler_type);
3353 return Err(Error::InvalidData("meta handler_type must be 'pict' for AVIF"));
3354 }
3355 },
3356 _ => skip_box_content(&mut b)?,
3357 }
3358
3359 check_parser_state(&b.head, &b.content)?;
3360 }
3361
3362 let primary_item_id = primary_item_id.ok_or(Error::InvalidData("Required pitm box not present in meta box"))?;
3363
3364 let item_infos = item_infos.ok_or(Error::InvalidData("iinf missing"))?;
3365
3366 if let Some(item_info) = item_infos.iter().find(|x| x.item_id == primary_item_id) {
3367 if item_info.item_type != b"av01" && item_info.item_type != b"grid" {
3369 warn!("primary_item_id type: {}", item_info.item_type);
3370 return Err(Error::InvalidData("primary_item_id type is not av01 or grid"));
3371 }
3372 } else {
3373 return Err(Error::InvalidData("primary_item_id not present in iinf box"));
3374 }
3375
3376 Ok(AvifInternalMeta {
3377 properties,
3378 item_references,
3379 primary_item_id,
3380 iloc_items: iloc_items.ok_or(Error::InvalidData("iloc missing"))?,
3381 item_infos,
3382 idat,
3383 entity_groups,
3384 })
3385}
3386
3387fn read_hdlr<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<HandlerBox> {
3390 let (_version, _flags) = read_fullbox_extra(src)?;
3391 skip(src, 4)?;
3393 let handler_type = be_u32(src)?;
3395 skip_box_remain(src)?;
3397 Ok(HandlerBox {
3398 handler_type: FourCC::from(handler_type),
3399 })
3400}
3401
3402fn read_pitm<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<u32> {
3405 let version = read_fullbox_version_no_flags(src, options)?;
3406
3407 let item_id = match version {
3408 0 => be_u16(src)?.into(),
3409 1 => be_u32(src)?,
3410 _ => return Err(Error::Unsupported("unsupported pitm version")),
3411 };
3412
3413 Ok(item_id)
3414}
3415
3416fn read_iinf<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<ItemInfoEntry>> {
3419 let version = read_fullbox_version_no_flags(src, options)?;
3420
3421 match version {
3422 0 | 1 => (),
3423 _ => return Err(Error::Unsupported("unsupported iinf version")),
3424 }
3425
3426 let entry_count = if version == 0 {
3427 be_u16(src)?.to_usize()
3428 } else {
3429 be_u32(src)?.to_usize()
3430 };
3431 let mut item_infos = TryVec::with_capacity(entry_count)?;
3432
3433 let mut iter = src.box_iter();
3434 while let Some(mut b) = iter.next_box()? {
3435 if b.head.name != BoxType::ItemInfoEntry {
3436 return Err(Error::InvalidData("iinf box should contain only infe boxes"));
3437 }
3438
3439 item_infos.push(read_infe(&mut b)?)?;
3440
3441 check_parser_state(&b.head, &b.content)?;
3442 }
3443
3444 Ok(item_infos)
3445}
3446
3447fn read_infe<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ItemInfoEntry> {
3450 let (version, _) = read_fullbox_extra(src)?;
3453
3454 let item_id = match version {
3456 2 => be_u16(src)?.into(),
3457 3 => be_u32(src)?,
3458 _ => return Err(Error::Unsupported("unsupported version in 'infe' box")),
3459 };
3460
3461 let item_protection_index = be_u16(src)?;
3462
3463 if item_protection_index != 0 {
3464 return Err(Error::Unsupported("protected items (infe.item_protection_index != 0) are not supported"));
3465 }
3466
3467 let item_type = FourCC::from(be_u32(src)?);
3468 debug!("infe item_id {item_id} item_type: {item_type}");
3469
3470 skip_box_remain(src)?;
3472
3473 Ok(ItemInfoEntry { item_id, item_type })
3474}
3475
3476fn read_iref<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<SingleItemTypeReferenceBox>> {
3477 let mut item_references = TryVec::new();
3478 let version = read_fullbox_version_no_flags(src, options)?;
3479 if version > 1 {
3480 return Err(Error::Unsupported("iref version"));
3481 }
3482
3483 let mut iter = src.box_iter();
3484 while let Some(mut b) = iter.next_box()? {
3485 let from_item_id = if version == 0 {
3486 be_u16(&mut b)?.into()
3487 } else {
3488 be_u32(&mut b)?
3489 };
3490 let reference_count = be_u16(&mut b)?;
3491 for reference_index in 0..reference_count {
3492 let to_item_id = if version == 0 {
3493 be_u16(&mut b)?.into()
3494 } else {
3495 be_u32(&mut b)?
3496 };
3497 if from_item_id == to_item_id {
3498 return Err(Error::InvalidData("from_item_id and to_item_id must be different"));
3499 }
3500 item_references.push(SingleItemTypeReferenceBox {
3501 item_type: b.head.name.into(),
3502 from_item_id,
3503 to_item_id,
3504 reference_index,
3505 })?;
3506 }
3507 check_parser_state(&b.head, &b.content)?;
3508 }
3509 Ok(item_references)
3510}
3511
3512const MUST_BE_ESSENTIAL: &[&[u8; 4]] = &[b"a1op", b"lsel", b"clap", b"irot", b"imir"];
3515
3516const MUST_NOT_BE_ESSENTIAL: &[&[u8; 4]] = &[b"a1lx"];
3519
3520fn read_iprp<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<AssociatedProperty>> {
3521 let mut iter = src.box_iter();
3522 let mut properties = TryVec::new();
3523 let mut associations = TryVec::new();
3524
3525 while let Some(mut b) = iter.next_box()? {
3526 match b.head.name {
3527 BoxType::ItemPropertyContainerBox => {
3528 properties = read_ipco(&mut b, options)?;
3529 },
3530 BoxType::ItemPropertyAssociationBox => {
3531 associations = read_ipma(&mut b)?;
3532 },
3533 _ => return Err(Error::InvalidData("unexpected ipco child")),
3534 }
3535 }
3536
3537 let mut associated = TryVec::new();
3538 for a in associations {
3539 let index = match a.property_index {
3540 0 => {
3541 if a.essential {
3543 return Err(Error::InvalidData(
3544 "ipma property_index 0 must not be marked essential",
3545 ));
3546 }
3547 continue;
3548 }
3549 x => x as usize - 1,
3550 };
3551
3552 let Some(entry) = properties.get(index) else {
3553 continue;
3554 };
3555
3556 let is_supported = entry.property != ItemProperty::Unsupported;
3557 let fourcc_bytes = &entry.fourcc.value;
3558
3559 if is_supported {
3560 if a.essential && MUST_NOT_BE_ESSENTIAL.contains(&fourcc_bytes) {
3562 warn!("item {} has {} marked essential (spec forbids it)", a.item_id, entry.fourcc);
3563 if !options.lenient {
3564 return Err(Error::InvalidData(
3565 "property must not be marked essential",
3566 ));
3567 }
3568 }
3569 if !a.essential && MUST_BE_ESSENTIAL.contains(&fourcc_bytes) {
3570 warn!("item {} has {} not marked essential (spec requires it)", a.item_id, entry.fourcc);
3571 if !options.lenient {
3572 return Err(Error::InvalidData(
3573 "property must be marked essential",
3574 ));
3575 }
3576 }
3577
3578 associated.push(AssociatedProperty {
3579 item_id: a.item_id,
3580 property: entry.property.try_clone()?,
3581 })?;
3582 } else if a.essential {
3583 warn!(
3585 "item {} has unsupported property {} marked essential; item will be unusable",
3586 a.item_id, entry.fourcc
3587 );
3588 if !options.lenient {
3589 return Err(Error::Unsupported(
3590 "unsupported property marked as essential",
3591 ));
3592 }
3593 }
3594 }
3596 Ok(associated)
3597}
3598
3599#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3601pub(crate) struct ImageSpatialExtents {
3602 pub(crate) width: u32,
3603 pub(crate) height: u32,
3604}
3605
3606#[derive(Debug, PartialEq)]
3607pub(crate) enum ItemProperty {
3608 Channels(ArrayVec<u8, 16>),
3609 AuxiliaryType(AuxiliaryTypeProperty),
3610 ImageSpatialExtents(ImageSpatialExtents),
3611 ImageGrid(GridConfig),
3612 AV1Config(AV1Config),
3613 ColorInformation(ColorInformation),
3614 Rotation(ImageRotation),
3615 Mirror(ImageMirror),
3616 CleanAperture(CleanAperture),
3617 PixelAspectRatio(PixelAspectRatio),
3618 ContentLightLevel(ContentLightLevel),
3619 MasteringDisplayColourVolume(MasteringDisplayColourVolume),
3620 ContentColourVolume(ContentColourVolume),
3621 AmbientViewingEnvironment(AmbientViewingEnvironment),
3622 OperatingPointSelector(OperatingPointSelector),
3623 LayerSelector(LayerSelector),
3624 AV1LayeredImageIndexing(AV1LayeredImageIndexing),
3625 Unsupported,
3626}
3627
3628impl TryClone for ItemProperty {
3629 fn try_clone(&self) -> Result<Self, TryReserveError> {
3630 Ok(match self {
3631 Self::Channels(val) => Self::Channels(val.clone()),
3632 Self::AuxiliaryType(val) => Self::AuxiliaryType(val.try_clone()?),
3633 Self::ImageSpatialExtents(val) => Self::ImageSpatialExtents(*val),
3634 Self::ImageGrid(val) => Self::ImageGrid(val.clone()),
3635 Self::AV1Config(val) => Self::AV1Config(val.clone()),
3636 Self::ColorInformation(val) => Self::ColorInformation(val.clone()),
3637 Self::Rotation(val) => Self::Rotation(*val),
3638 Self::Mirror(val) => Self::Mirror(*val),
3639 Self::CleanAperture(val) => Self::CleanAperture(*val),
3640 Self::PixelAspectRatio(val) => Self::PixelAspectRatio(*val),
3641 Self::ContentLightLevel(val) => Self::ContentLightLevel(*val),
3642 Self::MasteringDisplayColourVolume(val) => Self::MasteringDisplayColourVolume(*val),
3643 Self::ContentColourVolume(val) => Self::ContentColourVolume(*val),
3644 Self::AmbientViewingEnvironment(val) => Self::AmbientViewingEnvironment(*val),
3645 Self::OperatingPointSelector(val) => Self::OperatingPointSelector(*val),
3646 Self::LayerSelector(val) => Self::LayerSelector(*val),
3647 Self::AV1LayeredImageIndexing(val) => Self::AV1LayeredImageIndexing(*val),
3648 Self::Unsupported => Self::Unsupported,
3649 })
3650 }
3651}
3652
3653struct Association {
3654 item_id: u32,
3655 essential: bool,
3656 property_index: u16,
3657}
3658
3659pub(crate) struct AssociatedProperty {
3660 pub item_id: u32,
3661 pub property: ItemProperty,
3662}
3663
3664fn read_ipma<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<Association>> {
3665 let (version, flags) = read_fullbox_extra(src)?;
3666
3667 let mut associations = TryVec::new();
3668
3669 let entry_count = be_u32(src)?;
3670 for _ in 0..entry_count {
3671 let item_id = if version == 0 {
3672 be_u16(src)?.into()
3673 } else {
3674 be_u32(src)?
3675 };
3676 let association_count = src.read_u8()?;
3677 for _ in 0..association_count {
3678 let num_association_bytes = if flags & 1 == 1 { 2 } else { 1 };
3679 let association = &mut [0; 2][..num_association_bytes];
3680 src.read_exact(association)?;
3681 let mut association = BitReader::new(association);
3682 let essential = association.read_bool()?;
3683 let property_index = association.read_u16(association.remaining().try_into()?)?;
3684 associations.push(Association {
3685 item_id,
3686 essential,
3687 property_index,
3688 })?;
3689 }
3690 }
3691 Ok(associations)
3692}
3693
3694struct IndexedProperty {
3696 fourcc: FourCC,
3697 property: ItemProperty,
3698}
3699
3700fn read_ipco<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<IndexedProperty>> {
3701 let mut properties = TryVec::new();
3702
3703 let mut iter = src.box_iter();
3704 while let Some(mut b) = iter.next_box()? {
3705 let fourcc: FourCC = b.head.name.into();
3706 let prop = match b.head.name {
3708 BoxType::PixelInformationBox => ItemProperty::Channels(read_pixi(&mut b, options)?),
3709 BoxType::AuxiliaryTypeProperty => ItemProperty::AuxiliaryType(read_auxc(&mut b, options)?),
3710 BoxType::ImageSpatialExtentsBox => ItemProperty::ImageSpatialExtents(read_ispe(&mut b, options)?),
3711 BoxType::ImageGridBox => ItemProperty::ImageGrid(read_grid(&mut b, options)?),
3712 BoxType::AV1CodecConfigurationBox => ItemProperty::AV1Config(read_av1c(&mut b)?),
3713 BoxType::ColorInformationBox => {
3714 match read_colr(&mut b) {
3715 Ok(colr) => ItemProperty::ColorInformation(colr),
3716 Err(_) => ItemProperty::Unsupported,
3717 }
3718 },
3719 BoxType::ImageRotationBox => ItemProperty::Rotation(read_irot(&mut b)?),
3720 BoxType::ImageMirrorBox => ItemProperty::Mirror(read_imir(&mut b)?),
3721 BoxType::CleanApertureBox => ItemProperty::CleanAperture(read_clap(&mut b)?),
3722 BoxType::PixelAspectRatioBox => ItemProperty::PixelAspectRatio(read_pasp(&mut b)?),
3723 BoxType::ContentLightLevelBox => ItemProperty::ContentLightLevel(read_clli(&mut b)?),
3724 BoxType::MasteringDisplayColourVolumeBox => ItemProperty::MasteringDisplayColourVolume(read_mdcv(&mut b)?),
3725 BoxType::ContentColourVolumeBox => ItemProperty::ContentColourVolume(read_cclv(&mut b)?),
3726 BoxType::AmbientViewingEnvironmentBox => ItemProperty::AmbientViewingEnvironment(read_amve(&mut b)?),
3727 BoxType::OperatingPointSelectorBox => ItemProperty::OperatingPointSelector(read_a1op(&mut b)?),
3728 BoxType::LayerSelectorBox => ItemProperty::LayerSelector(read_lsel(&mut b)?),
3729 BoxType::AV1LayeredImageIndexingBox => ItemProperty::AV1LayeredImageIndexing(read_a1lx(&mut b)?),
3730 _ => {
3731 skip_box_remain(&mut b)?;
3732 ItemProperty::Unsupported
3733 },
3734 };
3735 properties.push(IndexedProperty { fourcc, property: prop })?;
3736 }
3737 Ok(properties)
3738}
3739
3740fn read_pixi<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<ArrayVec<u8, 16>> {
3741 let version = read_fullbox_version_no_flags(src, options)?;
3742 if version != 0 {
3743 return Err(Error::Unsupported("pixi version"));
3744 }
3745
3746 let num_channels = usize::from(src.read_u8()?);
3747 let mut channels = ArrayVec::new();
3748 channels.extend((0..num_channels.min(channels.capacity())).map(|_| 0));
3749 debug_assert_eq!(num_channels, channels.len());
3750 src.read_exact(&mut channels).map_err(|_| Error::InvalidData("invalid num_channels"))?;
3751
3752 if options.lenient && src.bytes_left() > 0 {
3754 skip(src, src.bytes_left())?;
3755 }
3756
3757 check_parser_state(&src.head, &src.content)?;
3758 Ok(channels)
3759}
3760
3761#[derive(Debug, PartialEq)]
3762struct AuxiliaryTypeProperty {
3763 aux_data: TryString,
3764}
3765
3766impl AuxiliaryTypeProperty {
3767 #[must_use]
3768 fn type_subtype(&self) -> (&[u8], &[u8]) {
3769 let split = self.aux_data.iter().position(|&b| b == b'\0')
3770 .map(|pos| self.aux_data.split_at(pos));
3771 if let Some((aux_type, rest)) = split {
3772 (aux_type, &rest[1..])
3773 } else {
3774 (&self.aux_data, &[])
3775 }
3776 }
3777}
3778
3779impl TryClone for AuxiliaryTypeProperty {
3780 fn try_clone(&self) -> Result<Self, TryReserveError> {
3781 Ok(Self {
3782 aux_data: self.aux_data.try_clone()?,
3783 })
3784 }
3785}
3786
3787fn read_auxc<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<AuxiliaryTypeProperty> {
3788 let version = read_fullbox_version_no_flags(src, options)?;
3789 if version != 0 {
3790 return Err(Error::Unsupported("auxC version"));
3791 }
3792
3793 let aux_data = src.read_into_try_vec()?;
3794
3795 Ok(AuxiliaryTypeProperty { aux_data })
3796}
3797
3798fn read_av1c<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<AV1Config> {
3801 let byte0 = src.read_u8()?;
3803 let marker = byte0 >> 7;
3804 let version = byte0 & 0x7F;
3805
3806 if marker != 1 {
3807 return Err(Error::InvalidData("av1C marker must be 1"));
3808 }
3809 if version != 1 {
3810 return Err(Error::Unsupported("av1C version must be 1"));
3811 }
3812
3813 let byte1 = src.read_u8()?;
3814 let profile = byte1 >> 5;
3815 let level = byte1 & 0x1F;
3816
3817 let byte2 = src.read_u8()?;
3818 let tier = byte2 >> 7;
3819 let high_bitdepth = (byte2 >> 6) & 1;
3820 let twelve_bit = (byte2 >> 5) & 1;
3821 let monochrome = (byte2 >> 4) & 1 != 0;
3822 let chroma_subsampling_x = (byte2 >> 3) & 1;
3823 let chroma_subsampling_y = (byte2 >> 2) & 1;
3824 let chroma_sample_position = byte2 & 0x03;
3825
3826 let byte3 = src.read_u8()?;
3827 let _ = byte3;
3830
3831 let bit_depth = if high_bitdepth != 0 {
3832 if twelve_bit != 0 { 12 } else { 10 }
3833 } else {
3834 8
3835 };
3836
3837 skip_box_remain(src)?;
3839
3840 Ok(AV1Config {
3841 profile,
3842 level,
3843 tier,
3844 bit_depth,
3845 monochrome,
3846 chroma_subsampling_x,
3847 chroma_subsampling_y,
3848 chroma_sample_position,
3849 })
3850}
3851
3852fn read_colr<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ColorInformation> {
3855 let colour_type = be_u32(src)?;
3857
3858 match &colour_type.to_be_bytes() {
3859 b"nclx" => {
3860 let color_primaries = be_u16(src)?;
3861 let transfer_characteristics = be_u16(src)?;
3862 let matrix_coefficients = be_u16(src)?;
3863 let full_range_byte = src.read_u8()?;
3864 let full_range = (full_range_byte >> 7) != 0;
3865 skip_box_remain(src)?;
3867 Ok(ColorInformation::Nclx {
3868 color_primaries,
3869 transfer_characteristics,
3870 matrix_coefficients,
3871 full_range,
3872 })
3873 }
3874 b"rICC" | b"prof" => {
3875 let icc_data = src.read_into_try_vec()?;
3876 Ok(ColorInformation::IccProfile(icc_data.to_vec()))
3877 }
3878 _ => {
3879 skip_box_remain(src)?;
3880 Err(Error::Unsupported("unsupported colr colour_type"))
3881 }
3882 }
3883}
3884
3885fn read_irot<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ImageRotation> {
3888 let byte = src.read_u8()?;
3889 let angle_code = byte & 0x03;
3890 let angle = match angle_code {
3891 0 => 0,
3892 1 => 90,
3893 2 => 180,
3894 _ => 270, };
3896 skip_box_remain(src)?;
3897 Ok(ImageRotation { angle })
3898}
3899
3900fn read_imir<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ImageMirror> {
3903 let byte = src.read_u8()?;
3904 let axis = byte & 0x01;
3905 skip_box_remain(src)?;
3906 Ok(ImageMirror { axis })
3907}
3908
3909fn read_clap<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<CleanAperture> {
3912 let width_n = be_u32(src)?;
3913 let width_d = be_u32(src)?;
3914 let height_n = be_u32(src)?;
3915 let height_d = be_u32(src)?;
3916 let horiz_off_n = be_i32(src)?;
3917 let horiz_off_d = be_u32(src)?;
3918 let vert_off_n = be_i32(src)?;
3919 let vert_off_d = be_u32(src)?;
3920 if width_d == 0 || height_d == 0 || horiz_off_d == 0 || vert_off_d == 0 {
3922 return Err(Error::InvalidData("clap denominator cannot be zero"));
3923 }
3924 skip_box_remain(src)?;
3925 Ok(CleanAperture {
3926 width_n, width_d,
3927 height_n, height_d,
3928 horiz_off_n, horiz_off_d,
3929 vert_off_n, vert_off_d,
3930 })
3931}
3932
3933fn read_pasp<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<PixelAspectRatio> {
3936 let h_spacing = be_u32(src)?;
3937 let v_spacing = be_u32(src)?;
3938 skip_box_remain(src)?;
3939 Ok(PixelAspectRatio { h_spacing, v_spacing })
3940}
3941
3942fn read_clli<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ContentLightLevel> {
3945 let max_content_light_level = be_u16(src)?;
3946 let max_pic_average_light_level = be_u16(src)?;
3947 skip_box_remain(src)?;
3948 Ok(ContentLightLevel {
3949 max_content_light_level,
3950 max_pic_average_light_level,
3951 })
3952}
3953
3954fn read_mdcv<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<MasteringDisplayColourVolume> {
3957 let primaries = [
3959 (be_u16(src)?, be_u16(src)?),
3960 (be_u16(src)?, be_u16(src)?),
3961 (be_u16(src)?, be_u16(src)?),
3962 ];
3963 let white_point = (be_u16(src)?, be_u16(src)?);
3964 let max_luminance = be_u32(src)?;
3965 let min_luminance = be_u32(src)?;
3966 skip_box_remain(src)?;
3967 Ok(MasteringDisplayColourVolume {
3968 primaries,
3969 white_point,
3970 max_luminance,
3971 min_luminance,
3972 })
3973}
3974
3975fn read_cclv<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ContentColourVolume> {
3978 let flags = src.read_u8()?;
3979 let primaries_present = flags & 0x20 != 0;
3980 let min_lum_present = flags & 0x10 != 0;
3981 let max_lum_present = flags & 0x08 != 0;
3982 let avg_lum_present = flags & 0x04 != 0;
3983
3984 let primaries = if primaries_present {
3985 Some([
3986 (be_i32(src)?, be_i32(src)?),
3987 (be_i32(src)?, be_i32(src)?),
3988 (be_i32(src)?, be_i32(src)?),
3989 ])
3990 } else {
3991 None
3992 };
3993
3994 let min_luminance = if min_lum_present { Some(be_u32(src)?) } else { None };
3995 let max_luminance = if max_lum_present { Some(be_u32(src)?) } else { None };
3996 let avg_luminance = if avg_lum_present { Some(be_u32(src)?) } else { None };
3997
3998 skip_box_remain(src)?;
3999 Ok(ContentColourVolume {
4000 primaries,
4001 min_luminance,
4002 max_luminance,
4003 avg_luminance,
4004 })
4005}
4006
4007fn read_amve<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<AmbientViewingEnvironment> {
4010 let ambient_illuminance = be_u32(src)?;
4011 let ambient_light_x = be_u16(src)?;
4012 let ambient_light_y = be_u16(src)?;
4013 skip_box_remain(src)?;
4014 Ok(AmbientViewingEnvironment {
4015 ambient_illuminance,
4016 ambient_light_x,
4017 ambient_light_y,
4018 })
4019}
4020
4021fn read_a1op<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<OperatingPointSelector> {
4024 let op_index = src.read_u8()?;
4025 if op_index > 31 {
4026 return Err(Error::InvalidData("a1op op_index must be 0..31"));
4027 }
4028 skip_box_remain(src)?;
4029 Ok(OperatingPointSelector { op_index })
4030}
4031
4032fn read_lsel<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<LayerSelector> {
4035 let layer_id = be_u16(src)?;
4036 skip_box_remain(src)?;
4037 Ok(LayerSelector { layer_id })
4038}
4039
4040fn read_a1lx<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<AV1LayeredImageIndexing> {
4043 let flags = src.read_u8()?;
4044 let large_size = flags & 0x01 != 0;
4045 let layer_sizes = if large_size {
4046 [be_u32(src)?, be_u32(src)?, be_u32(src)?]
4047 } else {
4048 [u32::from(be_u16(src)?), u32::from(be_u16(src)?), u32::from(be_u16(src)?)]
4049 };
4050 skip_box_remain(src)?;
4051 Ok(AV1LayeredImageIndexing { layer_sizes })
4052}
4053
4054fn read_ispe<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<ImageSpatialExtents> {
4057 let _version = read_fullbox_version_no_flags(src, options)?;
4058 let width = be_u32(src)?;
4061 let height = be_u32(src)?;
4062
4063 if width == 0 || height == 0 {
4065 return Err(Error::InvalidData("ispe dimensions cannot be zero"));
4066 }
4067
4068 Ok(ImageSpatialExtents { width, height })
4069}
4070
4071fn read_mvhd<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<MovieHeader> {
4074 let version = src.read_u8()?;
4075 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4076
4077 let (timescale, duration) = if version == 1 {
4078 let _creation_time = be_u64(src)?;
4079 let _modification_time = be_u64(src)?;
4080 let timescale = be_u32(src)?;
4081 let duration = be_u64(src)?;
4082 (timescale, duration)
4083 } else {
4084 let _creation_time = be_u32(src)?;
4085 let _modification_time = be_u32(src)?;
4086 let timescale = be_u32(src)?;
4087 let duration = be_u32(src)?;
4088 (timescale, duration as u64)
4089 };
4090
4091 skip_box_remain(src)?;
4093
4094 Ok(MovieHeader { _timescale: timescale, _duration: duration })
4095}
4096
4097fn read_mdhd<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<MediaHeader> {
4100 let version = src.read_u8()?;
4101 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4102
4103 let (timescale, duration) = if version == 1 {
4104 let _creation_time = be_u64(src)?;
4105 let _modification_time = be_u64(src)?;
4106 let timescale = be_u32(src)?;
4107 let duration = be_u64(src)?;
4108 (timescale, duration)
4109 } else {
4110 let _creation_time = be_u32(src)?;
4111 let _modification_time = be_u32(src)?;
4112 let timescale = be_u32(src)?;
4113 let duration = be_u32(src)?;
4114 (timescale, duration as u64)
4115 };
4116
4117 skip_box_remain(src)?;
4119
4120 Ok(MediaHeader { timescale, _duration: duration })
4121}
4122
4123fn read_stts<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<TimeToSampleEntry>> {
4126 let _version = src.read_u8()?;
4127 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4128 let entry_count = be_u32(src)?;
4129
4130 let mut entries = TryVec::new();
4131 for _ in 0..entry_count {
4132 entries.push(TimeToSampleEntry {
4133 sample_count: be_u32(src)?,
4134 sample_delta: be_u32(src)?,
4135 })?;
4136 }
4137
4138 Ok(entries)
4139}
4140
4141fn read_stsc<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<SampleToChunkEntry>> {
4144 let _version = src.read_u8()?;
4145 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4146 let entry_count = be_u32(src)?;
4147
4148 let mut entries = TryVec::new();
4149 for _ in 0..entry_count {
4150 entries.push(SampleToChunkEntry {
4151 first_chunk: be_u32(src)?,
4152 samples_per_chunk: be_u32(src)?,
4153 _sample_description_index: be_u32(src)?,
4154 })?;
4155 }
4156
4157 Ok(entries)
4158}
4159
4160fn read_stsz<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<u32>> {
4163 let _version = src.read_u8()?;
4164 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4165 let sample_size = be_u32(src)?;
4166 let sample_count = be_u32(src)?;
4167
4168 let mut sizes = TryVec::new();
4169 if sample_size == 0 {
4170 for _ in 0..sample_count {
4172 sizes.push(be_u32(src)?)?;
4173 }
4174 } else {
4175 for _ in 0..sample_count {
4177 sizes.push(sample_size)?;
4178 }
4179 }
4180
4181 Ok(sizes)
4182}
4183
4184fn read_chunk_offsets<T: Read>(src: &mut BMFFBox<'_, T>, is_64bit: bool) -> Result<TryVec<u64>> {
4187 let _version = src.read_u8()?;
4188 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4189 let entry_count = be_u32(src)?;
4190
4191 let mut offsets = TryVec::new();
4192 for _ in 0..entry_count {
4193 let offset = if is_64bit {
4194 be_u64(src)?
4195 } else {
4196 be_u32(src)? as u64
4197 };
4198 offsets.push(offset)?;
4199 }
4200
4201 Ok(offsets)
4202}
4203
4204fn read_stsd<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TrackCodecConfig> {
4210 let _version = src.read_u8()?;
4211 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4212 let entry_count = be_u32(src)?;
4213
4214 let mut config = TrackCodecConfig::default();
4215
4216 let mut iter = src.box_iter();
4218 for _ in 0..entry_count {
4219 let Some(mut entry_box) = iter.next_box()? else {
4220 break;
4221 };
4222
4223 if entry_box.head.name != BoxType::AV1SampleEntry {
4225 skip_box_remain(&mut entry_box)?;
4226 continue;
4227 }
4228
4229 const VISUAL_SAMPLE_ENTRY_SIZE: u64 = 78;
4234 if entry_box.bytes_left() < VISUAL_SAMPLE_ENTRY_SIZE {
4235 skip_box_remain(&mut entry_box)?;
4236 continue;
4237 }
4238 skip(&mut entry_box, VISUAL_SAMPLE_ENTRY_SIZE)?;
4239
4240 let mut sub_iter = entry_box.box_iter();
4242 while let Some(mut sub_box) = sub_iter.next_box()? {
4243 match sub_box.head.name {
4244 BoxType::AV1CodecConfigurationBox => {
4245 config.av1_config = Some(read_av1c(&mut sub_box)?);
4246 }
4247 BoxType::ColorInformationBox => {
4248 if let Ok(colr) = read_colr(&mut sub_box) {
4249 config.color_info = Some(colr);
4250 } else {
4251 skip_box_remain(&mut sub_box)?;
4252 }
4253 }
4254 _ => {
4255 skip_box_remain(&mut sub_box)?;
4256 }
4257 }
4258 }
4259
4260 if config.av1_config.is_some() {
4262 break;
4263 }
4264 }
4265
4266 Ok(config)
4267}
4268
4269fn read_stbl<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<(SampleTable, TrackCodecConfig)> {
4272 let mut time_to_sample = TryVec::new();
4273 let mut sample_to_chunk = TryVec::new();
4274 let mut sample_sizes = TryVec::new();
4275 let mut chunk_offsets = TryVec::new();
4276 let mut codec_config = TrackCodecConfig::default();
4277
4278 let mut iter = src.box_iter();
4279 while let Some(mut b) = iter.next_box()? {
4280 match b.head.name {
4281 BoxType::SampleDescriptionBox => {
4282 codec_config = read_stsd(&mut b)?;
4283 }
4284 BoxType::TimeToSampleBox => {
4285 time_to_sample = read_stts(&mut b)?;
4286 }
4287 BoxType::SampleToChunkBox => {
4288 sample_to_chunk = read_stsc(&mut b)?;
4289 }
4290 BoxType::SampleSizeBox => {
4291 sample_sizes = read_stsz(&mut b)?;
4292 }
4293 BoxType::ChunkOffsetBox => {
4294 chunk_offsets = read_chunk_offsets(&mut b, false)?;
4295 }
4296 BoxType::ChunkLargeOffsetBox => {
4297 chunk_offsets = read_chunk_offsets(&mut b, true)?;
4298 }
4299 _ => {
4300 skip_box_remain(&mut b)?;
4301 }
4302 }
4303 }
4304
4305 let mut sample_offsets = TryVec::new();
4308 let mut sample_idx = 0usize;
4309 for (i, entry) in sample_to_chunk.iter().enumerate() {
4310 let next_first_chunk = sample_to_chunk
4311 .get(i + 1)
4312 .map(|e| e.first_chunk)
4313 .unwrap_or(u32::MAX);
4314
4315 for chunk_no in entry.first_chunk..next_first_chunk {
4316 if chunk_no == 0 {
4317 break;
4318 }
4319 let co_idx = (chunk_no - 1) as usize;
4320 let chunk_offset = match chunk_offsets.get(co_idx) {
4321 Some(&o) => o,
4322 None => break,
4323 };
4324
4325 let mut offset = chunk_offset;
4326 for _ in 0..entry.samples_per_chunk {
4327 if sample_idx >= sample_sizes.len() {
4328 break;
4329 }
4330 sample_offsets.push(offset)?;
4331 offset += *sample_sizes.get(sample_idx)
4332 .ok_or(Error::InvalidData("sample index mismatch"))? as u64;
4333 sample_idx += 1;
4334 }
4335 }
4336 }
4337
4338 Ok((SampleTable {
4339 time_to_sample,
4340 sample_sizes,
4341 sample_offsets,
4342 }, codec_config))
4343}
4344
4345fn read_tkhd<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<u32> {
4348 let version = src.read_u8()?;
4349 let _flags = [src.read_u8()?, src.read_u8()?, src.read_u8()?];
4350
4351 let track_id = if version == 1 {
4352 let _creation_time = be_u64(src)?;
4353 let _modification_time = be_u64(src)?;
4354 let track_id = be_u32(src)?;
4355 let _reserved = be_u32(src)?;
4356 let _duration = be_u64(src)?;
4357 track_id
4358 } else {
4359 let _creation_time = be_u32(src)?;
4360 let _modification_time = be_u32(src)?;
4361 let track_id = be_u32(src)?;
4362 let _reserved = be_u32(src)?;
4363 let _duration = be_u32(src)?;
4364 track_id
4365 };
4366
4367 skip_box_remain(src)?;
4369 Ok(track_id)
4370}
4371
4372fn read_tref<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<TrackReference>> {
4377 let mut refs = TryVec::new();
4378 let mut iter = src.box_iter();
4379 while let Some(mut b) = iter.next_box()? {
4380 let reference_type = FourCC::from(u32::from(b.head.name));
4381 let bytes_left = b.bytes_left();
4382 if bytes_left < 4 || bytes_left % 4 != 0 {
4383 skip_box_remain(&mut b)?;
4384 continue;
4385 }
4386 let count = bytes_left / 4;
4387 let mut track_ids = TryVec::new();
4388 for _ in 0..count {
4389 track_ids.push(be_u32(&mut b)?)?;
4390 }
4391 refs.push(TrackReference { reference_type, track_ids })?;
4392 }
4393 Ok(refs)
4394}
4395
4396fn read_elst<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<u32> {
4401 let (version, flags) = read_fullbox_extra(src)?;
4402
4403 let entry_count = be_u32(src)?;
4404 let entry_size: u64 = if version == 1 { 20 } else { 12 };
4406 skip(src, entry_count as u64 * entry_size)?;
4407 skip_box_remain(src)?;
4408
4409 if flags & 1 != 0 {
4411 Ok(0) } else {
4413 Ok(1) }
4415}
4416
4417fn read_moov<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<ParsedTrack>> {
4420 let mut tracks = TryVec::new();
4421
4422 let mut iter = src.box_iter();
4423 while let Some(mut b) = iter.next_box()? {
4424 match b.head.name {
4425 BoxType::MovieHeaderBox => {
4426 let _mvhd = read_mvhd(&mut b)?;
4427 }
4428 BoxType::TrackBox => {
4429 if let Some(track) = read_trak(&mut b)? {
4430 tracks.push(track)?;
4431 }
4432 }
4433 _ => {
4434 skip_box_remain(&mut b)?;
4435 }
4436 }
4437 }
4438
4439 Ok(tracks)
4440}
4441
4442fn read_trak<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<Option<ParsedTrack>> {
4445 let mut track_id = 0u32;
4446 let mut references = TryVec::new();
4447 let mut loop_count = 1u32; let mut mdia_result: Option<(FourCC, u32, SampleTable, TrackCodecConfig)> = None;
4449
4450 let mut iter = src.box_iter();
4451 while let Some(mut b) = iter.next_box()? {
4452 match b.head.name {
4453 BoxType::TrackHeaderBox => {
4454 track_id = read_tkhd(&mut b)?;
4455 }
4456 BoxType::TrackReferenceBox => {
4457 references = read_tref(&mut b)?;
4458 }
4459 BoxType::EditBox => {
4460 let mut edts_iter = b.box_iter();
4462 while let Some(mut eb) = edts_iter.next_box()? {
4463 if eb.head.name == BoxType::EditListBox {
4464 loop_count = read_elst(&mut eb)?;
4465 } else {
4466 skip_box_remain(&mut eb)?;
4467 }
4468 }
4469 }
4470 BoxType::MediaBox => {
4471 mdia_result = read_mdia(&mut b)?;
4472 }
4473 _ => {
4474 skip_box_remain(&mut b)?;
4475 }
4476 }
4477 }
4478
4479 if let Some((handler_type, media_timescale, sample_table, codec_config)) = mdia_result {
4480 Ok(Some(ParsedTrack {
4481 track_id,
4482 handler_type,
4483 media_timescale,
4484 sample_table,
4485 references,
4486 loop_count,
4487 codec_config,
4488 }))
4489 } else {
4490 Ok(None)
4491 }
4492}
4493
4494fn read_mdia<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<Option<(FourCC, u32, SampleTable, TrackCodecConfig)>> {
4497 let mut media_timescale = 1000; let mut handler_type = FourCC::default();
4499 let mut stbl_result: Option<(SampleTable, TrackCodecConfig)> = None;
4500
4501 let mut iter = src.box_iter();
4502 while let Some(mut b) = iter.next_box()? {
4503 match b.head.name {
4504 BoxType::MediaHeaderBox => {
4505 let mdhd = read_mdhd(&mut b)?;
4506 media_timescale = mdhd.timescale;
4507 }
4508 BoxType::HandlerBox => {
4509 let hdlr = read_hdlr(&mut b)?;
4510 handler_type = hdlr.handler_type;
4511 }
4512 BoxType::MediaInformationBox => {
4513 stbl_result = read_minf(&mut b)?;
4514 }
4515 _ => {
4516 skip_box_remain(&mut b)?;
4517 }
4518 }
4519 }
4520
4521 if let Some((stbl, codec_config)) = stbl_result {
4522 Ok(Some((handler_type, media_timescale, stbl, codec_config)))
4523 } else {
4524 Ok(None)
4525 }
4526}
4527
4528fn associate_tracks(tracks: TryVec<ParsedTrack>) -> Result<ParsedAnimationData> {
4534 let color_idx = tracks
4536 .iter()
4537 .position(|t| t.handler_type == b"pict")
4538 .or_else(|| {
4539 tracks.iter().position(|t| t.handler_type != b"soun")
4541 })
4542 .ok_or(Error::InvalidData("no color track found in moov"))?;
4543
4544 let color_track = tracks.get(color_idx)
4545 .ok_or(Error::InvalidData("color track index out of bounds"))?;
4546 let color_track_id = color_track.track_id;
4547
4548 let alpha_idx = tracks.iter().position(|t| {
4550 t.handler_type == b"auxv"
4551 && t.references.iter().any(|r| {
4552 r.reference_type == b"auxl"
4553 && r.track_ids.iter().any(|&id| id == color_track_id)
4554 })
4555 });
4556
4557 if let Some(ai) = alpha_idx {
4558 let alpha_track = tracks.get(ai)
4559 .ok_or(Error::InvalidData("alpha track index out of bounds"))?;
4560 let color_track = tracks.get(color_idx)
4561 .ok_or(Error::InvalidData("color track index out of bounds"))?;
4562 let alpha_frames = alpha_track.sample_table.sample_sizes.len();
4563 let color_frames = color_track.sample_table.sample_sizes.len();
4564 if alpha_frames != color_frames {
4565 warn!(
4566 "alpha track has {} frames but color track has {} frames",
4567 alpha_frames, color_frames
4568 );
4569 }
4570 }
4571
4572 let mut tracks_vec: std::vec::Vec<ParsedTrack> = tracks.into_iter().collect();
4575
4576 let (color_track, alpha_track) = if let Some(ai) = alpha_idx {
4578 if ai > color_idx {
4579 let alpha = tracks_vec.remove(ai);
4580 let color = tracks_vec.remove(color_idx);
4581 (color, Some(alpha))
4582 } else {
4583 let color = tracks_vec.remove(color_idx);
4584 let alpha = tracks_vec.remove(ai);
4585 (color, Some(alpha))
4586 }
4587 } else {
4588 let color = tracks_vec.remove(color_idx);
4589 (color, None)
4590 };
4591
4592 let (alpha_timescale, alpha_sample_table) = match alpha_track {
4593 Some(t) => (Some(t.media_timescale), Some(t.sample_table)),
4594 None => (None, None),
4595 };
4596
4597 Ok(ParsedAnimationData {
4598 color_timescale: color_track.media_timescale,
4599 color_codec_config: color_track.codec_config,
4600 color_sample_table: color_track.sample_table,
4601 alpha_timescale,
4602 alpha_sample_table,
4603 loop_count: color_track.loop_count,
4604 })
4605}
4606
4607fn read_minf<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<Option<(SampleTable, TrackCodecConfig)>> {
4609 let mut iter = src.box_iter();
4610 while let Some(mut b) = iter.next_box()? {
4611 if b.head.name == BoxType::SampleTableBox {
4612 return Ok(Some(read_stbl(&mut b)?));
4613 } else {
4614 skip_box_remain(&mut b)?;
4615 }
4616 }
4617 Ok(None)
4618}
4619
4620#[cfg(feature = "eager")]
4622#[allow(deprecated)]
4623fn extract_animation_frames(
4624 sample_table: &SampleTable,
4625 media_timescale: u32,
4626 mdats: &mut [MediaDataBox],
4627) -> Result<TryVec<AnimationFrame>> {
4628 let mut frames = TryVec::new();
4629
4630 let mut frame_durations = TryVec::new();
4632 for entry in &sample_table.time_to_sample {
4633 for _ in 0..entry.sample_count {
4634 let duration_ms = if media_timescale > 0 {
4635 ((entry.sample_delta as u64) * 1000) / (media_timescale as u64)
4636 } else {
4637 0
4638 };
4639 frame_durations.push(duration_ms as u32)?;
4640 }
4641 }
4642
4643 for i in 0..sample_table.sample_sizes.len() {
4645 let sample_offset = *sample_table.sample_offsets.get(i)
4646 .ok_or(Error::InvalidData("sample offset index out of bounds"))?;
4647 let sample_size = *sample_table.sample_sizes.get(i)
4648 .ok_or(Error::InvalidData("sample size index out of bounds"))?;
4649 let duration_ms = frame_durations.get(i).copied().unwrap_or(0);
4650
4651 let mut frame_data = TryVec::new();
4652 let mut found = false;
4653
4654 for mdat in mdats.iter_mut() {
4655 let range = ExtentRange::WithLength(Range {
4656 start: sample_offset,
4657 end: sample_offset + sample_size as u64,
4658 });
4659
4660 if mdat.contains_extent(&range) {
4661 mdat.read_extent(&range, &mut frame_data)?;
4662 found = true;
4663 break;
4664 }
4665 }
4666
4667 if !found {
4668 log::warn!("Animation frame {} not found in mdat", i);
4669 }
4670
4671 frames.push(AnimationFrame {
4672 data: frame_data,
4673 duration_ms,
4674 })?;
4675 }
4676
4677 Ok(frames)
4678}
4679
4680fn read_grid<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<GridConfig> {
4683 let version = read_fullbox_version_no_flags(src, options)?;
4684 if version > 0 {
4685 return Err(Error::Unsupported("grid version > 0"));
4686 }
4687
4688 let flags_byte = src.read_u8()?;
4689 let rows = src.read_u8()?;
4690 let columns = src.read_u8()?;
4691
4692 let (output_width, output_height) = if flags_byte & 1 == 0 {
4694 (u32::from(be_u16(src)?), u32::from(be_u16(src)?))
4696 } else {
4697 (be_u32(src)?, be_u32(src)?)
4699 };
4700
4701 Ok(GridConfig {
4702 rows,
4703 columns,
4704 output_width,
4705 output_height,
4706 })
4707}
4708
4709fn read_iloc<T: Read>(src: &mut BMFFBox<'_, T>, options: &ParseOptions) -> Result<TryVec<ItemLocationBoxItem>> {
4712 let version: IlocVersion = read_fullbox_version_no_flags(src, options)?.try_into()?;
4713
4714 let iloc = src.read_into_try_vec()?;
4715 let mut iloc = BitReader::new(&iloc);
4716
4717 let offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
4718 let length_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
4719 let base_offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
4720
4721 let index_size: Option<IlocFieldSize> = match version {
4722 IlocVersion::One | IlocVersion::Two => Some(iloc.read_u8(4)?.try_into()?),
4723 IlocVersion::Zero => {
4724 let _reserved = iloc.read_u8(4)?;
4725 None
4726 },
4727 };
4728
4729 let item_count = match version {
4730 IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?,
4731 IlocVersion::Two => iloc.read_u32(32)?,
4732 };
4733
4734 let mut items = TryVec::with_capacity(item_count.to_usize())?;
4735
4736 for _ in 0..item_count {
4737 let item_id = match version {
4738 IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?,
4739 IlocVersion::Two => iloc.read_u32(32)?,
4740 };
4741
4742 let construction_method = match version {
4748 IlocVersion::Zero => ConstructionMethod::File,
4749 IlocVersion::One | IlocVersion::Two => {
4750 let _reserved = iloc.read_u16(12)?;
4751 match iloc.read_u16(4)? {
4752 0 => ConstructionMethod::File,
4753 1 => ConstructionMethod::Idat,
4754 2 => return Err(Error::Unsupported("construction_method 'item_offset' is not supported")),
4755 _ => return Err(Error::InvalidData("construction_method is taken from the set 0, 1 or 2 per ISO 14496-12:2015 § 8.11.3.3")),
4756 }
4757 },
4758 };
4759
4760 let data_reference_index = iloc.read_u16(16)?;
4761
4762 if data_reference_index != 0 {
4763 return Err(Error::Unsupported("external file references (iloc.data_reference_index != 0) are not supported"));
4764 }
4765
4766 let base_offset = iloc.read_u64(base_offset_size.to_bits())?;
4767 let extent_count = iloc.read_u16(16)?;
4768
4769 if extent_count < 1 {
4770 return Err(Error::InvalidData("extent_count must have a value 1 or greater per ISO 14496-12:2015 § 8.11.3.3"));
4771 }
4772
4773 let mut extents = TryVec::with_capacity(extent_count.to_usize())?;
4774
4775 for _ in 0..extent_count {
4776 let _extent_index = match &index_size {
4778 None | Some(IlocFieldSize::Zero) => None,
4779 Some(index_size) => {
4780 debug_assert!(version == IlocVersion::One || version == IlocVersion::Two);
4781 Some(iloc.read_u64(index_size.to_bits())?)
4782 },
4783 };
4784
4785 let extent_offset = iloc.read_u64(offset_size.to_bits())?;
4790 let extent_length = iloc.read_u64(length_size.to_bits())?;
4791
4792 let start = base_offset
4795 .checked_add(extent_offset)
4796 .ok_or(Error::InvalidData("offset calculation overflow"))?;
4797 let extent_range = if extent_length == 0 {
4798 ExtentRange::ToEnd(RangeFrom { start })
4799 } else {
4800 let end = start
4801 .checked_add(extent_length)
4802 .ok_or(Error::InvalidData("end calculation overflow"))?;
4803 ExtentRange::WithLength(Range { start, end })
4804 };
4805
4806 extents.push(ItemLocationBoxExtent { extent_range })?;
4807 }
4808
4809 items.push(ItemLocationBoxItem { item_id, construction_method, extents })?;
4810 }
4811
4812 if iloc.remaining() == 0 {
4813 Ok(items)
4814 } else {
4815 Err(Error::InvalidData("invalid iloc size"))
4816 }
4817}
4818
4819fn read_ftyp<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<FileTypeBox> {
4822 let major = be_u32(src)?;
4823 let minor = be_u32(src)?;
4824 let bytes_left = src.bytes_left();
4825 if !bytes_left.is_multiple_of(4) {
4826 return Err(Error::InvalidData("invalid ftyp size"));
4827 }
4828 let brand_count = bytes_left / 4;
4830 let mut brands = TryVec::with_capacity(brand_count.try_into()?)?;
4831 for _ in 0..brand_count {
4832 brands.push(be_u32(src)?.into())?;
4833 }
4834 Ok(FileTypeBox {
4835 major_brand: From::from(major),
4836 minor_version: minor,
4837 compatible_brands: brands,
4838 })
4839}
4840
4841#[cfg_attr(debug_assertions, track_caller)]
4842fn check_parser_state<T>(header: &BoxHeader, left: &Take<T>) -> Result<(), Error> {
4843 let limit = left.limit();
4844 if limit == 0 || header.size == u64::MAX {
4846 Ok(())
4847 } else {
4848 debug_assert_eq!(0, limit, "bad parser state bytes left");
4849 Err(Error::InvalidData("unread box content or bad parser sync"))
4850 }
4851}
4852
4853fn skip<T: Read>(src: &mut T, bytes: u64) -> Result<()> {
4855 std::io::copy(&mut src.take(bytes), &mut std::io::sink())?;
4856 Ok(())
4857}
4858
4859fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> {
4860 src.read_u16::<byteorder::BigEndian>().map_err(From::from)
4861}
4862
4863fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
4864 src.read_u32::<byteorder::BigEndian>().map_err(From::from)
4865}
4866
4867fn be_i32<T: ReadBytesExt>(src: &mut T) -> Result<i32> {
4868 src.read_i32::<byteorder::BigEndian>().map_err(From::from)
4869}
4870
4871fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
4872 src.read_u64::<byteorder::BigEndian>().map_err(From::from)
4873}