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=<some video target> ! \
video/x-raw,format=UYVY !  fakesink \
pipewiresrc name=audio target-object=<some audio target> ! \
audio/x-raw ! f akesink 2>&1 | \
grep PTS

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>

---

Changes in v2:
- Drop incorrect logic in case s == NULL
- Keep clock scaling in place
This commit is contained in:
Stefan Klug 2026-04-22 13:08:08 +02:00
parent 9e52e7ee7f
commit e2f2b9a273
2 changed files with 18 additions and 3 deletions

View file

@ -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;

View file

@ -22,6 +22,8 @@ struct _GstPipeWireClock {
GWeakRef stream;
GstClockTime last_time;
GstClockTime start_time;
bool start_time_valid;
GstClockTimeDiff time_offset;
};