From 9c0f6534bd669a7bc0252291a70f9184b0299819 Mon Sep 17 00:00:00 2001 From: jensenn Date: Wed, 11 Jan 2023 21:21:49 -0700 Subject: [PATCH] Feature/tfdt box (#90) * Add Tfdt box parsing * Derive Default for TfdtBox * Derive Eq for TfdtBox --- src/mp4box/mod.rs | 3 + src/mp4box/tfdt.rs | 137 +++++++++++++++++++++++++++++++++++++++++++++ src/mp4box/traf.rs | 8 ++- 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/mp4box/tfdt.rs diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index da4485d..1d75500 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -50,6 +50,7 @@ //! mfhd //! traf //! tfhd +//! tfdt //! trun //! mdat //! free @@ -92,6 +93,7 @@ pub(crate) mod stsd; pub(crate) mod stss; pub(crate) mod stsz; pub(crate) mod stts; +pub(crate) mod tfdt; pub(crate) mod tfhd; pub(crate) mod tkhd; pub(crate) mod traf; @@ -155,6 +157,7 @@ boxtype! { MoofBox => 0x6d6f6f66, TkhdBox => 0x746b6864, TfhdBox => 0x74666864, + TfdtBox => 0x74666474, EdtsBox => 0x65647473, MdiaBox => 0x6d646961, ElstBox => 0x656c7374, diff --git a/src/mp4box/tfdt.rs b/src/mp4box/tfdt.rs new file mode 100644 index 0000000..ef92889 --- /dev/null +++ b/src/mp4box/tfdt.rs @@ -0,0 +1,137 @@ +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use serde::Serialize; +use std::io::{Read, Seek, Write}; + +use crate::mp4box::*; + +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] +pub struct TfdtBox { + pub version: u8, + pub flags: u32, + pub base_media_decode_time: u64, +} + +impl TfdtBox { + pub fn get_type(&self) -> BoxType { + BoxType::TfdtBox + } + + pub fn get_size(&self) -> u64 { + let mut sum = HEADER_SIZE + HEADER_EXT_SIZE; + if self.version == 1 { + sum += 8; + } else { + sum += 4; + } + sum + } +} + +impl Mp4Box for TfdtBox { + 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!("base_media_decode_time={}", self.base_media_decode_time); + Ok(s) + } +} + +impl ReadBox<&mut R> for TfdtBox { + fn read_box(reader: &mut R, size: u64) -> Result { + let start = box_start(reader)?; + + let (version, flags) = read_box_header_ext(reader)?; + + let base_media_decode_time = if version == 1 { + reader.read_u64::()? + } else if version == 0 { + reader.read_u32::()? as u64 + } else { + return Err(Error::InvalidData("version must be 0 or 1")); + }; + + skip_bytes_to(reader, start + size)?; + + Ok(TfdtBox { + version, + flags, + base_media_decode_time, + }) + } +} + +impl WriteBox<&mut W> for TfdtBox { + 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.base_media_decode_time)?; + } else if self.version == 0 { + writer.write_u32::(self.base_media_decode_time as u32)?; + } else { + return Err(Error::InvalidData("version must be 0 or 1")); + } + + Ok(size) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mp4box::BoxHeader; + use std::io::Cursor; + + #[test] + fn test_tfdt32() { + let src_box = TfdtBox { + version: 0, + flags: 0, + base_media_decode_time: 0, + }; + 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::TfdtBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = TfdtBox::read_box(&mut reader, header.size).unwrap(); + assert_eq!(src_box, dst_box); + } + + #[test] + fn test_tfdt64() { + let src_box = TfdtBox { + version: 1, + flags: 0, + base_media_decode_time: 0, + }; + 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::TfdtBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = TfdtBox::read_box(&mut reader, header.size).unwrap(); + assert_eq!(src_box, dst_box); + } +} diff --git a/src/mp4box/traf.rs b/src/mp4box/traf.rs index a73532e..44d78f2 100644 --- a/src/mp4box/traf.rs +++ b/src/mp4box/traf.rs @@ -2,11 +2,12 @@ use serde::Serialize; use std::io::{Read, Seek, SeekFrom, Write}; use crate::mp4box::*; -use crate::mp4box::{tfhd::TfhdBox, trun::TrunBox}; +use crate::mp4box::{tfdt::TfdtBox, tfhd::TfhdBox, trun::TrunBox}; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct TrafBox { pub tfhd: TfhdBox, + pub tfdt: Option, pub trun: Option, } @@ -49,6 +50,7 @@ impl ReadBox<&mut R> for TrafBox { let start = box_start(reader)?; let mut tfhd = None; + let mut tfdt = None; let mut trun = None; let mut current = reader.seek(SeekFrom::Current(0))?; @@ -62,6 +64,9 @@ impl ReadBox<&mut R> for TrafBox { BoxType::TfhdBox => { tfhd = Some(TfhdBox::read_box(reader, s)?); } + BoxType::TfdtBox => { + tfdt = Some(TfdtBox::read_box(reader, s)?); + } BoxType::TrunBox => { trun = Some(TrunBox::read_box(reader, s)?); } @@ -82,6 +87,7 @@ impl ReadBox<&mut R> for TrafBox { Ok(TrafBox { tfhd: tfhd.unwrap(), + tfdt, trun, }) }