mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-18 07:47:17 +00:00
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.
This commit is contained in:
parent
59ac39499a
commit
cf925ebb6f
17 changed files with 469 additions and 396 deletions
49
ChangeLog
49
ChangeLog
|
@ -1,3 +1,52 @@
|
|||
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;
|
||||
|
|
12
gst/gstbin.c
12
gst/gstbin.c
|
@ -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);
|
||||
|
|
343
gst/gstclock.c
343
gst/gstclock.c
|
@ -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);
|
||||
clock->timeout = g_value_get_uint64 (value);
|
||||
GST_OBJECT_UNLOCK (clock);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -846,8 +1128,25 @@ gst_clock_get_property (GObject * object, guint prop_id,
|
|||
clock = GST_CLOCK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_STATS:
|
||||
case PROP_STATS:
|
||||
GST_OBJECT_LOCK (clock);
|
||||
g_value_set_boolean (value, clock->stats);
|
||||
GST_OBJECT_UNLOCK (clock);
|
||||
break;
|
||||
case PROP_WINDOW_SIZE:
|
||||
GST_OBJECT_LOCK (clock);
|
||||
g_value_set_int (value, clock->window_size);
|
||||
GST_OBJECT_UNLOCK (clock);
|
||||
break;
|
||||
case PROP_WINDOW_THRESHOLD:
|
||||
GST_OBJECT_LOCK (clock);
|
||||
g_value_set_int (value, clock->window_threshold);
|
||||
GST_OBJECT_UNLOCK (clock);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
GST_OBJECT_LOCK (clock);
|
||||
g_value_set_uint64 (value, clock->timeout);
|
||||
GST_OBJECT_UNLOCK (clock);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
|
|
@ -308,6 +308,7 @@ struct _GstClockEntry {
|
|||
* @GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC: clock can do sync periodic timeout requests
|
||||
* @GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC: clock can do async periodic timeout callbacks
|
||||
* @GST_CLOCK_FLAG_CAN_SET_RESOLUTION: clock's resolution can be changed
|
||||
* @GST_CLOCK_FLAG_CAN_SET_MASTER: clock can be slaved to a master clock
|
||||
* @GST_CLOCK_FLAG_LAST: subclasses can add additional flags starting from this flag
|
||||
*
|
||||
* The capabilities of this clock
|
||||
|
@ -318,6 +319,7 @@ typedef enum {
|
|||
GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC = (GST_OBJECT_FLAG_LAST << 2),
|
||||
GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC = (GST_OBJECT_FLAG_LAST << 3),
|
||||
GST_CLOCK_FLAG_CAN_SET_RESOLUTION = (GST_OBJECT_FLAG_LAST << 4),
|
||||
GST_CLOCK_FLAG_CAN_SET_MASTER = (GST_OBJECT_FLAG_LAST << 5),
|
||||
/* padding */
|
||||
GST_CLOCK_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 8),
|
||||
} GstClockFlags;
|
||||
|
@ -384,8 +386,17 @@ struct _GstClock {
|
|||
GstClockTime resolution;
|
||||
gboolean stats;
|
||||
|
||||
/* for master/slave clocks */
|
||||
GstClock *master;
|
||||
gboolean filling;
|
||||
gint window_size;
|
||||
gint window_threshold;
|
||||
gint time_index;
|
||||
GstClockTime timeout;
|
||||
GstClockTime *times;
|
||||
GstClockID clockid;
|
||||
|
||||
/*< private >*/
|
||||
GstClockTime _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
|
@ -422,9 +433,13 @@ void gst_clock_set_calibration (GstClock *clock, GstClockTime internal,
|
|||
void gst_clock_get_calibration (GstClock *clock, GstClockTime *internal,
|
||||
GstClockTime *external, gdouble *rate);
|
||||
/* master/slave clocks */
|
||||
void gst_clock_set_master (GstClock *clock, GstClock *master);
|
||||
gboolean gst_clock_set_master (GstClock *clock, GstClock *master);
|
||||
GstClock* gst_clock_get_master (GstClock *clock);
|
||||
gboolean gst_clock_add_observation (GstClock *clock, GstClockTime slave,
|
||||
GstClockTime master, gdouble *r_squared);
|
||||
|
||||
|
||||
/* getting and adjusting internal time */
|
||||
GstClockTime gst_clock_get_internal_time (GstClock *clock);
|
||||
GstClockTime gst_clock_adjust_unlocked (GstClock *clock, GstClockTime internal);
|
||||
|
||||
|
@ -451,6 +466,7 @@ GstClockReturn gst_clock_id_wait_async (GstClockID id,
|
|||
gpointer user_data);
|
||||
void gst_clock_id_unschedule (GstClockID id);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_CLOCK_H__ */
|
||||
|
|
|
@ -387,25 +387,31 @@ gst_element_provide_clock (GstElement * element)
|
|||
* refcount on the clock. Any previously set clock on the object
|
||||
* is unreffed.
|
||||
*
|
||||
* Returns: TRUE if the element accepted the clock.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
void
|
||||
gboolean
|
||||
gst_element_set_clock (GstElement * element, GstClock * clock)
|
||||
{
|
||||
GstElementClass *oclass;
|
||||
gboolean res = TRUE;
|
||||
|
||||
g_return_if_fail (GST_IS_ELEMENT (element));
|
||||
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
|
||||
|
||||
oclass = GST_ELEMENT_GET_CLASS (element);
|
||||
|
||||
GST_DEBUG_OBJECT (element, "setting clock %p", clock);
|
||||
|
||||
if (oclass->set_clock)
|
||||
oclass->set_clock (element, clock);
|
||||
res = oclass->set_clock (element, clock);
|
||||
|
||||
GST_OBJECT_LOCK (element);
|
||||
gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock);
|
||||
GST_OBJECT_UNLOCK (element);
|
||||
if (res) {
|
||||
GST_OBJECT_LOCK (element);
|
||||
gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock);
|
||||
GST_OBJECT_UNLOCK (element);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -462,7 +462,7 @@ struct _GstElementClass
|
|||
|
||||
/* set/get clocks */
|
||||
GstClock* (*provide_clock) (GstElement *element);
|
||||
void (*set_clock) (GstElement *element, GstClock *clock);
|
||||
gboolean (*set_clock) (GstElement *element, GstClock *clock);
|
||||
|
||||
/* index */
|
||||
GstIndex* (*get_index) (GstElement *element);
|
||||
|
@ -529,7 +529,7 @@ gboolean gst_element_requires_clock (GstElement *element);
|
|||
gboolean gst_element_provides_clock (GstElement *element);
|
||||
GstClock* gst_element_provide_clock (GstElement *element);
|
||||
GstClock* gst_element_get_clock (GstElement *element);
|
||||
void gst_element_set_clock (GstElement *element, GstClock *clock);
|
||||
gboolean gst_element_set_clock (GstElement *element, GstClock *clock);
|
||||
void gst_element_set_base_time (GstElement *element, GstClockTime time);
|
||||
GstClockTime gst_element_get_base_time (GstElement *element);
|
||||
|
||||
|
|
|
@ -336,7 +336,8 @@ gst_pipeline_change_state (GstElement * element, GstStateChange transition)
|
|||
|
||||
if (new_clock) {
|
||||
/* now distribute the clock (which could be NULL I guess) */
|
||||
gst_element_set_clock (element, clock);
|
||||
if (!gst_element_set_clock (element, clock))
|
||||
goto invalid_clock;
|
||||
|
||||
/* if we selected a new clock, let the app know about it */
|
||||
gst_element_post_message (element,
|
||||
|
@ -402,10 +403,11 @@ gst_pipeline_change_state (GstElement * element, GstStateChange transition)
|
|||
/* store the current stream time */
|
||||
if (pipeline->stream_time != GST_CLOCK_TIME_NONE)
|
||||
pipeline->stream_time = now - element->base_time;
|
||||
GST_DEBUG ("stream_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT
|
||||
GST_DEBUG_OBJECT (element,
|
||||
"stream_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT
|
||||
", base time %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (pipeline->stream_time),
|
||||
GST_TIME_ARGS (now), GST_TIME_ARGS (element->base_time));
|
||||
GST_TIME_ARGS (pipeline->stream_time), GST_TIME_ARGS (now),
|
||||
GST_TIME_ARGS (element->base_time));
|
||||
}
|
||||
GST_OBJECT_UNLOCK (element);
|
||||
break;
|
||||
|
@ -420,6 +422,14 @@ gst_pipeline_change_state (GstElement * element, GstStateChange transition)
|
|||
break;
|
||||
}
|
||||
return result;
|
||||
|
||||
invalid_clock:
|
||||
{
|
||||
/* FIXME, post error message */
|
||||
GST_DEBUG_OBJECT (pipeline,
|
||||
"Pipeline cannot operate with selected clock %p", clock);
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -584,15 +594,18 @@ gst_pipeline_use_clock (GstPipeline * pipeline, GstClock * clock)
|
|||
* Set the clock for the pipeline. The clock will be distributed
|
||||
* to all the elements managed by the pipeline.
|
||||
*
|
||||
* Returns: TRUE if the clock could be set on the pipeline.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
void
|
||||
gboolean
|
||||
gst_pipeline_set_clock (GstPipeline * pipeline, GstClock * clock)
|
||||
{
|
||||
g_return_if_fail (pipeline != NULL);
|
||||
g_return_if_fail (GST_IS_PIPELINE (pipeline));
|
||||
g_return_val_if_fail (pipeline != NULL, FALSE);
|
||||
g_return_val_if_fail (GST_IS_PIPELINE (pipeline), FALSE);
|
||||
|
||||
GST_ELEMENT_CLASS (parent_class)->set_clock (GST_ELEMENT (pipeline), clock);
|
||||
return GST_ELEMENT_CLASS (parent_class)->set_clock (GST_ELEMENT (pipeline),
|
||||
clock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -79,7 +79,7 @@ void gst_pipeline_set_new_stream_time (GstPipeline *pipeline, GstClockTime tim
|
|||
GstClockTime gst_pipeline_get_last_stream_time (GstPipeline *pipeline);
|
||||
|
||||
void gst_pipeline_use_clock (GstPipeline *pipeline, GstClock *clock);
|
||||
void gst_pipeline_set_clock (GstPipeline *pipeline, GstClock *clock);
|
||||
gboolean gst_pipeline_set_clock (GstPipeline *pipeline, GstClock *clock);
|
||||
GstClock* gst_pipeline_get_clock (GstPipeline *pipeline);
|
||||
void gst_pipeline_auto_clock (GstPipeline *pipeline);
|
||||
|
||||
|
|
|
@ -209,11 +209,11 @@ GstQuery * gst_query_new_application (GstQueryType type,
|
|||
GstStructure * gst_query_get_structure (GstQuery *query);
|
||||
|
||||
/* moved from old gstqueryutils.h */
|
||||
void gst_query_set_seeking (GstQuery *query, GstFormat format,
|
||||
gboolean seekable,
|
||||
gint64 segment_start,
|
||||
gint64 segment_end);
|
||||
void gst_query_set_formats (GstQuery *query, gint n_formats, ...);
|
||||
void gst_query_set_seeking (GstQuery *query, GstFormat format,
|
||||
gboolean seekable,
|
||||
gint64 segment_start,
|
||||
gint64 segment_end);
|
||||
void gst_query_set_formats (GstQuery *query, gint n_formats, ...);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
@ -63,7 +63,6 @@ G_STMT_START { \
|
|||
|
||||
#define DEFAULT_ADDRESS "127.0.0.1"
|
||||
#define DEFAULT_PORT 5637
|
||||
#define DEFAULT_WINDOW_SIZE 32
|
||||
#define DEFAULT_TIMEOUT GST_SECOND
|
||||
|
||||
enum
|
||||
|
@ -71,8 +70,6 @@ enum
|
|||
PROP_0,
|
||||
PROP_ADDRESS,
|
||||
PROP_PORT,
|
||||
PROP_WINDOW_SIZE,
|
||||
PROP_TIMEOUT
|
||||
};
|
||||
|
||||
#define _do_init(type) \
|
||||
|
@ -114,33 +111,22 @@ gst_net_client_clock_class_init (GstNetClientClockClass * klass)
|
|||
g_param_spec_int ("port", "port",
|
||||
"The port on which the remote server is listening", 0, G_MAXUINT16,
|
||||
DEFAULT_PORT, 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_TIMEOUT,
|
||||
g_param_spec_uint64 ("timeout", "Timeout",
|
||||
"The amount of time, in nanoseconds, to wait for replies", 0,
|
||||
G_MAXUINT64, DEFAULT_TIMEOUT, G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_net_client_clock_init (GstNetClientClock * self,
|
||||
GstNetClientClockClass * g_class)
|
||||
{
|
||||
GstClock *clock = GST_CLOCK_CAST (self);
|
||||
|
||||
self->port = DEFAULT_PORT;
|
||||
self->address = g_strdup (DEFAULT_ADDRESS);
|
||||
self->window_size = DEFAULT_WINDOW_SIZE;
|
||||
self->timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
clock->timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
self->sock = -1;
|
||||
self->thread = NULL;
|
||||
|
||||
self->filling = TRUE;
|
||||
self->time_index = 0;
|
||||
self->local_times = g_new0 (GstClockTime, self->window_size);
|
||||
self->remote_times = g_new0 (GstClockTime, self->window_size);
|
||||
|
||||
self->servaddr = NULL;
|
||||
|
||||
READ_SOCKET (self) = -1;
|
||||
|
@ -170,12 +156,6 @@ gst_net_client_clock_finalize (GObject * object)
|
|||
g_free (self->servaddr);
|
||||
self->servaddr = NULL;
|
||||
|
||||
g_free (self->local_times);
|
||||
self->local_times = NULL;
|
||||
|
||||
g_free (self->remote_times);
|
||||
self->remote_times = NULL;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
@ -196,12 +176,6 @@ gst_net_client_clock_set_property (GObject * object, guint prop_id,
|
|||
case PROP_PORT:
|
||||
self->port = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_WINDOW_SIZE:
|
||||
self->window_size = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
self->timeout = g_value_get_uint64 (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -221,146 +195,39 @@ gst_net_client_clock_get_property (GObject * object, guint prop_id,
|
|||
case PROP_PORT:
|
||||
g_value_set_int (value, self->port);
|
||||
break;
|
||||
case PROP_WINDOW_SIZE:
|
||||
g_value_set_int (value, self->window_size);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
g_value_set_uint64 (value, self->timeout);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* http://mathworld.wolfram.com/LeastSquaresFitting.html */
|
||||
static gboolean
|
||||
do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m,
|
||||
GstClockTime * b, GstClockTime * xbase, gdouble * r_squared)
|
||||
{
|
||||
GstClockTime *newx, *newy;
|
||||
GstClockTime xmin, ymin, xbar, ybar;
|
||||
GstClockTimeDiff sxx, sxy, syy;
|
||||
gint i;
|
||||
|
||||
xbar = ybar = sxx = syy = sxy = 0;
|
||||
|
||||
#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 = 0; i < n; i++) {
|
||||
xmin = MIN (xmin, x[i]);
|
||||
ymin = MIN (ymin, y[i]);
|
||||
}
|
||||
|
||||
DEBUG ("min x: %" G_GUINT64_FORMAT, xmin);
|
||||
DEBUG ("min y: %" G_GUINT64_FORMAT, ymin);
|
||||
|
||||
newx = g_new (GstClockTime, n);
|
||||
newy = g_new (GstClockTime, n);
|
||||
|
||||
/* strip off unnecessary bits of precision */
|
||||
for (i = 0; i < n; i++) {
|
||||
newx[i] = x[i] - xmin;
|
||||
newy[i] = y[i] - ymin;
|
||||
}
|
||||
|
||||
#ifdef DEBUGGING_ENABLED
|
||||
DEBUG ("reduced numbers:");
|
||||
for (i = 0; i < n; i++)
|
||||
DEBUG (" %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, newx[i], newy[i]);
|
||||
#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 = 0; i < n; i++) {
|
||||
xbar += newx[i];
|
||||
ybar += newy[i];
|
||||
}
|
||||
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. */
|
||||
for (i = 0; i < n; i++) {
|
||||
sxx += (newx[i] >> 4) * (newx[i] >> 4) - (xbar >> 4) * (xbar >> 4);
|
||||
syy += (newy[i] >> 4) * (newy[i] >> 4) - (ybar >> 4) * (ybar >> 4);
|
||||
sxy += (newx[i] >> 4) * (newy[i] >> 4) - (xbar >> 4) * (ybar >> 4);
|
||||
}
|
||||
|
||||
*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);
|
||||
|
||||
g_free (newx);
|
||||
g_free (newy);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_net_client_clock_observe_times (GstNetClientClock * self,
|
||||
GstClockTime local_1, GstClockTime remote, GstClockTime local_2)
|
||||
{
|
||||
GstClockTime local_avg;
|
||||
GstClockTime b, xbase;
|
||||
gdouble m, r_squared;
|
||||
gdouble r_squared;
|
||||
GstClock *clock;
|
||||
|
||||
if (local_2 < local_1)
|
||||
goto bogus_observation;
|
||||
|
||||
local_avg = (local_2 + local_1) / 2;
|
||||
|
||||
self->local_times[self->time_index] = local_avg;
|
||||
self->remote_times[self->time_index] = remote;
|
||||
clock = GST_CLOCK_CAST (self);
|
||||
|
||||
self->time_index++;
|
||||
if (self->time_index == self->window_size) {
|
||||
self->filling = FALSE;
|
||||
self->time_index = 0;
|
||||
}
|
||||
GST_OBJECT_LOCK (self);
|
||||
gst_clock_add_observation (GST_CLOCK (self), local_avg, remote, &r_squared);
|
||||
|
||||
if (!self->filling || self->time_index >= 4) {
|
||||
/* need to allow tuning of the "4" parameter -- means that we need 4 samples
|
||||
* before beginning to adjust the clock */
|
||||
do_linear_regression (self->local_times, self->remote_times,
|
||||
self->filling ? self->time_index : self->window_size, &m, &b,
|
||||
&xbase, &r_squared);
|
||||
|
||||
GST_LOG_OBJECT (self, "adjusting clock to m=%g, b=%" G_GINT64_FORMAT
|
||||
" (rsquared=%g)", m, b, r_squared);
|
||||
|
||||
gst_clock_set_calibration (GST_CLOCK (self), xbase, b, m);
|
||||
}
|
||||
|
||||
if (self->filling) {
|
||||
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, self->timeout);
|
||||
self->current_timeout = MIN (self->current_timeout, clock->timeout);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (clock);
|
||||
|
||||
return;
|
||||
|
||||
|
|
|
@ -71,15 +71,11 @@ struct _GstNetClientClock {
|
|||
|
||||
GstClockTime current_timeout;
|
||||
|
||||
gboolean filling;
|
||||
gint time_index;
|
||||
GstClockTime *local_times;
|
||||
GstClockTime *remote_times;
|
||||
|
||||
struct sockaddr_id *servaddr;
|
||||
|
||||
GThread *thread;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -63,7 +63,6 @@ G_STMT_START { \
|
|||
|
||||
#define DEFAULT_ADDRESS "127.0.0.1"
|
||||
#define DEFAULT_PORT 5637
|
||||
#define DEFAULT_WINDOW_SIZE 32
|
||||
#define DEFAULT_TIMEOUT GST_SECOND
|
||||
|
||||
enum
|
||||
|
@ -71,8 +70,6 @@ enum
|
|||
PROP_0,
|
||||
PROP_ADDRESS,
|
||||
PROP_PORT,
|
||||
PROP_WINDOW_SIZE,
|
||||
PROP_TIMEOUT
|
||||
};
|
||||
|
||||
#define _do_init(type) \
|
||||
|
@ -114,33 +111,22 @@ gst_net_client_clock_class_init (GstNetClientClockClass * klass)
|
|||
g_param_spec_int ("port", "port",
|
||||
"The port on which the remote server is listening", 0, G_MAXUINT16,
|
||||
DEFAULT_PORT, 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_TIMEOUT,
|
||||
g_param_spec_uint64 ("timeout", "Timeout",
|
||||
"The amount of time, in nanoseconds, to wait for replies", 0,
|
||||
G_MAXUINT64, DEFAULT_TIMEOUT, G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_net_client_clock_init (GstNetClientClock * self,
|
||||
GstNetClientClockClass * g_class)
|
||||
{
|
||||
GstClock *clock = GST_CLOCK_CAST (self);
|
||||
|
||||
self->port = DEFAULT_PORT;
|
||||
self->address = g_strdup (DEFAULT_ADDRESS);
|
||||
self->window_size = DEFAULT_WINDOW_SIZE;
|
||||
self->timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
clock->timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
self->sock = -1;
|
||||
self->thread = NULL;
|
||||
|
||||
self->filling = TRUE;
|
||||
self->time_index = 0;
|
||||
self->local_times = g_new0 (GstClockTime, self->window_size);
|
||||
self->remote_times = g_new0 (GstClockTime, self->window_size);
|
||||
|
||||
self->servaddr = NULL;
|
||||
|
||||
READ_SOCKET (self) = -1;
|
||||
|
@ -170,12 +156,6 @@ gst_net_client_clock_finalize (GObject * object)
|
|||
g_free (self->servaddr);
|
||||
self->servaddr = NULL;
|
||||
|
||||
g_free (self->local_times);
|
||||
self->local_times = NULL;
|
||||
|
||||
g_free (self->remote_times);
|
||||
self->remote_times = NULL;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
@ -196,12 +176,6 @@ gst_net_client_clock_set_property (GObject * object, guint prop_id,
|
|||
case PROP_PORT:
|
||||
self->port = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_WINDOW_SIZE:
|
||||
self->window_size = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
self->timeout = g_value_get_uint64 (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -221,146 +195,39 @@ gst_net_client_clock_get_property (GObject * object, guint prop_id,
|
|||
case PROP_PORT:
|
||||
g_value_set_int (value, self->port);
|
||||
break;
|
||||
case PROP_WINDOW_SIZE:
|
||||
g_value_set_int (value, self->window_size);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
g_value_set_uint64 (value, self->timeout);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* http://mathworld.wolfram.com/LeastSquaresFitting.html */
|
||||
static gboolean
|
||||
do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m,
|
||||
GstClockTime * b, GstClockTime * xbase, gdouble * r_squared)
|
||||
{
|
||||
GstClockTime *newx, *newy;
|
||||
GstClockTime xmin, ymin, xbar, ybar;
|
||||
GstClockTimeDiff sxx, sxy, syy;
|
||||
gint i;
|
||||
|
||||
xbar = ybar = sxx = syy = sxy = 0;
|
||||
|
||||
#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 = 0; i < n; i++) {
|
||||
xmin = MIN (xmin, x[i]);
|
||||
ymin = MIN (ymin, y[i]);
|
||||
}
|
||||
|
||||
DEBUG ("min x: %" G_GUINT64_FORMAT, xmin);
|
||||
DEBUG ("min y: %" G_GUINT64_FORMAT, ymin);
|
||||
|
||||
newx = g_new (GstClockTime, n);
|
||||
newy = g_new (GstClockTime, n);
|
||||
|
||||
/* strip off unnecessary bits of precision */
|
||||
for (i = 0; i < n; i++) {
|
||||
newx[i] = x[i] - xmin;
|
||||
newy[i] = y[i] - ymin;
|
||||
}
|
||||
|
||||
#ifdef DEBUGGING_ENABLED
|
||||
DEBUG ("reduced numbers:");
|
||||
for (i = 0; i < n; i++)
|
||||
DEBUG (" %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, newx[i], newy[i]);
|
||||
#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 = 0; i < n; i++) {
|
||||
xbar += newx[i];
|
||||
ybar += newy[i];
|
||||
}
|
||||
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. */
|
||||
for (i = 0; i < n; i++) {
|
||||
sxx += (newx[i] >> 4) * (newx[i] >> 4) - (xbar >> 4) * (xbar >> 4);
|
||||
syy += (newy[i] >> 4) * (newy[i] >> 4) - (ybar >> 4) * (ybar >> 4);
|
||||
sxy += (newx[i] >> 4) * (newy[i] >> 4) - (xbar >> 4) * (ybar >> 4);
|
||||
}
|
||||
|
||||
*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);
|
||||
|
||||
g_free (newx);
|
||||
g_free (newy);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_net_client_clock_observe_times (GstNetClientClock * self,
|
||||
GstClockTime local_1, GstClockTime remote, GstClockTime local_2)
|
||||
{
|
||||
GstClockTime local_avg;
|
||||
GstClockTime b, xbase;
|
||||
gdouble m, r_squared;
|
||||
gdouble r_squared;
|
||||
GstClock *clock;
|
||||
|
||||
if (local_2 < local_1)
|
||||
goto bogus_observation;
|
||||
|
||||
local_avg = (local_2 + local_1) / 2;
|
||||
|
||||
self->local_times[self->time_index] = local_avg;
|
||||
self->remote_times[self->time_index] = remote;
|
||||
clock = GST_CLOCK_CAST (self);
|
||||
|
||||
self->time_index++;
|
||||
if (self->time_index == self->window_size) {
|
||||
self->filling = FALSE;
|
||||
self->time_index = 0;
|
||||
}
|
||||
GST_OBJECT_LOCK (self);
|
||||
gst_clock_add_observation (GST_CLOCK (self), local_avg, remote, &r_squared);
|
||||
|
||||
if (!self->filling || self->time_index >= 4) {
|
||||
/* need to allow tuning of the "4" parameter -- means that we need 4 samples
|
||||
* before beginning to adjust the clock */
|
||||
do_linear_regression (self->local_times, self->remote_times,
|
||||
self->filling ? self->time_index : self->window_size, &m, &b,
|
||||
&xbase, &r_squared);
|
||||
|
||||
GST_LOG_OBJECT (self, "adjusting clock to m=%g, b=%" G_GINT64_FORMAT
|
||||
" (rsquared=%g)", m, b, r_squared);
|
||||
|
||||
gst_clock_set_calibration (GST_CLOCK (self), xbase, b, m);
|
||||
}
|
||||
|
||||
if (self->filling) {
|
||||
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, self->timeout);
|
||||
self->current_timeout = MIN (self->current_timeout, clock->timeout);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (clock);
|
||||
|
||||
return;
|
||||
|
||||
|
|
|
@ -71,15 +71,11 @@ struct _GstNetClientClock {
|
|||
|
||||
GstClockTime current_timeout;
|
||||
|
||||
gboolean filling;
|
||||
gint time_index;
|
||||
GstClockTime *local_times;
|
||||
GstClockTime *remote_times;
|
||||
|
||||
struct sockaddr_id *servaddr;
|
||||
|
||||
GThread *thread;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue