Source code

Revision control

Copy as Markdown

Other Tools

//! 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, &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_mvhd_v0_trailing_data() {
let mut stream = make_fullbox(BoxSize::Short(110), b"mvhd", 0, |s| {
s.B32(0)
.B32(0)
.B32(1234)
.B32(5678)
.append_repeated(0, 80)
.B16(0)
});
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, 110);
let parsed = super::read_mvhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, 5678);
}
#[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, &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());
}