@@ -755,6 +755,11 @@ impl AvifContext {
755
755
}
756
756
}
757
757
758
+ struct AvifMeta {
759
+ primary_item_id : u32 ,
760
+ iloc_items : TryVec < ItemLocationBoxItem > ,
761
+ }
762
+
758
763
/// A Media Data Box
759
764
/// See ISO 14496-12:2015 § 8.1.1
760
765
struct MediaDataBox {
@@ -901,7 +906,7 @@ struct ItemLocationBoxItem {
901
906
extents : TryVec < ItemLocationBoxExtent > ,
902
907
}
903
908
904
- #[ derive( Clone , Copy , Debug ) ]
909
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
905
910
enum ConstructionMethod {
906
911
File ,
907
912
Idat ,
@@ -1086,46 +1091,6 @@ impl<'a, T: Read> BMFFBox<'a, T> {
1086
1091
}
1087
1092
}
1088
1093
1089
- impl < ' a , T : Read + Offset > BMFFBox < ' a , T > {
1090
- /// Check whether the beginning of `extent` is within the bounds of the `BMFFBox`.
1091
- /// We assume extents to not cross box boundaries. If so, this will cause an error
1092
- /// in `read_extent`.
1093
- fn contains_extent ( & self , extent : & ExtentRange ) -> bool {
1094
- if self . offset ( ) <= extent. start ( ) {
1095
- let start_offset = extent. start ( ) - self . offset ( ) ;
1096
- start_offset < self . bytes_left ( )
1097
- } else {
1098
- false
1099
- }
1100
- }
1101
-
1102
- /// Read the range specified by `extent` into `buf` or return an error if the range is not
1103
- /// fully contained within the `BMFFBox`.
1104
- fn read_extent ( & mut self , extent : & ExtentRange , buf : & mut TryVec < u8 > ) -> Result < ( ) > {
1105
- let start_offset = extent
1106
- . start ( )
1107
- . checked_sub ( self . offset ( ) )
1108
- . expect ( "box does not contain extent" ) ;
1109
- skip ( self , start_offset) ?;
1110
- match extent {
1111
- ExtentRange :: WithLength ( range) => {
1112
- let len = range
1113
- . end
1114
- . checked_sub ( range. start )
1115
- . expect ( "range start > end" ) ;
1116
- if len > self . bytes_left ( ) {
1117
- return Err ( Error :: InvalidData ( "extent crosses box boundary" ) ) ;
1118
- }
1119
- self . take ( len) . try_read_to_end ( buf) ?;
1120
- }
1121
- ExtentRange :: ToEnd ( _) => {
1122
- self . try_read_to_end ( buf) ?;
1123
- }
1124
- }
1125
- Ok ( ( ) )
1126
- }
1127
- }
1128
-
1129
1094
impl < ' a , T > Drop for BMFFBox < ' a , T > {
1130
1095
fn drop ( & mut self ) {
1131
1096
if self . content . limit ( ) > 0 {
@@ -1264,43 +1229,20 @@ pub fn read_avif<T: Read>(f: &mut T, context: &mut AvifContext) -> Result<()> {
1264
1229
}
1265
1230
}
1266
1231
1267
- let mut read_meta = false ;
1232
+ let mut meta = None ;
1268
1233
let mut mdats = TryVec :: new ( ) ;
1269
- let mut primary_item_extents = None ;
1270
- let mut primary_item_extents_data: TryVec < TryVec < u8 > > = TryVec :: new ( ) ;
1271
1234
1272
1235
while let Some ( mut b) = iter. next_box ( ) ? {
1273
1236
match b. head . name {
1274
1237
BoxType :: MetadataBox => {
1275
- if read_meta {
1238
+ if meta . is_some ( ) {
1276
1239
return Err ( Error :: InvalidData (
1277
1240
"There should be zero or one meta boxes per ISO 14496-12:2015 § 8.11.1.1" ,
1278
1241
) ) ;
1279
1242
}
1280
- read_meta = true ;
1281
- let primary_item_loc = read_avif_meta ( & mut b) ?;
1282
- match primary_item_loc. construction_method {
1283
- ConstructionMethod :: File => {
1284
- primary_item_extents_data
1285
- . resize_with ( primary_item_loc. extents . len ( ) , Default :: default) ?;
1286
- primary_item_extents = Some ( primary_item_loc. extents ) ;
1287
- }
1288
- _ => return Err ( Error :: Unsupported ( "unsupported construction_method" ) ) ,
1289
- }
1243
+ meta = Some ( read_avif_meta ( & mut b) ?) ;
1290
1244
}
1291
1245
BoxType :: MediaDataBox => {
1292
- // See ISO 14496-12:2015 § 8.1.1
1293
- // If we know our primary item location by this point, try to read it out of this
1294
- // mdat directly and avoid a copy
1295
- if let Some ( extents) = & primary_item_extents {
1296
- for ( extent, data) in extents. iter ( ) . zip ( primary_item_extents_data. iter_mut ( ) ) {
1297
- if b. contains_extent ( & extent. extent_range ) {
1298
- b. read_extent ( & extent. extent_range , data) ?;
1299
- }
1300
- }
1301
- }
1302
-
1303
- // Store any remaining data for potential later extraction
1304
1246
if b. bytes_left ( ) > 0 {
1305
1247
let offset = b. offset ( ) ;
1306
1248
let data = b. read_into_try_vec ( ) ?;
@@ -1313,39 +1255,35 @@ pub fn read_avif<T: Read>(f: &mut T, context: &mut AvifContext) -> Result<()> {
1313
1255
check_parser_state ! ( b. content) ;
1314
1256
}
1315
1257
1316
- // If the `mdat` box came before the `meta` box, we need to fill in our primary item data
1317
- let primary_item_extents =
1318
- primary_item_extents. ok_or ( Error :: InvalidData ( "primary item extents missing" ) ) ?;
1319
- for ( extent, data) in primary_item_extents
1320
- . iter ( )
1321
- . zip ( primary_item_extents_data. iter_mut ( ) )
1322
- {
1323
- if data. is_empty ( ) {
1324
- // try to find an overlapping mdat
1325
- for mdat in mdats. iter_mut ( ) {
1326
- if mdat. matches_extent ( & extent. extent_range ) {
1327
- data. append ( & mut mdat. data ) ?;
1328
- } else if mdat. contains_extent ( & extent. extent_range ) {
1329
- mdat. read_extent ( & extent. extent_range , data) ?;
1330
- }
1258
+ let meta = meta. ok_or ( Error :: InvalidData ( "missing meta" ) ) ?;
1259
+ let primary_item_loc = get_primary_item_loc ( & meta) ?;
1260
+ if primary_item_loc. construction_method != ConstructionMethod :: File {
1261
+ return Err ( Error :: Unsupported ( "unsupported construction_method" ) ) ;
1262
+ }
1263
+
1264
+ let mut primary_item = TryVec :: new ( ) ;
1265
+ for extent in primary_item_loc. extents . iter ( ) {
1266
+ // try to find an overlapping mdat
1267
+ for mdat in mdats. iter_mut ( ) {
1268
+ if mdat. matches_extent ( & extent. extent_range ) {
1269
+ primary_item. append ( & mut mdat. data ) ?;
1270
+ break ;
1271
+ } else if mdat. contains_extent ( & extent. extent_range ) {
1272
+ mdat. read_extent ( & extent. extent_range , & mut primary_item) ?;
1273
+ break ;
1331
1274
}
1332
1275
}
1333
1276
}
1334
1277
1335
- context. primary_item = primary_item_extents_data. concat ( ) ?;
1336
-
1337
- if primary_item_extents_data. iter ( ) . any ( TryVec :: is_empty) {
1338
- Err ( Error :: InvalidData ( "Primary item data incomplete" ) )
1339
- } else {
1340
- Ok ( ( ) )
1341
- }
1278
+ context. primary_item = primary_item;
1279
+ Ok ( ( ) )
1342
1280
}
1343
1281
1344
1282
/// Parse a metadata box in the context of an AVIF
1345
1283
/// Currently requires the primary item to be an av01 item type and generates
1346
1284
/// an error otherwise.
1347
1285
/// See ISO 14496-12:2015 § 8.11.1
1348
- fn read_avif_meta < T : Read + Offset > ( src : & mut BMFFBox < T > ) -> Result < ItemLocationBoxItem > {
1286
+ fn read_avif_meta < T : Read + Offset > ( src : & mut BMFFBox < T > ) -> Result < AvifMeta > {
1349
1287
let version = read_fullbox_version_no_flags ( src) ?;
1350
1288
1351
1289
if version != 0 {
@@ -1397,11 +1335,9 @@ fn read_avif_meta<T: Read + Offset>(src: &mut BMFFBox<T>) -> Result<ItemLocation
1397
1335
"Required pitm box not present in meta box" ,
1398
1336
) ) ?;
1399
1337
1400
- if let Some ( item_info) = item_infos
1401
- . iter ( )
1402
- . flatten ( )
1403
- . find ( |x| x. item_id == primary_item_id)
1404
- {
1338
+ let item_infos = item_infos. ok_or ( Error :: InvalidData ( "iinf missing" ) ) ?;
1339
+
1340
+ if let Some ( item_info) = item_infos. iter ( ) . find ( |x| x. item_id == primary_item_id) {
1405
1341
if & item_info. item_type . to_be_bytes ( ) != b"av01" {
1406
1342
warn ! ( "primary_item_id type: {}" , U32BE ( item_info. item_type) ) ;
1407
1343
return Err ( Error :: InvalidData ( "primary_item_id type is not av01" ) ) ;
@@ -1412,10 +1348,17 @@ fn read_avif_meta<T: Read + Offset>(src: &mut BMFFBox<T>) -> Result<ItemLocation
1412
1348
) ) ;
1413
1349
}
1414
1350
1415
- if let Some ( loc) = iloc_items
1416
- . into_iter ( )
1417
- . flatten ( )
1418
- . find ( |loc| loc. item_id == primary_item_id)
1351
+ Ok ( AvifMeta {
1352
+ primary_item_id,
1353
+ iloc_items : iloc_items. ok_or ( Error :: InvalidData ( "iloc missing" ) ) ?,
1354
+ } )
1355
+ }
1356
+
1357
+ fn get_primary_item_loc ( meta : & AvifMeta ) -> Result < & ItemLocationBoxItem > {
1358
+ if let Some ( loc) = meta
1359
+ . iloc_items
1360
+ . iter ( )
1361
+ . find ( |loc| loc. item_id == meta. primary_item_id )
1419
1362
{
1420
1363
Ok ( loc)
1421
1364
} else {
0 commit comments