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