From 7360c5ccb1dde1b2c84f8e309a53d0f7c7cc5cfb Mon Sep 17 00:00:00 2001 From: Damitha Gunwardena Date: Mon, 23 Dec 2024 15:59:56 +1100 Subject: [PATCH 1/3] support sidx --- src/mp4box/mod.rs | 3 + src/mp4box/sidx.rs | 279 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+) create mode 100644 src/mp4box/sidx.rs diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 4bbdd410..264929b2 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 sidx; pub(crate) mod smhd; pub(crate) mod stbl; pub(crate) mod stco; @@ -129,6 +130,7 @@ pub use moov::MoovBox; pub use mp4a::Mp4aBox; pub use mvex::MvexBox; pub use mvhd::MvhdBox; +pub use sidx::SidxBox; pub use smhd::SmhdBox; pub use stbl::StblBox; pub use stco::StcoBox; @@ -204,6 +206,7 @@ boxtype! { HdlrBox => 0x68646c72, MinfBox => 0x6d696e66, VmhdBox => 0x766d6864, + SidxBox => 0x73696478, StblBox => 0x7374626c, StsdBox => 0x73747364, SttsBox => 0x73747473, diff --git a/src/mp4box/sidx.rs b/src/mp4box/sidx.rs new file mode 100644 index 00000000..cd6223c7 --- /dev/null +++ b/src/mp4box/sidx.rs @@ -0,0 +1,279 @@ +use serde::Serialize; + +use crate::mp4box::*; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct SidxBox { + pub version: u8, + pub flags: u32, + + pub reference_id: u32, + pub timescale: u32, + pub earliest_presentation_time: u64, + pub first_offset: u64, + pub reserved: u16, + pub reference_count: u16, + pub segments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct Segment { + pub reference_type: bool, + pub reference_size: u32, + pub subsegment_duration: u32, + pub starts_with_sap: bool, + pub sap_type: u8, + pub sap_delta_time: u32, +} + +impl SidxBox { + pub fn get_type(&self) -> BoxType { + BoxType::SidxBox + } + + pub fn get_size(&self) -> u64 { + let mut size = HEADER_SIZE + HEADER_EXT_SIZE; + if self.version == 1 { + size += 24; + } else if self.version == 0 { + size += 16; + } + size += 4; + size += self.reference_count as u64 * 12; + size + } +} + +impl Default for SidxBox { + fn default() -> Self { + SidxBox { + version: 0, + flags: 0, + timescale: 1000, + first_offset: 0, + earliest_presentation_time: 0, + reference_id: 0, + reserved: 0, + reference_count: 0, + segments: Vec::new(), + } + } +} + +impl Mp4Box for SidxBox { + 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!( + "timescale={} first_offset={} earliest_presentation_time={} reference_id={}, reserved={}, reference_count={}", + self.timescale, + self.first_offset, + self.earliest_presentation_time, + self.reference_id, + self.reserved, + self.reference_count, + ); + Ok(s) + } +} + +impl ReadBox<&mut R> for SidxBox { + fn read_box(reader: &mut R, size: u64) -> Result { + let start = box_start(reader)?; + + let (version, flags) = read_box_header_ext(reader)?; + + let (reference_id, timescale, earliest_presentation_time, first_offset) = if version == 0 { + ( + reader.read_u32::()?, + reader.read_u32::()?, + reader.read_u32::()? as u64, + reader.read_u32::()? as u64, + ) + } else if version == 1 { + ( + reader.read_u32::()?, + reader.read_u32::()?, + reader.read_u64::()?, + reader.read_u64::()?, + ) + } else { + return Err(Error::InvalidData("version must be 0 or 1")); + }; + let reserved = reader.read_u16::()?; + let reference_count = reader.read_u16::()?; + let mut segments = Vec::with_capacity(reference_count as usize); + + for _ in 0..reference_count { + let segment = Segment::read(reader)?; + segments.push(segment); + } + + skip_bytes_to(reader, start + size)?; + + Ok(SidxBox { + version, + flags, + reference_id, + timescale, + earliest_presentation_time, + first_offset, + reserved, + reference_count, + segments, + }) + } +} + +impl WriteBox<&mut W> for SidxBox { + 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_u32::(self.reference_id)?; + writer.write_u32::(self.timescale)?; + writer.write_u64::(self.earliest_presentation_time)?; + writer.write_u64::(self.first_offset)?; + } else if self.version == 0 { + writer.write_u32::(self.reference_id)?; + writer.write_u32::(self.timescale)?; + writer.write_u32::(self.earliest_presentation_time as u32)?; + writer.write_u32::(self.first_offset as u32)?; + } else { + return Err(Error::InvalidData("version must be 0 or 1")); + } + writer.write_u16::(self.reserved)?; // reserved = 0 + writer.write_u16::(self.reference_count)?; // reserved = 0 + + for segment in self.segments.iter() { + segment.write(writer)?; + } + + Ok(size) + } +} + +impl Segment { + fn size(&self) -> usize { + 12 as usize + } + + fn read(reader: &mut R) -> Result { + let reference = reader.read_u32::()?; + let reference_type = reference >> 31 == 1; + let reference_size = reference & 0x7FFFFFFF; + let subsegment_duration = reader.read_u32::()?; + let sap = reader.read_u32::()?; + let starts_with_sap = sap >> 31 == 1; + let sap_type = (sap & 0x70000000 >> 28) as u8; + let sap_delta_time = sap & 0x0FFFFFFF; + Ok(Segment { + reference_type, + reference_size, + subsegment_duration, + starts_with_sap, + sap_type, + sap_delta_time, + }) + } + + fn write(&self, writer: &mut W) -> Result { + let reference_type_flag = u32::from(self.reference_type) << 31; + writer.write_u32::(reference_type_flag | self.reference_size)?; + writer.write_u32::(self.subsegment_duration)?; + let starts_with_sap_flag = u32::from(self.starts_with_sap) << 31; + let sap_type = (self.sap_type as u32) << 28; + writer.write_u32::(starts_with_sap_flag | sap_type | self.sap_delta_time)?; + Ok(self.size() as u64) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mp4box::BoxHeader; + use std::io::Cursor; + + #[test] + fn test_sidx32() { + let segment = Segment { + reference_type: false, + reference_size: 0, + subsegment_duration: 123000, + starts_with_sap: false, + sap_type: 0, + sap_delta_time: 0, + }; + + let src_box = SidxBox { + version: 0, + flags: 0, + reference_id: 0, + timescale: 0, + earliest_presentation_time: 1344, + first_offset: 212, + reserved: 0, + reference_count: 1, + segments: vec![segment], + }; + 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::SidxBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = SidxBox::read_box(&mut reader, header.size).unwrap(); + assert_eq!(src_box, dst_box); + } + + #[test] + fn test_sidx64() { + let segment = Segment { + reference_type: false, + reference_size: 0, + subsegment_duration: 123000, + starts_with_sap: false, + sap_type: 0, + sap_delta_time: 0, + }; + + let src_box = SidxBox { + version: 1, + flags: 0, + reference_id: 0, + timescale: 0, + earliest_presentation_time: 1344, + first_offset: 212, + reserved: 0, + reference_count: 1, + segments: vec![segment], + }; + 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::SidxBox); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = SidxBox::read_box(&mut reader, header.size).unwrap(); + assert_eq!(src_box, dst_box); + } +} From da7debf4343291427b864fcff6ba6d3b4c80da56 Mon Sep 17 00:00:00 2001 From: Damitha Gunwardena Date: Mon, 23 Dec 2024 16:11:26 +1100 Subject: [PATCH 2/3] reader update --- src/reader.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/reader.rs b/src/reader.rs index e5ac2964..2b9cd2a1 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -10,6 +10,7 @@ pub struct Mp4Reader { reader: R, pub ftyp: FtypBox, pub moov: MoovBox, + pub sidx: Option, pub moofs: Vec, pub emsgs: Vec, @@ -23,6 +24,7 @@ impl Mp4Reader { let mut ftyp = None; let mut moov = None; + let mut sidx = None; let mut moofs = Vec::new(); let mut moof_offsets = Vec::new(); let mut emsgs = Vec::new(); @@ -57,6 +59,9 @@ impl Mp4Reader { BoxType::MoovBox => { moov = Some(MoovBox::read_box(&mut reader, s)?); } + BoxType::SidxBox => { + sidx = Some(SidxBox::read_box(&mut reader, s)?); + } BoxType::MoofBox => { let moof_offset = reader.stream_position()? - 8; let moof = MoofBox::read_box(&mut reader, s)?; @@ -122,6 +127,7 @@ impl Mp4Reader { reader, ftyp: ftyp.unwrap(), moov: moov.unwrap(), + sidx: sidx, moofs, emsgs, size, @@ -208,6 +214,7 @@ impl Mp4Reader { reader, ftyp: self.ftyp.clone(), moov: self.moov.clone(), + sidx: self.sidx.clone(), moofs, emsgs: Vec::new(), tracks, From f58f4efdf30942d1490b3949ff65e0873d6aaac3 Mon Sep 17 00:00:00 2001 From: Damitha Gunwardena Date: Mon, 23 Dec 2024 17:32:17 +1100 Subject: [PATCH 3/3] . --- src/reader.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reader.rs b/src/reader.rs index 2b9cd2a1..bd60afa9 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -127,7 +127,7 @@ impl Mp4Reader { reader, ftyp: ftyp.unwrap(), moov: moov.unwrap(), - sidx: sidx, + sidx, moofs, emsgs, size, @@ -213,7 +213,7 @@ impl Mp4Reader { Ok(Mp4Reader { reader, ftyp: self.ftyp.clone(), - moov: self.moov.clone(), + moov: self.moov.clone(), sidx: self.sidx.clone(), moofs, emsgs: Vec::new(),