clock: Add GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC and related API
authorSebastian Dröge <sebastian@centricular.com>
Wed, 3 Jun 2015 11:16:15 +0000 (13:16 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Wed, 3 Jun 2015 11:53:27 +0000 (13:53 +0200)
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

docs/gst/gstreamer-sections.txt
gst/gstclock.c
gst/gstclock.h
win32/common/libgstreamer.def

index 204296e..3273fc6 100644 (file)
@@ -617,6 +617,9 @@ gst_clock_get_calibration
 gst_clock_set_calibration
 gst_clock_get_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_wait
 gst_clock_id_wait_async
index 2d8de0f..6faade6 100644 (file)
@@ -128,6 +128,12 @@ enum
   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_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 */
 
+  GCond sync_cond;
+
   /* with LOCK */
   GstClockTime internal_calibration;
   GstClockTime external_calibration;
@@ -159,6 +167,8 @@ struct _GstClockPrivate
 
   gint pre_count;
   gint post_count;
+
+  gboolean synced;
 };
 
 /* 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,
     GValue * value, GParamSpec * pspec);
 
-/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
+static guint gst_clock_signals[SIGNAL_LAST] = { 0 };
 
 static GstClockID
 gst_clock_entry_new (GstClock * clock, GstClockTime time,
@@ -694,6 +704,25 @@ gst_clock_class_init (GstClockClass * klass)
           0, G_MAXUINT64, DEFAULT_TIMEOUT,
           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));
 }
 
@@ -713,6 +742,7 @@ gst_clock_init (GstClock * clock)
   priv->rate_denominator = 1;
 
   g_mutex_init (&priv->slave_lock);
+  g_cond_init (&priv->sync_cond);
   priv->window_size = DEFAULT_WINDOW_SIZE;
   priv->window_threshold = DEFAULT_WINDOW_THRESHOLD;
   priv->filling = TRUE;
@@ -754,6 +784,7 @@ gst_clock_finalize (GObject * object)
   GST_CLOCK_SLAVE_UNLOCK (clock);
 
   g_mutex_clear (&clock->priv->slave_lock);
+  g_cond_clear (&clock->priv->sync_cond);
 
   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);
 
+  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);
 
   if (G_UNLIKELY (cclass->get_internal_time == NULL))
@@ -1492,3 +1528,109 @@ gst_clock_get_property (GObject * object, guint prop_id,
       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);
+  }
+}
index c2239e5..9369755 100644 (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_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_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
  *
  * 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_SET_RESOLUTION     = (GST_OBJECT_FLAG_LAST << 4),
   GST_CLOCK_FLAG_CAN_SET_MASTER         = (GST_OBJECT_FLAG_LAST << 5),
+  GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC     = (GST_OBJECT_FLAG_LAST << 6),
   /* padding */
   GST_CLOCK_FLAG_LAST                   = (GST_OBJECT_FLAG_LAST << 8)
 } GstClockFlags;
@@ -496,6 +499,12 @@ GstClockTime            gst_clock_adjust_with_calibration (GstClock *clock,
                                                          GstClockTime cdenom);
 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 */
 GstClockID              gst_clock_new_single_shot_id    (GstClock *clock,
index c130747..76dcb07 100644 (file)
@@ -321,6 +321,7 @@ EXPORTS
        gst_clock_id_unschedule
        gst_clock_id_wait
        gst_clock_id_wait_async
+       gst_clock_is_synced
        gst_clock_new_periodic_id
        gst_clock_new_single_shot_id
        gst_clock_periodic_id_reinit
@@ -328,10 +329,12 @@ EXPORTS
        gst_clock_set_calibration
        gst_clock_set_master
        gst_clock_set_resolution
+       gst_clock_set_synced
        gst_clock_set_timeout
        gst_clock_single_shot_id_reinit
        gst_clock_type_get_type
        gst_clock_unadjust_unlocked
+       gst_clock_wait_for_sync
        gst_context_get_context_type
        gst_context_get_structure
        gst_context_get_type