/**
* SECTION:gstclock
+ * @title: GstClock
* @short_description: Abstract class for global clocks
* @see_also: #GstSystemClock, #GstPipeline
*
* 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
* 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.
#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
gint time_index;
GstClockTime timeout;
GstClockTime *times;
+ GstClockTime *times_temp;
GstClockID clockid;
gint pre_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);
{
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;
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;
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);
}
/**
* @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.
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)))
if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
entry->time = requested + entry->interval;
+ gst_object_unref (clock);
return res;
/* ERRORS */
{
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;
+ }
}
/**
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)))
res = cclass->wait_async (clock, entry);
+ gst_object_unref (clock);
return res;
/* ERRORS */
(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;
+ }
}
/**
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;
+ }
}
* GstClock abstract base class implementation
*/
#define gst_clock_parent_class parent_class
-G_DEFINE_ABSTRACT_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;
g_signal_new ("synced", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- g_type_class_add_private (klass, sizeof (GstClockPrivate));
}
static void
{
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;
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
}
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);
return 1;
}
+/* FIXME 2.0: Remove clock parameter below */
/**
* gst_clock_adjust_with_calibration:
- * @clock: a #GstClock to use
+ * @clock: (allow-none): a #GstClock to use
* @internal_target: a clock time
* @cinternal: a reference internal time
* @cexternal: a reference external time
* 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
return priv->last_time;
}
+/* FIXME 2.0: Remove clock parameter below */
/**
* gst_clock_unadjust_with_calibration:
- * @clock: a #GstClock to use
+ * @clock: (allow-none): a #GstClock to use
* @external_target: a clock time
* @cinternal: a reference internal time
* @cexternal: a reference external time
* 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
* @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
* 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.
/**
* 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
/**
* 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.
/**
* 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.
}
/**
+ * 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
+ */
+GstClock *
+gst_clock_id_get_clock (GstClockID id)
+{
+ GstClockEntry *entry;
+
+ g_return_val_if_fail (id != NULL, NULL);
+
+ entry = (GstClockEntry *) id;
+ return g_weak_ref_get (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+}
+
+/**
+ * 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;
+
+ g_return_val_if_fail (id != NULL, FALSE);
+ g_return_val_if_fail (clock != NULL, FALSE);
+
+ entry = (GstClockEntry *) id;
+ entry_clock = g_weak_ref_get (GST_CLOCK_ENTRY_CLOCK_WEAK_REF (entry));
+ if (entry_clock == clock)
+ ret = TRUE;
+
+ if (G_LIKELY (entry_clock != NULL))
+ gst_object_unref (entry_clock);
+
+ 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
* 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.
"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)) {
goto filling;
n = priv->filling ? priv->time_index : priv->window_size;
- if (!_priv_gst_do_linear_regression (priv->times, n, &m_num, &m_denom, &b,
- &xbase, r_squared))
+ 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);
{
/* no valid regression has been done, ignore the result then */
GST_CLOCK_SLAVE_UNLOCK (clock);
- return TRUE;
+ return FALSE;
}
}
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;
*
* 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.
*