From 4df9fa1bd4a9a36dfae5890f26ffcc89441877f1 Mon Sep 17 00:00:00 2001 From: Ian Jun <40660047+ian-spoonradio@users.noreply.github.com> Date: Wed, 29 Jul 2020 13:29:04 +0900 Subject: [PATCH] Feature/test write box (#12) * Add ReadBox trait * Add boxtype macro * Remove offset in BoxHeader * Fix parsing error when box has largesize * Remove duplicated codes reading version and flags * Add test code for all leaves in box tree * Remove entry_count that is duplacated with entries.len() * Change volume type to Ratio Co-authored-by: Byungwan Jun --- Cargo.toml | 1 + examples/mp4info.rs | 2 +- src/atoms/elst.rs | 84 ++++++++++++++++++++++++++++---- src/atoms/hdlr.rs | 38 ++++++++++++++- src/atoms/mdhd.rs | 115 +++++++++++++++++++++++++++++++++++++++++--- src/atoms/mod.rs | 22 +++++++++ src/atoms/mvhd.rs | 58 +++++++++++++++++++--- src/atoms/stsd.rs | 4 +- src/atoms/stts.rs | 75 ++++++++++++++++++++++++++--- src/atoms/tkhd.rs | 78 +++++++++++++++++++++++++++--- src/atoms/vmhd.rs | 39 ++++++++++++++- 11 files changed, 474 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 711bcb6..d9779aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ license = "MIT" [dependencies] thiserror = "^1.0" byteorder = "1" +num-rational = "0.3" \ No newline at end of file diff --git a/examples/mp4info.rs b/examples/mp4info.rs index 1269f29..39177eb 100644 --- a/examples/mp4info.rs +++ b/examples/mp4info.rs @@ -54,7 +54,7 @@ fn main() { " type: {:?}", get_handler_type(hdlr.handler_type.value.as_ref()) ); - println!(" language: {:?}", mdhd.language_string); + println!(" language: {:?}", mdhd.language); println!(" media:"); if let Some(ref s) = stts { diff --git a/src/atoms/elst.rs b/src/atoms/elst.rs index c120f18..7ed9ca6 100644 --- a/src/atoms/elst.rs +++ b/src/atoms/elst.rs @@ -4,15 +4,14 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use crate::*; -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct ElstBox { pub version: u8, pub flags: u32, - pub entry_count: u32, pub entries: Vec, } -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct ElstEntry { pub segment_duration: u64, pub media_time: u64, @@ -26,12 +25,12 @@ impl Mp4Box for ElstBox { } fn box_size(&self) -> u64 { - let mut size = HEADER_SIZE + HEADER_EXT_SIZE; + let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 4; if self.version == 1 { - size += self.entry_count as u64 * 20; + size += self.entries.len() as u64 * 20; } else { assert_eq!(self.version, 0); - size += self.entry_count as u64 * 12; + size += self.entries.len() as u64 * 12; } size } @@ -72,7 +71,6 @@ impl ReadBox<&mut BufReader> for ElstBox { Ok(ElstBox { version, flags, - entry_count, entries, }) } @@ -85,8 +83,7 @@ impl WriteBox<&mut BufWriter> for ElstBox { write_box_header_ext(writer, self.version, self.flags)?; - assert_eq!(self.entry_count as usize, self.entries.len()); - writer.write_u32::(self.entry_count)?; + writer.write_u32::(self.entries.len() as u32)?; for entry in self.entries.iter() { if self.version == 1 { writer.write_u64::(entry.segment_duration)?; @@ -102,3 +99,72 @@ impl WriteBox<&mut BufWriter> for ElstBox { Ok(size) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::read_box_header; + use std::io::Cursor; + + #[test] + fn test_elst32() { + let src_box = ElstBox { + version: 0, + flags: 0, + entries: vec![ElstEntry { + segment_duration: 634634, + media_time: 0, + media_rate: 1, + media_rate_fraction: 0, + }], + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::ElstBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } + + #[test] + fn test_elst64() { + let src_box = ElstBox { + version: 1, + flags: 0, + entries: vec![ElstEntry { + segment_duration: 634634, + media_time: 0, + media_rate: 1, + media_rate_fraction: 0, + }], + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::ElstBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } +} diff --git a/src/atoms/hdlr.rs b/src/atoms/hdlr.rs index be88301..e7bdec3 100644 --- a/src/atoms/hdlr.rs +++ b/src/atoms/hdlr.rs @@ -4,7 +4,7 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use crate::*; -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct HdlrBox { pub version: u8, pub flags: u32, @@ -33,7 +33,7 @@ impl ReadBox<&mut BufReader> for HdlrBox { let n = reader.seek(SeekFrom::Current(12))?; // 12 bytes reserved. - let buf_size = (size - (n - current)) - HEADER_SIZE; + let buf_size = (size - (n - current)) - HEADER_SIZE - 1; let mut buf = vec![0u8; buf_size as usize]; reader.read_exact(&mut buf)?; @@ -77,3 +77,37 @@ impl WriteBox<&mut BufWriter> for HdlrBox { Ok(size) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::read_box_header; + use std::io::Cursor; + + #[test] + fn test_hdlr() { + let src_box = HdlrBox { + version: 0, + flags: 0, + handler_type: FourCC::from("vide"), + name: String::from("VideoHandler"), + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::HdlrBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } +} diff --git a/src/atoms/mdhd.rs b/src/atoms/mdhd.rs index 1c048fb..1b7f1c4 100644 --- a/src/atoms/mdhd.rs +++ b/src/atoms/mdhd.rs @@ -5,7 +5,7 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use crate::*; -#[derive(Debug, Default)] +#[derive(Debug, PartialEq)] pub struct MdhdBox { pub version: u8, pub flags: u32, @@ -13,8 +13,21 @@ pub struct MdhdBox { pub modification_time: u64, pub timescale: u32, pub duration: u64, - pub language: u16, - pub language_string: String, + pub language: String, +} + +impl Default for MdhdBox { + fn default() -> Self { + MdhdBox { + version: 0, + flags: 0, + creation_time: 0, + modification_time: 0, + timescale: 1000, + duration: 0, + language: String::from("und"), + } + } } impl Mp4Box for MdhdBox { @@ -59,8 +72,8 @@ impl ReadBox<&mut BufReader> for MdhdBox { reader.read_u32::()? as u64, ) }; - let language = reader.read_u16::()?; - let language_string = get_language_string(language); + let language_code = reader.read_u16::()?; + let language = get_language_string(language_code); skip_read(reader, current, size)?; Ok(MdhdBox { @@ -71,7 +84,6 @@ impl ReadBox<&mut BufReader> for MdhdBox { timescale, duration, language, - language_string, }) } } @@ -96,7 +108,8 @@ impl WriteBox<&mut BufWriter> for MdhdBox { writer.write_u32::(self.duration as u32)?; } - writer.write_u16::(self.language)?; + let language_code = get_language_code(&self.language); + writer.write_u16::(language_code)?; writer.write_u16::(0)?; // pre-defined Ok(size) @@ -117,3 +130,91 @@ fn get_language_string(language: u16) -> String { return lang_str; } + +fn get_language_code(language: &str) -> u16 { + let mut lang = language.encode_utf16(); + let mut code = (lang.next().unwrap_or(0) & 0x1F) << 10; + code += (lang.next().unwrap_or(0) & 0x1F) << 5; + code += lang.next().unwrap_or(0) & 0x1F; + code +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::read_box_header; + use std::io::Cursor; + + fn test_language_code(lang: &str) { + let code = get_language_code(lang); + let lang2 = get_language_string(code); + assert_eq!(lang, lang2); + } + + #[test] + fn test_language_codes() { + test_language_code("und"); + test_language_code("eng"); + test_language_code("kor"); + } + + #[test] + fn test_mdhd32() { + let src_box = MdhdBox { + version: 0, + flags: 0, + creation_time: 100, + modification_time: 200, + timescale: 48000, + duration: 30439936, + language: String::from("und"), + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::MdhdBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } + + #[test] + fn test_mdhd64() { + let src_box = MdhdBox { + version: 0, + flags: 0, + creation_time: 100, + modification_time: 200, + timescale: 48000, + duration: 30439936, + language: String::from("eng"), + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::MdhdBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } +} diff --git a/src/atoms/mod.rs b/src/atoms/mod.rs index 29e1f6a..8dcd740 100644 --- a/src/atoms/mod.rs +++ b/src/atoms/mod.rs @@ -125,6 +125,28 @@ impl From<&FourCC> for u32 { } } +impl From for FourCC { + fn from(fourcc: String) -> FourCC { + let value = if fourcc.len() > 4 { + fourcc[0..4].to_string() + } else { + fourcc + }; + FourCC {value} + } +} + +impl From<&str> for FourCC { + fn from(fourcc: &str) -> FourCC { + let value = if fourcc.len() > 4 { + fourcc[0..4].to_string() + } else { + fourcc.to_string() + }; + FourCC {value} + } +} + impl From for FourCC { fn from(t: BoxType) -> FourCC { let box_num: u32 = Into::into(t); diff --git a/src/atoms/mvhd.rs b/src/atoms/mvhd.rs index 95f7bde..0a53628 100644 --- a/src/atoms/mvhd.rs +++ b/src/atoms/mvhd.rs @@ -1,10 +1,11 @@ use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use num_rational::Ratio; use crate::*; -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, PartialEq)] pub struct MvhdBox { pub version: u8, pub flags: u32, @@ -12,7 +13,21 @@ pub struct MvhdBox { pub modification_time: u64, pub timescale: u32, pub duration: u64, - pub rate: u32, + pub rate: Ratio, +} + +impl Default for MvhdBox { + fn default() -> Self { + MvhdBox { + version: 0, + flags: 0, + creation_time: 0, + modification_time: 0, + timescale: 1000, + duration: 0, + rate: Ratio::new_raw(0x00010000, 0x10000), + } + } } impl Mp4Box for MvhdBox { @@ -56,7 +71,8 @@ impl ReadBox<&mut BufReader> for MvhdBox { reader.read_u32::()? as u64, ) }; - let rate = reader.read_u32::()?; + let numer = reader.read_u32::()?; + let rate = Ratio::new_raw(numer, 0x10000); skip_read(reader, current, size)?; Ok(MvhdBox{ @@ -90,7 +106,7 @@ impl WriteBox<&mut BufWriter> for MvhdBox { writer.write_u32::(self.timescale)?; writer.write_u32::(self.duration as u32)?; } - writer.write_u32::(self.rate)?; + writer.write_u32::(*self.rate.numer())?; // XXX volume, ... skip_write(writer, 76)?; @@ -106,7 +122,7 @@ mod tests { use std::io::Cursor; #[test] - fn test_mvhd() { + fn test_mvhd32() { let src_box = MvhdBox { version: 0, flags: 0, @@ -114,7 +130,37 @@ mod tests { modification_time: 200, timescale: 1000, duration: 634634, - rate: 0x00010000, + rate: Ratio::new_raw(0x00010000, 0x10000), + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::MvhdBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } + + #[test] + fn test_mvhd64() { + let src_box = MvhdBox { + version: 1, + flags: 0, + creation_time: 100, + modification_time: 200, + timescale: 1000, + duration: 634634, + rate: Ratio::new_raw(0x00010000, 0x10000), }; let mut buf = Vec::new(); { diff --git a/src/atoms/stsd.rs b/src/atoms/stsd.rs index e53c728..5c216b7 100644 --- a/src/atoms/stsd.rs +++ b/src/atoms/stsd.rs @@ -8,7 +8,6 @@ use crate::*; pub struct StsdBox { pub version: u8, pub flags: u32, - pub entry_count: u32, } impl Mp4Box for StsdBox { @@ -28,7 +27,7 @@ impl ReadBox<&mut BufReader> for StsdBox { let (version, flags) = read_box_header_ext(reader)?; - let entry_count = reader.read_u32::()?; + let _entry_count = reader.read_u32::()?; let mut start = 0u64; while start < size { @@ -48,7 +47,6 @@ impl ReadBox<&mut BufReader> for StsdBox { Ok(StsdBox { version, flags, - entry_count, }) } } diff --git a/src/atoms/stts.rs b/src/atoms/stts.rs index 3143e6e..27bf0c3 100644 --- a/src/atoms/stts.rs +++ b/src/atoms/stts.rs @@ -4,15 +4,14 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use crate::*; -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct SttsBox { pub version: u8, pub flags: u32, - pub entry_count: u32, pub entries: Vec, } -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct SttsEntry { pub sample_count: u32, pub sample_delta: u32, @@ -24,7 +23,7 @@ impl Mp4Box for SttsBox { } fn box_size(&self) -> u64 { - HEADER_SIZE + HEADER_EXT_SIZE + 4 + (8 * self.entry_count as u64) + HEADER_SIZE + HEADER_EXT_SIZE + 4 + (8 * self.entries.len() as u64) } } @@ -48,7 +47,6 @@ impl ReadBox<&mut BufReader> for SttsBox { Ok(SttsBox { version, flags, - entry_count, entries, }) } @@ -61,7 +59,7 @@ impl WriteBox<&mut BufWriter> for SttsBox { write_box_header_ext(writer, self.version, self.flags)?; - writer.write_u32::(self.entry_count)?; + writer.write_u32::(self.entries.len() as u32)?; for entry in self.entries.iter() { writer.write_u32::(entry.sample_count)?; writer.write_u32::(entry.sample_delta)?; @@ -70,3 +68,68 @@ impl WriteBox<&mut BufWriter> for SttsBox { Ok(size) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::read_box_header; + use std::io::Cursor; + + #[test] + fn test_stts32() { + let src_box = SttsBox { + version: 0, + flags: 0, + entries: vec![ + SttsEntry {sample_count: 29726, sample_delta: 1024}, + SttsEntry {sample_count: 1, sample_delta: 512}, + ], + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::SttsBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = SttsBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } + + #[test] + fn test_stts64() { + let src_box = SttsBox { + version: 1, + flags: 0, + entries: vec![ + SttsEntry {sample_count: 29726, sample_delta: 1024}, + SttsEntry {sample_count: 1, sample_delta: 512}, + ], + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::SttsBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = SttsBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } +} diff --git a/src/atoms/tkhd.rs b/src/atoms/tkhd.rs index c8cc3a0..46c44b1 100644 --- a/src/atoms/tkhd.rs +++ b/src/atoms/tkhd.rs @@ -1,10 +1,11 @@ use std::io::{BufReader, Seek, SeekFrom, Read, BufWriter, Write}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use num_rational::Ratio; use crate::*; -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, PartialEq)] pub struct TkhdBox { pub version: u8, pub flags: u32, @@ -14,12 +15,31 @@ pub struct TkhdBox { pub duration: u64, pub layer: u16, pub alternate_group: u16, - pub volume: u16, + pub volume: Ratio, pub matrix: Matrix, pub width: u32, pub height: u32, } +impl Default for TkhdBox { + fn default() -> Self { + TkhdBox { + version: 0, + flags: 0, + creation_time: 0, + modification_time: 0, + track_id: 0, + duration: 0, + layer: 0, + alternate_group: 0, + volume: Ratio::new_raw(0x0100, 0x100), + matrix: Matrix::default(), + width: 0, + height: 0, + } + } +} + #[derive(Debug, Default, PartialEq)] pub struct Matrix { pub a: i32, @@ -79,7 +99,8 @@ impl ReadBox<&mut BufReader> for TkhdBox { reader.read_u64::()?; // reserved let layer = reader.read_u16::()?; let alternate_group = reader.read_u16::()?; - let volume = reader.read_u16::()?; + let volume_numer = reader.read_u16::()?; + let volume = Ratio::new_raw(volume_numer, 0x100); reader.read_u16::()?; // reserved let matrix = Matrix{ @@ -141,7 +162,7 @@ impl WriteBox<&mut BufWriter> for TkhdBox { writer.write_u64::(0)?; // reserved writer.write_u16::(self.layer)?; writer.write_u16::(self.alternate_group)?; - writer.write_u16::(self.volume)?; + writer.write_u16::(*self.volume.numer())?; writer.write_u16::(0)?; // reserved @@ -169,7 +190,7 @@ mod tests { use std::io::Cursor; #[test] - fn test_tkhd() { + fn test_tkhd32() { let src_box = TkhdBox { version: 0, flags: 0, @@ -179,7 +200,52 @@ mod tests { duration: 634634, layer: 0, alternate_group: 0, - volume: 0x0100, + volume: Ratio::new_raw(0x0100, 0x100), + matrix: Matrix { + a: 0x00010000, + b: 0, + u: 0, + c: 0, + d: 0x00010000, + v: 0, + x: 0, + y: 0, + w: 0x40000000, + }, + width: 512, + height: 288, + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::TkhdBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } + + #[test] + fn test_tkhd64() { + let src_box = TkhdBox { + version: 1, + flags: 0, + creation_time: 100, + modification_time: 200, + track_id: 1, + duration: 634634, + layer: 0, + alternate_group: 0, + volume: Ratio::new_raw(0x0100, 0x100), matrix: Matrix { a: 0x00010000, b: 0, diff --git a/src/atoms/vmhd.rs b/src/atoms/vmhd.rs index 8e3a656..c70a60b 100644 --- a/src/atoms/vmhd.rs +++ b/src/atoms/vmhd.rs @@ -4,7 +4,7 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use crate::*; -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct VmhdBox { pub version: u8, pub flags: u32, @@ -12,7 +12,7 @@ pub struct VmhdBox { pub op_color: RgbColor, } -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct RgbColor { pub red: u16, pub green: u16, @@ -67,3 +67,38 @@ impl WriteBox<&mut BufWriter> for VmhdBox { Ok(size) } } + + +#[cfg(test)] +mod tests { + use super::*; + use crate::read_box_header; + use std::io::Cursor; + + #[test] + fn test_vmhd() { + let src_box = VmhdBox { + version: 0, + flags: 1, + graphics_mode: 0, + op_color: RgbColor { red: 0, green: 0, blue: 0}, + }; + let mut buf = Vec::new(); + { + let mut writer = BufWriter::new(&mut buf); + src_box.write_box(&mut writer).unwrap(); + } + assert_eq!(buf.len(), src_box.box_size() as usize); + + { + let mut reader = BufReader::new(Cursor::new(&buf)); + let header = read_box_header(&mut reader, 0).unwrap(); + assert_eq!(header.name, BoxType::VmhdBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = VmhdBox::read_box(&mut reader, header.size).unwrap(); + + assert_eq!(src_box, dst_box); + } + } +}