rtsp-stream: Don't require address pool in the transport specific case
authorPatricia Muscalu <patricia@dovakhiin.com>
Fri, 23 Feb 2018 13:34:32 +0000 (14:34 +0100)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 14 Aug 2018 11:10:23 +0000 (14:10 +0300)
If "transport.client-settings" parameter is set to true, the client is
allowed to specify destination, ports and ttl.
There is no need for pre-configured address pool.

https://bugzilla.gnome.org/show_bug.cgi?id=793441

gst/rtsp-server/rtsp-stream.c
tests/check/gst/client.c

index 1a68fd5..0d17718 100644 (file)
@@ -1326,11 +1326,11 @@ error:
 static gboolean
 alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family,
     GSocket * socket_out[2], GstRTSPAddress ** server_addr_out,
-    gboolean multicast, GstRTSPTransport * ct)
+    gboolean multicast, GstRTSPTransport * ct, gboolean use_transport_settings)
 {
   GstRTSPStreamPrivate *priv = stream->priv;
   GSocket *rtp_socket = NULL;
-  GSocket *rtcp_socket;
+  GSocket *rtcp_socket = NULL;
   gint tmp_rtp, tmp_rtcp;
   guint count;
   GList *rejected_addresses = NULL;
@@ -1339,6 +1339,7 @@ alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family,
   GSocketAddress *rtp_sockaddr = NULL;
   GSocketAddress *rtcp_sockaddr = NULL;
   GstRTSPAddressPool *pool;
+  gboolean transport_settings_defined = FALSE;
 
   pool = priv->pool;
   count = 0;
@@ -1346,6 +1347,30 @@ alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family,
   /* Start with random port */
   tmp_rtp = 0;
 
+  if (use_transport_settings) {
+    if (!multicast)
+      goto no_mcast;
+
+    if (ct == NULL)
+      goto no_transport;
+
+    /* multicast and transport specific case */
+    if (ct->destination != NULL) {
+      tmp_rtp = ct->port.min;
+      tmp_rtcp = ct->port.max;
+      inetaddr = g_inet_address_new_from_string (ct->destination);
+      if (inetaddr == NULL)
+        goto destination_error;
+      if (!g_inet_address_get_is_multicast (inetaddr))
+        goto destination_no_mcast;
+      g_object_unref (inetaddr);
+      inetaddr = g_inet_address_new_any (family);
+
+      GST_DEBUG_OBJECT (stream, "use transport settings");
+      transport_settings_defined = TRUE;
+    }
+  }
+
   rtcp_socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM,
       G_SOCKET_PROTOCOL_UDP, NULL);
   if (!rtcp_socket)
@@ -1364,55 +1389,60 @@ again:
     g_socket_set_multicast_loopback (rtp_socket, FALSE);
   }
 
-  if ((pool && gst_rtsp_address_pool_has_unicast_addresses (pool)) || multicast) {
-    GstRTSPAddressFlags flags;
+  if (!transport_settings_defined) {
+    if ((pool && gst_rtsp_address_pool_has_unicast_addresses (pool))
+        || multicast) {
+      GstRTSPAddressFlags flags;
 
-    if (addr)
-      rejected_addresses = g_list_prepend (rejected_addresses, addr);
+      if (addr)
+        rejected_addresses = g_list_prepend (rejected_addresses, addr);
 
-    if (!pool)
-      goto no_pool;
+      if (!pool)
+        goto no_pool;
 
-    flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT;
-    if (multicast)
-      flags |= GST_RTSP_ADDRESS_FLAG_MULTICAST;
-    else
-      flags |= GST_RTSP_ADDRESS_FLAG_UNICAST;
+      flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT;
+      if (multicast)
+        flags |= GST_RTSP_ADDRESS_FLAG_MULTICAST;
+      else
+        flags |= GST_RTSP_ADDRESS_FLAG_UNICAST;
 
-    if (family == G_SOCKET_FAMILY_IPV6)
-      flags |= GST_RTSP_ADDRESS_FLAG_IPV6;
-    else
-      flags |= GST_RTSP_ADDRESS_FLAG_IPV4;
+      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 (pool, flags, 2);
+      addr = gst_rtsp_address_pool_acquire_address (pool, flags, 2);
 
-    if (addr == NULL)
-      goto no_address;
+      if (addr == NULL)
+        goto no_address;
 
-    tmp_rtp = addr->port;
+      tmp_rtp = addr->port;
 
-    g_clear_object (&inetaddr);
-    /* FIXME: Does it really work with the IP_MULTICAST_ALL socket option and
-     * socket control message set in udpsrc? */
-    if (multicast)
-      inetaddr = g_inet_address_new_any (family);
-    else
-      inetaddr = g_inet_address_new_from_string (addr->address);
-  } else {
-    if (tmp_rtp != 0) {
-      tmp_rtp += 2;
-      if (++count > 20)
-        goto no_ports;
-    }
+      g_clear_object (&inetaddr);
+      /* FIXME: Does it really work with the IP_MULTICAST_ALL socket option and
+       * socket control message set in udpsrc? */
+      if (multicast)
+        inetaddr = g_inet_address_new_any (family);
+      else
+        inetaddr = g_inet_address_new_from_string (addr->address);
+    } else {
+      if (tmp_rtp != 0) {
+        tmp_rtp += 2;
+        if (++count > 20)
+          goto no_ports;
+      }
 
-    if (inetaddr == NULL)
-      inetaddr = g_inet_address_new_any (family);
+      if (inetaddr == NULL)
+        inetaddr = g_inet_address_new_any (family);
+    }
   }
 
   rtp_sockaddr = g_inet_socket_address_new (inetaddr, tmp_rtp);
   if (!g_socket_bind (rtp_socket, rtp_sockaddr, FALSE, NULL)) {
     GST_DEBUG_OBJECT (stream, "rtp bind() failed, will try again");
     g_object_unref (rtp_sockaddr);
+    if (transport_settings_defined)
+      goto transport_settings_error;
     goto again;
   }
   g_object_unref (rtp_sockaddr);
@@ -1423,17 +1453,22 @@ again:
     goto socket_error;
   }
 
-  tmp_rtp =
-      g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (rtp_sockaddr));
-  g_object_unref (rtp_sockaddr);
-
-  /* check if port is even */
-  if ((tmp_rtp & 1) != 0) {
-    /* port not even, close and allocate another */
-    tmp_rtp++;
-    g_clear_object (&rtp_socket);
-    goto again;
+  if (!transport_settings_defined) {
+    tmp_rtp =
+        g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (rtp_sockaddr));
+
+    /* check if port is even. RFC 3550 encorages the use of an even/odd port
+     * pair, however it's not a strict requirement so this check is not done
+     * for the client selected ports. */
+    if ((tmp_rtp & 1) != 0) {
+      /* port not even, close and allocate another */
+      tmp_rtp++;
+      g_object_unref (rtp_sockaddr);
+      g_clear_object (&rtp_socket);
+      goto again;
+    }
   }
+  g_object_unref (rtp_sockaddr);
 
   /* set port */
   tmp_rtcp = tmp_rtp + 1;
@@ -1443,15 +1478,21 @@ again:
     GST_DEBUG_OBJECT (stream, "rctp bind() failed, will try again");
     g_object_unref (rtcp_sockaddr);
     g_clear_object (&rtp_socket);
+    if (transport_settings_defined)
+      goto transport_settings_error;
     goto again;
   }
   g_object_unref (rtcp_sockaddr);
 
   if (!addr) {
     addr = g_slice_new0 (GstRTSPAddress);
-    addr->address = g_inet_address_to_string (inetaddr);
     addr->port = tmp_rtp;
     addr->n_ports = 2;
+    if (transport_settings_defined)
+      addr->address = g_strdup (ct->destination);
+    else
+      addr->address = g_inet_address_to_string (inetaddr);
+    addr->ttl = ct->ttl;
   }
 
   g_clear_object (&inetaddr);
@@ -1468,6 +1509,28 @@ again:
   return TRUE;
 
   /* ERRORS */
+no_mcast:
+  {
+    GST_ERROR_OBJECT (stream, "failed to allocate UDP ports: wrong transport");
+    goto cleanup;
+  }
+no_transport:
+  {
+    GST_ERROR_OBJECT (stream, "failed to allocate UDP ports: no transport");
+    goto cleanup;
+  }
+destination_error:
+  {
+    GST_ERROR_OBJECT (stream,
+        "failed to allocate UDP ports: destination error");
+    goto cleanup;
+  }
+destination_no_mcast:
+  {
+    GST_ERROR_OBJECT (stream,
+        "failed to allocate UDP ports: destination not multicast address");
+    goto cleanup;
+  }
 no_udp_protocol:
   {
     GST_WARNING_OBJECT (stream, "failed to allocate UDP ports: protocol error");
@@ -1489,6 +1552,12 @@ no_ports:
     GST_WARNING_OBJECT (stream, "failed to allocate UDP ports: no ports");
     goto cleanup;
   }
+transport_settings_error:
+  {
+    GST_ERROR_OBJECT (stream,
+        "failed to allocate UDP ports with requested transport settings");
+    goto cleanup;
+  }
 socket_error:
   {
     GST_WARNING_OBJECT (stream, "failed to allocate UDP ports: socket error");
@@ -1563,12 +1632,13 @@ gst_rtsp_stream_allocate_udp_sockets (GstRTSPStream * stream,
       /* UDP unicast */
       GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_UDP, ipv4");
       ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4,
-          priv->socket_v4, &priv->server_addr_v4, FALSE, ct);
+          priv->socket_v4, &priv->server_addr_v4, FALSE, ct, FALSE);
     } else {
       /* multicast */
       GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_MCAST_UDP, ipv4");
       ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4,
-          priv->mcast_socket_v4, &priv->mcast_addr_v4, TRUE, ct);
+          priv->mcast_socket_v4, &priv->mcast_addr_v4, TRUE, ct,
+          use_transport_settings);
     }
   } else {
     /* IPv6 */
@@ -1576,13 +1646,14 @@ gst_rtsp_stream_allocate_udp_sockets (GstRTSPStream * stream,
       /* unicast */
       GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_UDP, ipv6");
       ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6,
-          priv->socket_v6, &priv->server_addr_v6, FALSE, ct);
+          priv->socket_v6, &priv->server_addr_v6, FALSE, ct, FALSE);
 
     } else {
       /* multicast */
       GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_MCAST_UDP, ipv6");
       ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6,
-          priv->mcast_socket_v6, &priv->mcast_addr_v6, TRUE, ct);
+          priv->mcast_socket_v6, &priv->mcast_addr_v6, TRUE, ct,
+          use_transport_settings);
     }
   }
   g_mutex_unlock (&priv->lock);
index ab95509..d4fdd9e 100644 (file)
@@ -726,7 +726,8 @@ GST_START_TEST (test_client_multicast_ignore_transport_specific)
 
 GST_END_TEST;
 
-GST_START_TEST (test_client_multicast_transport_specific)
+static void
+multicast_transport_specific (void)
 {
   GstRTSPClient *client;
   GstRTSPMessage request = { 0, };
@@ -760,7 +761,6 @@ GST_START_TEST (test_client_multicast_transport_specific)
   fail_unless (gst_rtsp_client_handle_message (client,
           &request) == GST_RTSP_OK);
   gst_rtsp_message_unset (&request);
-  expected_transport = NULL;
 
   gst_rtsp_client_set_send_func (client, test_setup_response_200_multicast,
       NULL, NULL);
@@ -769,14 +769,44 @@ GST_START_TEST (test_client_multicast_transport_specific)
   fail_unless (gst_rtsp_session_pool_get_n_sessions (session_pool) == 1);
   g_object_unref (session_pool);
 
-  send_teardown (client);
+  /* send PLAY request */
+  fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_PLAY,
+          "rtsp://localhost/test") == GST_RTSP_OK);
+  str = g_strdup_printf ("%d", cseq);
+  gst_rtsp_message_take_header (&request, GST_RTSP_HDR_CSEQ, str);
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SESSION, session_id);
+  gst_rtsp_client_set_send_func (client, test_response_200, NULL, NULL);
+  fail_unless (gst_rtsp_client_handle_message (client,
+          &request) == GST_RTSP_OK);
+  gst_rtsp_message_unset (&request);
 
+  send_teardown (client);
   teardown_client (client);
   g_object_unref (ctx.auth);
   gst_rtsp_token_unref (ctx.token);
   gst_rtsp_context_pop_current (&ctx);
 }
 
+/* CASE: multicast address requested by the client exists in the address pool */
+GST_START_TEST (test_client_multicast_transport_specific)
+{
+  expected_transport = "RTP/AVP;multicast;destination=233.252.0.1;"
+      "ttl=1;port=5000-5001;mode=\"PLAY\"";
+  multicast_transport_specific ();
+  expected_transport = NULL;
+}
+
+GST_END_TEST;
+
+/* CASE: multicast address requested by the client does not exist in the address pool */
+GST_START_TEST (test_client_multicast_transport_specific_no_address_in_pool)
+{
+  expected_transport = "RTP/AVP;multicast;destination=234.252.0.3;"
+      "ttl=1;port=6000-6001;mode=\"PLAY\"";
+  multicast_transport_specific ();
+  expected_transport = NULL;
+}
+
 GST_END_TEST;
 
 static gboolean
@@ -1061,7 +1091,8 @@ mcast_transport_specific_two_clients (gboolean shared)
   g_object_unref (thread_pool);
 }
 
-/* test if two multicast clients can choose different transport settings */
+/* test if two multicast clients can choose different transport settings
+ * CASE: media is shared */
 GST_START_TEST
     (test_client_multicast_transport_specific_two_clients_shared_media) {
   mcast_transport_specific_two_clients (TRUE);
@@ -1069,6 +1100,15 @@ GST_START_TEST
 
 GST_END_TEST;
 
+/* test if two multicast clients can choose different transport settings
+ * CASE: media is not shared */
+GST_START_TEST (test_client_multicast_transport_specific_two_clients)
+{
+  mcast_transport_specific_two_clients (FALSE);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtspclient_suite (void)
 {
@@ -1091,6 +1131,9 @@ rtspclient_suite (void)
   tcase_add_test (tc, test_client_sdp_with_no_bitrate_tags);
   tcase_add_test (tc,
       test_client_multicast_transport_specific_two_clients_shared_media);
+  tcase_add_test (tc, test_client_multicast_transport_specific_two_clients);
+  tcase_add_test (tc,
+      test_client_multicast_transport_specific_no_address_in_pool);
 
   return s;
 }