static gpointer
gst_net_client_internal_clock_thread (gpointer data)
{
  GstNetClientInternalClock *self = data;
  GSocket *socket = self->socket;
  GError *err = NULL;

  GST_INFO_OBJECT (self, "net client clock thread running, socket=%p", socket);

  g_socket_set_blocking (socket, TRUE);
  g_socket_set_timeout (socket, 0);

  while (!g_cancellable_is_cancelled (self->cancel)) {
    GstClockTime expiration_time = self->timeout_expiration;
    GstClockTime now = gst_util_get_timestamp ();
    gint64 socket_timeout;

    if (now >= expiration_time || (expiration_time - now) <= GST_MSECOND) {
      socket_timeout = 0;
    } else {
      socket_timeout = (expiration_time - now) / GST_USECOND;
    }

    GST_TRACE_OBJECT (self, "timeout: %" G_GINT64_FORMAT "us", socket_timeout);

    if (!g_socket_condition_timed_wait (socket, G_IO_IN, socket_timeout,
            self->cancel, &err)) {
      /* cancelled, timeout or error */
      if (err->code == G_IO_ERROR_CANCELLED) {
        GST_INFO_OBJECT (self, "cancelled");
        g_clear_error (&err);
        break;
      } else if (err->code == G_IO_ERROR_TIMED_OUT) {
        /* timed out, let's send another packet */
        GST_DEBUG_OBJECT (self, "timed out");

        if (self->is_ntp) {
          GstNtpPacket *packet;

          packet = gst_ntp_packet_new (NULL, NULL);

          packet->transmit_time =
              gst_clock_get_internal_time (GST_CLOCK_CAST (self));

          GST_DEBUG_OBJECT (self,
              "sending packet, local time = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->transmit_time));

          gst_ntp_packet_send (packet, self->socket, self->servaddr, NULL);

          g_free (packet);
        } else {
          GstNetTimePacket *packet;

          packet = gst_net_time_packet_new (NULL);

          packet->local_time =
              gst_clock_get_internal_time (GST_CLOCK_CAST (self));

          GST_DEBUG_OBJECT (self,
              "sending packet, local time = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->local_time));

          gst_net_time_packet_send (packet, self->socket, self->servaddr, NULL);

          g_free (packet);
        }

        /* reset timeout (but are expecting a response sooner anyway) */
        self->timeout_expiration =
            gst_util_get_timestamp () +
            gst_clock_get_timeout (GST_CLOCK_CAST (self));
      } else {
        GST_DEBUG_OBJECT (self, "socket error: %s", err->message);
        g_usleep (G_USEC_PER_SEC / 10); /* throttle */
      }
      g_clear_error (&err);
    } else {
      GstClockTime new_local;

      /* got packet */

      new_local = gst_clock_get_internal_time (GST_CLOCK_CAST (self));

      if (self->is_ntp) {
        GstNtpPacket *packet;

        packet = gst_ntp_packet_receive (socket, NULL, &err);

        if (packet != NULL) {
          GST_LOG_OBJECT (self, "got packet back");
          GST_LOG_OBJECT (self, "local_1 = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->origin_time));
          GST_LOG_OBJECT (self, "remote_1 = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->receive_time));
          GST_LOG_OBJECT (self, "remote_2 = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->transmit_time));
          GST_LOG_OBJECT (self, "local_2 = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (new_local));
          GST_LOG_OBJECT (self, "poll_interval = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->poll_interval));

          /* Remember the last poll interval we ever got from the server */
          if (packet->poll_interval != GST_CLOCK_TIME_NONE)
            self->last_remote_poll_interval = packet->poll_interval;

          /* observe_times will reset the timeout */
          gst_net_client_internal_clock_observe_times (self,
              packet->origin_time, packet->receive_time, packet->transmit_time,
              new_local);

          g_free (packet);
        } else if (err != NULL) {
          if (g_error_matches (err, GST_NTP_ERROR, GST_NTP_ERROR_WRONG_VERSION)
              || g_error_matches (err, GST_NTP_ERROR, GST_NTP_ERROR_KOD_DENY)) {
            GST_ERROR_OBJECT (self, "fatal receive error: %s", err->message);
            g_clear_error (&err);
            break;
          } else if (g_error_matches (err, GST_NTP_ERROR,
                  GST_NTP_ERROR_KOD_RATE)) {
            GST_WARNING_OBJECT (self, "need to limit rate");

            /* If the server did not tell us a poll interval before, double
             * our minimum poll interval. Otherwise we assume that the server
             * already told us something sensible and that this error here
             * was just a spurious error */
            if (self->last_remote_poll_interval == GST_CLOCK_TIME_NONE)
              self->minimum_update_interval *= 2;

            /* And wait a bit before we send the next packet instead of
             * sending it immediately */
            self->timeout_expiration =
                gst_util_get_timestamp () +
                gst_clock_get_timeout (GST_CLOCK_CAST (self));
          } else {
            GST_WARNING_OBJECT (self, "receive error: %s", err->message);
          }
          g_clear_error (&err);
        }
      } else {
        GstNetTimePacket *packet;

        packet = gst_net_time_packet_receive (socket, NULL, &err);

        if (packet != NULL) {
          GST_LOG_OBJECT (self, "got packet back");
          GST_LOG_OBJECT (self, "local_1 = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->local_time));
          GST_LOG_OBJECT (self, "remote = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (packet->remote_time));
          GST_LOG_OBJECT (self, "local_2 = %" GST_TIME_FORMAT,
              GST_TIME_ARGS (new_local));

          /* observe_times will reset the timeout */
          gst_net_client_internal_clock_observe_times (self, packet->local_time,
              packet->remote_time, packet->remote_time, new_local);

          g_free (packet);
        } else if (err != NULL) {
          GST_WARNING_OBJECT (self, "receive error: %s", err->message);
          g_clear_error (&err);
        }
      }
    }
  }
  GST_INFO_OBJECT (self, "shutting down net client clock thread");
  return NULL;
}
Пример #2
0
int
main (int argc, char *argv[])
{
  GstNetClientInternalClock *clock;
  GstBus *bus;
  GIOChannel *channel;
  GIOStatus status;
  GError *error = NULL;
  GOptionContext *context;
  gchar *line;
  int ret = 1;

  context = g_option_context_new (NULL);
  g_option_context_add_main_entries (context, entries, NULL);
  g_option_context_add_group (context, gst_init_get_option_group ());

  if (!g_option_context_parse (context, &argc, &argv, &error)) {
    g_print ("Failed to parse options: %s\n\n", error->message);
    g_error_free (error);
    return 1;
  }

  if (input) {
    if (!(channel = g_io_channel_new_file (input, "r", NULL))) {
      g_print ("Could not read input file: %s\n", input);
      return 1;
    }
  } else {
    if (!(channel = g_io_channel_unix_new (0))) {
      g_print ("Could not read stdin");
      return 1;
    }
  }

  clock = g_object_new (GST_TYPE_NET_CLIENT_INTERNAL_CLOCK, NULL);
  bus = gst_bus_new ();

  /* FIXME: Find a way to do this without touching the structure internals */
  if (rtt_limit)
    clock->roundtrip_limit = rtt_limit * GST_MSECOND;
  clock->busses = g_list_prepend (clock->busses, bus);

  while ((status = g_io_channel_read_line (channel, &line, NULL, NULL,
              &error)) == G_IO_STATUS_NORMAL) {
    GstClockTime local_1, local_2, remote_1, remote_2;
    GstMessage *message;

    if (sscanf (line, "%" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT " %"
            G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, &local_1, &remote_1,
            &remote_2, &local_2) != 4) {
      g_print ("Failed to get local/remote time values from: %s\n", line);
      goto done;
    }

    if (debug)
      g_print ("%s", line);

    gst_net_client_internal_clock_observe_times (clock, local_1, remote_1,
        remote_2, local_2);

    g_free (line);

    if ((message = gst_bus_pop_filtered (bus, GST_MESSAGE_ELEMENT))) {
      const GstStructure *st;
      gchar *str;

      st = gst_message_get_structure (message);
      str = gst_structure_to_string (st);

      g_print ("%s\n", str);

      g_free (str);
      gst_message_unref (message);
    }
  }

  if (status == G_IO_CHANNEL_ERROR) {
    g_print ("Error reading file: %s\n", error->message);
    g_error_free (error);
    goto done;
  }

  g_io_channel_unref (channel);
  g_free (input);
  gst_object_unref (bus);

  ret = 0;

done:
  return ret;
}