1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-06-02 13:39:54 +00:00

Support multiple trun boxes in a single traf box

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.
This commit is contained in:
Jerzy Wilczek 2024-02-21 11:54:29 +01:00
parent 35560e94f5
commit ca400ee05c
No known key found for this signature in database
3 changed files with 38 additions and 38 deletions

View file

@ -123,7 +123,7 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
for traf in moof.trafs.iter() { for traf in moof.trafs.iter() {
boxes.push(build_box(traf)); boxes.push(build_box(traf));
boxes.push(build_box(&traf.tfhd)); boxes.push(build_box(&traf.tfhd));
if let Some(ref trun) = &traf.trun { for trun in &traf.truns {
boxes.push(build_box(trun)); boxes.push(build_box(trun));
} }
} }

View file

@ -8,7 +8,7 @@ use crate::mp4box::{tfdt::TfdtBox, tfhd::TfhdBox, trun::TrunBox};
pub struct TrafBox { pub struct TrafBox {
pub tfhd: TfhdBox, pub tfhd: TfhdBox,
pub tfdt: Option<TfdtBox>, pub tfdt: Option<TfdtBox>,
pub trun: Option<TrunBox>, pub truns: Vec<TrunBox>,
} }
impl TrafBox { impl TrafBox {
@ -22,9 +22,10 @@ impl TrafBox {
if let Some(ref tfdt) = self.tfdt { if let Some(ref tfdt) = self.tfdt {
size += tfdt.box_size(); size += tfdt.box_size();
} }
if let Some(ref trun) = self.trun { for trun in &self.truns {
size += trun.box_size(); size += trun.box_size();
} }
size size
} }
} }
@ -54,7 +55,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
let mut tfhd = None; let mut tfhd = None;
let mut tfdt = None; let mut tfdt = None;
let mut trun = None; let mut truns = Vec::new();
let mut current = reader.stream_position()?; let mut current = reader.stream_position()?;
let end = start + size; let end = start + size;
@ -76,7 +77,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
tfdt = Some(TfdtBox::read_box(reader, s)?); tfdt = Some(TfdtBox::read_box(reader, s)?);
} }
BoxType::TrunBox => { BoxType::TrunBox => {
trun = Some(TrunBox::read_box(reader, s)?); truns.push(TrunBox::read_box(reader, s)?);
} }
_ => { _ => {
// XXX warn!() // XXX warn!()
@ -96,7 +97,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
Ok(TrafBox { Ok(TrafBox {
tfhd: tfhd.unwrap(), tfhd: tfhd.unwrap(),
tfdt, tfdt,
trun, truns,
}) })
} }
} }
@ -110,7 +111,7 @@ impl<W: Write> WriteBox<&mut W> for TrafBox {
if let Some(ref tfdt) = self.tfdt { if let Some(ref tfdt) = self.tfdt {
tfdt.write_box(writer)?; tfdt.write_box(writer)?;
} }
if let Some(ref trun) = self.trun { for trun in &self.truns {
trun.write_box(writer)?; trun.write_box(writer)?;
} }

View file

@ -236,7 +236,7 @@ impl Mp4Track {
if !self.trafs.is_empty() { if !self.trafs.is_empty() {
let mut sample_count = 0u32; let mut sample_count = 0u32;
for traf in self.trafs.iter() { for traf in self.trafs.iter() {
if let Some(ref trun) = traf.trun { for trun in &traf.truns {
sample_count = sample_count sample_count = sample_count
.checked_add(trun.sample_count) .checked_add(trun.sample_count)
.expect("attempt to sum trun sample_count with overflow"); .expect("attempt to sum trun sample_count with overflow");
@ -369,14 +369,14 @@ impl Mp4Track {
} }
/// return `(traf_idx, sample_idx_in_trun)` /// 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 global_idx = sample_id - 1;
let mut offset = 0; let mut offset = 0;
for traf_idx in 0..self.trafs.len() { 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; let sample_count = trun.sample_count;
if sample_count > (global_idx - offset) { 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 offset = offset
.checked_add(sample_count) .checked_add(sample_count)
@ -388,11 +388,10 @@ impl Mp4Track {
fn sample_size(&self, sample_id: u32) -> Result<u32> { fn sample_size(&self, sample_id: u32) -> Result<u32> {
if !self.trafs.is_empty() { 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)) =
if let Some(size) = self.trafs[traf_idx] self.find_traf_trun_and_sample_idx(sample_id)
.trun {
.as_ref() if let Some(size) = self.trafs[traf_idx].truns[trun_idx]
.unwrap()
.sample_sizes .sample_sizes
.get(sample_idx) .get(sample_idx)
{ {
@ -439,17 +438,15 @@ impl Mp4Track {
pub fn sample_offset(&self, sample_id: u32) -> Result<u64> { pub fn sample_offset(&self, sample_id: u32) -> Result<u64> {
if !self.trafs.is_empty() { 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] let mut sample_offset = self.trafs[traf_idx]
.tfhd .tfhd
.base_data_offset .base_data_offset
.unwrap_or(self.moof_offsets[traf_idx]); .unwrap_or(self.moof_offsets[traf_idx]);
if let Some(data_offset) = self.trafs[traf_idx] if let Some(data_offset) = self.trafs[traf_idx].truns[trun_idx].data_offset {
.trun
.as_ref()
.and_then(|trun| trun.data_offset)
{
sample_offset = sample_offset.checked_add_signed(data_offset as i64).ok_or( sample_offset = sample_offset.checked_add_signed(data_offset as i64).ok_or(
Error::InvalidData("attempt to calculate trun sample offset with overflow"), Error::InvalidData("attempt to calculate trun sample offset with overflow"),
)?; )?;
@ -503,7 +500,9 @@ impl Mp4Track {
if !self.trafs.is_empty() { if !self.trafs.is_empty() {
let mut base_start_time = 0; let mut base_start_time = 0;
let mut default_sample_duration = self.default_sample_duration; 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]; let traf = &self.trafs[traf_idx];
if let Some(tfdt) = &traf.tfdt { if let Some(tfdt) = &traf.tfdt {
base_start_time = tfdt.base_media_decode_time; base_start_time = tfdt.base_media_decode_time;
@ -511,17 +510,16 @@ impl Mp4Track {
if let Some(duration) = traf.tfhd.default_sample_duration { if let Some(duration) = traf.tfhd.default_sample_duration {
default_sample_duration = duration; default_sample_duration = duration;
} }
if let Some(trun) = &traf.trun { let trun = &traf.truns[trun_idx];
if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 { if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 {
let mut start_offset = 0u64; let mut start_offset = 0u64;
for duration in &trun.sample_durations[..sample_idx] { for duration in &trun.sample_durations[..sample_idx] {
start_offset = start_offset.checked_add(*duration as u64).ok_or( start_offset = start_offset.checked_add(*duration as u64).ok_or(
Error::InvalidData("attempt to sum sample durations with overflow"), 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 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; let start_offset = ((sample_id - 1) * default_sample_duration) as u64;
@ -559,11 +557,12 @@ impl Mp4Track {
fn sample_rendering_offset(&self, sample_id: u32) -> i32 { fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
if !self.trafs.is_empty() { 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)) =
if let Some(cts) = self.trafs[traf_idx] self.find_traf_trun_and_sample_idx(sample_id)
.trun {
.as_ref() if let Some(cts) = self.trafs[traf_idx].truns[trun_idx]
.and_then(|trun| trun.sample_cts.get(sample_idx)) .sample_cts
.get(sample_idx)
{ {
return *cts as i32; return *cts as i32;
} }