/* GStreamer unit test for GstRTSPServer * Copyright (C) 2012 Axis Communications * @author David Svensson Fors * Copyright (C) 2015 Centricular Ltd * @author Tim-Philipp Müller * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "rtsp-server.h" #define VIDEO_PIPELINE "videotestsrc ! " \ "video/x-raw,width=352,height=288 ! " \ "rtpgstpay name=pay0 pt=96" #define AUDIO_PIPELINE "audiotestsrc ! " \ "audio/x-raw,rate=8000 ! " \ "rtpgstpay name=pay1 pt=97" #define TEST_MOUNT_POINT "/test" #define TEST_PROTO "RTP/AVP" #define TEST_ENCODING "X-GST" #define TEST_CLOCK_RATE "90000" /* tested rtsp server */ static GstRTSPServer *server = NULL; /* tcp port that the test server listens for rtsp requests on */ static gint test_port = 0; /* id of the server's source within the GMainContext */ static guint source_id; /* iterate the default main loop until there are no events to dispatch */ static void iterate (void) { while (g_main_context_iteration (NULL, FALSE)) { GST_DEBUG ("iteration"); } } static void get_client_ports_full (GstRTSPRange * range, GSocket ** rtp_socket, GSocket ** rtcp_socket) { 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; } 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); if (rtp_port % 2 != 0) { rtp_port += 1; g_object_unref (rtp); continue; } rtcp_port = rtp_port + 1; 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; } 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); 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) { get_client_ports_full (range, NULL, NULL); } /* start the tested rtsp server */ static void start_server (void) { 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", 5000, 5010, 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_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 (void) { g_source_remove (source_id); source_id = 0; GST_DEBUG ("rtsp server stopped"); } /* create an rtsp connection to the server on test_port */ static GstRTSPConnection * connect_to_server (gint port, const gchar * mount_point) { GstRTSPConnection *conn = NULL; gchar *address; gchar *uri_string; GstRTSPUrl *url = NULL; address = gst_rtsp_server_get_address (server); uri_string = g_strdup_printf ("rtsp://%s:%d%s", address, port, mount_point); g_free (address); 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); gst_rtsp_url_free (url); fail_unless (gst_rtsp_connection_connect (conn, NULL) == GST_RTSP_OK); return conn; } /* create an rtsp request */ static GstRTSPMessage * create_request (GstRTSPConnection * conn, GstRTSPMethod method, const gchar * control) { GstRTSPMessage *request = NULL; gchar *base_uri; gchar *full_uri; base_uri = gst_rtsp_url_get_request_uri (gst_rtsp_connection_get_url (conn)); full_uri = g_strdup_printf ("%s/%s", base_uri, control ? control : ""); g_free (base_uri); if (gst_rtsp_message_new_request (&request, method, full_uri) != GST_RTSP_OK) { GST_DEBUG ("failed to create request object"); g_free (full_uri); return NULL; } g_free (full_uri); return request; } /* send an rtsp request */ static gboolean send_request (GstRTSPConnection * conn, GstRTSPMessage * request) { if (gst_rtsp_connection_send (conn, request, NULL) != GST_RTSP_OK) { GST_DEBUG ("failed to send request"); return FALSE; } return TRUE; } /* read rtsp response. response must be freed by the caller */ static GstRTSPMessage * read_response (GstRTSPConnection * conn) { GstRTSPMessage *response = NULL; if (gst_rtsp_message_new (&response) != GST_RTSP_OK) { GST_DEBUG ("failed to create response object"); return NULL; } if (gst_rtsp_connection_receive (conn, response, NULL) != GST_RTSP_OK) { GST_DEBUG ("failed to read response"); gst_rtsp_message_free (response); return NULL; } fail_unless (gst_rtsp_message_get_type (response) == GST_RTSP_MESSAGE_RESPONSE); 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_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 ** range_out, gchar ** unsupported_out) { GstRTSPMessage *request; GstRTSPMessage *response; GstRTSPStatusCode code; gchar *value; /* create request */ request = create_request (conn, method, control); /* add headers */ if (session_in) { gst_rtsp_message_add_header (request, GST_RTSP_HDR_SESSION, session_in); } 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)); gst_rtsp_message_free (request); iterate (); /* read response */ response = read_response (conn); /* 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; } /* get information from response */ if (content_type) { gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_TYPE, &value, 0); *content_type = g_strdup (value); } if (content_base) { gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_BASE, &value, 0); *content_base = g_strdup (value); } if (body) { *body = g_malloc (response->body_size + 1); strncpy (*body, (gchar *) response->body, response->body_size); } if (session_out) { gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &value, 0); value = g_strdup (value); /* Remove the timeout */ if (value) { char *pos = strchr (value, ';'); if (pos) *pos = 0; } if (session_in) { /* check that we got the same session back */ fail_unless (!g_strcmp0 (value, session_in)); } *session_out = value; } if (transport_out) { 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); } /* 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 = 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, NULL, &content_type, &content_base, &body, NULL, NULL, NULL) == GST_RTSP_STS_OK); /* check response values */ fail_unless (!g_strcmp0 (content_type, "application/sdp")); address = gst_rtsp_server_get_address (server); expected_content_base = g_strdup_printf ("rtsp://%s:%d%s/", address, test_port, mount_point); fail_unless (!g_strcmp0 (content_base, expected_content_base)); /* create sdp message */ fail_unless (gst_sdp_message_new (&sdp_message) == GST_SDP_OK); fail_unless (gst_sdp_message_parse_buffer ((guint8 *) body, strlen (body), sdp_message) == GST_SDP_OK); /* clean up */ g_free (content_type); g_free (content_base); g_free (body); g_free (address); g_free (expected_content_base); return sdp_message; } /* 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_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; GString *transport_string_in = NULL; gchar **session_out = NULL; gchar *transport_string_out = NULL; /* prepare and send SETUP request */ if (session) { if (*session) { session_in = *session; } else { session_out = session; } } 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_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 */ fail_unless (gst_rtsp_transport_new (transport) == GST_RTSP_OK); fail_unless (gst_rtsp_transport_parse (transport_string_out, *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) { server = gst_rtsp_server_new (); } /* fixture clean-up function */ static void teardown (void) { if (server) { g_object_unref (server); server = NULL; } test_port = 0; } GST_START_TEST (test_connect) { GstRTSPConnection *conn; start_server (); /* connect to server */ conn = connect_to_server (test_port, TEST_MOUNT_POINT); /* clean up */ gst_rtsp_connection_free (conn); stop_server (); /* iterate so the clean-up can finish */ iterate (); } GST_END_TEST; GST_START_TEST (test_describe) { GstRTSPConnection *conn; GstSDPMessage *sdp_message = NULL; const GstSDPMedia *sdp_media; gint32 format; gchar *expected_rtpmap; const gchar *rtpmap; const gchar *control_video; const gchar *control_audio; start_server (); conn = connect_to_server (test_port, TEST_MOUNT_POINT); /* send DESCRIBE request */ sdp_message = do_describe (conn, TEST_MOUNT_POINT); fail_unless (gst_sdp_message_medias_len (sdp_message) == 2); /* check video sdp */ sdp_media = gst_sdp_message_get_media (sdp_message, 0); fail_unless (!g_strcmp0 (gst_sdp_media_get_proto (sdp_media), TEST_PROTO)); fail_unless (gst_sdp_media_formats_len (sdp_media) == 1); sscanf (gst_sdp_media_get_format (sdp_media, 0), "%" G_GINT32_FORMAT, &format); expected_rtpmap = g_strdup_printf ("%d " TEST_ENCODING "/" TEST_CLOCK_RATE, format); rtpmap = gst_sdp_media_get_attribute_val (sdp_media, "rtpmap"); fail_unless (!g_strcmp0 (rtpmap, expected_rtpmap)); g_free (expected_rtpmap); control_video = gst_sdp_media_get_attribute_val (sdp_media, "control"); fail_unless (!g_strcmp0 (control_video, "stream=0")); /* check audio sdp */ sdp_media = gst_sdp_message_get_media (sdp_message, 1); fail_unless (!g_strcmp0 (gst_sdp_media_get_proto (sdp_media), TEST_PROTO)); fail_unless (gst_sdp_media_formats_len (sdp_media) == 1); sscanf (gst_sdp_media_get_format (sdp_media, 0), "%" G_GINT32_FORMAT, &format); expected_rtpmap = g_strdup_printf ("%d " TEST_ENCODING "/" TEST_CLOCK_RATE, format); rtpmap = gst_sdp_media_get_attribute_val (sdp_media, "rtpmap"); fail_unless (!g_strcmp0 (rtpmap, expected_rtpmap)); g_free (expected_rtpmap); control_audio = gst_sdp_media_get_attribute_val (sdp_media, "control"); fail_unless (!g_strcmp0 (control_audio, "stream=1")); /* clean up and iterate so the clean-up can finish */ gst_sdp_message_free (sdp_message); gst_rtsp_connection_free (conn); stop_server (); iterate (); } 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 (); /* send DESCRIBE request for a non-existing mount point * and check that we get a 404 Not Found */ conn = connect_to_server (test_port, "/non-existing"); fail_unless (do_simple_request (conn, GST_RTSP_DESCRIBE, NULL) == GST_RTSP_STS_NOT_FOUND); /* clean up and iterate so the clean-up can finish */ gst_rtsp_connection_free (conn); stop_server (); iterate (); } GST_END_TEST; 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 = { 0 }; gchar *session = NULL; GstRTSPTransport *video_transport = NULL; GstRTSPTransport *audio_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"); 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 video */ 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 == lower_transport); fail_unless (video_transport->mode_play); gst_rtsp_transport_free (video_transport); /* send SETUP request for audio */ 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 == 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); gst_rtsp_connection_free (conn); stop_server (); iterate (); } GST_END_TEST; GST_START_TEST (test_setup_non_existing_stream) { GstRTSPConnection *conn; GstRTSPRange client_ports; start_server (); conn = connect_to_server (test_port, TEST_MOUNT_POINT); get_client_ports (&client_ports); /* send SETUP request with a non-existing stream and check that we get a * 404 Not Found */ fail_unless (do_setup (conn, "stream=7", &client_ports, NULL, NULL) == GST_RTSP_STS_NOT_FOUND); /* clean up and iterate so the clean-up can finish */ gst_rtsp_connection_free (conn); stop_server (); iterate (); } GST_END_TEST; static 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; 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); 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 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); 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); gst_sdp_message_free (sdp_message); 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 (NULL); stop_server (); iterate (); } GST_END_TEST; GST_START_TEST (test_play_without_session) { GstRTSPConnection *conn; start_server (); conn = connect_to_server (test_port, TEST_MOUNT_POINT); /* send PLAY request without a session and check that we get a * 454 Session Not Found */ fail_unless (do_simple_request (conn, GST_RTSP_PLAY, NULL) == GST_RTSP_STS_SESSION_NOT_FOUND); /* 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_bind_already_in_use) { GstRTSPServer *serv; GSocketService *service; GError *error = NULL; guint16 port; gchar *port_str; serv = gst_rtsp_server_new (); service = g_socket_service_new (); /* bind service to port */ port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, &error); g_assert_no_error (error); port_str = g_strdup_printf ("%d\n", port); /* try to bind server to the same port */ g_object_set (serv, "service", port_str, NULL); g_free (port_str); /* attach to default main context */ fail_unless (gst_rtsp_server_attach (serv, NULL) == 0); /* cleanup */ g_object_unref (serv); g_socket_service_stop (service); g_object_unref (service); } GST_END_TEST; GST_START_TEST (test_play_multithreaded) { 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 (NULL); stop_server (); iterate (); } GST_END_TEST; enum { BLOCK_ME, BLOCKED, UNBLOCK }; static void media_constructed_block (GstRTSPMediaFactory * factory, GstRTSPMedia * media, gpointer user_data) { gint *block_state = user_data; g_mutex_lock (&check_mutex); *block_state = BLOCKED; g_cond_broadcast (&check_cond); while (*block_state != UNBLOCK) g_cond_wait (&check_cond, &check_mutex); g_mutex_unlock (&check_mutex); } GST_START_TEST (test_play_multithreaded_block_in_describe) { GstRTSPConnection *conn; GstRTSPMountPoints *mounts; GstRTSPMediaFactory *factory; gint block_state = BLOCK_ME; GstRTSPMessage *request; GstRTSPMessage *response; GstRTSPStatusCode code; GstRTSPThreadPool *pool; 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); factory = gst_rtsp_media_factory_new (); gst_rtsp_media_factory_set_launch (factory, "( " VIDEO_PIPELINE " " AUDIO_PIPELINE " )"); g_signal_connect (factory, "media-constructed", G_CALLBACK (media_constructed_block), &block_state); gst_rtsp_mount_points_add_factory (mounts, TEST_MOUNT_POINT "2", factory); g_object_unref (mounts); start_server (); conn = connect_to_server (test_port, TEST_MOUNT_POINT "2"); iterate (); /* do describe, it will not return now as we've blocked it */ request = create_request (conn, GST_RTSP_DESCRIBE, NULL); fail_unless (send_request (conn, request)); gst_rtsp_message_free (request); g_mutex_lock (&check_mutex); while (block_state != BLOCKED) g_cond_wait (&check_cond, &check_mutex); g_mutex_unlock (&check_mutex); /* Do a second connection while the first one is blocked */ do_test_play (NULL); /* Now unblock the describe */ g_mutex_lock (&check_mutex); block_state = UNBLOCK; g_cond_broadcast (&check_cond); g_mutex_unlock (&check_mutex); response = read_response (conn); gst_rtsp_message_parse_response (response, &code, NULL, NULL); fail_unless (code == GST_RTSP_STS_OK); gst_rtsp_message_free (response); gst_rtsp_connection_free (conn); stop_server (); iterate (); } GST_END_TEST; static void new_session_timeout_one (GstRTSPClient * client, GstRTSPSession * session, gpointer user_data) { gst_rtsp_session_set_timeout (session, 1); g_signal_handlers_disconnect_by_func (client, new_session_timeout_one, user_data); } static void session_connected_new_session_cb (GstRTSPServer * server, GstRTSPClient * client, gpointer user_data) { g_signal_connect (client, "new-session", user_data, NULL); } GST_START_TEST (test_play_multithreaded_timeout_client) { 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; 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 (); 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 (&client_port); /* do SETUP for video and audio */ 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); /* send PLAY request and check that we get 200 OK */ fail_unless (do_simple_request (conn, GST_RTSP_PLAY, session) == GST_RTSP_STS_OK); sleep (7); fail_unless (gst_rtsp_session_pool_cleanup (pool) == 1); 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); 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; GST_START_TEST (test_play_multithreaded_timeout_session) { GstRTSPConnection *conn; GstSDPMessage *sdp_message = NULL; const GstSDPMedia *sdp_media; const gchar *video_control; const gchar *audio_control; GstRTSPRange client_port; gchar *session1 = NULL; gchar *session2 = NULL; 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); 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 (); 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"); 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); /* do SETUP for video and audio */ fail_unless (do_setup (conn, video_control, &client_port, &session1, &video_transport) == GST_RTSP_STS_OK); fail_unless (do_setup (conn, audio_control, &client_port, &session2, &audio_transport) == GST_RTSP_STS_OK); fail_unless (gst_rtsp_session_pool_get_n_sessions (pool) == 2); /* send PLAY request and check that we get 200 OK */ fail_unless (do_simple_request (conn, GST_RTSP_PLAY, session1) == GST_RTSP_STS_OK); fail_unless (do_simple_request (conn, GST_RTSP_PLAY, session2) == GST_RTSP_STS_OK); sleep (7); fail_unless (gst_rtsp_session_pool_cleanup (pool) == 1); /* send TEARDOWN request and check that we get 454 Session Not found */ fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN, session1) == GST_RTSP_STS_SESSION_NOT_FOUND); fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN, session2) == GST_RTSP_STS_OK); /* clean up and iterate so the clean-up can finish */ g_object_unref (pool); g_free (session1); g_free (session2); 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; GST_START_TEST (test_play_disconnect) { 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; GstRTSPSessionPool *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 (); 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 (&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 (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); gst_rtsp_connection_free (conn); sleep (7); fail_unless (gst_rtsp_session_pool_get_n_sessions (pool) == 1); fail_unless (gst_rtsp_session_pool_cleanup (pool) == 1); /* clean up and iterate so the clean-up can finish */ g_object_unref (pool); g_free (session); gst_rtsp_transport_free (video_transport); gst_rtsp_transport_free (audio_transport); gst_sdp_message_free (sdp_message); stop_server (); iterate (); } 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 (); /* 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_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 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 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_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); tcase_add_test (tc, test_announce_without_sdp); tcase_add_test (tc, test_record_tcp); return s; } GST_CHECK_MAIN (rtspserver);