summaryrefslogtreecommitdiffstats
path: root/third_party/rust/mp4parse/src/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/mp4parse/src/tests.rs')
-rw-r--r--third_party/rust/mp4parse/src/tests.rs1352
1 files changed, 1352 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..0f22760020
--- /dev/null
+++ b/third_party/rust/mp4parse/src/tests.rs
@@ -0,0 +1,1352 @@
+//! 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::Error;
+use super::ParseStrictness;
+use fallible_collections::TryRead as _;
+
+use std::convert::TryInto as _;
+use std::io::Cursor;
+use std::io::Read as _;
+extern crate test_assembler;
+use self::test_assembler::*;
+
+use 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, "malformed size"),
+ _ => 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, "malformed wide size"),
+ _ => 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_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!(super::read_hdlr(&mut stream, ParseStrictness::Strict).is_err());
+}
+
+#[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);
+ match super::read_hdlr(&mut stream, ParseStrictness::Normal) {
+ Err(Error::Unsupported(msg)) => assert_eq!("hdlr version", msg),
+ result => {
+ eprintln!("{:?}", result);
+ panic!("expected Error::Unsupported")
+ }
+ }
+}
+
+#[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);
+ match super::read_hdlr(&mut stream, ParseStrictness::Strict) {
+ Err(Error::InvalidData(msg)) => assert_eq!(
+ "The HandlerBox 'pre_defined' field shall be 0 \
+ per ISOBMFF (ISO 14496-12:2020) § 8.4.3.2",
+ msg
+ ),
+ result => {
+ eprintln!("{:?}", result);
+ panic!("expected Error::InvalidData")
+ }
+ }
+}
+
+#[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);
+ match super::read_hdlr(&mut stream, ParseStrictness::Strict) {
+ Err(Error::InvalidData(msg)) => assert_eq!(
+ "The HandlerBox 'reserved' fields shall be 0 \
+ per ISOBMFF (ISO 14496-12:2020) § 8.4.3.2",
+ msg
+ ),
+ result => {
+ eprintln!("{:?}", result);
+ panic!("expected Error::InvalidData")
+ }
+ }
+}
+
+#[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);
+ match super::read_hdlr(&mut stream, ParseStrictness::Normal) {
+ Err(Error::InvalidData(msg)) => assert_eq!(
+ "The HandlerBox 'name' field shall be null-terminated \
+ per ISOBMFF (ISO 14496-12:2020) § 8.4.3.2",
+ msg
+ ),
+ result => {
+ eprintln!("{:?}", result);
+ panic!("expected Error::InvalidData")
+ }
+ }
+}
+
+#[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 = if last { 1 } else { 0 };
+ 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, "Invalid descriptor."),
+ _ => 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::usize::MAX.try_into().expect("usize < u64"));
+ assert!(src.read_into_try_vec().is_err());
+}