mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-02 17:53:48 +00:00
mp4mux: Use correct timescales for edit lists
The duration is using the movie timescale while the media time is using the media / track timescale. Previously both were using the track timescale. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2167>
This commit is contained in:
parent
2e2bbda7ba
commit
8a70f1617a
3 changed files with 101 additions and 77 deletions
|
@ -309,7 +309,7 @@ fn write_trak(
|
|||
if !references.is_empty() {
|
||||
write_box(v, b"tref", |v| write_tref(v, header, references))?;
|
||||
}
|
||||
write_box(v, b"edts", |v| write_edts(v, stream))?;
|
||||
write_box(v, b"edts", |v| write_edts(v, header, stream))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2288,32 +2288,69 @@ fn write_tref(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_edts(v: &mut Vec<u8>, stream: &super::Stream) -> Result<(), Error> {
|
||||
write_full_box(v, b"elst", FULL_BOX_VERSION_1, 0, |v| write_elst(v, stream))?;
|
||||
fn write_edts(
|
||||
v: &mut Vec<u8>,
|
||||
header: &super::Header,
|
||||
stream: &super::Stream,
|
||||
) -> Result<(), Error> {
|
||||
write_full_box(v, b"elst", FULL_BOX_VERSION_1, 0, |v| {
|
||||
write_elst(v, header, stream)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_elst(v: &mut Vec<u8>, stream: &super::Stream) -> Result<(), Error> {
|
||||
fn write_elst(
|
||||
v: &mut Vec<u8>,
|
||||
header: &super::Header,
|
||||
stream: &super::Stream,
|
||||
) -> Result<(), Error> {
|
||||
let movie_timescale = header_to_timescale(header);
|
||||
|
||||
// Entry count
|
||||
v.extend((stream.elst_infos.len() as u32).to_be_bytes());
|
||||
let mut num_entries = 0u32;
|
||||
let entry_count_position = v.len();
|
||||
// Entry count, rewritten in the end
|
||||
v.extend(0u32.to_be_bytes());
|
||||
|
||||
for elst_info in &stream.elst_infos {
|
||||
v.extend(
|
||||
elst_info
|
||||
.duration
|
||||
.expect("Should have been set by `get_elst_infos`")
|
||||
.to_be_bytes(),
|
||||
);
|
||||
// Edit duration (in movie timescale)
|
||||
let edit_duration = elst_info
|
||||
.duration
|
||||
.expect("Should have been set by `get_elst_infos`")
|
||||
.nseconds()
|
||||
.mul_div_round(movie_timescale as u64, gst::ClockTime::SECOND.nseconds())
|
||||
.unwrap();
|
||||
|
||||
// Media time
|
||||
v.extend(elst_info.start.to_be_bytes());
|
||||
if edit_duration == 0 {
|
||||
continue;
|
||||
}
|
||||
v.extend(edit_duration.to_be_bytes());
|
||||
|
||||
// Media time (in media timescale)
|
||||
let media_time = elst_info
|
||||
.start
|
||||
.map(|start| {
|
||||
i64::try_from(start)
|
||||
.unwrap()
|
||||
.mul_div_round(
|
||||
stream.timescale as i64,
|
||||
gst::ClockTime::SECOND.nseconds() as i64,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.unwrap_or(-1i64);
|
||||
v.extend(media_time.to_be_bytes());
|
||||
|
||||
// Media rate
|
||||
v.extend(1u16.to_be_bytes());
|
||||
v.extend(0u16.to_be_bytes());
|
||||
num_entries += 1;
|
||||
}
|
||||
|
||||
// Rewrite entry count
|
||||
v[entry_count_position..][..4].copy_from_slice(&num_entries.to_be_bytes());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use anyhow::{bail, Context};
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
|
@ -158,7 +158,6 @@ impl Stream {
|
|||
min_earliest_pts: gst::ClockTime,
|
||||
) -> Result<Vec<super::ElstInfo>, anyhow::Error> {
|
||||
let mut elst_infos = self.elst_infos.clone();
|
||||
let timescale = self.timescale();
|
||||
let earliest_pts = self
|
||||
.earliest_pts
|
||||
.expect("Streams without earliest_pts should have been skipped");
|
||||
|
@ -169,57 +168,47 @@ impl Stream {
|
|||
// If no elst info were set, use the whole track
|
||||
if self.elst_infos.is_empty() {
|
||||
let start = if let Some(start_dts) = self.start_dts {
|
||||
((gst::Signed::Positive(earliest_pts) - start_dts)
|
||||
.nseconds()
|
||||
.positive()
|
||||
.unwrap_or(0)
|
||||
.mul_div_round(timescale as u64, gst::ClockTime::SECOND.nseconds())
|
||||
.context("too big track duration")?) as i64
|
||||
gst::Signed::Positive(earliest_pts) - start_dts
|
||||
} else {
|
||||
0i64
|
||||
gst::Signed::Positive(gst::ClockTime::ZERO)
|
||||
};
|
||||
|
||||
elst_infos.push(super::ElstInfo {
|
||||
start,
|
||||
duration: Some(
|
||||
(end_pts - earliest_pts)
|
||||
.nseconds()
|
||||
.mul_div_round(timescale as u64, gst::ClockTime::SECOND.nseconds())
|
||||
.context("too big track duration")?,
|
||||
),
|
||||
start: Some(start),
|
||||
duration: Some(end_pts - earliest_pts),
|
||||
});
|
||||
}
|
||||
|
||||
// Add a gap at the beginning if needed
|
||||
if min_earliest_pts != earliest_pts {
|
||||
let gap_duration = (earliest_pts - min_earliest_pts)
|
||||
.nseconds()
|
||||
.mul_div_round(timescale as u64, gst::ClockTime::SECOND.nseconds())
|
||||
.context("too big gap")?;
|
||||
if earliest_pts > min_earliest_pts {
|
||||
let gap_duration = earliest_pts - min_earliest_pts;
|
||||
|
||||
if gap_duration > 0 {
|
||||
elst_infos.insert(
|
||||
0,
|
||||
super::ElstInfo {
|
||||
start: -1,
|
||||
duration: Some(gap_duration),
|
||||
},
|
||||
);
|
||||
}
|
||||
elst_infos.insert(
|
||||
0,
|
||||
super::ElstInfo {
|
||||
start: None,
|
||||
duration: Some(gap_duration),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let mut iter = elst_infos.iter_mut().peekable();
|
||||
let mut iter = elst_infos
|
||||
.iter_mut()
|
||||
.filter(|e| e.start.is_some())
|
||||
.peekable();
|
||||
while let Some(&mut ref mut elst_info) = iter.next() {
|
||||
if elst_info.duration.unwrap_or(0u64) == 0u64 {
|
||||
if elst_info
|
||||
.duration
|
||||
.map_or(true, |duration| duration.is_zero())
|
||||
{
|
||||
elst_info.duration = if let Some(next) = iter.peek_mut() {
|
||||
Some((next.start - elst_info.start) as u64)
|
||||
} else {
|
||||
Some(
|
||||
(end_pts - earliest_pts)
|
||||
.nseconds()
|
||||
.mul_div_round(timescale as u64, gst::ClockTime::SECOND.nseconds())
|
||||
.context("too big track duration")?,
|
||||
(next.start.unwrap() - elst_info.start.unwrap())
|
||||
.positive()
|
||||
.unwrap_or(gst::ClockTime::ZERO),
|
||||
)
|
||||
} else {
|
||||
Some(end_pts - earliest_pts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -355,53 +344,51 @@ impl MP4Mux {
|
|||
.get::<i32>("rate")
|
||||
.unwrap_or_else(|_| stream.timescale() as i32);
|
||||
|
||||
let gstclocktime_to_samples = move |v: gst::ClockTime| {
|
||||
v.nseconds()
|
||||
.mul_div_round(timescale as u64, gst::ClockTime::SECOND.nseconds())
|
||||
.context("Invalid start in the AudioClipMeta")
|
||||
let samples_to_gstclocktime = move |v: u64| {
|
||||
let nseconds = v
|
||||
.mul_div_round(gst::ClockTime::SECOND.nseconds(), timescale as u64)
|
||||
.context("Invalid start in the AudioClipMeta")?;
|
||||
Ok::<_, anyhow::Error>(gst::ClockTime::from_nseconds(nseconds))
|
||||
};
|
||||
|
||||
let generic_to_samples = move |t| -> Result<Option<u64>, anyhow::Error> {
|
||||
let generic_to_gstclocktime = move |t| -> Result<Option<gst::ClockTime>, anyhow::Error> {
|
||||
if let gst::GenericFormattedValue::Default(Some(v)) = t {
|
||||
let v = u64::from(v);
|
||||
Ok(Some(v).filter(|x| x != &0u64))
|
||||
let v = samples_to_gstclocktime(v)?;
|
||||
Ok(Some(v).filter(|x| !x.is_zero()))
|
||||
} else if let gst::GenericFormattedValue::Time(Some(v)) = t {
|
||||
Ok(Some(gstclocktime_to_samples(v)?))
|
||||
Ok(Some(v).filter(|x| !x.is_zero()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
};
|
||||
|
||||
let start = generic_to_samples(cmeta.start())?;
|
||||
let end = generic_to_samples(cmeta.end())?;
|
||||
let start = generic_to_gstclocktime(cmeta.start())?;
|
||||
let end = generic_to_gstclocktime(cmeta.end())?;
|
||||
|
||||
if end.is_none() && start.is_none() {
|
||||
return Err(anyhow!(
|
||||
"No start or end time in `default` format in the AudioClipingMeta"
|
||||
));
|
||||
bail!("No start or end time in `default` format in the AudioClipingMeta");
|
||||
}
|
||||
|
||||
let start = if let Some(start) = generic_to_samples(cmeta.start())? {
|
||||
start + gstclocktime_to_samples(buffer.pts)?
|
||||
let start = if let Some(start) = generic_to_gstclocktime(cmeta.start())? {
|
||||
start + buffer.pts
|
||||
} else {
|
||||
0
|
||||
gst::ClockTime::ZERO
|
||||
};
|
||||
let duration = if let Some(e) = end {
|
||||
let duration = if let Some(end) = end {
|
||||
Some(
|
||||
gstclocktime_to_samples(buffer.pts)?
|
||||
+ gstclocktime_to_samples(
|
||||
buffer
|
||||
.duration
|
||||
.context("No duration on buffer, we can't add edit list")?,
|
||||
)?
|
||||
- e,
|
||||
buffer.pts
|
||||
+ buffer
|
||||
.duration
|
||||
.context("No duration on buffer, we can't add edit list")?
|
||||
- end,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
stream.elst_infos.push(super::ElstInfo {
|
||||
start: start as i64,
|
||||
start: Some(start.into()),
|
||||
duration,
|
||||
});
|
||||
|
||||
|
|
|
@ -174,8 +174,8 @@ pub(crate) struct Chunk {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ElstInfo {
|
||||
start: i64,
|
||||
duration: Option<u64>,
|
||||
start: Option<gst::Signed<gst::ClockTime>>,
|
||||
duration: Option<gst::ClockTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
Loading…
Reference in a new issue