rtspstream: handle both ipv4 and ipv6 clients
authorAlexander Schrab <alexas@axis.com>
Thu, 30 May 2013 06:07:48 +0000 (08:07 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Mon, 3 Jun 2013 09:23:40 +0000 (11:23 +0200)
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=701129

gst/rtsp-server/rtsp-client.c
gst/rtsp-server/rtsp-stream.c
gst/rtsp-server/rtsp-stream.h

index 4b9d5d7..fec3ff3 100644 (file)
@@ -1133,6 +1133,8 @@ make_server_transport (GstRTSPClient * client, GstRTSPClientState * state,
     GstRTSPTransport * ct)
 {
   GstRTSPTransport *st;
+  GInetAddress *addr;
+  GSocketFamily family;
 
   /* prepare the server transport */
   gst_rtsp_transport_new (&st);
@@ -1141,10 +1143,22 @@ make_server_transport (GstRTSPClient * client, GstRTSPClientState * state,
   st->profile = ct->profile;
   st->lower_transport = ct->lower_transport;
 
+  addr = g_inet_address_new_from_string (ct->destination);
+
+  if (!addr) {
+    GST_ERROR ("failed to get inet addr from client destination");
+    family = G_SOCKET_FAMILY_IPV4;
+  } else {
+    family = g_inet_address_get_family (addr);
+    g_object_unref (addr);
+    addr = NULL;
+  }
+
   switch (st->lower_transport) {
     case GST_RTSP_LOWER_TRANS_UDP:
       st->client_port = ct->client_port;
-      gst_rtsp_stream_get_server_port (state->stream, &st->server_port);
+      gst_rtsp_stream_get_server_port (state->stream, &st->server_port, 
+          family);
       break;
     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
       st->port = ct->port;
index 38da5ef..8b8aa31 100644 (file)
@@ -36,7 +36,6 @@ struct _GstRTSPStreamPrivate
   guint idx;
   GstPad *srcpad;
   GstElement *payloader;
-  gboolean is_ipv6;
   guint buffer_size;
   gboolean is_joined;
 
@@ -48,10 +47,16 @@ struct _GstRTSPStreamPrivate
   /* the RTPSession object */
   GObject *session;
 
-  /* sinks used for sending and receiving RTP and RTCP, they share
+  /* sinks used for sending and receiving RTP and RTCP over ipv4, they share
    * sockets */
-  GstElement *udpsrc[2];
+  GstElement *udpsrc_v4[2];
+
+  /* sinks used for sending and receiving RTP and RTCP over ipv6, they share
+   * sockets */
+  GstElement *udpsrc_v6[2];
+
   GstElement *udpsink[2];
+
   /* for TCP transport */
   GstElement *appsrc[2];
   GstElement *appqueue[2];
@@ -60,9 +65,13 @@ struct _GstRTSPStreamPrivate
   GstElement *tee[2];
   GstElement *funnel[2];
 
-  /* server ports for sending/receiving */
-  GstRTSPRange server_port;
-  GstRTSPAddress *server_addr;
+  /* server ports for sending/receiving over ipv4 */
+  GstRTSPRange server_port_v4;
+  GstRTSPAddress *server_addr_v4;
+
+  /* server ports for sending/receiving over ipv6 */
+  GstRTSPRange server_port_v6;
+  GstRTSPAddress *server_addr_v6;
 
   /* multicast addresses */
   GstRTSPAddressPool *pool;
@@ -137,8 +146,10 @@ gst_rtsp_stream_finalize (GObject * obj)
 
   if (priv->addr)
     gst_rtsp_address_free (priv->addr);
-  if (priv->server_addr)
-    gst_rtsp_address_free (priv->server_addr);
+  if (priv->server_addr_v4)
+    gst_rtsp_address_free (priv->server_addr_v4);
+  if (priv->server_addr_v6)
+    gst_rtsp_address_free (priv->server_addr_v6);
   if (priv->pool)
     g_object_unref (priv->pool);
   gst_object_unref (priv->payloader);
@@ -428,11 +439,12 @@ different_address:
   }
 }
 
-/* must be called with lock */
 static gboolean
-alloc_ports (GstRTSPStream * stream)
+alloc_ports_one_family (GstRTSPAddressPool * pool, gint buffer_size,
+    GSocketFamily family, GstElement * udpsrc_out[2],
+    GstElement * udpsink_out[2], GstRTSPRange * server_port_out,
+    GstRTSPAddress ** server_addr_out)
 {
-  GstRTSPStreamPrivate *priv = stream->priv;
   GstStateChangeReturn ret;
   GstElement *udpsrc0, *udpsrc1;
   GstElement *udpsink0, *udpsink1;
@@ -443,10 +455,14 @@ alloc_ports (GstRTSPStream * stream)
   gint rtpport, rtcpport;
   GList *rejected_addresses = NULL;
   GstRTSPAddress *addr = NULL;
-  GSocketFamily family;
   GInetAddress *inetaddr = NULL;
   GSocketAddress *rtp_sockaddr = NULL;
   GSocketAddress *rtcp_sockaddr = NULL;
+  const gchar *multisink_socket = "socket";
+
+  if (family == G_SOCKET_FAMILY_IPV6) {
+    multisink_socket = "socket-v6";
+  }
 
   udpsrc0 = NULL;
   udpsrc1 = NULL;
@@ -457,19 +473,13 @@ alloc_ports (GstRTSPStream * stream)
   /* Start with random port */
   tmp_rtp = 0;
 
-  if (priv->is_ipv6) {
-    family = G_SOCKET_FAMILY_IPV6;
-  } else {
-    family = G_SOCKET_FAMILY_IPV4;
-  }
-
   rtcp_socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM,
       G_SOCKET_PROTOCOL_UDP, NULL);
   if (!rtcp_socket)
     goto no_udp_protocol;
 
-  if (priv->server_addr)
-    gst_rtsp_address_free (priv->server_addr);
+  if (*server_addr_out)
+    gst_rtsp_address_free (*server_addr_out);
 
   /* try to allocate 2 UDP ports, the RTP port should be an even
    * number and the RTCP port should be the next (uneven) port */
@@ -482,19 +492,19 @@ again:
       goto no_udp_protocol;
   }
 
-  if (priv->pool && gst_rtsp_address_pool_has_unicast_addresses (priv->pool)) {
+  if (pool && gst_rtsp_address_pool_has_unicast_addresses (pool)) {
     GstRTSPAddressFlags flags;
 
     if (addr)
       rejected_addresses = g_list_prepend (rejected_addresses, addr);
 
     flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT | GST_RTSP_ADDRESS_FLAG_UNICAST;
-    if (priv->is_ipv6)
+    if (family == G_SOCKET_FAMILY_IPV6)
       flags |= GST_RTSP_ADDRESS_FLAG_IPV6;
     else
       flags |= GST_RTSP_ADDRESS_FLAG_IPV4;
 
-    addr = gst_rtsp_address_pool_acquire_address (priv->pool, flags, 2);
+    addr = gst_rtsp_address_pool_acquire_address (pool, flags, 2);
 
     if (addr == NULL)
       goto no_ports;
@@ -576,23 +586,31 @@ again:
   if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
     goto port_error;
 
-  udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
+  if (udpsink_out[0])
+    udpsink0 = udpsink_out[0];
+  else
+    udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
+
   if (!udpsink0)
     goto no_udp_protocol;
 
   g_object_set (G_OBJECT (udpsink0), "close-socket", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink0), "socket", rtp_socket, NULL);
+  g_object_set (G_OBJECT (udpsink0), multisink_socket, rtp_socket, NULL);
+
+  if (udpsink_out[1])
+    udpsink1 = udpsink_out[1];
+  else
+    udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
 
-  udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
   if (!udpsink1)
     goto no_udp_protocol;
 
   g_object_set (G_OBJECT (udpsink0), "send-duplicates", FALSE, NULL);
   g_object_set (G_OBJECT (udpsink1), "send-duplicates", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink0), "buffer-size", priv->buffer_size, NULL);
+  g_object_set (G_OBJECT (udpsink0), "buffer-size", buffer_size, NULL);
 
   g_object_set (G_OBJECT (udpsink1), "close-socket", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink1), "socket", rtcp_socket, NULL);
+  g_object_set (G_OBJECT (udpsink1), multisink_socket, rtcp_socket, NULL);
   g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
   g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
   g_object_set (G_OBJECT (udpsink0), "auto-multicast", FALSE, NULL);
@@ -602,14 +620,14 @@ again:
 
   /* we keep these elements, we will further configure them when the
    * client told us to really use the UDP ports. */
-  priv->udpsrc[0] = udpsrc0;
-  priv->udpsrc[1] = udpsrc1;
-  priv->udpsink[0] = udpsink0;
-  priv->udpsink[1] = udpsink1;
-  priv->server_port.min = rtpport;
-  priv->server_port.max = rtcpport;
-
-  priv->server_addr = addr;
+  udpsrc_out[0] = udpsrc0;
+  udpsrc_out[1] = udpsrc1;
+  udpsink_out[0] = udpsink0;
+  udpsink_out[1] = udpsink1;
+  server_port_out->min = rtpport;
+  server_port_out->max = rtcpport;
+
+  *server_addr_out = addr;
   g_list_free_full (rejected_addresses, (GDestroyNotify) gst_rtsp_address_free);
 
   g_object_unref (rtp_socket);
@@ -670,6 +688,20 @@ cleanup:
   }
 }
 
+/* must be called with lock */
+static gboolean
+alloc_ports (GstRTSPStream * stream)
+{
+  GstRTSPStreamPrivate *priv = stream->priv;
+
+  return alloc_ports_one_family (priv->pool, priv->buffer_size,
+      G_SOCKET_FAMILY_IPV4, priv->udpsrc_v4, priv->udpsink,
+      &priv->server_port_v4, &priv->server_addr_v4) &&
+      alloc_ports_one_family (priv->pool, priv->buffer_size,
+      G_SOCKET_FAMILY_IPV6, priv->udpsrc_v6, priv->udpsink,
+      &priv->server_port_v6, &priv->server_addr_v6);
+}
+
 /**
  * gst_rtsp_stream_get_server_port:
  * @stream: a #GstRTSPStream
@@ -680,7 +712,7 @@ cleanup:
  */
 void
 gst_rtsp_stream_get_server_port (GstRTSPStream * stream,
-    GstRTSPRange * server_port)
+    GstRTSPRange * server_port, GSocketFamily family)
 {
   GstRTSPStreamPrivate *priv;
 
@@ -689,8 +721,13 @@ gst_rtsp_stream_get_server_port (GstRTSPStream * stream,
   g_return_if_fail (priv->is_joined);
 
   g_mutex_lock (&priv->lock);
-  if (server_port)
-    *server_port = priv->server_port;
+  if (family == G_SOCKET_FAMILY_IPV4) {
+    if (server_port)
+      *server_port = priv->server_port_v4;
+  } else {
+    if (server_port)
+      *server_port = priv->server_port_v6;
+  }
   g_mutex_unlock (&priv->lock);
 }
 
@@ -1104,13 +1141,23 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
 
     /* we set and keep these to playing so that they don't cause NO_PREROLL return
      * values */
-    gst_element_set_state (priv->udpsrc[i], GST_STATE_PLAYING);
-    gst_element_set_locked_state (priv->udpsrc[i], TRUE);
+    gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_PLAYING);
+    gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_PLAYING);
+    gst_element_set_locked_state (priv->udpsrc_v4[i], TRUE);
+    gst_element_set_locked_state (priv->udpsrc_v6[i], TRUE);
     /* add udpsrc */
-    gst_bin_add (bin, priv->udpsrc[i]);
-    /* and link to the funnel */
+    gst_bin_add (bin, priv->udpsrc_v4[i]);
+    gst_bin_add (bin, priv->udpsrc_v6[i]);
+    /* and link to the funnel v4 */
+    selpad = gst_element_get_request_pad (priv->funnel[i], "sink_%u");
+    pad = gst_element_get_static_pad (priv->udpsrc_v4[i], "src");
+    gst_pad_link (pad, selpad);
+    gst_object_unref (pad);
+    gst_object_unref (selpad);
+
+    /* and link to the funnel v6 */
     selpad = gst_element_get_request_pad (priv->funnel[i], "sink_%u");
-    pad = gst_element_get_static_pad (priv->udpsrc[i], "src");
+    pad = gst_element_get_static_pad (priv->udpsrc_v6[i], "src");
     gst_pad_link (pad, selpad);
     gst_object_unref (pad);
     gst_object_unref (selpad);
@@ -1213,12 +1260,15 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
     gst_element_set_state (priv->funnel[i], GST_STATE_NULL);
     gst_element_set_state (priv->appsrc[i], GST_STATE_NULL);
     /* and set udpsrc to NULL now before removing */
-    gst_element_set_locked_state (priv->udpsrc[i], FALSE);
-    gst_element_set_state (priv->udpsrc[i], GST_STATE_NULL);
+    gst_element_set_locked_state (priv->udpsrc_v4[i], FALSE);
+    gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_NULL);
+    gst_element_set_locked_state (priv->udpsrc_v6[i], FALSE);
+    gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_NULL);
 
     /* removing them should also nicely release the request
      * pads when they finalize */
-    gst_bin_remove (bin, priv->udpsrc[i]);
+    gst_bin_remove (bin, priv->udpsrc_v4[i]);
+    gst_bin_remove (bin, priv->udpsrc_v6[i]);
     gst_bin_remove (bin, priv->udpsink[i]);
     gst_bin_remove (bin, priv->appsrc[i]);
     gst_bin_remove (bin, priv->appsink[i]);
@@ -1230,7 +1280,8 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
     gst_object_unref (priv->recv_sink[i]);
     priv->recv_sink[i] = NULL;
 
-    priv->udpsrc[i] = NULL;
+    priv->udpsrc_v4[i] = NULL;
+    priv->udpsrc_v6[i] = NULL;
     priv->udpsink[i] = NULL;
     priv->appsrc[i] = NULL;
     priv->appsink[i] = NULL;
index b0512d8..f6507a9 100644 (file)
@@ -20,6 +20,7 @@
 #include <gst/gst.h>
 #include <gst/rtsp/gstrtsprange.h>
 #include <gst/rtsp/gstrtspurl.h>
+#include <gio/gio.h>
 
 #ifndef __GST_RTSP_STREAM_H__
 #define __GST_RTSP_STREAM_H__
@@ -88,7 +89,8 @@ gboolean          gst_rtsp_stream_leave_bin        (GstRTSPStream *stream,
                                                     GstBin *bin, GstElement *rtpbin);
 
 void              gst_rtsp_stream_get_server_port  (GstRTSPStream *stream,
-                                                    GstRTSPRange *server_port);
+                                                    GstRTSPRange *server_port,
+                                                    GSocketFamily family);
 void              gst_rtsp_stream_get_ssrc         (GstRTSPStream *stream,
                                                     guint *ssrc);