@@ -1594,6 +1594,10 @@ pub struct AvifContext {
15941594 pub sequence : Option < MediaContext > ,
15951595 /// A collection of unsupported features encountered during the parse
15961596 pub unsupported_features : UnsupportedFeatures ,
1597+ /// AVIF box containing the Exif metadata, if any
1598+ exif_metadata : Option < AvifItem > ,
1599+ /// AVIF box containing the XMP metadata, if any
1600+ xmp_metadata : Option < AvifItem > ,
15971601}
15981602
15991603impl AvifContext {
@@ -1656,6 +1660,18 @@ impl AvifContext {
16561660 }
16571661
16581662
1663+ pub fn exif_metadata ( & self ) -> Option < & [ u8 ] > {
1664+ self . exif_metadata
1665+ . as_ref ( )
1666+ . map ( |item| self . item_as_slice ( item) )
1667+ }
1668+
1669+ pub fn xmp_metadata ( & self ) -> Option < & [ u8 ] > {
1670+ self . xmp_metadata
1671+ . as_ref ( )
1672+ . map ( |item| self . item_as_slice ( item) )
1673+ }
1674+
16591675 pub fn spatial_extents ( & self ) -> Result < & ImageSpatialExtentsProperty > {
16601676 if let Some ( primary_item) = & self . primary_item {
16611677 match self
@@ -1970,7 +1986,7 @@ impl DataBox {
19701986#[ derive( Clone , Copy , Debug , Eq , Hash , PartialEq ) ]
19711987struct PropertyIndex ( u16 ) ;
19721988#[ derive( Clone , Copy , Debug , Eq , Hash , PartialEq , PartialOrd ) ]
1973- struct ItemId ( u32 ) ;
1989+ pub struct ItemId ( u32 ) ;
19741990
19751991impl ItemId {
19761992 fn read ( src : & mut impl ReadBytesExt , version : u8 ) -> Result < ItemId > {
@@ -1986,9 +2002,12 @@ impl ItemId {
19862002/// See ISOBMFF (ISO 14496-12:2020) § 8.11.6
19872003/// Only versions {2, 3} are supported
19882004#[ derive( Debug ) ]
1989- struct ItemInfoEntry {
1990- item_id : ItemId ,
1991- item_type : u32 ,
2005+ pub struct ItemInfoEntry {
2006+ pub item_id : ItemId ,
2007+ pub item_type : u32 ,
2008+ pub item_name : TryVec < u8 > ,
2009+ pub content_type : Option < TryVec < u8 > > ,
2010+ pub content_encoding : Option < TryVec < u8 > >
19922011}
19932012
19942013/// See ISOBMFF (ISO 14496-12:2020) § 8.11.12
@@ -2276,6 +2295,26 @@ impl<T> Drop for BMFFBox<'_, T> {
22762295 }
22772296}
22782297
2298+ impl < T : ReadBytesExt > BMFFBox < ' _ , T > {
2299+ fn read_string ( & mut self , strictness : ParseStrictness ) -> Result < TryVec < u8 > > {
2300+ let mut s = TryVec :: new ( ) ;
2301+ loop {
2302+ let v = self . read_u8 ( ) ?;
2303+ s. push ( v) ?;
2304+ if v == 0 {
2305+ break ;
2306+ }
2307+ }
2308+ if let Err ( _) = std:: str:: from_utf8 ( & s) {
2309+ fail_with_status_if (
2310+ strictness != ParseStrictness :: Permissive ,
2311+ Status :: InvalidUtf8 ,
2312+ ) ?;
2313+ }
2314+ Ok ( s)
2315+ }
2316+ }
2317+
22792318/// Read and parse a box header.
22802319///
22812320/// Call this first to determine the type of a particular mp4 box
@@ -2577,24 +2616,17 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
25772616 debug ! ( "alpha_item_id: {alpha_item_id:?}" ) ;
25782617 let mut primary_item = None ;
25792618 let mut alpha_item = None ;
2619+ let mut items: TryHashMap < ItemId , AvifItem > = Default :: default ( ) ;
25802620
25812621 // store data or record location of relevant items
25822622 for ( item_id, loc) in iloc_items {
2583- let item = if Some ( item_id) == primary_item_id {
2584- & mut primary_item
2585- } else if Some ( item_id) == alpha_item_id {
2586- & mut alpha_item
2587- } else {
2588- continue ;
2589- } ;
2590-
2591- assert ! ( item. is_none( ) ) ;
2623+ let mut item: Option < AvifItem > = None ;
25922624
25932625 // If our item is spread over multiple extents, we'll need to copy it
25942626 // into a contiguous buffer. Otherwise, we can just store the extent
25952627 // and return a pointer into the mdat/idat later to avoid the copy.
25962628 if loc. extents . len ( ) > 1 {
2597- * item = Some ( AvifItem :: with_inline_data ( item_id) )
2629+ item = Some ( AvifItem :: with_inline_data ( item_id) )
25982630 }
25992631
26002632 trace ! (
@@ -2607,10 +2639,10 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
26072639 // true if the extent is successfully added to the AvifItem
26082640 let mut find_and_add_to_item = |extent : & Extent , dat : & DataBox | -> Result < bool > {
26092641 if let Some ( extent_slice) = dat. get ( extent) {
2610- match item {
2642+ match & mut item {
26112643 None => {
26122644 trace ! ( "Using IsobmffItem::Location" ) ;
2613- * item = Some ( AvifItem {
2645+ item = Some ( AvifItem {
26142646 id : item_id,
26152647 image_data : dat. location ( extent) ,
26162648 } ) ;
@@ -2670,6 +2702,14 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
26702702 }
26712703
26722704 assert ! ( item. is_some( ) ) ;
2705+
2706+ if Some ( item_id) == primary_item_id {
2707+ primary_item = item;
2708+ } else if Some ( item_id) == alpha_item_id {
2709+ alpha_item = item;
2710+ } else {
2711+ items. insert ( item_id, item. unwrap ( ) ) ?;
2712+ } ;
26732713 }
26742714
26752715 if ( primary_item_id. is_some ( ) && primary_item. is_none ( ) )
@@ -2773,6 +2813,21 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
27732813 check_image_item ( & mut primary_item) ?;
27742814 check_image_item ( & mut alpha_item) ?;
27752815
2816+ let mut exif_metadata = None ;
2817+ let mut xmp_metadata = None ;
2818+
2819+ item_infos. into_iter ( ) . for_each ( |item| {
2820+ if & item. item_type . to_be_bytes ( ) == b"Exif" {
2821+ exif_metadata = items. remove ( & item. item_id ) ;
2822+ } else if & item. item_type . to_be_bytes ( ) == b"mime"
2823+ && item
2824+ . content_type
2825+ . is_some_and ( |v| v. as_slice ( ) == b"application/rdf+xml" )
2826+ {
2827+ xmp_metadata = items. remove ( & item. item_id ) ;
2828+ }
2829+ } ) ;
2830+
27762831 Ok ( AvifContext {
27772832 strictness,
27782833 media_storage,
@@ -2783,6 +2838,8 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
27832838 item_properties,
27842839 major_brand,
27852840 sequence : image_sequence,
2841+ exif_metadata,
2842+ xmp_metadata,
27862843 unsupported_features,
27872844 } )
27882845}
@@ -2988,15 +3045,30 @@ fn read_infe<T: Read>(
29883045 let item_type = be_u32 ( src) ?;
29893046 debug ! ( "infe {:?} item_type: {}" , item_id, U32BE ( item_type) ) ;
29903047
2991- // There are some additional fields here, but they're not of interest to us
2992- skip_box_remain ( src) ?;
2993-
29943048 if item_protection_index != 0 {
29953049 unsupported_features. insert ( Feature :: Ipro ) ;
2996- Ok ( None )
2997- } else {
2998- Ok ( Some ( ItemInfoEntry { item_id, item_type } ) )
3050+ skip_box_remain ( src) ?;
3051+ return Ok ( None ) ;
29993052 }
3053+
3054+ let item_name = src. read_string ( strictness) ?;
3055+
3056+ let ( content_type, content_encoding) = if item_name. as_slice ( ) == b"mime" {
3057+ (
3058+ Some ( src. read_string ( strictness) ?) ,
3059+ Some ( src. read_string ( strictness) ?) ,
3060+ )
3061+ } else {
3062+ ( None , None )
3063+ } ;
3064+
3065+ Ok ( Some ( ItemInfoEntry {
3066+ item_id,
3067+ item_type,
3068+ item_name,
3069+ content_type,
3070+ content_encoding
3071+ } ) )
30003072}
30013073
30023074/// Parse an Item Reference Box
0 commit comments