tracer: latency: Fix bug when dropping sub-latency probe event
[platform/upstream/gstreamer.git] / gst / gstclock.c
index c84700d..279b1f8 100644 (file)
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 /**
  * SECTION:gstclock
+ * @title: GstClock
  * @short_description: Abstract class for global clocks
  * @see_also: #GstSystemClock, #GstPipeline
  *
@@ -74,7 +75,7 @@
  * for unreffing the ids itself. This holds for both periodic and single shot
  * notifications. The reason being that the owner of the #GstClockID has to
  * keep a handle to the #GstClockID to unblock the wait on FLUSHING events or
- * state changes and if the entry would be unreffed automatically, the handle 
+ * state changes and if the entry would be unreffed automatically, the handle
  * might become invalid without any notification.
  *
  * These clock operations do not operate on the running time, so the callbacks
@@ -90,7 +91,7 @@
  * plugins that have an internal clock but must operate with another clock
  * selected by the #GstPipeline.  They can track the offset and rate difference
  * of their internal clock relative to the master clock by using the
- * gst_clock_get_calibration() function. 
+ * gst_clock_get_calibration() function.
  *
  * The master/slave synchronisation can be tuned with the #GstClock:timeout,
  * #GstClock:window-size and #GstClock:window-threshold properties.
  * clock and run the calibration functions. #GstClock:window-size defines the
  * number of samples to use when calibrating and #GstClock:window-threshold
  * defines the minimum number of samples before the calibration is performed.
- *
- * Last reviewed on 2012-03-28 (0.11.3)
  */
 
-
 #include "gst_private.h"
 #include <time.h>
 
 #include "gstutils.h"
 #include "glib-compat-private.h"
 
-#ifndef GST_DISABLE_TRACE
-/* #define GST_WITH_ALLOC_TRACE */
-#include "gsttrace.h"
-static GstAllocTrace *_gst_clock_entry_trace;
-#endif
-
 /* #define DEBUGGING_ENABLED */
 
 #define DEFAULT_WINDOW_SIZE             32
@@ -131,6 +123,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)
 
@@ -138,6 +136,8 @@ struct _GstClockPrivate
 {
   GMutex slave_lock;            /* order: SLAVE_LOCK, OBJECT_LOCK */
 
+  GCond sync_cond;
+
   /* with LOCK */
   GstClockTime internal_calibration;
   GstClockTime external_calibration;
@@ -158,12 +158,23 @@ struct _GstClockPrivate
   gint time_index;
   GstClockTime timeout;
   GstClockTime *times;
+  GstClockTime *times_temp;
   GstClockID clockid;
 
   gint pre_count;
   gint post_count;
+
+  gboolean synced;
 };
 
+typedef struct
+{
+  GstClockEntry entry;
+  GWeakRef clock;
+} GstClockEntryImpl;
+
+#define GST_CLOCK_ENTRY_CLOCK_WEAK_REF(entry) (&((GstClockEntryImpl *)(entry))->clock)
+
 /* seqlocks */
 #define read_seqbegin(clock)                                   \
   g_atomic_int_get (&clock->priv->post_count);
@@ -193,6 +204,35 @@ G_STMT_START {                                    \
   GST_OBJECT_UNLOCK (clock);                      \
 } G_STMT_END;
 
+#ifndef GST_DISABLE_GST_DEBUG
+static const gchar *
+gst_clock_return_get_name (GstClockReturn ret)
+{
+  switch (ret) {
+    case GST_CLOCK_OK:
+      return "ok";
+    case GST_CLOCK_EARLY:
+      return "early";
+    case GST_CLOCK_UNSCHEDULED:
+      return "unscheduled";
+    case GST_CLOCK_BUSY:
+      return "busy";
+    case GST_CLOCK_BADTIME:
+      return "bad-time";
+    case GST_CLOCK_ERROR:
+      return "error";
+    case GST_CLOCK_UNSUPPORTED:
+      return "unsupported";
+    case GST_CLOCK_DONE:
+      return "done";
+    default:
+      break;
+  }
+
+  return "unknown";
+}
+#endif /* GST_DISABLE_GST_DEBUG */
+
 static void gst_clock_dispose (GObject * object);
 static void gst_clock_finalize (GObject * object);
 
@@ -201,7 +241,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,
@@ -209,15 +249,22 @@ gst_clock_entry_new (GstClock * clock, GstClockTime time,
 {
   GstClockEntry *entry;
 
-  entry = g_slice_new (GstClockEntry);
-#ifndef GST_DISABLE_TRACE
-  _gst_alloc_trace_new (_gst_clock_entry_trace, entry);
-#endif
+  entry = (GstClockEntry *) g_slice_new (GstClockEntryImpl);
+
+  /* FIXME: add tracer hook for struct allocations such as clock entries */
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
       "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
 
   entry->refcount = 1;
+#ifndef GST_REMOVE_DEPRECATED
+#ifndef GST_DISABLE_DEPRECATED
   entry->clock = clock;
+#else
+  entry->_clock = clock;
+#endif
+#endif
+  g_weak_ref_init (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry), clock);
   entry->type = type;
   entry->time = time;
   entry->interval = interval;
@@ -238,7 +285,8 @@ gst_clock_entry_reinit (GstClock * clock, GstClockEntry * entry,
     GstClockTime time, GstClockTime interval, GstClockEntryType type)
 {
   g_return_val_if_fail (entry->status != GST_CLOCK_BUSY, FALSE);
-  g_return_val_if_fail (entry->clock == clock, FALSE);
+  g_return_val_if_fail (gst_clock_id_uses_clock ((GstClockID) entry, clock),
+      FALSE);
 
   entry->type = type;
   entry->time = time;
@@ -261,8 +309,6 @@ gst_clock_entry_reinit (GstClock * clock, GstClockEntry * entry,
  *
  * Returns: %TRUE if the GstClockID could be reinitialized to the provided
  * @time, else %FALSE.
- *
- * Since: 0.10.32
  */
 gboolean
 gst_clock_single_shot_id_reinit (GstClock * clock, GstClockID id,
@@ -284,9 +330,6 @@ gst_clock_single_shot_id_reinit (GstClock * clock, GstClockID id,
  *
  * Returns: %TRUE if the GstClockID could be reinitialized to the provided
  * @time, else %FALSE.
- *
- * Since: 0.10.33
- *
  */
 gboolean
 gst_clock_periodic_id_reinit (GstClock * clock, GstClockID id,
@@ -327,10 +370,11 @@ _gst_clock_id_free (GstClockID id)
   if (entry->destroy_data)
     entry->destroy_data (entry->user_data);
 
-#ifndef GST_DISABLE_TRACE
-  _gst_alloc_trace_free (_gst_clock_entry_trace, id);
-#endif
-  g_slice_free (GstClockEntry, id);
+  g_weak_ref_clear (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+
+  /* FIXME: add tracer hook for struct allocations such as clock entries */
+
+  g_slice_free (GstClockEntryImpl, (GstClockEntryImpl *) id);
 }
 
 /**
@@ -465,23 +509,23 @@ gst_clock_id_get_time (GstClockID id)
  * @jitter: (out) (allow-none): a pointer that will contain the jitter,
  *     can be %NULL.
  *
- * Perform a blocking wait on @id. 
+ * Perform a blocking wait on @id.
  * @id should have been created with gst_clock_new_single_shot_id()
  * or gst_clock_new_periodic_id() and should not have been unscheduled
- * with a call to gst_clock_id_unschedule(). 
+ * with a call to gst_clock_id_unschedule().
  *
  * If the @jitter argument is not %NULL and this function returns #GST_CLOCK_OK
  * or #GST_CLOCK_EARLY, it will contain the difference
  * against the clock and the time of @id when this method was
- * called. 
+ * called.
  * Positive values indicate how late @id was relative to the clock
- * (in which case this function will return #GST_CLOCK_EARLY). 
- * Negative values indicate how much time was spent waiting on the clock 
+ * (in which case this function will return #GST_CLOCK_EARLY).
+ * Negative values indicate how much time was spent waiting on the clock
  * before this function returned.
  *
  * Returns: the result of the blocking wait. #GST_CLOCK_EARLY will be returned
- * if the current clock time is past the time of @id, #GST_CLOCK_OK if 
- * @id was scheduled in time. #GST_CLOCK_UNSCHEDULED if @id was 
+ * if the current clock time is past the time of @id, #GST_CLOCK_OK if
+ * @id was scheduled in time. #GST_CLOCK_UNSCHEDULED if @id was
  * unscheduled with gst_clock_id_unschedule().
  *
  * MT safe.
@@ -500,7 +544,9 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
   entry = (GstClockEntry *) id;
   requested = GST_CLOCK_ENTRY_TIME (entry);
 
-  clock = GST_CLOCK_ENTRY_CLOCK (entry);
+  clock = g_weak_ref_get (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+  if (G_UNLIKELY (clock == NULL))
+    goto invalid_entry;
 
   /* can't sync on invalid times */
   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
@@ -517,11 +563,13 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
   res = cclass->wait (clock, entry, jitter);
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
-      "done waiting entry %p, res: %d", id, res);
+      "done waiting entry %p, res: %d (%s)", id, res,
+      gst_clock_return_get_name (res));
 
   if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
     entry->time = requested + entry->interval;
 
+  gst_object_unref (clock);
   return res;
 
   /* ERRORS */
@@ -529,13 +577,20 @@ invalid_time:
   {
     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
         "invalid time requested, returning _BADTIME");
+    gst_object_unref (clock);
     return GST_CLOCK_BADTIME;
   }
 not_supported:
   {
     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
+    gst_object_unref (clock);
     return GST_CLOCK_UNSUPPORTED;
   }
+invalid_entry:
+  {
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "clock entry %p lost its clock", id);
+    return GST_CLOCK_ERROR;
+  }
 }
 
 /**
@@ -557,8 +612,6 @@ not_supported:
  * Returns: the result of the non blocking wait.
  *
  * MT safe.
- *
- * Since: 0.10.30
  */
 GstClockReturn
 gst_clock_id_wait_async (GstClockID id,
@@ -575,7 +628,9 @@ gst_clock_id_wait_async (GstClockID id,
 
   entry = (GstClockEntry *) id;
   requested = GST_CLOCK_ENTRY_TIME (entry);
-  clock = GST_CLOCK_ENTRY_CLOCK (entry);
+  clock = g_weak_ref_get (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+  if (G_UNLIKELY (clock == NULL))
+    goto invalid_entry;
 
   /* can't sync on invalid times */
   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
@@ -592,6 +647,7 @@ gst_clock_id_wait_async (GstClockID id,
 
   res = cclass->wait_async (clock, entry);
 
+  gst_object_unref (clock);
   return res;
 
   /* ERRORS */
@@ -600,13 +656,20 @@ invalid_time:
     (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
         "invalid time requested, returning _BADTIME");
+    gst_object_unref (clock);
     return GST_CLOCK_BADTIME;
   }
 not_supported:
   {
     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
+    gst_object_unref (clock);
     return GST_CLOCK_UNSUPPORTED;
   }
+invalid_entry:
+  {
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "clock entry %p lost its clock", id);
+    return GST_CLOCK_ERROR;
+  }
 }
 
 /**
@@ -630,12 +693,23 @@ gst_clock_id_unschedule (GstClockID id)
   g_return_if_fail (id != NULL);
 
   entry = (GstClockEntry *) id;
-  clock = entry->clock;
+  clock = g_weak_ref_get (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+  if (G_UNLIKELY (clock == NULL))
+    goto invalid_entry;
 
   cclass = GST_CLOCK_GET_CLASS (clock);
 
   if (G_LIKELY (cclass->unschedule))
     cclass->unschedule (clock, entry);
+
+  gst_object_unref (clock);
+  return;
+
+invalid_entry:
+  {
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "clock entry %p lost its clock", id);
+    return;
+  }
 }
 
 
@@ -643,17 +717,13 @@ gst_clock_id_unschedule (GstClockID id)
  * GstClock abstract base class implementation
  */
 #define gst_clock_parent_class parent_class
-G_DEFINE_TYPE (GstClock, gst_clock, GST_TYPE_OBJECT);
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstClock, gst_clock, GST_TYPE_OBJECT);
 
 static void
 gst_clock_class_init (GstClockClass * klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-#ifndef GST_DISABLE_TRACE
-  _gst_clock_entry_trace = _gst_alloc_trace_register ("GstClockEntry", -1);
-#endif
-
   gobject_class->dispose = gst_clock_dispose;
   gobject_class->finalize = gst_clock_finalize;
   gobject_class->set_property = gst_clock_set_property;
@@ -674,7 +744,24 @@ gst_clock_class_init (GstClockClass * klass)
           0, G_MAXUINT64, DEFAULT_TIMEOUT,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
-  g_type_class_add_private (klass, sizeof (GstClockPrivate));
+  /**
+   * 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);
 }
 
 static void
@@ -682,8 +769,7 @@ gst_clock_init (GstClock * clock)
 {
   GstClockPrivate *priv;
 
-  clock->priv = priv =
-      G_TYPE_INSTANCE_GET_PRIVATE (clock, GST_TYPE_CLOCK, GstClockPrivate);
+  clock->priv = priv = gst_clock_get_instance_private (clock);
 
   priv->last_time = 0;
 
@@ -693,15 +779,14 @@ 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;
   priv->time_index = 0;
   priv->timeout = DEFAULT_TIMEOUT;
   priv->times = g_new0 (GstClockTime, 4 * priv->window_size);
-
-  /* clear floating flag */
-  gst_object_ref_sink (clock);
+  priv->times_temp = priv->times + 2 * priv->window_size;
 }
 
 static void
@@ -731,9 +816,11 @@ gst_clock_finalize (GObject * object)
   }
   g_free (clock->priv->times);
   clock->priv->times = NULL;
+  clock->priv->times_temp = NULL;
   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);
 }
@@ -796,31 +883,35 @@ gst_clock_get_resolution (GstClock * clock)
   return 1;
 }
 
+/* FIXME 2.0: Remove clock parameter below */
 /**
- * gst_clock_adjust_unlocked:
- * @clock: a #GstClock to use
- * @internal: a clock time
- *
- * Converts the given @internal clock time to the external time, adjusting for the
- * rate and reference time set with gst_clock_set_calibration() and making sure
- * that the returned time is increasing. This function should be called with the
- * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
- *
- * This function is the reverse of gst_clock_unadjust_unlocked().
+ * gst_clock_adjust_with_calibration:
+ * @clock: (allow-none): a #GstClock to use
+ * @internal_target: a clock time
+ * @cinternal: a reference internal time
+ * @cexternal: a reference external time
+ * @cnum: the numerator of the rate of the clock relative to its
+ *        internal time
+ * @cdenom: the denominator of the rate of the clock
+ *
+ * Converts the given @internal_target clock time to the external time,
+ * using the passed calibration parameters. This function performs the
+ * same calculation as gst_clock_adjust_unlocked() when called using the
+ * current calibration parameters, but doesn't ensure a monotonically
+ * increasing result as gst_clock_adjust_unlocked() does.
+ *
+ * Note: The @clock parameter is unused and can be NULL
  *
  * Returns: the converted time of the clock.
+ *
+ * Since: 1.6
  */
 GstClockTime
-gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
+gst_clock_adjust_with_calibration (GstClock * clock,
+    GstClockTime internal_target, GstClockTime cinternal,
+    GstClockTime cexternal, GstClockTime cnum, GstClockTime cdenom)
 {
-  GstClockTime ret, cinternal, cexternal, cnum, cdenom;
-  GstClockPrivate *priv = clock->priv;
-
-  /* get calibration values for readability */
-  cinternal = priv->internal_calibration;
-  cexternal = priv->external_calibration;
-  cnum = priv->rate_numerator;
-  cdenom = priv->rate_denominator;
+  GstClockTime ret;
 
   /* avoid divide by 0 */
   if (G_UNLIKELY (cdenom == 0))
@@ -832,12 +923,12 @@ gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
    * internal < cinternal to get the sign right. this case is not very common,
    * though.
    */
-  if (G_LIKELY (internal >= cinternal)) {
-    ret = internal - cinternal;
+  if (G_LIKELY (internal_target >= cinternal)) {
+    ret = internal_target - cinternal;
     ret = gst_util_uint64_scale (ret, cnum, cdenom);
     ret += cexternal;
   } else {
-    ret = cinternal - internal;
+    ret = cinternal - internal_target;
     ret = gst_util_uint64_scale (ret, cnum, cdenom);
     /* clamp to 0 */
     if (G_LIKELY (cexternal > ret))
@@ -846,30 +937,25 @@ gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
       ret = 0;
   }
 
-  /* make sure the time is increasing */
-  priv->last_time = MAX (ret, priv->last_time);
-
-  return priv->last_time;
+  return ret;
 }
 
 /**
- * gst_clock_unadjust_unlocked:
+ * gst_clock_adjust_unlocked:
  * @clock: a #GstClock to use
- * @external: an external clock time
- *
- * Converts the given @external clock time to the internal time of @clock,
- * using the rate and reference time set with gst_clock_set_calibration().
- * This function should be called with the clock's OBJECT_LOCK held and
- * is mainly used by clock subclasses.
+ * @internal: a clock time
  *
- * This function is the reverse of gst_clock_adjust_unlocked().
+ * Converts the given @internal clock time to the external time, adjusting for the
+ * rate and reference time set with gst_clock_set_calibration() and making sure
+ * that the returned time is increasing. This function should be called with the
+ * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
  *
- * Returns: the internal time of the clock corresponding to @external.
+ * This function is the reverse of gst_clock_unadjust_unlocked().
  *
- * Since: 0.10.13
+ * Returns: the converted time of the clock.
  */
 GstClockTime
-gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
+gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
 {
   GstClockTime ret, cinternal, cexternal, cnum, cdenom;
   GstClockPrivate *priv = clock->priv;
@@ -880,27 +966,97 @@ gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
   cnum = priv->rate_numerator;
   cdenom = priv->rate_denominator;
 
+  ret =
+      gst_clock_adjust_with_calibration (clock, internal, cinternal, cexternal,
+      cnum, cdenom);
+
+  /* make sure the time is increasing */
+  priv->last_time = MAX (ret, priv->last_time);
+
+  return priv->last_time;
+}
+
+/* FIXME 2.0: Remove clock parameter below */
+/**
+ * gst_clock_unadjust_with_calibration:
+ * @clock: (allow-none): a #GstClock to use
+ * @external_target: a clock time
+ * @cinternal: a reference internal time
+ * @cexternal: a reference external time
+ * @cnum: the numerator of the rate of the clock relative to its
+ *        internal time
+ * @cdenom: the denominator of the rate of the clock
+ *
+ * Converts the given @external_target clock time to the internal time,
+ * using the passed calibration parameters. This function performs the
+ * same calculation as gst_clock_unadjust_unlocked() when called using the
+ * current calibration parameters.
+ *
+ * Note: The @clock parameter is unused and can be NULL
+ *
+ * Returns: the converted time of the clock.
+ *
+ * Since: 1.8
+ */
+GstClockTime
+gst_clock_unadjust_with_calibration (GstClock * clock,
+    GstClockTime external_target, GstClockTime cinternal,
+    GstClockTime cexternal, GstClockTime cnum, GstClockTime cdenom)
+{
+  GstClockTime ret;
+
   /* avoid divide by 0 */
   if (G_UNLIKELY (cnum == 0))
     cnum = cdenom = 1;
 
   /* The formula is (external - cexternal) * cdenom / cnum + cinternal */
-  if (G_LIKELY (external >= cexternal)) {
-    ret = external - cexternal;
+  if (G_LIKELY (external_target >= cexternal)) {
+    ret = external_target - cexternal;
     ret = gst_util_uint64_scale (ret, cdenom, cnum);
     ret += cinternal;
   } else {
-    ret = cexternal - external;
+    ret = cexternal - external_target;
     ret = gst_util_uint64_scale (ret, cdenom, cnum);
     if (G_LIKELY (cinternal > ret))
       ret = cinternal - ret;
     else
       ret = 0;
   }
+
   return ret;
 }
 
 /**
+ * gst_clock_unadjust_unlocked:
+ * @clock: a #GstClock to use
+ * @external: an external clock time
+ *
+ * Converts the given @external clock time to the internal time of @clock,
+ * using the rate and reference time set with gst_clock_set_calibration().
+ * This function should be called with the clock's OBJECT_LOCK held and
+ * is mainly used by clock subclasses.
+ *
+ * This function is the reverse of gst_clock_adjust_unlocked().
+ *
+ * Returns: the internal time of the clock corresponding to @external.
+ */
+GstClockTime
+gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
+{
+  GstClockTime cinternal, cexternal, cnum, cdenom;
+  GstClockPrivate *priv = clock->priv;
+
+  /* get calibration values for readability */
+  cinternal = priv->internal_calibration;
+  cexternal = priv->external_calibration;
+  cnum = priv->rate_numerator;
+  cdenom = priv->rate_denominator;
+
+  return gst_clock_unadjust_with_calibration (clock, external, cinternal,
+      cexternal, cnum, cdenom);
+}
+
+/**
  * gst_clock_get_internal_time:
  * @clock: a #GstClock to query
  *
@@ -920,6 +1076,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,
+        "clock is not synchronized yet");
+
   cclass = GST_CLOCK_GET_CLASS (clock);
 
   if (G_UNLIKELY (cclass->get_internal_time == NULL))
@@ -984,7 +1145,7 @@ gst_clock_get_time (GstClock * clock)
  * @internal: a reference internal time
  * @external: a reference external time
  * @rate_num: the numerator of the rate of the clock relative to its
- *            internal time 
+ *            internal time
  * @rate_denom: the denominator of the rate of the clock
  *
  * Adjusts the rate and time of @clock. A rate of 1/1 is the normal speed of
@@ -998,9 +1159,9 @@ gst_clock_get_time (GstClock * clock)
  * Subsequent calls to gst_clock_get_time() will return clock times computed as
  * follows:
  *
- * <programlisting>
+ * |[
  *   time = (internal_time - internal) * rate_num / rate_denom + external
- * </programlisting>
+ * ]|
  *
  * This formula is implemented in gst_clock_adjust_unlocked(). Of course, it
  * tries to do the integer arithmetic as precisely as possible.
@@ -1039,7 +1200,7 @@ gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
 
 /**
  * gst_clock_get_calibration:
- * @clock: a #GstClock 
+ * @clock: a #GstClock
  * @internal: (out) (allow-none): a location to store the internal time
  * @external: (out) (allow-none): a location to store the external time
  * @rate_num: (out) (allow-none): a location to store the rate numerator
@@ -1086,6 +1247,12 @@ gst_clock_slave_callback (GstClock * master, GstClockTime time,
   GstClockTime stime, mtime;
   gdouble r_squared;
 
+  if (!gst_clock_is_synced (clock)) {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+        "Slave clock is not synced yet");
+    return TRUE;
+  }
+
   stime = gst_clock_get_internal_time (clock);
   mtime = gst_clock_get_time (master);
 
@@ -1103,22 +1270,22 @@ gst_clock_slave_callback (GstClock * master, GstClockTime time,
 
 /**
  * gst_clock_set_master:
- * @clock: a #GstClock 
- * @master: (allow-none): a master #GstClock 
+ * @clock: a #GstClock
+ * @master: (allow-none): a master #GstClock
  *
  * Set @master as the master clock for @clock. @clock will be automatically
  * calibrated so that gst_clock_get_time() reports the same time as the
- * master clock.  
- * 
+ * master clock.
+ *
  * A clock provider that slaves its clock to a master can get the current
  * calibration values with gst_clock_get_calibration().
  *
  * @master can be %NULL in which case @clock will not be slaved anymore. It will
- * however keep reporting its time adjusted with the last configured rate 
+ * however keep reporting its time adjusted with the last configured rate
  * and time offsets.
  *
- * Returns: %TRUE if the clock is capable of being slaved to a master clock. 
- * Trying to set a master on a clock without the 
+ * Returns: %TRUE if the clock is capable of being slaved to a master clock.
+ * Trying to set a master on a clock without the
  * #GST_CLOCK_FLAG_CAN_SET_MASTER flag will make this function return %FALSE.
  *
  * MT safe.
@@ -1136,6 +1303,9 @@ gst_clock_set_master (GstClock * clock, GstClock * master)
   /* we always allow setting the master to NULL */
   if (master && !GST_OBJECT_FLAG_IS_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER))
     goto not_supported;
+  if (master && !gst_clock_is_synced (master))
+    goto master_not_synced;
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
       "slaving %p to master clock %p", clock, master);
   GST_OBJECT_UNLOCK (clock);
@@ -1176,17 +1346,26 @@ not_supported:
     GST_OBJECT_UNLOCK (clock);
     return FALSE;
   }
+
+master_not_synced:
+  {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, master,
+        "master clock is not synced yet");
+    GST_OBJECT_UNLOCK (clock);
+    return FALSE;
+  }
 }
 
 /**
  * gst_clock_get_master:
- * @clock: a #GstClock 
+ * @clock: a #GstClock
  *
  * Get the master clock that @clock is slaved to or %NULL when the clock is
  * not slaved to any master clock.
  *
- * Returns: (transfer full): a master #GstClock or %NULL when this clock is
- *     not slaved to a master clock. Unref after usage.
+ * Returns: (transfer full) (nullable): a master #GstClock or %NULL
+ *     when this clock is not slaved to a master clock. Unref after
+ *     usage.
  *
  * MT safe.
  */
@@ -1208,135 +1387,71 @@ gst_clock_get_master (GstClock * clock)
   return result;
 }
 
-/* http://mathworld.wolfram.com/LeastSquaresFitting.html
- * with SLAVE_LOCK
+/**
+ * gst_clock_id_get_clock:
+ * @id: a #GstClockID
+ *
+ * This function returns the underlying clock.
+ *
+ * Returns: (transfer full) (nullable): a #GstClock or %NULL when the
+ *     underlying clock has been freed.  Unref after usage.
+ *
+ * MT safe.
+ *
+ * Since: 1.16
  */
-static gboolean
-do_linear_regression (GstClock * clock, GstClockTime * m_num,
-    GstClockTime * m_denom, GstClockTime * b, GstClockTime * xbase,
-    gdouble * r_squared)
+GstClock *
+gst_clock_id_get_clock (GstClockID id)
 {
-  GstClockTime *newx, *newy;
-  GstClockTime xmin, ymin, xbar, ybar, xbar4, ybar4;
-  GstClockTimeDiff sxx, sxy, syy;
-  GstClockTime *x, *y;
-  gint i, j;
-  guint n;
-  GstClockPrivate *priv;
-
-  xbar = ybar = sxx = syy = sxy = 0;
-
-  priv = clock->priv;
-
-  x = priv->times;
-  y = priv->times + 2;
-  n = priv->filling ? priv->time_index : priv->window_size;
-
-#ifdef DEBUGGING_ENABLED
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "doing regression on:");
-  for (i = j = 0; i < n; i++, j += 4)
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
-        "  %" G_GUINT64_FORMAT "  %" G_GUINT64_FORMAT, x[j], y[j]);
-#endif
-
-  xmin = ymin = G_MAXUINT64;
-  for (i = j = 0; i < n; i++, j += 4) {
-    xmin = MIN (xmin, x[j]);
-    ymin = MIN (ymin, y[j]);
-  }
-
-#ifdef DEBUGGING_ENABLED
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "min x: %" G_GUINT64_FORMAT,
-      xmin);
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "min y: %" G_GUINT64_FORMAT,
-      ymin);
-#endif
-
-  newx = priv->times + 1;
-  newy = priv->times + 3;
-
-  /* strip off unnecessary bits of precision */
-  for (i = j = 0; i < n; i++, j += 4) {
-    newx[j] = x[j] - xmin;
-    newy[j] = y[j] - ymin;
-  }
-
-#ifdef DEBUGGING_ENABLED
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "reduced numbers:");
-  for (i = j = 0; i < n; i++, j += 4)
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
-        "  %" G_GUINT64_FORMAT "  %" G_GUINT64_FORMAT, newx[j], newy[j]);
-#endif
+  GstClockEntry *entry;
 
-  /* have to do this precisely otherwise the results are pretty much useless.
-   * should guarantee that none of these accumulators can overflow */
+  g_return_val_if_fail (id != NULL, NULL);
 
-  /* quantities on the order of 1e10 -> 30 bits; window size a max of 2^10, so
-     this addition could end up around 2^40 or so -- ample headroom */
-  for (i = j = 0; i < n; i++, j += 4) {
-    xbar += newx[j];
-    ybar += newy[j];
-  }
-  xbar /= n;
-  ybar /= n;
-
-#ifdef DEBUGGING_ENABLED
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  xbar  = %" G_GUINT64_FORMAT,
-      xbar);
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  ybar  = %" G_GUINT64_FORMAT,
-      ybar);
-#endif
+  entry = (GstClockEntry *) id;
+  return g_weak_ref_get (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+}
 
-  /* multiplying directly would give quantities on the order of 1e20 -> 60 bits;
-     times the window size that's 70 which is too much. Instead we (1) subtract
-     off the xbar*ybar in the loop instead of after, to avoid accumulation; (2)
-     shift off 4 bits from each multiplicand, giving an expected ceiling of 52
-     bits, which should be enough. Need to check the incoming range and domain
-     to ensure this is an appropriate loss of precision though. */
-  xbar4 = xbar >> 4;
-  ybar4 = ybar >> 4;
-  for (i = j = 0; i < n; i++, j += 4) {
-    GstClockTime newx4, newy4;
-
-    newx4 = newx[j] >> 4;
-    newy4 = newy[j] >> 4;
-
-    sxx += newx4 * newx4 - xbar4 * xbar4;
-    syy += newy4 * newy4 - ybar4 * ybar4;
-    sxy += newx4 * newy4 - xbar4 * ybar4;
-  }
+/**
+ * gst_clock_id_uses_clock:
+ * @id: a #GstClockID to check
+ * @clock: a #GstClock to compare against
+ *
+ * This function returns whether @id uses @clock as the underlying clock.
+ * @clock can be NULL, in which case the return value indicates whether
+ * the underlying clock has been freed.  If this is the case, the @id is
+ * no longer usable and should be freed.
+ *
+ * Returns: whether the clock @id uses the same underlying #GstClock @clock.
+ *
+ * MT safe.
+ *
+ * Since: 1.16
+ */
+gboolean
+gst_clock_id_uses_clock (GstClockID id, GstClock * clock)
+{
+  GstClockEntry *entry;
+  GstClock *entry_clock;
+  gboolean ret = FALSE;
 
-  if (G_UNLIKELY (sxx == 0))
-    goto invalid;
+  g_return_val_if_fail (id != NULL, FALSE);
+  g_return_val_if_fail (clock != NULL, FALSE);
 
-  *m_num = sxy;
-  *m_denom = sxx;
-  *xbase = xmin;
-  *b = (ybar + ymin) - gst_util_uint64_scale (xbar, *m_num, *m_denom);
-  *r_squared = ((double) sxy * (double) sxy) / ((double) sxx * (double) syy);
-
-#ifdef DEBUGGING_ENABLED
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  m      = %g",
-      ((double) *m_num) / *m_denom);
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  b      = %" G_GUINT64_FORMAT,
-      *b);
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  xbase  = %" G_GUINT64_FORMAT,
-      *xbase);
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  r2     = %g", *r_squared);
-#endif
+  entry = (GstClockEntry *) id;
+  entry_clock = g_weak_ref_get (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+  if (entry_clock == clock)
+    ret = TRUE;
 
-  return TRUE;
+  if (G_LIKELY (entry_clock != NULL))
+    gst_object_unref (entry_clock);
 
-invalid:
-  {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "sxx == 0, regression failed");
-    return FALSE;
-  }
+  return ret;
 }
 
+
 /**
  * gst_clock_add_observation:
- * @clock: a #GstClock 
+ * @clock: a #GstClock
  * @slave: a time on the slave
  * @master: a time on the master
  * @r_squared: (out): a pointer to hold the result
@@ -1346,13 +1461,13 @@ invalid:
  * are available, a linear regression algorithm is run on the
  * observations and @clock is recalibrated.
  *
- * If this functions returns %TRUE, @r_squared will contain the 
+ * If this functions returns %TRUE, @r_squared will contain the
  * correlation coefficient of the interpolation. A value of 1.0
  * means a perfect regression was performed. This value can
  * be used to control the sampling frequency of the master and slave
  * clocks.
  *
- * Returns: %TRUE if enough observations were added to run the 
+ * Returns: %TRUE if enough observations were added to run the
  * regression algorithm.
  *
  * MT safe.
@@ -1362,7 +1477,46 @@ gst_clock_add_observation (GstClock * clock, GstClockTime slave,
     GstClockTime master, gdouble * r_squared)
 {
   GstClockTime m_num, m_denom, b, xbase;
+
+  if (!gst_clock_add_observation_unapplied (clock, slave, master, r_squared,
+          &xbase, &b, &m_num, &m_denom))
+    return FALSE;
+
+  /* if we have a valid regression, adjust the clock */
+  gst_clock_set_calibration (clock, xbase, b, m_num, m_denom);
+
+  return TRUE;
+}
+
+/**
+ * gst_clock_add_observation_unapplied:
+ * @clock: a #GstClock
+ * @slave: a time on the slave
+ * @master: a time on the master
+ * @r_squared: (out): a pointer to hold the result
+ * @internal: (out) (allow-none): a location to store the internal time
+ * @external: (out) (allow-none): a location to store the external time
+ * @rate_num: (out) (allow-none): a location to store the rate numerator
+ * @rate_denom: (out) (allow-none): a location to store the rate denominator
+ *
+ * Add a clock observation to the internal slaving algorithm the same as
+ * gst_clock_add_observation(), and return the result of the master clock
+ * estimation, without updating the internal calibration.
+ *
+ * The caller can then take the results and call gst_clock_set_calibration()
+ * with the values, or some modified version of them.
+ *
+ * Since: 1.6
+ */
+gboolean
+gst_clock_add_observation_unapplied (GstClock * clock, GstClockTime slave,
+    GstClockTime master, gdouble * r_squared,
+    GstClockTime * internal, GstClockTime * external,
+    GstClockTime * rate_num, GstClockTime * rate_denom)
+{
+  GstClockTime m_num, m_denom, b, xbase;
   GstClockPrivate *priv;
+  guint n;
 
   g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
   g_return_val_if_fail (r_squared != NULL, FALSE);
@@ -1375,8 +1529,8 @@ gst_clock_add_observation (GstClock * clock, GstClockTime slave,
       "adding observation slave %" GST_TIME_FORMAT ", master %" GST_TIME_FORMAT,
       GST_TIME_ARGS (slave), GST_TIME_ARGS (master));
 
-  priv->times[(4 * priv->time_index)] = slave;
-  priv->times[(4 * priv->time_index) + 2] = master;
+  priv->times[(2 * priv->time_index)] = slave;
+  priv->times[(2 * priv->time_index) + 1] = master;
 
   priv->time_index++;
   if (G_UNLIKELY (priv->time_index == priv->window_size)) {
@@ -1387,7 +1541,9 @@ gst_clock_add_observation (GstClock * clock, GstClockTime slave,
   if (G_UNLIKELY (priv->filling && priv->time_index < priv->window_threshold))
     goto filling;
 
-  if (!do_linear_regression (clock, &m_num, &m_denom, &b, &xbase, r_squared))
+  n = priv->filling ? priv->time_index : priv->window_size;
+  if (!gst_calculate_linear_regression (priv->times, priv->times_temp, n,
+          &m_num, &m_denom, &b, &xbase, r_squared))
     goto invalid;
 
   GST_CLOCK_SLAVE_UNLOCK (clock);
@@ -1396,8 +1552,14 @@ gst_clock_add_observation (GstClock * clock, GstClockTime slave,
       "adjusting clock to m=%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ", b=%"
       G_GUINT64_FORMAT " (rsquared=%g)", m_num, m_denom, b, *r_squared);
 
-  /* if we have a valid regression, adjust the clock */
-  gst_clock_set_calibration (clock, xbase, b, m_num, m_denom);
+  if (internal)
+    *internal = xbase;
+  if (external)
+    *external = b;
+  if (rate_num)
+    *rate_num = m_num;
+  if (rate_denom)
+    *rate_denom = m_denom;
 
   return TRUE;
 
@@ -1410,7 +1572,7 @@ invalid:
   {
     /* no valid regression has been done, ignore the result then */
     GST_CLOCK_SLAVE_UNLOCK (clock);
-    return TRUE;
+    return FALSE;
   }
 }
 
@@ -1470,6 +1632,7 @@ gst_clock_set_property (GObject * object, guint prop_id,
       priv->window_size = g_value_get_int (value);
       priv->window_threshold = MIN (priv->window_threshold, priv->window_size);
       priv->times = g_renew (GstClockTime, priv->times, 4 * priv->window_size);
+      priv->times_temp = priv->times + 2 * priv->window_size;
       /* restart calibration */
       priv->filling = TRUE;
       priv->time_index = 0;
@@ -1518,3 +1681,108 @@ 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_for_sync().
+ *
+ * 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);
+  }
+}