--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gstreamer_core/gst/gstclock.c Thu Dec 17 08:53:32 2009 +0200
@@ -0,0 +1,1482 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstclock.c: Clock subsystem for maintaining time sync
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:gstclock
+ * @short_description: Abstract class for global clocks
+ * @see_also: #GstSystemClock, #GstPipeline
+ *
+ * GStreamer uses a global clock to synchronize the plugins in a pipeline.
+ * Different clock implementations are possible by implementing this abstract
+ * base class.
+ *
+ * The #GstClock returns a monotonically increasing time with the method
+ * gst_clock_get_time(). Its accuracy and base time depend on the specific
+ * clock implementation but time is always expressed in nanoseconds. Since the
+ * baseline of the clock is undefined, the clock time returned is not
+ * meaningful in itself, what matters are the deltas between two clock times.
+ * The time returned by a clock is called the absolute time.
+ *
+ * The pipeline uses the clock to calculate the stream time. Usually all
+ * renderers synchronize to the global clock using the buffer timestamps, the
+ * newsegment events and the element's base time, see #GstPipeline.
+ *
+ * A clock implementation can support periodic and single shot clock
+ * notifications both synchronous and asynchronous.
+ *
+ * One first needs to create a #GstClockID for the periodic or single shot
+ * notification using gst_clock_new_single_shot_id() or
+ * gst_clock_new_periodic_id().
+ *
+ * To perform a blocking wait for the specific time of the #GstClockID use the
+ * gst_clock_id_wait(). To receive a callback when the specific time is reached
+ * in the clock use gst_clock_id_wait_async(). Both these calls can be
+ * interrupted with the gst_clock_id_unschedule() call. If the blocking wait is
+ * unscheduled a return value of GST_CLOCK_UNSCHEDULED is returned.
+ *
+ * Periodic callbacks scheduled async will be repeadedly called automatically
+ * until it is unscheduled. To schedule a sync periodic callback,
+ * gst_clock_id_wait() should be called repeadedly.
+ *
+ * The async callbacks can happen from any thread, either provided by the core
+ * or from a streaming thread. The application should be prepared for this.
+ *
+ * A #GstClockID that has been unscheduled cannot be used again for any wait
+ * operation, a new #GstClockID should be created and the old unscheduled one
+ * should be destroyed wirth gst_clock_id_unref().
+ *
+ * It is possible to perform a blocking wait on the same #GstClockID from
+ * multiple threads. However, registering the same #GstClockID for multiple
+ * async notifications is not possible, the callback will only be called for
+ * the thread registering the entry last.
+ *
+ * None of the wait operations unref the #GstClockID, the owner is responsible
+ * 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
+ * might become invalid without any notification.
+ *
+ * These clock operations do not operate on the stream time, so the callbacks
+ * will also occur when not in PLAYING state as if the clock just keeps on
+ * running. Some clocks however do not progress when the element that provided
+ * the clock is not PLAYING.
+ *
+ * When a clock has the GST_CLOCK_FLAG_CAN_SET_MASTER flag set, it can be
+ * slaved to another #GstClock with the gst_clock_set_master(). The clock will
+ * then automatically be synchronized to this master clock by repeadedly
+ * sampling the master clock and the slave clock and recalibrating the slave
+ * clock with gst_clock_set_calibration(). This feature is mostly useful for
+ * 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.
+ *
+ * The master/slave synchronisation can be tuned with the "timeout", "window-size"
+ * and "window-threshold" properties. The "timeout" property defines the interval
+ * to sample the master clock and run the calibration functions.
+ * "window-size" defines the number of samples to use when calibrating and
+ * "window-threshold" defines the minimum number of samples before the
+ * calibration is performed.
+ *
+ * Last reviewed on 2006-08-11 (0.10.10)
+ */
+
+
+#include "gst_private.h"
+#include <time.h>
+
+#include "gstclock.h"
+#include "gstinfo.h"
+#include "gstutils.h"
+
+#ifndef GST_DISABLE_TRACE
+/* #define GST_WITH_ALLOC_TRACE */
+#include "gsttrace.h"
+static GstAllocTrace *_gst_clock_entry_trace;
+#endif
+
+#ifdef __SYMBIAN32__
+#include <glib_global.h>
+#endif
+
+#if GLIB_CHECK_VERSION (2, 10, 0)
+#define ALLOC_ENTRY() g_slice_new (GstClockEntry)
+#define FREE_ENTRY(entry) g_slice_free (GstClockEntry, entry)
+#else
+#define ALLOC_ENTRY() g_new (GstClockEntry, 1)
+#define FREE_ENTRY(entry) g_free (entry)
+#endif
+
+/* #define DEBUGGING_ENABLED */
+
+#define DEFAULT_STATS FALSE
+#define DEFAULT_WINDOW_SIZE 32
+#define DEFAULT_WINDOW_THRESHOLD 4
+#define DEFAULT_TIMEOUT GST_SECOND / 10
+
+enum
+{
+ PROP_0,
+ PROP_STATS,
+ PROP_WINDOW_SIZE,
+ PROP_WINDOW_THRESHOLD,
+ PROP_TIMEOUT
+};
+
+static void gst_clock_class_init (GstClockClass * klass);
+static void gst_clock_init (GstClock * clock);
+static void gst_clock_dispose (GObject * object);
+static void gst_clock_finalize (GObject * object);
+
+static void gst_clock_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_clock_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_clock_update_stats (GstClock * clock);
+
+
+static GstObjectClass *parent_class = NULL;
+
+/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
+
+static GstClockID
+gst_clock_entry_new (GstClock * clock, GstClockTime time,
+ GstClockTime interval, GstClockEntryType type)
+{
+ GstClockEntry *entry;
+
+ entry = ALLOC_ENTRY ();
+#ifndef GST_DISABLE_TRACE
+ gst_alloc_trace_new (_gst_clock_entry_trace, entry);
+#endif
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
+
+ gst_atomic_int_set (&entry->refcount, 1);
+ entry->clock = clock;
+ entry->type = type;
+ entry->time = time;
+ entry->interval = interval;
+ entry->status = GST_CLOCK_BUSY;
+ entry->func = NULL;
+ entry->user_data = NULL;
+
+ return (GstClockID) entry;
+}
+
+/**
+ * gst_clock_id_ref:
+ * @id: The #GstClockID to ref
+ *
+ * Increase the refcount of given @id.
+ *
+ * Returns: The same #GstClockID with increased refcount.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockID
+gst_clock_id_ref (GstClockID id)
+{
+ g_return_val_if_fail (id != NULL, NULL);
+
+ g_atomic_int_inc (&((GstClockEntry *) id)->refcount);
+
+ return id;
+}
+
+static void
+_gst_clock_id_free (GstClockID id)
+{
+ g_return_if_fail (id != NULL);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
+
+#ifndef GST_DISABLE_TRACE
+ gst_alloc_trace_free (_gst_clock_entry_trace, id);
+#endif
+ FREE_ENTRY (id);
+}
+
+/**
+ * gst_clock_id_unref:
+ * @id: The #GstClockID to unref
+ *
+ * Unref given @id. When the refcount reaches 0 the
+ * #GstClockID will be freed.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_clock_id_unref (GstClockID id)
+{
+ gint zero;
+
+ g_return_if_fail (id != NULL);
+
+ zero = g_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
+ /* if we ended up with the refcount at zero, free the id */
+ if (zero) {
+ _gst_clock_id_free (id);
+ }
+}
+
+/**
+ * gst_clock_new_single_shot_id
+ * @clock: The #GstClockID to get a single shot notification from
+ * @time: the requested time
+ *
+ * Get a #GstClockID from @clock to trigger a single shot
+ * notification at the requested time. The single shot id should be
+ * unreffed after usage.
+ *
+ * Returns: A #GstClockID that can be used to request the time notification.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockID
+gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
+{
+ g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
+
+ return gst_clock_entry_new (clock,
+ time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
+}
+
+/**
+ * gst_clock_new_periodic_id
+ * @clock: The #GstClockID to get a periodic notification id from
+ * @start_time: the requested start time
+ * @interval: the requested interval
+ *
+ * Get an ID from @clock to trigger a periodic notification.
+ * The periodeic notifications will be start at time start_time and
+ * will then be fired with the given interval. @id should be unreffed
+ * after usage.
+ *
+ * Returns: A #GstClockID that can be used to request the time notification.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockID
+gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
+ GstClockTime interval)
+{
+ g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
+ g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
+ g_return_val_if_fail (interval != 0, NULL);
+ g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), NULL);
+
+ return gst_clock_entry_new (clock,
+ start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
+}
+
+/**
+ * gst_clock_id_compare_func
+ * @id1: A #GstClockID
+ * @id2: A #GstClockID to compare with
+ *
+ * Compares the two #GstClockID instances. This function can be used
+ * as a GCompareFunc when sorting ids.
+ *
+ * Returns: negative value if a < b; zero if a = b; positive value if a > b
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gint
+gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
+{
+ GstClockEntry *entry1, *entry2;
+
+ entry1 = (GstClockEntry *) id1;
+ entry2 = (GstClockEntry *) id2;
+
+ if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) {
+ return 1;
+ }
+ if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * gst_clock_id_get_time
+ * @id: The #GstClockID to query
+ *
+ * Get the time of the clock ID
+ *
+ * Returns: the time of the given clock id.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockTime
+gst_clock_id_get_time (GstClockID id)
+{
+ g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
+
+ return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
+}
+
+/**
+ * gst_clock_id_wait
+ * @id: The #GstClockID to wait on
+ * @jitter: A pointer that will contain the jitter, can be NULL.
+ *
+ * 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().
+ *
+ * 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.
+ * 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
+ * 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
+ * unscheduled with gst_clock_id_unschedule().
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockReturn
+gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
+{
+ GstClockEntry *entry;
+ GstClock *clock;
+ GstClockReturn res;
+ GstClockTime requested;
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
+
+ entry = (GstClockEntry *) id;
+ requested = GST_CLOCK_ENTRY_TIME (entry);
+
+ clock = GST_CLOCK_ENTRY_CLOCK (entry);
+
+ /* can't sync on invalid times */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
+ goto invalid_time;
+
+ /* a previously unscheduled entry cannot be scheduled again */
+ if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
+ goto unscheduled;
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "waiting on clock entry %p", id);
+
+ /* if we have a wait_jitter function, use that */
+ if (G_LIKELY (cclass->wait_jitter)) {
+ res = cclass->wait_jitter (clock, entry, jitter);
+ } else {
+ /* check if we have a simple _wait function otherwise. The function without
+ * the jitter arg is less optimal as we need to do an additional _get_time()
+ * which is not atomic with the _wait() and a typical _wait() function does
+ * yet another _get_time() anyway. */
+ if (G_UNLIKELY (cclass->wait == NULL))
+ goto not_supported;
+
+ if (jitter) {
+ GstClockTime now = gst_clock_get_time (clock);
+
+ /* jitter is the diff against the clock when this entry is scheduled. Negative
+ * values mean that the entry was in time, a positive value means that the
+ * entry was too late. */
+ *jitter = GST_CLOCK_DIFF (requested, now);
+ }
+ res = cclass->wait (clock, entry);
+ }
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "done waiting entry %p, res: %d", id, res);
+
+ if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
+ entry->time = requested + entry->interval;
+
+ if (clock->stats)
+ gst_clock_update_stats (clock);
+
+ return res;
+
+ /* ERRORS */
+invalid_time:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "invalid time requested, returning _BADTIME");
+ return GST_CLOCK_BADTIME;
+ }
+unscheduled:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "entry was unscheduled return _UNSCHEDULED");
+ return GST_CLOCK_UNSCHEDULED;
+ }
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
+ return GST_CLOCK_UNSUPPORTED;
+ }
+}
+
+/**
+ * gst_clock_id_wait_async:
+ * @id: a #GstClockID to wait on
+ * @func: The callback function
+ * @user_data: User data passed in the calback
+ *
+ * Register a callback on the given #GstClockID @id with the given
+ * function and user_data. When passing a #GstClockID with an invalid
+ * time to this function, the callback will be called immediatly
+ * with a time set to GST_CLOCK_TIME_NONE. The callback will
+ * be called when the time of @id has been reached.
+ *
+ * Returns: the result of the non blocking wait.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockReturn
+gst_clock_id_wait_async (GstClockID id,
+ GstClockCallback func, gpointer user_data)
+{
+ GstClockEntry *entry;
+ GstClock *clock;
+ GstClockReturn res;
+ GstClockClass *cclass;
+ GstClockTime requested;
+
+ g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
+ g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
+
+ entry = (GstClockEntry *) id;
+ requested = GST_CLOCK_ENTRY_TIME (entry);
+ clock = GST_CLOCK_ENTRY_CLOCK (entry);
+
+ /* can't sync on invalid times */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
+ goto invalid_time;
+
+ /* a previously unscheduled entry cannot be scheduled again */
+ if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
+ goto unscheduled;
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (G_UNLIKELY (cclass->wait_async == NULL))
+ goto not_supported;
+
+ entry->func = func;
+ entry->user_data = user_data;
+
+ res = cclass->wait_async (clock, entry);
+
+ return res;
+
+ /* ERRORS */
+invalid_time:
+ {
+ (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "invalid time requested, returning _BADTIME");
+ return GST_CLOCK_BADTIME;
+ }
+unscheduled:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "entry was unscheduled return _UNSCHEDULED");
+ return GST_CLOCK_UNSCHEDULED;
+ }
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
+ return GST_CLOCK_UNSUPPORTED;
+ }
+}
+
+/**
+ * gst_clock_id_unschedule:
+ * @id: The id to unschedule
+ *
+ * Cancel an outstanding request with @id. This can either
+ * be an outstanding async notification or a pending sync notification.
+ * After this call, @id cannot be used anymore to receive sync or
+ * async notifications, you need to create a new #GstClockID.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_clock_id_unschedule (GstClockID id)
+{
+ GstClockEntry *entry;
+ GstClock *clock;
+ GstClockClass *cclass;
+
+ g_return_if_fail (id != NULL);
+
+ entry = (GstClockEntry *) id;
+ clock = entry->clock;
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (G_LIKELY (cclass->unschedule))
+ cclass->unschedule (clock, entry);
+}
+
+
+/**
+ * GstClock abstract base class implementation
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GType
+gst_clock_get_type (void)
+{
+ static GType clock_type = 0;
+
+ if (G_UNLIKELY (clock_type == 0)) {
+ static const GTypeInfo clock_info = {
+ sizeof (GstClockClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_clock_class_init,
+ NULL,
+ NULL,
+ sizeof (GstClock),
+ 0,
+ (GInstanceInitFunc) gst_clock_init,
+ NULL
+ };
+
+ clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
+ &clock_info, G_TYPE_FLAG_ABSTRACT);
+ }
+ return clock_type;
+}
+
+static void
+gst_clock_class_init (GstClockClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstObjectClass *gstobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstobject_class = GST_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+
+#ifndef GST_DISABLE_TRACE
+ _gst_clock_entry_trace =
+ gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
+#endif
+
+ gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_clock_dispose);
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_clock_finalize);
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);
+
+ g_object_class_install_property (gobject_class, PROP_STATS,
+ g_param_spec_boolean ("stats", "Stats",
+ "Enable clock stats (unimplemented)", DEFAULT_STATS,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_WINDOW_SIZE,
+ g_param_spec_int ("window-size", "Window size",
+ "The size of the window used to calculate rate and offset", 2, 1024,
+ DEFAULT_WINDOW_SIZE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_WINDOW_THRESHOLD,
+ g_param_spec_int ("window-threshold", "Window threshold",
+ "The threshold to start calculating rate and offset", 2, 1024,
+ DEFAULT_WINDOW_THRESHOLD, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_TIMEOUT,
+ g_param_spec_uint64 ("timeout", "Timeout",
+ "The amount of time, in nanoseconds, to sample master and slave clocks",
+ 0, G_MAXUINT64, DEFAULT_TIMEOUT, G_PARAM_READWRITE));
+}
+
+static void
+gst_clock_init (GstClock * clock)
+{
+ clock->last_time = 0;
+ clock->entries = NULL;
+ clock->entries_changed = g_cond_new ();
+ clock->stats = FALSE;
+
+ clock->internal_calibration = 0;
+ clock->external_calibration = 0;
+ clock->rate_numerator = 1;
+ clock->rate_denominator = 1;
+
+ clock->slave_lock = g_mutex_new ();
+ clock->window_size = DEFAULT_WINDOW_SIZE;
+ clock->window_threshold = DEFAULT_WINDOW_THRESHOLD;
+ clock->filling = TRUE;
+ clock->time_index = 0;
+ clock->timeout = DEFAULT_TIMEOUT;
+ clock->times = g_new0 (GstClockTime, 4 * clock->window_size);
+}
+
+static void
+gst_clock_dispose (GObject * object)
+{
+ GstClock *clock = GST_CLOCK (object);
+ GstClock **master_p;
+
+ GST_OBJECT_LOCK (clock);
+ master_p = &clock->master;
+ gst_object_replace ((GstObject **) master_p, NULL);
+ GST_OBJECT_UNLOCK (clock);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_clock_finalize (GObject * object)
+{
+ GstClock *clock = GST_CLOCK (object);
+
+ GST_CLOCK_SLAVE_LOCK (clock);
+ if (clock->clockid) {
+ gst_clock_id_unschedule (clock->clockid);
+ gst_clock_id_unref (clock->clockid);
+ clock->clockid = NULL;
+ }
+ g_free (clock->times);
+ clock->times = NULL;
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+
+ g_cond_free (clock->entries_changed);
+ g_mutex_free (clock->slave_lock);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * gst_clock_set_resolution
+ * @clock: a #GstClock
+ * @resolution: The resolution to set
+ *
+ * Set the accuracy of the clock. Some clocks have the possibility to operate
+ * with different accuracy at the expense of more resource usage. There is
+ * normally no need to change the default resolution of a clock. The resolution
+ * of a clock can only be changed if the clock has the
+ * #GST_CLOCK_FLAG_CAN_SET_RESOLUTION flag set.
+ *
+ * Returns: the new resolution of the clock.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockTime
+gst_clock_set_resolution (GstClock * clock, GstClockTime resolution)
+{
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
+ g_return_val_if_fail (resolution != 0, 0);
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (cclass->change_resolution)
+ clock->resolution =
+ cclass->change_resolution (clock, clock->resolution, resolution);
+
+ return clock->resolution;
+}
+
+/**
+ * gst_clock_get_resolution
+ * @clock: a #GstClock
+ *
+ * Get the accuracy of the clock. The accuracy of the clock is the granularity
+ * of the values returned by gst_clock_get_time().
+ *
+ * Returns: the resolution of the clock in units of #GstClockTime.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockTime
+gst_clock_get_resolution (GstClock * clock)
+{
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (cclass->get_resolution)
+ return cclass->get_resolution (clock);
+
+ return 1;
+}
+
+/**
+ * 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 te reverse of gst_clock_unadjust_unlocked().
+ *
+ * Returns: the converted time of the clock.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockTime
+gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
+{
+ GstClockTime ret, cinternal, cexternal, cnum, cdenom;
+
+ /* get calibration values for readability */
+ cinternal = clock->internal_calibration;
+ cexternal = clock->external_calibration;
+ cnum = clock->rate_numerator;
+ cdenom = clock->rate_denominator;
+
+ /* avoid divide by 0 */
+ if (cdenom == 0)
+ cnum = cdenom = 1;
+
+ /* The formula is (internal - cinternal) * cnum / cdenom + cexternal
+ *
+ * Since we do math on unsigned 64-bit ints we have to special case for
+ * interal < cinternal to get the sign right. this case is not very common,
+ * though.
+ */
+ if (G_LIKELY (internal >= cinternal)) {
+ ret = gst_util_uint64_scale (internal - cinternal, cnum, cdenom);
+ ret += cexternal;
+ } else {
+ ret = gst_util_uint64_scale (cinternal - internal, cnum, cdenom);
+ /* clamp to 0 */
+ if (cexternal > ret)
+ ret = cexternal - ret;
+ else
+ ret = 0;
+ }
+
+ /* make sure the time is increasing */
+ clock->last_time = MAX (ret, clock->last_time);
+
+ return clock->last_time;
+}
+
+/**
+ * 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 te reverse of gst_clock_adjust_unlocked().
+ *
+ * Returns: the internal time of the clock corresponding to @external.
+ *
+ * Since: 0.10.13
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockTime
+gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
+{
+ GstClockTime ret, cinternal, cexternal, cnum, cdenom;
+
+ /* get calibration values for readability */
+ cinternal = clock->internal_calibration;
+ cexternal = clock->external_calibration;
+ cnum = clock->rate_numerator;
+ cdenom = clock->rate_denominator;
+
+ /* avoid divide by 0 */
+ if (cnum == 0)
+ cnum = cdenom = 1;
+
+ /* The formula is (external - cexternal) * cdenom / cnum + cinternal */
+ if (external >= cexternal) {
+ ret = gst_util_uint64_scale (external - cexternal, cdenom, cnum);
+ ret += cinternal;
+ } else {
+ ret = gst_util_uint64_scale (cexternal - external, cdenom, cnum);
+ if (cinternal > ret)
+ ret = cinternal - ret;
+ else
+ ret = 0;
+ }
+ return ret;
+}
+
+/**
+ * gst_clock_get_internal_time
+ * @clock: a #GstClock to query
+ *
+ * Gets the current internal time of the given clock. The time is returned
+ * unadjusted for the offset and the rate.
+ *
+ * Returns: the internal time of the clock. Or GST_CLOCK_TIME_NONE when
+ * giving wrong input.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockTime
+gst_clock_get_internal_time (GstClock * clock)
+{
+ GstClockTime ret;
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (G_UNLIKELY (cclass->get_internal_time == NULL))
+ goto not_supported;
+
+ ret = cclass->get_internal_time (clock);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "internal time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (ret));
+
+ return ret;
+
+ /* ERRORS */
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "internal time not supported, return 0");
+ return G_GINT64_CONSTANT (0);
+ }
+}
+
+/**
+ * gst_clock_get_time
+ * @clock: a #GstClock to query
+ *
+ * Gets the current time of the given clock. The time is always
+ * monotonically increasing and adjusted according to the current
+ * offset and rate.
+ *
+ * Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
+ * giving wrong input.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClockTime
+gst_clock_get_time (GstClock * clock)
+{
+ GstClockTime ret;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
+
+ ret = gst_clock_get_internal_time (clock);
+
+ GST_OBJECT_LOCK (clock);
+ /* this will scale for rate and offset */
+ ret = gst_clock_adjust_unlocked (clock, ret);
+ GST_OBJECT_UNLOCK (clock);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "adjusted time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (ret));
+
+ return ret;
+}
+
+/**
+ * gst_clock_set_calibration
+ * @clock: a #GstClock to calibrate
+ * @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
+ * @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
+ * the clock. Values bigger than 1/1 make the clock go faster.
+ *
+ * @internal and @external are calibration parameters that arrange that
+ * gst_clock_get_time() should have been @external at internal time @internal.
+ * This internal time should not be in the future; that is, it should be less
+ * than the value of gst_clock_get_internal_time() when this function is called.
+ *
+ * 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.
+ *
+ * Note that gst_clock_get_time() always returns increasing values so when you
+ * move the clock backwards, gst_clock_get_time() will report the previous value
+ * until the clock catches up.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
+ external, GstClockTime rate_num, GstClockTime rate_denom)
+{
+ g_return_if_fail (GST_IS_CLOCK (clock));
+ g_return_if_fail (rate_num >= 0);
+ g_return_if_fail (rate_denom > 0);
+ g_return_if_fail (internal <= gst_clock_get_internal_time (clock));
+
+ GST_OBJECT_LOCK (clock);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT " %"
+ G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %f", GST_TIME_ARGS (internal),
+ GST_TIME_ARGS (external), rate_num, rate_denom,
+ gst_guint64_to_gdouble (rate_num / rate_denom));
+
+ clock->internal_calibration = internal;
+ clock->external_calibration = external;
+ clock->rate_numerator = rate_num;
+ clock->rate_denominator = rate_denom;
+ GST_OBJECT_UNLOCK (clock);
+}
+
+/**
+ * gst_clock_get_calibration
+ * @clock: a #GstClock
+ * @internal: a location to store the internal time
+ * @external: a location to store the external time
+ * @rate_num: a location to store the rate numerator
+ * @rate_denom: a location to store the rate denominator
+ *
+ * Gets the internal rate and reference time of @clock. See
+ * gst_clock_set_calibration() for more information.
+ *
+ * @internal, @external, @rate_num, and @rate_denom can be left NULL if the
+ * caller is not interested in the values.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
+ GstClockTime * external, GstClockTime * rate_num, GstClockTime * rate_denom)
+{
+ g_return_if_fail (GST_IS_CLOCK (clock));
+
+ GST_OBJECT_LOCK (clock);
+ if (rate_num)
+ *rate_num = clock->rate_numerator;
+ if (rate_denom)
+ *rate_denom = clock->rate_denominator;
+ if (external)
+ *external = clock->external_calibration;
+ if (internal)
+ *internal = clock->internal_calibration;
+ GST_OBJECT_UNLOCK (clock);
+}
+
+/* will be called repeadedly to sample the master and slave clock
+ * to recalibrate the clock */
+static gboolean
+gst_clock_slave_callback (GstClock * master, GstClockTime time,
+ GstClockID id, GstClock * clock)
+{
+ GstClockTime stime, mtime;
+ gdouble r_squared;
+
+ stime = gst_clock_get_internal_time (clock);
+ mtime = gst_clock_get_time (master);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "master %" GST_TIME_FORMAT ", slave %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (mtime), GST_TIME_ARGS (stime));
+
+ gst_clock_add_observation (clock, stime, mtime, &r_squared);
+
+ /* FIXME, we can use the r_squared value to adjust the timeout
+ * value of the clockid */
+
+ return TRUE;
+}
+
+/**
+ * gst_clock_set_master
+ * @clock: a #GstClock
+ * @master: 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.
+ *
+ * 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
+ * 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
+ * GST_CLOCK_FLAG_CAN_SET_MASTER flag will make this function return FALSE.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_clock_set_master (GstClock * clock, GstClock * master)
+{
+ GstClock **master_p;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
+ g_return_val_if_fail (master != clock, FALSE);
+
+ GST_OBJECT_LOCK (clock);
+ /* 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;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "slaving %p to master clock %p", clock, master);
+ master_p = &clock->master;
+ gst_object_replace ((GstObject **) master_p, (GstObject *) master);
+ GST_OBJECT_UNLOCK (clock);
+
+ GST_CLOCK_SLAVE_LOCK (clock);
+ if (clock->clockid) {
+ gst_clock_id_unschedule (clock->clockid);
+ gst_clock_id_unref (clock->clockid);
+ clock->clockid = NULL;
+ }
+ if (master) {
+ clock->filling = TRUE;
+ clock->time_index = 0;
+ /* use the master periodic id to schedule sampling and
+ * clock calibration. */
+ clock->clockid = gst_clock_new_periodic_id (master,
+ gst_clock_get_time (master), clock->timeout);
+ gst_clock_id_wait_async (clock->clockid,
+ (GstClockCallback) gst_clock_slave_callback, clock);
+ }
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+
+ return TRUE;
+
+ /* ERRORS */
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "cannot be slaved to a master clock");
+ GST_OBJECT_UNLOCK (clock);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_clock_get_master
+ * @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: a master #GstClock or NULL when this clock is not slaved to a master
+ * clock. Unref after usage.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstClock *
+gst_clock_get_master (GstClock * clock)
+{
+ GstClock *result = NULL;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
+
+ GST_OBJECT_LOCK (clock);
+ if (clock->master)
+ result = gst_object_ref (clock->master);
+ GST_OBJECT_UNLOCK (clock);
+
+ return result;
+}
+
+/* http://mathworld.wolfram.com/LeastSquaresFitting.html
+ * with SLAVE_LOCK
+ */
+static gboolean
+do_linear_regression (GstClock * clock, GstClockTime * m_num,
+ GstClockTime * m_denom, GstClockTime * b, GstClockTime * xbase,
+ gdouble * r_squared)
+{
+ GstClockTime *newx, *newy;
+ GstClockTime xmin, ymin, xbar, ybar, xbar4, ybar4;
+ GstClockTimeDiff sxx, sxy, syy;
+ GstClockTime *x, *y;
+ gint i, j;
+ guint n;
+
+ xbar = ybar = sxx = syy = sxy = 0;
+
+ x = clock->times;
+ y = clock->times + 2;
+ n = clock->filling ? clock->time_index : clock->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 = clock->times + 1;
+ newy = clock->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
+
+ /* have to do this precisely otherwise the results are pretty much useless.
+ * should guarantee that none of these accumulators can overflow */
+
+ /* 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
+
+ /* 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;
+ }
+
+ if (G_UNLIKELY (sxx == 0))
+ goto invalid;
+
+ *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
+
+ return TRUE;
+
+invalid:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "sxx == 0, regression failed");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_clock_add_observation
+ * @clock: a #GstClock
+ * @slave: a time on the slave
+ * @master: a time on the master
+ * @r_squared: a pointer to hold the result
+ *
+ * The time @master of the master clock and the time @slave of the slave
+ * clock are added to the list of observations. If enough observations
+ * 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
+ * correlation coefficient of the interpollation. 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
+ * regression algorithm.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_clock_add_observation (GstClock * clock, GstClockTime slave,
+ GstClockTime master, gdouble * r_squared)
+{
+ GstClockTime m_num, m_denom, b, xbase;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
+ g_return_val_if_fail (r_squared != NULL, FALSE);
+
+ GST_CLOCK_SLAVE_LOCK (clock);
+
+ clock->times[(4 * clock->time_index)] = slave;
+ clock->times[(4 * clock->time_index) + 2] = master;
+
+ clock->time_index++;
+ if (G_UNLIKELY (clock->time_index == clock->window_size)) {
+ clock->filling = FALSE;
+ clock->time_index = 0;
+ }
+
+ if (G_UNLIKELY (clock->filling
+ && clock->time_index < clock->window_threshold))
+ goto filling;
+
+ if (!do_linear_regression (clock, &m_num, &m_denom, &b, &xbase, r_squared))
+ goto invalid;
+
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+
+ GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
+ "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);
+
+ return TRUE;
+
+filling:
+ {
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ return FALSE;
+ }
+invalid:
+ {
+ /* no valid regression has been done, ignore the result then */
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ return TRUE;
+ }
+}
+
+static void
+gst_clock_update_stats (GstClock * clock)
+{
+}
+
+static void
+gst_clock_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstClock *clock;
+
+ clock = GST_CLOCK (object);
+
+ switch (prop_id) {
+ case PROP_STATS:
+ GST_OBJECT_LOCK (clock);
+ clock->stats = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (clock);
+ g_object_notify (object, "stats");
+ break;
+ case PROP_WINDOW_SIZE:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ clock->window_size = g_value_get_int (value);
+ clock->window_threshold =
+ MIN (clock->window_threshold, clock->window_size);
+ clock->times =
+ g_renew (GstClockTime, clock->times, 4 * clock->window_size);
+ /* restart calibration */
+ clock->filling = TRUE;
+ clock->time_index = 0;
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_WINDOW_THRESHOLD:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ clock->window_threshold =
+ MIN (g_value_get_int (value), clock->window_size);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_TIMEOUT:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ clock->timeout = g_value_get_uint64 (value);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_clock_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstClock *clock;
+
+ clock = GST_CLOCK (object);
+
+ switch (prop_id) {
+ case PROP_STATS:
+ GST_OBJECT_LOCK (clock);
+ g_value_set_boolean (value, clock->stats);
+ GST_OBJECT_UNLOCK (clock);
+ break;
+ case PROP_WINDOW_SIZE:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ g_value_set_int (value, clock->window_size);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_WINDOW_THRESHOLD:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ g_value_set_int (value, clock->window_threshold);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_TIMEOUT:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ g_value_set_uint64 (value, clock->timeout);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}