Skip to content

Commit 76d811a

Browse files
SingingTreekinetiknz
authored andcommitted
Add tests to verify cbcs parsing.
Add tests to both mp4parse and mprparse_capi to verify parsing of encrypted mp4s. These primarily focus on cbcs encryption, but some new cenc tests have also been added to the c-api to provide further coverage.
1 parent 2bce2ce commit 76d811a

File tree

5 files changed

+301
-4
lines changed

5 files changed

+301
-4
lines changed
984 Bytes
Binary file not shown.
1.07 KB
Binary file not shown.

mp4parse/tests/public.rs

Lines changed: 162 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,19 @@ use std::io::{Cursor, Read};
1010
use std::fs::File;
1111

1212
static MINI_MP4: &'static str = "tests/minimal.mp4";
13-
static AUDIO_EME_MP4: &'static str = "tests/bipbop-cenc-audioinit.mp4";
14-
static VIDEO_EME_MP4: &'static str = "tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4";
13+
static AUDIO_EME_CENC_MP4: &'static str = "tests/bipbop-cenc-audioinit.mp4";
14+
static VIDEO_EME_CENC_MP4: &'static str = "tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4";
15+
// The cbcs files were created via shaka-packager from Firefox's test suite's bipbop.mp4 using:
16+
// packager-win.exe
17+
// in=bipbop.mp4,stream=audio,init_segment=bipbop_cbcs_audio_init.mp4,segment_template=bipbop_cbcs_audio_$Number$.m4s
18+
// in=bipbop.mp4,stream=video,init_segment=bipbop_cbcs_video_init.mp4,segment_template=bipbop_cbcs_video_$Number$.m4s
19+
// --protection_scheme cbcs --enable_raw_key_encryption
20+
// --keys label=:key_id=7e571d047e571d047e571d047e571d21:key=7e5744447e5744447e5744447e574421
21+
// --iv 11223344556677889900112233445566
22+
// --generate_static_mpd --mpd_output bipbop_cbcs.mpd
23+
// note: only the init files are needed for these tests
24+
static AUDIO_EME_CBCS_MP4: &'static str = "tests/bipbop_cbcs_audio_init.mp4";
25+
static VIDEO_EME_CBCS_MP4: &'static str = "tests/bipbop_cbcs_video_init.mp4";
1526
static VIDEO_AV1_MP4: &'static str = "tests/tiny_av1.mp4";
1627

1728
// Adapted from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53
@@ -135,7 +146,7 @@ fn public_audio_tenc() {
135146
vec![0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04,
136147
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04];
137148

138-
let mut fd = File::open(AUDIO_EME_MP4).expect("Unknown file");
149+
let mut fd = File::open(AUDIO_EME_CENC_MP4).expect("Unknown file");
139150
let mut buf = Vec::new();
140151
fd.read_to_end(&mut buf).expect("File error");
141152

@@ -189,7 +200,7 @@ fn public_video_cenc() {
189200
0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x11,
190201
0x00, 0x00, 0x00, 0x00];
191202

192-
let mut fd = File::open(VIDEO_EME_MP4).expect("Unknown file");
203+
let mut fd = File::open(VIDEO_EME_CENC_MP4).expect("Unknown file");
193204
let mut buf = Vec::new();
194205
fd.read_to_end(&mut buf).expect("File error");
195206

@@ -233,6 +244,153 @@ fn public_video_cenc() {
233244
}
234245
}
235246

247+
#[test]
248+
fn publicaudio_cbcs() {
249+
let system_id =
250+
vec![0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
251+
0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b];
252+
253+
let kid =
254+
vec![0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04,
255+
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x21];
256+
257+
let default_iv =
258+
vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
259+
0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
260+
261+
let pssh_box =
262+
vec![0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
263+
0x01, 0x00, 0x00, 0x00, 0x10, 0x77, 0xef, 0xec,
264+
0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
265+
0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00, 0x00, 0x01,
266+
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04,
267+
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x21,
268+
0x00, 0x00, 0x00, 0x00];
269+
270+
let mut fd = File::open(AUDIO_EME_CBCS_MP4).expect("Unknown file");
271+
let mut buf = Vec::new();
272+
fd.read_to_end(&mut buf).expect("File error");
273+
274+
let mut c = Cursor::new(&buf);
275+
let mut context = mp4::MediaContext::new();
276+
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
277+
for track in context.tracks {
278+
let stsd = track.stsd.expect("expected an stsd");
279+
assert_eq!(stsd.descriptions.len(), 2);
280+
let mut found_encrypted_sample_description = false;
281+
for description in stsd.descriptions {
282+
match description {
283+
mp4::SampleEntry::Audio(ref a) => {
284+
if let Some(p) = a.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
285+
found_encrypted_sample_description = true;
286+
assert_eq!(p.code_name, "mp4a");
287+
if let Some(ref tenc) = p.tenc {
288+
assert!(tenc.is_encrypted > 0);
289+
assert_eq!(tenc.iv_size, 0);
290+
assert_eq!(tenc.kid, kid);
291+
// Note: 0 for both crypt and skip seems odd but
292+
// that's what shaka-packager produced. It appears
293+
// to indicate full encryption.
294+
assert_eq!(tenc.crypt_byte_block_count, Some(0));
295+
assert_eq!(tenc.skip_byte_block_count, Some(0));
296+
assert_eq!(tenc.constant_iv, Some(default_iv.clone()));
297+
} else {
298+
assert!(false, "Invalid test condition");
299+
}
300+
}
301+
},
302+
_ => {
303+
panic!("expected a VideoSampleEntry");
304+
},
305+
}
306+
}
307+
assert!(found_encrypted_sample_description,
308+
"Should have found an encrypted sample description");
309+
}
310+
311+
for pssh in context.psshs {
312+
assert_eq!(pssh.system_id, system_id);
313+
for kid_id in pssh.kid {
314+
assert_eq!(kid_id, kid);
315+
}
316+
assert!(pssh.data.is_empty());
317+
assert_eq!(pssh.box_content, pssh_box);
318+
}
319+
}
320+
321+
#[test]
322+
fn public_video_cbcs() {
323+
let system_id =
324+
vec![0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
325+
0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b];
326+
327+
let kid =
328+
vec![0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04,
329+
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x21];
330+
331+
let default_iv =
332+
vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
333+
0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
334+
335+
let pssh_box =
336+
vec![0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
337+
0x01, 0x00, 0x00, 0x00, 0x10, 0x77, 0xef, 0xec,
338+
0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
339+
0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00, 0x00, 0x01,
340+
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04,
341+
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x21,
342+
0x00, 0x00, 0x00, 0x00];
343+
344+
let mut fd = File::open(VIDEO_EME_CBCS_MP4).expect("Unknown file");
345+
let mut buf = Vec::new();
346+
fd.read_to_end(&mut buf).expect("File error");
347+
348+
let mut c = Cursor::new(&buf);
349+
let mut context = mp4::MediaContext::new();
350+
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
351+
for track in context.tracks {
352+
let stsd = track.stsd.expect("expected an stsd");
353+
assert_eq!(stsd.descriptions.len(), 2);
354+
let mut found_encrypted_sample_description = false;
355+
for description in stsd.descriptions {
356+
match description {
357+
mp4::SampleEntry::Video(ref v) => {
358+
assert_eq!(v.width, 400);
359+
assert_eq!(v.height, 300);
360+
if let Some(p) = v.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
361+
found_encrypted_sample_description = true;
362+
assert_eq!(p.code_name, "avc1");
363+
if let Some(ref tenc) = p.tenc {
364+
assert!(tenc.is_encrypted > 0);
365+
assert_eq!(tenc.iv_size, 0);
366+
assert_eq!(tenc.kid, kid);
367+
assert_eq!(tenc.crypt_byte_block_count, Some(1));
368+
assert_eq!(tenc.skip_byte_block_count, Some(9));
369+
assert_eq!(tenc.constant_iv, Some(default_iv.clone()));
370+
} else {
371+
assert!(false, "Invalid test condition");
372+
}
373+
}
374+
},
375+
_ => {
376+
panic!("expected a VideoSampleEntry");
377+
},
378+
}
379+
}
380+
assert!(found_encrypted_sample_description,
381+
"Should have found an encrypted sample description");
382+
}
383+
384+
for pssh in context.psshs {
385+
assert_eq!(pssh.system_id, system_id);
386+
for kid_id in pssh.kid {
387+
assert_eq!(kid_id, kid);
388+
}
389+
assert!(pssh.data.is_empty());
390+
assert_eq!(pssh.box_content, pssh_box);
391+
}
392+
}
393+
236394
#[test]
237395
fn public_video_av1() {
238396
let mut fd = File::open(VIDEO_AV1_MP4).expect("Unknown file");
1.07 KB
Binary file not shown.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
extern crate mp4parse_capi;
2+
use std::io::Read;
3+
use mp4parse_capi::*;
4+
5+
extern 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 mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
8+
match input.read(&mut buf) {
9+
Ok(n) => n as isize,
10+
Err(_) => -1,
11+
}
12+
}
13+
14+
#[test]
15+
fn parse_cenc() {
16+
let mut file = std::fs::File::open("tests/short-cenc.mp4").expect("Unknown file");
17+
let io = Mp4parseIo {
18+
read: Some(buf_read),
19+
userdata: &mut file as *mut _ as *mut std::os::raw::c_void
20+
};
21+
22+
unsafe {
23+
let parser = mp4parse_new(&io);
24+
25+
let mut rv = mp4parse_read(parser);
26+
assert_eq!(rv, Mp4parseStatus::Ok);
27+
let mut counts: u32 = 0;
28+
rv = mp4parse_get_track_count(parser, &mut counts);
29+
assert_eq!(rv, Mp4parseStatus::Ok);
30+
assert_eq!(counts, 2);
31+
32+
// Make sure we have a video track and it's at index 0
33+
let mut video_track_info = Mp4parseTrackInfo::default();
34+
rv = mp4parse_get_track_info(parser, 0, &mut video_track_info);
35+
assert_eq!(rv, Mp4parseStatus::Ok);
36+
assert_eq!(video_track_info.track_type, Mp4parseTrackType::Video);
37+
38+
// Make sure we have a audio track and it's at index 1
39+
let mut audio_track_info = Mp4parseTrackInfo::default();
40+
rv = mp4parse_get_track_info(parser, 1, &mut audio_track_info);
41+
assert_eq!(rv, Mp4parseStatus::Ok);
42+
assert_eq!(audio_track_info.track_type, Mp4parseTrackType::Audio);
43+
44+
// Verify video track and crypto information
45+
let mut video = Mp4parseTrackVideoInfo::default();
46+
rv = mp4parse_get_track_video_info(parser, 0, &mut video);
47+
assert_eq!(rv, Mp4parseStatus::Ok);
48+
assert_eq!(video.sample_info_count, 1);
49+
assert_eq!((*video.sample_info).codec_type, Mp4parseCodec::Avc);
50+
assert_eq!((*video.sample_info).image_width, 320);
51+
assert_eq!((*video.sample_info).image_height, 240);
52+
let protected_data = &(*video.sample_info).protected_data;
53+
assert_eq!(protected_data.is_encrypted, 0x01);
54+
assert_eq!(protected_data.iv_size, 16);
55+
assert_eq!(protected_data.kid.length, 16);
56+
let expected_kid = [0x7e, 0x57, 0x1d, 0x01, 0x7e, 0x57, 0x1d, 0x01,
57+
0x7e, 0x57, 0x1d, 0x01, 0x7e, 0x57, 0x1d, 0x01];
58+
for (i, expected_byte) in expected_kid.iter().enumerate() {
59+
assert_eq!(&(*protected_data.kid.data.offset(i as isize)), expected_byte);
60+
}
61+
assert_eq!(protected_data.crypt_byte_block, 0);
62+
assert_eq!(protected_data.skip_byte_block, 0);
63+
assert_eq!(protected_data.constant_iv.length, 0);
64+
65+
// Verify audio track and crypto information
66+
let mut audio = Mp4parseTrackAudioInfo::default();
67+
rv = mp4parse_get_track_audio_info(parser, 1, &mut audio);
68+
assert_eq!(rv, Mp4parseStatus::Ok);
69+
assert_eq!(audio.sample_info_count, 1);
70+
assert_eq!((*audio.sample_info).codec_type, Mp4parseCodec::Aac);
71+
assert_eq!((*audio.sample_info).channels, 2);
72+
assert_eq!((*audio.sample_info).bit_depth, 16);
73+
assert_eq!((*audio.sample_info).sample_rate, 44100);
74+
let protected_data = &(*audio.sample_info).protected_data;
75+
assert_eq!(protected_data.is_encrypted, 0x01);
76+
assert_eq!(protected_data.iv_size, 16);
77+
assert_eq!(protected_data.kid.length, 16);
78+
let expected_kid = [0x7e, 0x57, 0x1d, 0x02, 0x7e, 0x57, 0x1d, 0x02,
79+
0x7e, 0x57, 0x1d, 0x02, 0x7e, 0x57, 0x1d, 0x02];
80+
for (i, expected_byte) in expected_kid.iter().enumerate() {
81+
assert_eq!(&(*protected_data.kid.data.offset(i as isize)), expected_byte);
82+
}
83+
assert_eq!(protected_data.crypt_byte_block, 0);
84+
assert_eq!(protected_data.skip_byte_block, 0);
85+
assert_eq!(protected_data.constant_iv.length, 0);
86+
}
87+
}
88+
89+
#[test]
90+
fn parse_cbcs() {
91+
let mut file = std::fs::File::open("tests/bipbop_cbcs_video_init.mp4").expect("Unknown file");
92+
let io = Mp4parseIo {
93+
read: Some(buf_read),
94+
userdata: &mut file as *mut _ as *mut std::os::raw::c_void
95+
};
96+
97+
unsafe {
98+
let parser = mp4parse_new(&io);
99+
100+
let mut rv = mp4parse_read(parser);
101+
assert_eq!(rv, Mp4parseStatus::Ok);
102+
let mut counts: u32 = 0;
103+
rv = mp4parse_get_track_count(parser, &mut counts);
104+
assert_eq!(rv, Mp4parseStatus::Ok);
105+
assert_eq!(counts, 1);
106+
107+
// Make sure we have a video track
108+
let mut video_track_info = Mp4parseTrackInfo::default();
109+
rv = mp4parse_get_track_info(parser, 0, &mut video_track_info);
110+
assert_eq!(rv, Mp4parseStatus::Ok);
111+
assert_eq!(video_track_info.track_type, Mp4parseTrackType::Video);
112+
113+
// Verify video track and crypto information
114+
let mut video = Mp4parseTrackVideoInfo::default();
115+
rv = mp4parse_get_track_video_info(parser, 0, &mut video);
116+
assert_eq!(rv, Mp4parseStatus::Ok);
117+
assert_eq!(video.sample_info_count, 2);
118+
assert_eq!((*video.sample_info).codec_type, Mp4parseCodec::Avc);
119+
assert_eq!((*video.sample_info).image_width, 400);
120+
assert_eq!((*video.sample_info).image_height, 300);
121+
let protected_data = &(*video.sample_info).protected_data;
122+
assert_eq!(protected_data.is_encrypted, 0x01);
123+
assert_eq!(protected_data.iv_size, 0);
124+
assert_eq!(protected_data.kid.length, 16);
125+
let expected_kid = [0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04,
126+
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x21];
127+
for (i, expected_byte) in expected_kid.iter().enumerate() {
128+
assert_eq!(&(*protected_data.kid.data.offset(i as isize)), expected_byte);
129+
}
130+
assert_eq!(protected_data.crypt_byte_block, 1);
131+
assert_eq!(protected_data.skip_byte_block, 9);
132+
assert_eq!(protected_data.constant_iv.length, 16);
133+
let expected_iv = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
134+
0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
135+
for (i, expected_byte) in expected_iv.iter().enumerate() {
136+
assert_eq!(&(*protected_data.constant_iv.data.offset(i as isize)), expected_byte);
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)