1#![allow(clippy::missing_safety_doc)]
2use arrayvec::ArrayVec;
9use log::{debug, warn};
10
11use bitreader::BitReader;
12use byteorder::ReadBytesExt;
13use fallible_collections::{TryClone, TryReserveError};
14use std::convert::{TryFrom, TryInto as _};
15
16use std::io::{Read, Take};
17use std::num::NonZeroU32;
18use std::ops::{Range, RangeFrom};
19
20mod obu;
21
22mod boxes;
23use crate::boxes::{BoxType, FourCC};
24
25#[cfg(feature = "c_api")]
27pub mod c_api;
28
29trait ToU64 {
35 fn to_u64(self) -> u64;
36}
37
38impl ToU64 for usize {
42 fn to_u64(self) -> u64 {
43 const _: () = assert!(std::mem::size_of::<usize>() <= std::mem::size_of::<u64>());
44 self.try_into().ok().unwrap()
45 }
46}
47
48pub(crate) trait ToUsize {
51 fn to_usize(self) -> usize;
52}
53
54macro_rules! impl_to_usize_from {
58 ( $from_type:ty ) => {
59 impl ToUsize for $from_type {
60 fn to_usize(self) -> usize {
61 const _: () = assert!(std::mem::size_of::<$from_type>() <= std::mem::size_of::<usize>());
62 self.try_into().ok().unwrap()
63 }
64 }
65 };
66}
67
68impl_to_usize_from!(u8);
69impl_to_usize_from!(u16);
70impl_to_usize_from!(u32);
71
72trait Offset {
74 fn offset(&self) -> u64;
75}
76
77struct OffsetReader<'a, T> {
79 reader: &'a mut T,
80 offset: u64,
81}
82
83impl<'a, T> OffsetReader<'a, T> {
84 fn new(reader: &'a mut T) -> Self {
85 Self { reader, offset: 0 }
86 }
87}
88
89impl<T> Offset for OffsetReader<'_, T> {
90 fn offset(&self) -> u64 {
91 self.offset
92 }
93}
94
95impl<T: Read> Read for OffsetReader<'_, T> {
96 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
97 let bytes_read = self.reader.read(buf)?;
98 self.offset = self
99 .offset
100 .checked_add(bytes_read.to_u64())
101 .ok_or(Error::Unsupported("total bytes read too large for offset type"))?;
102 Ok(bytes_read)
103 }
104}
105
106pub(crate) type TryVec<T> = fallible_collections::TryVec<T>;
107pub(crate) type TryString = fallible_collections::TryVec<u8>;
108
109#[allow(dead_code)]
111struct Vec;
112#[allow(dead_code)]
113struct Box;
114#[allow(dead_code)]
115struct HashMap;
116#[allow(dead_code)]
117struct String;
118
119#[derive(Debug)]
124pub enum Error {
125 InvalidData(&'static str),
127 Unsupported(&'static str),
129 UnexpectedEOF,
131 Io(std::io::Error),
133 NoMoov,
135 OutOfMemory,
137}
138
139impl std::fmt::Display for Error {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 let msg = match self {
142 Self::InvalidData(s) | Self::Unsupported(s) => s,
143 Self::UnexpectedEOF => "EOF",
144 Self::Io(err) => return err.fmt(f),
145 Self::NoMoov => "Missing Moov box",
146 Self::OutOfMemory => "OOM",
147 };
148 f.write_str(msg)
149 }
150}
151
152impl std::error::Error for Error {}
153
154impl From<bitreader::BitReaderError> for Error {
155 #[cold]
156 #[cfg_attr(debug_assertions, track_caller)]
157 fn from(err: bitreader::BitReaderError) -> Self {
158 log::warn!("bitreader: {err}");
159 debug_assert!(!matches!(err, bitreader::BitReaderError::TooManyBitsForType { .. })); Self::InvalidData("truncated bits")
161 }
162}
163
164impl From<std::io::Error> for Error {
165 fn from(err: std::io::Error) -> Self {
166 match err.kind() {
167 std::io::ErrorKind::UnexpectedEof => Self::UnexpectedEOF,
168 _ => Self::Io(err),
169 }
170 }
171}
172
173impl From<std::string::FromUtf8Error> for Error {
174 fn from(_: std::string::FromUtf8Error) -> Self {
175 Self::InvalidData("invalid utf8")
176 }
177}
178
179impl From<std::num::TryFromIntError> for Error {
180 fn from(_: std::num::TryFromIntError) -> Self {
181 Self::Unsupported("integer conversion failed")
182 }
183}
184
185impl From<Error> for std::io::Error {
186 fn from(err: Error) -> Self {
187 let kind = match err {
188 Error::InvalidData(_) => std::io::ErrorKind::InvalidData,
189 Error::UnexpectedEOF => std::io::ErrorKind::UnexpectedEof,
190 Error::Io(io_err) => return io_err,
191 _ => std::io::ErrorKind::Other,
192 };
193 Self::new(kind, err)
194 }
195}
196
197impl From<TryReserveError> for Error {
198 fn from(_: TryReserveError) -> Self {
199 Self::OutOfMemory
200 }
201}
202
203pub type Result<T, E = Error> = std::result::Result<T, E>;
205
206#[derive(Debug, Clone, Copy)]
215struct BoxHeader {
216 name: BoxType,
218 size: u64,
220 offset: u64,
222 #[allow(unused)]
224 uuid: Option<[u8; 16]>,
225}
226
227impl BoxHeader {
228 const MIN_SIZE: u64 = 8;
230 const MIN_LARGE_SIZE: u64 = 16;
232}
233
234#[derive(Debug)]
236#[allow(unused)]
237struct FileTypeBox {
238 major_brand: FourCC,
239 minor_version: u32,
240 compatible_brands: TryVec<FourCC>,
241}
242
243#[derive(Debug)]
245#[allow(unused)]
246struct HandlerBox {
247 handler_type: FourCC,
248}
249
250#[derive(Debug)]
251#[allow(unused)]
252pub(crate) struct AV1ConfigBox {
253 pub(crate) profile: u8,
254 pub(crate) level: u8,
255 pub(crate) tier: u8,
256 pub(crate) bit_depth: u8,
257 pub(crate) monochrome: bool,
258 pub(crate) chroma_subsampling_x: u8,
259 pub(crate) chroma_subsampling_y: u8,
260 pub(crate) chroma_sample_position: u8,
261 pub(crate) initial_presentation_delay_present: bool,
262 pub(crate) initial_presentation_delay_minus_one: u8,
263 pub(crate) config_obus: TryVec<u8>,
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271pub struct ContentLightLevel {
272 pub max_content_light_level: u16,
274 pub max_pic_average_light_level: u16,
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq)]
283pub struct MasteringDisplayColourVolume {
284 pub primaries: [(u16, u16); 3],
288 pub white_point: (u16, u16),
290 pub max_luminance: u32,
293 pub min_luminance: u32,
296}
297
298#[derive(Debug, Default)]
299#[non_exhaustive]
300pub struct AvifData {
301 pub primary_item: TryVec<u8>,
305 pub alpha_item: Option<TryVec<u8>>,
309 pub premultiplied_alpha: bool,
313 pub content_light_level: Option<ContentLightLevel>,
315 pub mastering_display: Option<MasteringDisplayColourVolume>,
317}
318
319impl AvifData {
320 pub fn from_reader<R: Read>(reader: &mut R) -> Result<Self> {
321 read_avif(reader)
322 }
323
324 pub fn primary_item_metadata(&self) -> Result<AV1Metadata> {
326 AV1Metadata::parse_av1_bitstream(&self.primary_item)
327 }
328
329 pub fn alpha_item_metadata(&self) -> Result<Option<AV1Metadata>> {
331 self.alpha_item.as_deref().map(AV1Metadata::parse_av1_bitstream).transpose()
332 }
333}
334
335#[non_exhaustive]
337#[derive(Debug, Clone)]
338pub struct AV1Metadata {
339 pub still_picture: bool,
341 pub max_frame_width: NonZeroU32,
342 pub max_frame_height: NonZeroU32,
343 pub bit_depth: u8,
345 pub seq_profile: u8,
347 pub chroma_subsampling: (bool, bool),
349 pub monochrome: bool,
350}
351
352impl AV1Metadata {
353 #[inline(never)]
358 pub fn parse_av1_bitstream(obu_bitstream: &[u8]) -> Result<Self> {
359 let h = obu::parse_obu(obu_bitstream)?;
360 Ok(Self {
361 still_picture: h.still_picture,
362 max_frame_width: h.max_frame_width,
363 max_frame_height: h.max_frame_height,
364 bit_depth: h.color.bit_depth,
365 seq_profile: h.seq_profile,
366 chroma_subsampling: h.color.chroma_subsampling,
367 monochrome: h.color.monochrome,
368 })
369 }
370}
371
372struct AvifInternalMeta {
373 item_references: TryVec<SingleItemTypeReferenceBox>,
374 properties: TryVec<AssociatedProperty>,
375 primary_item_id: u32,
376 iloc_items: TryVec<ItemLocationBoxItem>,
377}
378
379struct MediaDataBox {
382 offset: u64,
384 data: TryVec<u8>,
385}
386
387impl MediaDataBox {
388 fn contains_extent(&self, extent: &ExtentRange) -> bool {
392 if self.offset <= extent.start() {
393 let start_offset = extent.start() - self.offset;
394 start_offset < self.data.len().to_u64()
395 } else {
396 false
397 }
398 }
399
400 fn matches_extent(&self, extent: &ExtentRange) -> bool {
402 if self.offset == extent.start() {
403 match extent {
404 ExtentRange::WithLength(range) => {
405 if let Some(end) = self.offset.checked_add(self.data.len().to_u64()) {
406 end == range.end
407 } else {
408 false
409 }
410 },
411 ExtentRange::ToEnd(_) => true,
412 }
413 } else {
414 false
415 }
416 }
417
418 fn read_extent(&self, extent: &ExtentRange, buf: &mut TryVec<u8>) -> Result<()> {
421 let start_offset = extent
422 .start()
423 .checked_sub(self.offset)
424 .ok_or(Error::InvalidData("mdat does not contain extent"))?;
425 let slice = match extent {
426 ExtentRange::WithLength(range) => {
427 let range_len = range
428 .end
429 .checked_sub(range.start)
430 .ok_or(Error::InvalidData("range start > end"))?;
431 let end = start_offset
432 .checked_add(range_len)
433 .ok_or(Error::InvalidData("extent end overflow"))?;
434 self.data.get(start_offset.try_into()?..end.try_into()?)
435 },
436 ExtentRange::ToEnd(_) => self.data.get(start_offset.try_into()?..),
437 };
438 let slice = slice.ok_or(Error::InvalidData("extent crosses box boundary"))?;
439 buf.extend_from_slice(slice)?;
440 Ok(())
441 }
442}
443
444#[derive(Debug)]
448struct ItemInfoEntry {
449 item_id: u32,
450 item_type: FourCC,
451}
452
453#[derive(Debug)]
455struct SingleItemTypeReferenceBox {
456 item_type: FourCC,
457 from_item_id: u32,
458 to_item_id: u32,
459}
460
461#[derive(Debug)]
464enum IlocFieldSize {
465 Zero,
466 Four,
467 Eight,
468}
469
470impl IlocFieldSize {
471 const fn to_bits(&self) -> u8 {
472 match self {
473 Self::Zero => 0,
474 Self::Four => 32,
475 Self::Eight => 64,
476 }
477 }
478}
479
480impl TryFrom<u8> for IlocFieldSize {
481 type Error = Error;
482
483 fn try_from(value: u8) -> Result<Self> {
484 match value {
485 0 => Ok(Self::Zero),
486 4 => Ok(Self::Four),
487 8 => Ok(Self::Eight),
488 _ => Err(Error::InvalidData("value must be in the set {0, 4, 8}")),
489 }
490 }
491}
492
493#[derive(PartialEq)]
494enum IlocVersion {
495 Zero,
496 One,
497 Two,
498}
499
500impl TryFrom<u8> for IlocVersion {
501 type Error = Error;
502
503 fn try_from(value: u8) -> Result<Self> {
504 match value {
505 0 => Ok(Self::Zero),
506 1 => Ok(Self::One),
507 2 => Ok(Self::Two),
508 _ => Err(Error::Unsupported("unsupported version in 'iloc' box")),
509 }
510 }
511}
512
513#[derive(Debug)]
518struct ItemLocationBoxItem {
519 item_id: u32,
520 construction_method: ConstructionMethod,
521 extents: TryVec<ItemLocationBoxExtent>,
523}
524
525#[derive(Clone, Copy, Debug, PartialEq)]
526enum ConstructionMethod {
527 File,
528 Idat,
529 #[allow(dead_code)] Item,
531}
532
533#[derive(Clone, Debug)]
536struct ItemLocationBoxExtent {
537 extent_range: ExtentRange,
538}
539
540#[derive(Clone, Debug)]
541enum ExtentRange {
542 WithLength(Range<u64>),
543 ToEnd(RangeFrom<u64>),
544}
545
546impl ExtentRange {
547 const fn start(&self) -> u64 {
548 match self {
549 Self::WithLength(r) => r.start,
550 Self::ToEnd(r) => r.start,
551 }
552 }
553}
554
555struct BMFFBox<'a, T> {
557 head: BoxHeader,
558 content: Take<&'a mut T>,
559}
560
561impl<T: Read> BMFFBox<'_, T> {
562 fn read_into_try_vec(&mut self) -> std::io::Result<TryVec<u8>> {
563 let mut vec = std::vec::Vec::new();
564 vec.try_reserve_exact(self.content.limit() as usize)
565 .map_err(|_| std::io::ErrorKind::OutOfMemory)?;
566 self.content.read_to_end(&mut vec)?; Ok(vec.into())
568 }
569}
570
571#[test]
572fn box_read_to_end() {
573 let tmp = &mut b"1234567890".as_slice();
574 let mut src = BMFFBox {
575 head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None },
576 content: <_ as Read>::take(tmp, 5),
577 };
578 let buf = src.read_into_try_vec().unwrap();
579 assert_eq!(buf.len(), 5);
580 assert_eq!(buf, b"12345".as_ref());
581}
582
583#[test]
584fn box_read_to_end_oom() {
585 let tmp = &mut b"1234567890".as_slice();
586 let mut src = BMFFBox {
587 head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None },
588 content: <_ as Read>::take(tmp, usize::MAX.try_into().expect("usize < u64")),
589 };
590 assert!(src.read_into_try_vec().is_err());
591}
592
593struct BoxIter<'a, T> {
594 src: &'a mut T,
595}
596
597impl<T: Read> BoxIter<'_, T> {
598 fn new(src: &mut T) -> BoxIter<'_, T> {
599 BoxIter { src }
600 }
601
602 fn next_box(&mut self) -> Result<Option<BMFFBox<'_, T>>> {
603 let r = read_box_header(self.src);
604 match r {
605 Ok(h) => Ok(Some(BMFFBox {
606 head: h,
607 content: self.src.take(h.size - h.offset),
608 })),
609 Err(Error::UnexpectedEOF) => Ok(None),
610 Err(e) => Err(e),
611 }
612 }
613}
614
615impl<T: Read> Read for BMFFBox<'_, T> {
616 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
617 self.content.read(buf)
618 }
619}
620
621impl<T: Offset> Offset for BMFFBox<'_, T> {
622 fn offset(&self) -> u64 {
623 self.content.get_ref().offset()
624 }
625}
626
627impl<T: Read> BMFFBox<'_, T> {
628 fn bytes_left(&self) -> u64 {
629 self.content.limit()
630 }
631
632 const fn get_header(&self) -> &BoxHeader {
633 &self.head
634 }
635
636 fn box_iter(&mut self) -> BoxIter<'_, Self> {
637 BoxIter::new(self)
638 }
639}
640
641impl<T> Drop for BMFFBox<'_, T> {
642 fn drop(&mut self) {
643 if self.content.limit() > 0 {
644 let name: FourCC = From::from(self.head.name);
645 debug!("Dropping {} bytes in '{}'", self.content.limit(), name);
646 }
647 }
648}
649
650fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
659 let size32 = be_u32(src)?;
660 let name = BoxType::from(be_u32(src)?);
661 let size = match size32 {
662 0 => return Err(Error::Unsupported("unknown sized box")),
664 1 => {
665 let size64 = be_u64(src)?;
666 if size64 < BoxHeader::MIN_LARGE_SIZE {
667 return Err(Error::InvalidData("malformed wide size"));
668 }
669 size64
670 },
671 _ => {
672 if u64::from(size32) < BoxHeader::MIN_SIZE {
673 return Err(Error::InvalidData("malformed size"));
674 }
675 u64::from(size32)
676 },
677 };
678 let mut offset = match size32 {
679 1 => BoxHeader::MIN_LARGE_SIZE,
680 _ => BoxHeader::MIN_SIZE,
681 };
682 let uuid = if name == BoxType::UuidBox {
683 if size >= offset + 16 {
684 let mut buffer = [0u8; 16];
685 let count = src.read(&mut buffer)?;
686 offset += count.to_u64();
687 if count == 16 {
688 Some(buffer)
689 } else {
690 debug!("malformed uuid (short read), skipping");
691 None
692 }
693 } else {
694 debug!("malformed uuid, skipping");
695 None
696 }
697 } else {
698 None
699 };
700 assert!(offset <= size);
701 Ok(BoxHeader { name, size, offset, uuid })
702}
703
704fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> {
706 let version = src.read_u8()?;
707 let flags_a = src.read_u8()?;
708 let flags_b = src.read_u8()?;
709 let flags_c = src.read_u8()?;
710 Ok((
711 version,
712 u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c),
713 ))
714}
715
716fn read_fullbox_version_no_flags<T: ReadBytesExt>(src: &mut T) -> Result<u8> {
718 let (version, flags) = read_fullbox_extra(src)?;
719
720 if flags != 0 {
721 return Err(Error::Unsupported("expected flags to be 0"));
722 }
723
724 Ok(version)
725}
726
727fn skip_box_content<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<()> {
729 let to_skip = {
731 let header = src.get_header();
732 debug!("{header:?} (skipped)");
733 header
734 .size
735 .checked_sub(header.offset)
736 .ok_or(Error::InvalidData("header offset > size"))?
737 };
738 assert_eq!(to_skip, src.bytes_left());
739 skip(src, to_skip)
740}
741
742fn skip_box_remain<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<()> {
744 let remain = {
745 let header = src.get_header();
746 let len = src.bytes_left();
747 debug!("remain {len} (skipped) in {header:?}");
748 len
749 };
750 skip(src, remain)
751}
752
753pub fn read_avif<T: Read>(f: &mut T) -> Result<AvifData> {
757 let mut f = OffsetReader::new(f);
758
759 let mut iter = BoxIter::new(&mut f);
760
761 if let Some(mut b) = iter.next_box()? {
763 if b.head.name == BoxType::FileTypeBox {
764 let ftyp = read_ftyp(&mut b)?;
765 if ftyp.major_brand != b"avif" {
766 if ftyp.major_brand == b"avis" {
767 return Err(Error::Unsupported("Animated AVIF is not supported. Please use real AV1 videos instead."));
768 }
769 warn!("major_brand: {}", ftyp.major_brand);
770 return Err(Error::InvalidData("ftyp must be 'avif'"));
771 }
772 } else {
773 return Err(Error::InvalidData("'ftyp' box must occur first"));
774 }
775 }
776
777 let mut meta = None;
778 let mut mdats = TryVec::new();
779
780 while let Some(mut b) = iter.next_box()? {
781 match b.head.name {
782 BoxType::MetadataBox => {
783 if meta.is_some() {
784 return Err(Error::InvalidData("There should be zero or one meta boxes per ISO 14496-12:2015 § 8.11.1.1"));
785 }
786 meta = Some(read_avif_meta(&mut b)?);
787 },
788 BoxType::MediaDataBox => {
789 if b.bytes_left() > 0 {
790 let offset = b.offset();
791 let data = b.read_into_try_vec()?;
792 mdats.push(MediaDataBox { offset, data })?;
793 }
794 },
795 _ => skip_box_content(&mut b)?,
796 }
797
798 check_parser_state(&b.content)?;
799 }
800
801 let meta = meta.ok_or(Error::InvalidData("missing meta"))?;
802
803 let alpha_item_id = meta
804 .item_references
805 .iter()
806 .filter(|iref| {
808 iref.to_item_id == meta.primary_item_id
809 && iref.from_item_id != meta.primary_item_id
810 && iref.item_type == b"auxl"
811 })
812 .map(|iref| iref.from_item_id)
813 .find(|&item_id| {
815 meta.properties.iter().any(|prop| {
816 prop.item_id == item_id
817 && match &prop.property {
818 ItemProperty::AuxiliaryType(urn) => {
819 urn.type_subtype().0 == b"urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
820 }
821 _ => false,
822 }
823 })
824 });
825
826 let mut content_light_level = None;
828 let mut mastering_display = None;
829 for prop in meta.properties.iter() {
830 if prop.item_id == meta.primary_item_id {
831 match &prop.property {
832 ItemProperty::ContentLightLevel(cll) => content_light_level = Some(*cll),
833 ItemProperty::MasteringDisplayColourVolume(mdcv) => mastering_display = Some(*mdcv),
834 _ => {},
835 }
836 }
837 }
838
839 let mut context = AvifData {
840 premultiplied_alpha: alpha_item_id.is_some_and(|alpha_item_id| {
841 meta.item_references.iter().any(|iref| {
842 iref.from_item_id == meta.primary_item_id
843 && iref.to_item_id == alpha_item_id
844 && iref.item_type == b"prem"
845 })
846 }),
847 content_light_level,
848 mastering_display,
849 ..Default::default()
850 };
851
852 for loc in meta.iloc_items.iter() {
854 let item_data = if loc.item_id == meta.primary_item_id {
855 &mut context.primary_item
856 } else if Some(loc.item_id) == alpha_item_id {
857 context.alpha_item.get_or_insert_with(TryVec::new)
858 } else {
859 continue;
860 };
861
862 if loc.construction_method != ConstructionMethod::File {
863 return Err(Error::Unsupported("unsupported construction_method"));
864 }
865 for extent in loc.extents.iter() {
866 let mut found = false;
867 for mdat in mdats.iter_mut() {
869 if mdat.matches_extent(&extent.extent_range) {
870 item_data.append(&mut mdat.data)?;
871 found = true;
872 break;
873 } else if mdat.contains_extent(&extent.extent_range) {
874 mdat.read_extent(&extent.extent_range, item_data)?;
875 found = true;
876 break;
877 }
878 }
879 if !found {
880 return Err(Error::InvalidData("iloc contains an extent that is not in mdat"));
881 }
882 }
883 }
884
885 Ok(context)
886}
887
888fn read_avif_meta<T: Read + Offset>(src: &mut BMFFBox<'_, T>) -> Result<AvifInternalMeta> {
893 let version = read_fullbox_version_no_flags(src)?;
894
895 if version != 0 {
896 return Err(Error::Unsupported("unsupported meta version"));
897 }
898
899 let mut primary_item_id = None;
900 let mut item_infos = None;
901 let mut iloc_items = None;
902 let mut item_references = TryVec::new();
903 let mut properties = TryVec::new();
904
905 let mut iter = src.box_iter();
906 while let Some(mut b) = iter.next_box()? {
907 match b.head.name {
908 BoxType::ItemInfoBox => {
909 if item_infos.is_some() {
910 return Err(Error::InvalidData("There should be zero or one iinf boxes per ISO 14496-12:2015 § 8.11.6.1"));
911 }
912 item_infos = Some(read_iinf(&mut b)?);
913 },
914 BoxType::ItemLocationBox => {
915 if iloc_items.is_some() {
916 return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.3.1"));
917 }
918 iloc_items = Some(read_iloc(&mut b)?);
919 },
920 BoxType::PrimaryItemBox => {
921 if primary_item_id.is_some() {
922 return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.4.1"));
923 }
924 primary_item_id = Some(read_pitm(&mut b)?);
925 },
926 BoxType::ImageReferenceBox => {
927 item_references.append(&mut read_iref(&mut b)?)?;
928 },
929 BoxType::ImagePropertiesBox => {
930 properties = read_iprp(&mut b)?;
931 },
932 _ => skip_box_content(&mut b)?,
933 }
934
935 check_parser_state(&b.content)?;
936 }
937
938 let primary_item_id = primary_item_id.ok_or(Error::InvalidData("Required pitm box not present in meta box"))?;
939
940 let item_infos = item_infos.ok_or(Error::InvalidData("iinf missing"))?;
941
942 if let Some(item_info) = item_infos.iter().find(|x| x.item_id == primary_item_id) {
943 if item_info.item_type != b"av01" {
944 if item_info.item_type == b"grid" {
945 return Err(Error::Unsupported("Grid-based AVIF collage is not supported"));
946 }
947 warn!("primary_item_id type: {}", item_info.item_type);
948 return Err(Error::InvalidData("primary_item_id type is not av01"));
949 }
950 } else {
951 return Err(Error::InvalidData("primary_item_id not present in iinf box"));
952 }
953
954 Ok(AvifInternalMeta {
955 properties,
956 item_references,
957 primary_item_id,
958 iloc_items: iloc_items.ok_or(Error::InvalidData("iloc missing"))?,
959 })
960}
961
962fn read_pitm<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<u32> {
965 let version = read_fullbox_version_no_flags(src)?;
966
967 let item_id = match version {
968 0 => be_u16(src)?.into(),
969 1 => be_u32(src)?,
970 _ => return Err(Error::Unsupported("unsupported pitm version")),
971 };
972
973 Ok(item_id)
974}
975
976fn read_iinf<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<ItemInfoEntry>> {
979 let version = read_fullbox_version_no_flags(src)?;
980
981 match version {
982 0 | 1 => (),
983 _ => return Err(Error::Unsupported("unsupported iinf version")),
984 }
985
986 let entry_count = if version == 0 {
987 be_u16(src)?.to_usize()
988 } else {
989 be_u32(src)?.to_usize()
990 };
991 let mut item_infos = TryVec::with_capacity(entry_count)?;
992
993 let mut iter = src.box_iter();
994 while let Some(mut b) = iter.next_box()? {
995 if b.head.name != BoxType::ItemInfoEntry {
996 return Err(Error::InvalidData("iinf box should contain only infe boxes"));
997 }
998
999 item_infos.push(read_infe(&mut b)?)?;
1000
1001 check_parser_state(&b.content)?;
1002 }
1003
1004 Ok(item_infos)
1005}
1006
1007fn read_infe<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ItemInfoEntry> {
1010 let (version, _) = read_fullbox_extra(src)?;
1013
1014 let item_id = match version {
1016 2 => be_u16(src)?.into(),
1017 3 => be_u32(src)?,
1018 _ => return Err(Error::Unsupported("unsupported version in 'infe' box")),
1019 };
1020
1021 let item_protection_index = be_u16(src)?;
1022
1023 if item_protection_index != 0 {
1024 return Err(Error::Unsupported("protected items (infe.item_protection_index != 0) are not supported"));
1025 }
1026
1027 let item_type = FourCC::from(be_u32(src)?);
1028 debug!("infe item_id {item_id} item_type: {item_type}");
1029
1030 skip_box_remain(src)?;
1032
1033 Ok(ItemInfoEntry { item_id, item_type })
1034}
1035
1036fn read_iref<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<SingleItemTypeReferenceBox>> {
1037 let mut item_references = TryVec::new();
1038 let version = read_fullbox_version_no_flags(src)?;
1039 if version > 1 {
1040 return Err(Error::Unsupported("iref version"));
1041 }
1042
1043 let mut iter = src.box_iter();
1044 while let Some(mut b) = iter.next_box()? {
1045 let from_item_id = if version == 0 {
1046 be_u16(&mut b)?.into()
1047 } else {
1048 be_u32(&mut b)?
1049 };
1050 let reference_count = be_u16(&mut b)?;
1051 for _ in 0..reference_count {
1052 let to_item_id = if version == 0 {
1053 be_u16(&mut b)?.into()
1054 } else {
1055 be_u32(&mut b)?
1056 };
1057 if from_item_id == to_item_id {
1058 return Err(Error::InvalidData("from_item_id and to_item_id must be different"));
1059 }
1060 item_references.push(SingleItemTypeReferenceBox {
1061 item_type: b.head.name.into(),
1062 from_item_id,
1063 to_item_id,
1064 })?;
1065 }
1066 check_parser_state(&b.content)?;
1067 }
1068 Ok(item_references)
1069}
1070
1071fn read_iprp<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<AssociatedProperty>> {
1072 let mut iter = src.box_iter();
1073 let mut properties = TryVec::new();
1074 let mut associations = TryVec::new();
1075
1076 while let Some(mut b) = iter.next_box()? {
1077 match b.head.name {
1078 BoxType::ItemPropertyContainerBox => {
1079 properties = read_ipco(&mut b)?;
1080 },
1081 BoxType::ItemPropertyAssociationBox => {
1082 associations = read_ipma(&mut b)?;
1083 },
1084 _ => return Err(Error::InvalidData("unexpected ipco child")),
1085 }
1086 }
1087
1088 let mut associated = TryVec::new();
1089 for a in associations {
1090 let index = match a.property_index {
1091 0 => continue,
1092 x => x as usize - 1,
1093 };
1094 if let Some(prop) = properties.get(index)
1095 && *prop != ItemProperty::Unsupported
1096 {
1097 associated.push(AssociatedProperty {
1098 item_id: a.item_id,
1099 property: prop.try_clone()?,
1100 })?;
1101 }
1102 }
1103 Ok(associated)
1104}
1105
1106#[derive(Debug, PartialEq)]
1107pub(crate) enum ItemProperty {
1108 Channels(ArrayVec<u8, 16>),
1109 AuxiliaryType(AuxiliaryTypeProperty),
1110 ContentLightLevel(ContentLightLevel),
1111 MasteringDisplayColourVolume(MasteringDisplayColourVolume),
1112 Unsupported,
1113}
1114
1115impl TryClone for ItemProperty {
1116 fn try_clone(&self) -> Result<Self, TryReserveError> {
1117 Ok(match self {
1118 Self::Channels(val) => Self::Channels(val.clone()),
1119 Self::AuxiliaryType(val) => Self::AuxiliaryType(val.try_clone()?),
1120 Self::ContentLightLevel(val) => Self::ContentLightLevel(*val),
1121 Self::MasteringDisplayColourVolume(val) => Self::MasteringDisplayColourVolume(*val),
1122 Self::Unsupported => Self::Unsupported,
1123 })
1124 }
1125}
1126
1127struct Association {
1128 item_id: u32,
1129 #[allow(unused)]
1130 essential: bool,
1131 property_index: u16,
1132}
1133
1134pub(crate) struct AssociatedProperty {
1135 pub item_id: u32,
1136 pub property: ItemProperty,
1137}
1138
1139fn read_ipma<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<Association>> {
1140 let (version, flags) = read_fullbox_extra(src)?;
1141
1142 let mut associations = TryVec::new();
1143
1144 let entry_count = be_u32(src)?;
1145 for _ in 0..entry_count {
1146 let item_id = if version == 0 {
1147 be_u16(src)?.into()
1148 } else {
1149 be_u32(src)?
1150 };
1151 let association_count = src.read_u8()?;
1152 for _ in 0..association_count {
1153 let num_association_bytes = if flags & 1 == 1 { 2 } else { 1 };
1154 let association = &mut [0; 2][..num_association_bytes];
1155 src.read_exact(association)?;
1156 let mut association = BitReader::new(association);
1157 let essential = association.read_bool()?;
1158 let property_index = association.read_u16(association.remaining().try_into()?)?;
1159 associations.push(Association {
1160 item_id,
1161 essential,
1162 property_index,
1163 })?;
1164 }
1165 }
1166 Ok(associations)
1167}
1168
1169fn read_ipco<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<ItemProperty>> {
1170 let mut properties = TryVec::new();
1171
1172 let mut iter = src.box_iter();
1173 while let Some(mut b) = iter.next_box()? {
1174 properties.push(match b.head.name {
1176 BoxType::PixelInformationBox => ItemProperty::Channels(read_pixi(&mut b)?),
1177 BoxType::AuxiliaryTypeProperty => ItemProperty::AuxiliaryType(read_auxc(&mut b)?),
1178 BoxType::ContentLightLevelBox => ItemProperty::ContentLightLevel(read_clli(&mut b)?),
1179 BoxType::MasteringDisplayColourVolumeBox => ItemProperty::MasteringDisplayColourVolume(read_mdcv(&mut b)?),
1180 _ => {
1181 skip_box_remain(&mut b)?;
1182 ItemProperty::Unsupported
1183 },
1184 })?;
1185 }
1186 Ok(properties)
1187}
1188
1189fn read_pixi<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ArrayVec<u8, 16>> {
1190 let version = read_fullbox_version_no_flags(src)?;
1191 if version != 0 {
1192 return Err(Error::Unsupported("pixi version"));
1193 }
1194
1195 let num_channels = usize::from(src.read_u8()?);
1196 let mut channels = ArrayVec::new();
1197 channels.extend((0..num_channels.min(channels.capacity())).map(|_| 0));
1198 debug_assert_eq!(num_channels, channels.len());
1199 src.read_exact(&mut channels).map_err(|_| Error::InvalidData("invalid num_channels"))?;
1200
1201 check_parser_state(&src.content)?;
1202 Ok(channels)
1203}
1204
1205#[derive(Debug, PartialEq)]
1206#[doc(hidden)]
1207pub struct AuxiliaryTypeProperty {
1209 aux_data: TryString,
1210}
1211
1212impl AuxiliaryTypeProperty {
1213 #[must_use]
1214 pub fn type_subtype(&self) -> (&[u8], &[u8]) {
1215 let split = self.aux_data.iter().position(|&b| b == b'\0')
1216 .map(|pos| self.aux_data.split_at(pos));
1217 if let Some((aux_type, rest)) = split {
1218 (aux_type, &rest[1..])
1219 } else {
1220 (&self.aux_data, &[])
1221 }
1222 }
1223}
1224
1225impl TryClone for AuxiliaryTypeProperty {
1226 fn try_clone(&self) -> Result<Self, TryReserveError> {
1227 Ok(Self {
1228 aux_data: self.aux_data.try_clone()?,
1229 })
1230 }
1231}
1232
1233fn read_auxc<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<AuxiliaryTypeProperty> {
1234 let version = read_fullbox_version_no_flags(src)?;
1235 if version != 0 {
1236 return Err(Error::Unsupported("auxC version"));
1237 }
1238
1239 let aux_data = src.read_into_try_vec()?;
1240
1241 Ok(AuxiliaryTypeProperty { aux_data })
1242}
1243
1244fn read_clli<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<ContentLightLevel> {
1247 let max_content_light_level = be_u16(src)?;
1248 let max_pic_average_light_level = be_u16(src)?;
1249 skip_box_remain(src)?;
1250 Ok(ContentLightLevel {
1251 max_content_light_level,
1252 max_pic_average_light_level,
1253 })
1254}
1255
1256fn read_mdcv<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<MasteringDisplayColourVolume> {
1259 let primaries = [
1260 (be_u16(src)?, be_u16(src)?),
1261 (be_u16(src)?, be_u16(src)?),
1262 (be_u16(src)?, be_u16(src)?),
1263 ];
1264 let white_point = (be_u16(src)?, be_u16(src)?);
1265 let max_luminance = be_u32(src)?;
1266 let min_luminance = be_u32(src)?;
1267 skip_box_remain(src)?;
1268 Ok(MasteringDisplayColourVolume {
1269 primaries,
1270 white_point,
1271 max_luminance,
1272 min_luminance,
1273 })
1274}
1275
1276fn read_iloc<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<TryVec<ItemLocationBoxItem>> {
1279 let version: IlocVersion = read_fullbox_version_no_flags(src)?.try_into()?;
1280
1281 let iloc = src.read_into_try_vec()?;
1282 let mut iloc = BitReader::new(&iloc);
1283
1284 let offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
1285 let length_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
1286 let base_offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?;
1287
1288 let index_size: Option<IlocFieldSize> = match version {
1289 IlocVersion::One | IlocVersion::Two => Some(iloc.read_u8(4)?.try_into()?),
1290 IlocVersion::Zero => {
1291 let _reserved = iloc.read_u8(4)?;
1292 None
1293 },
1294 };
1295
1296 let item_count = match version {
1297 IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?,
1298 IlocVersion::Two => iloc.read_u32(32)?,
1299 };
1300
1301 let mut items = TryVec::with_capacity(item_count.to_usize())?;
1302
1303 for _ in 0..item_count {
1304 let item_id = match version {
1305 IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?,
1306 IlocVersion::Two => iloc.read_u32(32)?,
1307 };
1308
1309 let construction_method = match version {
1315 IlocVersion::Zero => ConstructionMethod::File,
1316 IlocVersion::One | IlocVersion::Two => {
1317 let _reserved = iloc.read_u16(12)?;
1318 match iloc.read_u16(4)? {
1319 0 => ConstructionMethod::File,
1320 1 => ConstructionMethod::Idat,
1321 2 => return Err(Error::Unsupported("construction_method 'item_offset' is not supported")),
1322 _ => return Err(Error::InvalidData("construction_method is taken from the set 0, 1 or 2 per ISO 14496-12:2015 § 8.11.3.3")),
1323 }
1324 },
1325 };
1326
1327 let data_reference_index = iloc.read_u16(16)?;
1328
1329 if data_reference_index != 0 {
1330 return Err(Error::Unsupported("external file references (iloc.data_reference_index != 0) are not supported"));
1331 }
1332
1333 let base_offset = iloc.read_u64(base_offset_size.to_bits())?;
1334 let extent_count = iloc.read_u16(16)?;
1335
1336 if extent_count < 1 {
1337 return Err(Error::InvalidData("extent_count must have a value 1 or greater per ISO 14496-12:2015 § 8.11.3.3"));
1338 }
1339
1340 let mut extents = TryVec::with_capacity(extent_count.to_usize())?;
1341
1342 for _ in 0..extent_count {
1343 let _extent_index = match &index_size {
1345 None | Some(IlocFieldSize::Zero) => None,
1346 Some(index_size) => {
1347 debug_assert!(version == IlocVersion::One || version == IlocVersion::Two);
1348 Some(iloc.read_u64(index_size.to_bits())?)
1349 },
1350 };
1351
1352 let extent_offset = iloc.read_u64(offset_size.to_bits())?;
1357 let extent_length = iloc.read_u64(length_size.to_bits())?;
1358
1359 let start = base_offset
1362 .checked_add(extent_offset)
1363 .ok_or(Error::InvalidData("offset calculation overflow"))?;
1364 let extent_range = if extent_length == 0 {
1365 ExtentRange::ToEnd(RangeFrom { start })
1366 } else {
1367 let end = start
1368 .checked_add(extent_length)
1369 .ok_or(Error::InvalidData("end calculation overflow"))?;
1370 ExtentRange::WithLength(Range { start, end })
1371 };
1372
1373 extents.push(ItemLocationBoxExtent { extent_range })?;
1374 }
1375
1376 items.push(ItemLocationBoxItem { item_id, construction_method, extents })?;
1377 }
1378
1379 if iloc.remaining() == 0 {
1380 Ok(items)
1381 } else {
1382 Err(Error::InvalidData("invalid iloc size"))
1383 }
1384}
1385
1386fn read_ftyp<T: Read>(src: &mut BMFFBox<'_, T>) -> Result<FileTypeBox> {
1389 let major = be_u32(src)?;
1390 let minor = be_u32(src)?;
1391 let bytes_left = src.bytes_left();
1392 if !bytes_left.is_multiple_of(4) {
1393 return Err(Error::InvalidData("invalid ftyp size"));
1394 }
1395 let brand_count = bytes_left / 4;
1397 let mut brands = TryVec::with_capacity(brand_count.try_into()?)?;
1398 for _ in 0..brand_count {
1399 brands.push(be_u32(src)?.into())?;
1400 }
1401 Ok(FileTypeBox {
1402 major_brand: From::from(major),
1403 minor_version: minor,
1404 compatible_brands: brands,
1405 })
1406}
1407
1408#[cfg_attr(debug_assertions, track_caller)]
1409fn check_parser_state<T>(left: &Take<T>) -> Result<(), Error> {
1410 let limit = left.limit();
1411 if limit == 0 {
1412 Ok(())
1413 } else {
1414 debug_assert_eq!(0, limit, "bad parser state bytes left");
1415 Err(Error::InvalidData("unread box content or bad parser sync"))
1416 }
1417}
1418
1419fn skip<T: Read>(src: &mut T, bytes: u64) -> Result<()> {
1421 std::io::copy(&mut src.take(bytes), &mut std::io::sink())?;
1422 Ok(())
1423}
1424
1425fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> {
1426 src.read_u16::<byteorder::BigEndian>().map_err(From::from)
1427}
1428
1429fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
1430 src.read_u32::<byteorder::BigEndian>().map_err(From::from)
1431}
1432
1433fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
1434 src.read_u64::<byteorder::BigEndian>().map_err(From::from)
1435}