--- a/gstreamer_core/gst/gstclock.c Wed Mar 31 22:03:18 2010 +0300
+++ b/gstreamer_core/gst/gstclock.c Tue Aug 31 15:30:33 2010 +0300
@@ -28,7 +28,7 @@
*
* GStreamer uses a global clock to synchronize the plugins in a pipeline.
* Different clock implementations are possible by implementing this abstract
- * base class.
+ * base class or, more conveniently, by subclassing #GstSystemClock.
*
* The #GstClock returns a monotonically increasing time with the method
* gst_clock_get_time(). Its accuracy and base time depend on the specific
@@ -37,7 +37,7 @@
* 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
+ * The pipeline uses the clock to calculate the running time. Usually all
* renderers synchronize to the global clock using the buffer timestamps, the
* newsegment events and the element's base time, see #GstPipeline.
*
@@ -52,18 +52,18 @@
* 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.
+ * unscheduled a return value of #GST_CLOCK_UNSCHEDULED is returned.
*
- * Periodic callbacks scheduled async will be repeadedly called automatically
+ * Periodic callbacks scheduled async will be repeatedly called automatically
* until it is unscheduled. To schedule a sync periodic callback,
- * gst_clock_id_wait() should be called repeadedly.
+ * gst_clock_id_wait() should be called repeatedly.
*
* 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().
+ * should be destroyed with 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
@@ -77,14 +77,14 @@
* 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
+ * These clock operations do not operate on the running 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
+ * 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
+ * then automatically be synchronized to this master clock by repeatedly
* 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
@@ -92,14 +92,14 @@
* 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.
+ * The master/slave synchronisation can be tuned with the #GstClock:timeout,
+ * #GstClock:window-size and #GstClock:window-threshold properties.
+ * The #GstClock:timeout property defines the interval to sample the master
+ * 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 2006-08-11 (0.10.10)
+ * Last reviewed on 2009-05-21 (0.10.24)
*/
@@ -119,15 +119,6 @@
#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
@@ -144,6 +135,41 @@
PROP_TIMEOUT
};
+struct _GstClockPrivate
+{
+ gint pre_count;
+ gint post_count;
+};
+
+/* seqlocks */
+#define read_seqbegin(clock) \
+ g_atomic_int_get (&clock->ABI.priv->post_count);
+
+static inline gboolean
+read_seqretry (GstClock * clock, gint seq)
+{
+ /* no retry if the seqnum did not change */
+ if (G_LIKELY (seq == g_atomic_int_get (&clock->ABI.priv->pre_count)))
+ return FALSE;
+
+ /* wait for the writer to finish and retry */
+ GST_OBJECT_LOCK (clock);
+ GST_OBJECT_UNLOCK (clock);
+ return TRUE;
+}
+
+#define write_seqlock(clock) \
+G_STMT_START { \
+ GST_OBJECT_LOCK (clock); \
+ g_atomic_int_inc (&clock->ABI.priv->pre_count); \
+} G_STMT_END;
+
+#define write_sequnlock(clock) \
+G_STMT_START { \
+ g_atomic_int_inc (&clock->ABI.priv->post_count); \
+ GST_OBJECT_UNLOCK (clock); \
+} G_STMT_END;
+
static void gst_clock_class_init (GstClockClass * klass);
static void gst_clock_init (GstClock * clock);
static void gst_clock_dispose (GObject * object);
@@ -166,19 +192,19 @@
{
GstClockEntry *entry;
- entry = ALLOC_ENTRY ();
+ entry = g_slice_new (GstClockEntry);
#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->refcount = 1;
entry->clock = clock;
entry->type = type;
entry->time = time;
entry->interval = interval;
- entry->status = GST_CLOCK_BUSY;
+ entry->status = GST_CLOCK_OK;
entry->func = NULL;
entry->user_data = NULL;
@@ -219,7 +245,7 @@
#ifndef GST_DISABLE_TRACE
gst_alloc_trace_free (_gst_clock_entry_trace, id);
#endif
- FREE_ENTRY (id);
+ g_slice_free (GstClockEntry, id);
}
/**
@@ -282,8 +308,8 @@
* @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
+ * The periodic notifications will 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.
@@ -365,14 +391,14 @@
/**
* gst_clock_id_wait
* @id: The #GstClockID to wait on
- * @jitter: A pointer that will contain the jitter, can be NULL.
+ * @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
+ * 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.
@@ -412,10 +438,6 @@
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);
@@ -448,7 +470,7 @@
if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
entry->time = requested + entry->interval;
- if (clock->stats)
+ if (G_UNLIKELY (clock->stats))
gst_clock_update_stats (clock);
return res;
@@ -460,12 +482,6 @@
"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");
@@ -477,14 +493,17 @@
* gst_clock_id_wait_async:
* @id: a #GstClockID to wait on
* @func: The callback function
- * @user_data: User data passed in the calback
+ * @user_data: User data passed in the callback
*
* 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
+ * time to this function, the callback will be called immediately
* with a time set to GST_CLOCK_TIME_NONE. The callback will
* be called when the time of @id has been reached.
*
+ * The callback @func can be invoked from any thread, either provided by the
+ * core or from a streaming thread. The application should be prepared for this.
+ *
* Returns: the result of the non blocking wait.
*
* MT safe.
@@ -514,10 +533,6 @@
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))
@@ -538,12 +553,6 @@
"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");
@@ -585,52 +594,18 @@
}
-/**
+/*
* 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;
-}
+G_DEFINE_TYPE (GstClock, gst_clock, GST_TYPE_OBJECT);
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);
+ GObjectClass *gobject_class = G_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);
@@ -644,19 +619,23 @@
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_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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));
+ DEFAULT_WINDOW_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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));
+ DEFAULT_WINDOW_THRESHOLD,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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));
+ 0, G_MAXUINT64, DEFAULT_TIMEOUT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (klass, sizeof (GstClockPrivate));
}
static void
@@ -667,6 +646,9 @@
clock->entries_changed = g_cond_new ();
clock->stats = FALSE;
+ clock->ABI.priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (clock, GST_TYPE_CLOCK, GstClockPrivate);
+
clock->internal_calibration = 0;
clock->external_calibration = 0;
clock->rate_numerator = 1;
@@ -790,7 +772,7 @@
* 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().
+ * This function is the reverse of gst_clock_unadjust_unlocked().
*
* Returns: the converted time of the clock.
*/
@@ -810,22 +792,24 @@
cdenom = clock->rate_denominator;
/* avoid divide by 0 */
- if (cdenom == 0)
+ if (G_UNLIKELY (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,
+ * internal < 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 = internal - cinternal;
+ ret = gst_util_uint64_scale (ret, cnum, cdenom);
ret += cexternal;
} else {
- ret = gst_util_uint64_scale (cinternal - internal, cnum, cdenom);
+ ret = cinternal - internal;
+ ret = gst_util_uint64_scale (ret, cnum, cdenom);
/* clamp to 0 */
- if (cexternal > ret)
+ if (G_LIKELY (cexternal > ret))
ret = cexternal - ret;
else
ret = 0;
@@ -847,7 +831,7 @@
* 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().
+ * This function is the reverse of gst_clock_adjust_unlocked().
*
* Returns: the internal time of the clock corresponding to @external.
*
@@ -869,16 +853,18 @@
cdenom = clock->rate_denominator;
/* avoid divide by 0 */
- if (cnum == 0)
+ if (G_UNLIKELY (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);
+ if (G_LIKELY (external >= cexternal)) {
+ ret = external - cexternal;
+ ret = gst_util_uint64_scale (ret, cdenom, cnum);
ret += cinternal;
} else {
- ret = gst_util_uint64_scale (cexternal - external, cdenom, cnum);
- if (cinternal > ret)
+ ret = cexternal - external;
+ ret = gst_util_uint64_scale (ret, cdenom, cnum);
+ if (G_LIKELY (cinternal > ret))
ret = cinternal - ret;
else
ret = 0;
@@ -894,7 +880,7 @@
* unadjusted for the offset and the rate.
*
* Returns: the internal time of the clock. Or GST_CLOCK_TIME_NONE when
- * giving wrong input.
+ * given invalid input.
*
* MT safe.
*/
@@ -940,7 +926,7 @@
* offset and rate.
*
* Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
- * giving wrong input.
+ * given invalid input.
*
* MT safe.
*/
@@ -952,15 +938,19 @@
gst_clock_get_time (GstClock * clock)
{
GstClockTime ret;
+ gint seq;
g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
- ret = gst_clock_get_internal_time (clock);
+ do {
+ /* reget the internal time when we retry to get the most current
+ * timevalue */
+ 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);
+ seq = read_seqbegin (clock);
+ /* this will scale for rate and offset */
+ ret = gst_clock_adjust_unlocked (clock, ret);
+ } while (read_seqretry (clock, seq));
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "adjusted time %" GST_TIME_FORMAT,
GST_TIME_ARGS (ret));
@@ -989,7 +979,7 @@
* follows:
*
* <programlisting>
- * time = (internal_time - @internal) * @rate_num / @rate_denom + @external
+ * time = (internal_time - internal) * rate_num / rate_denom + external
* </programlisting>
*
* This formula is implemented in gst_clock_adjust_unlocked(). Of course, it
@@ -1010,22 +1000,21 @@
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));
+ g_return_if_fail (rate_num != GST_CLOCK_TIME_NONE);
+ g_return_if_fail (rate_denom > 0 && rate_denom != GST_CLOCK_TIME_NONE);
- GST_OBJECT_LOCK (clock);
+ write_seqlock (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));
+ gst_guint64_to_gdouble (rate_num) / gst_guint64_to_gdouble (rate_denom));
clock->internal_calibration = internal;
clock->external_calibration = external;
clock->rate_numerator = rate_num;
clock->rate_denominator = rate_denom;
- GST_OBJECT_UNLOCK (clock);
+ write_sequnlock (clock);
}
/**
@@ -1039,7 +1028,7 @@
* 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
+ * @internal, @external, @rate_num, and @rate_denom can be left %NULL if the
* caller is not interested in the values.
*
* MT safe.
@@ -1052,21 +1041,24 @@
gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
GstClockTime * external, GstClockTime * rate_num, GstClockTime * rate_denom)
{
+ gint seq;
+
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);
+ do {
+ seq = read_seqbegin (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;
+ } while (read_seqretry (clock, seq));
}
-/* will be called repeadedly to sample the master and slave clock
+/* will be called repeatedly to sample the master and slave clock
* to recalibrate the clock */
static gboolean
gst_clock_slave_callback (GstClock * master, GstClockTime time,
@@ -1102,13 +1094,13 @@
* 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
+ * @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.
+ * 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.
+ * #GST_CLOCK_FLAG_CAN_SET_MASTER flag will make this function return %FALSE.
*
* MT safe.
*/
@@ -1169,11 +1161,11 @@
* gst_clock_get_master
* @clock: a #GstClock
*
- * Get the master clock that @clock is slaved to or NULL when the clock is
+ * 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.
+ * Returns: a master #GstClock or %NULL when this clock is not slaved to a
+ * master clock. Unref after usage.
*
* MT safe.
*/
@@ -1332,12 +1324,12 @@
* 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
+ * 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.
@@ -1357,6 +1349,10 @@
GST_CLOCK_SLAVE_LOCK (clock);
+ GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
+ "adding observation slave %" GST_TIME_FORMAT ", master %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (slave), GST_TIME_ARGS (master));
+
clock->times[(4 * clock->time_index)] = slave;
clock->times[(4 * clock->time_index) + 2] = master;