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; } }
static void gst_clock_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstClock *clock; GstClockPrivate *priv; clock = GST_CLOCK (object); priv = clock->priv; switch (prop_id) { case PROP_WINDOW_SIZE: GST_CLOCK_SLAVE_LOCK (clock); 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); /* restart calibration */ priv->filling = TRUE; priv->time_index = 0; GST_CLOCK_SLAVE_UNLOCK (clock); break; case PROP_WINDOW_THRESHOLD: GST_CLOCK_SLAVE_LOCK (clock); priv->window_threshold = MIN (g_value_get_int (value), priv->window_size); GST_CLOCK_SLAVE_UNLOCK (clock); break; case PROP_TIMEOUT: gst_clock_set_timeout (clock, g_value_get_uint64 (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
/** * gst_clock_set_timeout: * @clock: a #GstClock * @timeout: a timeout * * Set the amount of time, in nanoseconds, to sample master and slave * clocks */ void gst_clock_set_timeout (GstClock * clock, GstClockTime timeout) { g_return_if_fail (GST_IS_CLOCK (clock)); GST_CLOCK_SLAVE_LOCK (clock); clock->priv->timeout = timeout; GST_CLOCK_SLAVE_UNLOCK (clock); }
/** * 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 * * 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 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 * regression algorithm. * * MT safe. */ gboolean gst_clock_add_observation (GstClock * clock, GstClockTime slave, GstClockTime master, gdouble * r_squared) { GstClockTime m_num, m_denom, b, xbase; GstClockPrivate *priv; g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE); g_return_val_if_fail (r_squared != NULL, FALSE); priv = clock->priv; 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)); priv->times[(4 * priv->time_index)] = slave; priv->times[(4 * priv->time_index) + 2] = master; priv->time_index++; if (G_UNLIKELY (priv->time_index == priv->window_size)) { priv->filling = FALSE; priv->time_index = 0; } 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)) 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_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; } }
/** * 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. * * 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. */ gboolean gst_clock_set_master (GstClock * clock, GstClock * master) { GstClock **master_p; GstClockPrivate *priv; 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); GST_OBJECT_UNLOCK (clock); priv = clock->priv; GST_CLOCK_SLAVE_LOCK (clock); if (priv->clockid) { gst_clock_id_unschedule (priv->clockid); gst_clock_id_unref (priv->clockid); priv->clockid = NULL; } if (master) { priv->filling = TRUE; priv->time_index = 0; /* use the master periodic id to schedule sampling and * clock calibration. */ priv->clockid = gst_clock_new_periodic_id (master, gst_clock_get_time (master), priv->timeout); gst_clock_id_wait_async (priv->clockid, (GstClockCallback) gst_clock_slave_callback, gst_object_ref (clock), (GDestroyNotify) gst_object_unref); } GST_CLOCK_SLAVE_UNLOCK (clock); GST_OBJECT_LOCK (clock); master_p = &priv->master; gst_object_replace ((GstObject **) master_p, (GstObject *) master); GST_OBJECT_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_timeout: * @clock: a #GstClock * * Get the amount of time that master and slave clocks are sampled. * * Returns: the interval between samples. */ GstClockTime gst_clock_get_timeout (GstClock * clock) { GstClockTime result; g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE); GST_CLOCK_SLAVE_LOCK (clock); result = clock->priv->timeout; GST_CLOCK_SLAVE_UNLOCK (clock); return result; }
static void gst_clock_finalize (GObject * object) { GstClock *clock = GST_CLOCK (object); GST_CLOCK_SLAVE_LOCK (clock); if (clock->priv->clockid) { gst_clock_id_unschedule (clock->priv->clockid); gst_clock_id_unref (clock->priv->clockid); clock->priv->clockid = NULL; } g_free (clock->priv->times); clock->priv->times = NULL; GST_CLOCK_SLAVE_UNLOCK (clock); g_mutex_clear (&clock->priv->slave_lock); G_OBJECT_CLASS (parent_class)->finalize (object); }
static void gst_net_client_clock_observe_times (GstNetClientClock * self, GstClockTime local_1, GstClockTime remote, GstClockTime local_2) { GstClockTime local_avg; gdouble r_squared; GstClock *clock; if (local_2 < local_1) goto bogus_observation; local_avg = (local_2 + local_1) / 2; clock = GST_CLOCK_CAST (self); gst_clock_add_observation (GST_CLOCK (self), local_avg, remote, &r_squared); GST_CLOCK_SLAVE_LOCK (self); if (clock->filling) { self->current_timeout = 0; } else { /* geto formula */ self->current_timeout = (1e-3 / (1 - MIN (r_squared, 0.99999))) * GST_SECOND; self->current_timeout = MIN (self->current_timeout, clock->timeout); } GST_CLOCK_SLAVE_UNLOCK (clock); return; bogus_observation: { GST_WARNING_OBJECT (self, "time packet receive time < send time (%" GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")", GST_TIME_ARGS (local_1), GST_TIME_ARGS (local_2)); return; } }
/** * 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); priv = clock->priv; 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)); priv->times[(4 * priv->time_index)] = slave; priv->times[(4 * priv->time_index) + 2] = master; priv->time_index++; if (G_UNLIKELY (priv->time_index == priv->window_size)) { priv->filling = FALSE; priv->time_index = 0; } if (G_UNLIKELY (priv->filling && priv->time_index < priv->window_threshold)) 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)) 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 (internal) *internal = xbase; if (external) *external = b; if (rate_num) *rate_num = m_num; if (rate_denom) *rate_denom = 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; } }