Skip to content

Commit

Permalink
Support multiple trun boxes in a single traf box
Browse files Browse the repository at this point in the history
MPEG-4 part 12 section 8.8.8 states that a `traf` box may contain
multiple `trun` boxes. This patch allows parsing such files correctly.
Previously, the library would only parse the last `trun` box for each
`traf` box, which caused the tracks to be unplayable.
  • Loading branch information
jerzywilczek committed Feb 21, 2024
1 parent 35560e9 commit 83f0110
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 38 deletions.
2 changes: 1 addition & 1 deletion examples/mp4dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
for traf in moof.trafs.iter() {
boxes.push(build_box(traf));
boxes.push(build_box(&traf.tfhd));
if let Some(ref trun) = &traf.trun {
for trun in &traf.truns {
boxes.push(build_box(trun));
}
}
Expand Down
1 change: 1 addition & 0 deletions src/mp4box/avc1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
}
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
println!("{name:?}");
if s > size {
return Err(Error::InvalidData(
"avc1 box contains a box with a larger size than it",
Expand Down
13 changes: 7 additions & 6 deletions src/mp4box/traf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::mp4box::{tfdt::TfdtBox, tfhd::TfhdBox, trun::TrunBox};
pub struct TrafBox {
pub tfhd: TfhdBox,
pub tfdt: Option<TfdtBox>,
pub trun: Option<TrunBox>,
pub truns: Vec<TrunBox>,
}

impl TrafBox {
Expand All @@ -22,9 +22,10 @@ impl TrafBox {
if let Some(ref tfdt) = self.tfdt {
size += tfdt.box_size();
}
if let Some(ref trun) = self.trun {
for trun in &self.truns {
size += trun.box_size();
}

size
}
}
Expand Down Expand Up @@ -54,7 +55,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {

let mut tfhd = None;
let mut tfdt = None;
let mut trun = None;
let mut truns = Vec::new();

let mut current = reader.stream_position()?;
let end = start + size;
Expand All @@ -76,7 +77,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
tfdt = Some(TfdtBox::read_box(reader, s)?);
}
BoxType::TrunBox => {
trun = Some(TrunBox::read_box(reader, s)?);
truns.push(TrunBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
Expand All @@ -96,7 +97,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
Ok(TrafBox {
tfhd: tfhd.unwrap(),
tfdt,
trun,
truns,
})
}
}
Expand All @@ -110,7 +111,7 @@ impl<W: Write> WriteBox<&mut W> for TrafBox {
if let Some(ref tfdt) = self.tfdt {
tfdt.write_box(writer)?;
}
if let Some(ref trun) = self.trun {
for trun in &self.truns {
trun.write_box(writer)?;
}

Expand Down
61 changes: 30 additions & 31 deletions src/track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ impl Mp4Track {
if !self.trafs.is_empty() {
let mut sample_count = 0u32;
for traf in self.trafs.iter() {
if let Some(ref trun) = traf.trun {
for trun in &traf.truns {
sample_count = sample_count
.checked_add(trun.sample_count)
.expect("attempt to sum trun sample_count with overflow");
Expand Down Expand Up @@ -369,14 +369,14 @@ impl Mp4Track {
}

/// return `(traf_idx, sample_idx_in_trun)`
fn find_traf_idx_and_sample_idx(&self, sample_id: u32) -> Option<(usize, usize)> {
fn find_traf_trun_and_sample_idx(&self, sample_id: u32) -> Option<(usize, usize, usize)> {
let global_idx = sample_id - 1;
let mut offset = 0;
for traf_idx in 0..self.trafs.len() {
if let Some(trun) = &self.trafs[traf_idx].trun {
for (trun_idx, trun) in self.trafs[traf_idx].truns.iter().enumerate() {
let sample_count = trun.sample_count;
if sample_count > (global_idx - offset) {
return Some((traf_idx, (global_idx - offset) as _));
return Some((traf_idx, trun_idx, (global_idx - offset) as _));
}
offset = offset
.checked_add(sample_count)
Expand All @@ -388,11 +388,10 @@ impl Mp4Track {

fn sample_size(&self, sample_id: u32) -> Result<u32> {
if !self.trafs.is_empty() {
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
if let Some(size) = self.trafs[traf_idx]
.trun
.as_ref()
.unwrap()
if let Some((traf_idx, trun_idx, sample_idx)) =
self.find_traf_trun_and_sample_idx(sample_id)
{
if let Some(size) = self.trafs[traf_idx].truns[trun_idx]
.sample_sizes
.get(sample_idx)
{
Expand Down Expand Up @@ -439,17 +438,15 @@ impl Mp4Track {

pub fn sample_offset(&self, sample_id: u32) -> Result<u64> {
if !self.trafs.is_empty() {
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
if let Some((traf_idx, trun_idx, sample_idx)) =
self.find_traf_trun_and_sample_idx(sample_id)
{
let mut sample_offset = self.trafs[traf_idx]
.tfhd
.base_data_offset
.unwrap_or(self.moof_offsets[traf_idx]);

if let Some(data_offset) = self.trafs[traf_idx]
.trun
.as_ref()
.and_then(|trun| trun.data_offset)
{
if let Some(data_offset) = self.trafs[traf_idx].truns[trun_idx].data_offset {
sample_offset = sample_offset.checked_add_signed(data_offset as i64).ok_or(
Error::InvalidData("attempt to calculate trun sample offset with overflow"),
)?;
Expand Down Expand Up @@ -503,25 +500,26 @@ impl Mp4Track {
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) {
if let Some((traf_idx, trun_idx, sample_idx)) =
self.find_traf_trun_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;
}
if let Some(trun) = &traf.trun {
if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 {
let mut start_offset = 0u64;
for duration in &trun.sample_durations[..sample_idx] {
start_offset = start_offset.checked_add(*duration as u64).ok_or(
Error::InvalidData("attempt to sum sample durations with overflow"),
)?;
}
let duration = trun.sample_durations[sample_idx];
return Ok((base_start_time + start_offset, duration));
let trun = &traf.truns[trun_idx];
if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 {
let mut start_offset = 0u64;
for duration in &trun.sample_durations[..sample_idx] {
start_offset = start_offset.checked_add(*duration as u64).ok_or(
Error::InvalidData("attempt to sum sample durations with overflow"),
)?;
}
let duration = trun.sample_durations[sample_idx];
return Ok((base_start_time + start_offset, duration));
}
}
let start_offset = ((sample_id - 1) * default_sample_duration) as u64;
Expand Down Expand Up @@ -559,11 +557,12 @@ impl Mp4Track {

fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
if !self.trafs.is_empty() {
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
if let Some(cts) = self.trafs[traf_idx]
.trun
.as_ref()
.and_then(|trun| trun.sample_cts.get(sample_idx))
if let Some((traf_idx, trun_idx, sample_idx)) =
self.find_traf_trun_and_sample_idx(sample_id)
{
if let Some(cts) = self.trafs[traf_idx].truns[trun_idx]
.sample_cts
.get(sample_idx)
{
return *cts as i32;
}
Expand Down

0 comments on commit 83f0110

Please sign in to comment.