Skip to content

Commit 34e91fd

Browse files
authored
Merge pull request #400 from mozilla/avif-timescale
Add a way to get the timescale of an avis
2 parents 888ce90 + 085f505 commit 34e91fd

File tree

3 files changed

+139
-87
lines changed

3 files changed

+139
-87
lines changed

mp4parse_capi/src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,7 @@ pub unsafe extern "C" fn mp4parse_avif_get_indice_table(
12971297
parser: *mut Mp4parseAvifParser,
12981298
track_id: u32,
12991299
indices: *mut Mp4parseByteData,
1300+
timescale: *mut u64,
13001301
) -> Mp4parseStatus {
13011302
if parser.is_null() {
13021303
return Mp4parseStatus::BadArg;
@@ -1306,10 +1307,35 @@ pub unsafe extern "C" fn mp4parse_avif_get_indice_table(
13061307
return Mp4parseStatus::BadArg;
13071308
}
13081309

1310+
if timescale.is_null() {
1311+
return Mp4parseStatus::BadArg;
1312+
}
1313+
13091314
// Initialize fields to default values to ensure all fields are always valid.
13101315
*indices = Default::default();
13111316

13121317
if let Some(sequence) = &(*parser).context.sequence {
1318+
// Use the top level timescale, and the track timescale if present.
1319+
let mut found_timescale = false;
1320+
if let Some(context_timescale) = sequence.timescale {
1321+
*timescale = context_timescale.0;
1322+
found_timescale = true;
1323+
}
1324+
let maybe_track_timescale = match sequence
1325+
.tracks
1326+
.iter()
1327+
.find(|track| track.track_id == Some(track_id))
1328+
{
1329+
Some(track) => track.timescale,
1330+
_ => None,
1331+
};
1332+
if let Some(track_timescale) = maybe_track_timescale {
1333+
found_timescale = true;
1334+
*timescale = track_timescale.0;
1335+
}
1336+
if !found_timescale {
1337+
return Mp4parseStatus::Invalid;
1338+
}
13131339
return get_indice_table(
13141340
sequence,
13151341
&mut (*parser).sample_table,

mp4parse_capi/tests/test_avis.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use mp4parse_capi::*;
2+
use num_traits::ToPrimitive;
3+
use std::io::Read;
4+
5+
extern "C" fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
6+
let input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
7+
let buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
8+
match input.read(buf) {
9+
Ok(n) => n as isize,
10+
Err(_) => -1,
11+
}
12+
}
13+
14+
unsafe fn parse_file_and_get_info(path: &str) -> (*mut Mp4parseAvifParser, Mp4parseAvifInfo) {
15+
let mut file = std::fs::File::open(path).expect("Unknown file");
16+
let io = Mp4parseIo {
17+
read: Some(buf_read),
18+
userdata: &mut file as *mut _ as *mut std::os::raw::c_void,
19+
};
20+
21+
let mut parser = std::ptr::null_mut();
22+
let mut rv = mp4parse_avif_new(&io, ParseStrictness::Normal, &mut parser);
23+
assert_eq!(rv, Mp4parseStatus::Ok);
24+
assert!(!parser.is_null());
25+
26+
let mut info = Mp4parseAvifInfo {
27+
premultiplied_alpha: Default::default(),
28+
major_brand: Default::default(),
29+
unsupported_features_bitfield: Default::default(),
30+
spatial_extents: std::ptr::null(),
31+
nclx_colour_information: std::ptr::null(),
32+
icc_colour_information: Default::default(),
33+
image_rotation: mp4parse::ImageRotation::D0,
34+
image_mirror: std::ptr::null(),
35+
pixel_aspect_ratio: std::ptr::null(),
36+
has_primary_item: Default::default(),
37+
primary_item_bit_depth: Default::default(),
38+
has_alpha_item: Default::default(),
39+
alpha_item_bit_depth: Default::default(),
40+
has_sequence: Default::default(),
41+
loop_mode: Default::default(),
42+
loop_count: Default::default(),
43+
color_track_id: Default::default(),
44+
color_track_bit_depth: Default::default(),
45+
alpha_track_id: Default::default(),
46+
alpha_track_bit_depth: Default::default(),
47+
};
48+
rv = mp4parse_avif_get_info(parser, &mut info);
49+
assert_eq!(rv, Mp4parseStatus::Ok);
50+
(parser, info)
51+
}
52+
53+
fn check_loop_count(path: &str, expected_loop_count: i64) {
54+
let (parser, info) = unsafe { parse_file_and_get_info(path) };
55+
match info.loop_mode {
56+
Mp4parseAvifLoopMode::NoEdits => assert_eq!(expected_loop_count, -1),
57+
Mp4parseAvifLoopMode::LoopByCount => {
58+
assert_eq!(info.loop_count.to_i64(), Some(expected_loop_count))
59+
}
60+
Mp4parseAvifLoopMode::LoopInfinitely => assert_eq!(expected_loop_count, std::i64::MIN),
61+
}
62+
63+
unsafe { mp4parse_avif_free(parser) };
64+
}
65+
66+
fn check_timescale(path: &str, expected_timescale: u64) {
67+
let (parser, info) = unsafe { parse_file_and_get_info(path) };
68+
69+
let mut indices: Mp4parseByteData = Mp4parseByteData::default();
70+
let mut timescale: u64 = 0;
71+
let rv = unsafe {
72+
mp4parse_avif_get_indice_table(parser, info.color_track_id, &mut indices, &mut timescale)
73+
};
74+
75+
assert_eq!(rv, Mp4parseStatus::Ok);
76+
assert_eq!(timescale, expected_timescale);
77+
78+
unsafe { mp4parse_avif_free(parser) };
79+
}
80+
81+
#[test]
82+
fn loop_once() {
83+
check_loop_count("tests/loop_1.avif", 1);
84+
}
85+
86+
#[test]
87+
fn loop_twice() {
88+
check_loop_count("tests/loop_2.avif", 2);
89+
}
90+
91+
#[test]
92+
fn loop_four_times_due_to_ceiling() {
93+
check_loop_count("tests/loop_ceiled_4.avif", 4);
94+
}
95+
96+
#[test]
97+
fn loop_forever() {
98+
check_loop_count("tests/loop_forever.avif", std::i64::MIN);
99+
}
100+
101+
#[test]
102+
fn no_edts() {
103+
check_loop_count("tests/no_edts.avif", -1);
104+
}
105+
106+
#[test]
107+
fn check_timescales() {
108+
check_timescale("tests/loop_1.avif", 2);
109+
check_timescale("tests/loop_2.avif", 2);
110+
check_timescale("tests/loop_ceiled_4.avif", 2);
111+
check_timescale("tests/loop_forever.avif", 2);
112+
check_timescale("tests/no_edts.avif", 16384);
113+
}

mp4parse_capi/tests/test_avis_loop_count.rs

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)