Commit cf925ebb authored by Wim Taymans's avatar Wim Taymans

gst/base/gstbasesink.*: No need to store the clock, the parent element class already has it.

Original commit message from CVS:
* gst/base/gstbasesink.c: (gst_base_sink_class_init),
(gst_base_sink_wait), (gst_base_sink_do_sync),
(gst_base_sink_handle_event):
* gst/base/gstbasesink.h:
No need to store the clock, the parent element class already
has it.

* gst/gstbin.c: (gst_bin_set_clock_func), (gst_bin_add_func):
Updates for clock_set returning a gboolean

* gst/gstclock.c: (gst_clock_entry_new), (gst_clock_id_wait),
(gst_clock_id_wait_async), (gst_clock_class_init),
(gst_clock_init), (gst_clock_finalize),
(gst_clock_get_internal_time), (gst_clock_get_time),
(gst_clock_slave_callback), (gst_clock_set_master),
(gst_clock_get_master), (do_linear_regression),
(gst_clock_add_observation), (gst_clock_set_property),
(gst_clock_get_property):
* gst/gstclock.h:
Implement master/slave. When setting a clock as a slave, a
periodic timeout is scheduled to sample master and slave times.
Then the slave clock is recalibrated to match offset and rate
of the master clock.
Update logging a bit.
Add flag so that a clock can state that is cannot be slaved to
another clock.

* gst/gstelement.c: (gst_element_set_clock):
* gst/gstelement.h:
The set_clock returns a gboolean for when an element cannot
deal with the selected clock in the pipeline.

* gst/gstpipeline.c: (gst_pipeline_change_state),
(gst_pipeline_set_clock):
* gst/gstpipeline.h:
Handle the case where the selected clock cannot be set on
the pipeline.

* gst/net/gstnetclientclock.c: (gst_net_client_clock_class_init),
(gst_net_client_clock_init), (gst_net_client_clock_finalize),
(gst_net_client_clock_set_property),
(gst_net_client_clock_get_property),
(gst_net_client_clock_observe_times):
* gst/net/gstnetclientclock.h:
Use regression code in GstClock parent, remove duplicated
functionality.
parent 59ac3949
2005-11-22 Wim Taymans <wim@fluendo.com>
* gst/base/gstbasesink.c: (gst_base_sink_class_init),
(gst_base_sink_wait), (gst_base_sink_do_sync),
(gst_base_sink_handle_event):
* gst/base/gstbasesink.h:
No need to store the clock, the parent element class already
has it.
* gst/gstbin.c: (gst_bin_set_clock_func), (gst_bin_add_func):
Updates for clock_set returning a gboolean
* gst/gstclock.c: (gst_clock_entry_new), (gst_clock_id_wait),
(gst_clock_id_wait_async), (gst_clock_class_init),
(gst_clock_init), (gst_clock_finalize),
(gst_clock_get_internal_time), (gst_clock_get_time),
(gst_clock_slave_callback), (gst_clock_set_master),
(gst_clock_get_master), (do_linear_regression),
(gst_clock_add_observation), (gst_clock_set_property),
(gst_clock_get_property):
* gst/gstclock.h:
Implement master/slave. When setting a clock as a slave, a
periodic timeout is scheduled to sample master and slave times.
Then the slave clock is recalibrated to match offset and rate
of the master clock.
Update logging a bit.
Add flag so that a clock can state that is cannot be slaved to
another clock.
* gst/gstelement.c: (gst_element_set_clock):
* gst/gstelement.h:
The set clock returns a gboolean for when an element cannot
deal with the selected clock in the pipeline.
* gst/gstpipeline.c: (gst_pipeline_change_state),
(gst_pipeline_set_clock):
* gst/gstpipeline.h:
Handle the case where the selected clock cannot be set on
the pipeline.
* gst/net/gstnetclientclock.c: (gst_net_client_clock_class_init),
(gst_net_client_clock_init), (gst_net_client_clock_finalize),
(gst_net_client_clock_set_property),
(gst_net_client_clock_get_property),
(gst_net_client_clock_observe_times):
* gst/net/gstnetclientclock.h:
Use regression code in GstClock parent, remove duplicated
functionality.
2005-11-22 Michael Smith <msmith@fluendo.com>
* gst/gstutils.c: (gst_util_clock_time_scale):
......
......@@ -135,8 +135,6 @@ gst_base_sink_get_type (void)
return base_sink_type;
}
static void gst_base_sink_set_clock (GstElement * element, GstClock * clock);
static void gst_base_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_base_sink_get_property (GObject * object, guint prop_id,
......@@ -200,7 +198,6 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
G_PARAM_READWRITE));
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_base_sink_set_clock);
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
......@@ -330,16 +327,6 @@ gst_base_sink_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_base_sink_set_clock (GstElement * element, GstClock * clock)
{
GstBaseSink *sink;
sink = GST_BASE_SINK (element);
sink->clock = clock;
}
static void
gst_base_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
......@@ -890,7 +877,7 @@ gst_base_sink_wait (GstBaseSink * basesink, GstClockTime time)
g_assert (basesink->clock_id == NULL);
g_assert (GST_CLOCK_TIME_IS_VALID (time));
id = gst_clock_new_single_shot_id (basesink->clock, time);
id = gst_clock_new_single_shot_id (GST_ELEMENT_CLOCK (basesink), time);
basesink->clock_id = id;
/* release the object lock while waiting */
......@@ -956,7 +943,7 @@ gst_base_sink_do_sync (GstBaseSink * basesink, GstBuffer * buffer)
}
/* now do clocking */
if (basesink->clock) {
if (GST_ELEMENT_CLOCK (basesink)) {
GstClockTime base_time;
GstClockTimeDiff stream_start, stream_end;
......@@ -1016,7 +1003,7 @@ gst_base_sink_handle_event (GstBaseSink * basesink, GstEvent * event)
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
GST_OBJECT_LOCK (basesink);
if (basesink->clock) {
if (GST_ELEMENT_CLOCK (basesink)) {
/* wait for last buffer to finish if we have a valid end time */
if (GST_CLOCK_TIME_IS_VALID (basesink->end_time)) {
gst_base_sink_wait (basesink, basesink->end_time);
......
......@@ -36,13 +36,6 @@ G_BEGIN_DECLS
#define GST_IS_BASE_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_SINK))
#define GST_BASE_SINK_CAST(obj) ((GstBaseSink *) (obj))
/**
* GST_BASE_SINK_CLOCK:
* @obj: base sink instance
*
* Gives the pointer to the #GstClock object of the element.
*/
#define GST_BASE_SINK_CLOCK(obj) (GST_BASE_SINK_CAST (obj)->clock)
/**
* GST_BASE_SINK_PAD:
* @obj: base sink instance
......@@ -83,7 +76,6 @@ struct _GstBaseSink {
GstSegment segment;
/*< private >*/ /* with LOCK */
GstClock *clock;
GstClockID clock_id;
GstClockTime end_time;
gboolean sync;
......
......@@ -145,7 +145,7 @@ static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
#endif
static GstClock *gst_bin_provide_clock_func (GstElement * element);
static void gst_bin_set_clock_func (GstElement * element, GstClock * clock);
static gboolean gst_bin_set_clock_func (GstElement * element, GstClock * clock);
static void gst_bin_handle_message_func (GstBin * bin, GstMessage * message);
static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
......@@ -432,11 +432,12 @@ gst_bin_set_index_func (GstElement * element, GstIndex * index)
*
* MT safe
*/
static void
static gboolean
gst_bin_set_clock_func (GstElement * element, GstClock * clock)
{
GList *children;
GstBin *bin;
gboolean res = TRUE;
bin = GST_BIN (element);
......@@ -445,10 +446,12 @@ gst_bin_set_clock_func (GstElement * element, GstClock * clock)
for (children = bin->children; children; children = g_list_next (children)) {
GstElement *child = GST_ELEMENT (children->data);
gst_element_set_clock (child, clock);
res &= gst_element_set_clock (child, clock);
}
}
GST_OBJECT_UNLOCK (bin);
return res;
}
/* get the clock for this bin by asking all of the children in this bin
......@@ -726,6 +729,9 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
/* propagate the current base time and clock */
gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time);
/* it's possible that the element did not accept the clock but
* that is not important right now. When the pipeline goes to PLAYING,
* a new clock will be selected */
gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
bin->state_dirty = TRUE;
GST_OBJECT_UNLOCK (bin);
......
......@@ -98,15 +98,26 @@
static GstAllocTrace *_gst_clock_entry_trace;
#endif
#define DEFAULT_EVENT_DIFF (GST_SECOND)
#define DEFAULT_MAX_DIFF (2 * GST_SECOND)
/* #define DEBUGGING_ENABLED */
#ifdef DEBUGGING_ENABLED
#define DEBUG(x, args...) g_print (x "\n", ##args)
#else
#define DEBUG(x, args...) /* nop */
#endif
#define DEFAULT_STATS FALSE
#define DEFAULT_WINDOW_SIZE 32
#define DEFAULT_WINDOW_THRESHOLD 4
#define DEFAULT_TIMEOUT GST_SECOND / 10
enum
{
ARG_0,
ARG_STATS,
ARG_MAX_DIFF,
ARG_EVENT_DIFF
PROP_0,
PROP_STATS,
PROP_WINDOW_SIZE,
PROP_WINDOW_THRESHOLD,
PROP_TIMEOUT
};
static void gst_clock_class_init (GstClockClass * klass);
......@@ -134,8 +145,8 @@ gst_clock_entry_new (GstClock * clock, GstClockTime time,
#ifndef GST_DISABLE_TRACE
gst_alloc_trace_new (_gst_clock_entry_trace, entry);
#endif
GST_CAT_DEBUG (GST_CAT_CLOCK, "created entry %p, time %" GST_TIME_FORMAT,
entry, GST_TIME_ARGS (time));
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;
......@@ -327,20 +338,22 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
entry = (GstClockEntry *) id;
requested = GST_CLOCK_ENTRY_TIME (entry);
clock = GST_CLOCK_ENTRY_CLOCK (entry);
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
goto invalid_time;
if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
goto unscheduled;
clock = GST_CLOCK_ENTRY_CLOCK (entry);
cclass = GST_CLOCK_GET_CLASS (clock);
if (G_LIKELY (cclass->wait)) {
GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock entry %p", id);
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "waiting on clock entry %p",
id);
res = cclass->wait (clock, entry);
GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting entry %p", id);
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "done waiting entry %p", id);
if (jitter) {
GstClockTime now = gst_clock_get_time (clock);
......@@ -362,12 +375,14 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
/* ERRORS */
invalid_time:
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
"invalid time requested, returning _BADTIME");
return GST_CLOCK_BADTIME;
}
unscheduled:
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
"entry was unscheduled return _UNSCHEDULED");
return GST_CLOCK_UNSCHEDULED;
}
}
......@@ -427,12 +442,14 @@ gst_clock_id_wait_async (GstClockID id,
invalid_time:
{
(func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
"invalid time requested, returning _BADTIME");
return GST_CLOCK_BADTIME;
}
unscheduled:
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
"entry was unscheduled return _UNSCHEDULED");
return GST_CLOCK_UNSCHEDULED;
}
}
......@@ -518,9 +535,22 @@ gst_clock_class_init (GstClockClass * klass)
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 (G_OBJECT_CLASS (klass), ARG_STATS,
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_STATS,
g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
FALSE, G_PARAM_READWRITE));
DEFAULT_STATS, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), 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 (G_OBJECT_CLASS (klass),
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 (G_OBJECT_CLASS (klass), 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
......@@ -534,6 +564,12 @@ gst_clock_init (GstClock * clock)
clock->internal_calibration = 0;
clock->external_calibration = 0;
clock->rate = 1.0;
clock->filling = TRUE;
clock->window_size = DEFAULT_WINDOW_SIZE;
clock->window_threshold = DEFAULT_WINDOW_THRESHOLD;
clock->time_index = 0;
clock->timeout = DEFAULT_TIMEOUT;
clock->times = g_new0 (GstClockTime, 4 * clock->window_size);
}
static void
......@@ -541,8 +577,19 @@ gst_clock_finalize (GObject * object)
{
GstClock *clock = GST_CLOCK (object);
GST_OBJECT_LOCK (clock);
if (clock->clockid) {
gst_clock_id_unschedule (clock->clockid);
gst_clock_id_unref (clock->clockid);
clock->clockid = NULL;
}
g_cond_free (clock->entries_changed);
g_free (clock->times);
clock->times = NULL;
GST_OBJECT_UNLOCK (clock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
......@@ -652,7 +699,7 @@ gst_clock_get_internal_time (GstClock * clock)
} else {
ret = G_GINT64_CONSTANT (0);
}
GST_CAT_DEBUG (GST_CAT_CLOCK, "internal time %" GST_TIME_FORMAT,
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "internal time %" GST_TIME_FORMAT,
GST_TIME_ARGS (ret));
return ret;
......@@ -685,7 +732,7 @@ gst_clock_get_time (GstClock * clock)
ret = gst_clock_adjust_unlocked (clock, ret);
GST_OBJECT_UNLOCK (clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "adjusted time %" GST_TIME_FORMAT,
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "adjusted time %" GST_TIME_FORMAT,
GST_TIME_ARGS (ret));
return ret;
......@@ -766,6 +813,30 @@ gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
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
......@@ -778,14 +849,47 @@ gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
* A clock provider that slaves its clock to a master can get the current
* calibration values with gst_clock_get_calibration().
*
* Returns: TRUE if the clock is capable of being slaved to a master clock.
*
* MT safe.
*/
void
gboolean
gst_clock_set_master (GstClock * clock, GstClock * master)
{
g_return_val_if_fail (GST_IS_CLOCK (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_DEBUG_OBJECT (clock, "slaving to master clock %p", master);
gst_object_replace ((GstObject **) & clock->master, (GstObject *) master);
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_OBJECT_UNLOCK (clock);
return TRUE;
not_supported:
{
GST_DEBUG_OBJECT (clock, "cannot be slaved to a master clock");
return FALSE;
}
}
/**
......@@ -805,6 +909,8 @@ 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);
......@@ -813,6 +919,160 @@ gst_clock_get_master (GstClock * clock)
return result;
}
/* http://mathworld.wolfram.com/LeastSquaresFitting.html */
static gboolean
do_linear_regression (GstClock * clock, gdouble * m,
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
DEBUG ("doing regression on:");
for (i = 0; i < n; i++)
DEBUG (" %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, x[i], y[i]);
#endif
xmin = ymin = G_MAXUINT64;
for (i = j = 0; i < n; i++, j += 4) {
xmin = MIN (xmin, x[j]);
ymin = MIN (ymin, y[j]);
}
DEBUG ("min x: %" G_GUINT64_FORMAT, xmin);
DEBUG ("min y: %" G_GUINT64_FORMAT, ymin);
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
DEBUG ("reduced numbers:");
for (i = j = 0; i < n; i++, j += 4)
DEBUG (" %" 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;
DEBUG (" xbar = %" G_GUINT64_FORMAT, xbar);
DEBUG (" ybar = %" G_GUINT64_FORMAT, ybar);
/* 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;
}
*m = ((double) sxy) / sxx;
*xbase = xmin;
*b = (ybar + ymin) - (GstClockTime) (xbar * *m);
*r_squared = ((double) sxy * (double) sxy) / ((double) sxx * (double) syy);
DEBUG (" m = %g", *m);
DEBUG (" b = %" G_GUINT64_FORMAT, *b);
DEBUG (" xbase = %" G_GUINT64_FORMAT, *xbase);
DEBUG (" r2 = %g", *r_squared);
return TRUE;
}
/**
* gst_clock_add_observation
* @clock: a #GstClock
* @slave: an time on the slave
* @master: an 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.
*
* This function should be called with @clock OBJECT_LOCK.
*
* 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 b, xbase;
gdouble m;
g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
g_return_val_if_fail (r_squared != NULL, FALSE);
clock->times[(4 * clock->time_index)] = slave;
clock->times[(4 * clock->time_index) + 2] = master;
clock->time_index++;
if (clock->time_index == clock->window_size) {
clock->filling = FALSE;
clock->time_index = 0;
}
if (clock->filling && clock->time_index < clock->window_threshold)
goto filling;
do_linear_regression (clock, &m, &b, &xbase, r_squared);
GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
"adjusting clock to m=%g, b=%" G_GINT64_FORMAT " (rsquared=%g)", m, b,
*r_squared);
clock->internal_calibration = xbase;
clock->external_calibration = b;
clock->rate = m;
return TRUE;
filling:
{
return FALSE;
}
}
static void
gst_clock_update_stats (GstClock * clock)
{
......@@ -827,10 +1087,32 @@ gst_clock_set_property (GObject * object, guint prop_id,
clock = GST_CLOCK (object);
switch (prop_id) {
case ARG_STATS:
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_OBJECT_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);
GST_OBJECT_UNLOCK (clock);
break;
case PROP_WINDOW_THRESHOLD:
GST_OBJECT_LOCK (clock);
clock->window_threshold =
MIN (g_value_get_int (value), clock->window_size);
GST_OBJECT_UNLOCK (clock);
break;
case PROP_TIMEOUT:
GST_OBJECT_LOCK (clock);