stream: Added a list of multicast client addresses
authorPatricia Muscalu <patricia@axis.com>
Thu, 26 Jul 2018 10:01:16 +0000 (12:01 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 14 Aug 2018 11:31:42 +0000 (14:31 +0300)
When media is shared, the same media stream can be sent
to multiple multicast groups. Currently, there is no API
to retrieve multicast addresses from the stream.
When calling gst_rtsp_stream_get_multicast_address() function,
only the first multicast address is returned.
With this patch, each multicast destination requested in SETUP
will be stored in an internal list (call to
gst_rtsp_stream_add_multicast_client_address()).
The list of multicast groups requested by the clients can be
retrieved by calling gst_rtsp_stream_get_multicast_client_addresses().
There still exist some problems with the current implementation
in the multicast case:
1) The receiving part is currently only configured with
regard to the first multicast client (see
https://bugzilla.gnome.org/show_bug.cgi?id=796917).
2) Secondly, of security reasons, some constraints should be
put on the requested multicast destinations (see
https://bugzilla.gnome.org/show_bug.cgi?id=796916).

Change-Id: I6b060746e472a0734cc2fd828ffe4ea2956733ea

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

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

index e89203d..e7d17ca 100644 (file)
@@ -2030,6 +2030,11 @@ default_configure_client_transport (GstRTSPClient * client,
         ct->ttl = addr->ttl;
         gst_rtsp_address_free (addr);
       }
+
+      if (!gst_rtsp_stream_add_multicast_client_address (ctx->stream,
+              ct->destination, ct->port.min, ct->port.max, family))
+        goto error_mcast_transport;
+
     } else {
       GstRTSPUrl *url;
 
@@ -2103,6 +2108,11 @@ no_socket:
     GST_ERROR_OBJECT (client, "Failed to get UDP socket");
     return FALSE;
   }
+error_mcast_transport:
+  {
+    GST_ERROR_OBJECT (client, "Failed to add multicast client transport");
+    return FALSE;
+  }
 }
 
 static GstRTSPTransport *
index 33b6ff1..65f0f5d 100644 (file)
@@ -110,6 +110,7 @@ struct _GstRTSPStreamPrivate
   GstElement *mcast_udpsink[2];
   GSocket *mcast_socket_v4[2];
   GSocket *mcast_socket_v6[2];
+  GList *mcast_clients;
 
   /* for TCP transport */
   GstElement *appsrc[2];
@@ -292,6 +293,24 @@ gst_rtsp_stream_init (GstRTSPStream * stream)
       (GDestroyNotify) gst_caps_unref);
 }
 
+typedef struct _UdpClientAddrInfo UdpClientAddrInfo;
+
+struct _UdpClientAddrInfo
+{
+  gchar *address;
+  guint rtp_port;
+  guint add_count;              /* how often this address has been added */
+};
+
+static void
+free_mcast_client (gpointer data)
+{
+  UdpClientAddrInfo *client = data;
+
+  g_free (client->address);
+  g_free (client);
+}
+
 static void
 gst_rtsp_stream_finalize (GObject * obj)
 {
@@ -338,6 +357,7 @@ gst_rtsp_stream_finalize (GObject * obj)
   }
 
   g_free (priv->multicast_iface);
+  g_list_free_full (priv->mcast_clients, (GDestroyNotify) free_mcast_client);
 
   gst_object_unref (priv->payloader);
   if (priv->srcpad)
@@ -1585,6 +1605,100 @@ cleanup:
   }
 }
 
+/* must be called with lock */
+static gboolean
+add_mcast_client_addr (GstRTSPStream * stream, const gchar * destination,
+    guint rtp_port, guint rtcp_port)
+{
+  GstRTSPStreamPrivate *priv;
+  GList *walk;
+  UdpClientAddrInfo *client;
+  GInetAddress *inet;
+
+  priv = stream->priv;
+
+  if (destination == NULL)
+    return FALSE;
+
+  inet = g_inet_address_new_from_string (destination);
+  if (inet == NULL)
+    goto invalid_address;
+
+  if (!g_inet_address_get_is_multicast (inet)) {
+    g_object_unref (inet);
+    goto invalid_address;
+  }
+  g_object_unref (inet);
+
+  for (walk = priv->mcast_clients; walk; walk = g_list_next (walk)) {
+    UdpClientAddrInfo *cli = walk->data;
+
+    if ((g_strcmp0 (cli->address, destination) == 0) &&
+        (cli->rtp_port == rtp_port)) {
+      GST_DEBUG ("requested destination already exists: %s:%u-%u",
+          destination, rtp_port, rtcp_port);
+      cli->add_count++;
+      return TRUE;
+    }
+  }
+
+  client = g_new0 (UdpClientAddrInfo, 1);
+  client->address = g_strdup (destination);
+  client->rtp_port = rtp_port;
+  client->add_count = 1;
+  priv->mcast_clients = g_list_prepend (priv->mcast_clients, client);
+
+  GST_DEBUG ("added mcast client %s:%u-%u", destination, rtp_port, rtcp_port);
+
+  return TRUE;
+
+invalid_address:
+  {
+    GST_WARNING_OBJECT (stream, "Multicast address is invalid: %s",
+        destination);
+    return FALSE;
+  }
+}
+
+/* must be called with lock */
+static gboolean
+remove_mcast_client_addr (GstRTSPStream * stream, const gchar * destination,
+    guint rtp_port, guint rtcp_port)
+{
+  GstRTSPStreamPrivate *priv;
+  GList *walk;
+
+  priv = stream->priv;
+
+  if (destination == NULL)
+    goto no_destination;
+
+  for (walk = priv->mcast_clients; walk; walk = g_list_next (walk)) {
+    UdpClientAddrInfo *cli = walk->data;
+
+    if ((g_strcmp0 (cli->address, destination) == 0) &&
+        (cli->rtp_port == rtp_port)) {
+      cli->add_count--;
+
+      if (!cli->add_count) {
+        priv->mcast_clients = g_list_remove (priv->mcast_clients, cli);
+        free_mcast_client (cli);
+      }
+      return TRUE;
+    }
+  }
+
+  GST_WARNING_OBJECT (stream, "Address not found");
+  return FALSE;
+
+no_destination:
+  {
+    GST_WARNING_OBJECT (stream, "No destination has been provided");
+    return FALSE;
+  }
+}
+
+
 /**
  * gst_rtsp_stream_allocate_udp_sockets:
  * @stream: a #GstRTSPStream
@@ -3368,38 +3482,29 @@ udpsrc_error:
 }
 
 static gboolean
-check_mcast_part_for_transport (GstRTSPStream * stream,
-    const GstRTSPTransport * tr)
+check_mcast_client_addr (GstRTSPStream * stream, const GstRTSPTransport * tr)
 {
   GstRTSPStreamPrivate *priv = stream->priv;
-  GInetAddress *inetaddr;
-  GSocketFamily family;
-  GstRTSPAddress *mcast_addr;
+  GList *walk;
 
-  /* Check if it's a ipv4 or ipv6 transport */
-  inetaddr = g_inet_address_new_from_string (tr->destination);
-  family = g_inet_address_get_family (inetaddr);
-  g_object_unref (inetaddr);
+  if (priv->mcast_clients == NULL)
+    goto no_addr;
 
-  /* Select fields corresponding to the family */
-  if (family == G_SOCKET_FAMILY_IPV4) {
-    mcast_addr = priv->mcast_addr_v4;
-  } else {
-    mcast_addr = priv->mcast_addr_v6;
-  }
+  if (tr == NULL)
+    goto no_transport;
 
-  /* We support only one mcast group per family, make sure this transport
-   * matches it. */
-  if (!mcast_addr)
-    goto no_addr;
+  if (tr->destination == NULL)
+    goto no_destination;
 
-  if (g_ascii_strcasecmp (tr->destination, mcast_addr->address) != 0 ||
-      tr->port.min != mcast_addr->port ||
-      tr->port.max != mcast_addr->port + mcast_addr->n_ports - 1 ||
-      tr->ttl != mcast_addr->ttl)
-    goto wrong_addr;
+  for (walk = priv->mcast_clients; walk; walk = g_list_next (walk)) {
+    UdpClientAddrInfo *cli = walk->data;
 
-  return TRUE;
+    if ((g_strcmp0 (cli->address, tr->destination) == 0) &&
+        (cli->rtp_port == tr->port.min))
+      return TRUE;
+  }
+
+  return FALSE;
 
 no_addr:
   {
@@ -3407,7 +3512,13 @@ no_addr:
         "has been reserved");
     return FALSE;
   }
-wrong_addr:
+no_transport:
+  {
+    GST_WARNING_OBJECT (stream, "Adding mcast transport, but no transport "
+        "has been provided");
+    return FALSE;
+  }
+no_destination:
   {
     GST_WARNING_OBJECT (stream, "Adding mcast transport, but it doesn't match "
         "the reserved address");
@@ -4084,8 +4195,10 @@ update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans,
 
       if (add) {
         GST_INFO ("adding %s:%d-%d", dest, min, max);
-        if (!check_mcast_part_for_transport (stream, tr))
+        if (!check_mcast_client_addr (stream, tr))
           goto mcast_error;
+        add_client (priv->mcast_udpsink[0], priv->mcast_udpsink[1], dest, min,
+            max);
 
         if (tr->ttl > 0) {
           GST_INFO ("setting ttl-mc %d", tr->ttl);
@@ -4096,11 +4209,12 @@ update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans,
             g_object_set (G_OBJECT (priv->mcast_udpsink[1]), "ttl-mc", tr->ttl,
                 NULL);
         }
-        add_client (priv->mcast_udpsink[0], priv->mcast_udpsink[1], dest, min,
-            max);
         priv->transports = g_list_prepend (priv->transports, trans);
       } else {
         GST_INFO ("removing %s:%d-%d", dest, min, max);
+        if (!remove_mcast_client_addr (stream, dest, min, max))
+          GST_WARNING_OBJECT (stream,
+              "Failed to remove multicast address: %s:%d-%d", dest, min, max);
         remove_client (priv->mcast_udpsink[0], priv->mcast_udpsink[1], dest,
             min, max);
         priv->transports = g_list_remove (priv->transports, trans);
@@ -4451,6 +4565,95 @@ gst_rtsp_stream_get_rtcp_multicast_socket (GstRTSPStream * stream,
 }
 
 /**
+ * gst_rtsp_stream_add_multicast_client_address:
+ * @stream: a #GstRTSPStream
+ * @destination: (transfer none): a multicast address to add
+ * @rtp_port: RTP port
+ * @rtcp_port: RTCP port
+ * @family: socket family
+ *
+ * Add multicast client address to stream. At this point, the sockets that
+ * will stream RTP and RTCP data to @destination are supposed to be
+ * allocated.
+ *
+ * Returns: %TRUE if @destination can be addedd and handled by @stream.
+ */
+gboolean
+gst_rtsp_stream_add_multicast_client_address (GstRTSPStream * stream,
+    const gchar * destination, guint rtp_port, guint rtcp_port,
+    GSocketFamily family)
+{
+  GstRTSPStreamPrivate *priv;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+  g_return_val_if_fail (destination != NULL, FALSE);
+
+  priv = stream->priv;
+  g_mutex_lock (&priv->lock);
+  if ((family == G_SOCKET_FAMILY_IPV4) && (priv->mcast_socket_v4[0] == NULL))
+    goto socket_error;
+  else if ((family == G_SOCKET_FAMILY_IPV6) &&
+      (priv->mcast_socket_v6[0] == NULL))
+    goto socket_error;
+
+  if (!add_mcast_client_addr (stream, destination, rtp_port, rtcp_port))
+    goto add_addr_error;
+  g_mutex_unlock (&priv->lock);
+
+  return TRUE;
+
+socket_error:
+  {
+    GST_WARNING_OBJECT (stream,
+        "Failed to add multicast address: no udp socket");
+    g_mutex_unlock (&priv->lock);
+    return FALSE;
+  }
+add_addr_error:
+  {
+    GST_WARNING_OBJECT (stream,
+        "Failed to add multicast address: invalid address");
+    g_mutex_unlock (&priv->lock);
+    return FALSE;
+  }
+}
+
+/**
+ * gst_rtsp_stream_get_multicast_client_addresses
+ * @stream: a #GstRTSPStream
+ *
+ * Get all multicast client addresses that RTP data will be sent to
+ *
+ * Returns: A comma separated list of host:port pairs with destinations
+ */
+gchar *
+gst_rtsp_stream_get_multicast_client_addresses (GstRTSPStream * stream)
+{
+  GstRTSPStreamPrivate *priv;
+  GString *str;
+  GList *clients;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+
+  priv = stream->priv;
+  str = g_string_new ("");
+
+  g_mutex_lock (&priv->lock);
+  clients = priv->mcast_clients;
+  while (clients != NULL) {
+    UdpClientAddrInfo *client;
+
+    client = (UdpClientAddrInfo *) clients->data;
+    clients = g_list_next (clients);
+    g_string_append_printf (str, "%s:%d%s", client->address, client->rtp_port,
+        (clients != NULL ? "," : ""));
+  }
+  g_mutex_unlock (&priv->lock);
+
+  return g_string_free (str, FALSE);
+}
+
+/**
  * gst_rtsp_stream_set_seqnum:
  * @stream: a #GstRTSPStream
  * @seqnum: a new sequence number
index ea21858..0c6804d 100644 (file)
@@ -234,6 +234,16 @@ GSocket *         gst_rtsp_stream_get_rtcp_multicast_socket (GstRTSPStream *stre
                                                              GSocketFamily family);
 
 GST_RTSP_SERVER_API
+gboolean          gst_rtsp_stream_add_multicast_client_address (GstRTSPStream * stream,
+                                                                const gchar * destination,
+                                                                guint rtp_port,
+                                                                guint rtcp_port,
+                                                                GSocketFamily family);
+
+GST_RTSP_SERVER_API
+gchar *           gst_rtsp_stream_get_multicast_client_addresses (GstRTSPStream * stream);
+
+GST_RTSP_SERVER_API
 gboolean          gst_rtsp_stream_update_crypto    (GstRTSPStream * stream,
                                                     guint ssrc, GstCaps * crypto);
 
index a420f65..d890b82 100644 (file)
@@ -980,9 +980,10 @@ GST_START_TEST (test_client_sdp_with_no_bitrate_tags)
 GST_END_TEST;
 
 static void
-mcast_transport_specific_two_clients (gboolean shared, const gchar * transport1,
-    const gchar * expected_transport1, const gchar * transport2,
-    const gchar * expected_transport2)
+mcast_transport_two_clients (gboolean shared, const gchar * transport1,
+    const gchar * expected_transport1, const gchar * addr1,
+    const gchar * transport2, const gchar * expected_transport2,
+    const gchar * addr2)
 {
   GstRTSPClient *client1, *client2;
   GstRTSPMessage request = { 0, };
@@ -995,6 +996,7 @@ mcast_transport_specific_two_clients (gboolean shared, const gchar * transport1,
   GstRTSPAddressPool *address_pool;
   GstRTSPThreadPool *thread_pool;
   gchar *session_id1;
+  gchar *client_addr = NULL;
 
   mount_points = gst_rtsp_mount_points_new ();
   factory = gst_rtsp_media_factory_new ();
@@ -1054,6 +1056,13 @@ mcast_transport_specific_two_clients (gboolean shared, const gchar * transport1,
   fail_unless (gst_rtsp_client_handle_message (client1,
           &request) == GST_RTSP_OK);
   gst_rtsp_message_unset (&request);
+
+  /* check address */
+  client_addr = gst_rtsp_stream_get_multicast_client_addresses (ctx.stream);
+  fail_if (client_addr == NULL);
+  fail_unless (g_str_equal (client_addr, addr1));
+  g_free (client_addr);
+
   gst_rtsp_context_pop_current (&ctx);
   session_id1 = session_id;
 
@@ -1099,6 +1108,22 @@ mcast_transport_specific_two_clients (gboolean shared, const gchar * transport1,
           &request) == GST_RTSP_OK);
   gst_rtsp_message_unset (&request);
 
+  /* check addresses */
+  client_addr = gst_rtsp_stream_get_multicast_client_addresses (ctx2.stream);
+  fail_if (client_addr == NULL);
+  if (shared) {
+    if (g_str_equal (addr1, addr2)) {
+      fail_unless (g_str_equal (client_addr, addr1));
+    } else {
+      gchar *addr_str = g_strdup_printf ("%s,%s", addr2, addr1);
+      fail_unless (g_str_equal (client_addr, addr_str));
+      g_free (addr_str);
+    }
+  } else {
+    fail_unless (g_str_equal (client_addr, addr2));
+  }
+  g_free (client_addr);
+
   send_teardown (client2);
   gst_rtsp_context_pop_current (&ctx2);
 
@@ -1126,13 +1151,16 @@ GST_START_TEST
   const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;"
       "ttl=1;port=5000-5001;mode=\"PLAY\"";
   const gchar *expected_transport_1 = transport_client_1;
+  const gchar *addr_client_1 = "233.252.0.1:5000";
 
   const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;"
       "ttl=1;port=5002-5003;mode=\"PLAY\"";
   const gchar *expected_transport_2 = transport_client_2;
+  const gchar *addr_client_2 = "233.252.0.2:5002";
 
-  mcast_transport_specific_two_clients (TRUE, transport_client_1,
-      expected_transport_1, transport_client_2, expected_transport_2);
+  mcast_transport_two_clients (TRUE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
 }
 
 GST_END_TEST;
@@ -1144,13 +1172,105 @@ GST_START_TEST (test_client_multicast_transport_specific_two_clients)
   const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;"
       "ttl=1;port=5000-5001;mode=\"PLAY\"";
   const gchar *expected_transport_1 = transport_client_1;
+  const gchar *addr_client_1 = "233.252.0.1:5000";
 
   const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;"
       "ttl=1;port=5002-5003;mode=\"PLAY\"";
   const gchar *expected_transport_2 = transport_client_2;
+  const gchar *addr_client_2 = "233.252.0.2:5002";
+
+  mcast_transport_two_clients (FALSE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
+}
+
+GST_END_TEST;
+
+/* test if two multicast clients can choose the same transport settings.
+ * CASE: media is shared */
+GST_START_TEST
+    (test_client_multicast_transport_specific_two_clients_shared_media_same_transport)
+{
+
+  const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;"
+      "ttl=1;port=5000-5001;mode=\"PLAY\"";
+  const gchar *expected_transport_1 = transport_client_1;
+  const gchar *addr_client_1 = "233.252.0.1:5000";
+
+  const gchar *transport_client_2 = transport_client_1;
+  const gchar *expected_transport_2 = expected_transport_1;
+  const gchar *addr_client_2 = addr_client_1;
+
+  mcast_transport_two_clients (TRUE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
+}
+
+GST_END_TEST;
+
+/* test if two multicast clients get the same transport settings without
+ * requesting specific transport.
+ * CASE: media is shared */
+GST_START_TEST (test_client_multicast_two_clients_shared_media)
+{
+  const gchar *transport_client_1 = "RTP/AVP;multicast;mode=\"PLAY\"";
+  const gchar *expected_transport_1 =
+      "RTP/AVP;multicast;destination=233.252.0.1;"
+      "ttl=1;port=5000-5001;mode=\"PLAY\"";
+  const gchar *addr_client_1 = "233.252.0.1:5000";
+
+  const gchar *transport_client_2 = transport_client_1;
+  const gchar *expected_transport_2 = expected_transport_1;
+  const gchar *addr_client_2 = addr_client_1;
+
+  mcast_transport_two_clients (TRUE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
+}
+
+GST_END_TEST;
+
+/* test if two multicast clients get the different transport settings: the first client 
+ * requests the specific transport configuration while the second client lets
+ * the server select the multicast address and the ports.
+ * CASE: media is shared */
+GST_START_TEST
+    (test_client_multicast_two_clients_first_specific_transport_shared_media) {
+  const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;"
+      "ttl=1;port=5000-5001;mode=\"PLAY\"";
+  const gchar *expected_transport_1 = transport_client_1;
+  const gchar *addr_client_1 = "233.252.0.1:5000";
+
+  const gchar *transport_client_2 = "RTP/AVP;multicast;mode=\"PLAY\"";
+  const gchar *expected_transport_2 = expected_transport_1;
+  const gchar *addr_client_2 = addr_client_1;
+
+  mcast_transport_two_clients (TRUE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
+}
+
+GST_END_TEST;
+/* test if two multicast clients get the different transport settings: the first client lets
+ * the server select the multicast address and the ports while the second client requests 
+ * the specific transport configuration.
+ * CASE: media is shared */
+GST_START_TEST
+    (test_client_multicast_two_clients_second_specific_transport_shared_media) {
+  const gchar *transport_client_1 = "RTP/AVP;multicast;mode=\"PLAY\"";
+  const gchar *expected_transport_1 =
+      "RTP/AVP;multicast;destination=233.252.0.1;"
+      "ttl=1;port=5000-5001;mode=\"PLAY\"";
+  const gchar *addr_client_1 = "233.252.0.1:5000";
+
+  const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;"
+      "ttl=2;port=5004-5005;mode=\"PLAY\"";
+  const gchar *expected_transport_2 = transport_client_2;
+  const gchar *addr_client_2 = "233.252.0.2:5004";
 
-  mcast_transport_specific_two_clients (FALSE, transport_client_1,
-      expected_transport_1, transport_client_2, expected_transport_2);
+  mcast_transport_two_clients (TRUE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
 }
 
 GST_END_TEST;
@@ -1162,15 +1282,18 @@ GST_START_TEST (test_client_multicast_max_ttl_first_client)
   const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;"
       "ttl=3;port=5000-5001;mode=\"PLAY\"";
   const gchar *expected_transport_1 = transport_client_1;
+  const gchar *addr_client_1 = "233.252.0.1:5000";
 
   const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;"
       "ttl=1;port=5002-5003;mode=\"PLAY\"";
   const gchar *expected_transport_2 =
       "RTP/AVP;multicast;destination=233.252.0.2;"
       "ttl=3;port=5002-5003;mode=\"PLAY\"";
+  const gchar *addr_client_2 = "233.252.0.2:5002";
 
-  mcast_transport_specific_two_clients (TRUE, transport_client_1,
-      expected_transport_1, transport_client_2, expected_transport_2);
+  mcast_transport_two_clients (TRUE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
 }
 
 GST_END_TEST;
@@ -1182,13 +1305,16 @@ GST_START_TEST (test_client_multicast_max_ttl_second_client)
   const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;"
       "ttl=2;port=5000-5001;mode=\"PLAY\"";
   const gchar *expected_transport_1 = transport_client_1;
+  const gchar *addr_client_1 = "233.252.0.1:5000";
 
   const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;"
       "ttl=4;port=5002-5003;mode=\"PLAY\"";
   const gchar *expected_transport_2 = transport_client_2;
+  const gchar *addr_client_2 = "233.252.0.2:5002";
 
-  mcast_transport_specific_two_clients (TRUE, transport_client_1,
-      expected_transport_1, transport_client_2, expected_transport_2);
+  mcast_transport_two_clients (TRUE, transport_client_1,
+      expected_transport_1, addr_client_1, transport_client_2,
+      expected_transport_2, addr_client_2);
 }
 
 GST_END_TEST;
@@ -1260,6 +1386,13 @@ rtspclient_suite (void)
       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_two_clients_shared_media_same_transport);
+  tcase_add_test (tc, test_client_multicast_two_clients_shared_media);
+  tcase_add_test (tc,
+      test_client_multicast_two_clients_first_specific_transport_shared_media);
+  tcase_add_test (tc,
+      test_client_multicast_two_clients_second_specific_transport_shared_media);
+  tcase_add_test (tc,
       test_client_multicast_transport_specific_no_address_in_pool);
   tcase_add_test (tc, test_client_multicast_max_ttl_first_client);
   tcase_add_test (tc, test_client_multicast_max_ttl_second_client);
index 7b59c0a..d77180e 100644 (file)
@@ -486,6 +486,93 @@ GST_START_TEST (test_tcp_transport)
 
 GST_END_TEST;
 
+static void
+check_multicast_client_address (const gchar * destination, guint port,
+    const gchar * expected_addr_str, gboolean expected_res)
+{
+  GstPad *srcpad;
+  GstElement *pay;
+  GstRTSPStream *stream;
+  GstBin *bin;
+  GstElement *rtpbin;
+  GstRTSPTransport *transport;
+  GstRTSPRange ports = { 0 };
+  gchar *addr_str = NULL;
+
+  srcpad = gst_pad_new ("testsrcpad", GST_PAD_SRC);
+  fail_unless (srcpad != NULL);
+  gst_pad_set_active (srcpad, TRUE);
+  pay = gst_element_factory_make ("rtpgstpay", "testpayloader");
+  fail_unless (pay != NULL);
+  stream = gst_rtsp_stream_new (0, pay, srcpad);
+  fail_unless (stream != NULL);
+  gst_object_unref (pay);
+  gst_object_unref (srcpad);
+  rtpbin = gst_element_factory_make ("rtpbin", "testrtpbin");
+  fail_unless (rtpbin != NULL);
+  bin = GST_BIN (gst_bin_new ("testbin"));
+  fail_unless (bin != NULL);
+  fail_unless (gst_bin_add (bin, rtpbin));
+
+  fail_unless (gst_rtsp_stream_join_bin (stream, bin, rtpbin, GST_STATE_NULL));
+
+  fail_unless (gst_rtsp_transport_new (&transport) == GST_RTSP_OK);
+  transport->lower_transport = GST_RTSP_LOWER_TRANS_UDP_MCAST;
+  transport->destination = g_strdup (destination);
+  transport->ttl = 1;
+  ports.min = port;
+  ports.max = port + 1;
+  transport->port = ports;
+
+  /* allocate ports */
+  fail_unless (gst_rtsp_stream_allocate_udp_sockets (stream,
+          G_SOCKET_FAMILY_IPV4, transport, TRUE) == expected_res);
+
+  fail_unless (gst_rtsp_stream_add_multicast_client_address (stream,
+          destination, ports.min, ports.max, G_SOCKET_FAMILY_IPV4) == expected_res);
+
+  fail_unless (gst_rtsp_stream_complete_stream (stream, transport) == expected_res);
+
+  fail_unless (gst_rtsp_transport_free (transport) == GST_RTSP_OK);
+  addr_str = gst_rtsp_stream_get_multicast_client_addresses (stream);
+
+  fail_unless (g_str_equal (addr_str, expected_addr_str));
+  g_free (addr_str);
+
+  fail_unless (gst_rtsp_stream_leave_bin (stream, bin, rtpbin));
+
+  gst_object_unref (bin);
+  gst_object_unref (stream);
+}
+
+/* test if the provided transport destination is correct.
+ * CASE: valid multicast address */
+GST_START_TEST (test_multicast_client_address)
+{
+  const gchar *addr = "233.252.0.1";
+  guint port = 50000;
+  const gchar *expected_addr_str = "233.252.0.1:50000";
+  gboolean expected_res = TRUE;
+
+  check_multicast_client_address (addr, port, expected_addr_str, expected_res);
+}
+
+GST_END_TEST;
+
+/* test if the provided transport destination is correct.
+ * CASE: invalid multicast address */
+GST_START_TEST (test_multicast_client_address_invalid)
+{
+  const gchar *addr = "1.2.3.4";
+  guint port = 50000;
+  const gchar *expected_addr_str = "";
+  gboolean expected_res = FALSE;
+
+  check_multicast_client_address (addr, port, expected_addr_str, expected_res);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtspstream_suite (void)
 {
@@ -501,6 +588,8 @@ rtspstream_suite (void)
   tcase_add_test (tc, test_allocate_udp_ports_multicast);
   tcase_add_test (tc, test_allocate_udp_ports_client_settings);
   tcase_add_test (tc, test_tcp_transport);
+  tcase_add_test (tc, test_multicast_client_address);
+  tcase_add_test (tc, test_multicast_client_address_invalid);
 
   return s;
 }