fmp4mux: Ensure that DTS (or PTS for intra-only streams) are monotonically increasing

This commit is contained in:
Sebastian Dröge 2022-06-01 20:04:58 +03:00
parent 402500f79c
commit e52d1fa317

View file

@ -101,6 +101,10 @@ struct Stream {
// Difference between the first DTS and 0 in case of negative DTS // Difference between the first DTS and 0 in case of negative DTS
dts_offset: Option<gst::ClockTime>, dts_offset: Option<gst::ClockTime>,
// Current position (DTS, or PTS for intra-only) to prevent
// timestamps from going backwards when queueing new buffers
current_position: gst::ClockTime,
last_force_keyunit_time: Option<gst::ClockTime>, last_force_keyunit_time: Option<gst::ClockTime>,
} }
@ -167,7 +171,7 @@ impl FMP4Mux {
let duration = buffer.duration(); let duration = buffer.duration();
let end_pts_position = duration.map_or(pts_position, |duration| pts_position + duration); let end_pts_position = duration.map_or(pts_position, |duration| pts_position + duration);
let pts = match segment.to_running_time_full(pts_position) { let mut pts = match segment.to_running_time_full(pts_position) {
(_, None) => { (_, None) => {
gst::error!(CAT, obj: &stream.sinkpad, "Couldn't convert PTS to running time"); gst::error!(CAT, obj: &stream.sinkpad, "Couldn't convert PTS to running time");
return Err(gst::FlowError::Error); return Err(gst::FlowError::Error);
@ -179,7 +183,7 @@ impl FMP4Mux {
(_, Some(pts)) => pts, (_, Some(pts)) => pts,
}; };
let end_pts = match segment.to_running_time_full(end_pts_position) { let mut end_pts = match segment.to_running_time_full(end_pts_position) {
(_, None) => { (_, None) => {
gst::error!( gst::error!(
CAT, CAT,
@ -195,6 +199,23 @@ impl FMP4Mux {
(_, Some(pts)) => pts, (_, Some(pts)) => pts,
}; };
// Enforce monotonically increasing PTS for intra-only streams
if intra_only {
if pts < stream.current_position {
gst::warning!(
CAT,
obj: &stream.sinkpad,
"Decreasing PTS {} < {} for intra-only stream",
pts,
stream.current_position,
);
pts = stream.current_position;
} else {
stream.current_position = pts;
}
end_pts = std::cmp::max(end_pts, pts);
}
let (dts_position, dts, end_dts) = if intra_only { let (dts_position, dts, end_dts) = if intra_only {
(None, None, None) (None, None, None)
} else { } else {
@ -204,7 +225,7 @@ impl FMP4Mux {
let end_dts_position = let end_dts_position =
duration.map_or(dts_position, |duration| dts_position + duration); duration.map_or(dts_position, |duration| dts_position + duration);
let dts = match segment.to_running_time_full(dts_position) { let mut dts = match segment.to_running_time_full(dts_position) {
(_, None) => { (_, None) => {
gst::error!(CAT, obj: &stream.sinkpad, "Couldn't convert DTS to running time"); gst::error!(CAT, obj: &stream.sinkpad, "Couldn't convert DTS to running time");
return Err(gst::FlowError::Error); return Err(gst::FlowError::Error);
@ -231,7 +252,7 @@ impl FMP4Mux {
} }
}; };
let end_dts = match segment.to_running_time_full(end_dts_position) { let mut end_dts = match segment.to_running_time_full(end_dts_position) {
(_, None) => { (_, None) => {
gst::error!( gst::error!(
CAT, CAT,
@ -261,6 +282,24 @@ impl FMP4Mux {
} }
} }
}; };
// Enforce monotonically increasing DTS for intra-only streams
// NOTE: PTS stays the same so this will cause a bigger PTS/DTS difference
// FIXME: Is this correct?
if dts < stream.current_position {
gst::warning!(
CAT,
obj: &stream.sinkpad,
"Decreasing DTS {} < {}",
dts,
stream.current_position,
);
dts = stream.current_position;
} else {
stream.current_position = dts;
}
end_dts = std::cmp::max(end_dts, dts);
(Some(dts_position), Some(dts), Some(end_dts)) (Some(dts_position), Some(dts), Some(end_dts))
}; };
@ -316,8 +355,9 @@ impl FMP4Mux {
pts, pts,
dts.display(), dts.display(),
); );
prev_gop.end_pts = pts;
prev_gop.end_dts = dts; prev_gop.end_pts = std::cmp::max(prev_gop.end_pts, pts);
prev_gop.end_dts = std::cmp::max(prev_gop.end_dts, dts);
if intra_only { if intra_only {
prev_gop.final_end_pts = true; prev_gop.final_end_pts = true;
@ -368,15 +408,17 @@ impl FMP4Mux {
gop.earliest_pts_position = pts_position; gop.earliest_pts_position = pts_position;
if let Some(prev_gop) = stream.queued_gops.get_mut(1) { if let Some(prev_gop) = stream.queued_gops.get_mut(1) {
gst::debug!( if prev_gop.end_pts < pts {
CAT, gst::debug!(
obj: &stream.sinkpad, CAT,
"Updating previous GOP starting PTS {} end time from {} to {}", obj: &stream.sinkpad,
pts, "Updating previous GOP starting PTS {} end time from {} to {}",
prev_gop.end_pts, pts,
pts prev_gop.end_pts,
); pts
prev_gop.end_pts = pts; );
prev_gop.end_pts = pts;
}
} }
} }
@ -666,7 +708,10 @@ impl FMP4Mux {
} }
}; };
let duration = end_timestamp.saturating_sub(timestamp); // Timestamps are enforced to monotonically increase when queueing buffers
let duration = end_timestamp
.checked_sub(timestamp)
.expect("Timestamps going backwards");
let composition_time_offset = if stream.intra_only { let composition_time_offset = if stream.intra_only {
None None
@ -982,6 +1027,7 @@ impl FMP4Mux {
queued_gops: VecDeque::new(), queued_gops: VecDeque::new(),
fragment_filled: false, fragment_filled: false,
dts_offset: None, dts_offset: None,
current_position: gst::ClockTime::ZERO,
last_force_keyunit_time: None, last_force_keyunit_time: None,
}); });
} }
@ -1432,6 +1478,7 @@ impl AggregatorImpl for FMP4Mux {
for stream in &mut state.streams { for stream in &mut state.streams {
stream.queued_gops.clear(); stream.queued_gops.clear();
stream.dts_offset = None; stream.dts_offset = None;
stream.current_position = gst::ClockTime::ZERO;
stream.last_force_keyunit_time = None; stream.last_force_keyunit_time = None;
stream.fragment_filled = false; stream.fragment_filled = false;
} }