(dyn|multi)udpsink: Add properties to specify the bind address and port
authorSebastian Dröge <slomo@circular-chaos.org>
Thu, 23 May 2013 16:42:09 +0000 (18:42 +0200)
committerSebastian Dröge <slomo@circular-chaos.org>
Thu, 23 May 2013 16:42:09 +0000 (18:42 +0200)
By default we use the any addresses and a random port for binding the socket.

gst/udp/gstdynudpsink.c
gst/udp/gstdynudpsink.h
gst/udp/gstmultiudpsink.c
gst/udp/gstmultiudpsink.h

index bf43c49..328e902 100644 (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;
-
-    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 *
index 386e9eb..9b82eec 100644 (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;
index 097f397..c11ff06 100644 (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;
-
-    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
index cb84571..a1be566 100644 (file)
@@ -83,6 +83,8 @@ struct _GstMultiUDPSink {
 
   gboolean       send_duplicates;
   gint           buffer_size;
+  gchar         *bind_address;
+  gint           bind_port;
 };
 
 struct _GstMultiUDPSinkClass {