/** * gst_net_time_packet_receive: * @fd: a file descriptor created by socket(2) * @addr: a pointer to a sockaddr to hold the address of the sender * @len: a pointer to the size of the data pointed to by @addr * * Receives a #GstNetTimePacket over a socket. Handles interrupted system calls, * but otherwise returns NULL on error. See recvfrom(2) for more information on * how to interpret @sockaddr. * * MT safe. Caller owns return value (g_free to free). * * Returns: The new #GstNetTimePacket. */ GstNetTimePacket * gst_net_time_packet_receive (gint fd, struct sockaddr * addr, socklen_t * len) { guint8 buffer[GST_NET_TIME_PACKET_SIZE]; gint ret; while (TRUE) { ret = recvfrom (fd, buffer, GST_NET_TIME_PACKET_SIZE, 0, (struct sockaddr *) addr, len); if (ret < 0) { if (errno != EAGAIN && errno != EINTR) goto receive_error; else continue; } else if (ret < GST_NET_TIME_PACKET_SIZE) { goto short_packet; } else { return gst_net_time_packet_new (buffer); } } receive_error: { GST_DEBUG ("receive error %d: %s (%d)", ret, g_strerror (errno), errno); return NULL; } short_packet: { GST_DEBUG ("someone sent us a short packet (%d < %d)", ret, GST_NET_TIME_PACKET_SIZE); return NULL; } }
/** * gst_net_time_packet_receive: * @socket: socket to receive the time packet on * @src_address: (out): address of variable to return sender address * @error: return address for a #GError, or NULL * * Receives a #GstNetTimePacket over a socket. Handles interrupted system * calls, but otherwise returns NULL on error. * * Returns: (transfer full): a new #GstNetTimePacket, or NULL on error. Free * with gst_net_time_packet_free() when done. */ GstNetTimePacket * gst_net_time_packet_receive (GSocket * socket, GSocketAddress ** src_address, GError ** error) { gchar buffer[GST_NET_TIME_PACKET_SIZE]; GError *err = NULL; gssize ret; g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); while (TRUE) { ret = g_socket_receive_from (socket, src_address, buffer, GST_NET_TIME_PACKET_SIZE, NULL, &err); if (ret < 0) { if (err->code == G_IO_ERROR_WOULD_BLOCK) { g_error_free (err); err = NULL; continue; } else { goto receive_error; } } else if (ret < GST_NET_TIME_PACKET_SIZE) { goto short_packet; } else { return gst_net_time_packet_new ((const guint8 *) buffer); } } receive_error: { GST_DEBUG ("receive error: %s", err->message); g_propagate_error (error, err); return NULL; } short_packet: { GST_DEBUG ("someone sent us a short packet (%" G_GSSIZE_FORMAT " < %d)", ret, GST_NET_TIME_PACKET_SIZE); g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "short time packet (%d < %d)", (int) ret, GST_NET_TIME_PACKET_SIZE); return NULL; } }
static gpointer gst_net_client_clock_thread (gpointer data) { GstNetClientClock *self = data; GstNetTimePacket *packet; GSocket *socket = self->priv->socket; GError *err = NULL; GstClock *clock = data; 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->priv->cancel)) { GstClockTime expiration_time = self->priv->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->priv->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"); packet = gst_net_time_packet_new (NULL); packet->local_time = gst_clock_get_internal_time (GST_CLOCK (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->priv->socket, self->priv->servaddr, NULL); g_free (packet); /* reset timeout (but are expecting a response sooner anyway) */ self->priv->timeout_expiration = gst_util_get_timestamp () + gst_clock_get_timeout (clock); } 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 (self)); 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_clock_observe_times (self, packet->local_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; }
void test_functioning() { GstNetTimeProvider *ntp; GstNetTimePacket *packet; GstClock *clock; GstClockTime local; struct sockaddr_in servaddr; gint port = -1, sockfd, ret; socklen_t len; xmlfile = "test_functioning"; std_log(LOG_FILENAME_LINE, "Test Started test_functioning"); clock = gst_system_clock_obtain (); fail_unless (clock != NULL, "failed to get system clock"); ntp = gst_net_time_provider_new (clock, "127.0.0.1", 0); fail_unless (ntp != NULL, "failed to create net time provider"); g_object_get (ntp, "port", &port, NULL); fail_unless (port > 0); sockfd = socket (AF_INET, SOCK_DGRAM, 0); fail_if (sockfd < 0, "socket failed"); memset (&servaddr, 0, sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons (port); inet_aton ("127.0.0.1", &servaddr.sin_addr); packet = gst_net_time_packet_new (NULL); fail_unless (packet != NULL, "failed to create packet"); packet->local_time = local = gst_clock_get_time (clock); len = sizeof (servaddr); ret = gst_net_time_packet_send (packet, sockfd, (struct sockaddr *) &servaddr, len); fail_unless (ret == GST_NET_TIME_PACKET_SIZE, "failed to send packet"); g_free (packet); packet = gst_net_time_packet_receive (sockfd, (struct sockaddr *) &servaddr, &len); fail_unless (packet != NULL, "failed to receive packet"); // fail_unless (packet->local_time == local, "local time is not the same"); //local time has changed fail_unless (packet->remote_time > local, "remote time not after local time"); fail_unless (packet->remote_time < gst_clock_get_time (clock), "remote time in the future"); g_free (packet); close (sockfd); //gst_object_unref (ntp); //thread is blocking gst_object_unref (clock); std_log(LOG_FILENAME_LINE, "Test Successful"); create_xml(0); }
static gpointer gst_net_client_clock_thread (gpointer data) { GstNetClientClock *self = data; GstNetTimePacket *packet; GMainContext *ctx; GSourceFuncs funcs = { NULL, }; GSource *source; GIOCondition cond; gboolean timeout; GSocket *socket = self->priv->socket; GError *err = NULL; GstClock *clock = data; GST_INFO_OBJECT (self, "net client clock thread running, socket=%p", socket); g_socket_set_blocking (socket, TRUE); g_socket_set_timeout (socket, 0); ctx = g_main_context_new (); source = g_socket_create_source (socket, G_IO_IN, self->priv->cancel); g_source_set_name (source, "GStreamer net client clock thread socket"); g_source_set_callback (source, (GSourceFunc) gst_net_client_clock_socket_cb, &cond, NULL); g_source_attach (source, ctx); g_source_unref (source); /* GSocket only support second granularity for timeouts, so roll our own * timeout source (so we don't have to create a new source whenever the * timeout changes, as we would have to do with the default timeout source) */ funcs.prepare = gst_net_client_clock_timeout_source_prepare; funcs.check = gst_net_client_clock_timeout_source_check; funcs.dispatch = gst_net_client_clock_timeout_source_dispatch; funcs.finalize = NULL; source = g_source_new (&funcs, sizeof (GstNetClientClockTimeoutSource)); ((GstNetClientClockTimeoutSource *) source)->clock = self; ((GstNetClientClockTimeoutSource *) source)->p_timeout = &timeout; g_source_set_name (source, "GStreamer net client clock timeout"); g_source_attach (source, ctx); g_source_unref (source); while (!g_cancellable_is_cancelled (self->priv->cancel)) { cond = 0; timeout = FALSE; g_main_context_iteration (ctx, TRUE); if (g_cancellable_is_cancelled (self->priv->cancel)) break; if (timeout) { /* timed out, let's send another packet */ GST_DEBUG_OBJECT (self, "timed out"); packet = gst_net_time_packet_new (NULL); packet->local_time = gst_clock_get_internal_time (GST_CLOCK (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->priv->socket, self->priv->servaddr, NULL); g_free (packet); /* reset timeout (but are expecting a response sooner anyway) */ self->priv->timeout_expiration = gst_util_get_timestamp () + gst_clock_get_timeout (clock); continue; } /* got data to read? */ if ((cond & G_IO_IN)) { GstClockTime new_local; new_local = gst_clock_get_internal_time (GST_CLOCK (self)); packet = gst_net_time_packet_receive (socket, NULL, &err); if (err != NULL) { GST_WARNING_OBJECT (self, "receive error: %s", err->message); g_error_free (err); err = NULL; continue; } 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_clock_observe_times (self, packet->local_time, packet->remote_time, new_local); g_free (packet); continue; } if ((cond & (G_IO_ERR | G_IO_HUP))) { GST_DEBUG_OBJECT (self, "socket error?! %s", g_strerror (errno)); g_usleep (G_USEC_PER_SEC / 10); continue; } } GST_INFO_OBJECT (self, "shutting down net client clock thread"); g_main_context_unref (ctx); return NULL; }
static gpointer gst_net_client_clock_thread (gpointer data) { GstNetClientClock *self = data; struct sockaddr_in tmpaddr; socklen_t len; GstNetTimePacket *packet; gint ret; GstClock *clock = data; while (TRUE) { ret = gst_net_client_clock_do_select (self); if (ret < 0 && errno == EBUSY) { GST_LOG_OBJECT (self, "stop"); goto stopped; } else if (ret == 0) { /* timed out, let's send another packet */ GST_DEBUG_OBJECT (self, "timed out"); packet = gst_net_time_packet_new (NULL); packet->local_time = gst_clock_get_internal_time (GST_CLOCK (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->priv->sock.fd, (struct sockaddr *) self->servaddr, sizeof (struct sockaddr_in)); g_free (packet); /* reset timeout */ self->current_timeout = clock->timeout; continue; } else if (gst_poll_fd_can_read (self->priv->fdset, &self->priv->sock)) { /* got data in */ GstClockTime new_local = gst_clock_get_internal_time (GST_CLOCK (self)); len = sizeof (struct sockaddr); packet = gst_net_time_packet_receive (self->priv->sock.fd, (struct sockaddr *) &tmpaddr, &len); if (!packet) goto receive_error; 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_clock_observe_times (self, packet->local_time, packet->remote_time, new_local); g_free (packet); continue; } else { GST_WARNING_OBJECT (self, "unhandled select return state?"); continue; } g_assert_not_reached (); stopped: { GST_DEBUG_OBJECT (self, "shutting down"); /* socket gets closed in _stop() */ return NULL; } receive_error: { GST_WARNING_OBJECT (self, "receive error"); continue; } g_assert_not_reached (); } g_assert_not_reached (); return NULL; }
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; }