Skip to content

Fix fragmeted wrong start time and sync #131

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
10 changes: 5 additions & 5 deletions examples/mp4dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {

// collect known boxes
let mut boxes = vec![
build_box(&mp4.ftyp),
build_box(&mp4.moov),
build_box(&mp4.moov.mvhd),
build_box(&mp4.header.ftyp),
build_box(&mp4.header.moov),
build_box(&mp4.header.moov.mvhd),
];

if let Some(ref mvex) = &mp4.moov.mvex {
if let Some(ref mvex) = &mp4.header.moov.mvex {
boxes.push(build_box(mvex));
if let Some(mehd) = &mvex.mehd {
boxes.push(build_box(mehd));
Expand Down Expand Up @@ -117,7 +117,7 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
}

// If fragmented, add moof boxes.
for moof in mp4.moofs.iter() {
for moof in mp4.header.moofs.iter() {
boxes.push(build_box(moof));
boxes.push(build_box(&moof.mfhd));
for traf in moof.trafs.iter() {
Expand Down
4 changes: 2 additions & 2 deletions examples/mp4info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
println!(" compatible_brands: {}\n", compatible_brands);

println!("Movie:");
println!(" version: {}", mp4.moov.mvhd.version);
println!(" version: {}", mp4.header.moov.mvhd.version);
println!(
" creation time: {}",
creation_time(mp4.moov.mvhd.creation_time)
creation_time(mp4.header.moov.mvhd.creation_time)
);
println!(" duration: {:?}", mp4.duration());
println!(" fragments: {:?}", mp4.is_fragmented());
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
//! let mp4 = mp4::Mp4Reader::read_header(reader, size)?;
//!
//! // Print boxes.
//! println!("major brand: {}", mp4.ftyp.major_brand);
//! println!("timescale: {}", mp4.moov.mvhd.timescale);
//! println!("major brand: {}", mp4.header.ftyp.major_brand);
//! println!("timescale: {}", mp4.header.moov.mvhd.timescale);
//!
//! // Use available methods.
//! println!("size: {}", mp4.size());
Expand Down Expand Up @@ -83,7 +83,7 @@ mod track;
pub use track::{Mp4Track, TrackConfig};

mod reader;
pub use reader::Mp4Reader;
pub use reader::{Mp4Header, Mp4Reader};

mod writer;
pub use writer::{Mp4Config, Mp4Writer};
Expand Down
141 changes: 109 additions & 32 deletions src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use crate::meta::MetaBox;
use crate::*;

#[derive(Debug)]
pub struct Mp4Reader<R> {
reader: R,
pub struct Mp4Header {
pub ftyp: FtypBox,
pub moov: MoovBox,
pub moofs: Vec<MoofBox>,
Expand All @@ -17,8 +16,8 @@ pub struct Mp4Reader<R> {
size: u64,
}

impl<R: Read + Seek> Mp4Reader<R> {
pub fn read_header(mut reader: R, size: u64) -> Result<Self> {
impl Mp4Header {
pub fn read<R: Read + Seek>(reader: &mut R, size: u64) -> Result<Self> {
let start = reader.stream_position()?;

let mut ftyp = None;
Expand All @@ -30,12 +29,10 @@ impl<R: Read + Seek> Mp4Reader<R> {
let mut current = start;
while current < size {
// Get box header.
let header = BoxHeader::read(&mut reader)?;
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"file contains a box with a larger size than it",
));
break;
}

// Break if size zero BoxHeader, which can result in dead-loop.
Expand All @@ -46,30 +43,30 @@ impl<R: Read + Seek> Mp4Reader<R> {
// Match and parse the atom boxes.
match name {
BoxType::FtypBox => {
ftyp = Some(FtypBox::read_box(&mut reader, s)?);
ftyp = Some(FtypBox::read_box(reader, s)?);
}
BoxType::FreeBox => {
skip_box(&mut reader, s)?;
skip_box(reader, s)?;
}
BoxType::MdatBox => {
skip_box(&mut reader, s)?;
skip_box(reader, s)?;
}
BoxType::MoovBox => {
moov = Some(MoovBox::read_box(&mut reader, s)?);
moov = Some(MoovBox::read_box(reader, s)?);
}
BoxType::MoofBox => {
let moof_offset = reader.stream_position()? - 8;
let moof = MoofBox::read_box(&mut reader, s)?;
let moof = MoofBox::read_box(reader, s)?;
moofs.push(moof);
moof_offsets.push(moof_offset);
}
BoxType::EmsgBox => {
let emsg = EmsgBox::read_box(&mut reader, s)?;
let emsg = EmsgBox::read_box(reader, s)?;
emsgs.push(emsg);
}
_ => {
// XXX warn!()
skip_box(&mut reader, s)?;
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
Expand Down Expand Up @@ -118,8 +115,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
}
}

Ok(Mp4Reader {
reader,
Ok(Mp4Header {
ftyp: ftyp.unwrap(),
moov: moov.unwrap(),
moofs,
Expand All @@ -129,11 +125,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
})
}

pub fn read_fragment_header<FR: Read + Seek>(
&self,
mut reader: FR,
size: u64,
) -> Result<Mp4Reader<FR>> {
pub fn read_fragment<R: Read + Seek>(&self, reader: &mut R, size: u64) -> Result<Self> {
let start = reader.stream_position()?;

let mut moofs = Vec::new();
Expand All @@ -142,7 +134,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
let mut current = start;
while current < size {
// Get box header.
let header = BoxHeader::read(&mut reader)?;
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
Expand All @@ -158,17 +150,17 @@ impl<R: Read + Seek> Mp4Reader<R> {
// Match and parse the atom boxes.
match name {
BoxType::MdatBox => {
skip_box(&mut reader, s)?;
skip_box(reader, s)?;
}
BoxType::MoofBox => {
let moof_offset = reader.stream_position()? - 8;
let moof = MoofBox::read_box(&mut reader, s)?;
let moof = MoofBox::read_box(reader, s)?;
moofs.push(moof);
moof_offsets.push(moof_offset);
}
_ => {
// XXX warn!()
skip_box(&mut reader, s)?;
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
Expand Down Expand Up @@ -204,8 +196,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
}
}

Ok(Mp4Reader {
reader,
Ok(Mp4Header {
ftyp: self.ftyp.clone(),
moov: self.moov.clone(),
moofs,
Expand All @@ -215,10 +206,12 @@ impl<R: Read + Seek> Mp4Reader<R> {
})
}

#[inline]
pub fn size(&self) -> u64 {
self.size
}

#[inline]
pub fn major_brand(&self) -> &FourCC {
&self.ftyp.major_brand
}
Expand Down Expand Up @@ -255,9 +248,14 @@ impl<R: Read + Seek> Mp4Reader<R> {
}
}

pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result<Option<Mp4Sample>> {
pub fn read_sample<R: Read + Seek>(
&mut self,
reader: &mut R,
track_id: u32,
sample_id: u32,
) -> Result<Option<Mp4Sample>> {
if let Some(track) = self.tracks.get(&track_id) {
track.read_sample(&mut self.reader, sample_id)
track.read_sample(reader, sample_id)
} else {
Err(Error::TrakNotFound(track_id))
}
Expand All @@ -270,9 +268,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
Err(Error::TrakNotFound(track_id))
}
}
}

impl<R> Mp4Reader<R> {
pub fn metadata(&self) -> impl Metadata<'_> {
self.moov.udta.as_ref().and_then(|udta| {
udta.meta.as_ref().and_then(|meta| match meta {
Expand All @@ -282,3 +278,84 @@ impl<R> Mp4Reader<R> {
})
}
}

#[derive(Debug)]
pub struct Mp4Reader<R> {
reader: R,
pub header: Mp4Header,
}

impl<R: Read + Seek> Mp4Reader<R> {
pub fn from_reader(reader: R, header: Mp4Header) -> Self {
Self { reader, header }
}

pub fn read_header(mut reader: R, size: u64) -> Result<Self> {
Ok(Mp4Reader {
header: Mp4Header::read(&mut reader, size)?,
reader,
})
}

pub fn read_fragment_header<FR: Read + Seek>(
&self,
mut reader: FR,
size: u64,
) -> Result<Mp4Reader<FR>> {
Ok(Mp4Reader {
header: self.header.read_fragment(&mut reader, size)?,
reader,
})
}

pub fn size(&self) -> u64 {
self.header.size()
}

pub fn major_brand(&self) -> &FourCC {
self.header.major_brand()
}

pub fn minor_version(&self) -> u32 {
self.header.minor_version()
}

pub fn compatible_brands(&self) -> &[FourCC] {
self.header.compatible_brands()
}

pub fn duration(&self) -> Duration {
self.header.duration()
}

pub fn timescale(&self) -> u32 {
self.header.timescale()
}

pub fn is_fragmented(&self) -> bool {
self.header.is_fragmented()
}

pub fn tracks(&self) -> &HashMap<u32, Mp4Track> {
self.header.tracks()
}

pub fn sample_count(&self, track_id: u32) -> Result<u32> {
self.header.sample_count(track_id)
}

pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result<Option<Mp4Sample>> {
self.header
.read_sample(&mut self.reader, track_id, sample_id)
}

pub fn sample_offset(&mut self, track_id: u32, sample_id: u32) -> Result<u64> {
self.header.sample_offset(track_id, sample_id)
}
}

impl<R> Mp4Reader<R> {
pub fn metadata(&self) -> impl Metadata<'_> {
self.header.metadata()
}
}
37 changes: 25 additions & 12 deletions src/track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,16 +501,14 @@ impl Mp4Track {

fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
if !self.trafs.is_empty() {
let mut base_start_time = 0;
let mut default_sample_duration = self.default_sample_duration;
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
let traf = &self.trafs[traf_idx];
if let Some(tfdt) = &traf.tfdt {
base_start_time = tfdt.base_media_decode_time;
}
if let Some(duration) = traf.tfhd.default_sample_duration {
default_sample_duration = duration;
}
let base_start_time = traf
.tfdt
.as_ref()
.map(|tfdt| tfdt.base_media_decode_time)
.unwrap_or(0);

if let Some(trun) = &traf.trun {
if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 {
let mut start_offset = 0u64;
Expand All @@ -523,9 +521,21 @@ impl Mp4Track {
return Ok((base_start_time + start_offset, duration));
}
}

let default_sample_duration = traf
.tfhd
.default_sample_duration
.unwrap_or(self.default_sample_duration);

let start_offset = sample_idx as u64 * default_sample_duration as u64;

Ok((base_start_time + start_offset, default_sample_duration))
} else {
Ok((
((sample_id - 1) * self.default_sample_duration) as u64,
self.default_sample_duration,
))
}
let start_offset = ((sample_id - 1) * default_sample_duration) as u64;
Ok((base_start_time + start_offset, default_sample_duration))
} else {
let stts = &self.trak.mdia.minf.stbl.stts;

Expand Down Expand Up @@ -579,8 +589,11 @@ impl Mp4Track {

fn is_sync_sample(&self, sample_id: u32) -> bool {
if !self.trafs.is_empty() {
let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
return sample_id == 1 || sample_id % sample_sizes_count == 0;
if let Some((_, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
return sample_idx == 0;
}

return sample_id == 1;
}

if let Some(ref stss) = self.trak.mdia.minf.stbl.stss {
Expand Down