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:
Sebastian Dröge 2015-06-03 13:16:15 +02:00
parent 7130230ddb
commit fe3249b0e1
4 changed files with 158 additions and 1 deletions

View file

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

View file

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

View file

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

View file

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