Hello hackers!
We ingest streams from multiple sources, they all need to be in sync, preferably on the frame level.
To achieve this we make use of the frame capture time, embedded in the SEI header of h.264/h.265.
Our pipeline is sort of like this, and is uses a NTP clock:
srtsrc → tsdemux →rtpbin → a journey in k8s for post processing.
In order to get the sync, again down on frame level, we have a step in this pipeline that re-timestamps the buffers before reaching the rtpbin, so that the rtp ntp64 header / rtcp carries pts with an absolute capture time of the frame.
Sort of using these two methods:
pub fn calculate_spiideo_sei_diff(
pad: &gst::Pad,
buffer: &gst::Buffer,
) -> Option<gst::ClockTimeDiff> {
let sei_time = get_spiideo_sei_ntp_time(buffer)?;
let buffer_timestamp = get_ntp_time_from_pad(pad, buffer).ok()?;
let buffer_ntp_ns = buffer_timestamp.nseconds() as i64;
let sei_ntp_ns = sei_time.nseconds() as i64;
buffer_ntp_ns.checked_add(-sei_ntp_ns)
}
pub fn get_ntp_time_from_pad(
pad: &gst::Pad,
buffer: &gst::BufferRef,
) → Result<gst::ClockTime, Error> {
let pts = buffer.pts()
.ok_or_else(|| anyhow!(“failed to get pts”))?;
let event = pad
.sticky_event::gst::event::Segment(0)
.ok_or_else(|| anyhow!(“Failed to get segment”))?;
let segment = event.segment();
let base_time = pad
.parent_element()
.unwrap()
.base_time()
.ok_or_else(|| anyhow!("failed to get base time"))?;
let running_time = segment
.downcast_ref::<gst::ClockTime>()
.ok_or_else(|| anyhow!("failed to downcast segment"))?
.to_running_time(pts)
.ok_or_else(|| anyhow!("failed to get running time"))?;
let ntp_time = running_time.checked_add(base_time).ok_or_else(|| {
let msg = format!("ntp time overflow: running: {running_time} base: {base_time}");
tracing::warn!(msg);
anyhow!(msg)
})?;
Ok(ntp_time)
Then we use this to alter the PTS before sending out the RTP.
This works. Most of the time. We get a good inter-stream sync. But!
When there is packet loss, we get woes! The SEI diff starts going wild and increases with seconds! I suspect this is somehow connected to mpeg-ts skew(?) but I am not sure. I feel a bit lost.
I tried to use the mpegtslivesrc hoping it would help, but then I am forced to a monotonic clock, right? And I am not sure how I would go about re-timestamping using SEI then …
I also tried to set skew-correction=false and that made the diff stable … but it seemed I got issues downstream when trying to create fmp4 items of the stream… have not dug deep.
Does anyone have any insight? Is our setup sound? Or are there inherent issues with the way we re-timestamp? Any tips on how to improve?
Thanks
Jonas