From e26b8c28327f1215be785db1754a482a936de694 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 23 May 2013 18:42:09 +0200 Subject: [PATCH] (dyn|multi)udpsink: Add properties to specify the bind address and port By default we use the any addresses and a random port for binding the socket. --- gst/udp/gstdynudpsink.c | 118 +++++++++++++++++++++++++++++++++++++--------- gst/udp/gstdynudpsink.h | 2 + gst/udp/gstmultiudpsink.c | 116 +++++++++++++++++++++++++++++++++++++-------- gst/udp/gstmultiudpsink.h | 2 + 4 files changed, 198 insertions(+), 40 deletions(-) diff --git a/gst/udp/gstdynudpsink.c b/gst/udp/gstdynudpsink.c index bf43c49..328e902 100644 --- a/gst/udp/gstdynudpsink.c +++ b/gst/udp/gstdynudpsink.c @@ -54,13 +54,17 @@ enum #define UDP_DEFAULT_SOCKET NULL #define UDP_DEFAULT_CLOSE_SOCKET TRUE +#define UDP_DEFAULT_BIND_ADDRESS NULL +#define UDP_DEFAULT_BIND_PORT 0 enum { PROP_0, PROP_SOCKET, PROP_SOCKET_V6, - PROP_CLOSE_SOCKET + PROP_CLOSE_SOCKET, + PROP_BIND_ADDRESS, + PROP_BIND_PORT }; static void gst_dynudpsink_finalize (GObject * object); @@ -121,6 +125,14 @@ gst_dynudpsink_class_init (GstDynUDPSinkClass * klass) "Close socket if passed as property on state change", UDP_DEFAULT_CLOSE_SOCKET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_BIND_ADDRESS, + g_param_spec_string ("bind-address", "Bind Address", + "Address to bind the socket to", UDP_DEFAULT_BIND_ADDRESS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_BIND_PORT, + g_param_spec_int ("bind-port", "Bind Port", + "Port to bind the socket to", 0, G_MAXUINT16, + UDP_DEFAULT_BIND_PORT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sink_template)); @@ -148,6 +160,8 @@ gst_dynudpsink_init (GstDynUDPSink * sink) sink->socket_v6 = UDP_DEFAULT_SOCKET; sink->close_socket = UDP_DEFAULT_CLOSE_SOCKET; sink->external_socket = FALSE; + sink->bind_address = UDP_DEFAULT_BIND_ADDRESS; + sink->bind_port = UDP_DEFAULT_BIND_PORT; sink->used_socket = NULL; sink->used_socket_v6 = NULL; @@ -181,6 +195,9 @@ gst_dynudpsink_finalize (GObject * object) g_object_unref (sink->used_socket_v6); sink->used_socket_v6 = NULL; + g_free (sink->bind_address); + sink->bind_address = NULL; + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -306,6 +323,13 @@ gst_dynudpsink_set_property (GObject * object, guint prop_id, case PROP_CLOSE_SOCKET: udpsink->close_socket = g_value_get_boolean (value); break; + case PROP_BIND_ADDRESS: + g_free (udpsink->bind_address); + udpsink->bind_address = g_value_dup_string (value); + break; + case PROP_BIND_PORT: + udpsink->bind_port = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -330,6 +354,12 @@ gst_dynudpsink_get_property (GObject * object, guint prop_id, GValue * value, case PROP_CLOSE_SOCKET: g_value_set_boolean (value, udpsink->close_socket); break; + case PROP_BIND_ADDRESS: + g_value_set_string (value, udpsink->bind_address); + break; + case PROP_BIND_PORT: + g_value_set_int (value, udpsink->bind_port); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -376,32 +406,70 @@ gst_dynudpsink_start (GstBaseSink * bsink) GSocketAddress *bind_addr; GInetAddress *bind_iaddr; - /* create sender sockets if none available */ - if ((udpsink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, - G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) - goto no_socket; - - bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); - bind_addr = g_inet_socket_address_new (bind_iaddr, 0); - g_socket_bind (udpsink->used_socket, bind_addr, TRUE, &err); - g_object_unref (bind_addr); - g_object_unref (bind_iaddr); - if (err != NULL) - goto bind_error; - - if ((udpsink->used_socket_v6 = g_socket_new (G_SOCKET_FAMILY_IPV6, - G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) { - GST_INFO_OBJECT (udpsink, "Failed to create IPv6 socket: %s", - err->message); - g_clear_error (&err); + if (udpsink->bind_address) { + GSocketFamily family; + + bind_iaddr = g_inet_address_new_from_string (udpsink->bind_address); + if (!bind_iaddr) { + GList *results; + GResolver *resolver; + + resolver = g_resolver_get_default (); + results = + g_resolver_lookup_by_name (resolver, udpsink->bind_address, + udpsink->cancellable, &err); + if (!results) { + g_object_unref (resolver); + goto name_resolve; + } + bind_iaddr = G_INET_ADDRESS (g_object_ref (results->data)); + g_resolver_free_addresses (results); + g_object_unref (resolver); + } + + bind_addr = g_inet_socket_address_new (bind_iaddr, udpsink->bind_port); + g_object_unref (bind_iaddr); + family = g_socket_address_get_family (G_SOCKET_ADDRESS (bind_addr)); + + if ((udpsink->used_socket = + g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, + G_SOCKET_PROTOCOL_UDP, &err)) == NULL) { + g_object_unref (bind_addr); + goto no_socket; + } + + g_socket_bind (udpsink->used_socket, bind_addr, TRUE, &err); + if (err != NULL) + goto bind_error; } else { - bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); + /* create sender sockets if none available */ + if ((udpsink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) + goto no_socket; + + bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); bind_addr = g_inet_socket_address_new (bind_iaddr, 0); - g_socket_bind (udpsink->used_socket_v6, bind_addr, TRUE, &err); + g_socket_bind (udpsink->used_socket, bind_addr, TRUE, &err); g_object_unref (bind_addr); g_object_unref (bind_iaddr); if (err != NULL) goto bind_error; + + if ((udpsink->used_socket_v6 = g_socket_new (G_SOCKET_FAMILY_IPV6, + G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, + &err)) == NULL) { + GST_INFO_OBJECT (udpsink, "Failed to create IPv6 socket: %s", + err->message); + g_clear_error (&err); + } else { + bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); + bind_addr = g_inet_socket_address_new (bind_iaddr, 0); + g_socket_bind (udpsink->used_socket_v6, bind_addr, TRUE, &err); + g_object_unref (bind_addr); + g_object_unref (bind_iaddr); + if (err != NULL) + goto bind_error; + } } } @@ -427,6 +495,14 @@ bind_error: g_clear_error (&err); return FALSE; } +name_resolve: + { + GST_ELEMENT_ERROR (udpsink, RESOURCE, FAILED, (NULL), + ("Failed to resolve bind address %s: %s", udpsink->bind_address, + err->message)); + g_clear_error (&err); + return FALSE; + } } static GstStructure * diff --git a/gst/udp/gstdynudpsink.h b/gst/udp/gstdynudpsink.h index 386e9eb..9b82eec 100644 --- a/gst/udp/gstdynudpsink.h +++ b/gst/udp/gstdynudpsink.h @@ -46,6 +46,8 @@ struct _GstDynUDPSink { /* properties */ GSocket *socket, *socket_v6; gboolean close_socket; + gchar *bind_address; + gint bind_port; /* the socket in use */ GSocket *used_socket, *used_socket_v6; diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c index 097f397..c11ff06 100644 --- a/gst/udp/gstmultiudpsink.c +++ b/gst/udp/gstmultiudpsink.c @@ -92,6 +92,8 @@ enum #define DEFAULT_QOS_DSCP -1 #define DEFAULT_SEND_DUPLICATES TRUE #define DEFAULT_BUFFER_SIZE 0 +#define DEFAULT_BIND_ADDRESS NULL +#define DEFAULT_BIND_PORT 0 enum { @@ -113,6 +115,8 @@ enum PROP_QOS_DSCP, PROP_SEND_DUPLICATES, PROP_BUFFER_SIZE, + PROP_BIND_ADDRESS, + PROP_BIND_PORT, PROP_LAST }; @@ -340,6 +344,15 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) "Size of the kernel send buffer in bytes, 0=default", 0, G_MAXINT, DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_BIND_ADDRESS, + g_param_spec_string ("bind-address", "Bind Address", + "Address to bind the socket to", DEFAULT_BIND_ADDRESS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_BIND_PORT, + g_param_spec_int ("bind-port", "Bind Port", + "Port to bind the socket to", 0, G_MAXUINT16, + DEFAULT_BIND_PORT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sink_template)); @@ -493,7 +506,12 @@ gst_multiudpsink_finalize (GObject * object) sink->multi_iface = NULL; g_free (sink->vec); + sink->vec = NULL; g_free (sink->map); + sink->map = NULL; + + g_free (sink->bind_address); + sink->bind_address = NULL; g_mutex_clear (&sink->client_lock); @@ -799,6 +817,13 @@ gst_multiudpsink_set_property (GObject * object, guint prop_id, case PROP_BUFFER_SIZE: udpsink->buffer_size = g_value_get_int (value); break; + case PROP_BIND_ADDRESS: + g_free (udpsink->bind_address); + udpsink->bind_address = g_value_dup_string (value); + break; + case PROP_BIND_PORT: + udpsink->bind_port = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -866,6 +891,12 @@ gst_multiudpsink_get_property (GObject * object, guint prop_id, GValue * value, case PROP_BUFFER_SIZE: g_value_set_int (value, udpsink->buffer_size); break; + case PROP_BIND_ADDRESS: + g_value_set_string (value, udpsink->bind_address); + break; + case PROP_BIND_PORT: + g_value_set_int (value, udpsink->bind_port); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -973,31 +1004,70 @@ gst_multiudpsink_start (GstBaseSink * bsink) GSocketAddress *bind_addr; GInetAddress *bind_iaddr; - /* create sender sockets if none available */ - if ((sink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, - G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) - goto no_socket; - - bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); - bind_addr = g_inet_socket_address_new (bind_iaddr, 0); - g_socket_bind (sink->used_socket, bind_addr, TRUE, &err); - g_object_unref (bind_addr); - g_object_unref (bind_iaddr); - if (err != NULL) - goto bind_error; - - if ((sink->used_socket_v6 = g_socket_new (G_SOCKET_FAMILY_IPV6, - G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) { - GST_INFO_OBJECT (sink, "Failed to create IPv6 socket: %s", err->message); - g_clear_error (&err); + if (sink->bind_address) { + GSocketFamily family; + + bind_iaddr = g_inet_address_new_from_string (sink->bind_address); + if (!bind_iaddr) { + GList *results; + GResolver *resolver; + + resolver = g_resolver_get_default (); + results = + g_resolver_lookup_by_name (resolver, sink->bind_address, + sink->cancellable, &err); + if (!results) { + g_object_unref (resolver); + goto name_resolve; + } + bind_iaddr = G_INET_ADDRESS (g_object_ref (results->data)); + g_resolver_free_addresses (results); + g_object_unref (resolver); + } + + bind_addr = g_inet_socket_address_new (bind_iaddr, sink->bind_port); + g_object_unref (bind_iaddr); + family = g_socket_address_get_family (G_SOCKET_ADDRESS (bind_addr)); + + if ((sink->used_socket = + g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, + G_SOCKET_PROTOCOL_UDP, &err)) == NULL) { + g_object_unref (bind_addr); + goto no_socket; + } + + g_socket_bind (sink->used_socket, bind_addr, TRUE, &err); + if (err != NULL) + goto bind_error; } else { - bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); + /* create sender sockets if none available */ + if ((sink->used_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err)) == NULL) + goto no_socket; + + bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); bind_addr = g_inet_socket_address_new (bind_iaddr, 0); - g_socket_bind (sink->used_socket_v6, bind_addr, TRUE, &err); + g_socket_bind (sink->used_socket, bind_addr, TRUE, &err); g_object_unref (bind_addr); g_object_unref (bind_iaddr); if (err != NULL) goto bind_error; + + if ((sink->used_socket_v6 = g_socket_new (G_SOCKET_FAMILY_IPV6, + G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, + &err)) == NULL) { + GST_INFO_OBJECT (sink, "Failed to create IPv6 socket: %s", + err->message); + g_clear_error (&err); + } else { + bind_iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); + bind_addr = g_inet_socket_address_new (bind_iaddr, 0); + g_socket_bind (sink->used_socket_v6, bind_addr, TRUE, &err); + g_object_unref (bind_addr); + g_object_unref (bind_iaddr); + if (err != NULL) + goto bind_error; + } } } #ifdef SO_SNDBUF @@ -1098,6 +1168,14 @@ bind_error: g_clear_error (&err); return FALSE; } +name_resolve: + { + GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL), + ("Failed to resolve bind address %s: %s", sink->bind_address, + err->message)); + g_clear_error (&err); + return FALSE; + } } static gboolean diff --git a/gst/udp/gstmultiudpsink.h b/gst/udp/gstmultiudpsink.h index cb84571..a1be566 100644 --- a/gst/udp/gstmultiudpsink.h +++ b/gst/udp/gstmultiudpsink.h @@ -83,6 +83,8 @@ struct _GstMultiUDPSink { gboolean send_duplicates; gint buffer_size; + gchar *bind_address; + gint bind_port; }; struct _GstMultiUDPSinkClass { -- 2.7.4