diff --git a/gst/tcp/gsttcpclientsrc.c b/gst/tcp/gsttcpclientsrc.c index b007650807..d64856ed4d 100644 --- a/gst/tcp/gsttcpclientsrc.c +++ b/gst/tcp/gsttcpclientsrc.c @@ -43,18 +43,9 @@ #include "config.h" #endif -/* macOS and iOS have the .h files but the tcp_info struct is private API */ -#if defined(HAVE_NETINET_TCP_H) && defined(HAVE_NETINET_IN_H) && defined(HAVE_SYS_SOCKET_H) -#include -#include -#include -#if defined(TCP_INFO) -#define HAVE_SOCKET_METRIC_HEADERS -#endif -#endif - #include #include "gsttcpclientsrc.h" +#include "gsttcpsrcstats.h" #include "gsttcp.h" GST_DEBUG_CATEGORY_STATIC (tcpclientsrc_debug); @@ -576,36 +567,7 @@ gst_tcp_client_src_get_stats (GstTCPClientSrc * src) s = gst_structure_new ("GstTCPClientSrcStats", "bytes-received", G_TYPE_UINT64, src->bytes_received, NULL); -#ifdef HAVE_SOCKET_METRIC_HEADERS - if (src->socket) { - struct tcp_info info; - socklen_t info_len = sizeof info; - int fd; - - fd = g_socket_get_fd (src->socket); - - if (getsockopt (fd, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0) { - /* this is system-specific */ -#ifdef HAVE_BSD_TCP_INFO - gst_structure_set (s, - "reordering", G_TYPE_UINT, info.__tcpi_reordering, - "unacked", G_TYPE_UINT, info.__tcpi_unacked, - "sacked", G_TYPE_UINT, info.__tcpi_sacked, - "lost", G_TYPE_UINT, info.__tcpi_lost, - "retrans", G_TYPE_UINT, info.__tcpi_retrans, - "fackets", G_TYPE_UINT, info.__tcpi_fackets, NULL); -#elif defined(HAVE_LINUX_TCP_INFO) - gst_structure_set (s, - "reordering", G_TYPE_UINT, info.tcpi_reordering, - "unacked", G_TYPE_UINT, info.tcpi_unacked, - "sacked", G_TYPE_UINT, info.tcpi_sacked, - "lost", G_TYPE_UINT, info.tcpi_lost, - "retrans", G_TYPE_UINT, info.tcpi_retrans, - "fackets", G_TYPE_UINT, info.tcpi_fackets, NULL); -#endif - } - } -#endif + gst_tcp_stats_from_socket (s, src->socket); return s; } diff --git a/gst/tcp/gsttcpserversrc.c b/gst/tcp/gsttcpserversrc.c index 0218510cfb..328f916423 100644 --- a/gst/tcp/gsttcpserversrc.c +++ b/gst/tcp/gsttcpserversrc.c @@ -42,6 +42,7 @@ #include #include "gsttcp.h" +#include "gsttcpsrcstats.h" #include "gsttcpserversrc.h" GST_DEBUG_CATEGORY_STATIC (tcpserversrc_debug); @@ -62,7 +63,8 @@ enum PROP_0, PROP_HOST, PROP_PORT, - PROP_CURRENT_PORT + PROP_CURRENT_PORT, + PROP_STATS, }; #define gst_tcp_server_src_parent_class parent_class @@ -81,6 +83,7 @@ static void gst_tcp_server_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_tcp_server_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static GstStructure *gst_tcp_server_src_get_stats (GstTCPServerSrc * src); static void gst_tcp_server_src_class_init (GstTCPServerSrcClass * klass) @@ -123,6 +126,30 @@ gst_tcp_server_src_class_init (GstTCPServerSrcClass * klass) "The port number the socket is currently bound to", 0, TCP_HIGHEST_PORT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * GstTCPServerSrc::stats: + * + * Sends a GstStructure with statistics. We count bytes-received in a + * platform-independent way and the rest via the tcp_info struct, if it's + * available. The OS takes care of the TCP layer for us so we can't know it + * from here. + * + * Struct members: + * + * bytes-received (uint64): Total bytes received (platform-independent) + * reordering (uint): Amount of reordering (linux-specific) + * unacked (uint): Un-acked packets (linux-specific) + * sacked (uint): Selective acked packets (linux-specific) + * lost (uint): Lost packets (linux-specific) + * retrans (uint): Retransmits (linux-specific) + * fackets (uint): Forward acknowledgement (linux-specific) + * + * Since: 1.18 + */ + g_object_class_install_property (gobject_class, PROP_STATS, + g_param_spec_boxed ("stats", "Stats", "Retrieve a statistics structure", + GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + gst_element_class_add_static_pad_template (gstelement_class, &srctemplate); gst_element_class_set_static_metadata (gstelement_class, @@ -171,6 +198,8 @@ gst_tcp_server_src_finalize (GObject * gobject) g_free (src->host); src->host = NULL; + gst_clear_structure (&src->stats); + G_OBJECT_CLASS (parent_class)->finalize (gobject); } @@ -277,6 +306,7 @@ gst_tcp_server_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) ret = GST_FLOW_OK; gst_buffer_unmap (*outbuf, &map); gst_buffer_resize (*outbuf, 0, rret); + src->bytes_received += read; GST_LOG_OBJECT (src, "Returning buffer from _get of size %" G_GSIZE_FORMAT ", ts %" @@ -372,6 +402,9 @@ gst_tcp_server_src_get_property (GObject * object, guint prop_id, case PROP_CURRENT_PORT: g_value_set_int (value, g_atomic_int_get (&tcpserversrc->current_port)); break; + case PROP_STATS: + g_value_take_boxed (value, gst_tcp_server_src_get_stats (tcpserversrc)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -389,6 +422,9 @@ gst_tcp_server_src_start (GstBaseSrc * bsrc) GResolver *resolver; gint bound_port = 0; + src->bytes_received = 0; + gst_clear_structure (&src->stats); + /* look up name if we need to */ addr = g_inet_address_new_from_string (src->host); if (!addr) { @@ -516,6 +552,8 @@ gst_tcp_server_src_stop (GstBaseSrc * bsrc) if (src->client_socket) { GST_DEBUG_OBJECT (src, "closing socket"); + src->stats = gst_tcp_server_src_get_stats (src); + if (!g_socket_close (src->client_socket, &err)) { GST_ERROR_OBJECT (src, "Failed to close socket: %s", err->message); g_clear_error (&err); @@ -564,3 +602,21 @@ gst_tcp_server_src_unlock_stop (GstBaseSrc * bsrc) return TRUE; } + + +static GstStructure * +gst_tcp_server_src_get_stats (GstTCPServerSrc * src) +{ + GstStructure *s; + + /* we can't get the values post stop so just return the saved ones */ + if (src->stats) + return gst_structure_copy (src->stats); + + s = gst_structure_new ("GstTCPServerSrcStats", + "bytes-received", G_TYPE_UINT64, src->bytes_received, NULL); + + gst_tcp_stats_from_socket (s, src->client_socket); + + return s; +} diff --git a/gst/tcp/gsttcpserversrc.h b/gst/tcp/gsttcpserversrc.h index ad0a31320c..8cf5f732fd 100644 --- a/gst/tcp/gsttcpserversrc.h +++ b/gst/tcp/gsttcpserversrc.h @@ -61,6 +61,9 @@ struct _GstTCPServerSrc { GCancellable *cancellable; GSocket *server_socket; GSocket *client_socket; + + guint64 bytes_received; + GstStructure *stats; }; struct _GstTCPServerSrcClass { diff --git a/gst/tcp/gsttcpsrcstats.c b/gst/tcp/gsttcpsrcstats.c new file mode 100644 index 0000000000..d942cdcb47 --- /dev/null +++ b/gst/tcp/gsttcpsrcstats.c @@ -0,0 +1,38 @@ +/* macOS and iOS have the .h files but the tcp_info struct is private API */ +#if defined(HAVE_NETINET_TCP_H) && defined(HAVE_NETINET_IN_H) && defined(HAVE_SYS_SOCKET_H) +#include +#include +#include +#if defined(TCP_INFO) +#define HAVE_SOCKET_METRIC_HEADERS +#endif +#endif + +#include "gsttcpsrcstats.h" + +void +gst_tcp_stats_from_socket (GstStructure * structure, GSocket * socket) +{ +#ifdef HAVE_SOCKET_METRIC_HEADERS + if (getsockopt (fd, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0) { + /* this is system-specific */ +#ifdef HAVE_BSD_TCP_INFO + gst_structure_set (s, + "reordering", G_TYPE_UINT, info.__tcpi_reordering, + "unacked", G_TYPE_UINT, info.__tcpi_unacked, + "sacked", G_TYPE_UINT, info.__tcpi_sacked, + "lost", G_TYPE_UINT, info.__tcpi_lost, + "retrans", G_TYPE_UINT, info.__tcpi_retrans, + "fackets", G_TYPE_UINT, info.__tcpi_fackets, NULL); +#elif defined(HAVE_LINUX_TCP_INFO) + gst_structure_set (s, + "reordering", G_TYPE_UINT, info.tcpi_reordering, + "unacked", G_TYPE_UINT, info.tcpi_unacked, + "sacked", G_TYPE_UINT, info.tcpi_sacked, + "lost", G_TYPE_UINT, info.tcpi_lost, + "retrans", G_TYPE_UINT, info.tcpi_retrans, + "fackets", G_TYPE_UINT, info.tcpi_fackets, NULL); +#endif + } +#endif +} diff --git a/gst/tcp/gsttcpsrcstats.h b/gst/tcp/gsttcpsrcstats.h new file mode 100644 index 0000000000..5a75153acb --- /dev/null +++ b/gst/tcp/gsttcpsrcstats.h @@ -0,0 +1,4 @@ +#include +#include + +void gst_tcp_stats_from_socket (GstStructure *structure, GSocket *socket); diff --git a/gst/tcp/meson.build b/gst/tcp/meson.build index d89e2658ef..93dfb6056a 100644 --- a/gst/tcp/meson.build +++ b/gst/tcp/meson.build @@ -6,6 +6,7 @@ tcp_sources = [ 'gsttcpclientsink.c', 'gsttcpserversrc.c', 'gsttcpserversink.c', + 'gsttcpsrcstats.c', 'gsttcpplugin.c', ]