extern crate mp4parse as mp4;
use mp4::Error;
use std::convert::TryInto;
use std::fs::File;
use std::io::{Cursor, Read};
use std::path::Path;
static MINI_MP4: &str = "tests/minimal.mp4";
static MINI_MP4_WITH_METADATA: &str = "tests/metadata.mp4";
static MINI_MP4_WITH_METADATA_STD_GENRE: &str = "tests/metadata_gnre.mp4";
static AUDIO_EME_CENC_MP4: &str = "tests/bipbop-cenc-audioinit.mp4";
static VIDEO_EME_CENC_MP4: &str = "tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4";
static AUDIO_EME_CBCS_MP4: &str = "tests/bipbop_cbcs_audio_init.mp4";
static VIDEO_EME_CBCS_MP4: &str = "tests/bipbop_cbcs_video_init.mp4";
static VIDEO_AV1_MP4: &str = "tests/tiny_av1.mp4";
static IMAGE_AVIF: &str = "av1-avif/testFiles/Microsoft/Monochrome.avif";
static IMAGE_AVIF_EXTENTS: &str = "tests/kodim-extents.avif";
static IMAGE_AVIF_ALPHA: &str = "tests/bug-1661347.avif";
static IMAGE_AVIF_CORRUPT: &str = "tests/corrupt/bug-1655846.avif";
static IMAGE_AVIF_CORRUPT_2: &str = "tests/corrupt/bug-1661347.avif";
static IMAGE_AVIF_CORRUPT_3: &str = "tests/corrupt/bad-ipma-version.avif";
static IMAGE_AVIF_CORRUPT_4: &str = "tests/corrupt/bad-ipma-flags.avif";
static IMAGE_AVIF_GRID: &str = "av1-avif/testFiles/Microsoft/Summer_in_Tomsk_720p_5x4_grid.avif";
static AVIF_TEST_DIRS: &[&str] = &["tests", "av1-avif/testFiles"];
static AVIF_CORRUPT_IMAGES: &str = "tests/corrupt";
#[test]
#[allow(clippy::cognitive_complexity)] fn public_api() {
let mut fd = File::open(MINI_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000)));
for track in context.tracks {
match track.track_type {
mp4::TrackType::Video => {
assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0)));
assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0)));
assert_eq!(track.media_time, Some(mp4::TrackScaledTime(0, 0)));
assert_eq!(track.timescale, Some(mp4::TrackTimeScale(12800, 0)));
let tkhd = track.tkhd.unwrap();
assert_eq!(tkhd.disabled, false);
assert_eq!(tkhd.duration, 40);
assert_eq!(tkhd.width, 20_971_520);
assert_eq!(tkhd.height, 15_728_640);
let stsd = track.stsd.expect("expected an stsd");
let v = match stsd.descriptions.first().expect("expected a SampleEntry") {
mp4::SampleEntry::Video(v) => v,
_ => panic!("expected a VideoSampleEntry"),
};
assert_eq!(v.width, 320);
assert_eq!(v.height, 240);
assert_eq!(
match v.codec_specific {
mp4::VideoCodecSpecific::AVCConfig(ref avc) => {
assert!(!avc.is_empty());
"AVC"
}
mp4::VideoCodecSpecific::VPxConfig(ref vpx) => {
assert!(vpx.bit_depth > 0);
assert!(vpx.colour_primaries > 0);
assert!(vpx.chroma_subsampling > 0);
assert!(!vpx.codec_init.is_empty());
"VPx"
}
mp4::VideoCodecSpecific::ESDSConfig(ref mp4v) => {
assert!(!mp4v.is_empty());
"MP4V"
}
mp4::VideoCodecSpecific::AV1Config(ref _av1c) => {
"AV1"
}
},
"AVC"
);
}
mp4::TrackType::Audio => {
assert_eq!(track.duration, Some(mp4::TrackScaledTime(2944, 1)));
assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0)));
assert_eq!(track.media_time, Some(mp4::TrackScaledTime(1024, 1)));
assert_eq!(track.timescale, Some(mp4::TrackTimeScale(48000, 1)));
let tkhd = track.tkhd.unwrap();
assert_eq!(tkhd.disabled, false);
assert_eq!(tkhd.duration, 62);
assert_eq!(tkhd.width, 0);
assert_eq!(tkhd.height, 0);
let stsd = track.stsd.expect("expected an stsd");
let a = match stsd.descriptions.first().expect("expected a SampleEntry") {
mp4::SampleEntry::Audio(a) => a,
_ => panic!("expected a AudioSampleEntry"),
};
assert_eq!(
match a.codec_specific {
mp4::AudioCodecSpecific::ES_Descriptor(ref esds) => {
assert_eq!(esds.audio_codec, mp4::CodecType::AAC);
assert_eq!(esds.audio_sample_rate.unwrap(), 48000);
assert_eq!(esds.audio_object_type.unwrap(), 2);
"ES"
}
mp4::AudioCodecSpecific::FLACSpecificBox(ref flac) => {
assert!(!flac.blocks.is_empty());
assert_eq!(flac.blocks[0].block_type, 0);
assert_eq!(flac.blocks[0].data.len(), 34);
"FLAC"
}
mp4::AudioCodecSpecific::OpusSpecificBox(ref opus) => {
assert!(opus.version > 0);
"Opus"
}
mp4::AudioCodecSpecific::ALACSpecificBox(ref alac) => {
assert!(alac.data.len() == 24 || alac.data.len() == 48);
"ALAC"
}
mp4::AudioCodecSpecific::MP3 => {
"MP3"
}
mp4::AudioCodecSpecific::LPCM => {
"LPCM"
}
},
"ES"
);
assert!(a.samplesize > 0);
assert!(a.samplerate > 0.0);
}
mp4::TrackType::Metadata | mp4::TrackType::Unknown => {}
}
}
}
#[test]
#[allow(clippy::cognitive_complexity)] fn public_metadata() {
let mut fd = File::open(MINI_MP4_WITH_METADATA).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
let udta = context
.userdata
.expect("didn't find udta")
.expect("failed to parse udta");
let meta = udta.meta.expect("didn't find meta");
assert_eq!(meta.title.unwrap(), "Title");
assert_eq!(meta.artist.unwrap(), "Artist");
assert_eq!(meta.album_artist.unwrap(), "Album Artist");
assert_eq!(meta.comment.unwrap(), "Comments");
assert_eq!(meta.year.unwrap(), "2019");
assert_eq!(
meta.genre.unwrap(),
mp4::Genre::CustomGenre("Custom Genre".try_into().unwrap())
);
assert_eq!(meta.encoder.unwrap(), "Lavf56.40.101");
assert_eq!(meta.encoded_by.unwrap(), "Encoded-by");
assert_eq!(meta.copyright.unwrap(), "Copyright");
assert_eq!(meta.track_number.unwrap(), 3);
assert_eq!(meta.total_tracks.unwrap(), 6);
assert_eq!(meta.disc_number.unwrap(), 5);
assert_eq!(meta.total_discs.unwrap(), 10);
assert_eq!(meta.beats_per_minute.unwrap(), 128);
assert_eq!(meta.composer.unwrap(), "Composer");
assert_eq!(meta.compilation.unwrap(), true);
assert_eq!(meta.gapless_playback.unwrap(), false);
assert_eq!(meta.podcast.unwrap(), false);
assert_eq!(meta.advisory.unwrap(), mp4::AdvisoryRating::Clean);
assert_eq!(meta.media_type.unwrap(), mp4::MediaType::Normal);
assert_eq!(meta.rating.unwrap(), "50");
assert_eq!(meta.grouping.unwrap(), "Grouping");
assert_eq!(meta.category.unwrap(), "Category");
assert_eq!(meta.keyword.unwrap(), "Keyword");
assert_eq!(meta.description.unwrap(), "Description");
assert_eq!(meta.lyrics.unwrap(), "Lyrics");
assert_eq!(meta.long_description.unwrap(), "Long Description");
assert_eq!(meta.tv_episode_name.unwrap(), "Episode Name");
assert_eq!(meta.tv_network_name.unwrap(), "Network Name");
assert_eq!(meta.tv_episode_number.unwrap(), 15);
assert_eq!(meta.tv_season.unwrap(), 10);
assert_eq!(meta.tv_show_name.unwrap(), "Show Name");
assert_eq!(meta.hd_video.unwrap(), true);
assert_eq!(meta.owner.unwrap(), "Owner");
assert_eq!(meta.sort_name.unwrap(), "Sort Name");
assert_eq!(meta.sort_album.unwrap(), "Sort Album");
assert_eq!(meta.sort_artist.unwrap(), "Sort Artist");
assert_eq!(meta.sort_album_artist.unwrap(), "Sort Album Artist");
assert_eq!(meta.sort_composer.unwrap(), "Sort Composer");
let covers = meta.cover_art.unwrap();
let cover = &covers[0];
let mut bytes = [0u8; 4];
bytes[0] = cover[0];
bytes[1] = cover[1];
bytes[2] = cover[2];
assert_eq!(u32::from_le_bytes(bytes), 0x00ff_d8ff);
}
#[test]
#[allow(clippy::cognitive_complexity)] fn public_metadata_gnre() {
let mut fd = File::open(MINI_MP4_WITH_METADATA_STD_GENRE).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
let udta = context
.userdata
.expect("didn't find udta")
.expect("failed to parse udta");
let meta = udta.meta.expect("didn't find meta");
assert_eq!(meta.title.unwrap(), "Title");
assert_eq!(meta.artist.unwrap(), "Artist");
assert_eq!(meta.album_artist.unwrap(), "Album Artist");
assert_eq!(meta.comment.unwrap(), "Comments");
assert_eq!(meta.year.unwrap(), "2019");
assert_eq!(meta.genre.unwrap(), mp4::Genre::StandardGenre(3));
assert_eq!(meta.encoder.unwrap(), "Lavf56.40.101");
assert_eq!(meta.encoded_by.unwrap(), "Encoded-by");
assert_eq!(meta.copyright.unwrap(), "Copyright");
assert_eq!(meta.track_number.unwrap(), 3);
assert_eq!(meta.total_tracks.unwrap(), 6);
assert_eq!(meta.disc_number.unwrap(), 5);
assert_eq!(meta.total_discs.unwrap(), 10);
assert_eq!(meta.beats_per_minute.unwrap(), 128);
assert_eq!(meta.composer.unwrap(), "Composer");
assert_eq!(meta.compilation.unwrap(), true);
assert_eq!(meta.gapless_playback.unwrap(), false);
assert_eq!(meta.podcast.unwrap(), false);
assert_eq!(meta.advisory.unwrap(), mp4::AdvisoryRating::Clean);
assert_eq!(meta.media_type.unwrap(), mp4::MediaType::Normal);
assert_eq!(meta.rating.unwrap(), "50");
assert_eq!(meta.grouping.unwrap(), "Grouping");
assert_eq!(meta.category.unwrap(), "Category");
assert_eq!(meta.keyword.unwrap(), "Keyword");
assert_eq!(meta.description.unwrap(), "Description");
assert_eq!(meta.lyrics.unwrap(), "Lyrics");
assert_eq!(meta.long_description.unwrap(), "Long Description");
assert_eq!(meta.tv_episode_name.unwrap(), "Episode Name");
assert_eq!(meta.tv_network_name.unwrap(), "Network Name");
assert_eq!(meta.tv_episode_number.unwrap(), 15);
assert_eq!(meta.tv_season.unwrap(), 10);
assert_eq!(meta.tv_show_name.unwrap(), "Show Name");
assert_eq!(meta.hd_video.unwrap(), true);
assert_eq!(meta.owner.unwrap(), "Owner");
assert_eq!(meta.sort_name.unwrap(), "Sort Name");
assert_eq!(meta.sort_album.unwrap(), "Sort Album");
assert_eq!(meta.sort_artist.unwrap(), "Sort Artist");
assert_eq!(meta.sort_album_artist.unwrap(), "Sort Album Artist");
assert_eq!(meta.sort_composer.unwrap(), "Sort Composer");
let covers = meta.cover_art.unwrap();
let cover = &covers[0];
let mut bytes = [0u8; 4];
bytes[0] = cover[0];
bytes[1] = cover[1];
bytes[2] = cover[2];
assert_eq!(u32::from_le_bytes(bytes), 0x00ff_d8ff);
}
#[test]
fn public_audio_tenc() {
let kid = vec![
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d,
0x04,
];
let mut fd = File::open(AUDIO_EME_CENC_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
for track in context.tracks {
let stsd = track.stsd.expect("expected an stsd");
let a = match stsd.descriptions.first().expect("expected a SampleEntry") {
mp4::SampleEntry::Audio(a) => a,
_ => panic!("expected a AudioSampleEntry"),
};
assert_eq!(a.codec_type, mp4::CodecType::EncryptedAudio);
match a.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
Some(ref p) => {
assert_eq!(p.original_format, b"mp4a");
if let Some(ref schm) = p.scheme_type {
assert_eq!(schm.scheme_type, b"cenc");
} else {
panic!("Expected scheme type info");
}
if let Some(ref tenc) = p.tenc {
assert!(tenc.is_encrypted > 0);
assert_eq!(tenc.iv_size, 16);
assert_eq!(tenc.kid, kid);
assert_eq!(tenc.crypt_byte_block_count, None);
assert_eq!(tenc.skip_byte_block_count, None);
assert_eq!(tenc.constant_iv, None);
} else {
panic!("Invalid test condition");
}
}
_ => {
panic!("Invalid test condition");
}
}
}
}
#[test]
fn public_video_cenc() {
let system_id = vec![
0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb,
0x4b,
];
let kid = vec![
0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d,
0x11,
];
let pssh_box = vec![
0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x01, 0x00, 0x00, 0x00, 0x10, 0x77, 0xef,
0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00,
0x00, 0x01, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e,
0x57, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
];
let mut fd = File::open(VIDEO_EME_CENC_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
for track in context.tracks {
let stsd = track.stsd.expect("expected an stsd");
let v = match stsd.descriptions.first().expect("expected a SampleEntry") {
mp4::SampleEntry::Video(ref v) => v,
_ => panic!("expected a VideoSampleEntry"),
};
assert_eq!(v.codec_type, mp4::CodecType::EncryptedVideo);
match v.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
Some(ref p) => {
assert_eq!(p.original_format, b"avc1");
if let Some(ref schm) = p.scheme_type {
assert_eq!(schm.scheme_type, b"cenc");
} else {
panic!("Expected scheme type info");
}
if let Some(ref tenc) = p.tenc {
assert!(tenc.is_encrypted > 0);
assert_eq!(tenc.iv_size, 16);
assert_eq!(tenc.kid, kid);
assert_eq!(tenc.crypt_byte_block_count, None);
assert_eq!(tenc.skip_byte_block_count, None);
assert_eq!(tenc.constant_iv, None);
} else {
panic!("Invalid test condition");
}
}
_ => {
panic!("Invalid test condition");
}
}
}
for pssh in context.psshs {
assert_eq!(pssh.system_id, system_id);
for kid_id in pssh.kid {
assert_eq!(kid_id, kid);
}
assert!(pssh.data.is_empty());
assert_eq!(pssh.box_content, pssh_box);
}
}
#[test]
fn public_audio_cbcs() {
let system_id = vec![
0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb,
0x4b,
];
let kid = vec![
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d,
0x21,
];
let default_iv = vec![
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
0x66,
];
let pssh_box = vec![
0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x01, 0x00, 0x00, 0x00, 0x10, 0x77, 0xef,
0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00,
0x00, 0x01, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e,
0x57, 0x1d, 0x21, 0x00, 0x00, 0x00, 0x00,
];
let mut fd = File::open(AUDIO_EME_CBCS_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
for track in context.tracks {
let stsd = track.stsd.expect("expected an stsd");
assert_eq!(stsd.descriptions.len(), 2);
let mut found_encrypted_sample_description = false;
for description in stsd.descriptions {
match description {
mp4::SampleEntry::Audio(ref a) => {
if let Some(p) = a.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
found_encrypted_sample_description = true;
assert_eq!(p.original_format, b"mp4a");
if let Some(ref schm) = p.scheme_type {
assert_eq!(schm.scheme_type, b"cbcs");
} else {
panic!("Expected scheme type info");
}
if let Some(ref tenc) = p.tenc {
assert!(tenc.is_encrypted > 0);
assert_eq!(tenc.iv_size, 0);
assert_eq!(tenc.kid, kid);
assert_eq!(tenc.crypt_byte_block_count, Some(0));
assert_eq!(tenc.skip_byte_block_count, Some(0));
assert_eq!(tenc.constant_iv, Some(default_iv.clone().into()));
} else {
panic!("Invalid test condition");
}
}
}
_ => {
panic!("expected a VideoSampleEntry");
}
}
}
assert!(
found_encrypted_sample_description,
"Should have found an encrypted sample description"
);
}
for pssh in context.psshs {
assert_eq!(pssh.system_id, system_id);
for kid_id in pssh.kid {
assert_eq!(kid_id, kid);
}
assert!(pssh.data.is_empty());
assert_eq!(pssh.box_content, pssh_box);
}
}
#[test]
#[allow(clippy::cognitive_complexity)] fn public_video_cbcs() {
let system_id = vec![
0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb,
0x4b,
];
let kid = vec![
0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d,
0x21,
];
let default_iv = vec![
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
0x66,
];
let pssh_box = vec![
0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x01, 0x00, 0x00, 0x00, 0x10, 0x77, 0xef,
0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00,
0x00, 0x01, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04, 0x7e,
0x57, 0x1d, 0x21, 0x00, 0x00, 0x00, 0x00,
];
let mut fd = File::open(VIDEO_EME_CBCS_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
for track in context.tracks {
let stsd = track.stsd.expect("expected an stsd");
assert_eq!(stsd.descriptions.len(), 2);
let mut found_encrypted_sample_description = false;
for description in stsd.descriptions {
match description {
mp4::SampleEntry::Video(ref v) => {
assert_eq!(v.width, 400);
assert_eq!(v.height, 300);
if let Some(p) = v.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
found_encrypted_sample_description = true;
assert_eq!(p.original_format, b"avc1");
if let Some(ref schm) = p.scheme_type {
assert_eq!(schm.scheme_type, b"cbcs");
} else {
panic!("Expected scheme type info");
}
if let Some(ref tenc) = p.tenc {
assert!(tenc.is_encrypted > 0);
assert_eq!(tenc.iv_size, 0);
assert_eq!(tenc.kid, kid);
assert_eq!(tenc.crypt_byte_block_count, Some(1));
assert_eq!(tenc.skip_byte_block_count, Some(9));
assert_eq!(tenc.constant_iv, Some(default_iv.clone().into()));
} else {
panic!("Invalid test condition");
}
}
}
_ => {
panic!("expected a VideoSampleEntry");
}
}
}
assert!(
found_encrypted_sample_description,
"Should have found an encrypted sample description"
);
}
for pssh in context.psshs {
assert_eq!(pssh.system_id, system_id);
for kid_id in pssh.kid {
assert_eq!(kid_id, kid);
}
assert!(pssh.data.is_empty());
assert_eq!(pssh.box_content, pssh_box);
}
}
#[test]
#[allow(clippy::cognitive_complexity)] fn public_video_av1() {
let mut fd = File::open(VIDEO_AV1_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
for track in context.tracks {
assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0)));
assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0)));
assert_eq!(track.media_time, Some(mp4::TrackScaledTime(0, 0)));
assert_eq!(track.timescale, Some(mp4::TrackTimeScale(12288, 0)));
let tkhd = track.tkhd.unwrap();
assert_eq!(tkhd.disabled, false);
assert_eq!(tkhd.duration, 42);
assert_eq!(tkhd.width, 4_194_304);
assert_eq!(tkhd.height, 4_194_304);
let stsd = track.stsd.expect("expected an stsd");
let v = match stsd.descriptions.first().expect("expected a SampleEntry") {
mp4::SampleEntry::Video(ref v) => v,
_ => panic!("expected a VideoSampleEntry"),
};
assert_eq!(v.codec_type, mp4::CodecType::AV1);
assert_eq!(v.width, 64);
assert_eq!(v.height, 64);
match v.codec_specific {
mp4::VideoCodecSpecific::AV1Config(ref av1c) => {
assert_eq!(av1c.profile, 0);
assert_eq!(av1c.level, 0);
assert_eq!(av1c.tier, 0);
assert_eq!(av1c.bit_depth, 8);
assert_eq!(av1c.monochrome, false);
assert_eq!(av1c.chroma_subsampling_x, 1);
assert_eq!(av1c.chroma_subsampling_y, 1);
assert_eq!(av1c.chroma_sample_position, 0);
assert_eq!(av1c.initial_presentation_delay_present, false);
assert_eq!(av1c.initial_presentation_delay_minus_one, 0);
}
_ => panic!("Invalid test condition"),
}
}
}
#[test]
fn public_mp4_bug_1185230() {
let input = &mut File::open("tests/test_case_1185230.mp4").expect("Unknown file");
let context = mp4::read_mp4(input).expect("read_mp4 failed");
let number_video_tracks = context
.tracks
.iter()
.filter(|t| t.track_type == mp4::TrackType::Video)
.count();
let number_audio_tracks = context
.tracks
.iter()
.filter(|t| t.track_type == mp4::TrackType::Audio)
.count();
assert_eq!(number_video_tracks, 2);
assert_eq!(number_audio_tracks, 2);
}
#[test]
fn public_avif_primary_item() {
let input = &mut File::open(IMAGE_AVIF).expect("Unknown file");
let context = mp4::read_avif(input).expect("read_avif failed");
assert_eq!(context.primary_item().len(), 6979);
assert_eq!(context.primary_item()[0..4], [0x12, 0x00, 0x0a, 0x0a]);
}
#[test]
fn public_avif_primary_item_split_extents() {
let input = &mut File::open(IMAGE_AVIF_EXTENTS).expect("Unknown file");
let context = mp4::read_avif(input).expect("read_avif failed");
assert_eq!(context.primary_item().len(), 4387);
}
#[test]
fn public_avif_alpha_item() {
let input = &mut File::open(IMAGE_AVIF_ALPHA).expect("Unknown file");
let context = mp4::read_avif(input).expect("read_avif failed");
assert!(context.alpha_item().is_some());
assert!(!context.premultiplied_alpha);
}
#[test]
fn public_avif_bug_1655846() {
let input = &mut File::open(IMAGE_AVIF_CORRUPT).expect("Unknown file");
assert!(mp4::read_avif(input).is_err());
}
#[test]
fn public_avif_bug_1661347() {
let input = &mut File::open(IMAGE_AVIF_CORRUPT_2).expect("Unknown file");
assert!(mp4::read_avif(input).is_err());
}
fn assert_invalid_data<T: std::fmt::Debug>(result: mp4::Result<T>, expected_msg: &str) {
match result {
Err(Error::InvalidData(msg)) if msg == expected_msg => {}
r => panic!(
"Expected Err(Error::InvalidData({:?})), found {:?}",
expected_msg, r
),
}
}
#[test]
fn public_avif_bad_ipma_version() {
let input = &mut File::open(IMAGE_AVIF_CORRUPT_3).expect("Unknown file");
let expected_msg = "The version 0 should be used unless 32-bit item_ID values are needed";
assert_invalid_data(mp4::read_avif(input), expected_msg);
}
#[test]
fn public_avif_bad_ipma_flags() {
let input = &mut File::open(IMAGE_AVIF_CORRUPT_4).expect("Unknown file");
let expected_msg = "flags should be equal to 0 unless there are more than 127 properties in the ItemPropertyContainerBox";
assert_invalid_data(mp4::read_avif(input), expected_msg);
}
#[test]
#[ignore] fn public_avif_primary_item_is_grid() {
let input = &mut File::open(IMAGE_AVIF_GRID).expect("Unknown file");
mp4::read_avif(input).expect("read_avif failed");
}
#[test]
fn public_avif_read_samples() {
for dir in AVIF_TEST_DIRS {
for entry in walkdir::WalkDir::new(dir) {
let entry = entry.expect("AVIF entry");
let path = entry.path();
if !path.is_file() || path.extension().unwrap_or_default() != "avif" {
eprintln!("Skipping {:?}", path);
continue; }
if path.parent().unwrap() == Path::new(AVIF_CORRUPT_IMAGES) {
eprintln!("Skipping {:?}", path);
continue;
}
if path == Path::new(IMAGE_AVIF_GRID) {
eprintln!("Skipping {:?}", path);
continue; }
println!("parsing {:?}", path);
let input = &mut File::open(path).expect("Unknow file");
mp4::read_avif(input).expect("read_avif failed");
}
}
}