From 1101df41bb6de4ca4c2f1073e33ed8c9f106525c Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 7 Jul 2023 10:49:49 -0400 Subject: [PATCH 01/23] expose TrakBox --- src/mp4box/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 0af14b5..45f6076 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -110,6 +110,7 @@ pub use emsg::EmsgBox; pub use ftyp::FtypBox; pub use moof::MoofBox; pub use moov::MoovBox; +pub use trak::TrakBox; pub const HEADER_SIZE: u64 = 8; // const HEADER_LARGE_SIZE: u64 = 16; From 2fb21bbbc8da994c2929e5318254f6236c37b294 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Wed, 12 Jul 2023 13:20:20 -0400 Subject: [PATCH 02/23] expose more boxes --- src/mp4box/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 45f6076..feea896 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -108,9 +108,16 @@ pub(crate) mod vpcc; pub use emsg::EmsgBox; pub use ftyp::FtypBox; +pub use mdia::MdiaBox; pub use moof::MoofBox; pub use moov::MoovBox; +pub use mvex::MvexBox; +pub use tfdt::TfdtBox; +pub use tfhd::TfhdBox; +pub use traf::TrafBox; pub use trak::TrakBox; +pub use trex::TrexBox; +pub use trun::TrunBox; pub const HEADER_SIZE: u64 = 8; // const HEADER_LARGE_SIZE: u64 = 16; From b72a26b3f5e35cfb895e585629c14465118b2f1f Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Thu, 13 Jul 2023 10:09:27 -0400 Subject: [PATCH 03/23] feat(opus): add opus box --- src/mp4box/mod.rs | 6 +- src/mp4box/opus.rs | 170 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 src/mp4box/opus.rs diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index feea896..2bc749e 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -85,6 +85,7 @@ pub(crate) mod moov; pub(crate) mod mp4a; pub(crate) mod mvex; pub(crate) mod mvhd; +pub(crate) mod opus; pub(crate) mod smhd; pub(crate) mod stbl; pub(crate) mod stco; @@ -206,7 +207,10 @@ boxtype! { DayBox => 0xa9646179, CovrBox => 0x636f7672, DescBox => 0x64657363, - WideBox => 0x77696465 + WideBox => 0x77696465, + DopsBox => 0x644F7073, + OpusBox => 0x4F707573 + } pub trait Mp4Box: Sized { diff --git a/src/mp4box/opus.rs b/src/mp4box/opus.rs new file mode 100644 index 0000000..7889fb9 --- /dev/null +++ b/src/mp4box/opus.rs @@ -0,0 +1,170 @@ +use crate::mp4box::*; +use crate::Mp4Box; +use serde::Serialize; + +// taken from the following sources +// - https://opus-codec.org/docs/opus_in_isobmff.html +// - chromium source code: box_definitions.h - OpusSpecificBox +// - async-mp4 crate: https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs + +// this OpusBox is a combination of the AudioSampleEntry box and OpusSpecificBox +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct OpusBox { + pub data_reference_index: u16, + pub channelcount: u16, + pub samplesize: u16, + + #[serde(with = "value_u32")] + pub samplerate: FixedPointU16, + pub dops: DopsBox, +} + +impl Mp4Box for OpusBox { + fn box_type(&self) -> BoxType { + BoxType::OpusBox + } + + fn box_size(&self) -> u64 { + // the +19 is for DopsBox + 36 + 19 + } + + fn to_json(&self) -> Result { + Ok(serde_json::to_string(&self).unwrap()) + } + + fn summary(&self) -> Result { + Ok(format!("{self:?}")) + } +} + +impl WriteBox<&mut W> for OpusBox { + fn write_box(&self, writer: &mut W) -> Result { + let mut written = 0; + written += BoxHeader::new(self.box_type(), self.box_size()).write(writer)?; + + writer.write_u32::(0)?; // reserved + written += 4; + writer.write_u16::(0)?; // reserved + written += 2; + writer.write_u16::(self.data_reference_index)?; + written += 2; + + writer.write_u16::(0)?; // reserved + written += 2; + writer.write_u16::(0)?; // reserved + written += 2; + writer.write_u32::(0)?; // reserved + written += 4; + writer.write_u16::(self.channelcount)?; + written += 2; + writer.write_u16::(self.samplesize)?; + written += 2; + writer.write_u32::(0)?; // reserved + written += 4; + writer.write_u32::(self.samplerate.raw_value())?; + written += 4; + + written += self.dops.write_box(writer)?; + + assert_eq!(written, self.box_size()); + Ok(written) + } +} + +// https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct DopsBox { + pub version: u8, + pub pre_skip: u16, + pub input_sample_rate: u32, + pub output_gain: i16, + pub channel_mapping_family: ChannelMappingFamily, +} + +impl Mp4Box for DopsBox { + fn box_type(&self) -> BoxType { + BoxType::DopsBox + } + + fn box_size(&self) -> u64 { + // if channel_mapping_family is updates to support more than 2 channels, + // box_size could change, depending on the channel mapping. + 19 + } + + fn to_json(&self) -> Result { + Ok(serde_json::to_string(&self).unwrap()) + } + + fn summary(&self) -> Result { + Ok(format!("{self:?}")) + } +} + +// https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs +impl WriteBox<&mut W> for DopsBox { + fn write_box(&self, writer: &mut W) -> Result { + let mut written = 0; + written += BoxHeader::new(self.box_type(), self.box_size()).write(writer)?; + writer.write_u8(self.version)?; + written += 1; + + let num_channels = match self.channel_mapping_family { + ChannelMappingFamily::Family0 { stereo } => match stereo { + true => 2, + false => 1, + }, + }; + writer.write_u8(num_channels)?; + written += 1; + writer.write_u16::(self.pre_skip)?; + written += 2; + writer.write_u32::(self.input_sample_rate)?; + written += 4; + writer.write_i16::(self.output_gain)?; + written += 2; + + // channel mapping family 0 + writer.write_u8(0)?; + written += 1; + + // todo: StreamCount? CoupledCount? ChannelMapping? + + assert_eq!(written, self.box_size()); + Ok(written) + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub enum ChannelMappingFamily { + Family0 { stereo: bool }, +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_opus_writer() { + let dops = DopsBox { + version: 0, + pre_skip: 1, + input_sample_rate: 2, + output_gain: 3, + channel_mapping_family: ChannelMappingFamily::Family0 { stereo: false }, + }; + + let opus = OpusBox { + data_reference_index: 1, + channelcount: 1, + samplesize: 2, + samplerate: FixedPointU16::new(48000), + dops, + }; + + let mut buffer = Vec::::new(); + opus.write_box(&mut buffer).expect("write_box failed"); + } +} From 09343a20eed538280ff6af38aa9c2e01ebe003ad Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Thu, 13 Jul 2023 10:09:58 -0400 Subject: [PATCH 04/23] fix(opus): export opus --- src/mp4box/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 2bc749e..ed7b948 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -113,6 +113,7 @@ pub use mdia::MdiaBox; pub use moof::MoofBox; pub use moov::MoovBox; pub use mvex::MvexBox; +pub use opus::*; pub use tfdt::TfdtBox; pub use tfhd::TfhdBox; pub use traf::TrafBox; From c84183a9429931aa12752310be8dc6fefb39088f Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Thu, 13 Jul 2023 11:04:43 -0400 Subject: [PATCH 05/23] feat(opus): add opus to StsdBox and implement read/write --- src/mp4box/opus.rs | 67 +++++++++++++++++++++++++++++++++++++++++----- src/mp4box/stsd.rs | 22 +++++++++++++++ 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/src/mp4box/opus.rs b/src/mp4box/opus.rs index 7889fb9..7d48b5c 100644 --- a/src/mp4box/opus.rs +++ b/src/mp4box/opus.rs @@ -38,6 +38,38 @@ impl Mp4Box for OpusBox { } } +impl ReadBox<&mut R> for OpusBox { + fn read_box(reader: &mut R, size: u64) -> Result { + box_start(reader)?; + reader.read_u32::()?; // reserved + reader.read_u16::()?; // reserved + let data_reference_index = reader.read_u16::()?; + reader.read_u64::()?; // reserved + let channelcount = reader.read_u16::()?; + let samplesize = reader.read_u16::()?; + reader.read_u32::()?; // reserved + let samplerate = reader.read_u32::()?; + + let num_read = 36; + if num_read > size { + return Err(Error::InvalidData("opus box is too small")); + } + let remaining = num_read.saturating_sub(size); + + // todo: if the size of DopsBox becomes variable, then the size of + // OpusBox will be needed to determine what size to pass to DopsBox::read_box() + let dops = DopsBox::read_box(reader, remaining)?; + + Ok(Self { + data_reference_index, + channelcount, + samplesize, + samplerate: FixedPointU16::new_raw(samplerate), + dops, + }) + } +} + impl WriteBox<&mut W> for OpusBox { fn write_box(&self, writer: &mut W) -> Result { let mut written = 0; @@ -50,12 +82,8 @@ impl WriteBox<&mut W> for OpusBox { writer.write_u16::(self.data_reference_index)?; written += 2; - writer.write_u16::(0)?; // reserved - written += 2; - writer.write_u16::(0)?; // reserved - written += 2; - writer.write_u32::(0)?; // reserved - written += 4; + writer.write_u64::(0)?; // reserved + written += 8; writer.write_u16::(self.channelcount)?; written += 2; writer.write_u16::(self.samplesize)?; @@ -102,6 +130,33 @@ impl Mp4Box for DopsBox { } } +impl ReadBox<&mut R> for DopsBox { + fn read_box(reader: &mut R, size: u64) -> Result { + if size < 19 { + return Err(Error::InvalidData("Dops box size too small")); + } + box_start(reader)?; + let version = reader.read_u8()?; + let num_channels = reader.read_u8()?; + let pre_skip = reader.read_u16::()?; + let input_sample_rate = reader.read_u32::()?; + let output_gain = reader.read_i16::()?; + + // todo: should this be used? + let _channel_mapping_family = reader.read_u8()?; + + Ok(DopsBox { + version, + pre_skip, + input_sample_rate, + output_gain, + channel_mapping_family: ChannelMappingFamily::Family0 { + stereo: num_channels == 2, + }, + }) + } +} + // https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs impl WriteBox<&mut W> for DopsBox { fn write_box(&self, writer: &mut W) -> Result { diff --git a/src/mp4box/stsd.rs b/src/mp4box/stsd.rs index af947c6..656742b 100644 --- a/src/mp4box/stsd.rs +++ b/src/mp4box/stsd.rs @@ -25,8 +25,21 @@ pub struct StsdBox { #[serde(skip_serializing_if = "Option::is_none")] pub tx3g: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub opus: Option, } +// todo: all the Options in StsdBox may be better as an enumeration +// pub enum StsdChildBox { +// Avc1(Avc1Box), +// Hev1(Hev1Box), +// Vp09(Vp09Box), +// Mp4a(Mp4aBox), +// Tx3g(Tx3gBox), +// Opus(OpusBox), +// } + impl StsdBox { pub fn get_type(&self) -> BoxType { BoxType::StsdBox @@ -44,6 +57,8 @@ impl StsdBox { size += mp4a.box_size(); } else if let Some(ref tx3g) = self.tx3g { size += tx3g.box_size(); + } else if let Some(ref opus) = self.opus { + size += opus.box_size(); } size } @@ -81,6 +96,7 @@ impl ReadBox<&mut R> for StsdBox { let mut vp09 = None; let mut mp4a = None; let mut tx3g = None; + let mut opus = None; // Get box header. let header = BoxHeader::read(reader)?; @@ -107,6 +123,9 @@ impl ReadBox<&mut R> for StsdBox { BoxType::Tx3gBox => { tx3g = Some(Tx3gBox::read_box(reader, s)?); } + BoxType::OpusBox => { + opus = Some(OpusBox::read_box(reader, s)?); + } _ => {} } @@ -120,6 +139,7 @@ impl ReadBox<&mut R> for StsdBox { vp09, mp4a, tx3g, + opus, }) } } @@ -143,6 +163,8 @@ impl WriteBox<&mut W> for StsdBox { mp4a.write_box(writer)?; } else if let Some(ref tx3g) = self.tx3g { tx3g.write_box(writer)?; + } else if let Some(ref opus) = self.opus { + opus.write_box(writer)?; } Ok(size) From 9d002140615d82769407f6a505c2b6573dff3042 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Thu, 13 Jul 2023 11:40:45 -0400 Subject: [PATCH 06/23] expose stsd box --- src/mp4box/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index ed7b948..cf2a616 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -114,6 +114,7 @@ pub use moof::MoofBox; pub use moov::MoovBox; pub use mvex::MvexBox; pub use opus::*; +pub use stsd::StsdBox; pub use tfdt::TfdtBox; pub use tfhd::TfhdBox; pub use traf::TrafBox; From 66f177005bbaeb4fd9db283e04a673f095444c0a Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Thu, 13 Jul 2023 16:38:57 -0400 Subject: [PATCH 07/23] expose mfhd box --- src/mp4box/mod.rs | 1 + src/mp4box/mvex.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index cf2a616..3085777 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -110,6 +110,7 @@ pub(crate) mod vpcc; pub use emsg::EmsgBox; pub use ftyp::FtypBox; pub use mdia::MdiaBox; +pub use mfhd::MfhdBox; pub use moof::MoofBox; pub use moov::MoovBox; pub use mvex::MvexBox; diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index 8be683b..8eb0e53 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -7,6 +7,7 @@ use crate::mp4box::{mehd::MehdBox, trex::TrexBox}; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct MvexBox { pub mehd: Option, + // todo: this should be a vec pub trex: TrexBox, } From c669d7b08038884d202da1b02e1fdaef49b5305e Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 00:54:25 -0400 Subject: [PATCH 08/23] fix(TrafBox): write out tfdt and trun --- src/mp4box/traf.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mp4box/traf.rs b/src/mp4box/traf.rs index 51f812d..95e6f6e 100644 --- a/src/mp4box/traf.rs +++ b/src/mp4box/traf.rs @@ -104,6 +104,12 @@ impl WriteBox<&mut W> for TrafBox { BoxHeader::new(self.box_type(), size).write(writer)?; self.tfhd.write_box(writer)?; + if let Some(ref tfdt) = self.tfdt { + tfdt.write_box(writer)?; + } + if let Some(ref trun) = self.trun { + trun.write_box(writer)?; + } Ok(size) } From d5eec0666e2d56c7cc4a3ed9f11e753fd7134673 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 10:50:51 -0400 Subject: [PATCH 09/23] fix get_size() for trunbox --- src/mp4box/trun.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mp4box/trun.rs b/src/mp4box/trun.rs index efbb2b0..3ca2989 100644 --- a/src/mp4box/trun.rs +++ b/src/mp4box/trun.rs @@ -55,6 +55,11 @@ impl TrunBox { if TrunBox::FLAG_SAMPLE_CTS & self.flags > 0 { sum += 4 * self.sample_count as u64; } + + sum += (4 * self.sample_durations.len()) as u64; + sum += (4 * self.sample_sizes.len()) as u64; + sum += (4 * self.sample_flags.len()) as u64; + sum += (4 * self.sample_cts.len()) as u64; sum } } From cd86f1c4be6bdc264f366f100132247cc56d96ca Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 11:00:16 -0400 Subject: [PATCH 10/23] undo bugs introduced to TrunBox by previous commit(s) --- src/mp4box/trun.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/mp4box/trun.rs b/src/mp4box/trun.rs index 3ca2989..6f9f1d0 100644 --- a/src/mp4box/trun.rs +++ b/src/mp4box/trun.rs @@ -44,22 +44,18 @@ impl TrunBox { sum += 4; } if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 { - sum += 4 * self.sample_count as u64; + sum += (4 * self.sample_durations.len()) as u64; } if TrunBox::FLAG_SAMPLE_SIZE & self.flags > 0 { - sum += 4 * self.sample_count as u64; + sum += (4 * self.sample_sizes.len()) as u64; } if TrunBox::FLAG_SAMPLE_FLAGS & self.flags > 0 { - sum += 4 * self.sample_count as u64; + sum += (4 * self.sample_flags.len()) as u64; } if TrunBox::FLAG_SAMPLE_CTS & self.flags > 0 { - sum += 4 * self.sample_count as u64; + sum += (4 * self.sample_cts.len()) as u64; } - sum += (4 * self.sample_durations.len()) as u64; - sum += (4 * self.sample_sizes.len()) as u64; - sum += (4 * self.sample_flags.len()) as u64; - sum += (4 * self.sample_cts.len()) as u64; sum } } @@ -190,9 +186,7 @@ impl WriteBox<&mut W> for TrunBox { if let Some(v) = self.first_sample_flags { writer.write_u32::(v)?; } - if self.sample_count != self.sample_sizes.len() as u32 { - return Err(Error::InvalidData("sample count out of sync")); - } + for i in 0..self.sample_count as usize { if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 { writer.write_u32::(self.sample_durations[i])?; From 8ce513414ca7063a7cb324e2064c5cf9f99d14bb Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 11:38:41 -0400 Subject: [PATCH 11/23] fix(moof): size returned by write_box --- src/mp4box/moof.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mp4box/moof.rs b/src/mp4box/moof.rs index 20c3565..1b1e932 100644 --- a/src/mp4box/moof.rs +++ b/src/mp4box/moof.rs @@ -102,6 +102,6 @@ impl WriteBox<&mut W> for MoofBox { for traf in self.trafs.iter() { traf.write_box(writer)?; } - Ok(0) + Ok(size) } } From 23b8c86eeccd204a31adae24356b91192fab9299 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 12:13:42 -0400 Subject: [PATCH 12/23] fix(traf): box size didn't include tfdt --- src/mp4box/traf.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mp4box/traf.rs b/src/mp4box/traf.rs index 95e6f6e..f9d6bfa 100644 --- a/src/mp4box/traf.rs +++ b/src/mp4box/traf.rs @@ -19,9 +19,8 @@ impl TrafBox { pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE; size += self.tfhd.box_size(); - if let Some(ref trun) = self.trun { - size += trun.box_size(); - } + size += self.tfdt.as_ref().map(|x| x.box_size()).unwrap_or(0); + size += self.trun.as_ref().map(|x| x.box_size()).unwrap_or(0); size } } From 90b90301ab0173c82f1ec024fd47a954f4c5563a Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 15:00:24 -0400 Subject: [PATCH 13/23] expose more boxes --- src/mp4box/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 3085777..9daa90b 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -107,17 +107,27 @@ pub(crate) mod vmhd; pub(crate) mod vp09; pub(crate) mod vpcc; +pub use dinf::*; pub use emsg::EmsgBox; pub use ftyp::FtypBox; +pub use hdlr::HdlrBox; pub use mdia::MdiaBox; pub use mfhd::MfhdBox; +pub use minf::MinfBox; pub use moof::MoofBox; pub use moov::MoovBox; pub use mvex::MvexBox; +pub use mvhd::MvhdBox; pub use opus::*; +pub use smhd::SmhdBox; +pub use stbl::StblBox; +pub use stsc::StscBox; pub use stsd::StsdBox; +pub use stsz::StszBox; +pub use stts::SttsBox; pub use tfdt::TfdtBox; pub use tfhd::TfhdBox; +pub use tkhd::*; pub use traf::TrafBox; pub use trak::TrakBox; pub use trex::TrexBox; From 5d070ebfbf6eb595d0a1a0a1e84bc63c0e9b8647 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 15:01:05 -0400 Subject: [PATCH 14/23] expose more boxes --- src/mp4box/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 9daa90b..3d8412c 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -111,6 +111,7 @@ pub use dinf::*; pub use emsg::EmsgBox; pub use ftyp::FtypBox; pub use hdlr::HdlrBox; +pub use mdhd::MdhdBox; pub use mdia::MdiaBox; pub use mfhd::MfhdBox; pub use minf::MinfBox; From 52e8a8b654d7e49dde50f02c8b83e49d4572d592 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 16:40:42 -0400 Subject: [PATCH 15/23] expose StcoBox --- src/mp4box/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 3d8412c..58e79d3 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -122,6 +122,7 @@ pub use mvhd::MvhdBox; pub use opus::*; pub use smhd::SmhdBox; pub use stbl::StblBox; +pub use stco::StcoBox; pub use stsc::StscBox; pub use stsd::StsdBox; pub use stsz::StszBox; From aeb75eb9f274133adeab091614c0e41634535a0f Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 16:46:36 -0400 Subject: [PATCH 16/23] fix(moovbox): write out mvex box. --- src/mp4box/moov.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mp4box/moov.rs b/src/mp4box/moov.rs index ac19381..872ec7f 100644 --- a/src/mp4box/moov.rs +++ b/src/mp4box/moov.rs @@ -131,16 +131,20 @@ impl WriteBox<&mut W> for MoovBox { BoxHeader::new(self.box_type(), size).write(writer)?; self.mvhd.write_box(writer)?; - for trak in self.traks.iter() { - trak.write_box(writer)?; - } if let Some(meta) = &self.meta { meta.write_box(writer)?; } + if let Some(mvex) = &self.mvex { + mvex.write_box(writer)?; + } + for trak in self.traks.iter() { + trak.write_box(writer)?; + } + if let Some(udta) = &self.udta { udta.write_box(writer)?; } - Ok(0) + Ok(size) } } From 4b88f0459e576092300a8e0b7dbe3a0eb97c0fbd Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 16:53:49 -0400 Subject: [PATCH 17/23] fix(trex): write trexbox and make it a vec --- src/mp4box/moov.rs | 1 - src/mp4box/mvex.rs | 21 ++++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/mp4box/moov.rs b/src/mp4box/moov.rs index 872ec7f..cec21c6 100644 --- a/src/mp4box/moov.rs +++ b/src/mp4box/moov.rs @@ -140,7 +140,6 @@ impl WriteBox<&mut W> for MoovBox { for trak in self.traks.iter() { trak.write_box(writer)?; } - if let Some(udta) = &self.udta { udta.write_box(writer)?; } diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index 8eb0e53..44a4f19 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -7,8 +7,7 @@ use crate::mp4box::{mehd::MehdBox, trex::TrexBox}; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct MvexBox { pub mehd: Option, - // todo: this should be a vec - pub trex: TrexBox, + pub trex: Vec, } impl MvexBox { @@ -17,7 +16,8 @@ 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 trex_sizes = self.trex.iter().fold(0, |acc, x| acc + x.box_size()); + HEADER_SIZE + self.mehd.as_ref().map(|x| x.box_size()).unwrap_or(0) + trex_sizes } } @@ -45,7 +45,7 @@ impl ReadBox<&mut R> for MvexBox { let start = box_start(reader)?; let mut mehd = None; - let mut trex = None; + let mut trex: Vec = Vec::new(); let mut current = reader.stream_position()?; let end = start + size; @@ -64,7 +64,7 @@ impl ReadBox<&mut R> for MvexBox { mehd = Some(MehdBox::read_box(reader, s)?); } BoxType::TrexBox => { - trex = Some(TrexBox::read_box(reader, s)?); + trex.push(TrexBox::read_box(reader, s)?); } _ => { // XXX warn!() @@ -75,16 +75,13 @@ impl ReadBox<&mut R> for MvexBox { current = reader.stream_position()?; } - if trex.is_none() { + if trex.is_empty() { return Err(Error::BoxNotFound(BoxType::TrexBox)); } skip_bytes_to(reader, start + size)?; - Ok(MvexBox { - mehd, - trex: trex.unwrap(), - }) + Ok(MvexBox { mehd, trex }) } } @@ -96,7 +93,9 @@ 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.trex { + trex.write_box(writer)?; + } Ok(size) } From 4d676b9da2b8cbd6c800e69e48ec3e126d555123 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 16:57:27 -0400 Subject: [PATCH 18/23] fix(reader): didn't compile --- src/reader.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/reader.rs b/src/reader.rs index 5a39eeb..80c31c1 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -97,7 +97,12 @@ impl Mp4Reader { 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 + // todo: do this better. matches the original behavior before mvex.trex was changed to an array. + default_sample_duration = mvex + .trex + .first() + .map(|x| x.default_sample_duration) + .unwrap_or(0) } } From 6a591766df8d8607fde943cf6b69c3ad5032c03d Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 16:58:32 -0400 Subject: [PATCH 19/23] fix(trex): didn't compile --- examples/mp4dump.rs | 4 +++- src/mp4box/mvex.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/mp4dump.rs b/examples/mp4dump.rs index 6a97d9a..597bb70 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.trex { + boxes.push(build_box(trex)); + } } // trak. diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index 44a4f19..b0afe9a 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -93,7 +93,7 @@ impl WriteBox<&mut W> for MvexBox { if let Some(mehd) = &self.mehd { mehd.write_box(writer)?; } - for trex in self.trex { + for trex in &self.trex { trex.write_box(writer)?; } From 68c905d5f811e24b0d73155871032bbc289aec6c Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 17:29:03 -0400 Subject: [PATCH 20/23] fix(moov): update box size now that mvex is getting written --- src/mp4box/moov.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/mp4box/moov.rs b/src/mp4box/moov.rs index cec21c6..debdaeb 100644 --- a/src/mp4box/moov.rs +++ b/src/mp4box/moov.rs @@ -29,15 +29,14 @@ impl MoovBox { pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE + self.mvhd.box_size(); + size += self.meta.as_ref().map(|x| x.box_size()).unwrap_or(0); + size += self.mvex.as_ref().map(|x| x.box_size()).unwrap_or(0); + for trak in self.traks.iter() { size += trak.box_size(); } - if let Some(meta) = &self.meta { - size += meta.box_size(); - } - if let Some(udta) = &self.udta { - size += udta.box_size(); - } + + size += self.udta.as_ref().map(|x| x.box_size()).unwrap_or(0); size } } From c41654fdd590fb1856cd76ed2254ff8a18cb728c Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Fri, 14 Jul 2023 17:36:31 -0400 Subject: [PATCH 21/23] fix(mvex): wrong BoxType --- src/mp4box/mvex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index b0afe9a..2941c4a 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -12,7 +12,7 @@ pub struct MvexBox { impl MvexBox { pub fn get_type(&self) -> BoxType { - BoxType::MdiaBox + BoxType::MvexBox } pub fn get_size(&self) -> u64 { From 9887024513f59e5fcda9f44442b2a3a2cdfe9706 Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Mon, 17 Jul 2023 09:55:17 -0400 Subject: [PATCH 22/23] fix(opus): remove assert_eq --- src/mp4box/opus.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mp4box/opus.rs b/src/mp4box/opus.rs index 7d48b5c..438e40e 100644 --- a/src/mp4box/opus.rs +++ b/src/mp4box/opus.rs @@ -95,8 +95,11 @@ impl WriteBox<&mut W> for OpusBox { written += self.dops.write_box(writer)?; - assert_eq!(written, self.box_size()); - Ok(written) + if written != self.box_size() { + Err(Error::InvalidData("written != box_size for OpusBox")) + } else { + Ok(written) + } } } From b3ba12433e27a928f1e66f1f86ec6094a1eb0a2f Mon Sep 17 00:00:00 2001 From: Stuart Woodbury Date: Thu, 3 Aug 2023 11:18:24 -0400 Subject: [PATCH 23/23] fix(read_header): find correct default sample duration --- src/reader.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/reader.rs b/src/reader.rs index 9021124..a144583 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -97,23 +97,21 @@ 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 { - // todo: do this better. matches the original behavior before mvex.trex was changed to an array. - default_sample_duration = mvex - .trex - .first() - .map(|x| x.default_sample_duration) - .unwrap_or(0) - } - } - for (moof, moof_offset) in moofs.iter().zip(moof_offsets) { for traf in moof.trafs.iter() { let track_id = traf.tfhd.track_id; if let Some(track) = tracks.get_mut(&track_id) { - track.default_sample_duration = default_sample_duration; + track.default_sample_duration = moov + .as_ref() + .and_then(|moov| { + moov.mvex.as_ref().and_then(|mvex| { + mvex.trex + .iter() + .find(|trex| trex.track_id == track.track_id()) + .map(|trex| trex.default_sample_duration) + }) + }) + .unwrap_or(0); track.moof_offsets.push(moof_offset); track.trafs.push(traf.clone()) } else {