From 72506b94e341e3c8e8b65d6b9baf1a5468e025b5 Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Wed, 25 Oct 2023 18:26:58 +0200 Subject: [PATCH] livesync: Split fallback_duration into in_ and out_duration Make it independent of the `latency`; this was inconsistent anyway, where the default latency of zero got you a fallback duration of 100 ms and something else got you half the latency. Maintain a separate duration for the `in` and the `out` side so we change the duration of repeat buffers after a caps change, not just before. Part-of: --- utils/livesync/src/livesync/imp.rs | 81 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/utils/livesync/src/livesync/imp.rs b/utils/livesync/src/livesync/imp.rs index b585eed2..236e75fb 100644 --- a/utils/livesync/src/livesync/imp.rs +++ b/utils/livesync/src/livesync/imp.rs @@ -36,6 +36,16 @@ fn audio_info_from_caps( .transpose() } +fn duration_from_caps(caps: &gst::CapsRef) -> Option { + caps.structure(0) + .filter(|s| s.name().starts_with("video/")) + .and_then(|s| s.get::("framerate").ok()) + .filter(|framerate| framerate.denom() > 0 && framerate.numer() > 0) + .and_then(|framerate| { + gst::ClockTime::SECOND.mul_div_round(framerate.denom() as u64, framerate.numer() as u64) + }) +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum BufferLateness { OnTime, @@ -82,9 +92,6 @@ struct State { /// Latency reported by upstream upstream_latency: Option, - /// Duration we assume for buffers without one - fallback_duration: gst::ClockTime, - /// Whether we're in PLAYING state playing: bool, @@ -118,6 +125,12 @@ struct State { /// Audio format of our srcpad out_audio_info: Option, + /// Duration from caps on our sinkpad + in_duration: Option, + + /// Duration from caps on our srcpad + out_duration: Option, + /// Queue between sinkpad and srcpad queue: VecDeque, @@ -159,7 +172,9 @@ const PROP_OUT: &str = "out"; const PROP_DUPLICATE: &str = "duplicate"; const DEFAULT_LATENCY: gst::ClockTime = gst::ClockTime::ZERO; +const MINIMUM_DURATION: gst::ClockTime = gst::ClockTime::from_mseconds(8); const DEFAULT_DURATION: gst::ClockTime = gst::ClockTime::from_mseconds(100); +const MAXIMUM_DURATION: gst::ClockTime = gst::ClockTime::from_seconds(10); const MINIMUM_LATE_THRESHOLD: gst::ClockTime = gst::ClockTime::ZERO; const DEFAULT_LATE_THRESHOLD: Option = Some(gst::ClockTime::from_seconds(2)); @@ -170,7 +185,6 @@ impl Default for State { late_threshold: DEFAULT_LATE_THRESHOLD, single_segment: false, upstream_latency: None, - fallback_duration: DEFAULT_DURATION, playing: false, eos: false, srcresult: Err(gst::FlowError::Flushing), @@ -180,6 +194,8 @@ impl Default for State { out_segment: None, in_caps: None, pending_caps: None, + in_duration: None, + out_duration: None, in_audio_info: None, out_audio_info: None, queue: VecDeque::with_capacity(32), @@ -348,7 +364,6 @@ impl ObjectImpl for LiveSync { match pspec.name() { PROP_LATENCY => { state.latency = value.get().unwrap(); - state.update_fallback_duration(); let _ = self.obj().post_message(gst::message::Latency::new()); } @@ -490,31 +505,6 @@ impl State { }) } - fn update_fallback_duration(&mut self) { - self.fallback_duration = self - // First, try 1/framerate from the caps - .in_caps - .as_ref() - .and_then(|c| c.structure(0)) - .filter(|s| s.name().starts_with("video/")) - .and_then(|s| s.get::("framerate").ok()) - .filter(|framerate| framerate.denom() > 0 && framerate.numer() > 0) - .and_then(|framerate| { - gst::ClockTime::SECOND - .mul_div_round(framerate.denom() as u64, framerate.numer() as u64) - }) - .filter(|&dur| dur > 8.mseconds() && dur < 10.seconds()) - // Otherwise, half the configured latency - .or_else(|| Some(self.latency / 2)) - // In any case, don't allow a zero duration - .filter(|&dur| dur > gst::ClockTime::ZERO) - // Safe default - .unwrap_or(DEFAULT_DURATION); - - // Change the duration of the next duplicate buffer - self.out_buffer_duplicate = false; - } - fn pending_events(&self) -> bool { self.pending_caps.is_some() || self.pending_segment.is_some() } @@ -585,8 +575,8 @@ impl LiveSync { state.in_segment = None; state.in_caps = None; state.in_audio_info = None; + state.in_duration = None; state.in_timestamp = None; - state.update_fallback_duration(); } fn src_reset(&self, state: &mut State) { @@ -594,6 +584,7 @@ impl LiveSync { state.out_segment = None; state.pending_caps = None; state.out_audio_info = None; + state.out_duration = None; state.out_buffer = None; state.out_buffer_duplicate = false; state.out_timestamp = None; @@ -671,10 +662,12 @@ impl LiveSync { } }; + let duration = duration_from_caps(&caps); + let mut state = self.state.lock(); state.in_caps = Some(caps); state.in_audio_info = audio_info; - state.update_fallback_duration(); + state.in_duration = duration; } gst::EventView::Gap(_) => { @@ -894,13 +887,25 @@ impl LiveSync { ); } } else { - gst::debug!(CAT, imp: self, "Incoming buffer without duration"); + gst::debug!( + CAT, + imp: self, + "Patching incoming buffer with duration {calc_duration}" + ); } buf_mut.set_duration(calc_duration); } else if buf_mut.duration().is_none() { - gst::debug!(CAT, imp: self, "Incoming buffer without duration"); - buf_mut.set_duration(state.fallback_duration); + let duration = state.in_duration.map_or(DEFAULT_DURATION, |dur| { + dur.clamp(MINIMUM_DURATION, MAXIMUM_DURATION) + }); + + gst::debug!( + CAT, + imp: self, + "Patching incoming buffer with duration {duration}" + ); + buf_mut.set_duration(duration); } // At this stage we should really really have a segment @@ -1077,7 +1082,6 @@ impl LiveSync { gst::EventView::Caps(e) => { state.pending_caps = Some(e.caps_owned()); - state.update_fallback_duration(); push = false; } @@ -1164,6 +1168,7 @@ impl LiveSync { state.srcresult?; state.out_audio_info = audio_info_from_caps(&caps).unwrap(); + state.out_duration = duration_from_caps(&caps); } if let Some(segment) = segment { @@ -1317,7 +1322,9 @@ impl LiveSync { let buffer = out_buffer.make_mut(); if !duplicate { - let duration = state.fallback_duration; + let duration = state.out_duration.map_or(DEFAULT_DURATION, |dur| { + dur.clamp(MINIMUM_DURATION, MAXIMUM_DURATION) + }); if let Some(audio_info) = &state.out_audio_info { let Some(size) = audio_info