pipewire/src/gst/gstpipewireclock.c
Stefan Klug f5b21666bd 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 was not purposely initialized with a
value != 0.

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>
2026-04-22 14:54:52 +02:00

115 lines
3 KiB
C

/* GStreamer */
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#include "config.h"
#include <gst/gst.h>
#include "gstpipewireclock.h"
GST_DEBUG_CATEGORY_STATIC (gst_pipewire_clock_debug_category);
#define GST_CAT_DEFAULT gst_pipewire_clock_debug_category
G_DEFINE_TYPE (GstPipeWireClock, gst_pipewire_clock, GST_TYPE_SYSTEM_CLOCK);
GstClock *
gst_pipewire_clock_new (GstPipeWireStream *stream, GstClockTime last_time)
{
GstPipeWireClock *clock;
clock = g_object_new (GST_TYPE_PIPEWIRE_CLOCK, NULL);
g_weak_ref_set (&clock->stream, stream);
clock->last_time = last_time;
clock->time_offset = last_time;
return GST_CLOCK_CAST (clock);
}
static GstClockTime
gst_pipewire_clock_get_internal_time (GstClock * clock)
{
GstPipeWireClock *pclock = (GstPipeWireClock *) clock;
g_autoptr (GstPipeWireStream) s = g_weak_ref_get (&pclock->stream);
GstClockTime result;
uint64_t now;
now = pw_stream_get_nsec(s->pwstream);
if (G_UNLIKELY (!s))
return pclock->last_time ? pclock->last_time : now;
#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 ? pclock->last_time : now;
result = gst_util_uint64_scale (t.ticks, GST_SECOND * t.rate.num, t.rate.denom);
result += now - t.now;
result += pclock->time_offset;
pclock->last_time = result;
GST_DEBUG ("%"PRId64", %d/%d %"PRId64" %"PRId64" %"PRId64,
t.ticks, t.rate.num, t.rate.denom, t.now, result, now);
#else
result = now + pclock->time_offset;
pclock->last_time = result;
#endif
return result;
}
static void
gst_pipewire_clock_finalize (GObject * object)
{
GstPipeWireClock *clock = GST_PIPEWIRE_CLOCK (object);
GST_DEBUG_OBJECT (clock, "finalize");
g_weak_ref_set (&clock->stream, NULL);
G_OBJECT_CLASS (gst_pipewire_clock_parent_class)->finalize (object);
}
static void
gst_pipewire_clock_class_init (GstPipeWireClockClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstClockClass *gstclock_class = GST_CLOCK_CLASS (klass);
gobject_class->finalize = gst_pipewire_clock_finalize;
gstclock_class->get_internal_time = gst_pipewire_clock_get_internal_time;
GST_DEBUG_CATEGORY_INIT (gst_pipewire_clock_debug_category, "pipewireclock", 0,
"debug category for pipewireclock object");
}
static void
gst_pipewire_clock_init (GstPipeWireClock * clock)
{
GST_OBJECT_FLAG_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER);
}
void
gst_pipewire_clock_reset (GstPipeWireClock * clock, GstClockTime time)
{
#if 0
GstClockTimeDiff time_offset;
if (clock->last_time >= time)
time_offset = clock->last_time - time;
else
time_offset = -(time - clock->last_time);
clock->time_offset = time_offset;
GST_DEBUG_OBJECT (clock,
"reset clock to %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT
", offset %" GST_STIME_FORMAT, GST_TIME_ARGS (time),
GST_TIME_ARGS (clock->last_time), GST_STIME_ARGS (time_offset));
#endif
}