mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 10:40:34 +00:00
netclock: Implement rate limits for polling and fix up skew limits
Add the minimum-update-interval property to the clock, with a default of 50ms and don't send polling requests faster than that. That helps to ensure we spread the initial observations out a little - startup takes a little longer, but tracking is more stable. Move the discont skew limiting code inside an if statement, so that it's only done when the linear regression succeeds and the clock parameters might actually change.
This commit is contained in:
parent
81d613694c
commit
27997cb8a3
1 changed files with 73 additions and 37 deletions
|
@ -67,6 +67,9 @@ GST_DEBUG_CATEGORY_STATIC (ncc_debug);
|
|||
#define DEFAULT_PORT 5637
|
||||
#define DEFAULT_TIMEOUT GST_SECOND
|
||||
#define DEFAULT_ROUNDTRIP_LIMIT GST_SECOND
|
||||
/* Minimum timeout will be immediately (ie, as fast as one RTT), but no
|
||||
* more often than 1/20th second (arbitrarily, to spread observations a little) */
|
||||
#define DEFAULT_MINIMUM_UPDATE_INTERVAL (GST_SECOND / 20)
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -74,6 +77,7 @@ enum
|
|||
PROP_ADDRESS,
|
||||
PROP_PORT,
|
||||
PROP_ROUNDTRIP_LIMIT,
|
||||
PROP_MINIMUM_UPDATE_INTERVAL,
|
||||
PROP_BUS
|
||||
};
|
||||
|
||||
|
@ -91,6 +95,7 @@ struct _GstNetClientClockPrivate
|
|||
GstClockTime timeout_expiration;
|
||||
GstClockTime roundtrip_limit;
|
||||
GstClockTime rtt_avg;
|
||||
GstClockTime minimum_update_interval;
|
||||
|
||||
gchar *address;
|
||||
gint port;
|
||||
|
@ -157,6 +162,12 @@ gst_net_client_clock_class_init (GstNetClientClockClass * klass)
|
|||
"Maximum tolerable round-trip interval for packets, in nanoseconds "
|
||||
"(0 = no limit)", 0, G_MAXUINT64, DEFAULT_ROUNDTRIP_LIMIT,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_MINIMUM_UPDATE_INTERVAL,
|
||||
g_param_spec_uint64 ("minimum-update-interval", "minimum update interval",
|
||||
"Minimum polling interval for packets, in nanoseconds"
|
||||
"(0 = no limit)", 0, G_MAXUINT64, DEFAULT_MINIMUM_UPDATE_INTERVAL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -177,6 +188,7 @@ gst_net_client_clock_init (GstNetClientClock * self)
|
|||
priv->servaddr = NULL;
|
||||
priv->rtt_avg = GST_CLOCK_TIME_NONE;
|
||||
priv->roundtrip_limit = DEFAULT_ROUNDTRIP_LIMIT;
|
||||
priv->minimum_update_interval = DEFAULT_MINIMUM_UPDATE_INTERVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -235,6 +247,11 @@ gst_net_client_clock_set_property (GObject * object, guint prop_id,
|
|||
self->priv->roundtrip_limit = g_value_get_uint64 (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_MINIMUM_UPDATE_INTERVAL:
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->priv->minimum_update_interval = g_value_get_uint64 (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_BUS:
|
||||
GST_OBJECT_LOCK (self);
|
||||
if (self->priv->bus)
|
||||
|
@ -268,6 +285,11 @@ gst_net_client_clock_get_property (GObject * object, guint prop_id,
|
|||
g_value_set_uint64 (value, self->priv->roundtrip_limit);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_MINIMUM_UPDATE_INTERVAL:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_uint64 (value, self->priv->minimum_update_interval);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_BUS:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_object (value, self->priv->bus);
|
||||
|
@ -288,17 +310,19 @@ gst_net_client_clock_observe_times (GstNetClientClock * self,
|
|||
GstClockTime local_avg;
|
||||
gdouble r_squared;
|
||||
GstClock *clock;
|
||||
GstClockTime rtt, rtt_limit;
|
||||
GstClockTime rtt, rtt_limit, min_update_interval;
|
||||
GstBus *bus = NULL;
|
||||
/* Use for discont tracking */
|
||||
GstClockTime time_before = 0;
|
||||
GstClockTime min_guess = 0;
|
||||
GstClockTimeDiff time_discont;
|
||||
GstClockTimeDiff time_discont = 0;
|
||||
gboolean synched;
|
||||
GstClockTime internal_time, external_time, rate_num, rate_den;
|
||||
GstClockTime max_discont;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
rtt_limit = self->priv->roundtrip_limit;
|
||||
min_update_interval = self->priv->minimum_update_interval;
|
||||
if (self->priv->bus)
|
||||
bus = gst_object_ref (self->priv->bus);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
@ -355,56 +379,67 @@ gst_net_client_clock_observe_times (GstNetClientClock * self,
|
|||
gst_clock_adjust_with_calibration (GST_CLOCK (self), local_2,
|
||||
internal_time, external_time, rate_num, rate_den);
|
||||
|
||||
/* If the remote observation was within our min/max estimates, we're synched */
|
||||
synched = (GST_CLOCK_DIFF (remote, min_guess) < 0
|
||||
&& GST_CLOCK_DIFF (remote, time_before) > 0);
|
||||
/* Maximum discontinuity, when we're synched with the master. Could make this a property,
|
||||
* but this value seems to work fine */
|
||||
max_discont = priv->rtt_avg / 4;
|
||||
|
||||
/* If the remote observation was within 1/4 RTT of our min/max estimates, we're synched */
|
||||
synched =
|
||||
(GST_CLOCK_DIFF (remote, min_guess) < (GstClockTimeDiff) (max_discont)
|
||||
&& GST_CLOCK_DIFF (time_before,
|
||||
remote) < (GstClockTimeDiff) (max_discont));
|
||||
|
||||
if (gst_clock_add_observation_unapplied (GST_CLOCK (self), local_avg, remote,
|
||||
&r_squared, &internal_time, &external_time, &rate_num, &rate_den)) {
|
||||
|
||||
/* Now compare the difference (discont) in the clock
|
||||
* after this observation */
|
||||
time_discont = GST_CLOCK_DIFF (time_before,
|
||||
gst_clock_adjust_with_calibration (GST_CLOCK (self), local_2,
|
||||
internal_time, external_time, rate_num, rate_den));
|
||||
|
||||
/* If we were in sync with the remote clock, clamp the allowed
|
||||
* discontinuity to within quarter of one RTT. In sync means our send/receive estimates
|
||||
* of remote time correctly windowed the actual remote time observation */
|
||||
if (synched && ABS (time_discont) > max_discont) {
|
||||
GstClockTimeDiff offset;
|
||||
GST_DEBUG_OBJECT (clock,
|
||||
"Too large a discont, clamping to 1/4 average RTT = %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (max_discont));
|
||||
if (time_discont > 0) { /* Too large a forward step - add a -ve offset */
|
||||
offset = max_discont - time_discont;
|
||||
if (-offset > external_time)
|
||||
external_time = 0;
|
||||
else
|
||||
external_time += offset;
|
||||
} else { /* Too large a backward step - add a +ve offset */
|
||||
offset = -(max_discont + time_discont);
|
||||
external_time += offset;
|
||||
}
|
||||
|
||||
time_discont += offset;
|
||||
}
|
||||
|
||||
gst_clock_set_calibration (GST_CLOCK (self), internal_time, external_time,
|
||||
rate_num, rate_den);
|
||||
|
||||
/* ghetto formula - shorter timeout for bad correlations */
|
||||
current_timeout = (1e-3 / (1 - MIN (r_squared, 0.99999))) * GST_SECOND;
|
||||
current_timeout = MIN (current_timeout, gst_clock_get_timeout (clock));
|
||||
} else {
|
||||
/* No correlation, short timeout when starting up or lost sync */
|
||||
current_timeout = 0;
|
||||
}
|
||||
|
||||
/* Now compare the difference (discont) in the clock
|
||||
* after this observation */
|
||||
time_discont = GST_CLOCK_DIFF (time_before,
|
||||
gst_clock_adjust_with_calibration (GST_CLOCK (self), local_2,
|
||||
internal_time, external_time, rate_num, rate_den));
|
||||
|
||||
/* If we were in sync with the remote clock, clamp the allowed
|
||||
* discontinuity to within quarter of one RTT. In sync means our send/receive estimates
|
||||
* of remote time correctly windowed the actual remote time observation */
|
||||
if (synched && ABS (time_discont) > priv->rtt_avg / 4) {
|
||||
GstClockTimeDiff offset;
|
||||
GstClockTime max_discont = priv->rtt_avg / 4;
|
||||
GST_LOG_OBJECT (clock,
|
||||
"Too large a discont, clamping to 1/2 average RTT = %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (max_discont));
|
||||
if (time_discont > 0) { /* Too large a forward step - add a -ve offset */
|
||||
offset = max_discont - time_discont;
|
||||
if (-offset > external_time)
|
||||
external_time = 0;
|
||||
else
|
||||
external_time += offset;
|
||||
} else { /* Too large a backward step - add a +ve offset */
|
||||
offset = -(max_discont + time_discont);
|
||||
external_time += offset;
|
||||
}
|
||||
|
||||
time_discont += offset;
|
||||
}
|
||||
|
||||
gst_clock_set_calibration (GST_CLOCK (self), internal_time, external_time,
|
||||
rate_num, rate_den);
|
||||
/* Limit the polling to at most one per minimum_update_interval */
|
||||
if (rtt < min_update_interval)
|
||||
current_timeout = MAX (min_update_interval - rtt, current_timeout);
|
||||
|
||||
if (bus) {
|
||||
GstStructure *s;
|
||||
GstMessage *msg;
|
||||
|
||||
|
||||
/* Output a stats message, whether we updated the clock or not */
|
||||
s = gst_structure_new ("gst-netclock-statistics",
|
||||
"synchronised", G_TYPE_BOOLEAN, synched,
|
||||
"rtt", G_TYPE_UINT64, rtt,
|
||||
|
@ -424,6 +459,7 @@ gst_net_client_clock_observe_times (GstNetClientClock * self,
|
|||
"external-time", G_TYPE_UINT64, external_time,
|
||||
"rate-num", G_TYPE_UINT64, rate_num,
|
||||
"rate-den", G_TYPE_UINT64, rate_den,
|
||||
"rate", G_TYPE_DOUBLE, (gdouble) (rate_num) / rate_den,
|
||||
"local-clock-offset", G_TYPE_INT64, GST_CLOCK_DIFF (internal_time,
|
||||
external_time), NULL);
|
||||
msg = gst_message_new_element (GST_OBJECT (self), s);
|
||||
|
|
Loading…
Reference in a new issue