@@ -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 {
@@ -1655,6 +1659,25 @@ impl AvifContext {
16551659 }
16561660 }
16571661
1662+ pub fn exif_metadata_is_present ( & self ) -> bool {
1663+ self . exif_metadata . is_some ( )
1664+ }
1665+
1666+ pub fn exif_metadata ( & self ) -> Option < & [ u8 ] > {
1667+ self . exif_metadata
1668+ . as_ref ( )
1669+ . map ( |item| self . item_as_slice ( item) )
1670+ }
1671+
1672+ pub fn xmp_metadata_is_present ( & self ) -> bool {
1673+ self . xmp_metadata . is_some ( )
1674+ }
1675+
1676+ pub fn xmp_metadata ( & self ) -> Option < & [ u8 ] > {
1677+ self . xmp_metadata
1678+ . as_ref ( )
1679+ . map ( |item| self . item_as_slice ( item) )
1680+ }
16581681
16591682 pub fn spatial_extents ( & self ) -> Result < & ImageSpatialExtentsProperty > {
16601683 if let Some ( primary_item) = & self . primary_item {
@@ -1970,7 +1993,7 @@ impl DataBox {
19701993#[ derive( Clone , Copy , Debug , Eq , Hash , PartialEq ) ]
19711994struct PropertyIndex ( u16 ) ;
19721995#[ derive( Clone , Copy , Debug , Eq , Hash , PartialEq , PartialOrd ) ]
1973- struct ItemId ( u32 ) ;
1996+ pub struct ItemId ( u32 ) ;
19741997
19751998impl ItemId {
19761999 fn read ( src : & mut impl ReadBytesExt , version : u8 ) -> Result < ItemId > {
@@ -1986,9 +2009,12 @@ impl ItemId {
19862009/// See ISOBMFF (ISO 14496-12:2020) § 8.11.6
19872010/// Only versions {2, 3} are supported
19882011#[ derive( Debug ) ]
1989- struct ItemInfoEntry {
1990- item_id : ItemId ,
1991- item_type : u32 ,
2012+ pub struct ItemInfoEntry {
2013+ pub item_id : ItemId ,
2014+ pub item_type : u32 ,
2015+ pub item_name : TryVec < u8 > ,
2016+ pub content_type : Option < TryVec < u8 > > ,
2017+ pub content_encoding : Option < TryVec < u8 > >
19922018}
19932019
19942020/// See ISOBMFF (ISO 14496-12:2020) § 8.11.12
@@ -2276,6 +2302,26 @@ impl<T> Drop for BMFFBox<'_, T> {
22762302 }
22772303}
22782304
2305+ impl < T : ReadBytesExt > BMFFBox < ' _ , T > {
2306+ fn read_string ( & mut self , strictness : ParseStrictness ) -> Result < TryVec < u8 > > {
2307+ let mut s = TryVec :: new ( ) ;
2308+ loop {
2309+ let v = self . read_u8 ( ) ?;
2310+ s. push ( v) ?;
2311+ if v == 0 {
2312+ break ;
2313+ }
2314+ }
2315+ if let Err ( _) = std:: str:: from_utf8 ( & s) {
2316+ fail_with_status_if (
2317+ strictness != ParseStrictness :: Permissive ,
2318+ Status :: InvalidUtf8 ,
2319+ ) ?;
2320+ }
2321+ Ok ( s)
2322+ }
2323+ }
2324+
22792325/// Read and parse a box header.
22802326///
22812327/// Call this first to determine the type of a particular mp4 box
@@ -2577,24 +2623,17 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
25772623 debug ! ( "alpha_item_id: {alpha_item_id:?}" ) ;
25782624 let mut primary_item = None ;
25792625 let mut alpha_item = None ;
2626+ let mut items: std:: collections:: HashMap < ItemId , AvifItem > = Default :: default ( ) ;
25802627
25812628 // store data or record location of relevant items
25822629 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( ) ) ;
2630+ let mut item: Option < AvifItem > = None ;
25922631
25932632 // If our item is spread over multiple extents, we'll need to copy it
25942633 // into a contiguous buffer. Otherwise, we can just store the extent
25952634 // and return a pointer into the mdat/idat later to avoid the copy.
25962635 if loc. extents . len ( ) > 1 {
2597- * item = Some ( AvifItem :: with_inline_data ( item_id) )
2636+ item = Some ( AvifItem :: with_inline_data ( item_id) )
25982637 }
25992638
26002639 trace ! (
@@ -2607,10 +2646,10 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
26072646 // true if the extent is successfully added to the AvifItem
26082647 let mut find_and_add_to_item = |extent : & Extent , dat : & DataBox | -> Result < bool > {
26092648 if let Some ( extent_slice) = dat. get ( extent) {
2610- match item {
2649+ match & mut item {
26112650 None => {
26122651 trace ! ( "Using IsobmffItem::Location" ) ;
2613- * item = Some ( AvifItem {
2652+ item = Some ( AvifItem {
26142653 id : item_id,
26152654 image_data : dat. location ( extent) ,
26162655 } ) ;
@@ -2670,6 +2709,14 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
26702709 }
26712710
26722711 assert ! ( item. is_some( ) ) ;
2712+
2713+ if Some ( item_id) == primary_item_id {
2714+ primary_item = item;
2715+ } else if Some ( item_id) == alpha_item_id {
2716+ alpha_item = item;
2717+ } else {
2718+ items. insert ( item_id, item. unwrap ( ) ) ;
2719+ } ;
26732720 }
26742721
26752722 if ( primary_item_id. is_some ( ) && primary_item. is_none ( ) )
@@ -2773,6 +2820,21 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
27732820 check_image_item ( & mut primary_item) ?;
27742821 check_image_item ( & mut alpha_item) ?;
27752822
2823+ let mut exif_metadata = None ;
2824+ let mut xmp_metadata = None ;
2825+
2826+ item_infos. into_iter ( ) . for_each ( |item| {
2827+ if & item. item_type . to_be_bytes ( ) == b"Exif" {
2828+ exif_metadata = items. remove ( & item. item_id ) ;
2829+ } else if & item. item_type . to_be_bytes ( ) == b"mime"
2830+ && item
2831+ . content_type
2832+ . is_some_and ( |v| v. as_slice ( ) == b"application/rdf+xml" )
2833+ {
2834+ xmp_metadata = items. remove ( & item. item_id ) ;
2835+ }
2836+ } ) ;
2837+
27762838 Ok ( AvifContext {
27772839 strictness,
27782840 media_storage,
@@ -2783,6 +2845,8 @@ pub fn read_avif<T: Read>(f: &mut T, strictness: ParseStrictness) -> Result<Avif
27832845 item_properties,
27842846 major_brand,
27852847 sequence : image_sequence,
2848+ exif_metadata,
2849+ xmp_metadata,
27862850 unsupported_features,
27872851 } )
27882852}
@@ -2988,15 +3052,30 @@ fn read_infe<T: Read>(
29883052 let item_type = be_u32 ( src) ?;
29893053 debug ! ( "infe {:?} item_type: {}" , item_id, U32BE ( item_type) ) ;
29903054
2991- // There are some additional fields here, but they're not of interest to us
2992- skip_box_remain ( src) ?;
2993-
29943055 if item_protection_index != 0 {
29953056 unsupported_features. insert ( Feature :: Ipro ) ;
2996- Ok ( None )
2997- } else {
2998- Ok ( Some ( ItemInfoEntry { item_id, item_type } ) )
3057+ skip_box_remain ( src) ?;
3058+ return Ok ( None ) ;
29993059 }
3060+
3061+ let item_name = src. read_string ( strictness) ?;
3062+
3063+ let ( content_type, content_encoding) = if item_name. as_slice ( ) == b"mime" {
3064+ (
3065+ Some ( src. read_string ( strictness) ?) ,
3066+ Some ( src. read_string ( strictness) ?) ,
3067+ )
3068+ } else {
3069+ ( None , None )
3070+ } ;
3071+
3072+ Ok ( Some ( ItemInfoEntry {
3073+ item_id,
3074+ item_type,
3075+ item_name,
3076+ content_type,
3077+ content_encoding
3078+ } ) )
30003079}
30013080
30023081/// Parse an Item Reference Box
0 commit comments