+ GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->busses = busses;
+ GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->roundtrip_limit =
+ roundtrip_limit;
+ GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->minimum_update_interval =
+ minimum_update_interval;
+
+ GST_OBJECT_UNLOCK (cache->clock);
+}
+
+static gboolean
+remove_clock_cache (GstClock * clock, GstClockTime time, GstClockID id,
+ gpointer user_data)
+{
+ ClockCache *cache = user_data;
+
+ G_LOCK (clocks_lock);
+ if (!cache->clocks) {
+ gst_clock_id_unref (cache->remove_id);
+ gst_object_unref (cache->clock);
+ clocks = g_list_remove (clocks, cache);
+ g_free (cache);
+ }
+ G_UNLOCK (clocks_lock);
+
+ return TRUE;
+}
+
+static void
+gst_net_client_clock_finalize (GObject * object)
+{
+ GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object);
+ GList *l;
+
+ if (self->priv->synced_id)
+ g_signal_handler_disconnect (self->priv->internal_clock,
+ self->priv->synced_id);
+ self->priv->synced_id = 0;
+
+ G_LOCK (clocks_lock);
+ for (l = clocks; l; l = l->next) {
+ ClockCache *cache = l->data;
+
+ if (cache->clock == self->priv->internal_clock) {
+ cache->clocks = g_list_remove (cache->clocks, self);
+
+ if (cache->clocks) {
+ update_clock_cache (cache);
+ } else {
+ GstClock *sysclock = gst_system_clock_obtain ();
+ GstClockTime time = gst_clock_get_time (sysclock) + 60 * GST_SECOND;
+
+ cache->remove_id = gst_clock_new_single_shot_id (sysclock, time);
+ gst_clock_id_wait_async (cache->remove_id, remove_clock_cache, cache,
+ NULL);
+ gst_object_unref (sysclock);
+ }
+ break;
+ }
+ }
+ G_UNLOCK (clocks_lock);
+
+ g_free (self->priv->address);
+ self->priv->address = NULL;
+
+ if (self->priv->bus != NULL) {
+ gst_object_unref (self->priv->bus);
+ self->priv->bus = NULL;
+ }
+
+ G_OBJECT_CLASS (gst_net_client_clock_parent_class)->finalize (object);
+}
+
+static void
+gst_net_client_clock_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object);
+ gboolean update = FALSE;
+
+ switch (prop_id) {
+ case PROP_ADDRESS:
+ GST_OBJECT_LOCK (self);
+ g_free (self->priv->address);
+ self->priv->address = g_value_dup_string (value);
+ if (self->priv->address == NULL)
+ self->priv->address = g_strdup (DEFAULT_ADDRESS);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_PORT:
+ GST_OBJECT_LOCK (self);
+ self->priv->port = g_value_get_int (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_ROUNDTRIP_LIMIT:
+ GST_OBJECT_LOCK (self);
+ self->priv->roundtrip_limit = g_value_get_uint64 (value);
+ GST_OBJECT_UNLOCK (self);
+ update = TRUE;
+ break;
+ case PROP_MINIMUM_UPDATE_INTERVAL:
+ GST_OBJECT_LOCK (self);
+ self->priv->minimum_update_interval = g_value_get_uint64 (value);
+ GST_OBJECT_UNLOCK (self);
+ update = TRUE;
+ break;
+ case PROP_BUS:
+ GST_OBJECT_LOCK (self);
+ if (self->priv->bus)
+ gst_object_unref (self->priv->bus);
+ self->priv->bus = g_value_dup_object (value);
+ GST_OBJECT_UNLOCK (self);
+ update = TRUE;
+ break;
+ case PROP_BASE_TIME:{
+ GstClock *clock;
+
+ self->priv->base_time = g_value_get_uint64 (value);
+ clock = gst_system_clock_obtain ();
+ self->priv->internal_base_time = gst_clock_get_time (clock);
+ gst_object_unref (clock);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ if (update && self->priv->internal_clock) {
+ GList *l;
+
+ G_LOCK (clocks_lock);
+ for (l = clocks; l; l = l->next) {
+ ClockCache *cache = l->data;
+
+ if (cache->clock == self->priv->internal_clock) {
+ update_clock_cache (cache);
+ }
+ }
+ G_UNLOCK (clocks_lock);
+ }
+}
+
+static void
+gst_net_client_clock_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object);
+
+ switch (prop_id) {
+ case PROP_ADDRESS:
+ GST_OBJECT_LOCK (self);
+ g_value_set_string (value, self->priv->address);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_PORT:
+ g_value_set_int (value, self->priv->port);
+ break;
+ case PROP_ROUNDTRIP_LIMIT:
+ GST_OBJECT_LOCK (self);
+ 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);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_BASE_TIME:
+ g_value_set_uint64 (value, self->priv->base_time);
+ break;
+ case PROP_INTERNAL_CLOCK:
+ g_value_set_object (value, self->priv->internal_clock);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_net_client_clock_synced_cb (GstClock * internal_clock, gboolean synced,
+ GstClock * self)
+{
+ gst_clock_set_synced (self, synced);
+}
+
+static void
+gst_net_client_clock_constructed (GObject * object)
+{
+ GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object);
+ GstClock *internal_clock;
+ GList *l;
+ ClockCache *cache = NULL;
+
+ G_OBJECT_CLASS (gst_net_client_clock_parent_class)->constructed (object);
+
+ G_LOCK (clocks_lock);
+ for (l = clocks; l; l = l->next) {
+ ClockCache *tmp = l->data;
+ GstNetClientInternalClock *internal_clock =
+ GST_NET_CLIENT_INTERNAL_CLOCK (tmp->clock);
+
+ if (strcmp (internal_clock->address, self->priv->address) == 0 &&
+ internal_clock->port == self->priv->port) {
+ cache = tmp;
+
+ if (cache->remove_id) {
+ gst_clock_id_unschedule (cache->remove_id);
+ cache->remove_id = NULL;
+ }
+ break;
+ }
+ }
+
+ if (!cache) {
+ cache = g_new0 (ClockCache, 1);
+
+ cache->clock =
+ g_object_new (GST_TYPE_NET_CLIENT_INTERNAL_CLOCK, "address",
+ self->priv->address, "port", self->priv->port, "is-ntp",
+ self->priv->is_ntp, NULL);
+ clocks = g_list_prepend (clocks, cache);
+
+ /* Not actually leaked but is cached for a while before being disposed,
+ * see gst_net_client_clock_finalize, so pretend it is to not confuse
+ * tests. */
+ GST_OBJECT_FLAG_SET (cache->clock, GST_OBJECT_FLAG_MAY_BE_LEAKED);
+ }
+
+ cache->clocks = g_list_prepend (cache->clocks, self);
+
+ GST_OBJECT_LOCK (cache->clock);
+ if (gst_clock_is_synced (cache->clock))
+ gst_clock_set_synced (GST_CLOCK (self), TRUE);
+ self->priv->synced_id =
+ g_signal_connect (cache->clock, "synced",
+ G_CALLBACK (gst_net_client_clock_synced_cb), self);
+ GST_OBJECT_UNLOCK (cache->clock);
+
+ G_UNLOCK (clocks_lock);
+
+ self->priv->internal_clock = internal_clock = cache->clock;
+
+ /* all systems go, cap'n */
+}
+
+static GstClockTime
+gst_net_client_clock_get_internal_time (GstClock * clock)
+{
+ GstNetClientClock *self = GST_NET_CLIENT_CLOCK (clock);
+
+ if (!gst_clock_is_synced (self->priv->internal_clock)) {
+ GstClockTime now = gst_clock_get_internal_time (self->priv->internal_clock);
+ return gst_clock_adjust_with_calibration (self->priv->internal_clock, now,
+ self->priv->internal_base_time, self->priv->base_time, 1, 1);
+ }
+
+ return gst_clock_get_time (self->priv->internal_clock);