Skip to content

Commit c196ca8

Browse files
committed
Make parsers return context objects rather that using out params
This simplifies the caller by eliminating the need to create the context, ensures that an empty/invalid context doesn't exist to accidentally operate on, and makes the code more idiomatic rust. Also, make some other minor cleanups.
1 parent 7a0b275 commit c196ca8

File tree

5 files changed

+71
-83
lines changed

5 files changed

+71
-83
lines changed

mp4parse/benches/avif_benchmark.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ criterion_group!(benches, criterion_benchmark);
1515
criterion_main!(benches);
1616

1717
fn avif_largest() {
18-
let context = &mut mp4::AvifContext::new();
1918
let input = &mut File::open(
2019
"av1-avif/testFiles/Netflix/avif/cosmos_frame05000_yuv444_12bpc_bt2020_pq_qlossless.avif",
2120
)
2221
.expect("Unknown file");
23-
assert!(mp4::read_avif(input, context).is_ok());
22+
assert!(mp4::read_avif(input).is_ok());
2423
}

mp4parse/src/lib.rs

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,10 +1218,7 @@ fn skip_box_remain<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
12181218
}
12191219

12201220
/// Read the contents of an AVIF file
1221-
///
1222-
/// Metadata is accumulated in the passed-through `AvifContext` struct,
1223-
/// which can be examined later.
1224-
pub fn read_avif<T: Read>(f: &mut T, context: &mut AvifContext) -> Result<()> {
1221+
pub fn read_avif<T: Read>(f: &mut T) -> Result<AvifContext> {
12251222
let _ = env_logger::try_init();
12261223

12271224
let mut f = OffsetReader::new(f);
@@ -1297,20 +1294,23 @@ pub fn read_avif<T: Read>(f: &mut T, context: &mut AvifContext) -> Result<()> {
12971294
return Err(Error::InvalidData("multiple alpha planes"));
12981295
}
12991296

1300-
context.premultiplied_alpha = alpha_item_id.map_or(false, |alpha_item_id| {
1297+
let premultiplied_alpha = alpha_item_id.map_or(false, |alpha_item_id| {
13011298
meta.item_references.iter().any(|iref| {
13021299
iref.from_item_id == meta.primary_item_id
13031300
&& iref.to_item_id == alpha_item_id
13041301
&& iref.item_type == b"prem"
13051302
})
13061303
});
13071304

1305+
let mut primary_item = TryVec::new();
1306+
let mut alpha_item = None;
1307+
13081308
// load data of relevant items
13091309
for loc in meta.iloc_items.iter() {
13101310
let mut item_data = if loc.item_id == meta.primary_item_id {
1311-
&mut context.primary_item
1311+
&mut primary_item
13121312
} else if Some(loc.item_id) == alpha_item_id {
1313-
context.alpha_item.get_or_insert_with(TryVec::new)
1313+
alpha_item.get_or_insert_with(TryVec::new)
13141314
} else {
13151315
continue;
13161316
};
@@ -1340,7 +1340,11 @@ pub fn read_avif<T: Read>(f: &mut T, context: &mut AvifContext) -> Result<()> {
13401340
}
13411341
}
13421342

1343-
Ok(())
1343+
Ok(AvifContext {
1344+
primary_item,
1345+
alpha_item,
1346+
premultiplied_alpha,
1347+
})
13441348
}
13451349

13461350
/// Parse a metadata box in the context of an AVIF
@@ -2040,12 +2044,9 @@ fn read_iloc<T: Read>(src: &mut BMFFBox<T>) -> Result<TryVec<ItemLocationBoxItem
20402044
}
20412045

20422046
/// Read the contents of a box, including sub boxes.
2043-
///
2044-
/// Metadata is accumulated in the passed-through `MediaContext` struct,
2045-
/// which can be examined later.
2046-
pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
2047+
pub fn read_mp4<T: Read>(f: &mut T) -> Result<MediaContext> {
2048+
let mut context = None;
20472049
let mut found_ftyp = false;
2048-
let mut found_moov = false;
20492050
// TODO(kinetik): Top-level parsing should handle zero-sized boxes
20502051
// rather than throwing an error.
20512052
let mut iter = BoxIter::new(f);
@@ -2072,13 +2073,12 @@ pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
20722073
debug!("{:?}", ftyp);
20732074
}
20742075
BoxType::MovieBox => {
2075-
read_moov(&mut b, context)?;
2076-
found_moov = true;
2076+
context = Some(read_moov(&mut b)?);
20772077
}
20782078
_ => skip_box_content(&mut b)?,
20792079
};
20802080
check_parser_state!(b.content);
2081-
if found_moov {
2081+
if context.is_some() {
20822082
debug!(
20832083
"found moov {}, could stop pure 'moov' parser now",
20842084
if found_ftyp {
@@ -2093,56 +2093,64 @@ pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
20932093
// XXX(kinetik): This isn't perfect, as a "moov" with no contents is
20942094
// treated as okay but we haven't found anything useful. Needs more
20952095
// thought for clearer behaviour here.
2096-
if found_moov {
2097-
Ok(())
2098-
} else {
2099-
Err(Error::NoMoov)
2100-
}
2096+
context.ok_or(Error::NoMoov)
21012097
}
21022098

2103-
fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<(MovieHeaderBox, Option<MediaTimeScale>)> {
2099+
fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
21042100
let mvhd = read_mvhd(f)?;
2101+
debug!("{:?}", mvhd);
21052102
if mvhd.timescale == 0 {
21062103
return Err(Error::InvalidData("zero timescale in mdhd"));
21072104
}
21082105
let timescale = Some(MediaTimeScale(u64::from(mvhd.timescale)));
2109-
Ok((mvhd, timescale))
2106+
Ok(timescale)
21102107
}
21112108

2112-
fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<()> {
2109+
fn read_moov<T: Read>(f: &mut BMFFBox<T>) -> Result<MediaContext> {
2110+
let MediaContext {
2111+
mut timescale,
2112+
mut tracks,
2113+
mut mvex,
2114+
mut psshs,
2115+
mut userdata,
2116+
} = Default::default();
2117+
21132118
let mut iter = f.box_iter();
21142119
while let Some(mut b) = iter.next_box()? {
21152120
match b.head.name {
21162121
BoxType::MovieHeaderBox => {
2117-
let (mvhd, timescale) = parse_mvhd(&mut b)?;
2118-
context.timescale = timescale;
2119-
debug!("{:?}", mvhd);
2122+
timescale = parse_mvhd(&mut b)?;
21202123
}
21212124
BoxType::TrackBox => {
2122-
let mut track = Track::new(context.tracks.len());
2125+
let mut track = Track::new(tracks.len());
21232126
read_trak(&mut b, &mut track)?;
2124-
context.tracks.push(track)?;
2127+
tracks.push(track)?;
21252128
}
21262129
BoxType::MovieExtendsBox => {
2127-
let mvex = read_mvex(&mut b)?;
2130+
mvex = Some(read_mvex(&mut b)?);
21282131
debug!("{:?}", mvex);
2129-
context.mvex = Some(mvex);
21302132
}
21312133
BoxType::ProtectionSystemSpecificHeaderBox => {
21322134
let pssh = read_pssh(&mut b)?;
21332135
debug!("{:?}", pssh);
2134-
context.psshs.push(pssh)?;
2136+
psshs.push(pssh)?;
21352137
}
21362138
BoxType::UserdataBox => {
2137-
let udta = read_udta(&mut b);
2138-
debug!("{:?}", udta);
2139-
context.userdata = Some(udta);
2139+
userdata = Some(read_udta(&mut b));
2140+
debug!("{:?}", userdata);
21402141
}
21412142
_ => skip_box_content(&mut b)?,
21422143
};
21432144
check_parser_state!(b.content);
21442145
}
2145-
Ok(())
2146+
2147+
Ok(MediaContext {
2148+
timescale,
2149+
tracks,
2150+
mvex,
2151+
psshs,
2152+
userdata,
2153+
})
21462154
}
21472155

21482156
fn read_pssh<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSystemSpecificHeaderBox> {

mp4parse/src/tests.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
use super::read_mp4;
99
use super::Error;
10-
use super::MediaContext;
1110
use fallible_collections::TryRead as _;
1211

1312
use std::convert::TryInto as _;
@@ -195,8 +194,7 @@ fn read_truncated_ftyp() {
195194
.B32(0) // minor version
196195
.append_bytes(b"isom")
197196
});
198-
let mut context = MediaContext::new();
199-
match read_mp4(&mut stream, &mut context) {
197+
match read_mp4(&mut stream) {
200198
Err(Error::UnexpectedEOF) => (),
201199
Ok(_) => panic!("expected an error result"),
202200
_ => panic!("expected a different error result"),
@@ -346,7 +344,7 @@ fn read_mdhd_invalid_timescale() {
346344
assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
347345
assert_eq!(stream.head.size, 44);
348346
let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0));
349-
assert_eq!(r.is_err(), true);
347+
assert!(r.is_err());
350348
}
351349

352350
#[test]
@@ -387,7 +385,7 @@ fn read_mvhd_invalid_timescale() {
387385
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
388386
assert_eq!(stream.head.size, 120);
389387
let r = super::parse_mvhd(&mut stream);
390-
assert_eq!(r.is_err(), true);
388+
assert!(r.is_err());
391389
}
392390

393391
#[test]
@@ -1249,9 +1247,8 @@ fn read_invalid_pssh() {
12491247
let mut stream = make_box(BoxSize::Auto, b"moov", |s| s.append_bytes(pssh.as_slice()));
12501248
let mut iter = super::BoxIter::new(&mut stream);
12511249
let mut stream = iter.next_box().unwrap().unwrap();
1252-
let mut context = super::MediaContext::new();
12531250

1254-
match super::read_moov(&mut stream, &mut context) {
1251+
match super::read_moov(&mut stream) {
12551252
Err(Error::InvalidData(s)) => assert_eq!(s, "read_buf size exceeds BUF_SIZE_LIMIT"),
12561253
_ => panic!("unexpected result with invalid descriptor"),
12571254
}

mp4parse/tests/public.rs

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ fn public_api() {
4343
fd.read_to_end(&mut buf).expect("File error");
4444

4545
let mut c = Cursor::new(&buf);
46-
let mut context = mp4::MediaContext::new();
47-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
46+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
4847
assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000)));
4948
for track in context.tracks {
5049
match track.track_type {
@@ -164,8 +163,7 @@ fn public_metadata() {
164163
fd.read_to_end(&mut buf).expect("File error");
165164

166165
let mut c = Cursor::new(&buf);
167-
let mut context = mp4::MediaContext::new();
168-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
166+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
169167
let udta = context
170168
.userdata
171169
.expect("didn't find udta")
@@ -232,8 +230,7 @@ fn public_metadata_gnre() {
232230
fd.read_to_end(&mut buf).expect("File error");
233231

234232
let mut c = Cursor::new(&buf);
235-
let mut context = mp4::MediaContext::new();
236-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
233+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
237234
let udta = context
238235
.userdata
239236
.expect("didn't find udta")
@@ -301,8 +298,7 @@ fn public_audio_tenc() {
301298
fd.read_to_end(&mut buf).expect("File error");
302299

303300
let mut c = Cursor::new(&buf);
304-
let mut context = mp4::MediaContext::new();
305-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
301+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
306302
for track in context.tracks {
307303
let stsd = track.stsd.expect("expected an stsd");
308304
let a = match stsd.descriptions.first().expect("expected a SampleEntry") {
@@ -360,8 +356,7 @@ fn public_video_cenc() {
360356
fd.read_to_end(&mut buf).expect("File error");
361357

362358
let mut c = Cursor::new(&buf);
363-
let mut context = mp4::MediaContext::new();
364-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
359+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
365360
for track in context.tracks {
366361
let stsd = track.stsd.expect("expected an stsd");
367362
let v = match stsd.descriptions.first().expect("expected a SampleEntry") {
@@ -433,8 +428,7 @@ fn public_audio_cbcs() {
433428
fd.read_to_end(&mut buf).expect("File error");
434429

435430
let mut c = Cursor::new(&buf);
436-
let mut context = mp4::MediaContext::new();
437-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
431+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
438432
for track in context.tracks {
439433
let stsd = track.stsd.expect("expected an stsd");
440434
assert_eq!(stsd.descriptions.len(), 2);
@@ -516,8 +510,7 @@ fn public_video_cbcs() {
516510
fd.read_to_end(&mut buf).expect("File error");
517511

518512
let mut c = Cursor::new(&buf);
519-
let mut context = mp4::MediaContext::new();
520-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
513+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
521514
for track in context.tracks {
522515
let stsd = track.stsd.expect("expected an stsd");
523516
assert_eq!(stsd.descriptions.len(), 2);
@@ -576,8 +569,7 @@ fn public_video_av1() {
576569
fd.read_to_end(&mut buf).expect("File error");
577570

578571
let mut c = Cursor::new(&buf);
579-
let mut context = mp4::MediaContext::new();
580-
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
572+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
581573
for track in context.tracks {
582574
// track part
583575
assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0)));
@@ -623,41 +615,36 @@ fn public_video_av1() {
623615

624616
#[test]
625617
fn public_avif_primary_item() {
626-
let context = &mut mp4::AvifContext::new();
627618
let input = &mut File::open(IMAGE_AVIF).expect("Unknown file");
628-
mp4::read_avif(input, context).expect("read_avif failed");
619+
let context = mp4::read_avif(input).expect("read_avif failed");
629620
assert_eq!(context.primary_item.len(), 6979);
630621
assert_eq!(context.primary_item[0..4], [0x12, 0x00, 0x0a, 0x0a]);
631622
}
632623

633624
#[test]
634625
fn public_avif_primary_item_split_extents() {
635-
let context = &mut mp4::AvifContext::new();
636626
let input = &mut File::open(IMAGE_AVIF_EXTENTS).expect("Unknown file");
637-
mp4::read_avif(input, context).expect("read_avif failed");
627+
let context = mp4::read_avif(input).expect("read_avif failed");
638628
assert_eq!(context.primary_item.len(), 4387);
639629
}
640630

641631
#[test]
642632
fn public_avif_bug_1655846() {
643-
let context = &mut mp4::AvifContext::new();
644633
let input = &mut File::open(IMAGE_AVIF_CORRUPT).expect("Unknown file");
645-
assert!(mp4::read_avif(input, context).is_err());
634+
assert!(mp4::read_avif(input).is_err());
646635
}
647636

648637
#[test]
649638
fn public_avif_bug_1661347() {
650-
let context = &mut mp4::AvifContext::new();
651639
let input = &mut File::open(IMAGE_AVIF_CORRUPT_2).expect("Unknown file");
652-
assert!(mp4::read_avif(input, context).is_err());
640+
assert!(mp4::read_avif(input).is_err());
653641
}
654642

655643
#[test]
656644
#[ignore] // Remove when we add support; see https://github.com/mozilla/mp4parse-rust/issues/198
657645
fn public_avif_primary_item_is_grid() {
658-
let context = &mut mp4::AvifContext::new();
659646
let input = &mut File::open(IMAGE_AVIF_GRID).expect("Unknown file");
660-
mp4::read_avif(input, context).expect("read_avif failed");
647+
mp4::read_avif(input).expect("read_avif failed");
661648
// Add some additional checks
662649
}
663650

@@ -675,8 +662,7 @@ fn public_avif_read_samples() {
675662
continue; // Remove when public_avif_primary_item_is_grid passes
676663
}
677664
println!("parsing {:?}", path);
678-
let context = &mut mp4::AvifContext::new();
679665
let input = &mut File::open(path).expect("Unknow file");
680-
mp4::read_avif(input, context).expect("read_avif failed");
666+
mp4::read_avif(input).expect("read_avif failed");
681667
}
682668
}

0 commit comments

Comments
 (0)