-/* 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
#include "rtsp-server.h"
+#define ERRORIGNORE "errorignore ignore-error=false ignore-notlinked=true " \
+ "ignore-notnegotiated=false convert-to=ok"
#define VIDEO_PIPELINE "videotestsrc ! " \
+ ERRORIGNORE " ! " \
"video/x-raw,width=352,height=288 ! " \
"rtpgstpay name=pay0 pt=96"
#define AUDIO_PIPELINE "audiotestsrc ! " \
+ ERRORIGNORE " ! " \
"audio/x-raw,rate=8000 ! " \
"rtpgstpay name=pay1 pt=97"
/* start the tested rtsp server */
static void
-start_server (void)
+start_server (gboolean set_shared_factory)
+{
+ GstRTSPMountPoints *mounts;
+ gchar *service;
+ GstRTSPMediaFactory *factory;
+ GstRTSPAddressPool *pool;
+
+ mounts = gst_rtsp_server_get_mount_points (server);
+
+ factory = gst_rtsp_media_factory_new ();
+
+ 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);
+
+ /* 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", 5500, 5510, 16);
+ gst_rtsp_address_pool_add_range (pool, GST_RTSP_ADDRESS_POOL_ANY_IPV4,
+ GST_RTSP_ADDRESS_POOL_ANY_IPV4, 6000, 6010, 0);
+ gst_rtsp_media_factory_set_address_pool (factory, pool);
+ gst_rtsp_media_factory_set_shared (factory, set_shared_factory);
+ 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);
+}
+
+static void
+start_tcp_server (gboolean set_shared_factory)
{
GstRTSPMountPoints *mounts;
gchar *service;
factory = gst_rtsp_media_factory_new ();
+ gst_rtsp_media_factory_set_protocols (factory, GST_RTSP_LOWER_TRANS_TCP);
gst_rtsp_media_factory_set_launch (factory,
"( " VIDEO_PIPELINE " " AUDIO_PIPELINE " )");
gst_rtsp_mount_points_add_factory (mounts, TEST_MOUNT_POINT, factory);
+ gst_rtsp_media_factory_set_shared (factory, set_shared_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);
+
+}
+
+/* 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 */
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;
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);
read_response (GstRTSPConnection * conn)
{
GstRTSPMessage *response = NULL;
+ GstRTSPMsgType type;
if (gst_rtsp_message_new (&response) != GST_RTSP_OK) {
GST_DEBUG ("failed to create response object");
gst_rtsp_message_free (response);
return NULL;
}
- fail_unless (gst_rtsp_message_get_type (response) ==
- GST_RTSP_MESSAGE_RESPONSE);
+ type = gst_rtsp_message_get_type (response);
+ fail_unless (type == GST_RTSP_MESSAGE_RESPONSE
+ || type == GST_RTSP_MESSAGE_DATA);
return response;
}
/* 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 * range_in, const gchar * require_in,
gchar ** content_type, gchar ** content_base, gchar ** body,
- gchar ** session_out, gchar ** transport_out, gchar ** range_out)
+ gchar ** session_out, gchar ** transport_out, gchar ** range_out,
+ gchar ** unsupported_out)
{
GstRTSPMessage *request;
GstRTSPMessage *response;
GstRTSPStatusCode code;
gchar *value;
+ GstRTSPMsgType msg_type;
/* create request */
request = create_request (conn, method, control);
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));
/* read response */
response = read_response (conn);
+ fail_unless (response != NULL);
+
+ msg_type = gst_rtsp_message_get_type (response);
+
+ if (msg_type == GST_RTSP_MESSAGE_DATA) {
+ do {
+ gst_rtsp_message_free (response);
+ response = read_response (conn);
+ msg_type = gst_rtsp_message_get_type (response);
+ } while (msg_type == GST_RTSP_MESSAGE_DATA);
+ }
+
+ fail_unless (msg_type == GST_RTSP_MESSAGE_RESPONSE);
/* 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;
}
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,
NULL, NULL, NULL, NULL, NULL);
}
+/* send an rtsp request with a method,session and range in,
+ * and receive response. range_in is the Range in req header */
+static GstRTSPStatusCode
+do_simple_request_rangein (GstRTSPConnection * conn, GstRTSPMethod method,
+ const gchar * session, const gchar * rangein)
+{
+ return do_request (conn, method, NULL, session, NULL, rangein, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+}
+
/* send a DESCRIBE request and receive response. returns a received
* GstSDPMessage that must be freed by the caller */
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;
* 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;
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, NULL, session_out,
- &transport_string_out, NULL);
- 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 */
*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)
{
GstRTSPConnection *conn;
- start_server ();
+ start_server (FALSE);
/* connect to server */
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
const gchar *control_video;
const gchar *control_audio;
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
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;
- start_server ();
+ start_server (FALSE);
/* send DESCRIBE request for a non-existing mount point
* and check that we get a 404 Not Found */
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;
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
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 (FALSE);
+
+ 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 (FALSE);
+
+ 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);
GstRTSPConnection *conn;
GstRTSPRange client_ports;
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
}
static void
-do_test_play (const gchar * range)
+do_test_play_tcp_full (const gchar * range)
{
GstRTSPConnection *conn;
GstSDPMessage *sdp_message = NULL;
gchar *session = NULL;
GstRTSPTransport *video_transport = NULL;
GstRTSPTransport *audio_transport = NULL;
- GSocket *rtp_socket, *rtcp_socket;
+ gchar *range_out = NULL;
+ GstRTSPLowerTrans lower_transport = GST_RTSP_LOWER_TRANS_TCP;
+
+ conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+ sdp_message = do_describe (conn, TEST_MOUNT_POINT);
+ get_client_ports (&client_port);
+
+ /* 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");
+ sdp_media = gst_sdp_message_get_media (sdp_message, 1);
+ audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+ /* do SETUP for video and audio */
+ 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_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);
+
+ {
+ GstRTSPMessage *message;
+ fail_unless (gst_rtsp_message_new (&message) == GST_RTSP_OK);
+ fail_unless (gst_rtsp_connection_receive (conn, message,
+ NULL) == GST_RTSP_OK);
+ fail_unless (gst_rtsp_message_get_type (message) == GST_RTSP_MESSAGE_DATA);
+ gst_rtsp_message_free (message);
+ }
+
+ /* 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_free (session);
+ gst_rtsp_transport_free (video_transport);
+ gst_rtsp_transport_free (audio_transport);
+ gst_sdp_message_free (sdp_message);
+ gst_rtsp_connection_free (conn);
+}
+
+static void
+do_test_play_full (const gchar * range, GstRTSPLowerTrans lower_transport,
+ GMutex * lock)
+{
+ GstRTSPConnection *conn;
+ GstSDPMessage *sdp_message = NULL;
+ const GstSDPMedia *sdp_media;
+ const gchar *video_control;
+ const gchar *audio_control;
+ GstRTSPRange client_port;
+ 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);
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_request (conn, GST_RTSP_PLAY, NULL, session, NULL, range,
fail_unless_equals_string (range, range_out);
g_free (range_out);
- receive_rtp (rtp_socket, NULL);
- receive_rtcp (rtcp_socket, NULL, 0);
+ for (;;) {
+ receive_rtp (rtp_socket, NULL);
+ receive_rtcp (rtcp_socket, NULL, 0);
+
+ if (lock != NULL) {
+ if (g_mutex_trylock (lock) == TRUE) {
+ g_mutex_unlock (lock);
+ break;
+ }
+ } else {
+ break;
+ }
+
+ }
/* send TEARDOWN request and check that we get 200 OK */
fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
gst_rtsp_connection_free (conn);
}
+static void
+do_test_play (const gchar * range)
+{
+ do_test_play_full (range, GST_RTSP_LOWER_TRANS_UDP, NULL);
+}
GST_START_TEST (test_play)
{
- start_server ();
+ start_server (FALSE);
do_test_play (NULL);
GST_END_TEST;
+GST_START_TEST (test_play_tcp)
+{
+ GstRTSPConnection *conn;
+ GstSDPMessage *sdp_message = NULL;
+ const GstSDPMedia *sdp_media;
+ const gchar *video_control;
+ const gchar *audio_control;
+ GstRTSPRange client_ports = { 0 };
+ gchar *session = NULL;
+ GstRTSPTransport *video_transport = NULL;
+ GstRTSPTransport *audio_transport = NULL;
+
+ start_tcp_server (FALSE);
+
+ conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+ /* send DESCRIBE request */
+ 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");
+ 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_ports);
+
+ /* send SETUP request for the first media */
+ fail_unless (do_setup_full (conn, video_control, GST_RTSP_LOWER_TRANS_TCP,
+ &client_ports, NULL, &session, &video_transport,
+ NULL) == GST_RTSP_STS_OK);
+
+ /* 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_TCP);
+ fail_unless (video_transport->mode_play);
+ gst_rtsp_transport_free (video_transport);
+
+ /* send SETUP request for the second media */
+ fail_unless (do_setup_full (conn, audio_control, GST_RTSP_LOWER_TRANS_TCP,
+ &client_ports, NULL, &session, &audio_transport,
+ NULL) == GST_RTSP_STS_OK);
+
+ /* 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_TCP);
+ fail_unless (audio_transport->mode_play);
+ gst_rtsp_transport_free (audio_transport);
+
+ /* send PLAY request and check that we get 200 OK */
+ fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
+ session) == GST_RTSP_STS_OK);
+
+ /* 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_END_TEST;
+
GST_START_TEST (test_play_without_session)
{
GstRTSPConnection *conn;
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
gst_rtsp_thread_pool_set_max_threads (pool, 2);
g_object_unref (pool);
- start_server ();
+ start_server (FALSE);
do_test_play (NULL);
gst_rtsp_mount_points_add_factory (mounts, TEST_MOUNT_POINT "2", factory);
g_object_unref (mounts);
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT "2");
iterate ();
g_signal_connect (server, "client-connected",
G_CALLBACK (session_connected_new_session_cb), new_session_timeout_one);
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
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);
g_signal_connect (server, "client-connected",
G_CALLBACK (session_connected_new_session_cb), new_session_timeout_one);
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
GST_END_TEST;
+GST_START_TEST (test_no_session_timeout)
+{
+ GstRTSPSession *session;
+ gint64 now;
+ gboolean is_expired;
+
+ session = gst_rtsp_session_new ("test-session");
+ gst_rtsp_session_set_timeout (session, 0);
+
+ now = g_get_monotonic_time ();
+ /* add more than the extra 5 seconds that are usually added in
+ * gst_rtsp_session_next_timeout_usec */
+ now += 7000000;
+
+ is_expired = gst_rtsp_session_is_expired_usec (session, now);
+ fail_unless (is_expired == FALSE);
+
+ g_object_unref (session);
+}
+
+GST_END_TEST;
+
+/* media contains two streams: video and audio but only one
+ * stream is requested */
+GST_START_TEST (test_play_one_active_stream)
+{
+ GstRTSPConnection *conn;
+ GstSDPMessage *sdp_message = NULL;
+ const GstSDPMedia *sdp_media;
+ const gchar *video_control;
+ GstRTSPRange client_port;
+ gchar *session = NULL;
+ GstRTSPTransport *video_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);
+
+ 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);
+
+ start_server (FALSE);
+
+ conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+ gst_rtsp_connection_set_remember_session_id (conn, FALSE);
+
+ 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_port);
+
+ /* do SETUP for video only */
+ fail_unless (do_setup (conn, video_control, &client_port, &session,
+ &video_transport) == GST_RTSP_STS_OK);
+
+ fail_unless (gst_rtsp_session_pool_get_n_sessions (pool) == 1);
+
+ /* send PLAY request and check that we get 200 OK */
+ fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
+ session) == GST_RTSP_STS_OK);
+
+
+ /* send TEARDOWN request */
+ 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_object_unref (pool);
+ 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_disconnect)
{
GstRTSPConnection *conn;
g_signal_connect (server, "client-connected",
G_CALLBACK (session_connected_new_session_cb), new_session_timeout_one);
- start_server ();
+ start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
mounts = gst_rtsp_server_get_mount_points (server);
factory = gst_rtsp_media_factory_new ();
+ /* we have to suspend media after SDP in order to make sure that
+ * we can reconfigure UDP sink with new UDP ports */
+ gst_rtsp_media_factory_set_suspend_mode (factory,
+ GST_RTSP_SUSPEND_MODE_RESET);
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_START_TEST (test_play_smpte_range)
{
- start_server ();
+ start_server (FALSE);
do_test_play ("npt=5-");
do_test_play ("smpte=0:00:00-");
GST_END_TEST;
+GST_START_TEST (test_play_smpte_range_tcp)
+{
+ start_tcp_server (FALSE);
+
+ do_test_play_tcp_full ("npt=5-");
+ do_test_play_tcp_full ("smpte=0:00:00-");
+ do_test_play_tcp_full ("smpte=1:00:00-");
+ do_test_play_tcp_full ("smpte=1:00:03-");
+ do_test_play_tcp_full ("clock=20120321T152256Z-");
-static Suite *
-rtspserver_suite (void)
+ stop_server ();
+ iterate ();
+}
+
+GST_END_TEST;
+
+static gpointer
+thread_func_udp (gpointer data)
{
- Suite *s = suite_create ("rtspserver");
- TCase *tc = tcase_create ("general");
+ do_test_play_full (NULL, GST_RTSP_LOWER_TRANS_UDP, (GMutex *) data);
+ return NULL;
+}
+
+static gpointer
+thread_func_tcp (gpointer data)
+{
+ do_test_play_tcp_full (NULL);
+ return NULL;
+}
+
+static void
+test_shared (gpointer (thread_func) (gpointer data))
+{
+ GMutex lock1, lock2, lock3, lock4;
+ GThread *thread1, *thread2, *thread3, *thread4;
+
+ /* Locks for each thread. Each thread will keep reading data as long as the
+ * thread is locked. */
+ g_mutex_init (&lock1);
+ g_mutex_init (&lock2);
+ g_mutex_init (&lock3);
+ g_mutex_init (&lock4);
+
+ if (thread_func == thread_func_tcp)
+ start_tcp_server (TRUE);
+ else
+ start_server (TRUE);
+
+ /* Start the first receiver thread. */
+ g_mutex_lock (&lock1);
+ thread1 = g_thread_new ("thread1", thread_func, &lock1);
+
+ /* Connect and disconnect another client. */
+ g_mutex_lock (&lock2);
+ thread2 = g_thread_new ("thread2", thread_func, &lock2);
+ g_mutex_unlock (&lock2);
+ g_mutex_clear (&lock2);
+ g_thread_join (thread2);
+
+ /* Do it again. */
+ g_mutex_lock (&lock3);
+ thread3 = g_thread_new ("thread3", thread_func, &lock3);
+ g_mutex_unlock (&lock3);
+ g_mutex_clear (&lock3);
+ g_thread_join (thread3);
+
+ /* Disconnect the last client. This will clean up the media. */
+ g_mutex_unlock (&lock1);
+ g_mutex_clear (&lock1);
+ g_thread_join (thread1);
+
+ /* Connect and disconnect another client. This will create and clean up the
+ * media. */
+ g_mutex_lock (&lock4);
+ thread4 = g_thread_new ("thread4", thread_func, &lock4);
+ g_mutex_unlock (&lock4);
+ g_mutex_clear (&lock4);
+ g_thread_join (thread4);
+
+ stop_server ();
+ iterate ();
+}
+
+/* Test adding and removing clients to a 'Shared' media.
+ * CASE: unicast UDP */
+GST_START_TEST (test_shared_udp)
+{
+ test_shared (thread_func_udp);
+}
+
+GST_END_TEST;
+
+/* Test adding and removing clients to a 'Shared' media.
+ * CASE: unicast TCP */
+GST_START_TEST (test_shared_tcp)
+{
+ test_shared (thread_func_tcp);
+}
+
+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);
+ gst_object_unref (bin);
+}
+
+#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 async=false )");
+
+ 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 void
+do_test_multiple_transports (GstRTSPLowerTrans trans1, GstRTSPLowerTrans trans2)
+{
+ GstRTSPConnection *conn1;
+ GstRTSPConnection *conn2;
+ GstSDPMessage *sdp_message1 = NULL;
+ GstSDPMessage *sdp_message2 = NULL;
+ const GstSDPMedia *sdp_media;
+ const gchar *video_control;
+ const gchar *audio_control;
+ GstRTSPRange client_port1, client_port2;
+ gchar *session1 = NULL;
+ gchar *session2 = NULL;
+ GstRTSPTransport *video_transport = NULL;
+ GstRTSPTransport *audio_transport = NULL;
+ GSocket *rtp_socket, *rtcp_socket;
+
+ conn1 = connect_to_server (test_port, TEST_MOUNT_POINT);
+ conn2 = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+ sdp_message1 = do_describe (conn1, TEST_MOUNT_POINT);
+
+ get_client_ports_full (&client_port1, &rtp_socket, &rtcp_socket);
+ /* get control strings from DESCRIBE response */
+ sdp_media = gst_sdp_message_get_media (sdp_message1, 0);
+ video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+ sdp_media = gst_sdp_message_get_media (sdp_message1, 1);
+ audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+ /* do SETUP for video and audio */
+ fail_unless (do_setup_full (conn1, video_control, trans1,
+ &client_port1, NULL, &session1, &video_transport,
+ NULL) == GST_RTSP_STS_OK);
+ fail_unless (do_setup_full (conn1, audio_control, trans1,
+ &client_port1, NULL, &session1, &audio_transport,
+ NULL) == GST_RTSP_STS_OK);
+
+ gst_rtsp_transport_free (video_transport);
+ gst_rtsp_transport_free (audio_transport);
+
+ sdp_message2 = do_describe (conn2, TEST_MOUNT_POINT);
+
+ /* get control strings from DESCRIBE response */
+ sdp_media = gst_sdp_message_get_media (sdp_message2, 0);
+ video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+ sdp_media = gst_sdp_message_get_media (sdp_message2, 1);
+ audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+ get_client_ports_full (&client_port2, NULL, NULL);
+ /* do SETUP for video and audio */
+ fail_unless (do_setup_full (conn2, video_control, trans2,
+ &client_port2, NULL, &session2, &video_transport,
+ NULL) == GST_RTSP_STS_OK);
+ fail_unless (do_setup_full (conn2, audio_control, trans2,
+ &client_port2, NULL, &session2, &audio_transport,
+ NULL) == GST_RTSP_STS_OK);
+
+ /* send PLAY request and check that we get 200 OK */
+ fail_unless (do_request (conn1, GST_RTSP_PLAY, NULL, session1, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL) == GST_RTSP_STS_OK);
+ /* send PLAY request and check that we get 200 OK */
+ fail_unless (do_request (conn2, GST_RTSP_PLAY, NULL, session2, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL) == GST_RTSP_STS_OK);
+
+
+ /* receive UDP data */
+ receive_rtp (rtp_socket, NULL);
+ receive_rtcp (rtcp_socket, NULL, 0);
+
+ /* receive TCP data */
+ {
+ GstRTSPMessage *message;
+ fail_unless (gst_rtsp_message_new (&message) == GST_RTSP_OK);
+ fail_unless (gst_rtsp_connection_receive (conn2, message,
+ NULL) == GST_RTSP_OK);
+ fail_unless (gst_rtsp_message_get_type (message) == GST_RTSP_MESSAGE_DATA);
+ gst_rtsp_message_free (message);
+ }
+
+ /* send TEARDOWN request and check that we get 200 OK */
+ fail_unless (do_simple_request (conn1, GST_RTSP_TEARDOWN,
+ session1) == GST_RTSP_STS_OK);
+ /* send TEARDOWN request and check that we get 200 OK */
+ fail_unless (do_simple_request (conn2, GST_RTSP_TEARDOWN,
+ session2) == GST_RTSP_STS_OK);
+
+ /* clean up and iterate so the clean-up can finish */
+ g_object_unref (rtp_socket);
+ g_object_unref (rtcp_socket);
+ g_free (session1);
+ g_free (session2);
+ gst_rtsp_transport_free (video_transport);
+ gst_rtsp_transport_free (audio_transport);
+ gst_sdp_message_free (sdp_message1);
+ gst_sdp_message_free (sdp_message2);
+ gst_rtsp_connection_free (conn1);
+ gst_rtsp_connection_free (conn2);
+}
+
+GST_START_TEST (test_multiple_transports)
+{
+ start_server (TRUE);
+ do_test_multiple_transports (GST_RTSP_LOWER_TRANS_UDP,
+ GST_RTSP_LOWER_TRANS_TCP);
+ stop_server ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_suspend_mode_reset_only_audio)
+{
+ GstRTSPMountPoints *mounts;
+ gchar *service;
+ GstRTSPMediaFactory *factory;
+ GstRTSPConnection *conn;
+ GstSDPMessage *sdp_message = NULL;
+ const GstSDPMedia *sdp_media;
+ const gchar *audio_control;
+ GstRTSPRange client_port;
+ gchar *session = NULL;
+ GstRTSPTransport *audio_transport = NULL;
+ GSocket *rtp_socket, *rtcp_socket;
+
+ mounts = gst_rtsp_server_get_mount_points (server);
+
+ factory = gst_rtsp_media_factory_new ();
+ gst_rtsp_media_factory_set_suspend_mode (factory,
+ GST_RTSP_SUSPEND_MODE_RESET);
+ 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 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);
+
+ 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, 1);
+ audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+ get_client_ports_full (&client_port, &rtp_socket, &rtcp_socket);
+
+ /* do SETUP for audio */
+ fail_unless (do_setup (conn, audio_control, &client_port, &session,
+ &audio_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);
+
+ /* 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_rtsp_transport_free (audio_transport);
+ gst_sdp_message_free (sdp_message);
+ gst_rtsp_connection_free (conn);
+
+ stop_server ();
+ iterate ();
+}
+
+GST_END_TEST;
+
+
+static GstRTSPStatusCode
+adjust_play_mode (GstRTSPClient * client, GstRTSPContext * ctx,
+ GstRTSPTimeRange ** range, GstSeekFlags * flags, gdouble * rate,
+ GstClockTime * trickmode_interval, gboolean * enable_rate_control)
+{
+ GstRTSPState rtspstate;
+
+ rtspstate = gst_rtsp_session_media_get_rtsp_state (ctx->sessmedia);
+ if (rtspstate == GST_RTSP_STATE_PLAYING) {
+ if (!gst_rtsp_session_media_set_state (ctx->sessmedia, GST_STATE_PAUSED))
+ return GST_RTSP_STS_INTERNAL_SERVER_ERROR;
+
+ if (!gst_rtsp_media_unsuspend (ctx->media))
+ return GST_RTSP_STS_INTERNAL_SERVER_ERROR;
+ }
+
+ return GST_RTSP_STS_OK;
+}
+
+GST_START_TEST (test_double_play)
+{
+ GstRTSPMountPoints *mounts;
+ gchar *service;
+ GstRTSPMediaFactory *factory;
+ GstRTSPConnection *conn;
+ GstSDPMessage *sdp_message = NULL;
+ const GstSDPMedia *sdp_media;
+ const gchar *video_control;
+ const gchar *audio_control;
+ GstRTSPRange client_port;
+ gchar *session = NULL;
+ GstRTSPTransport *audio_transport = NULL;
+ GstRTSPTransport *video_transport = NULL;
+ GSocket *rtp_socket, *rtcp_socket;
+ GstRTSPClient *client;
+ GstRTSPClientClass *klass;
+
+ client = gst_rtsp_client_new ();
+ klass = GST_RTSP_CLIENT_GET_CLASS (client);
+ klass->adjust_play_mode = adjust_play_mode;
+
+ mounts = gst_rtsp_server_get_mount_points (server);
+
+ factory = gst_rtsp_media_factory_new ();
+ 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 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);
+
+ 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");
+ sdp_media = gst_sdp_message_get_media (sdp_message, 1);
+ audio_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);
+
+ /* do SETUP for audio */
+ fail_unless (do_setup (conn, audio_control, &client_port, &session,
+ &audio_transport) == GST_RTSP_STS_OK);
+
+ /* send PLAY request and check that we get 200 OK */
+ fail_unless (do_simple_request_rangein (conn, GST_RTSP_PLAY,
+ session, "npt=0-") == GST_RTSP_STS_OK);
+
+ /* let it play for a while, so it needs to seek
+ * for next play (npt=0-) */
+ g_usleep (30000);
+
+ /* send PLAY request and check that we get 200 OK */
+ fail_unless (do_simple_request_rangein (conn, GST_RTSP_PLAY,
+ session, "npt=0-") == GST_RTSP_STS_OK);
+
+ /* 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_rtsp_transport_free (video_transport);
+ gst_rtsp_transport_free (audio_transport);
+ gst_sdp_message_free (sdp_message);
+ gst_rtsp_connection_free (conn);
+
+ stop_server ();
+ iterate ();
+}
+
+GST_END_TEST;
+
+
+static Suite *
+rtspserver_suite (void)
+{
+ Suite *s = suite_create ("rtspserver");
+ TCase *tc = tcase_create ("general");
+
+ suite_add_tcase (s, tc);
+ tcase_add_checked_fixture (tc, setup, teardown);
+ 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_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_tcp);
+ tcase_add_test (tc, test_play_without_session);
+ tcase_add_test (tc, test_bind_already_in_use);
+ tcase_add_test (tc, test_play_multithreaded);
+ tcase_add_test (tc, test_play_multithreaded_block_in_describe);
+ tcase_add_test (tc, test_play_multithreaded_timeout_client);
+ tcase_add_test (tc, test_play_multithreaded_timeout_session);
+ tcase_add_test (tc, test_no_session_timeout);
+ tcase_add_test (tc, test_play_one_active_stream);
+ 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_play_smpte_range_tcp);
+ tcase_add_test (tc, test_shared_udp);
+ tcase_add_test (tc, test_shared_tcp);
+ tcase_add_test (tc, test_announce_without_sdp);
+ tcase_add_test (tc, test_record_tcp);
+ tcase_add_test (tc, test_multiple_transports);
+ tcase_add_test (tc, test_suspend_mode_reset_only_audio);
+ tcase_add_test (tc, test_double_play);
- suite_add_tcase (s, tc);
- tcase_add_checked_fixture (tc, setup, teardown);
- tcase_set_timeout (tc, 20);
- 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_setup_non_existing_stream);
- tcase_add_test (tc, test_play);
- tcase_add_test (tc, test_play_without_session);
- tcase_add_test (tc, test_bind_already_in_use);
- tcase_add_test (tc, test_play_multithreaded);
- tcase_add_test (tc, test_play_multithreaded_block_in_describe);
- 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);
return s;
}