rtspserver: Add udp-mcast transport SETUP test
[platform/upstream/gstreamer.git] / tests / check / gst / rtspserver.c
index 9cd07ad..ec66f99 100644 (file)
@@ -1,9 +1,8 @@
-/* GStreamer
- *
- * unit test for GstRTSPServer
- *
+/* GStreamer unit test for GstRTSPServer
  * Copyright (C) 2012 Axis Communications <dev-gstreamer at axis dot com>
- * @author David Svensson Fors <davidsf at axis dot com>
+ *   @author David Svensson Fors <davidsf at axis dot com>
+ * Copyright (C) 2015 Centricular Ltd
+ *   @author Tim-Philipp Müller <tim@centricular.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -23,6 +22,8 @@
 
 #include <gst/check/gstcheck.h>
 #include <gst/sdp/gstsdpmessage.h>
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
 
 #include <stdio.h>
 #include <netinet/in.h>
@@ -59,86 +60,101 @@ iterate (void)
   }
 }
 
-/* returns an unused port that can be used by the test */
-static int
-get_unused_port (gint type)
+static void
+get_client_ports_full (GstRTSPRange * range, GSocket ** rtp_socket,
+    GSocket ** rtcp_socket)
 {
-  int sock;
-  struct sockaddr_in addr;
-  socklen_t addr_len;
-  gint port;
-
-  /* create socket */
-  fail_unless ((sock = socket (AF_INET, type, 0)) > 0);
-
-  /* pass port 0 to bind, which will bind to any free port */
-  memset (&addr, 0, sizeof addr);
-  addr.sin_family = AF_INET;
-  addr.sin_addr.s_addr = INADDR_ANY;
-  addr.sin_port = htons (0);
-  fail_unless (bind (sock, (struct sockaddr *) &addr, sizeof addr) == 0);
-
-  /* ask what port was bound using getsockname */
-  addr_len = sizeof addr;
-  memset (&addr, 0, addr_len);
-  fail_unless (getsockname (sock, (struct sockaddr *) &addr, &addr_len) == 0);
-  port = ntohs (addr.sin_port);
-
-  /* close the socket so the port gets unbound again (and can be used by the
-   * test) */
-  close (sock);
-
-  return port;
-}
+  GSocket *rtp = NULL;
+  GSocket *rtcp = NULL;
+  gint rtp_port = 0;
+  gint rtcp_port;
+  GInetAddress *anyaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
+  GSocketAddress *sockaddr;
+  gboolean bound;
+
+  for (;;) {
+    if (rtp_port != 0)
+      rtp_port += 2;
+
+    rtp = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
+        G_SOCKET_PROTOCOL_UDP, NULL);
+    fail_unless (rtp != NULL);
+
+    sockaddr = g_inet_socket_address_new (anyaddr, rtp_port);
+    fail_unless (sockaddr != NULL);
+    bound = g_socket_bind (rtp, sockaddr, FALSE, NULL);
+    g_object_unref (sockaddr);
+    if (!bound) {
+      g_object_unref (rtp);
+      continue;
+    }
 
-/* returns TRUE if the given port is not currently bound */
-static gboolean
-port_is_unused (gint port, gint type)
-{
-  int sock;
-  struct sockaddr_in addr;
-  gboolean is_bound;
+    sockaddr = g_socket_get_local_address (rtp, NULL);
+    fail_unless (sockaddr != NULL && G_IS_INET_SOCKET_ADDRESS (sockaddr));
+    rtp_port =
+        g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (sockaddr));
+    g_object_unref (sockaddr);
 
-  /* create socket */
-  fail_unless ((sock = socket (AF_INET, type, 0)) > 0);
+    if (rtp_port % 2 != 0) {
+      rtp_port += 1;
+      g_object_unref (rtp);
+      continue;
+    }
+
+    rtcp_port = rtp_port + 1;
 
-  /* check if the port is already bound by trying to bind to it (again) */
-  memset (&addr, 0, sizeof addr);
-  addr.sin_family = AF_INET;
-  addr.sin_addr.s_addr = INADDR_ANY;
-  addr.sin_port = htons (port);
-  is_bound = (bind (sock, (struct sockaddr *) &addr, sizeof addr) != 0);
+    rtcp = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
+        G_SOCKET_PROTOCOL_UDP, NULL);
+    fail_unless (rtcp != NULL);
+
+    sockaddr = g_inet_socket_address_new (anyaddr, rtcp_port);
+    fail_unless (sockaddr != NULL);
+    bound = g_socket_bind (rtcp, sockaddr, FALSE, NULL);
+    g_object_unref (sockaddr);
+    if (!bound) {
+      g_object_unref (rtp);
+      g_object_unref (rtcp);
+      continue;
+    }
 
-  /* close the socket, which will unbind if bound by our call to bind */
-  close (sock);
+    sockaddr = g_socket_get_local_address (rtcp, NULL);
+    fail_unless (sockaddr != NULL && G_IS_INET_SOCKET_ADDRESS (sockaddr));
+    fail_unless (rtcp_port ==
+        g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (sockaddr)));
+    g_object_unref (sockaddr);
 
-  return !is_bound;
+    break;
+  }
+
+  range->min = rtp_port;
+  range->max = rtcp_port;
+  if (rtp_socket)
+    *rtp_socket = rtp;
+  else
+    g_object_unref (rtp);
+  if (rtcp_socket)
+    *rtcp_socket = rtcp;
+  else
+    g_object_unref (rtcp);
+  GST_DEBUG ("client_port=%d-%d", range->min, range->max);
+  g_object_unref (anyaddr);
 }
 
 /* get a free rtp/rtcp client port pair */
 static void
 get_client_ports (GstRTSPRange * range)
 {
-  gint rtp_port;
-  gint rtcp_port;
-
-  /* get a pair of unused ports, where the rtp port is even */
-  do {
-    rtp_port = get_unused_port (SOCK_DGRAM);
-    rtcp_port = rtp_port + 1;
-  } while (rtp_port % 2 != 0 || !port_is_unused (rtcp_port, SOCK_DGRAM));
-  range->min = rtp_port;
-  range->max = rtcp_port;
-  GST_DEBUG ("client_port=%d-%d", range->min, range->max);
+  get_client_ports_full (range, NULL, NULL);
 }
 
 /* start the tested rtsp server */
 static void
-start_server ()
+start_server (void)
 {
   GstRTSPMountPoints *mounts;
   gchar *service;
   GstRTSPMediaFactory *factory;
+  GstRTSPAddressPool *pool;
 
   mounts = gst_rtsp_server_get_mount_points (server);
 
@@ -146,26 +162,70 @@ start_server ()
 
   gst_rtsp_media_factory_set_launch (factory,
       "( " VIDEO_PIPELINE "  " AUDIO_PIPELINE " )");
-
   gst_rtsp_mount_points_add_factory (mounts, TEST_MOUNT_POINT, factory);
   g_object_unref (mounts);
 
-  /* set port */
-  test_port = get_unused_port (SOCK_STREAM);
-  service = g_strdup_printf ("%d", test_port);
-  gst_rtsp_server_set_service (server, service);
+  /* use an address pool for multicast */
+  pool = gst_rtsp_address_pool_new ();
+  gst_rtsp_address_pool_add_range (pool,
+      "224.3.0.0", "224.3.0.10", 5000, 5010, 16);
+  gst_rtsp_media_factory_set_address_pool (factory, pool);
+  gst_object_unref (pool);
+
+  /* set port to any */
+  gst_rtsp_server_set_service (server, "0");
+
+  /* attach to default main context */
+  source_id = gst_rtsp_server_attach (server, NULL);
+  fail_if (source_id == 0);
+
+  /* get port */
+  service = gst_rtsp_server_get_service (server);
+  test_port = atoi (service);
+  fail_unless (test_port != 0);
   g_free (service);
 
+  GST_DEBUG ("rtsp server listening on port %d", test_port);
+}
+
+/* start the testing rtsp server for RECORD mode */
+static GstRTSPMediaFactory *
+start_record_server (const gchar * launch_line)
+{
+  GstRTSPMediaFactory *factory;
+  GstRTSPMountPoints *mounts;
+  gchar *service;
+
+  mounts = gst_rtsp_server_get_mount_points (server);
+
+  factory = gst_rtsp_media_factory_new ();
+
+  gst_rtsp_media_factory_set_transport_mode (factory,
+      GST_RTSP_TRANSPORT_MODE_RECORD);
+  gst_rtsp_media_factory_set_launch (factory, launch_line);
+  gst_rtsp_mount_points_add_factory (mounts, TEST_MOUNT_POINT, factory);
+  g_object_unref (mounts);
+
+  /* set port to any */
+  gst_rtsp_server_set_service (server, "0");
+
   /* attach to default main context */
   source_id = gst_rtsp_server_attach (server, NULL);
   fail_if (source_id == 0);
 
+  /* get port */
+  service = gst_rtsp_server_get_service (server);
+  test_port = atoi (service);
+  fail_unless (test_port != 0);
+  g_free (service);
+
   GST_DEBUG ("rtsp server listening on port %d", test_port);
+  return factory;
 }
 
 /* stop the tested rtsp server */
 static void
-stop_server ()
+stop_server (void)
 {
   g_source_remove (source_id);
   source_id = 0;
@@ -185,7 +245,7 @@ connect_to_server (gint port, const gchar * mount_point)
   address = gst_rtsp_server_get_address (server);
   uri_string = g_strdup_printf ("rtsp://%s:%d%s", address, port, mount_point);
   g_free (address);
-  gst_rtsp_url_parse (uri_string, &url);
+  fail_unless (gst_rtsp_url_parse (uri_string, &url) == GST_RTSP_OK);
   g_free (uri_string);
 
   fail_unless (gst_rtsp_connection_create (url, &conn) == GST_RTSP_OK);
@@ -251,10 +311,12 @@ read_response (GstRTSPConnection * conn)
 /* send an rtsp request and receive response. gchar** parameters are out
  * parameters that have to be freed by the caller */
 static GstRTSPStatusCode
-do_request (GstRTSPConnection * conn, GstRTSPMethod method,
+do_request_full (GstRTSPConnection * conn, GstRTSPMethod method,
     const gchar * control, const gchar * session_in, const gchar * transport_in,
+    const gchar * range_in, const gchar * require_in,
     gchar ** content_type, gchar ** content_base, gchar ** body,
-    gchar ** session_out, gchar ** transport_out)
+    gchar ** session_out, gchar ** transport_out, gchar ** range_out,
+    gchar ** unsupported_out)
 {
   GstRTSPMessage *request;
   GstRTSPMessage *response;
@@ -271,6 +333,12 @@ do_request (GstRTSPConnection * conn, GstRTSPMethod method,
   if (transport_in) {
     gst_rtsp_message_add_header (request, GST_RTSP_HDR_TRANSPORT, transport_in);
   }
+  if (range_in) {
+    gst_rtsp_message_add_header (request, GST_RTSP_HDR_RANGE, range_in);
+  }
+  if (require_in) {
+    gst_rtsp_message_add_header (request, GST_RTSP_HDR_REQUIRE, require_in);
+  }
 
   /* send request */
   fail_unless (send_request (conn, request));
@@ -284,6 +352,11 @@ do_request (GstRTSPConnection * conn, GstRTSPMethod method,
   /* check status line */
   gst_rtsp_message_parse_response (response, &code, NULL, NULL);
   if (code != GST_RTSP_STS_OK) {
+    if (unsupported_out != NULL && code == GST_RTSP_STS_OPTION_NOT_SUPPORTED) {
+      gst_rtsp_message_get_header (response, GST_RTSP_HDR_UNSUPPORTED,
+          &value, 0);
+      *unsupported_out = g_strdup (value);
+    }
     gst_rtsp_message_free (response);
     return code;
   }
@@ -324,18 +397,36 @@ do_request (GstRTSPConnection * conn, GstRTSPMethod method,
     gst_rtsp_message_get_header (response, GST_RTSP_HDR_TRANSPORT, &value, 0);
     *transport_out = g_strdup (value);
   }
+  if (range_out) {
+    gst_rtsp_message_get_header (response, GST_RTSP_HDR_RANGE, &value, 0);
+    *range_out = g_strdup (value);
+  }
 
   gst_rtsp_message_free (response);
   return code;
 }
 
+/* send an rtsp request and receive response. gchar** parameters are out
+ * parameters that have to be freed by the caller */
+static GstRTSPStatusCode
+do_request (GstRTSPConnection * conn, GstRTSPMethod method,
+    const gchar * control, const gchar * session_in,
+    const gchar * transport_in, const gchar * range_in,
+    gchar ** content_type, gchar ** content_base, gchar ** body,
+    gchar ** session_out, gchar ** transport_out, gchar ** range_out)
+{
+  return do_request_full (conn, method, control, session_in, transport_in,
+      range_in, NULL, content_type, content_base, body, session_out,
+      transport_out, range_out, NULL);
+}
+
 /* send an rtsp request with a method and a session, and receive response */
 static GstRTSPStatusCode
 do_simple_request (GstRTSPConnection * conn, GstRTSPMethod method,
     const gchar * session)
 {
   return do_request (conn, method, NULL, session, NULL, NULL, NULL,
-      NULL, NULL, NULL);
+      NULL, NULL, NULL, NULL, NULL);
 }
 
 /* send a DESCRIBE request and receive response. returns a received
@@ -344,15 +435,16 @@ static GstSDPMessage *
 do_describe (GstRTSPConnection * conn, const gchar * mount_point)
 {
   GstSDPMessage *sdp_message;
-  gchar *content_type;
-  gchar *content_base;
-  gchar *body;
+  gchar *content_type = NULL;
+  gchar *content_base = NULL;
+  gchar *body = NULL;
   gchar *address;
   gchar *expected_content_base;
 
   /* send DESCRIBE request */
-  fail_unless (do_request (conn, GST_RTSP_DESCRIBE, NULL, NULL, NULL,
-          &content_type, &content_base, &body, NULL, NULL) == GST_RTSP_STS_OK);
+  fail_unless (do_request (conn, GST_RTSP_DESCRIBE, NULL, NULL, NULL, NULL,
+          &content_type, &content_base, &body, NULL, NULL, NULL) ==
+      GST_RTSP_STS_OK);
 
   /* check response values */
   fail_unless (!g_strcmp0 (content_type, "application/sdp"));
@@ -381,13 +473,14 @@ do_describe (GstRTSPConnection * conn, const gchar * mount_point)
  * session string that must be freed by the caller. the returned
  * transport must be freed by the caller. */
 static GstRTSPStatusCode
-do_setup (GstRTSPConnection * conn, const gchar * control,
-    const GstRTSPRange * client_ports, gchar ** session,
-    GstRTSPTransport ** transport)
+do_setup_full (GstRTSPConnection * conn, const gchar * control,
+    GstRTSPLowerTrans lower_transport, const GstRTSPRange * client_ports,
+    const gchar * require, gchar ** session, GstRTSPTransport ** transport,
+    gchar ** unsupported)
 {
   GstRTSPStatusCode code;
   gchar *session_in = NULL;
-  gchar *transport_string_in = NULL;
+  GString *transport_string_in = NULL;
   gchar **session_out = NULL;
   gchar *transport_string_out = NULL;
 
@@ -399,14 +492,36 @@ do_setup (GstRTSPConnection * conn, const gchar * control,
       session_out = session;
     }
   }
-  transport_string_in =
-      g_strdup_printf (TEST_PROTO ";unicast;client_port=%d-%d",
-      client_ports->min, client_ports->max);
+
+  transport_string_in = g_string_new (TEST_PROTO);
+  switch (lower_transport) {
+    case GST_RTSP_LOWER_TRANS_UDP:
+      transport_string_in =
+          g_string_append (transport_string_in, "/UDP;unicast");
+      break;
+    case GST_RTSP_LOWER_TRANS_UDP_MCAST:
+      transport_string_in =
+          g_string_append (transport_string_in, "/UDP;multicast");
+      break;
+    case GST_RTSP_LOWER_TRANS_TCP:
+      transport_string_in =
+          g_string_append (transport_string_in, "/TCP;unicast");
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  if (client_ports) {
+    g_string_append_printf (transport_string_in, ";client_port=%d-%d",
+        client_ports->min, client_ports->max);
+  }
+
   code =
-      do_request (conn, GST_RTSP_SETUP, control, session_in,
-      transport_string_in, NULL, NULL, NULL, session_out,
-      &transport_string_out);
-  g_free (transport_string_in);
+      do_request_full (conn, GST_RTSP_SETUP, control, session_in,
+      transport_string_in->str, NULL, require, NULL, NULL, NULL, session_out,
+      &transport_string_out, NULL, unsupported);
+  g_string_free (transport_string_in, TRUE);
 
   if (transport_string_out) {
     /* create transport */
@@ -415,10 +530,23 @@ do_setup (GstRTSPConnection * conn, const gchar * control,
             *transport) == GST_RTSP_OK);
     g_free (transport_string_out);
   }
-
+  GST_INFO ("code=%d", code);
   return code;
 }
 
+/* send a SETUP request and receive response. if *session is not NULL,
+ * it is used in the request. otherwise, *session is set to a returned
+ * session string that must be freed by the caller. the returned
+ * transport must be freed by the caller. */
+static GstRTSPStatusCode
+do_setup (GstRTSPConnection * conn, const gchar * control,
+    const GstRTSPRange * client_ports, gchar ** session,
+    GstRTSPTransport ** transport)
+{
+  return do_setup_full (conn, control, GST_RTSP_LOWER_TRANS_UDP, client_ports,
+      NULL, session, transport, NULL);
+}
+
 /* fixture setup function */
 static void
 setup (void)
@@ -513,6 +641,27 @@ GST_START_TEST (test_describe)
 
 GST_END_TEST;
 
+GST_START_TEST (test_describe_record_media)
+{
+  GstRTSPConnection *conn;
+
+  start_record_server ("( fakesink name=depay0 )");
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  /* send DESCRIBE request */
+  fail_unless_equals_int (do_request (conn, GST_RTSP_DESCRIBE, NULL, NULL, NULL,
+          NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+      GST_RTSP_STS_METHOD_NOT_ALLOWED);
+
+  /* clean up and iterate so the clean-up can finish */
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_describe_non_existing_mount_point)
 {
   GstRTSPConnection *conn;
@@ -533,14 +682,15 @@ GST_START_TEST (test_describe_non_existing_mount_point)
 
 GST_END_TEST;
 
-GST_START_TEST (test_setup)
+static void
+do_test_setup (GstRTSPLowerTrans lower_transport)
 {
   GstRTSPConnection *conn;
   GstSDPMessage *sdp_message = NULL;
   const GstSDPMedia *sdp_media;
   const gchar *video_control;
   const gchar *audio_control;
-  GstRTSPRange client_ports;
+  GstRTSPRange client_ports = { 0 };
   gchar *session = NULL;
   GstRTSPTransport *video_transport = NULL;
   GstRTSPTransport *audio_transport = NULL;
@@ -561,29 +711,194 @@ GST_START_TEST (test_setup)
   get_client_ports (&client_ports);
 
   /* send SETUP request for video */
-  fail_unless (do_setup (conn, video_control, &client_ports, &session,
-          &video_transport) == GST_RTSP_STS_OK);
+  fail_unless (do_setup_full (conn, video_control, lower_transport,
+          &client_ports, NULL, &session, &video_transport,
+          NULL) == GST_RTSP_STS_OK);
   GST_DEBUG ("set up video %s, got session '%s'", video_control, session);
 
   /* check response from SETUP */
   fail_unless (video_transport->trans == GST_RTSP_TRANS_RTP);
   fail_unless (video_transport->profile == GST_RTSP_PROFILE_AVP);
-  fail_unless (video_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
+  fail_unless (video_transport->lower_transport == lower_transport);
   fail_unless (video_transport->mode_play);
   gst_rtsp_transport_free (video_transport);
 
   /* send SETUP request for audio */
-  fail_unless (do_setup (conn, audio_control, &client_ports, &session,
-          &audio_transport) == GST_RTSP_STS_OK);
+  fail_unless (do_setup_full (conn, audio_control, lower_transport,
+          &client_ports, NULL, &session, &audio_transport,
+          NULL) == GST_RTSP_STS_OK);
   GST_DEBUG ("set up audio %s with session '%s'", audio_control, session);
 
   /* check response from SETUP */
   fail_unless (audio_transport->trans == GST_RTSP_TRANS_RTP);
   fail_unless (audio_transport->profile == GST_RTSP_PROFILE_AVP);
-  fail_unless (audio_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
+  fail_unless (audio_transport->lower_transport == lower_transport);
   fail_unless (audio_transport->mode_play);
   gst_rtsp_transport_free (audio_transport);
 
+  /* send TEARDOWN request and check that we get 200 OK */
+  fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
+          session) == GST_RTSP_STS_OK);
+
+  /* clean up and iterate so the clean-up can finish */
+  g_free (session);
+  gst_sdp_message_free (sdp_message);
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_START_TEST (test_setup_udp)
+{
+  do_test_setup (GST_RTSP_LOWER_TRANS_UDP);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_setup_tcp)
+{
+  do_test_setup (GST_RTSP_LOWER_TRANS_TCP);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_setup_udp_mcast)
+{
+  do_test_setup (GST_RTSP_LOWER_TRANS_UDP_MCAST);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_setup_twice)
+{
+  GstRTSPConnection *conn;
+  GstSDPMessage *sdp_message;
+  const GstSDPMedia *sdp_media;
+  const gchar *video_control;
+  GstRTSPRange client_ports;
+  GstRTSPTransport *video_transport = NULL;
+  gchar *session1 = NULL;
+  gchar *session2 = NULL;
+
+  start_server ();
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  /* we wan't more than one session for this connection */
+  gst_rtsp_connection_set_remember_session_id (conn, FALSE);
+
+  sdp_message = do_describe (conn, TEST_MOUNT_POINT);
+
+  /* get the control url */
+  fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
+  sdp_media = gst_sdp_message_get_media (sdp_message, 0);
+  video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+  get_client_ports (&client_ports);
+
+  /* send SETUP request for one session */
+  fail_unless (do_setup (conn, video_control, &client_ports, &session1,
+          &video_transport) == GST_RTSP_STS_OK);
+  GST_DEBUG ("set up video %s, got session '%s'", video_control, session1);
+
+  /* check response from SETUP */
+  fail_unless (video_transport->trans == GST_RTSP_TRANS_RTP);
+  fail_unless (video_transport->profile == GST_RTSP_PROFILE_AVP);
+  fail_unless (video_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
+  fail_unless (video_transport->mode_play);
+  gst_rtsp_transport_free (video_transport);
+
+  /* send SETUP request for another session */
+  fail_unless (do_setup (conn, video_control, &client_ports, &session2,
+          &video_transport) == GST_RTSP_STS_OK);
+  GST_DEBUG ("set up video %s, got session '%s'", video_control, session2);
+
+  /* check response from SETUP */
+  fail_unless (video_transport->trans == GST_RTSP_TRANS_RTP);
+  fail_unless (video_transport->profile == GST_RTSP_PROFILE_AVP);
+  fail_unless (video_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
+  fail_unless (video_transport->mode_play);
+  gst_rtsp_transport_free (video_transport);
+
+  /* session can not be the same */
+  fail_unless (strcmp (session1, session2));
+
+  /* send TEARDOWN request for the first session */
+  fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
+          session1) == GST_RTSP_STS_OK);
+
+  /* send TEARDOWN request for the second session */
+  fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
+          session2) == GST_RTSP_STS_OK);
+
+  g_free (session1);
+  g_free (session2);
+  gst_sdp_message_free (sdp_message);
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_setup_with_require_header)
+{
+  GstRTSPConnection *conn;
+  GstSDPMessage *sdp_message = NULL;
+  const GstSDPMedia *sdp_media;
+  const gchar *video_control;
+  GstRTSPRange client_ports;
+  gchar *session = NULL;
+  gchar *unsupported = NULL;
+  GstRTSPTransport *video_transport = NULL;
+
+  start_server ();
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  sdp_message = do_describe (conn, TEST_MOUNT_POINT);
+
+  /* get control strings from DESCRIBE response */
+  fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
+  sdp_media = gst_sdp_message_get_media (sdp_message, 0);
+  video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+  get_client_ports (&client_ports);
+
+  /* send SETUP request for video, with single Require header */
+  fail_unless_equals_int (do_setup_full (conn, video_control,
+          GST_RTSP_LOWER_TRANS_UDP, &client_ports, "funky-feature", &session,
+          &video_transport, &unsupported), GST_RTSP_STS_OPTION_NOT_SUPPORTED);
+  fail_unless_equals_string (unsupported, "funky-feature");
+  g_free (unsupported);
+  unsupported = NULL;
+
+  /* send SETUP request for video, with multiple Require headers */
+  fail_unless_equals_int (do_setup_full (conn, video_control,
+          GST_RTSP_LOWER_TRANS_UDP, &client_ports,
+          "funky-feature, foo-bar, superburst", &session, &video_transport,
+          &unsupported), GST_RTSP_STS_OPTION_NOT_SUPPORTED);
+  fail_unless_equals_string (unsupported, "funky-feature, foo-bar, superburst");
+  g_free (unsupported);
+  unsupported = NULL;
+
+  /* ok, just do a normal setup then (make sure that still works) */
+  fail_unless_equals_int (do_setup (conn, video_control, &client_ports,
+          &session, &video_transport), GST_RTSP_STS_OK);
+
+  GST_DEBUG ("set up video %s, got session '%s'", video_control, session);
+
+  /* check response from SETUP */
+  fail_unless (video_transport->trans == GST_RTSP_TRANS_RTP);
+  fail_unless (video_transport->profile == GST_RTSP_PROFILE_AVP);
+  fail_unless (video_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
+  fail_unless (video_transport->mode_play);
+  gst_rtsp_transport_free (video_transport);
+
+  /* send TEARDOWN request and check that we get 200 OK */
+  fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
+          session) == GST_RTSP_STS_OK);
+
   /* clean up and iterate so the clean-up can finish */
   g_free (session);
   gst_sdp_message_free (sdp_message);
@@ -614,17 +929,85 @@ GST_START_TEST (test_setup_non_existing_stream)
   gst_rtsp_connection_free (conn);
   stop_server ();
   iterate ();
-
-  /* need to unref the server here, otherwise threads will remain
-   * and teardown won't be run */
-  g_object_unref (server);
-  server = NULL;
 }
 
 GST_END_TEST;
 
 static void
-do_test_play (void)
+receive_rtp (GSocket * socket, GSocketAddress ** addr)
+{
+  GstBuffer *buffer = gst_buffer_new_allocate (NULL, 65536, NULL);
+
+  for (;;) {
+    gssize bytes;
+    GstMapInfo map = GST_MAP_INFO_INIT;
+    GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
+
+    gst_buffer_map (buffer, &map, GST_MAP_WRITE);
+    bytes = g_socket_receive_from (socket, addr, (gchar *) map.data,
+        map.maxsize, NULL, NULL);
+    fail_unless (bytes > 0);
+    gst_buffer_unmap (buffer, &map);
+    gst_buffer_set_size (buffer, bytes);
+
+    if (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtpbuffer)) {
+      gst_rtp_buffer_unmap (&rtpbuffer);
+      break;
+    }
+
+    if (addr)
+      g_clear_object (addr);
+  }
+
+  gst_buffer_unref (buffer);
+}
+
+static void
+receive_rtcp (GSocket * socket, GSocketAddress ** addr, GstRTCPType type)
+{
+  GstBuffer *buffer = gst_buffer_new_allocate (NULL, 65536, NULL);
+
+  for (;;) {
+    gssize bytes;
+    GstMapInfo map = GST_MAP_INFO_INIT;
+
+    gst_buffer_map (buffer, &map, GST_MAP_WRITE);
+    bytes = g_socket_receive_from (socket, addr, (gchar *) map.data,
+        map.maxsize, NULL, NULL);
+    fail_unless (bytes > 0);
+    gst_buffer_unmap (buffer, &map);
+    gst_buffer_set_size (buffer, bytes);
+
+    if (gst_rtcp_buffer_validate (buffer)) {
+      GstRTCPBuffer rtcpbuffer = GST_RTCP_BUFFER_INIT;
+      GstRTCPPacket packet;
+
+      if (type) {
+        fail_unless (gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcpbuffer));
+        fail_unless (gst_rtcp_buffer_get_first_packet (&rtcpbuffer, &packet));
+        do {
+          if (gst_rtcp_packet_get_type (&packet) == type) {
+            gst_rtcp_buffer_unmap (&rtcpbuffer);
+            goto done;
+          }
+        } while (gst_rtcp_packet_move_to_next (&packet));
+        gst_rtcp_buffer_unmap (&rtcpbuffer);
+      } else {
+        break;
+      }
+    }
+
+    if (addr)
+      g_clear_object (addr);
+  }
+
+done:
+
+  gst_buffer_unref (buffer);
+}
+
+static void
+do_test_play_full (const gchar * range, GstRTSPLowerTrans lower_transport)
 {
   GstRTSPConnection *conn;
   GstSDPMessage *sdp_message = NULL;
@@ -635,6 +1018,8 @@ do_test_play (void)
   gchar *session = NULL;
   GstRTSPTransport *video_transport = NULL;
   GstRTSPTransport *audio_transport = NULL;
+  GSocket *rtp_socket, *rtcp_socket;
+  gchar *range_out = NULL;
 
   conn = connect_to_server (test_port, TEST_MOUNT_POINT);
 
@@ -647,23 +1032,38 @@ do_test_play (void)
   sdp_media = gst_sdp_message_get_media (sdp_message, 1);
   audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
 
-  get_client_ports (&client_port);
+  get_client_ports_full (&client_port, &rtp_socket, &rtcp_socket);
 
   /* do SETUP for video and audio */
-  fail_unless (do_setup (conn, video_control, &client_port, &session,
-          &video_transport) == GST_RTSP_STS_OK);
-  fail_unless (do_setup (conn, audio_control, &client_port, &session,
-          &audio_transport) == GST_RTSP_STS_OK);
+  fail_unless (do_setup_full (conn, video_control, lower_transport,
+          &client_port, NULL, &session, &video_transport,
+          NULL) == GST_RTSP_STS_OK);
+  fail_unless (do_setup_full (conn, audio_control, lower_transport,
+          &client_port, NULL, &session, &audio_transport,
+          NULL) == GST_RTSP_STS_OK);
 
   /* send PLAY request and check that we get 200 OK */
-  fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
-          session) == GST_RTSP_STS_OK);
+  fail_unless (do_request (conn, GST_RTSP_PLAY, NULL, session, NULL, range,
+          NULL, NULL, NULL, NULL, NULL, &range_out) == GST_RTSP_STS_OK);
+  if (range)
+    fail_unless_equals_string (range, range_out);
+  g_free (range_out);
+
+  receive_rtp (rtp_socket, NULL);
+  receive_rtcp (rtcp_socket, NULL, 0);
 
   /* send TEARDOWN request and check that we get 200 OK */
   fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
           session) == GST_RTSP_STS_OK);
 
+  /* FIXME: The rtsp-server always disconnects the transport before
+   * sending the RTCP BYE
+   * receive_rtcp (rtcp_socket, NULL, GST_RTCP_TYPE_BYE);
+   */
+
   /* clean up and iterate so the clean-up can finish */
+  g_object_unref (rtp_socket);
+  g_object_unref (rtcp_socket);
   g_free (session);
   gst_rtsp_transport_free (video_transport);
   gst_rtsp_transport_free (audio_transport);
@@ -671,12 +1071,17 @@ do_test_play (void)
   gst_rtsp_connection_free (conn);
 }
 
+static void
+do_test_play (const gchar * range)
+{
+  do_test_play_full (range, GST_RTSP_LOWER_TRANS_UDP);
+}
 
 GST_START_TEST (test_play)
 {
   start_server ();
 
-  do_test_play ();
+  do_test_play (NULL);
 
   stop_server ();
   iterate ();
@@ -733,7 +1138,7 @@ GST_START_TEST (test_bind_already_in_use)
 
   /* cleanup */
   g_object_unref (serv);
-  g_socket_listener_close (G_SOCKET_LISTENER (service));
+  g_socket_service_stop (service);
   g_object_unref (service);
 }
 
@@ -742,11 +1147,15 @@ GST_END_TEST;
 
 GST_START_TEST (test_play_multithreaded)
 {
-  gst_rtsp_server_set_max_threads (server, 2);
+  GstRTSPThreadPool *pool;
+
+  pool = gst_rtsp_server_get_thread_pool (server);
+  gst_rtsp_thread_pool_set_max_threads (pool, 2);
+  g_object_unref (pool);
 
   start_server ();
 
-  do_test_play ();
+  do_test_play (NULL);
 
   stop_server ();
   iterate ();
@@ -788,8 +1197,11 @@ GST_START_TEST (test_play_multithreaded_block_in_describe)
   GstRTSPMessage *request;
   GstRTSPMessage *response;
   GstRTSPStatusCode code;
+  GstRTSPThreadPool *pool;
 
-  gst_rtsp_server_set_max_threads (server, 2);
+  pool = gst_rtsp_server_get_thread_pool (server);
+  gst_rtsp_thread_pool_set_max_threads (pool, 2);
+  g_object_unref (pool);
 
   mounts = gst_rtsp_server_get_mount_points (server);
   fail_unless (mounts != NULL);
@@ -817,7 +1229,7 @@ GST_START_TEST (test_play_multithreaded_block_in_describe)
   g_mutex_unlock (&check_mutex);
 
   /* Do a second connection while the first one is blocked */
-  do_test_play ();
+  do_test_play (NULL);
 
   /* Now unblock the describe */
   g_mutex_lock (&check_mutex);
@@ -870,10 +1282,12 @@ GST_START_TEST (test_play_multithreaded_timeout_client)
   GstRTSPTransport *video_transport = NULL;
   GstRTSPTransport *audio_transport = NULL;
   GstRTSPSessionPool *pool;
-  GstRTSPMessage *request;
-  GstRTSPMessage *response;
+  GstRTSPThreadPool *thread_pool;
+
+  thread_pool = gst_rtsp_server_get_thread_pool (server);
+  gst_rtsp_thread_pool_set_max_threads (thread_pool, 2);
+  g_object_unref (thread_pool);
 
-  gst_rtsp_server_set_max_threads (server, 2);
   pool = gst_rtsp_server_get_session_pool (server);
   g_signal_connect (server, "client-connected",
       G_CALLBACK (session_connected_new_session_cb), new_session_timeout_one);
@@ -895,10 +1309,12 @@ GST_START_TEST (test_play_multithreaded_timeout_client)
   get_client_ports (&client_port);
 
   /* do SETUP for video and audio */
-  fail_unless (do_setup (conn, video_control, &client_port, &session,
-          &video_transport) == GST_RTSP_STS_OK);
-  fail_unless (do_setup (conn, audio_control, &client_port, &session,
-          &audio_transport) == GST_RTSP_STS_OK);
+  fail_unless (do_setup_full (conn, video_control, GST_RTSP_LOWER_TRANS_UDP,
+          &client_port, NULL, &session, &video_transport,
+          NULL) == GST_RTSP_STS_OK);
+  fail_unless (do_setup_full (conn, audio_control, GST_RTSP_LOWER_TRANS_UDP,
+          &client_port, NULL, &session, &audio_transport,
+          NULL) == GST_RTSP_STS_OK);
 
   fail_unless (gst_rtsp_session_pool_get_n_sessions (pool) == 1);
 
@@ -909,19 +1325,7 @@ GST_START_TEST (test_play_multithreaded_timeout_client)
   sleep (7);
 
   fail_unless (gst_rtsp_session_pool_cleanup (pool) == 1);
-
-
-  /* send TEARDOWN request and check that we get 454 Session Not found */
-  request = create_request (conn, GST_RTSP_TEARDOWN, NULL);
-  gst_rtsp_message_add_header (request, GST_RTSP_HDR_SESSION, session);
-  fail_unless (send_request (conn, request));
-  gst_rtsp_message_free (request);
-
-  fail_unless (gst_rtsp_message_new (&response) == GST_RTSP_OK);
-  fail_unless (gst_rtsp_connection_receive (conn, response, NULL) ==
-      GST_RTSP_ESYS);
-  fail_unless (errno == ECONNRESET);
-  gst_rtsp_message_free (response);
+  fail_unless (gst_rtsp_session_pool_get_n_sessions (pool) == 0);
 
   /* clean up and iterate so the clean-up can finish */
   g_object_unref (pool);
@@ -951,8 +1355,12 @@ GST_START_TEST (test_play_multithreaded_timeout_session)
   GstRTSPTransport *video_transport = NULL;
   GstRTSPTransport *audio_transport = NULL;
   GstRTSPSessionPool *pool;
+  GstRTSPThreadPool *thread_pool;
+
+  thread_pool = gst_rtsp_server_get_thread_pool (server);
+  gst_rtsp_thread_pool_set_max_threads (thread_pool, 2);
+  g_object_unref (thread_pool);
 
-  gst_rtsp_server_set_max_threads (server, 2);
   pool = gst_rtsp_server_get_session_pool (server);
   g_signal_connect (server, "client-connected",
       G_CALLBACK (session_connected_new_session_cb), new_session_timeout_one);
@@ -1016,7 +1424,6 @@ GST_START_TEST (test_play_multithreaded_timeout_session)
 GST_END_TEST;
 
 
-
 GST_START_TEST (test_play_disconnect)
 {
   GstRTSPConnection *conn;
@@ -1082,6 +1489,409 @@ GST_START_TEST (test_play_disconnect)
 
 GST_END_TEST;
 
+/* Only different with test_play is the specific ports selected */
+
+GST_START_TEST (test_play_specific_server_port)
+{
+  GstRTSPMountPoints *mounts;
+  gchar *service;
+  GstRTSPMediaFactory *factory;
+  GstRTSPAddressPool *pool;
+  GstRTSPConnection *conn;
+  GstSDPMessage *sdp_message = NULL;
+  const GstSDPMedia *sdp_media;
+  const gchar *video_control;
+  GstRTSPRange client_port;
+  gchar *session = NULL;
+  GstRTSPTransport *video_transport = NULL;
+  GSocket *rtp_socket, *rtcp_socket;
+  GSocketAddress *rtp_address, *rtcp_address;
+  guint16 rtp_port, rtcp_port;
+
+  mounts = gst_rtsp_server_get_mount_points (server);
+
+  factory = gst_rtsp_media_factory_new ();
+  pool = gst_rtsp_address_pool_new ();
+  gst_rtsp_address_pool_add_range (pool, GST_RTSP_ADDRESS_POOL_ANY_IPV4,
+      GST_RTSP_ADDRESS_POOL_ANY_IPV4, 7770, 7780, 0);
+  gst_rtsp_media_factory_set_address_pool (factory, pool);
+  g_object_unref (pool);
+  gst_rtsp_media_factory_set_launch (factory, "( " VIDEO_PIPELINE " )");
+  gst_rtsp_mount_points_add_factory (mounts, TEST_MOUNT_POINT, factory);
+  g_object_unref (mounts);
+
+  /* set port to any */
+  gst_rtsp_server_set_service (server, "0");
+
+  /* attach to default main context */
+  source_id = gst_rtsp_server_attach (server, NULL);
+  fail_if (source_id == 0);
+
+  /* get port */
+  service = gst_rtsp_server_get_service (server);
+  test_port = atoi (service);
+  fail_unless (test_port != 0);
+  g_free (service);
+
+  GST_DEBUG ("rtsp server listening on port %d", test_port);
+
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  sdp_message = do_describe (conn, TEST_MOUNT_POINT);
+
+  /* get control strings from DESCRIBE response */
+  fail_unless (gst_sdp_message_medias_len (sdp_message) == 1);
+  sdp_media = gst_sdp_message_get_media (sdp_message, 0);
+  video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+  get_client_ports_full (&client_port, &rtp_socket, &rtcp_socket);
+
+  /* do SETUP for video */
+  fail_unless (do_setup (conn, video_control, &client_port, &session,
+          &video_transport) == GST_RTSP_STS_OK);
+
+  /* send PLAY request and check that we get 200 OK */
+  fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
+          session) == GST_RTSP_STS_OK);
+
+  receive_rtp (rtp_socket, &rtp_address);
+  receive_rtcp (rtcp_socket, &rtcp_address, 0);
+
+  fail_unless (G_IS_INET_SOCKET_ADDRESS (rtp_address));
+  fail_unless (G_IS_INET_SOCKET_ADDRESS (rtcp_address));
+  rtp_port =
+      g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (rtp_address));
+  rtcp_port =
+      g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (rtcp_address));
+  fail_unless (rtp_port >= 7770 && rtp_port <= 7780 && rtp_port % 2 == 0);
+  fail_unless (rtcp_port >= 7770 && rtcp_port <= 7780 && rtcp_port % 2 == 1);
+  fail_unless (rtp_port + 1 == rtcp_port);
+
+  g_object_unref (rtp_address);
+  g_object_unref (rtcp_address);
+
+  /* send TEARDOWN request and check that we get 200 OK */
+  fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
+          session) == GST_RTSP_STS_OK);
+
+  /* FIXME: The rtsp-server always disconnects the transport before
+   * sending the RTCP BYE
+   * receive_rtcp (rtcp_socket, NULL, GST_RTCP_TYPE_BYE);
+   */
+
+  /* clean up and iterate so the clean-up can finish */
+  g_object_unref (rtp_socket);
+  g_object_unref (rtcp_socket);
+  g_free (session);
+  gst_rtsp_transport_free (video_transport);
+  gst_sdp_message_free (sdp_message);
+  gst_rtsp_connection_free (conn);
+
+
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_play_smpte_range)
+{
+  start_server ();
+
+  do_test_play ("npt=5-");
+  do_test_play ("smpte=0:00:00-");
+  do_test_play ("smpte=1:00:00-");
+  do_test_play ("smpte=1:00:03-");
+  do_test_play ("clock=20120321T152256Z-");
+
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_announce_without_sdp)
+{
+  GstRTSPConnection *conn;
+  GstRTSPStatusCode status;
+  GstRTSPMessage *request;
+  GstRTSPMessage *response;
+
+  start_record_server ("( fakesink name=depay0 )");
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  /* create and send ANNOUNCE request */
+  request = create_request (conn, GST_RTSP_ANNOUNCE, NULL);
+
+  fail_unless (send_request (conn, request));
+
+  iterate ();
+
+  response = read_response (conn);
+
+  /* check response */
+  gst_rtsp_message_parse_response (response, &status, NULL, NULL);
+  fail_unless_equals_int (status, GST_RTSP_STS_BAD_REQUEST);
+  gst_rtsp_message_free (response);
+
+  /* try again, this type with content-type, but still no SDP */
+  gst_rtsp_message_add_header (request, GST_RTSP_HDR_CONTENT_TYPE,
+      "application/sdp");
+
+  fail_unless (send_request (conn, request));
+
+  iterate ();
+
+  response = read_response (conn);
+
+  /* check response */
+  gst_rtsp_message_parse_response (response, &status, NULL, NULL);
+  fail_unless_equals_int (status, GST_RTSP_STS_BAD_REQUEST);
+  gst_rtsp_message_free (response);
+
+  /* try again, this type with an unknown content-type */
+  gst_rtsp_message_remove_header (request, GST_RTSP_HDR_CONTENT_TYPE, -1);
+  gst_rtsp_message_add_header (request, GST_RTSP_HDR_CONTENT_TYPE,
+      "application/x-something");
+
+  fail_unless (send_request (conn, request));
+
+  iterate ();
+
+  response = read_response (conn);
+
+  /* check response */
+  gst_rtsp_message_parse_response (response, &status, NULL, NULL);
+  fail_unless_equals_int (status, GST_RTSP_STS_BAD_REQUEST);
+  gst_rtsp_message_free (response);
+
+  /* clean up and iterate so the clean-up can finish */
+  gst_rtsp_message_free (request);
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+static GstRTSPStatusCode
+do_announce (GstRTSPConnection * conn, GstSDPMessage * sdp)
+{
+  GstRTSPMessage *request;
+  GstRTSPMessage *response;
+  GstRTSPStatusCode code;
+  gchar *str;
+
+  /* create request */
+  request = create_request (conn, GST_RTSP_ANNOUNCE, NULL);
+
+  gst_rtsp_message_add_header (request, GST_RTSP_HDR_CONTENT_TYPE,
+      "application/sdp");
+
+  /* add SDP to the response body */
+  str = gst_sdp_message_as_text (sdp);
+  gst_rtsp_message_take_body (request, (guint8 *) str, strlen (str));
+  gst_sdp_message_free (sdp);
+
+  /* send request */
+  fail_unless (send_request (conn, request));
+  gst_rtsp_message_free (request);
+
+  iterate ();
+
+  /* read response */
+  response = read_response (conn);
+
+  /* check status line */
+  gst_rtsp_message_parse_response (response, &code, NULL, NULL);
+
+  gst_rtsp_message_free (response);
+  return code;
+}
+
+static void
+media_constructed_cb (GstRTSPMediaFactory * mfactory, GstRTSPMedia * media,
+    gpointer user_data)
+{
+  GstElement **p_sink = user_data;
+  GstElement *bin;
+
+  bin = gst_rtsp_media_get_element (media);
+  *p_sink = gst_bin_get_by_name (GST_BIN (bin), "sink");
+  GST_INFO ("media constructed!: %" GST_PTR_FORMAT, *p_sink);
+}
+
+#define RECORD_N_BUFS 10
+
+GST_START_TEST (test_record_tcp)
+{
+  GstRTSPMediaFactory *mfactory;
+  GstRTSPConnection *conn;
+  GstRTSPStatusCode status;
+  GstRTSPMessage *response;
+  GstRTSPMessage *request;
+  GstSDPMessage *sdp;
+  GstRTSPResult rres;
+  GSocketAddress *sa;
+  GInetAddress *ia;
+  GstElement *server_sink = NULL;
+  GSocket *conn_socket;
+  const gchar *proto;
+  gchar *client_ip, *sess_id, *session = NULL;
+  gint i;
+
+  mfactory =
+      start_record_server ("( rtppcmadepay name=depay0 ! appsink name=sink )");
+
+  g_signal_connect (mfactory, "media-constructed",
+      G_CALLBACK (media_constructed_cb), &server_sink);
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  conn_socket = gst_rtsp_connection_get_read_socket (conn);
+
+  sa = g_socket_get_local_address (conn_socket, NULL);
+  ia = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sa));
+  client_ip = g_inet_address_to_string (ia);
+  if (g_socket_address_get_family (sa) == G_SOCKET_FAMILY_IPV6)
+    proto = "IP6";
+  else if (g_socket_address_get_family (sa) == G_SOCKET_FAMILY_IPV4)
+    proto = "IP4";
+  else
+    g_assert_not_reached ();
+  g_object_unref (sa);
+
+  gst_sdp_message_new (&sdp);
+
+  /* some standard things first */
+  gst_sdp_message_set_version (sdp, "0");
+
+  /* session ID doesn't have to be super-unique in this case */
+  sess_id = g_strdup_printf ("%u", g_random_int ());
+  gst_sdp_message_set_origin (sdp, "-", sess_id, "1", "IN", proto, client_ip);
+  g_free (sess_id);
+  g_free (client_ip);
+
+  gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
+  gst_sdp_message_set_information (sdp, "rtsp-server-test");
+  gst_sdp_message_add_time (sdp, "0", "0", NULL);
+  gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
+
+  /* add stream 0 */
+  {
+    GstSDPMedia *smedia;
+
+    gst_sdp_media_new (&smedia);
+    gst_sdp_media_set_media (smedia, "audio");
+    gst_sdp_media_add_format (smedia, "8");     /* pcma/alaw */
+    gst_sdp_media_set_port_info (smedia, 0, 1);
+    gst_sdp_media_set_proto (smedia, "RTP/AVP");
+    gst_sdp_media_add_attribute (smedia, "rtpmap", "8 PCMA/8000");
+    gst_sdp_message_add_media (sdp, smedia);
+    gst_sdp_media_free (smedia);
+  }
+
+  /* send ANNOUNCE request */
+  status = do_announce (conn, sdp);
+  fail_unless_equals_int (status, GST_RTSP_STS_OK);
+
+  /* create and send SETUP request */
+  request = create_request (conn, GST_RTSP_SETUP, NULL);
+  gst_rtsp_message_add_header (request, GST_RTSP_HDR_TRANSPORT,
+      "RTP/AVP/TCP;interleaved=0;mode=record");
+  fail_unless (send_request (conn, request));
+  gst_rtsp_message_free (request);
+  iterate ();
+  response = read_response (conn);
+  gst_rtsp_message_parse_response (response, &status, NULL, NULL);
+  fail_unless_equals_int (status, GST_RTSP_STS_OK);
+
+  rres =
+      gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &session, 0);
+  session = g_strdup (session);
+  fail_unless_equals_int (rres, GST_RTSP_OK);
+  gst_rtsp_message_free (response);
+
+  /* send RECORD */
+  request = create_request (conn, GST_RTSP_RECORD, NULL);
+  gst_rtsp_message_add_header (request, GST_RTSP_HDR_SESSION, session);
+  fail_unless (send_request (conn, request));
+  gst_rtsp_message_free (request);
+  iterate ();
+  response = read_response (conn);
+  gst_rtsp_message_parse_response (response, &status, NULL, NULL);
+  fail_unless_equals_int (status, GST_RTSP_STS_OK);
+  gst_rtsp_message_free (response);
+
+  /* send some data */
+  {
+    GstElement *pipeline, *src, *enc, *pay, *sink;
+
+    pipeline = gst_pipeline_new ("send-pipeline");
+    src = gst_element_factory_make ("audiotestsrc", NULL);
+    g_object_set (src, "num-buffers", RECORD_N_BUFS,
+        "samplesperbuffer", 1000, NULL);
+    enc = gst_element_factory_make ("alawenc", NULL);
+    pay = gst_element_factory_make ("rtppcmapay", NULL);
+    sink = gst_element_factory_make ("appsink", NULL);
+    fail_unless (pipeline && src && enc && pay && sink);
+    gst_bin_add_many (GST_BIN (pipeline), src, enc, pay, sink, NULL);
+    gst_element_link_many (src, enc, pay, sink, NULL);
+    gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+    do {
+      GstRTSPMessage *data_msg;
+      GstMapInfo map = GST_MAP_INFO_INIT;
+      GstRTSPResult rres;
+      GstSample *sample = NULL;
+      GstBuffer *buf;
+
+      g_signal_emit_by_name (G_OBJECT (sink), "pull-sample", &sample);
+      if (sample == NULL)
+        break;
+      buf = gst_sample_get_buffer (sample);
+      rres = gst_rtsp_message_new_data (&data_msg, 0);
+      fail_unless_equals_int (rres, GST_RTSP_OK);
+      gst_buffer_map (buf, &map, GST_MAP_READ);
+      GST_INFO ("sending %u bytes of data on channel 0", (guint) map.size);
+      GST_MEMDUMP ("data on channel 0", map.data, map.size);
+      rres = gst_rtsp_message_set_body (data_msg, map.data, map.size);
+      fail_unless_equals_int (rres, GST_RTSP_OK);
+      gst_buffer_unmap (buf, &map);
+      rres = gst_rtsp_connection_send (conn, data_msg, NULL);
+      fail_unless_equals_int (rres, GST_RTSP_OK);
+      gst_rtsp_message_free (data_msg);
+      gst_sample_unref (sample);
+    } while (TRUE);
+
+    gst_element_set_state (pipeline, GST_STATE_NULL);
+    gst_object_unref (pipeline);
+  }
+
+  /* check received data (we assume every buffer created by audiotestsrc and
+   * subsequently encoded by mulawenc results in exactly one RTP packet) */
+  for (i = 0; i < RECORD_N_BUFS; ++i) {
+    GstSample *sample = NULL;
+
+    g_signal_emit_by_name (G_OBJECT (server_sink), "pull-sample", &sample);
+    GST_INFO ("%2d recv sample: %p", i, sample);
+    gst_sample_unref (sample);
+  }
+
+  fail_unless_equals_int (GST_STATE (server_sink), GST_STATE_PLAYING);
+
+  /* clean up and iterate so the clean-up can finish */
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+  g_free (session);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtspserver_suite (void)
 {
@@ -1090,11 +1900,16 @@ rtspserver_suite (void)
 
   suite_add_tcase (s, tc);
   tcase_add_checked_fixture (tc, setup, teardown);
-  tcase_set_timeout (tc, 20);
+  tcase_set_timeout (tc, 120);
   tcase_add_test (tc, test_connect);
   tcase_add_test (tc, test_describe);
   tcase_add_test (tc, test_describe_non_existing_mount_point);
-  tcase_add_test (tc, test_setup);
+  tcase_add_test (tc, test_describe_record_media);
+  tcase_add_test (tc, test_setup_udp);
+  tcase_add_test (tc, test_setup_tcp);
+  tcase_add_test (tc, test_setup_udp_mcast);
+  tcase_add_test (tc, test_setup_twice);
+  tcase_add_test (tc, test_setup_with_require_header);
   tcase_add_test (tc, test_setup_non_existing_stream);
   tcase_add_test (tc, test_play);
   tcase_add_test (tc, test_play_without_session);
@@ -1104,7 +1919,10 @@ rtspserver_suite (void)
   tcase_add_test (tc, test_play_multithreaded_timeout_client);
   tcase_add_test (tc, test_play_multithreaded_timeout_session);
   tcase_add_test (tc, test_play_disconnect);
-
+  tcase_add_test (tc, test_play_specific_server_port);
+  tcase_add_test (tc, test_play_smpte_range);
+  tcase_add_test (tc, test_announce_without_sdp);
+  tcase_add_test (tc, test_record_tcp);
   return s;
 }