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