From e2f2b9a2735bdc434a4eb18e60ec42f4c3e98d41 Mon Sep 17 00:00:00 2001 From: Stefan Klug Date: Wed, 22 Apr 2026 13:08:08 +0200 Subject: [PATCH] gst: gstpipewireclock: Return a valid clock before stream start When a gstreamer pipeline transits to playing state, it sets the base_time of all elements to the internal time of the current clock. At that point, the pipewire stream has not yet started and the gstpipewireclock returns last_time which is initialized to 0 in gstpipewirestream. This leads to a incorrect base_time in the gstreamer element and various synchronization issues. The use-case for last_time is not really clear to me. My basic guesswork is: If a stream is no longer streaming the internal clock should pause at that time and return last_time. So this patch keeps this behaviour in place and only ensures that a valid time is returned when the stream is not yet started and last_time is not in the future. To keep the time scaling logic in place, a start time is recorded when the stream was started, to properly match stream time and clock monotonic. A gstreamer pipeline that can be used to replicate the issue is: GST_DEBUG="pipeline:5,GST_CLOCK:6,pipewiresrc:6" gst-launch-1.0 \ pipewiresrc name=video target-object= ! \ video/x-raw,format=UYVY ! fakesink \ pipewiresrc name=audio target-object= ! \ audio/x-raw ! f akesink 2>&1 | \ grep PTS Signed-off-by: Stefan Klug --- Changes in v2: - Drop incorrect logic in case s == NULL - Keep clock scaling in place --- src/gst/gstpipewireclock.c | 19 ++++++++++++++++--- src/gst/gstpipewireclock.h | 2 ++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/gst/gstpipewireclock.c b/src/gst/gstpipewireclock.c index 10607c3e7..fc7e72448 100644 --- a/src/gst/gstpipewireclock.c +++ b/src/gst/gstpipewireclock.c @@ -22,6 +22,7 @@ gst_pipewire_clock_new (GstPipeWireStream *stream, GstClockTime last_time) g_weak_ref_set (&clock->stream, stream); clock->last_time = last_time; clock->time_offset = last_time; + clock->start_time_valid = false; return GST_CLOCK_CAST (clock); } @@ -38,14 +39,26 @@ gst_pipewire_clock_get_internal_time (GstClock * clock) return pclock->last_time; now = pw_stream_get_nsec(s->pwstream); + #if 1 struct pw_time t; if (s->pwstream == NULL || pw_stream_get_time_n (s->pwstream, &t, sizeof(t)) < 0 || - t.rate.denom == 0) - return pclock->last_time; + t.rate.denom == 0) { + pclock->start_time_valid = false; + if (now < pclock->last_time) + return pclock->last_time; + return now; + } - result = gst_util_uint64_scale (t.ticks, GST_SECOND * t.rate.num, t.rate.denom); + uint64_t elapsed = gst_util_uint64_scale (t.ticks, GST_SECOND * t.rate.num, t.rate.denom); + + if (!pclock->start_time_valid) { + pclock->start_time = t.now - elapsed; + pclock->start_time_valid = true; + } + + result = pclock->start_time + elapsed; result += now - t.now; result += pclock->time_offset; diff --git a/src/gst/gstpipewireclock.h b/src/gst/gstpipewireclock.h index 8b41598ef..8b916ccca 100644 --- a/src/gst/gstpipewireclock.h +++ b/src/gst/gstpipewireclock.h @@ -22,6 +22,8 @@ struct _GstPipeWireClock { GWeakRef stream; GstClockTime last_time; + GstClockTime start_time; + bool start_time_valid; GstClockTimeDiff time_offset; };