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:
parent
35560e94f5
commit
ca400ee05c
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
61
src/track.rs
61
src/track.rs
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue