mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
clock: Add GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC and related API
gst_clock_wait_for_sync(), gst_clock_is_synced() and gst_clock_set_synced() plus a signal to asynchronously wait for the clock to be synced. This can be used by clocks to signal that they need initial synchronization before they can report any time, and that this synchronization can also get completely lost at some point. Network clocks, like the GStreamer netclientclock, NTP or PTP clocks are examples for clocks where this is useful to have as they can't report any time at all before they're synced. https://bugzilla.gnome.org/show_bug.cgi?id=749391
This commit is contained in:
parent
7130230ddb
commit
fe3249b0e1
4 changed files with 158 additions and 1 deletions
|
@ -617,6 +617,9 @@ gst_clock_get_calibration
|
||||||
gst_clock_set_calibration
|
gst_clock_set_calibration
|
||||||
gst_clock_get_timeout
|
gst_clock_get_timeout
|
||||||
gst_clock_set_timeout
|
gst_clock_set_timeout
|
||||||
|
gst_clock_wait_for_sync
|
||||||
|
gst_clock_is_synced
|
||||||
|
gst_clock_set_synced
|
||||||
gst_clock_id_get_time
|
gst_clock_id_get_time
|
||||||
gst_clock_id_wait
|
gst_clock_id_wait
|
||||||
gst_clock_id_wait_async
|
gst_clock_id_wait_async
|
||||||
|
|
144
gst/gstclock.c
144
gst/gstclock.c
|
@ -128,6 +128,12 @@ enum
|
||||||
PROP_TIMEOUT
|
PROP_TIMEOUT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SIGNAL_SYNCED,
|
||||||
|
SIGNAL_LAST
|
||||||
|
};
|
||||||
|
|
||||||
#define GST_CLOCK_SLAVE_LOCK(clock) g_mutex_lock (&GST_CLOCK_CAST (clock)->priv->slave_lock)
|
#define GST_CLOCK_SLAVE_LOCK(clock) g_mutex_lock (&GST_CLOCK_CAST (clock)->priv->slave_lock)
|
||||||
#define GST_CLOCK_SLAVE_UNLOCK(clock) g_mutex_unlock (&GST_CLOCK_CAST (clock)->priv->slave_lock)
|
#define GST_CLOCK_SLAVE_UNLOCK(clock) g_mutex_unlock (&GST_CLOCK_CAST (clock)->priv->slave_lock)
|
||||||
|
|
||||||
|
@ -135,6 +141,8 @@ struct _GstClockPrivate
|
||||||
{
|
{
|
||||||
GMutex slave_lock; /* order: SLAVE_LOCK, OBJECT_LOCK */
|
GMutex slave_lock; /* order: SLAVE_LOCK, OBJECT_LOCK */
|
||||||
|
|
||||||
|
GCond sync_cond;
|
||||||
|
|
||||||
/* with LOCK */
|
/* with LOCK */
|
||||||
GstClockTime internal_calibration;
|
GstClockTime internal_calibration;
|
||||||
GstClockTime external_calibration;
|
GstClockTime external_calibration;
|
||||||
|
@ -159,6 +167,8 @@ struct _GstClockPrivate
|
||||||
|
|
||||||
gint pre_count;
|
gint pre_count;
|
||||||
gint post_count;
|
gint post_count;
|
||||||
|
|
||||||
|
gboolean synced;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* seqlocks */
|
/* seqlocks */
|
||||||
|
@ -227,7 +237,7 @@ static void gst_clock_set_property (GObject * object, guint prop_id,
|
||||||
static void gst_clock_get_property (GObject * object, guint prop_id,
|
static void gst_clock_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
GValue * value, GParamSpec * pspec);
|
||||||
|
|
||||||
/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
|
static guint gst_clock_signals[SIGNAL_LAST] = { 0 };
|
||||||
|
|
||||||
static GstClockID
|
static GstClockID
|
||||||
gst_clock_entry_new (GstClock * clock, GstClockTime time,
|
gst_clock_entry_new (GstClock * clock, GstClockTime time,
|
||||||
|
@ -694,6 +704,25 @@ gst_clock_class_init (GstClockClass * klass)
|
||||||
0, G_MAXUINT64, DEFAULT_TIMEOUT,
|
0, G_MAXUINT64, DEFAULT_TIMEOUT,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstClock::synced:
|
||||||
|
* @clock: the clock
|
||||||
|
* @synced: if the clock is synced now
|
||||||
|
*
|
||||||
|
* Signaled on clocks with GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC set once
|
||||||
|
* the clock is synchronized, or when it completely lost synchronization.
|
||||||
|
* This signal will not be emitted on clocks without the flag.
|
||||||
|
*
|
||||||
|
* This signal will be emitted from an arbitrary thread, most likely not
|
||||||
|
* the application's main thread.
|
||||||
|
*
|
||||||
|
* Since: 1.6
|
||||||
|
*/
|
||||||
|
gst_clock_signals[SIGNAL_SYNCED] =
|
||||||
|
g_signal_new ("synced", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
|
||||||
|
0, NULL, NULL,
|
||||||
|
g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
||||||
|
|
||||||
g_type_class_add_private (klass, sizeof (GstClockPrivate));
|
g_type_class_add_private (klass, sizeof (GstClockPrivate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,6 +742,7 @@ gst_clock_init (GstClock * clock)
|
||||||
priv->rate_denominator = 1;
|
priv->rate_denominator = 1;
|
||||||
|
|
||||||
g_mutex_init (&priv->slave_lock);
|
g_mutex_init (&priv->slave_lock);
|
||||||
|
g_cond_init (&priv->sync_cond);
|
||||||
priv->window_size = DEFAULT_WINDOW_SIZE;
|
priv->window_size = DEFAULT_WINDOW_SIZE;
|
||||||
priv->window_threshold = DEFAULT_WINDOW_THRESHOLD;
|
priv->window_threshold = DEFAULT_WINDOW_THRESHOLD;
|
||||||
priv->filling = TRUE;
|
priv->filling = TRUE;
|
||||||
|
@ -754,6 +784,7 @@ gst_clock_finalize (GObject * object)
|
||||||
GST_CLOCK_SLAVE_UNLOCK (clock);
|
GST_CLOCK_SLAVE_UNLOCK (clock);
|
||||||
|
|
||||||
g_mutex_clear (&clock->priv->slave_lock);
|
g_mutex_clear (&clock->priv->slave_lock);
|
||||||
|
g_cond_clear (&clock->priv->sync_cond);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
@ -972,6 +1003,11 @@ gst_clock_get_internal_time (GstClock * clock)
|
||||||
|
|
||||||
g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
|
g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (GST_OBJECT_FLAG_IS_SET (clock,
|
||||||
|
GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC) && !clock->priv->synced))
|
||||||
|
GST_CAT_WARNING_OBJECT (GST_CAT_CLOCK, clock,
|
||||||
|
"clocked is not synchronized yet");
|
||||||
|
|
||||||
cclass = GST_CLOCK_GET_CLASS (clock);
|
cclass = GST_CLOCK_GET_CLASS (clock);
|
||||||
|
|
||||||
if (G_UNLIKELY (cclass->get_internal_time == NULL))
|
if (G_UNLIKELY (cclass->get_internal_time == NULL))
|
||||||
|
@ -1492,3 +1528,109 @@ gst_clock_get_property (GObject * object, guint prop_id,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_clock_wait_for_sync:
|
||||||
|
* @clock: a GstClock
|
||||||
|
* @timeout: timeout for waiting or %GST_CLOCK_TIME_NONE
|
||||||
|
*
|
||||||
|
* Waits until @clock is synced for reporting the current time. If @timeout
|
||||||
|
* is %GST_CLOCK_TIME_NONE it will wait forever, otherwise it will time out
|
||||||
|
* after @timeout nanoseconds.
|
||||||
|
*
|
||||||
|
* For asynchronous waiting, the GstClock::synced signal can be used.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This returns immediately with TRUE if GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC
|
||||||
|
* is not set on the clock, or if the clock is already synced.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if waiting was successful, or %FALSE on timeout
|
||||||
|
*
|
||||||
|
* Since: 1.6
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_clock_wait_for_sync (GstClock * clock, GstClockTime timeout)
|
||||||
|
{
|
||||||
|
gboolean timed_out = FALSE;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clock);
|
||||||
|
if (!GST_OBJECT_FLAG_IS_SET (clock, GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC)
|
||||||
|
|| clock->priv->synced) {
|
||||||
|
GST_OBJECT_UNLOCK (clock);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout != GST_CLOCK_TIME_NONE) {
|
||||||
|
gint64 end_time = g_get_monotonic_time () + gst_util_uint64_scale (timeout,
|
||||||
|
G_TIME_SPAN_SECOND, GST_SECOND);
|
||||||
|
|
||||||
|
while (!clock->priv->synced && !timed_out) {
|
||||||
|
timed_out =
|
||||||
|
!g_cond_wait_until (&clock->priv->sync_cond,
|
||||||
|
GST_OBJECT_GET_LOCK (clock), end_time);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timed_out = FALSE;
|
||||||
|
while (!clock->priv->synced) {
|
||||||
|
g_cond_wait (&clock->priv->sync_cond, GST_OBJECT_GET_LOCK (clock));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (clock);
|
||||||
|
|
||||||
|
return !timed_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_clock_is_synced:
|
||||||
|
* @clock: a GstClock
|
||||||
|
*
|
||||||
|
* Checks if the clock is currently synced.
|
||||||
|
*
|
||||||
|
* This returns if GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC is not set on the clock.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the clock is currently synced
|
||||||
|
*
|
||||||
|
* Since: 1.6
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_clock_is_synced (GstClock * clock)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (GST_IS_CLOCK (clock), TRUE);
|
||||||
|
|
||||||
|
return !GST_OBJECT_FLAG_IS_SET (clock, GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC)
|
||||||
|
|| clock->priv->synced;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_clock_set_synced:
|
||||||
|
* @clock: a GstClock
|
||||||
|
* @synced: if the clock is synced
|
||||||
|
*
|
||||||
|
* Sets @clock to synced and emits the GstClock::synced signal, and wakes up any
|
||||||
|
* thread waiting in gst_clock_wait_synced().
|
||||||
|
*
|
||||||
|
* This function must only be called if GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC
|
||||||
|
* is set on the clock, and is intended to be called by subclasses only.
|
||||||
|
*
|
||||||
|
* Since: 1.6
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_clock_set_synced (GstClock * clock, gboolean synced)
|
||||||
|
{
|
||||||
|
g_return_if_fail (GST_IS_CLOCK (clock));
|
||||||
|
g_return_if_fail (GST_OBJECT_FLAG_IS_SET (clock,
|
||||||
|
GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC));
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clock);
|
||||||
|
if (clock->priv->synced != ! !synced) {
|
||||||
|
clock->priv->synced = ! !synced;
|
||||||
|
g_cond_signal (&clock->priv->sync_cond);
|
||||||
|
GST_OBJECT_UNLOCK (clock);
|
||||||
|
g_signal_emit (clock, gst_clock_signals[SIGNAL_SYNCED], 0, ! !synced);
|
||||||
|
} else {
|
||||||
|
GST_OBJECT_UNLOCK (clock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -375,6 +375,8 @@ struct _GstClockEntry {
|
||||||
* @GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC: clock can do async periodic timeout callbacks
|
* @GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC: clock can do async periodic timeout callbacks
|
||||||
* @GST_CLOCK_FLAG_CAN_SET_RESOLUTION: clock's resolution can be changed
|
* @GST_CLOCK_FLAG_CAN_SET_RESOLUTION: clock's resolution can be changed
|
||||||
* @GST_CLOCK_FLAG_CAN_SET_MASTER: clock can be slaved to a master clock
|
* @GST_CLOCK_FLAG_CAN_SET_MASTER: clock can be slaved to a master clock
|
||||||
|
* @GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC: clock needs to be synced before it can be used
|
||||||
|
* Since: 1.6
|
||||||
* @GST_CLOCK_FLAG_LAST: subclasses can add additional flags starting from this flag
|
* @GST_CLOCK_FLAG_LAST: subclasses can add additional flags starting from this flag
|
||||||
*
|
*
|
||||||
* The capabilities of this clock
|
* The capabilities of this clock
|
||||||
|
@ -386,6 +388,7 @@ typedef enum {
|
||||||
GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC = (GST_OBJECT_FLAG_LAST << 3),
|
GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC = (GST_OBJECT_FLAG_LAST << 3),
|
||||||
GST_CLOCK_FLAG_CAN_SET_RESOLUTION = (GST_OBJECT_FLAG_LAST << 4),
|
GST_CLOCK_FLAG_CAN_SET_RESOLUTION = (GST_OBJECT_FLAG_LAST << 4),
|
||||||
GST_CLOCK_FLAG_CAN_SET_MASTER = (GST_OBJECT_FLAG_LAST << 5),
|
GST_CLOCK_FLAG_CAN_SET_MASTER = (GST_OBJECT_FLAG_LAST << 5),
|
||||||
|
GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC = (GST_OBJECT_FLAG_LAST << 6),
|
||||||
/* padding */
|
/* padding */
|
||||||
GST_CLOCK_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 8)
|
GST_CLOCK_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 8)
|
||||||
} GstClockFlags;
|
} GstClockFlags;
|
||||||
|
@ -496,6 +499,12 @@ GstClockTime gst_clock_adjust_with_calibration (GstClock *clock,
|
||||||
GstClockTime cdenom);
|
GstClockTime cdenom);
|
||||||
GstClockTime gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external);
|
GstClockTime gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external);
|
||||||
|
|
||||||
|
/* waiting for, signalling and checking for synchronization */
|
||||||
|
gboolean gst_clock_wait_for_sync (GstClock * clock, GstClockTime timeout);
|
||||||
|
gboolean gst_clock_is_synced (GstClock * clock);
|
||||||
|
|
||||||
|
/* to be used by subclasses only */
|
||||||
|
void gst_clock_set_synced (GstClock * clock, gboolean synced);
|
||||||
|
|
||||||
/* creating IDs that can be used to get notifications */
|
/* creating IDs that can be used to get notifications */
|
||||||
GstClockID gst_clock_new_single_shot_id (GstClock *clock,
|
GstClockID gst_clock_new_single_shot_id (GstClock *clock,
|
||||||
|
|
|
@ -321,6 +321,7 @@ EXPORTS
|
||||||
gst_clock_id_unschedule
|
gst_clock_id_unschedule
|
||||||
gst_clock_id_wait
|
gst_clock_id_wait
|
||||||
gst_clock_id_wait_async
|
gst_clock_id_wait_async
|
||||||
|
gst_clock_is_synced
|
||||||
gst_clock_new_periodic_id
|
gst_clock_new_periodic_id
|
||||||
gst_clock_new_single_shot_id
|
gst_clock_new_single_shot_id
|
||||||
gst_clock_periodic_id_reinit
|
gst_clock_periodic_id_reinit
|
||||||
|
@ -328,10 +329,12 @@ EXPORTS
|
||||||
gst_clock_set_calibration
|
gst_clock_set_calibration
|
||||||
gst_clock_set_master
|
gst_clock_set_master
|
||||||
gst_clock_set_resolution
|
gst_clock_set_resolution
|
||||||
|
gst_clock_set_synced
|
||||||
gst_clock_set_timeout
|
gst_clock_set_timeout
|
||||||
gst_clock_single_shot_id_reinit
|
gst_clock_single_shot_id_reinit
|
||||||
gst_clock_type_get_type
|
gst_clock_type_get_type
|
||||||
gst_clock_unadjust_unlocked
|
gst_clock_unadjust_unlocked
|
||||||
|
gst_clock_wait_for_sync
|
||||||
gst_context_get_context_type
|
gst_context_get_context_type
|
||||||
gst_context_get_structure
|
gst_context_get_structure
|
||||||
gst_context_get_type
|
gst_context_get_type
|
||||||
|
|
Loading…
Reference in a new issue