X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstclock.c;h=279b1f8bc5a75bd55300d9c646971c6ba6ec96fa;hb=706e10ad0586bf98c829cdfaa079118447b94ef5;hp=fe789d71c6754f3963a8d40a5a52b399d6b97765;hpb=6a0e594ebcadbe8d9e32819579072920ef422274;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstclock.c b/gst/gstclock.c index fe789d7..279b1f8 100644 --- a/gst/gstclock.c +++ b/gst/gstclock.c @@ -17,12 +17,13 @@ * * 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. @@ -98,11 +99,8 @@ * 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 @@ -111,12 +109,6 @@ #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); } /** @@ -413,7 +457,7 @@ gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time, } /** - * gst_clock_id_compare_func + * gst_clock_id_compare_func: * @id1: A #GstClockID * @id2: A #GstClockID to compare with * @@ -442,7 +486,7 @@ gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2) } /** - * gst_clock_id_get_time + * gst_clock_id_get_time: * @id: The #GstClockID to query * * Get the time of the clock ID @@ -460,28 +504,28 @@ gst_clock_id_get_time (GstClockID id) } /** - * gst_clock_id_wait + * gst_clock_id_wait: * @id: The #GstClockID to wait on * @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,17 +577,24 @@ 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; + } } /** - * gst_clock_id_wait_async_full: + * gst_clock_id_wait_async: * @id: a #GstClockID to wait on * @func: The callback function * @user_data: User data passed in the callback @@ -557,11 +612,9 @@ not_supported: * Returns: the result of the non blocking wait. * * MT safe. - * - * Since: 0.10.30 */ GstClockReturn -gst_clock_id_wait_async_full (GstClockID id, +gst_clock_id_wait_async (GstClockID id, GstClockCallback func, gpointer user_data, GDestroyNotify destroy_data) { GstClockEntry *entry; @@ -575,7 +628,9 @@ gst_clock_id_wait_async_full (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_full (GstClockID id, res = cclass->wait_async (clock, entry); + gst_object_unref (clock); return res; /* ERRORS */ @@ -600,39 +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; } -} - -/** - * gst_clock_id_wait_async: - * @id: a #GstClockID to wait on - * @func: The callback function - * @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 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. - */ -GstClockReturn -gst_clock_id_wait_async (GstClockID id, - GstClockCallback func, gpointer user_data) -{ - return gst_clock_id_wait_async_full (id, func, user_data, NULL); +invalid_entry: + { + GST_CAT_DEBUG (GST_CAT_CLOCK, "clock entry %p lost its clock", id); + return GST_CLOCK_ERROR; + } } /** @@ -656,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; + } } @@ -669,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; @@ -700,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 @@ -708,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; @@ -719,12 +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); + priv->times_temp = priv->times + 2 * priv->window_size; } static void @@ -754,15 +816,17 @@ 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); } /** - * gst_clock_set_resolution + * gst_clock_set_resolution: * @clock: a #GstClock * @resolution: The resolution to set * @@ -794,7 +858,7 @@ gst_clock_set_resolution (GstClock * clock, GstClockTime resolution) } /** - * gst_clock_get_resolution + * gst_clock_get_resolution: * @clock: a #GstClock * * Get the accuracy of the clock. The accuracy of the clock is the granularity @@ -819,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)) @@ -855,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)) @@ -869,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; @@ -903,28 +966,98 @@ 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_get_internal_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 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 * * Gets the current internal time of the given clock. The time is returned @@ -943,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)) @@ -965,7 +1103,7 @@ not_supported: } /** - * gst_clock_get_time + * gst_clock_get_time: * @clock: a #GstClock to query * * Gets the current time of the given clock. The time is always @@ -1002,12 +1140,12 @@ gst_clock_get_time (GstClock * clock) } /** - * gst_clock_set_calibration + * 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 + * 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 @@ -1021,9 +1159,9 @@ gst_clock_get_time (GstClock * clock) * Subsequent calls to gst_clock_get_time() will return clock times computed as * follows: * - * + * |[ * time = (internal_time - internal) * rate_num / rate_denom + external - * + * ]| * * This formula is implemented in gst_clock_adjust_unlocked(). Of course, it * tries to do the integer arithmetic as precisely as possible. @@ -1061,8 +1199,8 @@ gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime } /** - * gst_clock_get_calibration - * @clock: a #GstClock + * gst_clock_get_calibration: + * @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 @@ -1109,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); @@ -1125,23 +1269,23 @@ gst_clock_slave_callback (GstClock * master, GstClockTime time, } /** - * gst_clock_set_master - * @clock: a #GstClock - * @master: (allow-none): a master #GstClock + * gst_clock_set_master: + * @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. @@ -1159,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); @@ -1178,7 +1325,7 @@ gst_clock_set_master (GstClock * clock, GstClock * master) * clock calibration. */ priv->clockid = gst_clock_new_periodic_id (master, gst_clock_get_time (master), priv->timeout); - gst_clock_id_wait_async_full (priv->clockid, + gst_clock_id_wait_async (priv->clockid, (GstClockCallback) gst_clock_slave_callback, gst_object_ref (clock), (GDestroyNotify) gst_object_unref); } @@ -1199,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. */ @@ -1231,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 + * gst_clock_add_observation: + * @clock: a #GstClock * @slave: a time on the slave * @master: a time on the master * @r_squared: (out): a pointer to hold the result @@ -1369,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. @@ -1385,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); @@ -1398,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)) { @@ -1410,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); @@ -1419,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; @@ -1433,7 +1572,7 @@ invalid: { /* no valid regression has been done, ignore the result then */ GST_CLOCK_SLAVE_UNLOCK (clock); - return TRUE; + return FALSE; } } @@ -1493,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; @@ -1541,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); + } +}