rtsp-stream: Compare IP address strings case insensitive
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-stream.c
index 70abb99..c57d13d 100644 (file)
@@ -70,7 +70,7 @@ struct _GstRTSPStreamPrivate
   GstPad *srcpad, *sinkpad;
   GstElement *payloader;
   guint buffer_size;
-  gboolean is_joined;
+  GstBin *joined_bin;
 
   /* TRUE if this stream is running on
    * the client side of an RTSP link (for RECORD) */
@@ -94,21 +94,18 @@ struct _GstRTSPStreamPrivate
   GstElement *srtpdec;
   GHashTable *keys;
 
-  /* sinks used for sending and receiving RTP and RTCP over ipv4, they share
-   * sockets */
+  /* for UDP unicast */
   GstElement *udpsrc_v4[2];
-  /* UDP sources for UDP multicast transports */
-  GstElement *udpsrc_mcast_v4[2];
-
-  /* sinks used for sending and receiving RTP and RTCP over ipv6, they share
-   * sockets */
   GstElement *udpsrc_v6[2];
-  /* UDP sources for UDP multicast transports */
-  GstElement *udpsrc_mcast_v6[2];
-
   GstElement *udpqueue[2];
   GstElement *udpsink[2];
 
+  /* for UDP multicast */
+  GstElement *mcast_udpsrc_v4[2];
+  GstElement *mcast_udpsrc_v6[2];
+  GstElement *mcast_udpqueue[2];
+  GstElement *mcast_udpsink[2];
+
   /* for TCP transport */
   GstElement *appsrc[2];
   GstClockTime appsrc_base_time[2];
@@ -123,22 +120,18 @@ struct _GstRTSPStreamPrivate
   guint rtx_pt;
   GstClockTime rtx_time;
 
-  /* server ports for sending/receiving over ipv4 */
-  GstRTSPRange server_port_v4;
-  GstRTSPAddress *server_addr_v4;
-  gboolean have_ipv4;
+  /* pool used to manage unicast and multicast addresses */
+  GstRTSPAddressPool *pool;
 
-  /* server ports for sending/receiving over ipv6 */
-  GstRTSPRange server_port_v6;
+  /* unicast server addr/port */
+  GstRTSPAddress *server_addr_v4;
   GstRTSPAddress *server_addr_v6;
-  gboolean have_ipv6;
 
   /* multicast addresses */
-  GstRTSPAddressPool *pool;
-  GstRTSPAddress *addr_v4;
-  GstRTSPAddress *addr_v6;
-  gboolean have_ipv4_mcast;
-  gboolean have_ipv6_mcast;
+  GstRTSPAddress *mcast_addr_v4;
+  GstRTSPAddress *mcast_addr_v6;
+
+  gchar *multicast_iface;
 
   /* the caps of the stream */
   gulong caps_sig;
@@ -153,7 +146,6 @@ struct _GstRTSPStreamPrivate
   guint tr_cache_cookie_rtp;
   guint tr_cache_cookie_rtcp;
 
-
   gint dscp_qos;
 
   /* stream blocking */
@@ -162,6 +154,8 @@ struct _GstRTSPStreamPrivate
 
   /* pt->caps map for RECORD streams */
   GHashTable *ptmap;
+
+  GstRTSPPublishClockMode publish_clock_mode;
 };
 
 #define DEFAULT_CONTROL         NULL
@@ -257,6 +251,7 @@ gst_rtsp_stream_init (GstRTSPStream * stream)
   priv->control = g_strdup (DEFAULT_CONTROL);
   priv->profiles = DEFAULT_PROFILES;
   priv->protocols = DEFAULT_PROTOCOLS;
+  priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
 
   g_mutex_init (&priv->lock);
 
@@ -278,12 +273,12 @@ gst_rtsp_stream_finalize (GObject * obj)
   GST_DEBUG ("finalize stream %p", stream);
 
   /* we really need to be unjoined now */
-  g_return_if_fail (!priv->is_joined);
+  g_return_if_fail (priv->joined_bin == NULL);
 
-  if (priv->addr_v4)
-    gst_rtsp_address_free (priv->addr_v4);
-  if (priv->addr_v6)
-    gst_rtsp_address_free (priv->addr_v6);
+  if (priv->mcast_addr_v4)
+    gst_rtsp_address_free (priv->mcast_addr_v4);
+  if (priv->mcast_addr_v6)
+    gst_rtsp_address_free (priv->mcast_addr_v6);
   if (priv->server_addr_v4)
     gst_rtsp_address_free (priv->server_addr_v4);
   if (priv->server_addr_v6)
@@ -293,6 +288,8 @@ gst_rtsp_stream_finalize (GObject * obj)
   if (priv->rtxsend)
     g_object_unref (priv->rtxsend);
 
+  g_free (priv->multicast_iface);
+
   gst_object_unref (priv->payloader);
   if (priv->srcpad)
     gst_object_unref (priv->srcpad);
@@ -588,22 +585,18 @@ gst_rtsp_stream_get_mtu (GstRTSPStream * stream)
 
 /* Update the dscp qos property on the udp sinks */
 static void
-update_dscp_qos (GstRTSPStream * stream)
+update_dscp_qos (GstRTSPStream * stream, GstElement * udpsink[2])
 {
   GstRTSPStreamPrivate *priv;
 
-  g_return_if_fail (GST_IS_RTSP_STREAM (stream));
-
   priv = stream->priv;
 
-  if (priv->udpsink[0]) {
-    g_object_set (G_OBJECT (priv->udpsink[0]), "qos-dscp", priv->dscp_qos,
-        NULL);
+  if (udpsink[0]) {
+    g_object_set (G_OBJECT (udpsink[0]), "qos-dscp", priv->dscp_qos, NULL);
   }
 
-  if (priv->udpsink[1]) {
-    g_object_set (G_OBJECT (priv->udpsink[1]), "qos-dscp", priv->dscp_qos,
-        NULL);
+  if (udpsink[1]) {
+    g_object_set (G_OBJECT (udpsink[1]), "qos-dscp", priv->dscp_qos, NULL);
   }
 }
 
@@ -632,7 +625,7 @@ gst_rtsp_stream_set_dscp_qos (GstRTSPStream * stream, gint dscp_qos)
 
   priv->dscp_qos = dscp_qos;
 
-  update_dscp_qos (stream);
+  update_dscp_qos (stream, priv->udpsink);
 }
 
 /**
@@ -862,18 +855,67 @@ gst_rtsp_stream_get_address_pool (GstRTSPStream * stream)
 }
 
 /**
- * gst_rtsp_stream_get_multicast_address:
+ * gst_rtsp_stream_set_multicast_iface:
+ * @stream: a #GstRTSPStream
+ * @multicast_iface: (transfer none): a multicast interface
+ *
+ * configure @multicast_iface to be used for @stream.
+ */
+void
+gst_rtsp_stream_set_multicast_iface (GstRTSPStream * stream,
+    const gchar * multicast_iface)
+{
+  GstRTSPStreamPrivate *priv;
+  gchar *old;
+
+  g_return_if_fail (GST_IS_RTSP_STREAM (stream));
+
+  priv = stream->priv;
+
+  GST_LOG_OBJECT (stream, "set multicast iface %s",
+      GST_STR_NULL (multicast_iface));
+
+  g_mutex_lock (&priv->lock);
+  if ((old = priv->multicast_iface) != multicast_iface)
+    priv->multicast_iface = multicast_iface ? g_strdup (multicast_iface) : NULL;
+  else
+    old = NULL;
+  g_mutex_unlock (&priv->lock);
+
+  if (old)
+    g_free (old);
+}
+
+/**
+ * gst_rtsp_stream_get_multicast_iface:
  * @stream: a #GstRTSPStream
- * @family: the #GSocketFamily
  *
- * Get the multicast address of @stream for @family.
+ * Get the multicast interface used for @stream.
  *
- * Returns: (transfer full) (nullable): the #GstRTSPAddress of @stream
- * or %NULL when no address could be allocated. gst_rtsp_address_free()
- * after usage.
+ * Returns: (transfer full): the multicast interface for @stream. g_free() after
+ * usage.
  */
-GstRTSPAddress *
-gst_rtsp_stream_get_multicast_address (GstRTSPStream * stream,
+gchar *
+gst_rtsp_stream_get_multicast_iface (GstRTSPStream * stream)
+{
+  GstRTSPStreamPrivate *priv;
+  gchar *result;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+
+  priv = stream->priv;
+
+  g_mutex_lock (&priv->lock);
+  if ((result = priv->multicast_iface))
+    result = g_strdup (result);
+  g_mutex_unlock (&priv->lock);
+
+  return result;
+}
+
+
+static GstRTSPAddress *
+gst_rtsp_stream_get_multicast_address_locked (GstRTSPStream * stream,
     GSocketFamily family)
 {
   GstRTSPStreamPrivate *priv;
@@ -881,19 +923,16 @@ gst_rtsp_stream_get_multicast_address (GstRTSPStream * stream,
   GstRTSPAddress **addrp;
   GstRTSPAddressFlags flags;
 
-  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
-
   priv = stream->priv;
 
   if (family == G_SOCKET_FAMILY_IPV6) {
     flags = GST_RTSP_ADDRESS_FLAG_IPV6;
-    addrp = &priv->addr_v6;
+    addrp = &priv->mcast_addr_v6;
   } else {
     flags = GST_RTSP_ADDRESS_FLAG_IPV4;
-    addrp = &priv->addr_v4;
+    addrp = &priv->mcast_addr_v4;
   }
 
-  g_mutex_lock (&priv->lock);
   if (*addrp == NULL) {
     if (priv->pool == NULL)
       goto no_pool;
@@ -903,9 +942,14 @@ gst_rtsp_stream_get_multicast_address (GstRTSPStream * stream,
     *addrp = gst_rtsp_address_pool_acquire_address (priv->pool, flags, 2);
     if (*addrp == NULL)
       goto no_address;
+
+    /* FIXME: Also reserve the same port with unicast ANY address, since that's
+     * where we are going to bind our socket. Probably loop until we find a port
+     * available in both mcast and unicast pools. Maybe GstRTSPAddressPool
+     * should do it for us when both GST_RTSP_ADDRESS_FLAG_MULTICAST and
+     * GST_RTSP_ADDRESS_FLAG_UNICAST are givent. */
   }
   result = gst_rtsp_address_copy (*addrp);
-  g_mutex_unlock (&priv->lock);
 
   return result;
 
@@ -913,18 +957,44 @@ gst_rtsp_stream_get_multicast_address (GstRTSPStream * stream,
 no_pool:
   {
     GST_ERROR_OBJECT (stream, "no address pool specified");
-    g_mutex_unlock (&priv->lock);
     return NULL;
   }
 no_address:
   {
     GST_ERROR_OBJECT (stream, "failed to acquire address from pool");
-    g_mutex_unlock (&priv->lock);
     return NULL;
   }
 }
 
 /**
+ * gst_rtsp_stream_get_multicast_address:
+ * @stream: a #GstRTSPStream
+ * @family: the #GSocketFamily
+ *
+ * Get the multicast address of @stream for @family. The original
+ * #GstRTSPAddress is cached and copy is returned, so freeing the return value
+ * won't release the address from the pool.
+ *
+ * Returns: (transfer full) (nullable): the #GstRTSPAddress of @stream
+ * or %NULL when no address could be allocated. gst_rtsp_address_free()
+ * after usage.
+ */
+GstRTSPAddress *
+gst_rtsp_stream_get_multicast_address (GstRTSPStream * stream,
+    GSocketFamily family)
+{
+  GstRTSPAddress *result;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+
+  g_mutex_lock (&stream->priv->lock);
+  result = gst_rtsp_stream_get_multicast_address_locked (stream, family);
+  g_mutex_unlock (&stream->priv->lock);
+
+  return result;
+}
+
+/**
  * gst_rtsp_stream_reserve_address:
  * @stream: a #GstRTSPStream
  * @address: an address
@@ -932,7 +1002,9 @@ no_address:
  * @n_ports: n_ports
  * @ttl: a TTL
  *
- * Reserve @address and @port as the address and port of @stream.
+ * Reserve @address and @port as the address and port of @stream. The original
+ * #GstRTSPAddress is cached and copy is returned, so freeing the return value
+ * won't release the address from the pool.
  *
  * Returns: (nullable): the #GstRTSPAddress of @stream or %NULL when
  * the address could be reserved. gst_rtsp_address_free() after usage.
@@ -965,9 +1037,9 @@ gst_rtsp_stream_reserve_address (GstRTSPStream * stream,
   }
 
   if (family == G_SOCKET_FAMILY_IPV6)
-    addrp = &priv->addr_v6;
+    addrp = &priv->mcast_addr_v6;
   else
-    addrp = &priv->addr_v4;
+    addrp = &priv->mcast_addr_v4;
 
   g_mutex_lock (&priv->lock);
   if (*addrp == NULL) {
@@ -980,8 +1052,11 @@ gst_rtsp_stream_reserve_address (GstRTSPStream * stream,
         port, n_ports, ttl, addrp);
     if (res != GST_RTSP_ADDRESS_POOL_OK)
       goto no_address;
+
+    /* FIXME: Also reserve the same port with unicast ANY address, since that's
+     * where we are going to bind our socket. */
   } else {
-    if (strcmp ((*addrp)->address, address) ||
+    if (g_ascii_strcasecmp ((*addrp)->address, address) ||
         (*addrp)->port != port || (*addrp)->n_ports != n_ports ||
         (*addrp)->ttl != ttl)
       goto different_address;
@@ -1007,8 +1082,9 @@ no_address:
   }
 different_address:
   {
-    GST_ERROR_OBJECT (stream, "address %s is not the same that was already"
-        " reserved", address);
+    GST_ERROR_OBJECT (stream,
+        "address %s is not the same as %s that was already" " reserved",
+        address, (*addrp)->address);
     g_mutex_unlock (&priv->lock);
     return NULL;
   }
@@ -1016,10 +1092,9 @@ different_address:
 
 /* must be called with lock */
 static void
-set_sockets_for_udpsinks (GstRTSPStream * stream, GSocket * rtp_socket,
+set_sockets_for_udpsinks (GstElement * udpsink[2], GSocket * rtp_socket,
     GSocket * rtcp_socket, GSocketFamily family)
 {
-  GstRTSPStreamPrivate *priv = stream->priv;
   const gchar *multisink_socket;
 
   if (family == G_SOCKET_FAMILY_IPV6)
@@ -1027,36 +1102,20 @@ set_sockets_for_udpsinks (GstRTSPStream * stream, GSocket * rtp_socket,
   else
     multisink_socket = "socket";
 
-  g_object_set (G_OBJECT (priv->udpsink[0]), multisink_socket, rtp_socket,
-      NULL);
-  g_object_set (G_OBJECT (priv->udpsink[1]), multisink_socket, rtcp_socket,
-      NULL);
+  g_object_set (G_OBJECT (udpsink[0]), multisink_socket, rtp_socket, NULL);
+  g_object_set (G_OBJECT (udpsink[1]), multisink_socket, rtcp_socket, NULL);
 }
 
-/* must be called with lock */
 static gboolean
-create_and_configure_udpsinks (GstRTSPStream * stream)
+create_and_configure_udpsinks (GstRTSPStream * stream, GstElement * udpsink[2])
 {
   GstRTSPStreamPrivate *priv = stream->priv;
   GstElement *udpsink0, *udpsink1;
 
-  udpsink0 = NULL;
-  udpsink1 = NULL;
-
-  if (priv->udpsink[0])
-    udpsink0 = priv->udpsink[0];
-  else
-    udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
-
-  if (!udpsink0)
-    goto no_udp_protocol;
-
-  if (priv->udpsink[1])
-    udpsink1 = priv->udpsink[1];
-  else
-    udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
+  udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
+  udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
 
-  if (!udpsink1)
+  if (!udpsink0 || !udpsink1)
     goto no_udp_protocol;
 
   /* configure sinks */
@@ -1077,17 +1136,20 @@ create_and_configure_udpsinks (GstRTSPStream * stream)
     g_object_set (G_OBJECT (udpsink0), "async", FALSE, NULL);
   g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
 
-  g_object_set (G_OBJECT (udpsink0), "auto-multicast", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink1), "auto-multicast", FALSE, NULL);
+  /* join multicast group when adding clients, so we'll start receiving from it.
+   * We cannot rely on the udpsrc to join the group since its socket is always a
+   * local unicast one. */
+  g_object_set (G_OBJECT (udpsink0), "auto-multicast", TRUE, NULL);
+  g_object_set (G_OBJECT (udpsink1), "auto-multicast", TRUE, NULL);
 
   g_object_set (G_OBJECT (udpsink0), "loop", FALSE, NULL);
   g_object_set (G_OBJECT (udpsink1), "loop", FALSE, NULL);
 
-  /* update the dscp qos field in the sinks */
-  update_dscp_qos (stream);
+  udpsink[0] = udpsink0;
+  udpsink[1] = udpsink1;
 
-  priv->udpsink[0] = udpsink0;
-  priv->udpsink[1] = udpsink1;
+  /* update the dscp qos field in the sinks */
+  update_dscp_qos (stream, udpsink);
 
   return TRUE;
 
@@ -1099,47 +1161,9 @@ no_udp_protocol:
 }
 
 /* must be called with lock */
-static void
-play_udpsources_one_family (GstRTSPStream * stream, GstElement * udpsrc_out[2],
-    GSocketFamily family)
-{
-  GstRTSPStreamPrivate *priv;
-  GstPad *pad, *selpad;
-  guint i;
-  GstBin *bin;
-
-  priv = stream->priv;
-  bin = GST_BIN (gst_object_get_parent (GST_OBJECT (priv->funnel[1])));
-
-  for (i = 0; i < 2; i++) {
-    if (priv->sinkpad || i == 1) {
-      if (priv->srcpad) {
-        /* we set and keep these to playing so that they don't cause NO_PREROLL return
-         * values. This is only relevant for PLAY pipelines */
-        gst_element_set_state (udpsrc_out[i], GST_STATE_PLAYING);
-        gst_element_set_locked_state (udpsrc_out[i], TRUE);
-      }
-      /* add udpsrc */
-      gst_bin_add (bin, udpsrc_out[i]);
-
-      /* and link to the funnel */
-      selpad = gst_element_get_request_pad (priv->funnel[i], "sink_%u");
-      pad = gst_element_get_static_pad (udpsrc_out[i], "src");
-      gst_pad_link (pad, selpad);
-      gst_object_unref (pad);
-      gst_object_unref (selpad);
-    }
-  }
-
-  gst_object_unref (bin);
-}
-
-/* must be called with lock */
 static gboolean
-create_and_configure_udpsources_one_family (GstElement * udpsrc_out[2],
-    GSocket * rtp_socket, GSocket * rtcp_socket, GSocketFamily family,
-    const gchar * address, gint rtpport, gint rtcpport,
-    GstRTSPLowerTrans transport)
+create_and_configure_udpsources (GstElement * udpsrc_out[2],
+    GSocket * rtp_socket, GSocket * rtcp_socket)
 {
   GstStateChangeReturn ret;
 
@@ -1149,18 +1173,17 @@ create_and_configure_udpsources_one_family (GstElement * udpsrc_out[2],
   if (udpsrc_out[0] == NULL || udpsrc_out[1] == NULL)
     goto error;
 
-  if (transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
-    g_object_set (G_OBJECT (udpsrc_out[0]), "address", address, NULL);
-    g_object_set (G_OBJECT (udpsrc_out[1]), "address", address, NULL);
-    g_object_set (G_OBJECT (udpsrc_out[0]), "port", rtpport, NULL);
-    g_object_set (G_OBJECT (udpsrc_out[1]), "port", rtcpport, NULL);
-    g_object_set (G_OBJECT (udpsrc_out[0]), "loop", FALSE, NULL);
-    g_object_set (G_OBJECT (udpsrc_out[1]), "loop", FALSE, NULL);
-  }
-
   g_object_set (G_OBJECT (udpsrc_out[0]), "socket", rtp_socket, NULL);
   g_object_set (G_OBJECT (udpsrc_out[1]), "socket", rtcp_socket, NULL);
 
+  /* The udpsrc cannot do the join because its socket is always a local unicast
+   * one. The udpsink sharing the same socket will do it for us. */
+  g_object_set (G_OBJECT (udpsrc_out[0]), "auto-multicast", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsrc_out[1]), "auto-multicast", FALSE, NULL);
+
+  g_object_set (G_OBJECT (udpsrc_out[0]), "loop", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsrc_out[1]), "loop", FALSE, NULL);
+
   ret = gst_element_set_state (udpsrc_out[0], GST_STATE_READY);
   if (ret == GST_STATE_CHANGE_FAILURE)
     goto error;
@@ -1169,24 +1192,26 @@ create_and_configure_udpsources_one_family (GstElement * udpsrc_out[2],
     goto error;
 
   return TRUE;
-  return TRUE;
 
   /* ERRORS */
 error:
   {
-    if (udpsrc_out[0])
-      gst_object_unref (udpsrc_out[0]);
-    if (udpsrc_out[1])
-      gst_object_unref (udpsrc_out[1]);
+    if (udpsrc_out[0]) {
+      gst_element_set_state (udpsrc_out[0], GST_STATE_NULL);
+      g_clear_object (&udpsrc_out[0]);
+    }
+    if (udpsrc_out[1]) {
+      gst_element_set_state (udpsrc_out[1], GST_STATE_NULL);
+      g_clear_object (&udpsrc_out[1]);
+    }
     return FALSE;
   }
 }
 
 static gboolean
 alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family,
-    GstElement * udpsrc_out[2], GstRTSPRange * server_port_out,
-    GstRTSPTransport * ct, GstRTSPAddress ** server_addr_out,
-    gboolean use_client_settings)
+    GstElement * udpsrc_out[2], GstElement * udpsink_out[2],
+    GstRTSPAddress ** server_addr_out, gboolean multicast)
 {
   GstRTSPStreamPrivate *priv = stream->priv;
   GSocket *rtp_socket = NULL;
@@ -1201,11 +1226,15 @@ alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family,
   GSocketAddress *rtp_sockaddr = NULL;
   GSocketAddress *rtcp_sockaddr = NULL;
   GstRTSPAddressPool *pool;
-  GstRTSPLowerTrans transport;
+
+  g_assert (!udpsrc_out[0]);
+  g_assert (!udpsrc_out[1]);
+  g_assert ((!udpsink_out[0] && !udpsink_out[1]) ||
+      (udpsink_out[0] && udpsink_out[1]));
+  g_assert (*server_addr_out == NULL);
 
   pool = priv->pool;
   count = 0;
-  transport = ct->lower_transport;
 
   /* Start with random port */
   tmp_rtp = 0;
@@ -1214,9 +1243,7 @@ alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family,
       G_SOCKET_PROTOCOL_UDP, NULL);
   if (!rtcp_socket)
     goto no_udp_protocol;
-
-  if (*server_addr_out)
-    gst_rtsp_address_free (*server_addr_out);
+  g_socket_set_multicast_loopback (rtcp_socket, FALSE);
 
   /* try to allocate 2 UDP ports, the RTP port should be an even
    * number and the RTCP port should be the next (uneven) port */
@@ -1227,33 +1254,30 @@ again:
         G_SOCKET_PROTOCOL_UDP, NULL);
     if (!rtp_socket)
       goto no_udp_protocol;
+    g_socket_set_multicast_loopback (rtp_socket, FALSE);
   }
 
-  if (pool) {
+  if ((pool && gst_rtsp_address_pool_has_unicast_addresses (pool)) || multicast) {
     GstRTSPAddressFlags flags;
 
-    if (transport == GST_RTSP_LOWER_TRANS_UDP &&
-        gst_rtsp_address_pool_has_unicast_addresses (pool))
-      flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT | GST_RTSP_ADDRESS_FLAG_UNICAST;
-    else if (transport == GST_RTSP_LOWER_TRANS_UDP_MCAST)
-      flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT | GST_RTSP_ADDRESS_FLAG_MULTICAST;
-    else
-      goto no_ports;
-
     if (addr)
       rejected_addresses = g_list_prepend (rejected_addresses, addr);
 
+    if (!pool)
+      goto no_ports;
+
+    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 (ct->destination && transport == GST_RTSP_LOWER_TRANS_UDP_MCAST
-        && use_client_settings)
-      gst_rtsp_address_pool_reserve_address (pool, ct->destination,
-          ct->port.min, 2, ct->ttl, &addr);
-    else
-      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_ports;
@@ -1261,16 +1285,10 @@ again:
     tmp_rtp = addr->port;
 
     g_clear_object (&inetaddr);
-    inetaddr = g_inet_address_new_from_string (addr->address);
-
-    /* Don't bind to multicast addresses, this does not work on
-     * Windows. You're supposed to bind to ANY and then join the
-     * multicast group, which udpsrc/sink does for us already.
-     */
-    if (g_inet_address_get_is_multicast (inetaddr)) {
-      g_object_unref (inetaddr);
+    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;
@@ -1318,24 +1336,20 @@ again:
   }
   g_object_unref (rtcp_sockaddr);
 
-  if (addr == NULL)
-    addr_str = g_inet_address_to_string (inetaddr);
-  else
-    addr_str = addr->address;
+  if (!addr) {
+    addr = g_slice_new0 (GstRTSPAddress);
+    addr->address = g_inet_address_to_string (inetaddr);
+    addr->port = tmp_rtp;
+    addr->n_ports = 2;
+  }
+
+  addr_str = addr->address;
   g_clear_object (&inetaddr);
 
-  if (!create_and_configure_udpsources_one_family (udpsrc_out, rtp_socket,
-          rtcp_socket, family, addr_str, tmp_rtp, tmp_rtcp, transport)) {
-    if (addr == NULL)
-      g_free (addr_str);
+  if (!create_and_configure_udpsources (udpsrc_out, rtp_socket, rtcp_socket)) {
     goto no_udp_protocol;
   }
 
-  if (addr == NULL)
-    g_free (addr_str);
-
-  play_udpsources_one_family (stream, udpsrc_out, family);
-
   g_object_get (G_OBJECT (udpsrc_out[0]), "port", &rtpport, NULL);
   g_object_get (G_OBJECT (udpsrc_out[1]), "port", &rtcpport, NULL);
 
@@ -1343,13 +1357,26 @@ again:
   if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
     goto port_error;
 
-  /* set RTP and RTCP sockets */
-  set_sockets_for_udpsinks (stream, rtp_socket, rtcp_socket, family);
+  /* This function is called twice (for v4 and v6) but we create only one pair
+   * of udpsinks. */
+  if (!udpsink_out[0]
+      && !create_and_configure_udpsinks (stream, udpsink_out))
+    goto no_udp_protocol;
 
-  server_port_out->min = rtpport;
-  server_port_out->max = rtcpport;
+  if (multicast) {
+    g_object_set (G_OBJECT (udpsink_out[0]), "multicast-iface",
+        priv->multicast_iface, NULL);
+    g_object_set (G_OBJECT (udpsink_out[1]), "multicast-iface",
+        priv->multicast_iface, NULL);
+
+    g_signal_emit_by_name (udpsink_out[0], "add", addr_str, rtpport, NULL);
+    g_signal_emit_by_name (udpsink_out[1], "add", addr_str, rtcpport, NULL);
+  }
+
+  set_sockets_for_udpsinks (udpsink_out, rtp_socket, rtcp_socket, family);
 
   *server_addr_out = addr;
+
   g_list_free_full (rejected_addresses, (GDestroyNotify) gst_rtsp_address_free);
 
   g_object_unref (rtp_socket);
@@ -1399,60 +1426,14 @@ cleanup:
  * Allocates RTP and RTCP ports.
  *
  * Returns: %TRUE if the RTP and RTCP sockets have been succeccully allocated.
+ * Deprecated: This function shouldn't have been made public
  */
 gboolean
 gst_rtsp_stream_allocate_udp_sockets (GstRTSPStream * stream,
     GSocketFamily family, GstRTSPTransport * ct, gboolean use_client_settings)
 {
-  GstRTSPStreamPrivate *priv;
-  gboolean result = FALSE;
-  GstRTSPLowerTrans transport = ct->lower_transport;
-
-  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
-  priv = stream->priv;
-  g_return_val_if_fail (priv->is_joined, FALSE);
-
-  g_mutex_lock (&priv->lock);
-
-  if (family == G_SOCKET_FAMILY_IPV4) {
-    if (transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
-      if (priv->have_ipv4_mcast)
-        goto done;
-      priv->have_ipv4_mcast =
-          alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4,
-          priv->udpsrc_mcast_v4, &priv->server_port_v4, ct, &priv->addr_v4,
-          use_client_settings);
-    } else {
-      priv->have_ipv4 =
-          alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4, priv->udpsrc_v4,
-          &priv->server_port_v4, ct, &priv->server_addr_v4,
-          use_client_settings);
-    }
-  } else {
-    if (transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
-      if (priv->have_ipv6_mcast)
-        goto done;
-      priv->have_ipv6_mcast =
-          alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6,
-          priv->udpsrc_mcast_v6, &priv->server_port_v6, ct, &priv->addr_v6,
-          use_client_settings);
-    } else {
-      if (priv->have_ipv6)
-        goto done;
-      priv->have_ipv6 =
-          alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6, priv->udpsrc_v6,
-          &priv->server_port_v6, ct, &priv->server_addr_v6,
-          use_client_settings);
-    }
-  }
-
-done:
-  result = priv->have_ipv4 || priv->have_ipv4_mcast || priv->have_ipv6 ||
-      priv->have_ipv6_mcast;
-
-  g_mutex_unlock (&priv->lock);
-
-  return result;
+  g_warn_if_reached ();
+  return FALSE;
 }
 
 /**
@@ -1480,7 +1461,7 @@ gst_rtsp_stream_set_client_side (GstRTSPStream * stream, gboolean client_side)
 }
 
 /**
- * gst_rtsp_stream_set_client_side:
+ * gst_rtsp_stream_is_client_side:
  * @stream: a #GstRTSPStream
  *
  * See gst_rtsp_stream_set_client_side()
@@ -1503,6 +1484,33 @@ gst_rtsp_stream_is_client_side (GstRTSPStream * stream)
   return ret;
 }
 
+/* must be called with lock */
+static gboolean
+alloc_ports (GstRTSPStream * stream)
+{
+  GstRTSPStreamPrivate *priv = stream->priv;
+  gboolean ret = TRUE;
+
+  if (priv->protocols & GST_RTSP_LOWER_TRANS_UDP) {
+    ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4,
+        priv->udpsrc_v4, priv->udpsink, &priv->server_addr_v4, FALSE);
+
+    ret |= alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6,
+        priv->udpsrc_v6, priv->udpsink, &priv->server_addr_v6, FALSE);
+  }
+
+  /* FIXME: Maybe actually consider the return values? */
+  if (priv->protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST) {
+    ret |= alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4,
+        priv->mcast_udpsrc_v4, priv->mcast_udpsink, &priv->mcast_addr_v4, TRUE);
+
+    ret |= alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6,
+        priv->mcast_udpsrc_v6, priv->mcast_udpsink, &priv->mcast_addr_v6, TRUE);
+  }
+
+  return ret;
+}
+
 /**
  * gst_rtsp_stream_get_server_port:
  * @stream: a #GstRTSPStream
@@ -1520,15 +1528,21 @@ gst_rtsp_stream_get_server_port (GstRTSPStream * stream,
 
   g_return_if_fail (GST_IS_RTSP_STREAM (stream));
   priv = stream->priv;
-  g_return_if_fail (priv->is_joined);
+  g_return_if_fail (priv->joined_bin != NULL);
 
   g_mutex_lock (&priv->lock);
   if (family == G_SOCKET_FAMILY_IPV4) {
-    if (server_port)
-      *server_port = priv->server_port_v4;
+    if (server_port) {
+      server_port->min = priv->server_addr_v4->port;
+      server_port->max =
+          priv->server_addr_v4->port + priv->server_addr_v4->n_ports - 1;
+    }
   } else {
-    if (server_port)
-      *server_port = priv->server_port_v6;
+    if (server_port) {
+      server_port->min = priv->server_addr_v6->port;
+      server_port->max =
+          priv->server_addr_v6->port + priv->server_addr_v6->n_ports - 1;
+    }
   }
   g_mutex_unlock (&priv->lock);
 }
@@ -1560,6 +1574,32 @@ gst_rtsp_stream_get_rtpsession (GstRTSPStream * stream)
 }
 
 /**
+ * gst_rtsp_stream_get_encoder:
+ * @stream: a #GstRTSPStream
+ *
+ * Get the SRTP encoder for this stream.
+ *
+ * Returns: (transfer full): The SRTP encoder for this stream. Unref after usage.
+ */
+GstElement *
+gst_rtsp_stream_get_srtp_encoder (GstRTSPStream * stream)
+{
+  GstRTSPStreamPrivate *priv;
+  GstElement *encoder;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+
+  priv = stream->priv;
+
+  g_mutex_lock (&priv->lock);
+  if ((encoder = priv->srtpenc))
+    g_object_ref (encoder);
+  g_mutex_unlock (&priv->lock);
+
+  return encoder;
+}
+
+/**
  * gst_rtsp_stream_get_ssrc:
  * @stream: a #GstRTSPStream
  * @ssrc: (out): result ssrc
@@ -1574,7 +1614,7 @@ gst_rtsp_stream_get_ssrc (GstRTSPStream * stream, guint * ssrc)
 
   g_return_if_fail (GST_IS_RTSP_STREAM (stream));
   priv = stream->priv;
-  g_return_if_fail (priv->is_joined);
+  g_return_if_fail (priv->joined_bin != NULL);
 
   g_mutex_lock (&priv->lock);
   if (ssrc && priv->session)
@@ -2198,6 +2238,51 @@ gst_rtsp_stream_set_pt_map (GstRTSPStream * stream, guint pt, GstCaps * caps)
   g_mutex_unlock (&priv->lock);
 }
 
+/**
+ * gst_rtsp_stream_set_publish_clock_mode:
+ * @stream: a #GstRTSPStream
+ * @mode: the clock publish mode
+ *
+ * Sets if and how the stream clock should be published according to RFC7273.
+ *
+ * Since: 1.8
+ */
+void
+gst_rtsp_stream_set_publish_clock_mode (GstRTSPStream * stream,
+    GstRTSPPublishClockMode mode)
+{
+  GstRTSPStreamPrivate *priv;
+
+  priv = stream->priv;
+  g_mutex_lock (&priv->lock);
+  priv->publish_clock_mode = mode;
+  g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_stream_get_publish_clock_mode:
+ * @factory: a #GstRTSPStream
+ *
+ * Gets if and how the stream clock should be published according to RFC7273.
+ *
+ * Returns: The GstRTSPPublishClockMode
+ *
+ * Since: 1.8
+ */
+GstRTSPPublishClockMode
+gst_rtsp_stream_get_publish_clock_mode (GstRTSPStream * stream)
+{
+  GstRTSPStreamPrivate *priv;
+  GstRTSPPublishClockMode ret;
+
+  priv = stream->priv;
+  g_mutex_lock (&priv->lock);
+  ret = priv->publish_clock_mode;
+  g_mutex_unlock (&priv->lock);
+
+  return ret;
+}
+
 static GstCaps *
 request_pt_map (GstElement * rtpbin, guint session, guint pt,
     GstRTSPStream * stream)
@@ -2275,13 +2360,43 @@ on_npt_stop (GstElement * rtpbin, guint session, guint ssrc,
   gst_pad_send_event (stream->priv->sinkpad, gst_event_new_eos ());
 }
 
+static void
+plug_sink (GstBin * bin, GstElement * tee, GstElement * sink,
+    GstElement ** queue_out)
+{
+  GstPad *pad;
+  GstPad *teepad;
+  GstPad *queuepad;
+
+  gst_bin_add (bin, sink);
+
+  *queue_out = gst_element_factory_make ("queue", NULL);
+  g_object_set (*queue_out, "max-size-buffers", 1, "max-size-bytes", 0,
+      "max-size-time", G_GINT64_CONSTANT (0), NULL);
+  gst_bin_add (bin, *queue_out);
+
+  /* link tee to queue */
+  teepad = gst_element_get_request_pad (tee, "src_%u");
+  pad = gst_element_get_static_pad (*queue_out, "sink");
+  gst_pad_link (teepad, pad);
+  gst_object_unref (pad);
+  gst_object_unref (teepad);
+
+  /* link queue to sink */
+  queuepad = gst_element_get_static_pad (*queue_out, "src");
+  pad = gst_element_get_static_pad (sink, "sink");
+  gst_pad_link (queuepad, pad);
+  gst_object_unref (queuepad);
+  gst_object_unref (pad);
+}
+
 /* must be called with lock */
-static gboolean
+static void
 create_sender_part (GstRTSPStream * stream, GstBin * bin, GstState state)
 {
   GstRTSPStreamPrivate *priv;
-  GstPad *pad, *sinkpad = NULL;
-  gboolean is_tcp = FALSE, is_udp = FALSE;
+  GstPad *pad;
+  gboolean is_tcp, is_udp;
   gint i;
 
   priv = stream->priv;
@@ -2290,11 +2405,7 @@ create_sender_part (GstRTSPStream * stream, GstBin * bin, GstState state)
   is_udp = ((priv->protocols & GST_RTSP_LOWER_TRANS_UDP) ||
       (priv->protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST));
 
-  if (is_udp && !create_and_configure_udpsinks (stream))
-    goto no_udp_protocol;
-
   for (i = 0; i < 2; i++) {
-    GstPad *teepad, *queuepad;
     /* For the sender we create this bit of pipeline for both
      * RTP and RTCP. Sync and preroll are enabled on udpsink so
      * we need to add a queue before appsink and udpsink to make
@@ -2315,113 +2426,103 @@ create_sender_part (GstRTSPStream * stream, GstBin * bin, GstState state)
      * and link the udpsink (for UDP) or appsink (for TCP) directly to
      * the session.
      */
+
     /* Only link the RTP send src if we're going to send RTP, link
      * the RTCP send src always */
-    if (priv->srcpad || i == 1) {
-      if (is_udp) {
-        /* add udpsink */
-        gst_bin_add (bin, priv->udpsink[i]);
-        sinkpad = gst_element_get_static_pad (priv->udpsink[i], "sink");
-      }
+    if (!priv->srcpad && i == 0)
+      continue;
+
+    if (is_tcp) {
+      /* make appsink */
+      priv->appsink[i] = gst_element_factory_make ("appsink", NULL);
+      g_object_set (priv->appsink[i], "emit-signals", FALSE, NULL);
+      gst_app_sink_set_callbacks (GST_APP_SINK_CAST (priv->appsink[i]),
+          &sink_cb, stream, NULL);
+    }
+
+    /* If we have udp always use a tee because we could have mcast clients
+     * requesting different ports, in which case we'll have to plug more
+     * udpsinks. */
+    if (is_udp) {
+      /* make tee for RTP/RTCP */
+      priv->tee[i] = gst_element_factory_make ("tee", NULL);
+      gst_bin_add (bin, priv->tee[i]);
+
+      /* and link to rtpbin send pad */
+      pad = gst_element_get_static_pad (priv->tee[i], "sink");
+      gst_pad_link (priv->send_src[i], pad);
+      gst_object_unref (pad);
+
+      if (priv->udpsink[i])
+        plug_sink (bin, priv->tee[i], priv->udpsink[i], &priv->udpqueue[i]);
+
+      if (priv->mcast_udpsink[i])
+        plug_sink (bin, priv->tee[i], priv->mcast_udpsink[i],
+            &priv->mcast_udpqueue[i]);
 
       if (is_tcp) {
-        /* make appsink */
-        priv->appsink[i] = gst_element_factory_make ("appsink", NULL);
-        g_object_set (priv->appsink[i], "emit-signals", FALSE, NULL);
-        gst_bin_add (bin, priv->appsink[i]);
-        gst_app_sink_set_callbacks (GST_APP_SINK_CAST (priv->appsink[i]),
-            &sink_cb, stream, NULL);
+        g_object_set (priv->appsink[i], "async", FALSE, "sync", FALSE, NULL);
+        plug_sink (bin, priv->tee[i], priv->appsink[i], &priv->appqueue[i]);
       }
+    } else if (is_tcp) {
+      /* only appsink needed, link it to the session */
+      pad = gst_element_get_static_pad (priv->appsink[i], "sink");
+      gst_pad_link (priv->send_src[i], pad);
+      gst_object_unref (pad);
 
-      if (is_udp && is_tcp) {
+      /* when its only TCP, we need to set sync and preroll to FALSE
+       * for the sink to avoid deadlock. And this is only needed for
+       * sink used for RTCP data, not the RTP data. */
+      if (i == 1)
         g_object_set (priv->appsink[i], "async", FALSE, "sync", FALSE, NULL);
-
-        /* make tee for RTP/RTCP */
-        priv->tee[i] = gst_element_factory_make ("tee", NULL);
-        gst_bin_add (bin, priv->tee[i]);
-
-        /* and link to rtpbin send pad */
-        pad = gst_element_get_static_pad (priv->tee[i], "sink");
-        gst_pad_link (priv->send_src[i], pad);
-        gst_object_unref (pad);
-
-        priv->udpqueue[i] = gst_element_factory_make ("queue", NULL);
-        g_object_set (priv->udpqueue[i], "max-size-buffers",
-            1, "max-size-bytes", 0, "max-size-time", G_GINT64_CONSTANT (0),
-            NULL);
-        gst_bin_add (bin, priv->udpqueue[i]);
-        /* link tee to udpqueue */
-        teepad = gst_element_get_request_pad (priv->tee[i], "src_%u");
-        pad = gst_element_get_static_pad (priv->udpqueue[i], "sink");
-        gst_pad_link (teepad, pad);
-        gst_object_unref (pad);
-        gst_object_unref (teepad);
-
-        /* link udpqueue to udpsink */
-        queuepad = gst_element_get_static_pad (priv->udpqueue[i], "src");
-        gst_pad_link (queuepad, sinkpad);
-        gst_object_unref (queuepad);
-        gst_object_unref (sinkpad);
-
-        /* make appqueue */
-        priv->appqueue[i] = gst_element_factory_make ("queue", NULL);
-        g_object_set (priv->appqueue[i], "max-size-buffers",
-            1, "max-size-bytes", 0, "max-size-time", G_GINT64_CONSTANT (0),
-            NULL);
-        gst_bin_add (bin, priv->appqueue[i]);
-        /* and link tee to appqueue */
-        teepad = gst_element_get_request_pad (priv->tee[i], "src_%u");
-        pad = gst_element_get_static_pad (priv->appqueue[i], "sink");
-        gst_pad_link (teepad, pad);
-        gst_object_unref (pad);
-        gst_object_unref (teepad);
-
-        /* and link appqueue to appsink */
-        queuepad = gst_element_get_static_pad (priv->appqueue[i], "src");
-        pad = gst_element_get_static_pad (priv->appsink[i], "sink");
-        gst_pad_link (queuepad, pad);
-        gst_object_unref (pad);
-        gst_object_unref (queuepad);
-      } else if (is_tcp) {
-        /* only appsink needed, link it to the session */
-        pad = gst_element_get_static_pad (priv->appsink[i], "sink");
-        gst_pad_link (priv->send_src[i], pad);
-        gst_object_unref (pad);
-
-        /* when its only TCP, we need to set sync and preroll to FALSE
-         * for the sink to avoid deadlock. And this is only needed for
-         * sink used for RTCP data, not the RTP data. */
-        if (i == 1)
-          g_object_set (priv->appsink[i], "async", FALSE, "sync", FALSE, NULL);
-      } else {
-        /* else only udpsink needed, link it to the session */
-        gst_pad_link (priv->send_src[i], sinkpad);
-        gst_object_unref (sinkpad);
-      }
     }
 
     /* check if we need to set to a special state */
     if (state != GST_STATE_NULL) {
-      if (priv->udpsink[i] && (priv->srcpad || i == 1))
+      if (priv->udpsink[i])
         gst_element_set_state (priv->udpsink[i], state);
-      if (priv->appsink[i] && (priv->srcpad || i == 1))
+      if (priv->mcast_udpsink[i])
+        gst_element_set_state (priv->mcast_udpsink[i], state);
+      if (priv->appsink[i])
         gst_element_set_state (priv->appsink[i], state);
-      if (priv->appqueue[i] && (priv->srcpad || i == 1))
+      if (priv->appqueue[i])
         gst_element_set_state (priv->appqueue[i], state);
-      if (priv->udpqueue[i] && (priv->srcpad || i == 1))
+      if (priv->udpqueue[i])
         gst_element_set_state (priv->udpqueue[i], state);
-      if (priv->tee[i] && (priv->srcpad || i == 1))
+      if (priv->mcast_udpqueue[i])
+        gst_element_set_state (priv->mcast_udpqueue[i], state);
+      if (priv->tee[i])
         gst_element_set_state (priv->tee[i], state);
     }
   }
+}
 
-  return TRUE;
+/* must be called with lock */
+static void
+plug_src (GstRTSPStream * stream, GstBin * bin, GstElement * src,
+    GstElement * funnel)
+{
+  GstRTSPStreamPrivate *priv;
+  GstPad *pad, *selpad;
 
-  /* ERRORS */
-no_udp_protocol:
-  {
-    return FALSE;
+  priv = stream->priv;
+
+  if (priv->srcpad) {
+    /* we set and keep these to playing so that they don't cause NO_PREROLL return
+     * values. This is only relevant for PLAY pipelines */
+    gst_element_set_state (src, GST_STATE_PLAYING);
+    gst_element_set_locked_state (src, TRUE);
   }
+
+  /* add src */
+  gst_bin_add (bin, src);
+
+  /* and link to the funnel */
+  selpad = gst_element_get_request_pad (funnel, "sink_%u");
+  pad = gst_element_get_static_pad (src, "src");
+  gst_pad_link (pad, selpad);
+  gst_object_unref (pad);
+  gst_object_unref (selpad);
 }
 
 /* must be called with lock */
@@ -2429,7 +2530,7 @@ static void
 create_receiver_part (GstRTSPStream * stream, GstBin * bin, GstState state)
 {
   GstRTSPStreamPrivate *priv;
-  GstPad *pad, *selpad;
+  GstPad *pad;
   gboolean is_tcp;
   gint i;
 
@@ -2438,88 +2539,110 @@ create_receiver_part (GstRTSPStream * stream, GstBin * bin, GstState state)
   is_tcp = priv->protocols & GST_RTSP_LOWER_TRANS_TCP;
 
   for (i = 0; i < 2; i++) {
-    /* Only connect recv RTP sink if we expect to receive RTP. Connect recv
-     * RTCP sink always */
-    if (priv->sinkpad || i == 1) {
-      /* For the receiver we create this bit of pipeline for both
-       * RTP and RTCP. We receive RTP/RTCP on appsrc and udpsrc
-       * and it is all funneled into the rtpbin receive pad.
-       *
-       * .--------.     .--------.    .--------.
-       * | udpsrc |     | funnel |    | rtpbin |
-       * |       src->sink      src->sink      |
-       * '--------'     |        |    '--------'
-       * .--------.     |        |
-       * | appsrc |     |        |
-       * |       src->sink       |
-       * '--------'     '--------'
-       */
-      /* make funnel for the RTP/RTCP receivers */
-      priv->funnel[i] = gst_element_factory_make ("funnel", NULL);
-      gst_bin_add (bin, priv->funnel[i]);
-
-      pad = gst_element_get_static_pad (priv->funnel[i], "src");
-      gst_pad_link (pad, priv->recv_sink[i]);
-      gst_object_unref (pad);
+    /* For the receiver we create this bit of pipeline for both
+     * RTP and RTCP. We receive RTP/RTCP on appsrc and udpsrc
+     * and it is all funneled into the rtpbin receive pad.
+     *
+     * .--------.     .--------.    .--------.
+     * | udpsrc |     | funnel |    | rtpbin |
+     * |       src->sink      src->sink      |
+     * '--------'     |        |    '--------'
+     * .--------.     |        |
+     * | appsrc |     |        |
+     * |       src->sink       |
+     * '--------'     '--------'
+     */
 
-      if (priv->udpsrc_v4[i]) {
-        if (priv->srcpad) {
-          /* we set and keep these to playing so that they don't cause NO_PREROLL return
-           * values. This is only relevant for PLAY pipelines */
-          gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_PLAYING);
-          gst_element_set_locked_state (priv->udpsrc_v4[i], TRUE);
-        }
-        /* add udpsrc */
-        gst_bin_add (bin, priv->udpsrc_v4[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);
-      }
+    if (!priv->sinkpad && i == 0) {
+      /* Only connect recv RTP sink if we expect to receive RTP. Connect recv
+       * RTCP sink always */
+      continue;
+    }
 
-      if (priv->udpsrc_v6[i]) {
-        if (priv->srcpad) {
-          gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_PLAYING);
-          gst_element_set_locked_state (priv->udpsrc_v6[i], TRUE);
-        }
-        gst_bin_add (bin, priv->udpsrc_v6[i]);
-
-        /* 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_v6[i], "src");
-        gst_pad_link (pad, selpad);
-        gst_object_unref (pad);
-        gst_object_unref (selpad);
-      }
+    /* make funnel for the RTP/RTCP receivers */
+    priv->funnel[i] = gst_element_factory_make ("funnel", NULL);
+    gst_bin_add (bin, priv->funnel[i]);
 
-      if (is_tcp) {
-        /* make and add appsrc */
-        priv->appsrc[i] = gst_element_factory_make ("appsrc", NULL);
-        priv->appsrc_base_time[i] = -1;
-        g_object_set (priv->appsrc[i], "format", GST_FORMAT_TIME, NULL);
-        gst_bin_add (bin, priv->appsrc[i]);
-        /* and link to the funnel */
-        selpad = gst_element_get_request_pad (priv->funnel[i], "sink_%u");
-        pad = gst_element_get_static_pad (priv->appsrc[i], "src");
-        gst_pad_link (pad, selpad);
-        gst_object_unref (pad);
-        gst_object_unref (selpad);
-      }
+    pad = gst_element_get_static_pad (priv->funnel[i], "src");
+    gst_pad_link (pad, priv->recv_sink[i]);
+    gst_object_unref (pad);
+
+    if (priv->udpsrc_v4[i])
+      plug_src (stream, bin, priv->udpsrc_v4[i], priv->funnel[i]);
+
+    if (priv->udpsrc_v6[i])
+      plug_src (stream, bin, priv->udpsrc_v6[i], priv->funnel[i]);
+
+    if (priv->mcast_udpsrc_v4[i])
+      plug_src (stream, bin, priv->mcast_udpsrc_v4[i], priv->funnel[i]);
+
+    if (priv->mcast_udpsrc_v6[i])
+      plug_src (stream, bin, priv->mcast_udpsrc_v6[i], priv->funnel[i]);
+
+    if (is_tcp) {
+      /* make and add appsrc */
+      priv->appsrc[i] = gst_element_factory_make ("appsrc", NULL);
+      priv->appsrc_base_time[i] = -1;
+      g_object_set (priv->appsrc[i], "format", GST_FORMAT_TIME, "is-live",
+          TRUE, NULL);
+      plug_src (stream, bin, priv->appsrc[i], priv->funnel[i]);
     }
 
     /* check if we need to set to a special state */
     if (state != GST_STATE_NULL) {
-      if (priv->funnel[i] && (priv->sinkpad || i == 1))
-        gst_element_set_state (priv->funnel[i], state);
-      if (priv->appsrc[i] && (priv->sinkpad || i == 1))
-        gst_element_set_state (priv->appsrc[i], state);
+      gst_element_set_state (priv->funnel[i], state);
     }
   }
 }
 
+static gboolean
+check_mcast_part_for_transport (GstRTSPStream * stream,
+    const GstRTSPTransport * tr)
+{
+  GstRTSPStreamPrivate *priv = stream->priv;
+  GInetAddress *inetaddr;
+  GSocketFamily family;
+  GstRTSPAddress *mcast_addr;
+
+  /* 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);
+
+  /* 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;
+  }
+
+  /* We support only one mcast group per family, make sure this transport
+   * matches it. */
+  if (!mcast_addr)
+    goto no_addr;
+
+  if (!g_str_equal (tr->destination, mcast_addr->address) ||
+      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;
+
+  return TRUE;
+
+no_addr:
+  {
+    GST_WARNING_OBJECT (stream, "Adding mcast transport, but no mcast address "
+        "has been reserved");
+    return FALSE;
+  }
+wrong_addr:
+  {
+    GST_WARNING_OBJECT (stream, "Adding mcast transport, but it doesn't match "
+        "the reserved address");
+    return FALSE;
+  }
+}
+
 /**
  * gst_rtsp_stream_join_bin:
  * @stream: a #GstRTSPStream
@@ -2550,7 +2673,7 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
   priv = stream->priv;
 
   g_mutex_lock (&priv->lock);
-  if (priv->is_joined)
+  if (priv->joined_bin != NULL)
     goto was_joined;
 
   /* create a session with the same index as the stream */
@@ -2558,6 +2681,9 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
 
   GST_INFO ("stream %p joining bin as session %u", stream, idx);
 
+  if (!alloc_ports (stream))
+    goto no_ports;
+
   if (priv->profiles & GST_RTSP_PROFILE_SAVP
       || priv->profiles & GST_RTSP_PROFILE_SAVPF) {
     /* For SRTP */
@@ -2633,9 +2759,7 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
   g_signal_connect (priv->session, "on-sender-ssrc-active",
       (GCallback) on_sender_ssrc_active, stream);
 
-  if (!create_sender_part (stream, bin, state))
-    goto no_udp_protocol;
-
+  create_sender_part (stream, bin, state);
   create_receiver_part (stream, bin, state);
 
   if (priv->srcpad) {
@@ -2644,7 +2768,7 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
         (GCallback) caps_notify, stream);
   }
 
-  priv->is_joined = TRUE;
+  priv->joined_bin = gst_object_ref (bin);
   g_mutex_unlock (&priv->lock);
 
   return TRUE;
@@ -2655,76 +2779,36 @@ was_joined:
     g_mutex_unlock (&priv->lock);
     return TRUE;
   }
-link_failed:
+no_ports:
   {
-    GST_WARNING ("failed to link stream %u", idx);
-    gst_object_unref (priv->send_rtp_sink);
-    priv->send_rtp_sink = NULL;
     g_mutex_unlock (&priv->lock);
+    GST_WARNING ("failed to allocate ports %u", idx);
     return FALSE;
   }
-no_udp_protocol:
+link_failed:
   {
-    GST_WARNING ("failed to allocate ports %u", idx);
+    GST_WARNING ("failed to link stream %u", idx);
     gst_object_unref (priv->send_rtp_sink);
     priv->send_rtp_sink = NULL;
-    gst_object_unref (priv->send_src[0]);
-    priv->send_src[0] = NULL;
-    gst_object_unref (priv->send_src[1]);
-    priv->send_src[1] = NULL;
-    gst_object_unref (priv->recv_sink[0]);
-    priv->recv_sink[0] = NULL;
-    gst_object_unref (priv->recv_sink[1]);
-    priv->recv_sink[1] = NULL;
-    if (priv->udpsink[0])
-      gst_element_set_state (priv->udpsink[0], GST_STATE_NULL);
-    if (priv->udpsink[1])
-      gst_element_set_state (priv->udpsink[1], GST_STATE_NULL);
-    if (priv->udpsrc_v4[0]) {
-      gst_element_set_state (priv->udpsrc_v4[0], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_v4[0]);
-      priv->udpsrc_v4[0] = NULL;
-    }
-    if (priv->udpsrc_v4[1]) {
-      gst_element_set_state (priv->udpsrc_v4[1], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_v4[1]);
-      priv->udpsrc_v4[1] = NULL;
-    }
-    if (priv->udpsrc_mcast_v4[0]) {
-      gst_element_set_state (priv->udpsrc_mcast_v4[0], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_mcast_v4[0]);
-      priv->udpsrc_mcast_v4[0] = NULL;
-    }
-    if (priv->udpsrc_mcast_v4[1]) {
-      gst_element_set_state (priv->udpsrc_mcast_v4[1], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_mcast_v4[1]);
-      priv->udpsrc_mcast_v4[1] = NULL;
-    }
-    if (priv->udpsrc_v6[0]) {
-      gst_element_set_state (priv->udpsrc_v6[0], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_v6[0]);
-      priv->udpsrc_v6[0] = NULL;
-    }
-    if (priv->udpsrc_v6[1]) {
-      gst_element_set_state (priv->udpsrc_v6[1], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_v6[1]);
-      priv->udpsrc_v6[1] = NULL;
-    }
-    if (priv->udpsrc_mcast_v6[0]) {
-      gst_element_set_state (priv->udpsrc_mcast_v6[0], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_mcast_v6[0]);
-      priv->udpsrc_mcast_v6[0] = NULL;
-    }
-    if (priv->udpsrc_mcast_v6[1]) {
-      gst_element_set_state (priv->udpsrc_mcast_v6[1], GST_STATE_NULL);
-      gst_object_unref (priv->udpsrc_mcast_v6[1]);
-      priv->udpsrc_mcast_v6[1] = NULL;
-    }
     g_mutex_unlock (&priv->lock);
     return FALSE;
   }
 }
 
+static void
+clear_element (GstBin * bin, GstElement ** elementptr)
+{
+  if (*elementptr) {
+    gst_element_set_locked_state (*elementptr, FALSE);
+    gst_element_set_state (*elementptr, GST_STATE_NULL);
+    if (GST_ELEMENT_PARENT (*elementptr))
+      gst_bin_remove (bin, *elementptr);
+    else
+      gst_object_unref (*elementptr);
+    *elementptr = NULL;
+  }
+}
+
 /**
  * gst_rtsp_stream_leave_bin:
  * @stream: a #GstRTSPStream
@@ -2741,7 +2825,6 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
 {
   GstRTSPStreamPrivate *priv;
   gint i;
-  gboolean is_tcp, is_udp;
 
   g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
@@ -2750,8 +2833,12 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
   priv = stream->priv;
 
   g_mutex_lock (&priv->lock);
-  if (!priv->is_joined)
+  if (priv->joined_bin == NULL)
     goto was_not_joined;
+  if (priv->joined_bin != bin)
+    goto wrong_bin;
+
+  priv->joined_bin = NULL;
 
   /* all transports must be removed by now */
   if (priv->transports != NULL)
@@ -2775,110 +2862,29 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
     priv->recv_rtp_src = NULL;
   }
 
-  is_tcp = priv->protocols & GST_RTSP_LOWER_TRANS_TCP;
-
-  is_udp = ((priv->protocols & GST_RTSP_LOWER_TRANS_UDP) ||
-      (priv->protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST));
-
-
   for (i = 0; i < 2; i++) {
-    if (priv->udpsink[i])
-      gst_element_set_state (priv->udpsink[i], GST_STATE_NULL);
-    if (priv->appsink[i])
-      gst_element_set_state (priv->appsink[i], GST_STATE_NULL);
-    if (priv->appqueue[i])
-      gst_element_set_state (priv->appqueue[i], GST_STATE_NULL);
-    if (priv->udpqueue[i])
-      gst_element_set_state (priv->udpqueue[i], GST_STATE_NULL);
-    if (priv->tee[i])
-      gst_element_set_state (priv->tee[i], GST_STATE_NULL);
-    if (priv->funnel[i])
-      gst_element_set_state (priv->funnel[i], GST_STATE_NULL);
-    if (priv->appsrc[i])
-      gst_element_set_state (priv->appsrc[i], GST_STATE_NULL);
-
-    if (priv->udpsrc_v4[i]) {
-      if (priv->sinkpad || i == 1) {
-        /* and set udpsrc to NULL now before removing */
-        gst_element_set_locked_state (priv->udpsrc_v4[i], FALSE);
-        gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_NULL);
-        /* removing them should also nicely release the request
-         * pads when they finalize */
-        gst_bin_remove (bin, priv->udpsrc_v4[i]);
-      } else {
-        /* we need to set the state to NULL before unref */
-        gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_NULL);
-        gst_object_unref (priv->udpsrc_v4[i]);
-      }
-    }
+    clear_element (bin, &priv->udpsrc_v4[i]);
+    clear_element (bin, &priv->udpsrc_v6[i]);
+    clear_element (bin, &priv->udpqueue[i]);
+    clear_element (bin, &priv->udpsink[i]);
 
-    if (priv->udpsrc_mcast_v4[i]) {
-      if (priv->sinkpad || i == 1) {
-        /* and set udpsrc to NULL now before removing */
-        gst_element_set_locked_state (priv->udpsrc_mcast_v4[i], FALSE);
-        gst_element_set_state (priv->udpsrc_mcast_v4[i], GST_STATE_NULL);
-        /* removing them should also nicely release the request
-         * pads when they finalize */
-        gst_bin_remove (bin, priv->udpsrc_mcast_v4[i]);
-      } else {
-        gst_element_set_state (priv->udpsrc_mcast_v4[i], GST_STATE_NULL);
-        gst_object_unref (priv->udpsrc_mcast_v4[i]);
-      }
-    }
+    clear_element (bin, &priv->mcast_udpsrc_v4[i]);
+    clear_element (bin, &priv->mcast_udpsrc_v6[i]);
+    clear_element (bin, &priv->mcast_udpqueue[i]);
+    clear_element (bin, &priv->mcast_udpsink[i]);
 
-    if (priv->udpsrc_v6[i]) {
-      if (priv->sinkpad || i == 1) {
-        gst_element_set_locked_state (priv->udpsrc_v6[i], FALSE);
-        gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_NULL);
-        gst_bin_remove (bin, priv->udpsrc_v6[i]);
-      } else {
-        gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_NULL);
-        gst_object_unref (priv->udpsrc_v6[i]);
-      }
-    }
-    if (priv->udpsrc_mcast_v6[i]) {
-      if (priv->sinkpad || i == 1) {
-        gst_element_set_locked_state (priv->udpsrc_mcast_v6[i], FALSE);
-        gst_element_set_state (priv->udpsrc_mcast_v6[i], GST_STATE_NULL);
-        gst_bin_remove (bin, priv->udpsrc_mcast_v6[i]);
-      } else {
-        gst_element_set_state (priv->udpsrc_mcast_v6[i], GST_STATE_NULL);
-        gst_object_unref (priv->udpsrc_mcast_v6[i]);
-      }
-    }
+    clear_element (bin, &priv->appsrc[i]);
+    clear_element (bin, &priv->appqueue[i]);
+    clear_element (bin, &priv->appsink[i]);
 
-    if (priv->udpsink[i] && is_udp && (priv->srcpad || i == 1))
-      gst_bin_remove (bin, priv->udpsink[i]);
-    if (priv->appsrc[i] && (priv->sinkpad || i == 1))
-      gst_bin_remove (bin, priv->appsrc[i]);
-    if (priv->appsink[i] && is_tcp && (priv->srcpad || i == 1))
-      gst_bin_remove (bin, priv->appsink[i]);
-    if (priv->appqueue[i] && is_tcp && is_udp && (priv->srcpad || i == 1))
-      gst_bin_remove (bin, priv->appqueue[i]);
-    if (priv->udpqueue[i] && is_tcp && is_udp && (priv->srcpad || i == 1))
-      gst_bin_remove (bin, priv->udpqueue[i]);
-    if (priv->tee[i] && is_tcp && is_udp && (priv->srcpad || i == 1))
-      gst_bin_remove (bin, priv->tee[i]);
-    if (priv->funnel[i] && (priv->sinkpad || i == 1))
-      gst_bin_remove (bin, priv->funnel[i]);
+    clear_element (bin, &priv->tee[i]);
+    clear_element (bin, &priv->funnel[i]);
 
     if (priv->sinkpad || i == 1) {
       gst_element_release_request_pad (rtpbin, priv->recv_sink[i]);
       gst_object_unref (priv->recv_sink[i]);
       priv->recv_sink[i] = NULL;
     }
-
-    priv->udpsrc_v4[i] = NULL;
-    priv->udpsrc_v6[i] = NULL;
-    priv->udpsrc_mcast_v4[i] = NULL;
-    priv->udpsrc_mcast_v6[i] = NULL;
-    priv->udpsink[i] = NULL;
-    priv->appsrc[i] = NULL;
-    priv->appsink[i] = NULL;
-    priv->appqueue[i] = NULL;
-    priv->udpqueue[i] = NULL;
-    priv->tee[i] = NULL;
-    priv->funnel[i] = NULL;
   }
 
   if (priv->srcpad) {
@@ -2901,7 +2907,20 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
   if (priv->srtpdec)
     gst_object_unref (priv->srtpdec);
 
-  priv->is_joined = FALSE;
+  if (priv->mcast_addr_v4)
+    gst_rtsp_address_free (priv->mcast_addr_v4);
+  priv->mcast_addr_v4 = NULL;
+  if (priv->mcast_addr_v6)
+    gst_rtsp_address_free (priv->mcast_addr_v6);
+  priv->mcast_addr_v6 = NULL;
+  if (priv->server_addr_v4)
+    gst_rtsp_address_free (priv->server_addr_v4);
+  priv->server_addr_v4 = NULL;
+  if (priv->server_addr_v6)
+    gst_rtsp_address_free (priv->server_addr_v6);
+  priv->server_addr_v6 = NULL;
+
+  g_clear_object (&priv->joined_bin);
   g_mutex_unlock (&priv->lock);
 
   return TRUE;
@@ -2917,6 +2936,37 @@ transports_not_removed:
     g_mutex_unlock (&priv->lock);
     return FALSE;
   }
+wrong_bin:
+  {
+    GST_ERROR_OBJECT (stream, "leaving the wrong bin");
+    g_mutex_unlock (&priv->lock);
+    return FALSE;
+  }
+}
+
+/**
+ * gst_rtsp_stream_get_joined_bin:
+ * @stream: a #GstRTSPStream
+ *
+ * Get the previous joined bin with gst_rtsp_stream_join_bin() or NULL.
+ *
+ * Return: (transfer full): the joined bin or NULL.
+ */
+GstBin *
+gst_rtsp_stream_get_joined_bin (GstRTSPStream * stream)
+{
+  GstRTSPStreamPrivate *priv;
+  GstBin *bin = NULL;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+  priv = stream->priv;
+
+  g_mutex_lock (&priv->lock);
+  bin = priv->joined_bin ? gst_object_ref (priv->joined_bin) : NULL;
+  g_mutex_unlock (&priv->lock);
+
+  return bin;
 }
 
 /**
@@ -3103,7 +3153,7 @@ gst_rtsp_stream_recv_rtp (GstRTSPStream * stream, GstBuffer * buffer)
   g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), GST_FLOW_ERROR);
   priv = stream->priv;
   g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
-  g_return_val_if_fail (priv->is_joined, FALSE);
+  g_return_val_if_fail (priv->joined_bin != NULL, FALSE);
 
   g_mutex_lock (&priv->lock);
   if (priv->appsrc[0])
@@ -3169,7 +3219,7 @@ gst_rtsp_stream_recv_rtcp (GstRTSPStream * stream, GstBuffer * buffer)
   priv = stream->priv;
   g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
 
-  if (!priv->is_joined) {
+  if (priv->joined_bin == NULL) {
     gst_buffer_unref (buffer);
     return GST_FLOW_NOT_LINKED;
   }
@@ -3227,6 +3277,16 @@ update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans,
 
   switch (tr->lower_transport) {
     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
+    {
+      if (add) {
+        if (!check_mcast_part_for_transport (stream, tr))
+          goto mcast_error;
+        priv->transports = g_list_prepend (priv->transports, trans);
+      } else {
+        priv->transports = g_list_remove (priv->transports, trans);
+      }
+      break;
+    }
     case GST_RTSP_LOWER_TRANS_UDP:
     {
       gchar *dest;
@@ -3288,6 +3348,10 @@ unknown_transport:
     GST_INFO ("Unknown transport %d", tr->lower_transport);
     return FALSE;
   }
+mcast_error:
+  {
+    return FALSE;
+  }
 }
 
 
@@ -3315,7 +3379,7 @@ gst_rtsp_stream_add_transport (GstRTSPStream * stream,
   g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
   priv = stream->priv;
   g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
-  g_return_val_if_fail (priv->is_joined, FALSE);
+  g_return_val_if_fail (priv->joined_bin != NULL, FALSE);
 
   g_mutex_lock (&priv->lock);
   res = update_transport (stream, trans, TRUE);
@@ -3348,7 +3412,7 @@ gst_rtsp_stream_remove_transport (GstRTSPStream * stream,
   g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
   priv = stream->priv;
   g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
-  g_return_val_if_fail (priv->is_joined, FALSE);
+  g_return_val_if_fail (priv->joined_bin != NULL, FALSE);
 
   g_mutex_lock (&priv->lock);
   res = update_transport (stream, trans, FALSE);