(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.
This commit is contained in:
Sebastian Dröge 2013-05-23 18:42:09 +02:00
parent 5b79b8ff3c
commit e26b8c2832
4 changed files with 194 additions and 36 deletions

View file

@ -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;
if (udpsink->bind_address) {
GSocketFamily family;
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;
bind_iaddr = g_inet_address_new_from_string (udpsink->bind_address);
if (!bind_iaddr) {
GList *results;
GResolver *resolver;
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);
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 *

View file

@ -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;

View file

@ -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;
if (sink->bind_address) {
GSocketFamily family;
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;
bind_iaddr = g_inet_address_new_from_string (sink->bind_address);
if (!bind_iaddr) {
GList *results;
GResolver *resolver;
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);
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

View file

@ -83,6 +83,8 @@ struct _GstMultiUDPSink {
gboolean send_duplicates;
gint buffer_size;
gchar *bind_address;
gint bind_port;
};
struct _GstMultiUDPSinkClass {