Skip to content

support sidx atom #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/mp4box/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -204,6 +206,7 @@ boxtype! {
HdlrBox => 0x68646c72,
MinfBox => 0x6d696e66,
VmhdBox => 0x766d6864,
SidxBox => 0x73696478,
StblBox => 0x7374626c,
StsdBox => 0x73747364,
SttsBox => 0x73747473,
Expand Down
279 changes: 279 additions & 0 deletions src/mp4box/sidx.rs
Original file line number Diff line number Diff line change
@@ -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<Segment>,
}

#[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<String> {
Ok(serde_json::to_string(&self).unwrap())
}

fn summary(&self) -> Result<String> {
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<R: Read + Seek> ReadBox<&mut R> for SidxBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
)
} else if version == 1 {
(
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
return Err(Error::InvalidData("version must be 0 or 1"));
};
let reserved = reader.read_u16::<BigEndian>()?;
let reference_count = reader.read_u16::<BigEndian>()?;
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<W: Write> WriteBox<&mut W> for SidxBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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::<BigEndian>(self.reference_id)?;
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u64::<BigEndian>(self.earliest_presentation_time)?;
writer.write_u64::<BigEndian>(self.first_offset)?;
} else if self.version == 0 {
writer.write_u32::<BigEndian>(self.reference_id)?;
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u32::<BigEndian>(self.earliest_presentation_time as u32)?;
writer.write_u32::<BigEndian>(self.first_offset as u32)?;
} else {
return Err(Error::InvalidData("version must be 0 or 1"));
}
writer.write_u16::<BigEndian>(self.reserved)?; // reserved = 0
writer.write_u16::<BigEndian>(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<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let reference = reader.read_u32::<BigEndian>()?;
let reference_type = reference >> 31 == 1;
let reference_size = reference & 0x7FFFFFFF;
let subsegment_duration = reader.read_u32::<BigEndian>()?;
let sap = reader.read_u32::<BigEndian>()?;
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<W: Write>(&self, writer: &mut W) -> Result<u64> {
let reference_type_flag = u32::from(self.reference_type) << 31;
writer.write_u32::<BigEndian>(reference_type_flag | self.reference_size)?;
writer.write_u32::<BigEndian>(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::<BigEndian>(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);
}
}
9 changes: 8 additions & 1 deletion src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct Mp4Reader<R> {
reader: R,
pub ftyp: FtypBox,
pub moov: MoovBox,
pub sidx: Option<SidxBox>,
pub moofs: Vec<MoofBox>,
pub emsgs: Vec<EmsgBox>,

Expand All @@ -23,6 +24,7 @@ impl<R: Read + Seek> Mp4Reader<R> {

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();
Expand Down Expand Up @@ -57,6 +59,9 @@ impl<R: Read + Seek> Mp4Reader<R> {
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)?;
Expand Down Expand Up @@ -122,6 +127,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
reader,
ftyp: ftyp.unwrap(),
moov: moov.unwrap(),
sidx,
moofs,
emsgs,
size,
Expand Down Expand Up @@ -207,7 +213,8 @@ impl<R: Read + Seek> Mp4Reader<R> {
Ok(Mp4Reader {
reader,
ftyp: self.ftyp.clone(),
moov: self.moov.clone(),
moov: self.moov.clone(),
sidx: self.sidx.clone(),
moofs,
emsgs: Vec::new(),
tracks,
Expand Down
Loading