//! 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(size: BoxSize, name: &[u8; 4], func: F) -> Cursor> 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(size: BoxSize, uuid: &[u8; 16], func: F) -> Cursor> 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(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Cursor> 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 { 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> { 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> { 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::::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::::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> { 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()); }