use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use serde::Serialize; use std::io::{Read, Seek, Write}; use crate::mp4box::*; #[derive(Debug, Clone, PartialEq, Serialize)] pub struct MvhdBox { pub version: u8, pub flags: u32, pub creation_time: u64, pub modification_time: u64, pub timescale: u32, pub duration: u64, #[serde(with = "value_u32")] pub rate: FixedPointU16, #[serde(with = "value_u8")] pub volume: FixedPointU8, pub matrix: tkhd::Matrix, pub next_track_id: u32, } impl MvhdBox { pub fn get_type(&self) -> BoxType { BoxType::MvhdBox } pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE + HEADER_EXT_SIZE; if self.version == 1 { size += 28; } else if self.version == 0 { size += 16; } size += 80; size } } impl Default for MvhdBox { fn default() -> Self { MvhdBox { version: 0, flags: 0, creation_time: 0, modification_time: 0, timescale: 1000, duration: 0, rate: FixedPointU16::new(1), matrix: tkhd::Matrix::default(), volume: FixedPointU8::new(1), next_track_id: 1, } } } impl Mp4Box for MvhdBox { fn box_type(&self) -> BoxType { self.get_type() } fn box_size(&self) -> u64 { self.get_size() } fn to_json(&self) -> Result { Ok(serde_json::to_string(&self).unwrap()) } fn summary(&self) -> Result { let s = format!( "creation_time={} timescale={} duration={} rate={} volume={}, matrix={}, next_track_id={}", self.creation_time, self.timescale, self.duration, self.rate.value(), self.volume.value(), self.matrix, self.next_track_id ); Ok(s) } } impl ReadBox<&mut R> for MvhdBox { fn read_box(reader: &mut R, size: u64) -> Result { let start = box_start(reader)?; let (version, flags) = read_box_header_ext(reader)?; let (creation_time, modification_time, timescale, duration) = if version == 1 { ( reader.read_u64::()?, reader.read_u64::()?, reader.read_u32::()?, reader.read_u64::()?, ) } else if version == 0 { ( reader.read_u32::()? as u64, reader.read_u32::()? as u64, reader.read_u32::()?, reader.read_u32::()? as u64, ) } else { return Err(Error::InvalidData("version must be 0 or 1")); }; let rate = FixedPointU16::new_raw(reader.read_u32::()?); let volume = FixedPointU8::new_raw(reader.read_u16::()?); reader.read_u16::()?; // reserved = 0 reader.read_u64::()?; // reserved = 0 let matrix = tkhd::Matrix { a: reader.read_i32::()?, b: reader.read_i32::()?, u: reader.read_i32::()?, c: reader.read_i32::()?, d: reader.read_i32::()?, v: reader.read_i32::()?, x: reader.read_i32::()?, y: reader.read_i32::()?, w: reader.read_i32::()?, }; skip_bytes(reader, 24)?; // pre_defined = 0 let next_track_id = reader.read_u32::()?; skip_bytes_to(reader, start + size)?; Ok(MvhdBox { version, flags, creation_time, modification_time, timescale, duration, rate, volume, matrix, next_track_id, }) } } impl WriteBox<&mut W> for MvhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); BoxHeader::new(self.box_type(), size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; if self.version == 1 { writer.write_u64::(self.creation_time)?; writer.write_u64::(self.modification_time)?; writer.write_u32::(self.timescale)?; writer.write_u64::(self.duration)?; } else if self.version == 0 { writer.write_u32::(self.creation_time as u32)?; writer.write_u32::(self.modification_time as u32)?; writer.write_u32::(self.timescale)?; writer.write_u32::(self.duration as u32)?; } else { return Err(Error::InvalidData("version must be 0 or 1")); } writer.write_u32::(self.rate.raw_value())?; writer.write_u16::(self.volume.raw_value())?; writer.write_u16::(0)?; // reserved = 0 writer.write_u64::(0)?; // reserved = 0 writer.write_i32::(self.matrix.a)?; writer.write_i32::(self.matrix.b)?; writer.write_i32::(self.matrix.u)?; writer.write_i32::(self.matrix.c)?; writer.write_i32::(self.matrix.d)?; writer.write_i32::(self.matrix.v)?; writer.write_i32::(self.matrix.x)?; writer.write_i32::(self.matrix.y)?; writer.write_i32::(self.matrix.w)?; write_zeros(writer, 24)?; // pre_defined = 0 writer.write_u32::(self.next_track_id)?; Ok(size) } } #[cfg(test)] mod tests { use super::*; use crate::mp4box::BoxHeader; use std::io::Cursor; #[test] fn test_mvhd32() { let src_box = MvhdBox { version: 0, flags: 0, creation_time: 100, modification_time: 200, timescale: 1000, duration: 634634, rate: FixedPointU16::new(1), volume: FixedPointU8::new(1), matrix: tkhd::Matrix::default(), next_track_id: 1, }; let mut buf = Vec::new(); src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); let mut reader = Cursor::new(&buf); let header = BoxHeader::read(&mut reader).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: FixedPointU16::new(1), volume: FixedPointU8::new(1), matrix: tkhd::Matrix::default(), next_track_id: 1, }; let mut buf = Vec::new(); src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); let mut reader = Cursor::new(&buf); let header = BoxHeader::read(&mut reader).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); } }