diff options
Diffstat (limited to 'third_party/rust/mp4parse/src/tests.rs')
-rw-r--r-- | third_party/rust/mp4parse/src/tests.rs | 1347 |
1 files changed, 1347 insertions, 0 deletions
diff --git a/third_party/rust/mp4parse/src/tests.rs b/third_party/rust/mp4parse/src/tests.rs new file mode 100644 index 0000000000..a628e2675e --- /dev/null +++ b/third_party/rust/mp4parse/src/tests.rs @@ -0,0 +1,1347 @@ +//! Module for parsing ISO Base Media Format aka video/mp4 streams. +//! Internal unit tests. + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use super::read_mp4; +use super::ParseStrictness; +use super::{Error, Status}; +use fallible_collections::TryRead as _; + +use std::convert::TryInto as _; +use std::io::Cursor; +use std::io::Read as _; +use test_assembler::*; + +use crate::boxes::BoxType; + +enum BoxSize { + Short(u32), + Long(u64), + UncheckedShort(u32), + UncheckedLong(u64), + Auto, +} + +#[allow(clippy::trivially_copy_pass_by_ref)] // TODO: Consider reworking to a copy +fn make_box<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>> +where + F: Fn(Section) -> Section, +{ + let mut section = Section::new(); + let box_size = Label::new(); + section = match size { + BoxSize::Short(size) | BoxSize::UncheckedShort(size) => section.B32(size), + BoxSize::Long(_) | BoxSize::UncheckedLong(_) => section.B32(1), + BoxSize::Auto => section.B32(&box_size), + }; + section = section.append_bytes(name); + section = match size { + // The spec allows the 32-bit size to be 0 to indicate unknown + // length streams. It's not clear if this is valid when using a + // 64-bit size, so prohibit it for now. + BoxSize::Long(size) => { + assert!(size > 0); + section.B64(size) + } + BoxSize::UncheckedLong(size) => section.B64(size), + _ => section, + }; + section = func(section); + match size { + BoxSize::Short(size) => { + if size > 0 { + assert_eq!(u64::from(size), section.size()) + } + } + BoxSize::Long(size) => assert_eq!(size, section.size()), + BoxSize::Auto => { + assert!( + section.size() <= u64::from(u32::max_value()), + "Tried to use a long box with BoxSize::Auto" + ); + box_size.set_const(section.size()); + } + // Skip checking BoxSize::Unchecked* cases. + _ => (), + } + Cursor::new(section.get_contents().unwrap()) +} + +fn make_uuid_box<F>(size: BoxSize, uuid: &[u8; 16], func: F) -> Cursor<Vec<u8>> +where + F: Fn(Section) -> Section, +{ + make_box(size, b"uuid", |mut s| { + for b in uuid { + s = s.B8(*b); + } + func(s) + }) +} + +#[allow(clippy::trivially_copy_pass_by_ref)] // TODO: Consider reworking to a copy +fn make_fullbox<F>(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>> +where + F: Fn(Section) -> Section, +{ + make_box(size, name, |s| func(s.B8(version).B8(0).B8(0).B8(0))) +} + +#[test] +fn read_box_header_short() { + let mut stream = make_box(BoxSize::Short(8), b"test", |s| s); + let header = super::read_box_header(&mut stream).unwrap(); + assert_eq!(header.name, BoxType::UnknownBox(0x7465_7374)); // "test" + assert_eq!(header.size, 8); + assert!(header.uuid.is_none()); +} + +#[test] +fn read_box_header_long() { + let mut stream = make_box(BoxSize::Long(16), b"test", |s| s); + let header = super::read_box_header(&mut stream).unwrap(); + assert_eq!(header.name, BoxType::UnknownBox(0x7465_7374)); // "test" + assert_eq!(header.size, 16); + assert!(header.uuid.is_none()); +} + +#[test] +fn read_box_header_short_unknown_size() { + let mut stream = make_box(BoxSize::Short(0), b"test", |s| s); + match super::read_box_header(&mut stream) { + Err(Error::Unsupported(s)) => assert_eq!(s, "unknown sized box"), + _ => panic!("unexpected result reading box with unknown size"), + }; +} + +#[test] +fn read_box_header_short_invalid_size() { + let mut stream = make_box(BoxSize::UncheckedShort(2), b"test", |s| s); + match super::read_box_header(&mut stream) { + Err(Error::InvalidData(s)) => assert_eq!(s, Status::BoxBadSize), + _ => panic!("unexpected result reading box with invalid size"), + }; +} + +#[test] +fn read_box_header_long_invalid_size() { + let mut stream = make_box(BoxSize::UncheckedLong(2), b"test", |s| s); + match super::read_box_header(&mut stream) { + Err(Error::InvalidData(s)) => assert_eq!(s, Status::BoxBadWideSize), + _ => panic!("unexpected result reading box with invalid size"), + }; +} + +#[test] +fn read_box_header_uuid() { + const HEADER_UUID: [u8; 16] = [ + 0x85, 0xc0, 0xb6, 0x87, 0x82, 0x0f, 0x11, 0xe0, 0x81, 0x11, 0xf4, 0xce, 0x46, 0x2b, 0x6a, + 0x48, + ]; + + let mut stream = make_uuid_box(BoxSize::Short(24), &HEADER_UUID, |s| s); + let mut iter = super::BoxIter::new(&mut stream); + let stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::UuidBox); + assert_eq!(stream.head.size, 24); + assert!(stream.head.uuid.is_some()); + assert_eq!(stream.head.uuid.unwrap(), HEADER_UUID); +} + +#[test] +fn read_box_header_truncated_uuid() { + const HEADER_UUID: [u8; 16] = [ + 0x85, 0xc0, 0xb6, 0x87, 0x82, 0x0f, 0x11, 0xe0, 0x81, 0x11, 0xf4, 0xce, 0x46, 0x2b, 0x6a, + 0x48, + ]; + + let mut stream = make_uuid_box(BoxSize::UncheckedShort(23), &HEADER_UUID, |s| s); + let mut iter = super::BoxIter::new(&mut stream); + let stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::UuidBox); + assert_eq!(stream.head.size, 23); + assert!(stream.head.uuid.is_none()); +} + +#[test] +fn read_box_header_uuid_past_eof() { + const HEADER_UUID: [u8; 20] = [ + 0x00, 0x00, 0x00, 0x18, // size = 24 + 0x75, 0x75, 0x69, 0x64, // type = uuid + 0x85, 0xc0, 0xb6, 0x87, 0x82, 0x0f, 0x11, 0xe0, 0x81, 0x11, 0xf4, 0xce, + ]; + + let mut cursor = Cursor::new(HEADER_UUID); + let mut iter = super::BoxIter::new(&mut cursor); + match iter.next_box() { + Ok(None) => (), + Ok(_) => panic!("unexpected box read"), + _ => panic!("unexpected error"), + }; +} + +#[test] +fn read_ftyp() { + let mut stream = make_box(BoxSize::Short(24), b"ftyp", |s| { + s.append_bytes(b"mp42") + .B32(0) // minor version + .append_bytes(b"isom") + .append_bytes(b"mp42") + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FileTypeBox); + assert_eq!(stream.head.size, 24); + let parsed = super::read_ftyp(&mut stream).unwrap(); + assert_eq!(parsed.major_brand, b"mp42"); // mp42 + assert_eq!(parsed.minor_version, 0); + assert_eq!(parsed.compatible_brands.len(), 2); + assert_eq!(parsed.compatible_brands[0], b"isom"); // isom + assert_eq!(parsed.compatible_brands[1], b"mp42"); // mp42 +} + +#[test] +fn read_truncated_ftyp() { + // We declare a 24 byte box, but only write 20 bytes. + let mut stream = make_box(BoxSize::UncheckedShort(24), b"ftyp", |s| { + s.append_bytes(b"mp42") + .B32(0) // minor version + .append_bytes(b"isom") + }); + match read_mp4(&mut stream) { + Err(Error::UnexpectedEOF) => (), + Ok(_) => panic!("expected an error result"), + _ => panic!("expected a different error result"), + } +} + +#[test] +fn read_ftyp_case() { + // Brands in BMFF are represented as a u32, so it would seem clear that + // 0x6d703432 ("mp42") is not equal to 0x4d503432 ("MP42"), but some + // demuxers treat these as case-insensitive strings, e.g. street.mp4's + // major brand is "MP42". I haven't seen case-insensitive + // compatible_brands (which we also test here), but it doesn't seem + // unlikely given the major_brand behaviour. + let mut stream = make_box(BoxSize::Auto, b"ftyp", |s| { + s.append_bytes(b"MP42") + .B32(0) // minor version + .append_bytes(b"ISOM") + .append_bytes(b"MP42") + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FileTypeBox); + assert_eq!(stream.head.size, 24); + let parsed = super::read_ftyp(&mut stream).unwrap(); + assert_eq!(parsed.major_brand, b"MP42"); + assert_eq!(parsed.minor_version, 0); + assert_eq!(parsed.compatible_brands.len(), 2); + assert_eq!(parsed.compatible_brands[0], b"ISOM"); // ISOM + assert_eq!(parsed.compatible_brands[1], b"MP42"); // MP42 +} + +#[test] +fn read_elst_v0() { + let mut stream = make_fullbox(BoxSize::Short(28), b"elst", 0, |s| { + s.B32(1) // list count + // first entry + .B32(1234) // duration + .B32(5678) // time + .B16(12) // rate integer + .B16(34) // rate fraction + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::EditListBox); + assert_eq!(stream.head.size, 28); + let parsed = super::read_elst(&mut stream).unwrap(); + assert_eq!(parsed.edits.len(), 1); + assert_eq!(parsed.edits[0].segment_duration, 1234); + assert_eq!(parsed.edits[0].media_time, 5678); + assert_eq!(parsed.edits[0].media_rate_integer, 12); + assert_eq!(parsed.edits[0].media_rate_fraction, 34); +} + +#[test] +fn read_elst_v1() { + let mut stream = make_fullbox(BoxSize::Short(56), b"elst", 1, |s| { + s.B32(2) // list count + // first entry + .B64(1234) // duration + .B64(5678) // time + .B16(12) // rate integer + .B16(34) // rate fraction + // second entry + .B64(1234) // duration + .B64(5678) // time + .B16(12) // rate integer + .B16(34) // rate fraction + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::EditListBox); + assert_eq!(stream.head.size, 56); + let parsed = super::read_elst(&mut stream).unwrap(); + assert_eq!(parsed.edits.len(), 2); + assert_eq!(parsed.edits[1].segment_duration, 1234); + assert_eq!(parsed.edits[1].media_time, 5678); + assert_eq!(parsed.edits[1].media_rate_integer, 12); + assert_eq!(parsed.edits[1].media_rate_fraction, 34); +} + +#[test] +fn read_mdhd_v0() { + let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { + s.B32(0) + .B32(0) + .B32(1234) // timescale + .B32(5678) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 32); + let parsed = super::read_mdhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mdhd_v1() { + let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { + s.B64(0) + .B64(0) + .B32(1234) // timescale + .B64(5678) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 44); + let parsed = super::read_mdhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mdhd_unknown_duration() { + let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { + s.B32(0) + .B32(0) + .B32(1234) // timescale + .B32(::std::u32::MAX) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 32); + let parsed = super::read_mdhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, ::std::u64::MAX); +} + +#[test] +fn read_mdhd_invalid_timescale() { + let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { + s.B64(0) + .B64(0) + .B32(0) // timescale + .B64(5678) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 44); + let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0)); + assert!(r.is_err()); +} + +#[test] +fn read_mvhd_v0() { + let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { + s.B32(0).B32(0).B32(1234).B32(5678).append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 108); + let parsed = super::read_mvhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mvhd_v1() { + let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { + s.B64(0).B64(0).B32(1234).B64(5678).append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 120); + let parsed = super::read_mvhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mvhd_invalid_timescale() { + let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { + s.B64(0).B64(0).B32(0).B64(5678).append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 120); + let r = super::parse_mvhd(&mut stream); + assert!(r.is_err()); +} + +#[test] +fn read_mvhd_unknown_duration() { + let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { + s.B32(0) + .B32(0) + .B32(1234) + .B32(::std::u32::MAX) + .append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 108); + let parsed = super::read_mvhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, ::std::u64::MAX); +} + +#[test] +fn read_vpcc_version_0() { + let data_length = 12u16; + let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 0, |s| { + s.B8(2) + .B8(0) + .B8(0x82) + .B8(0) + .B16(data_length) + .append_repeated(42, data_length as usize) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox); + let r = super::read_vpcc(&mut stream); + assert!(r.is_ok()); +} + +// TODO: it'd be better to find a real sample here. +#[test] +#[allow(clippy::unusual_byte_groupings)] // Allow odd grouping for test readability. +fn read_vpcc_version_1() { + let data_length = 12u16; + let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 1, |s| { + s.B8(2) // profile + .B8(0) // level + .B8(0b1000_011_0) // bitdepth (4 bits), chroma (3 bits), video full range (1 bit) + .B8(1) // color primaries + .B8(1) // transfer characteristics + .B8(1) // matrix + .B16(data_length) + .append_repeated(42, data_length as usize) + }); + + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox); + let r = super::read_vpcc(&mut stream); + match r { + Ok(vpcc) => { + assert_eq!(vpcc.bit_depth, 8); + assert_eq!(vpcc.chroma_subsampling, 3); + assert!(!vpcc.video_full_range_flag); + assert_eq!(vpcc.matrix_coefficients.unwrap(), 1); + } + _ => panic!("vpcc parsing error"), + } +} + +#[test] +fn read_hdlr() { + let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| { + s.B32(0) + .append_bytes(b"vide") + .B32(0) + .B32(0) + .B32(0) + .append_bytes(b"VideoHandler") + .B8(0) // null-terminate string + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 45); + let parsed = super::read_hdlr(&mut stream, ParseStrictness::Normal).unwrap(); + assert_eq!(parsed.handler_type, b"vide"); +} + +#[test] +fn read_hdlr_multiple_nul_in_name() { + let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| { + s.B32(0) + .append_bytes(b"vide") + .B32(0) + .B32(0) + .B32(0) + .append_bytes(b"Vide\0Handler") + .B8(0) // null-terminate string + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 45); + assert_eq!( + super::Status::from(super::read_hdlr(&mut stream, ParseStrictness::Strict)), + super::Status::Ok, + ); +} + +#[test] +fn read_hdlr_short_name() { + let mut stream = make_fullbox(BoxSize::Short(33), b"hdlr", 0, |s| { + s.B32(0).append_bytes(b"vide").B32(0).B32(0).B32(0).B8(0) // null-terminate string + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 33); + let parsed = super::read_hdlr(&mut stream, ParseStrictness::Normal).unwrap(); + assert_eq!(parsed.handler_type, b"vide"); +} + +#[test] +fn read_hdlr_unsupported_version() { + let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 1, |s| { + s.B32(0).append_bytes(b"vide").B32(0).B32(0).B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 32); + assert_eq!( + super::Status::from(super::read_hdlr(&mut stream, ParseStrictness::Normal)), + super::Status::HdlrUnsupportedVersion, + ); +} + +#[test] +fn read_hdlr_invalid_pre_defined_field() { + let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| { + s.B32(1).append_bytes(b"vide").B32(0).B32(0).B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 32); + assert_eq!( + super::Status::from(super::read_hdlr(&mut stream, ParseStrictness::Strict)), + super::Status::HdlrPredefinedNonzero, + ); +} + +#[test] +fn read_hdlr_invalid_reserved_field() { + let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| { + s.B32(0).append_bytes(b"vide").B32(0).B32(1).B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 32); + assert_eq!( + super::Status::from(super::read_hdlr(&mut stream, ParseStrictness::Strict)), + super::Status::HdlrReservedNonzero, + ); +} + +#[test] +fn read_hdlr_zero_length_name() { + let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| { + s.B32(0).append_bytes(b"vide").B32(0).B32(0).B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 32); + assert_eq!( + super::Status::from(super::read_hdlr(&mut stream, ParseStrictness::Normal)), + super::Status::HdlrNameNoNul, + ); +} + +#[test] +fn read_hdlr_zero_length_name_permissive() { + let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| { + s.B32(0).append_bytes(b"vide").B32(0).B32(0).B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 32); + let parsed = super::read_hdlr(&mut stream, ParseStrictness::Permissive).unwrap(); + assert_eq!(parsed.handler_type, b"vide"); +} + +fn flac_streaminfo() -> Vec<u8> { + vec![ + 0x10, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x11, 0x00, 0x38, 0x32, 0x0a, 0xc4, 0x42, 0xf0, 0x00, + 0xc9, 0xdf, 0xae, 0xb5, 0x66, 0xfc, 0x02, 0x15, 0xa3, 0xb1, 0x54, 0x61, 0x47, 0x0f, 0xfb, + 0x05, 0x00, 0x33, 0xad, + ] +} + +#[test] +fn read_flac() { + let mut stream = make_box(BoxSize::Auto, b"fLaC", |s| { + s.append_repeated(0, 6) // reserved + .B16(1) // data reference index + .B32(0) // reserved + .B32(0) // reserved + .B16(2) // channel count + .B16(16) // bits per sample + .B16(0) // pre_defined + .B16(0) // reserved + .B32(44100 << 16) // Sample rate + .append_bytes( + &make_dfla( + FlacBlockType::StreamInfo, + true, + &flac_streaminfo(), + FlacBlockLength::Correct, + ) + .into_inner(), + ) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let r = super::read_audio_sample_entry(&mut stream); + assert!(r.is_ok()); +} + +#[derive(Clone, Copy)] +enum FlacBlockType { + StreamInfo = 0, + _Padding = 1, + _Application = 2, + _Seektable = 3, + _Comment = 4, + _Cuesheet = 5, + _Picture = 6, + _Reserved, + _Invalid = 127, +} + +enum FlacBlockLength { + Correct, + Incorrect(usize), +} + +fn make_dfla( + block_type: FlacBlockType, + last: bool, + data: &[u8], + data_length: FlacBlockLength, +) -> Cursor<Vec<u8>> { + assert!(data.len() < 1 << 24); + make_fullbox(BoxSize::Auto, b"dfLa", 0, |s| { + let flag = u32::from(last); + let size = match data_length { + FlacBlockLength::Correct => (data.len() as u32) & 0x00ff_ffff, + FlacBlockLength::Incorrect(size) => { + assert!(size < 1 << 24); + (size as u32) & 0x00ff_ffff + } + }; + let block_type = (block_type as u32) & 0x7f; + s.B32(flag << 31 | block_type << 24 | size) + .append_bytes(data) + }) +} + +#[test] +fn read_dfla() { + let mut stream = make_dfla( + FlacBlockType::StreamInfo, + true, + &flac_streaminfo(), + FlacBlockLength::Correct, + ); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FLACSpecificBox); + let dfla = super::read_dfla(&mut stream).unwrap(); + assert_eq!(dfla.version, 0); +} + +#[test] +fn long_flac_metadata() { + let streaminfo = flac_streaminfo(); + let mut stream = make_dfla( + FlacBlockType::StreamInfo, + true, + &streaminfo, + FlacBlockLength::Incorrect(streaminfo.len() + 4), + ); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FLACSpecificBox); + let r = super::read_dfla(&mut stream); + assert!(r.is_err()); +} + +#[test] +fn read_opus() { + let mut stream = make_box(BoxSize::Auto, b"Opus", |s| { + s.append_repeated(0, 6) + .B16(1) // data reference index + .B32(0) + .B32(0) + .B16(2) // channel count + .B16(16) // bits per sample + .B16(0) + .B16(0) + .B32(48000 << 16) // Sample rate is always 48 kHz for Opus. + .append_bytes(&make_dops().into_inner()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let r = super::read_audio_sample_entry(&mut stream); + assert!(r.is_ok()); +} + +fn make_dops() -> Cursor<Vec<u8>> { + make_box(BoxSize::Auto, b"dOps", |s| { + s.B8(0) // version + .B8(2) // channel count + .B16(348) // pre-skip + .B32(44100) // original sample rate + .B16(0) // gain + .B8(0) // channel mapping + }) +} + +#[test] +fn read_dops() { + let mut stream = make_dops(); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::OpusSpecificBox); + let r = super::read_dops(&mut stream); + assert!(r.is_ok()); +} + +#[test] +fn serialize_opus_header() { + let opus = super::OpusSpecificBox { + version: 0, + output_channel_count: 1, + pre_skip: 342, + input_sample_rate: 24000, + output_gain: 0, + channel_mapping_family: 0, + channel_mapping_table: None, + }; + let mut v = Vec::<u8>::new(); + super::serialize_opus_header(&opus, &mut v).unwrap(); + assert_eq!(v.len(), 19); + assert_eq!( + v, + vec![ + 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x01, 0x56, 0x01, 0xc0, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ); + let opus = super::OpusSpecificBox { + version: 0, + output_channel_count: 6, + pre_skip: 152, + input_sample_rate: 48000, + output_gain: 0, + channel_mapping_family: 1, + channel_mapping_table: Some(super::ChannelMappingTable { + stream_count: 4, + coupled_count: 2, + channel_mapping: vec![0, 4, 1, 2, 3, 5].into(), + }), + }; + let mut v = Vec::<u8>::new(); + super::serialize_opus_header(&opus, &mut v).unwrap(); + assert_eq!(v.len(), 27); + assert_eq!( + v, + vec![ + 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x06, 0x98, 0x00, 0x80, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x02, 0x00, 0x04, 0x01, 0x02, 0x03, 0x05, + ] + ); +} + +#[test] +fn read_alac() { + let mut stream = make_box(BoxSize::Auto, b"alac", |s| { + s.append_repeated(0, 6) // reserved + .B16(1) // data reference index + .B32(0) // reserved + .B32(0) // reserved + .B16(2) // channel count + .B16(16) // bits per sample + .B16(0) // pre_defined + .B16(0) // reserved + .B32(44100 << 16) // Sample rate + .append_bytes( + &make_fullbox(BoxSize::Auto, b"alac", 0, |s| s.append_bytes(&[0xfa; 24])) + .into_inner(), + ) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let r = super::read_audio_sample_entry(&mut stream); + assert!(r.is_ok()); +} + +#[test] +fn esds_limit() { + let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { + s.append_repeated(0, 6) + .B16(1) + .B32(0) + .B32(0) + .B16(2) + .B16(16) + .B16(0) + .B16(0) + .B32(48000 << 16) + .B32(8) + .append_bytes(b"esds") + .append_repeated(0, 4) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + match super::read_audio_sample_entry(&mut stream) { + Err(Error::UnexpectedEOF) => (), + Ok(_) => panic!("expected an error result"), + _ => panic!("expected a different error result"), + } +} + +#[test] +fn read_elst_zero_entries() { + let mut stream = make_fullbox(BoxSize::Auto, b"elst", 0, |s| s.B32(0).B16(12).B16(34)); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + match super::read_elst(&mut stream) { + Ok(elst) => assert_eq!(elst.edits.len(), 0), + _ => panic!("expected no error"), + } +} + +fn make_elst() -> Cursor<Vec<u8>> { + make_fullbox(BoxSize::Auto, b"elst", 1, |s| { + s.B32(1) + // first entry + .B64(1234) // duration + .B64(0xffff_ffff_ffff_ffff) // time + .B16(12) // rate integer + .B16(34) // rate fraction + }) +} + +#[test] +fn read_edts_bogus() { + // First edit list entry has a media_time of -1, so we expect a second + // edit list entry to be present to provide a valid media_time. + // Bogus edts are ignored. + let mut stream = make_box(BoxSize::Auto, b"edts", |s| { + s.append_bytes(&make_elst().into_inner()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + match super::read_edts(&mut stream, &mut track) { + Ok(_) => { + assert_eq!(track.media_time, None); + assert_eq!(track.empty_duration, None); + } + _ => panic!("expected no error"), + } +} + +#[test] +fn skip_padding_in_boxes() { + // Padding data could be added in the end of these boxes. Parser needs to skip + // them instead of returning error. + let box_names = vec![b"stts", b"stsc", b"stsz", b"stco", b"co64", b"stss"]; + + for name in box_names { + let mut stream = make_fullbox(BoxSize::Auto, name, 1, |s| { + s.append_repeated(0, 100) // add padding data + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + match name { + b"stts" => { + super::read_stts(&mut stream).expect("fail to skip padding: stts"); + } + b"stsc" => { + super::read_stsc(&mut stream).expect("fail to skip padding: stsc"); + } + b"stsz" => { + super::read_stsz(&mut stream).expect("fail to skip padding: stsz"); + } + b"stco" => { + super::read_stco(&mut stream).expect("fail to skip padding: stco"); + } + b"co64" => { + super::read_co64(&mut stream).expect("fail to skip padding: co64"); + } + b"stss" => { + super::read_stss(&mut stream).expect("fail to skip padding: stss"); + } + _ => (), + } + } +} + +#[test] +fn skip_padding_in_stsd() { + // Padding data could be added in the end of stsd boxes. Parser needs to skip + // them instead of returning error. + let avc = make_box(BoxSize::Auto, b"avc1", |s| { + s.append_repeated(0, 6) + .B16(1) + .append_repeated(0, 16) + .B16(320) + .B16(240) + .append_repeated(0, 14) + .append_repeated(0, 32) + .append_repeated(0, 4) + .B32(0xffff_ffff) + .append_bytes(b"avcC") + .append_repeated(0, 100) + }) + .into_inner(); + let mut stream = make_fullbox(BoxSize::Auto, b"stsd", 0, |s| { + s.B32(1) + .append_bytes(avc.as_slice()) + .append_repeated(0, 100) // add padding data + }); + + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + super::read_stsd(&mut stream, &mut super::Track::new(0)).expect("fail to skip padding: stsd"); +} + +#[test] +fn read_qt_wave_atom() { + let esds = make_fullbox(BoxSize::Auto, b"esds", 0, |s| { + s.B8(0x03) // elementary stream descriptor tag + .B8(0x12) // esds length + .append_repeated(0, 2) + .B8(0x00) // flags + .B8(0x04) // decoder config descriptor tag + .B8(0x0d) // dcds length + .B8(0x6b) // mp3 + .append_repeated(0, 12) + }) + .into_inner(); + let chan = make_box(BoxSize::Auto, b"chan", |s| { + s.append_repeated(0, 10) // we don't care its data. + }) + .into_inner(); + let wave = make_box(BoxSize::Auto, b"wave", |s| s.append_bytes(esds.as_slice())).into_inner(); + let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { + s.append_repeated(0, 6) + .B16(1) // data_reference_count + .B16(1) // verion: qt -> 1 + .append_repeated(0, 6) + .B16(2) + .B16(16) + .append_repeated(0, 4) + .B32(48000 << 16) + .append_repeated(0, 16) + .append_bytes(wave.as_slice()) + .append_bytes(chan.as_slice()) + }); + + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let sample_entry = + super::read_audio_sample_entry(&mut stream).expect("fail to read qt wave atom"); + match sample_entry { + super::SampleEntry::Audio(sample_entry) => { + assert_eq!(sample_entry.codec_type, super::CodecType::MP3) + } + _ => panic!("fail to read audio sample enctry"), + } +} + +#[test] +fn read_descriptor_80() { + let aac_esds = vec![ + 0x03, 0x80, 0x80, 0x80, 0x22, 0x00, 0x02, 0x00, 0x04, 0x80, 0x80, 0x80, 0x17, 0x40, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x22, 0xBC, 0x00, 0x01, 0xF5, 0x83, 0x05, 0x80, 0x80, 0x80, + 0x02, 0x11, 0x90, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02, + ]; + let aac_dc_descriptor = &aac_esds[31..33]; + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .append_bytes(aac_esds.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + let es = super::read_esds(&mut stream).unwrap(); + + assert_eq!(es.audio_codec, super::CodecType::AAC); + assert_eq!(es.audio_object_type, Some(2)); + assert_eq!(es.extended_audio_object_type, None); + assert_eq!(es.audio_sample_rate, Some(48000)); + assert_eq!(es.audio_channel_count, Some(2)); + assert_eq!(es.codec_esds, aac_esds); + assert_eq!(es.decoder_specific_data, aac_dc_descriptor); +} + +#[test] +fn read_esds() { + let aac_esds = vec![ + 0x03, 0x24, 0x00, 0x00, 0x00, 0x04, 0x1c, 0x40, 0x15, 0x00, 0x12, 0x00, 0x00, 0x01, 0xf4, + 0x00, 0x00, 0x01, 0xf4, 0x00, 0x05, 0x0d, 0x13, 0x00, 0x05, 0x88, 0x05, 0x00, 0x48, 0x21, + 0x10, 0x00, 0x56, 0xe5, 0x98, 0x06, 0x01, 0x02, + ]; + let aac_dc_descriptor = &aac_esds[22..35]; + + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .append_bytes(aac_esds.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + let es = super::read_esds(&mut stream).unwrap(); + + assert_eq!(es.audio_codec, super::CodecType::AAC); + assert_eq!(es.audio_object_type, Some(2)); + assert_eq!(es.extended_audio_object_type, None); + assert_eq!(es.audio_sample_rate, Some(24000)); + assert_eq!(es.audio_channel_count, Some(6)); + assert_eq!(es.codec_esds, aac_esds); + assert_eq!(es.decoder_specific_data, aac_dc_descriptor); +} + +#[test] +fn read_esds_aac_type5() { + let aac_esds = vec![ + 0x03, 0x80, 0x80, 0x80, 0x2F, 0x00, 0x00, 0x00, 0x04, 0x80, 0x80, 0x80, 0x21, 0x40, 0x15, + 0x00, 0x15, 0x00, 0x00, 0x03, 0xED, 0xAA, 0x00, 0x03, 0x6B, 0x00, 0x05, 0x80, 0x80, 0x80, + 0x0F, 0x2B, 0x01, 0x88, 0x02, 0xC4, 0x04, 0x90, 0x2C, 0x10, 0x8C, 0x80, 0x00, 0x00, 0xED, + 0x40, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02, + ]; + + let aac_dc_descriptor = &aac_esds[31..46]; + + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .append_bytes(aac_esds.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + let es = super::read_esds(&mut stream).unwrap(); + + assert_eq!(es.audio_codec, super::CodecType::AAC); + assert_eq!(es.audio_object_type, Some(2)); + assert_eq!(es.extended_audio_object_type, Some(5)); + assert_eq!(es.audio_sample_rate, Some(24000)); + assert_eq!(es.audio_channel_count, Some(8)); + assert_eq!(es.codec_esds, aac_esds); + assert_eq!(es.decoder_specific_data, aac_dc_descriptor); +} + +#[test] +fn read_esds_mpeg2_aac_lc() { + // Recognize MPEG-2 AAC LC (ISO 13818-7) object type as AAC. + // Extracted from BMO #1722497 sdasdasdasd_001.mp4 using Bento4. + // "mp4extract --payload-only moov/trak[1]/mdia/minf/stbl/stsd/mp4a/esds sdasdasdasd_001.mp4 /dev/stdout | xxd -i -c 15" + let aac_esds = vec![ + 0x03, 0x19, 0x00, 0x00, 0x00, 0x04, 0x11, 0x67, 0x15, 0x00, 0x02, 0x38, 0x00, 0x01, 0x0f, + 0xd0, 0x00, 0x00, 0xf5, 0x48, 0x05, 0x02, 0x13, 0x90, 0x06, 0x01, 0x02, + ]; + let aac_dc_descriptor = &aac_esds[22..24]; + + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .append_bytes(aac_esds.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + let es = super::read_esds(&mut stream).unwrap(); + + assert_eq!(es.audio_codec, super::CodecType::AAC); + assert_eq!(es.audio_object_type, Some(2)); + assert_eq!(es.extended_audio_object_type, None); + assert_eq!(es.audio_sample_rate, Some(22050)); + assert_eq!(es.audio_channel_count, Some(2)); + assert_eq!(es.codec_esds, aac_esds); + assert_eq!(es.decoder_specific_data, aac_dc_descriptor); +} + +#[test] +fn read_stsd_mp4v() { + let mp4v = vec![ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x01, 0xe0, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x73, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x3e, 0x00, 0x00, 0x1f, 0x04, 0x36, 0x20, 0x11, 0x01, 0x77, 0x00, 0x00, 0x03, 0xe8, + 0x00, 0x00, 0x03, 0xe8, 0x00, 0x05, 0x27, 0x00, 0x00, 0x01, 0xb0, 0x05, 0x00, 0x00, 0x01, + 0xb5, 0x0e, 0xcf, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x86, 0xe0, 0x00, + 0x2e, 0xa6, 0x60, 0x16, 0xf4, 0x01, 0xf4, 0x24, 0xc8, 0x01, 0xe5, 0x16, 0x84, 0x3c, 0x14, + 0x63, 0x06, 0x01, 0x02, + ]; + #[cfg(not(feature = "mp4v"))] + let esds_specific_data = &mp4v[90..]; + #[cfg(feature = "mp4v")] + let esds_specific_data = &mp4v[112..151]; + println!("esds_specific_data {esds_specific_data:?}"); + + let mut stream = make_box(BoxSize::Auto, b"mp4v", |s| s.append_bytes(mp4v.as_slice())); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + let sample_entry = super::read_video_sample_entry(&mut stream).unwrap(); + + match sample_entry { + super::SampleEntry::Video(v) => { + assert_eq!(v.codec_type, super::CodecType::MP4V); + assert_eq!(v.width, 720); + assert_eq!(v.height, 480); + match v.codec_specific { + super::VideoCodecSpecific::ESDSConfig(esds_data) => { + assert_eq!(esds_data.as_slice(), esds_specific_data); + } + _ => panic!("it should be ESDSConfig!"), + } + } + _ => panic!("it should be a video sample entry!"), + } +} + +#[test] +fn read_esds_one_byte_extension_descriptor() { + let esds = vec![ + 0x00, 0x03, 0x80, 0x1b, 0x00, 0x00, 0x00, 0x04, 0x80, 0x12, 0x40, 0x15, 0x00, 0x06, 0x00, + 0x00, 0x01, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x11, 0x90, 0x06, 0x01, + 0x02, + ]; + + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .append_bytes(esds.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + let es = super::read_esds(&mut stream).unwrap(); + + assert_eq!(es.audio_codec, super::CodecType::AAC); + assert_eq!(es.audio_object_type, Some(2)); + assert_eq!(es.extended_audio_object_type, None); + assert_eq!(es.audio_sample_rate, Some(48000)); + assert_eq!(es.audio_channel_count, Some(2)); +} + +#[test] +fn read_esds_byte_extension_descriptor() { + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .B16(0x0003) + .B16(0x8181) // extension byte length 0x81 + .append_repeated(0, 0x81) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + match super::read_esds(&mut stream) { + Ok(_) => (), + _ => panic!("fail to parse descriptor extension byte length"), + } +} + +#[test] +fn read_f4v_stsd() { + let mut stream = make_box(BoxSize::Auto, b".mp3", |s| { + s.append_repeated(0, 6) + .B16(1) + .B16(0) + .append_repeated(0, 6) + .B16(2) + .B16(16) + .append_repeated(0, 4) + .B32(48000 << 16) + }); + + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let sample_entry = + super::read_audio_sample_entry(&mut stream).expect("failed to read f4v stsd atom"); + match sample_entry { + super::SampleEntry::Audio(sample_entry) => { + assert_eq!(sample_entry.codec_type, super::CodecType::MP3) + } + _ => panic!("fail to read audio sample enctry"), + } +} + +#[test] +fn unknown_video_sample_entry() { + let unknown_codec = make_box(BoxSize::Auto, b"yyyy", |s| s.append_repeated(0, 16)).into_inner(); + let mut stream = make_box(BoxSize::Auto, b"xxxx", |s| { + s.append_repeated(0, 6) + .B16(1) + .append_repeated(0, 16) + .B16(0) + .B16(0) + .append_repeated(0, 14) + .append_repeated(0, 32) + .append_repeated(0, 4) + .append_bytes(unknown_codec.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + match super::read_video_sample_entry(&mut stream) { + Ok(super::SampleEntry::Unknown) => (), + _ => panic!("expected a different error result"), + } +} + +#[test] +fn unknown_audio_sample_entry() { + let unknown_codec = make_box(BoxSize::Auto, b"yyyy", |s| s.append_repeated(0, 16)).into_inner(); + let mut stream = make_box(BoxSize::Auto, b"xxxx", |s| { + s.append_repeated(0, 6) + .B16(1) + .B32(0) + .B32(0) + .B16(2) + .B16(16) + .B16(0) + .B16(0) + .B32(48000 << 16) + .append_bytes(unknown_codec.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + match super::read_audio_sample_entry(&mut stream) { + Ok(super::SampleEntry::Unknown) => (), + _ => panic!("expected a different error result"), + } +} + +#[test] +fn read_esds_invalid_descriptor() { + // tag 0x06, 0xff, 0x7f is incorrect. + let esds = vec![ + 0x03, 0x80, 0x80, 0x80, 0x22, 0x00, 0x00, 0x00, 0x04, 0x80, 0x80, 0x80, 0x14, 0x40, 0x01, + 0x00, 0x04, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x05, 0x80, 0x80, 0x80, + 0x02, 0xe8, 0x35, 0x06, 0xff, 0x7f, 0x00, 0x00, + ]; + + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .append_bytes(esds.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + match super::read_esds(&mut stream) { + Err(Error::InvalidData(s)) => assert_eq!(s, Status::EsdsBadDescriptor), + _ => panic!("unexpected result with invalid descriptor"), + } +} + +#[test] +fn read_esds_redundant_descriptor() { + // the '2' at the end is redundant data. + let esds = vec![ + 3, 25, 0, 1, 0, 4, 19, 64, 21, 0, 0, 0, 0, 0, 0, 0, 0, 1, 119, 0, 5, 2, 18, 16, 6, 1, 2, + ]; + + let mut stream = make_box(BoxSize::Auto, b"esds", |s| { + s.B32(0) // reserved + .append_bytes(esds.as_slice()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + match super::read_esds(&mut stream) { + Ok(esds) => assert_eq!(esds.audio_codec, super::CodecType::AAC), + _ => panic!("unexpected result with invalid descriptor"), + } +} + +#[test] +fn read_stsd_lpcm() { + // Extract from sample converted by ffmpeg. + // "ffmpeg -i ./gizmo-short.mp4 -acodec pcm_s16le -ar 96000 -vcodec copy -f mov gizmo-short.mov" + let lpcm = vec![ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x10, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x48, 0x40, 0xf7, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x63, 0x68, 0x61, 0x6e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let mut stream = make_box(BoxSize::Auto, b"lpcm", |s| s.append_bytes(lpcm.as_slice())); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + + let sample_entry = super::read_audio_sample_entry(&mut stream).unwrap(); + + match sample_entry { + #[allow(clippy::float_cmp)] // The float comparison below is valid and intended. + super::SampleEntry::Audio(a) => { + assert_eq!(a.codec_type, super::CodecType::LPCM); + assert_eq!(a.samplerate, 96000.0); + assert_eq!(a.channelcount, 1); + match a.codec_specific { + super::AudioCodecSpecific::LPCM => (), + _ => panic!("it should be LPCM!"), + } + } + _ => panic!("it should be a audio sample entry!"), + } +} + +#[test] +fn read_to_end_() { + let mut src = b"1234567890".take(5); + let buf = src.read_into_try_vec().unwrap(); + assert_eq!(buf.len(), 5); + assert_eq!(buf, b"12345".as_ref()); +} + +#[test] +fn read_to_end_oom() { + let mut src = b"1234567890".take(std::isize::MAX.try_into().expect("isize < u64")); + assert!(src.read_into_try_vec().is_err()); +} |