From edc35124c4e12bd732053fef55dd3993d2c552e4 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 28 Apr 2023 14:29:57 -0700 Subject: [PATCH 1/5] Add support for multiple trex boxes. While it's common to construct a fMP4 file for a single track, it's totally valid to have multiple tracks. This means you also need multiple to support multiple trex boxes. Untested. --- examples/mp4dump.rs | 4 +++- src/mp4box/mvex.rs | 27 ++++++++++++++++++++------- src/reader.rs | 16 ++++++++++------ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/examples/mp4dump.rs b/examples/mp4dump.rs index 6a97d9a0..657e5478 100644 --- a/examples/mp4dump.rs +++ b/examples/mp4dump.rs @@ -56,7 +56,9 @@ fn get_boxes(file: File) -> Result> { if let Some(mehd) = &mvex.mehd { boxes.push(build_box(mehd)); } - boxes.push(build_box(&mvex.trex)); + for trex in mvex.trexs.iter() { + boxes.push(build_box(trex)); + } } // trak. diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index 8be683b8..07bb26e2 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -7,7 +7,9 @@ use crate::mp4box::{mehd::MehdBox, trex::TrexBox}; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct MvexBox { pub mehd: Option, - pub trex: TrexBox, + + #[serde(rename = "trex")] + pub trexs: Vec, } impl MvexBox { @@ -16,7 +18,15 @@ impl MvexBox { } pub fn get_size(&self) -> u64 { - HEADER_SIZE + self.mehd.as_ref().map(|x| x.box_size()).unwrap_or(0) + self.trex.box_size() + let mut size = HEADER_SIZE; + + size += self.mehd.as_ref().map_or(0,|x| x.box_size()); + + for trex in self.trexs.iter() { + size += trex.box_size(); + } + + size } } @@ -44,7 +54,7 @@ impl ReadBox<&mut R> for MvexBox { let start = box_start(reader)?; let mut mehd = None; - let mut trex = None; + let mut trexs = Vec::new(); let mut current = reader.stream_position()?; let end = start + size; @@ -63,7 +73,7 @@ impl ReadBox<&mut R> for MvexBox { mehd = Some(MehdBox::read_box(reader, s)?); } BoxType::TrexBox => { - trex = Some(TrexBox::read_box(reader, s)?); + trexs.push(TrexBox::read_box(reader, s)?); } _ => { // XXX warn!() @@ -74,7 +84,7 @@ impl ReadBox<&mut R> for MvexBox { current = reader.stream_position()?; } - if trex.is_none() { + if trexs.is_empty() { return Err(Error::BoxNotFound(BoxType::TrexBox)); } @@ -82,7 +92,7 @@ impl ReadBox<&mut R> for MvexBox { Ok(MvexBox { mehd, - trex: trex.unwrap(), + trexs, }) } } @@ -95,7 +105,10 @@ impl WriteBox<&mut W> for MvexBox { if let Some(mehd) = &self.mehd { mehd.write_box(writer)?; } - self.trex.write_box(writer)?; + + for trex in self.trexs.iter() { + trex.write_box(writer)?; + } Ok(size) } diff --git a/src/reader.rs b/src/reader.rs index 5a39eebb..8f2ad6be 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -94,16 +94,20 @@ impl Mp4Reader { // Update tracks if any fragmented (moof) boxes are found. if !moofs.is_empty() { - let mut default_sample_duration = 0; - if let Some(ref moov) = moov { - if let Some(ref mvex) = &moov.mvex { - default_sample_duration = mvex.trex.default_sample_duration - } - } + // Grab the mvex box if it exists. + let mvex = moov.as_ref().and_then(|moov| moov.mvex.as_ref()); for moof in moofs.iter() { for traf in moof.trafs.iter() { let track_id = traf.tfhd.track_id; + + // Get the default sample duration for the indicated track. + // This is buried in the optional mvex box, in a trex box per track. + let default_sample_duration = mvex.and_then(|mvex| + mvex.trexs.iter().find(|trex| trex.track_id == track_id) + .and_then(|trex| Some(trex.default_sample_duration)) + ).unwrap_or(0); + if let Some(track) = tracks.get_mut(&track_id) { track.default_sample_duration = default_sample_duration; track.trafs.push(traf.clone()) From 94a133b1fd2a3ca8ee744f8936cdf5d453ca6f23 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 2 May 2023 10:09:07 -0700 Subject: [PATCH 2/5] cargo fmt --- src/mp4box/mvex.rs | 7 ++----- src/reader.rs | 12 ++++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index 07bb26e2..76097b54 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -20,7 +20,7 @@ impl MvexBox { pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE; - size += self.mehd.as_ref().map_or(0,|x| x.box_size()); + size += self.mehd.as_ref().map_or(0, |x| x.box_size()); for trex in self.trexs.iter() { size += trex.box_size(); @@ -90,10 +90,7 @@ impl ReadBox<&mut R> for MvexBox { skip_bytes_to(reader, start + size)?; - Ok(MvexBox { - mehd, - trexs, - }) + Ok(MvexBox { mehd, trexs }) } } diff --git a/src/reader.rs b/src/reader.rs index 8f2ad6be..64d69b1a 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -103,10 +103,14 @@ impl Mp4Reader { // Get the default sample duration for the indicated track. // This is buried in the optional mvex box, in a trex box per track. - let default_sample_duration = mvex.and_then(|mvex| - mvex.trexs.iter().find(|trex| trex.track_id == track_id) - .and_then(|trex| Some(trex.default_sample_duration)) - ).unwrap_or(0); + let default_sample_duration = mvex + .and_then(|mvex| { + mvex.trexs + .iter() + .find(|trex| trex.track_id == track_id) + .and_then(|trex| Some(trex.default_sample_duration)) + }) + .unwrap_or(0); if let Some(track) = tracks.get_mut(&track_id) { track.default_sample_duration = default_sample_duration; From a6f1d280b59c91675a005f29d05734405e2a2317 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 2 May 2023 10:42:33 -0700 Subject: [PATCH 3/5] Add a missing write_box call for mvex. --- src/mp4box/moov.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mp4box/moov.rs b/src/mp4box/moov.rs index ac19381a..e203a245 100644 --- a/src/mp4box/moov.rs +++ b/src/mp4box/moov.rs @@ -134,6 +134,9 @@ impl WriteBox<&mut W> for MoovBox { for trak in self.traks.iter() { trak.write_box(writer)?; } + if let Some(mvex) = &self.mvex { + mvex.write_box(writer)?; + } if let Some(meta) = &self.meta { meta.write_box(writer)?; } From efefcc47353f477518bff01493785ae0daa8efd4 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 2 May 2023 10:58:53 -0700 Subject: [PATCH 4/5] Ran clippy --- src/reader.rs | 2 +- src/types.rs | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/reader.rs b/src/reader.rs index 64d69b1a..567cb2f2 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -108,7 +108,7 @@ impl Mp4Reader { mvex.trexs .iter() .find(|trex| trex.track_id == track_id) - .and_then(|trex| Some(trex.default_sample_duration)) + .map(|trex| trex.default_sample_duration) }) .unwrap_or(0); diff --git a/src/types.rs b/src/types.rs index 19fd40b3..a26c0b5a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -657,20 +657,15 @@ pub fn creation_time(creation_time: u64) -> u64 { } } -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)] pub enum DataType { + #[default] Binary = 0x000000, Text = 0x000001, Image = 0x00000D, TempoCpil = 0x000015, } -impl std::default::Default for DataType { - fn default() -> Self { - DataType::Binary - } -} - impl TryFrom for DataType { type Error = Error; fn try_from(value: u32) -> Result { From bb98664ee786187225f40f5d526df0b2cbd9cca2 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 2 May 2023 11:32:31 -0700 Subject: [PATCH 5/5] Fix more errors when serializing a mvex box. udta is also broken but that can be fixed in another PR. --- src/mp4box/moov.rs | 3 +++ src/mp4box/mvex.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mp4box/moov.rs b/src/mp4box/moov.rs index e203a245..80f9953f 100644 --- a/src/mp4box/moov.rs +++ b/src/mp4box/moov.rs @@ -38,6 +38,9 @@ impl MoovBox { if let Some(udta) = &self.udta { size += udta.box_size(); } + if let Some(mvex) = &self.mvex { + size += mvex.box_size(); + } size } } diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index 76097b54..58d33329 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -14,7 +14,7 @@ pub struct MvexBox { impl MvexBox { pub fn get_type(&self) -> BoxType { - BoxType::MdiaBox + BoxType::MvexBox } pub fn get_size(&self) -> u64 {