From: Andy Wingo Date: Fri, 18 Nov 2005 15:30:18 +0000 (+0000) Subject: gst/net/gstnetclientclock.c (do_linear_regression): Use all integer arithmetic. Retur... X-Git-Tag: RELEASE-0_9_6~93 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fa09daa3d2ee4560f9853693cb28810298de2fa8;p=platform%2Fupstream%2Fgstreamer.git gst/net/gstnetclientclock.c (do_linear_regression): Use all integer arithmetic. Return the minimum of the domain, whi... Original commit message from CVS: 2005-11-18 Andy Wingo * gst/net/gstnetclientclock.c (do_linear_regression): Use all integer arithmetic. Return the minimum of the domain, which can be set as "internal" for gst_clock_set_calibration. (gst_net_client_clock_observe_times): Call _set_calibration. (gst_net_client_clock_new): Call _set_calibration instead of rate_offset. * check/net/gstnetclientclock.c (test_functioning): Use the right adjustment api. * gst/gstclock.h: * gst/gstclock.c (gst_clock_get_calibration) (gst_clock_set_calibration): New functions, obsolete the ones I added yesterday. Doh. Precision issues mean we have to extrapolate from a point in the more recent past than 1970. (gst_clock_get_rate_offset, gst_clock_set_rate_offset): Mark as obsolete. (gst_clock_adjust_unlocked): Use the right calibration data. --- diff --git a/ChangeLog b/ChangeLog index 0e5b646..a45ec19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2005-11-18 Andy Wingo + + * gst/net/gstnetclientclock.c (do_linear_regression): Use all + integer arithmetic. Return the minimum of the domain, which can be + set as "internal" for gst_clock_set_calibration. + (gst_net_client_clock_observe_times): Call _set_calibration. + (gst_net_client_clock_new): Call _set_calibration instead of + rate_offset. + + * check/net/gstnetclientclock.c (test_functioning): Use the right + adjustment api. + + * gst/gstclock.h: + * gst/gstclock.c (gst_clock_get_calibration) + (gst_clock_set_calibration): New functions, obsolete the ones I + added yesterday. Doh. Precision issues mean we have to extrapolate + from a point in the more recent past than 1970. + (gst_clock_get_rate_offset, gst_clock_set_rate_offset): Mark as + obsolete. + (gst_clock_adjust_unlocked): Use the right calibration data. + 2005-11-18 Edward Hervey * gst/base/gstbasesink.c: (gst_base_sink_change_state): diff --git a/check/net/gstnetclientclock.c b/check/net/gstnetclientclock.c index b058e5a..595d7e4 100644 --- a/check/net/gstnetclientclock.c +++ b/check/net/gstnetclientclock.c @@ -50,7 +50,7 @@ GST_START_TEST (test_functioning) { GstNetTimeProvider *ntp; GstClock *client, *server; - GstClockTimeDiff offset; + GstClockTime basex, basey; GstClockTime servint; //, servtime, localtime; gint port; gdouble rate; @@ -59,9 +59,9 @@ GST_START_TEST (test_functioning) fail_unless (server != NULL, "failed to get system clock"); /* move the clock ahead 100 seconds */ - gst_clock_get_rate_offset (server, &rate, &offset); - offset += 100 * GST_SECOND; - gst_clock_set_rate_offset (server, rate, offset); + gst_clock_get_calibration (server, &basex, &basey, &rate); + basey += 100 * GST_SECOND; + gst_clock_set_calibration (server, basex, basey, rate); servint = gst_clock_get_internal_time (GST_CLOCK (server)); ntp = gst_net_time_provider_new (server, "127.0.0.1", 0); diff --git a/gst/gstclock.c b/gst/gstclock.c index 7c471fa..5d4d9b4 100644 --- a/gst/gstclock.c +++ b/gst/gstclock.c @@ -604,9 +604,9 @@ gst_clock_get_resolution (GstClock * clock) * @internal: a clock time * * Converts the given @internal clock time to the real time, adjusting for the - * rate and offset set with gst_clock_set_rate_offset() and making sure that the - * returned time is increasing. This function should be called with the clock - * LOCK held and is mainly used by clock subclasses. + * 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 LOCK held and is mainly used by clock subclasses. * * Returns: the converted time of the clock. * @@ -617,25 +617,8 @@ gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal) { GstClockTime ret; - /* internal is uint64, rate is double, offset is int64, ret is uint64 */ - - ret = internal * clock->A.rate; - - if (clock->A.offset < 0) { - if ((clock->A.offset == G_MININT64 && ret <= G_MAXINT64) - || (clock->A.offset + ((gint64) ret) < 0)) - /* underflow */ - ret = 0; - else - ret -= (guint64) (-clock->A.offset); - } else { - if (clock->A.offset > 0 && ret >= G_MAXINT64 - && G_MAXUINT64 - ret - 1 <= clock->A.offset) - /* overflow, but avoiding CLOCK_TIME_NONE which is MAXUINT64 */ - ret = G_MAXUINT64 - 1; - else - ret += (guint64) clock->A.offset; - } + ret = (internal - clock->adjust) * clock->A.rate; + ret += clock->A.offset; /* make sure the time is increasing */ clock->last_time = MAX (ret, clock->last_time); @@ -734,16 +717,68 @@ gst_clock_set_time_adjust (GstClock * clock, GstClockTime adjust) * @rate: the new rate * @offset: the "initial" offset of @clock relative to its internal time * - * Adjusts the internal rate and offset of @clock. + * Adjusts the internal rate and offset of @clock. Obsolete, do not use. * - * A @rate of 1.0 is the normal speed of the clock. Values bigger than 1.0 make - * the clock go faster. @rate must be positive. + * MT safe. + */ +void +gst_clock_set_rate_offset (GstClock * clock, gdouble rate, + GstClockTimeDiff offset) +{ + g_return_if_fail (GST_IS_CLOCK (clock)); + g_return_if_fail (rate > 0.0); + + GST_LOCK (clock); + clock->A.rate = rate; + clock->A.offset = offset; + GST_UNLOCK (clock); +} + +/** + * gst_clock_get_rate_offset + * @clock: a #GstClock to adjust + * @rate: a location to store the rate + * @offset: a location to store the offset + * + * Obsolete, do not use. + * + * MT safe. + */ +void +gst_clock_get_rate_offset (GstClock * clock, gdouble * rate, + GstClockTimeDiff * offset) +{ + g_return_if_fail (GST_IS_CLOCK (clock)); + g_return_if_fail (rate != NULL); + g_return_if_fail (offset != NULL); + + GST_LOCK (clock); + *rate = clock->A.rate; + *offset = clock->A.offset; + GST_UNLOCK (clock); +} + +/** + * gst_clock_set_calibration + * @clock: a #GstClock to calibrate + * @internal: a reference internal time + * @external: a reference external time + * @rate: the rate of the clock relative to its internal time + * + * Adjusts the rate and time of @clock. A @rate of 1.0 is the normal speed of + * the clock. Values bigger than 1.0 make the clock go faster. @rate must be + * positive. + * + * @internal and @external are calibration parameters that arrange that + * gst_clock_get_time() should have been @external at internal time @internal. + * This internal time should not be in the future; that is, it should be less + * than the value of gst_clock_get_internal_time() when this function is called. * * Subsequent calls to gst_clock_get_time() will return clock times computed as * follows: * * - * time = internal_time * @rate + @offset + * time = (internal_time - @internal) * @rate + @external * * * Note that gst_clock_get_time() always returns increasing values so when you @@ -753,15 +788,19 @@ gst_clock_set_time_adjust (GstClock * clock, GstClockTime adjust) * MT safe. */ void -gst_clock_set_rate_offset (GstClock * clock, gdouble rate, - GstClockTimeDiff offset) +gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime + external, gdouble rate) { g_return_if_fail (GST_IS_CLOCK (clock)); g_return_if_fail (rate > 0.0); + g_return_if_fail (internal < gst_clock_get_internal_time (clock)); GST_LOCK (clock); + /* these need to be reworked for the api freeze break, we're really abusing + * them now */ + clock->adjust = internal; clock->A.rate = rate; - clock->A.offset = offset; + clock->A.offset = external; GST_UNLOCK (clock); } @@ -771,24 +810,24 @@ gst_clock_set_rate_offset (GstClock * clock, gdouble rate, * @rate: a location to store the rate * @offset: a location to store the offset * - * Gets the internal rate and offset of @clock. The rate and offset are relative - * to the clock's internal time. + * Gets the internal rate and reference time of @clock. See + * gst_clock_set_calibration() for more information. * * MT safe. - * - * See also: gst_clock_set_rate_offset(). */ void -gst_clock_get_rate_offset (GstClock * clock, gdouble * rate, - GstClockTimeDiff * offset) +gst_clock_get_calibration (GstClock * clock, GstClockTime * internal, + GstClockTime * external, gdouble * rate) { g_return_if_fail (GST_IS_CLOCK (clock)); g_return_if_fail (rate != NULL); - g_return_if_fail (offset != NULL); + g_return_if_fail (internal != NULL); + g_return_if_fail (external != NULL); GST_LOCK (clock); *rate = clock->A.rate; - *offset = clock->A.offset; + *external = clock->A.offset; + *internal = clock->adjust; GST_UNLOCK (clock); } diff --git a/gst/gstclock.h b/gst/gstclock.h index 9d2b910..fa22807 100644 --- a/gst/gstclock.h +++ b/gst/gstclock.h @@ -373,7 +373,7 @@ struct _GstClock { GstClockFlags flags; /*< protected >*/ /* with LOCK */ - GstClockTime adjust; /* remove me */ + GstClockTime adjust; /* rename me... */ GstClockTime last_time; GList *entries; GCond *entries_changed; @@ -423,6 +423,10 @@ void gst_clock_set_rate_offset (GstClock *clock, gdouble rate, GstClockTimeDiff offset); void gst_clock_get_rate_offset (GstClock *clock, gdouble *rate, GstClockTimeDiff *offset); +void gst_clock_set_calibration (GstClock *clock, GstClockTime internal, + GstClockTime external, gdouble rate); +void gst_clock_get_calibration (GstClock *clock, GstClockTime *internal, + GstClockTime *external, gdouble *rate); /* remove me */ void gst_clock_set_time_adjust (GstClock *clock, GstClockTime adjust); diff --git a/gst/net/gstnetclientclock.c b/gst/net/gstnetclientclock.c index 9f88300..04fdadb 100644 --- a/gst/net/gstnetclientclock.c +++ b/gst/net/gstnetclientclock.c @@ -34,7 +34,7 @@ GST_DEBUG_CATEGORY (ncc_debug); #define GST_CAT_DEFAULT (ncc_debug) -/* #define DEBUGGING_ENABLED */ +#define DEBUGGING_ENABLED #ifdef DEBUGGING_ENABLED #define DEBUG(x, args...) g_print (x "\n", ##args) @@ -236,14 +236,14 @@ gst_net_client_clock_get_property (GObject * object, guint prop_id, /* http://mathworld.wolfram.com/LeastSquaresFitting.html */ static gboolean do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, - GstClockTimeDiff * b, gdouble * r_squared) + GstClockTime * b, GstClockTime * xbase, gdouble * r_squared) { - gint64 *newx, *newy; - gint64 xbar, ybar, sxx, sxy, syy; - GstClockTime xmin, ymin; + GstClockTime *newx, *newy; + GstClockTime xmin, ymin, xbar, ybar; + GstClockTimeDiff sxx, sxy, syy; gint i; - sxx = syy = sxy = xbar = ybar = 0; + xbar = ybar = sxx = syy = sxy = 0; #ifdef DEBUGGING_ENABLED DEBUG ("doing regression on:"); @@ -251,7 +251,7 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, DEBUG (" %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, x[i], y[i]); #endif - xmin = ymin = G_MAXINT64; + xmin = ymin = G_MAXUINT64; for (i = 0; i < n; i++) { xmin = MIN (xmin, x[i]); ymin = MIN (ymin, y[i]); @@ -260,8 +260,8 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, DEBUG ("min x: %" G_GUINT64_FORMAT, xmin); DEBUG ("min y: %" G_GUINT64_FORMAT, ymin); - newx = g_new (gint64, n); - newy = g_new (gint64, n); + newx = g_new (GstClockTime, n); + newy = g_new (GstClockTime, n); /* strip off unnecessary bits of precision */ for (i = 0; i < n; i++) { @@ -287,8 +287,8 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, xbar /= n; ybar /= n; - DEBUG (" xbar = %" G_GINT64_FORMAT, xbar); - DEBUG (" ybar = %" G_GINT64_FORMAT, ybar); + 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 @@ -303,13 +303,14 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, } *m = ((double) sxy) / sxx; - *b = ((GstClockTimeDiff) (ybar + ymin)) - (GstClockTimeDiff) ((xbar + - xmin) * *m); + *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_GINT64_FORMAT, *b); - DEBUG (" r2 = %g", *r_squared); + 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); @@ -322,7 +323,7 @@ gst_net_client_clock_observe_times (GstNetClientClock * self, GstClockTime local_1, GstClockTime remote, GstClockTime local_2) { GstClockTime local_avg; - GstClockTimeDiff b; + GstClockTime b, xbase; gdouble m, r_squared; if (local_2 < local_1) @@ -344,12 +345,12 @@ gst_net_client_clock_observe_times (GstNetClientClock * self, * 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, - &r_squared); + &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_rate_offset (GST_CLOCK (self), m, b); + gst_clock_set_calibration (GST_CLOCK (self), xbase, b, m); } if (self->filling) { @@ -633,7 +634,6 @@ gst_net_client_clock_new (gchar * name, const gchar * remote_address, { GstNetClientClock *ret; GstClockTime internal; - GstClockTimeDiff offset; gint iret; g_return_val_if_fail (remote_address != NULL, NULL); @@ -653,16 +653,7 @@ gst_net_client_clock_new (gchar * name, const gchar * remote_address, /* update our internal time so get_time() give something around base_time. assume that the rate is 1 in the beginning. */ internal = gst_clock_get_internal_time (GST_CLOCK (ret)); - /* MAXINT64 + 1 so as to avoid overflow */ - if ((base_time > internal - && base_time - internal > ((guint64) G_MAXINT64) + 1) - || (base_time < internal && internal - base_time > G_MAXINT64)) - goto bad_base_time; - - offset = base_time > internal ? (base_time - internal) - : -(gint64) (internal - base_time); - - gst_clock_set_rate_offset (GST_CLOCK (ret), 1.0, offset); + gst_clock_set_calibration (GST_CLOCK (ret), internal, base_time, 1.0); { GstClockTime now = gst_clock_get_time (GST_CLOCK (ret)); @@ -684,14 +675,6 @@ gst_net_client_clock_new (gchar * name, const gchar * remote_address, /* all systems go, cap'n */ return (GstClock *) ret; -bad_base_time: - { - GST_ERROR_OBJECT (ret, "base time (%" GST_TIME_FORMAT ") too far off from " - "internal time (%" GST_TIME_FORMAT ")", GST_TIME_ARGS (base_time), - GST_TIME_ARGS (internal)); - gst_object_unref (ret); - return NULL; - } no_socket_pair: { GST_ERROR_OBJECT (ret, "no socket pair %d: %s (%d)", iret, diff --git a/libs/gst/net/gstnetclientclock.c b/libs/gst/net/gstnetclientclock.c index 9f88300..04fdadb 100644 --- a/libs/gst/net/gstnetclientclock.c +++ b/libs/gst/net/gstnetclientclock.c @@ -34,7 +34,7 @@ GST_DEBUG_CATEGORY (ncc_debug); #define GST_CAT_DEFAULT (ncc_debug) -/* #define DEBUGGING_ENABLED */ +#define DEBUGGING_ENABLED #ifdef DEBUGGING_ENABLED #define DEBUG(x, args...) g_print (x "\n", ##args) @@ -236,14 +236,14 @@ gst_net_client_clock_get_property (GObject * object, guint prop_id, /* http://mathworld.wolfram.com/LeastSquaresFitting.html */ static gboolean do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, - GstClockTimeDiff * b, gdouble * r_squared) + GstClockTime * b, GstClockTime * xbase, gdouble * r_squared) { - gint64 *newx, *newy; - gint64 xbar, ybar, sxx, sxy, syy; - GstClockTime xmin, ymin; + GstClockTime *newx, *newy; + GstClockTime xmin, ymin, xbar, ybar; + GstClockTimeDiff sxx, sxy, syy; gint i; - sxx = syy = sxy = xbar = ybar = 0; + xbar = ybar = sxx = syy = sxy = 0; #ifdef DEBUGGING_ENABLED DEBUG ("doing regression on:"); @@ -251,7 +251,7 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, DEBUG (" %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, x[i], y[i]); #endif - xmin = ymin = G_MAXINT64; + xmin = ymin = G_MAXUINT64; for (i = 0; i < n; i++) { xmin = MIN (xmin, x[i]); ymin = MIN (ymin, y[i]); @@ -260,8 +260,8 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, DEBUG ("min x: %" G_GUINT64_FORMAT, xmin); DEBUG ("min y: %" G_GUINT64_FORMAT, ymin); - newx = g_new (gint64, n); - newy = g_new (gint64, n); + newx = g_new (GstClockTime, n); + newy = g_new (GstClockTime, n); /* strip off unnecessary bits of precision */ for (i = 0; i < n; i++) { @@ -287,8 +287,8 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, xbar /= n; ybar /= n; - DEBUG (" xbar = %" G_GINT64_FORMAT, xbar); - DEBUG (" ybar = %" G_GINT64_FORMAT, ybar); + 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 @@ -303,13 +303,14 @@ do_linear_regression (GstClockTime * x, GstClockTime * y, gint n, gdouble * m, } *m = ((double) sxy) / sxx; - *b = ((GstClockTimeDiff) (ybar + ymin)) - (GstClockTimeDiff) ((xbar + - xmin) * *m); + *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_GINT64_FORMAT, *b); - DEBUG (" r2 = %g", *r_squared); + 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); @@ -322,7 +323,7 @@ gst_net_client_clock_observe_times (GstNetClientClock * self, GstClockTime local_1, GstClockTime remote, GstClockTime local_2) { GstClockTime local_avg; - GstClockTimeDiff b; + GstClockTime b, xbase; gdouble m, r_squared; if (local_2 < local_1) @@ -344,12 +345,12 @@ gst_net_client_clock_observe_times (GstNetClientClock * self, * 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, - &r_squared); + &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_rate_offset (GST_CLOCK (self), m, b); + gst_clock_set_calibration (GST_CLOCK (self), xbase, b, m); } if (self->filling) { @@ -633,7 +634,6 @@ gst_net_client_clock_new (gchar * name, const gchar * remote_address, { GstNetClientClock *ret; GstClockTime internal; - GstClockTimeDiff offset; gint iret; g_return_val_if_fail (remote_address != NULL, NULL); @@ -653,16 +653,7 @@ gst_net_client_clock_new (gchar * name, const gchar * remote_address, /* update our internal time so get_time() give something around base_time. assume that the rate is 1 in the beginning. */ internal = gst_clock_get_internal_time (GST_CLOCK (ret)); - /* MAXINT64 + 1 so as to avoid overflow */ - if ((base_time > internal - && base_time - internal > ((guint64) G_MAXINT64) + 1) - || (base_time < internal && internal - base_time > G_MAXINT64)) - goto bad_base_time; - - offset = base_time > internal ? (base_time - internal) - : -(gint64) (internal - base_time); - - gst_clock_set_rate_offset (GST_CLOCK (ret), 1.0, offset); + gst_clock_set_calibration (GST_CLOCK (ret), internal, base_time, 1.0); { GstClockTime now = gst_clock_get_time (GST_CLOCK (ret)); @@ -684,14 +675,6 @@ gst_net_client_clock_new (gchar * name, const gchar * remote_address, /* all systems go, cap'n */ return (GstClock *) ret; -bad_base_time: - { - GST_ERROR_OBJECT (ret, "base time (%" GST_TIME_FORMAT ") too far off from " - "internal time (%" GST_TIME_FORMAT ")", GST_TIME_ARGS (base_time), - GST_TIME_ARGS (internal)); - gst_object_unref (ret); - return NULL; - } no_socket_pair: { GST_ERROR_OBJECT (ret, "no socket pair %d: %s (%d)", iret, diff --git a/tests/check/libs/gstnetclientclock.c b/tests/check/libs/gstnetclientclock.c index b058e5a..595d7e4 100644 --- a/tests/check/libs/gstnetclientclock.c +++ b/tests/check/libs/gstnetclientclock.c @@ -50,7 +50,7 @@ GST_START_TEST (test_functioning) { GstNetTimeProvider *ntp; GstClock *client, *server; - GstClockTimeDiff offset; + GstClockTime basex, basey; GstClockTime servint; //, servtime, localtime; gint port; gdouble rate; @@ -59,9 +59,9 @@ GST_START_TEST (test_functioning) fail_unless (server != NULL, "failed to get system clock"); /* move the clock ahead 100 seconds */ - gst_clock_get_rate_offset (server, &rate, &offset); - offset += 100 * GST_SECOND; - gst_clock_set_rate_offset (server, rate, offset); + gst_clock_get_calibration (server, &basex, &basey, &rate); + basey += 100 * GST_SECOND; + gst_clock_set_calibration (server, basex, basey, rate); servint = gst_clock_get_internal_time (GST_CLOCK (server)); ntp = gst_net_time_provider_new (server, "127.0.0.1", 0);