3 * unit test for GstRTSPServer
5 * Copyright (C) 2012 Axis Communications <dev-gstreamer at axis dot com>
6 * @author David Svensson Fors <davidsf at axis dot com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include <gst/check/gstcheck.h>
25 #include <gst/sdp/gstsdpmessage.h>
28 #include <netinet/in.h>
30 #include "rtsp-server.h"
32 #define VIDEO_PIPELINE "videotestsrc ! " \
33 "video/x-raw,width=352,height=288 ! " \
34 "rtpgstpay name=pay0 pt=96"
35 #define AUDIO_PIPELINE "audiotestsrc ! " \
36 "audio/x-raw,rate=8000 ! " \
37 "rtpgstpay name=pay1 pt=97"
39 #define TEST_MOUNT_POINT "/test"
40 #define TEST_PROTO "RTP/AVP"
41 #define TEST_ENCODING "X-GST"
42 #define TEST_CLOCK_RATE "90000"
44 /* tested rtsp server */
45 static GstRTSPServer *server = NULL;
47 /* tcp port that the test server listens for rtsp requests on */
48 static gint test_port = 0;
50 /* id of the server's source within the GMainContext */
51 static guint source_id;
53 /* iterate the default main loop until there are no events to dispatch */
57 while (g_main_context_iteration (NULL, FALSE)) {
58 GST_DEBUG ("iteration");
62 /* returns an unused port that can be used by the test */
64 get_unused_port (gint type)
67 struct sockaddr_in addr;
72 fail_unless ((sock = socket (AF_INET, type, 0)) > 0);
74 /* pass port 0 to bind, which will bind to any free port */
75 memset (&addr, 0, sizeof addr);
76 addr.sin_family = AF_INET;
77 addr.sin_addr.s_addr = INADDR_ANY;
78 addr.sin_port = htons (0);
79 fail_unless (bind (sock, (struct sockaddr *) &addr, sizeof addr) == 0);
81 /* ask what port was bound using getsockname */
82 addr_len = sizeof addr;
83 memset (&addr, 0, addr_len);
84 fail_unless (getsockname (sock, (struct sockaddr *) &addr, &addr_len) == 0);
85 port = ntohs (addr.sin_port);
87 /* close the socket so the port gets unbound again (and can be used by the
94 /* returns TRUE if the given port is not currently bound */
96 port_is_unused (gint port, gint type)
99 struct sockaddr_in addr;
103 fail_unless ((sock = socket (AF_INET, type, 0)) > 0);
105 /* check if the port is already bound by trying to bind to it (again) */
106 memset (&addr, 0, sizeof addr);
107 addr.sin_family = AF_INET;
108 addr.sin_addr.s_addr = INADDR_ANY;
109 addr.sin_port = htons (port);
110 is_bound = (bind (sock, (struct sockaddr *) &addr, sizeof addr) != 0);
112 /* close the socket, which will unbind if bound by our call to bind */
118 /* get a free rtp/rtcp client port pair */
120 get_client_ports (GstRTSPRange * range)
125 /* get a pair of unused ports, where the rtp port is even */
127 rtp_port = get_unused_port (SOCK_DGRAM);
128 rtcp_port = rtp_port + 1;
129 } while (rtp_port % 2 != 0 || !port_is_unused (rtcp_port, SOCK_DGRAM));
130 range->min = rtp_port;
131 range->max = rtcp_port;
132 GST_DEBUG ("client_port=%d-%d", range->min, range->max);
135 /* start the tested rtsp server */
139 GstRTSPMediaMapping *mapping;
141 GstRTSPMediaFactory *factory;
143 mapping = gst_rtsp_server_get_media_mapping (server);
145 factory = gst_rtsp_media_factory_new ();
147 gst_rtsp_media_factory_set_launch (factory,
148 "( " VIDEO_PIPELINE " " AUDIO_PIPELINE " )");
150 gst_rtsp_media_mapping_add_factory (mapping, TEST_MOUNT_POINT, factory);
151 g_object_unref (mapping);
154 test_port = get_unused_port (SOCK_STREAM);
155 service = g_strdup_printf ("%d", test_port);
156 gst_rtsp_server_set_service (server, service);
159 /* attach to default main context */
160 source_id = gst_rtsp_server_attach (server, NULL);
161 fail_if (source_id == 0);
163 GST_DEBUG ("rtsp server listening on port %d", test_port);
166 /* stop the tested rtsp server */
170 g_source_remove (source_id);
173 GST_DEBUG ("rtsp server stopped");
176 /* create an rtsp connection to the server on test_port */
177 static GstRTSPConnection *
178 connect_to_server (gint port, const gchar * mount_point)
180 GstRTSPConnection *conn = NULL;
183 GstRTSPUrl *url = NULL;
185 address = gst_rtsp_server_get_address (server);
186 uri_string = g_strdup_printf ("rtsp://%s:%d%s", address, port, mount_point);
188 gst_rtsp_url_parse (uri_string, &url);
191 fail_unless (gst_rtsp_connection_create (url, &conn) == GST_RTSP_OK);
192 gst_rtsp_url_free (url);
194 fail_unless (gst_rtsp_connection_connect (conn, NULL) == GST_RTSP_OK);
199 /* create an rtsp request */
200 static GstRTSPMessage *
201 create_request (GstRTSPConnection * conn, GstRTSPMethod method,
202 const gchar * control)
204 GstRTSPMessage *request = NULL;
208 base_uri = gst_rtsp_url_get_request_uri (gst_rtsp_connection_get_url (conn));
209 full_uri = g_strdup_printf ("%s/%s", base_uri, control ? control : "");
211 if (gst_rtsp_message_new_request (&request, method, full_uri) != GST_RTSP_OK) {
212 GST_DEBUG ("failed to create request object");
220 /* send an rtsp request */
222 send_request (GstRTSPConnection * conn, GstRTSPMessage * request)
224 if (gst_rtsp_connection_send (conn, request, NULL) != GST_RTSP_OK) {
225 GST_DEBUG ("failed to send request");
231 /* read rtsp response. response must be freed by the caller */
232 static GstRTSPMessage *
233 read_response (GstRTSPConnection * conn)
235 GstRTSPMessage *response = NULL;
237 if (gst_rtsp_message_new (&response) != GST_RTSP_OK) {
238 GST_DEBUG ("failed to create response object");
241 if (gst_rtsp_connection_receive (conn, response, NULL) != GST_RTSP_OK) {
242 GST_DEBUG ("failed to read response");
243 gst_rtsp_message_free (response);
246 fail_unless (gst_rtsp_message_get_type (response) ==
247 GST_RTSP_MESSAGE_RESPONSE);
251 /* send an rtsp request and receive response. gchar** parameters are out
252 * parameters that have to be freed by the caller */
253 static GstRTSPStatusCode
254 do_request (GstRTSPConnection * conn, GstRTSPMethod method,
255 const gchar * control, const gchar * session_in, const gchar * transport_in,
256 gchar ** content_type, gchar ** content_base, gchar ** body,
257 gchar ** session_out, gchar ** transport_out)
259 GstRTSPMessage *request;
260 GstRTSPMessage *response;
261 GstRTSPStatusCode code;
265 request = create_request (conn, method, control);
269 gst_rtsp_message_add_header (request, GST_RTSP_HDR_SESSION, session_in);
272 gst_rtsp_message_add_header (request, GST_RTSP_HDR_TRANSPORT, transport_in);
276 fail_unless (send_request (conn, request));
277 gst_rtsp_message_free (request);
282 response = read_response (conn);
284 /* check status line */
285 gst_rtsp_message_parse_response (response, &code, NULL, NULL);
286 if (code != GST_RTSP_STS_OK) {
287 gst_rtsp_message_free (response);
291 /* get information from response */
293 gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_TYPE,
295 *content_type = g_strdup (value);
298 gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_BASE,
300 *content_base = g_strdup (value);
303 *body = g_malloc (response->body_size + 1);
304 strncpy (*body, (gchar *) response->body, response->body_size);
307 gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &value, 0);
309 /* check that we got the same session back */
310 fail_unless (!g_strcmp0 (value, session_in));
312 *session_out = g_strdup (value);
315 gst_rtsp_message_get_header (response, GST_RTSP_HDR_TRANSPORT, &value, 0);
316 *transport_out = g_strdup (value);
319 gst_rtsp_message_free (response);
323 /* send an rtsp request with a method and a session, and receive response */
324 static GstRTSPStatusCode
325 do_simple_request (GstRTSPConnection * conn, GstRTSPMethod method,
326 const gchar * session)
328 return do_request (conn, method, NULL, session, NULL, NULL, NULL,
332 /* send a DESCRIBE request and receive response. returns a received
333 * GstSDPMessage that must be freed by the caller */
334 static GstSDPMessage *
335 do_describe (GstRTSPConnection * conn, const gchar * mount_point)
337 GstSDPMessage *sdp_message;
342 gchar *expected_content_base;
344 /* send DESCRIBE request */
345 fail_unless (do_request (conn, GST_RTSP_DESCRIBE, NULL, NULL, NULL,
346 &content_type, &content_base, &body, NULL, NULL) == GST_RTSP_STS_OK);
348 /* check response values */
349 fail_unless (!g_strcmp0 (content_type, "application/sdp"));
350 address = gst_rtsp_server_get_address (server);
351 expected_content_base =
352 g_strdup_printf ("rtsp://%s:%d%s/", address, test_port, mount_point);
353 fail_unless (!g_strcmp0 (content_base, expected_content_base));
355 /* create sdp message */
356 fail_unless (gst_sdp_message_new (&sdp_message) == GST_SDP_OK);
357 fail_unless (gst_sdp_message_parse_buffer ((guint8 *) body,
358 strlen (body), sdp_message) == GST_SDP_OK);
361 g_free (content_type);
362 g_free (content_base);
365 g_free (expected_content_base);
370 /* send a SETUP request and receive response. if *session is not NULL,
371 * it is used in the request. otherwise, *session is set to a returned
372 * session string that must be freed by the caller. the returned
373 * transport must be freed by the caller. */
374 static GstRTSPStatusCode
375 do_setup (GstRTSPConnection * conn, const gchar * control,
376 const GstRTSPRange * client_ports, gchar ** session,
377 GstRTSPTransport ** transport)
379 GstRTSPStatusCode code;
380 gchar *session_in = NULL;
381 gchar *transport_string_in = NULL;
382 gchar **session_out = NULL;
383 gchar *transport_string_out = NULL;
385 /* prepare and send SETUP request */
388 session_in = *session;
390 session_out = session;
393 transport_string_in =
394 g_strdup_printf (TEST_PROTO ";unicast;client_port=%d-%d",
395 client_ports->min, client_ports->max);
397 do_request (conn, GST_RTSP_SETUP, control, session_in,
398 transport_string_in, NULL, NULL, NULL, session_out,
399 &transport_string_out);
400 g_free (transport_string_in);
402 if (transport_string_out) {
403 /* create transport */
404 fail_unless (gst_rtsp_transport_new (transport) == GST_RTSP_OK);
405 fail_unless (gst_rtsp_transport_parse (transport_string_out,
406 *transport) == GST_RTSP_OK);
407 g_free (transport_string_out);
413 /* fixture setup function */
417 server = gst_rtsp_server_new ();
420 /* fixture clean-up function */
425 g_object_unref (server);
431 GST_START_TEST (test_connect)
433 GstRTSPConnection *conn;
437 /* connect to server */
438 conn = connect_to_server (test_port, TEST_MOUNT_POINT);
441 gst_rtsp_connection_free (conn);
444 /* iterate so the clean-up can finish */
450 GST_START_TEST (test_describe)
452 GstRTSPConnection *conn;
453 GstSDPMessage *sdp_message = NULL;
454 const GstSDPMedia *sdp_media;
456 gchar *expected_rtpmap;
458 const gchar *control_video;
459 const gchar *control_audio;
463 conn = connect_to_server (test_port, TEST_MOUNT_POINT);
465 /* send DESCRIBE request */
466 sdp_message = do_describe (conn, TEST_MOUNT_POINT);
468 fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
470 /* check video sdp */
471 sdp_media = gst_sdp_message_get_media (sdp_message, 0);
472 fail_unless (!g_strcmp0 (gst_sdp_media_get_proto (sdp_media), TEST_PROTO));
473 fail_unless (gst_sdp_media_formats_len (sdp_media) == 1);
474 sscanf (gst_sdp_media_get_format (sdp_media, 0), "%" G_GINT32_FORMAT,
477 g_strdup_printf ("%d " TEST_ENCODING "/" TEST_CLOCK_RATE, format);
478 rtpmap = gst_sdp_media_get_attribute_val (sdp_media, "rtpmap");
479 fail_unless (!g_strcmp0 (rtpmap, expected_rtpmap));
480 g_free (expected_rtpmap);
481 control_video = gst_sdp_media_get_attribute_val (sdp_media, "control");
482 fail_unless (!g_strcmp0 (control_video, "stream=0"));
484 /* check audio sdp */
485 sdp_media = gst_sdp_message_get_media (sdp_message, 1);
486 fail_unless (!g_strcmp0 (gst_sdp_media_get_proto (sdp_media), TEST_PROTO));
487 fail_unless (gst_sdp_media_formats_len (sdp_media) == 1);
488 sscanf (gst_sdp_media_get_format (sdp_media, 0), "%" G_GINT32_FORMAT,
491 g_strdup_printf ("%d " TEST_ENCODING "/" TEST_CLOCK_RATE, format);
492 rtpmap = gst_sdp_media_get_attribute_val (sdp_media, "rtpmap");
493 fail_unless (!g_strcmp0 (rtpmap, expected_rtpmap));
494 g_free (expected_rtpmap);
495 control_audio = gst_sdp_media_get_attribute_val (sdp_media, "control");
496 fail_unless (!g_strcmp0 (control_audio, "stream=1"));
498 /* clean up and iterate so the clean-up can finish */
499 gst_sdp_message_free (sdp_message);
500 gst_rtsp_connection_free (conn);
507 GST_START_TEST (test_describe_non_existing_mount_point)
509 GstRTSPConnection *conn;
513 /* send DESCRIBE request for a non-existing mount point
514 * and check that we get a 404 Not Found */
515 conn = connect_to_server (test_port, "/non-existing");
516 fail_unless (do_simple_request (conn, GST_RTSP_DESCRIBE, NULL)
517 == GST_RTSP_STS_NOT_FOUND);
519 /* clean up and iterate so the clean-up can finish */
520 gst_rtsp_connection_free (conn);
527 GST_START_TEST (test_setup)
529 GstRTSPConnection *conn;
530 GstSDPMessage *sdp_message = NULL;
531 const GstSDPMedia *sdp_media;
532 const gchar *video_control;
533 const gchar *audio_control;
534 GstRTSPRange client_ports;
535 gchar *session = NULL;
536 GstRTSPTransport *video_transport = NULL;
537 GstRTSPTransport *audio_transport = NULL;
541 conn = connect_to_server (test_port, TEST_MOUNT_POINT);
543 sdp_message = do_describe (conn, TEST_MOUNT_POINT);
545 /* get control strings from DESCRIBE response */
546 fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
547 sdp_media = gst_sdp_message_get_media (sdp_message, 0);
548 video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
549 sdp_media = gst_sdp_message_get_media (sdp_message, 1);
550 audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
552 get_client_ports (&client_ports);
554 /* send SETUP request for video */
555 fail_unless (do_setup (conn, video_control, &client_ports, &session,
556 &video_transport) == GST_RTSP_STS_OK);
557 GST_DEBUG ("set up video %s, got session '%s'", video_control, session);
559 /* check response from SETUP */
560 fail_unless (video_transport->trans == GST_RTSP_TRANS_RTP);
561 fail_unless (video_transport->profile == GST_RTSP_PROFILE_AVP);
562 fail_unless (video_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
563 fail_unless (video_transport->mode_play);
564 gst_rtsp_transport_free (video_transport);
566 /* send SETUP request for audio */
567 fail_unless (do_setup (conn, audio_control, &client_ports, &session,
568 &audio_transport) == GST_RTSP_STS_OK);
569 GST_DEBUG ("set up audio %s with session '%s'", audio_control, session);
571 /* check response from SETUP */
572 fail_unless (audio_transport->trans == GST_RTSP_TRANS_RTP);
573 fail_unless (audio_transport->profile == GST_RTSP_PROFILE_AVP);
574 fail_unless (audio_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
575 fail_unless (audio_transport->mode_play);
576 gst_rtsp_transport_free (audio_transport);
578 /* clean up and iterate so the clean-up can finish */
580 gst_sdp_message_free (sdp_message);
581 gst_rtsp_connection_free (conn);
588 GST_START_TEST (test_setup_non_existing_stream)
590 GstRTSPConnection *conn;
591 GstRTSPRange client_ports;
595 conn = connect_to_server (test_port, TEST_MOUNT_POINT);
597 get_client_ports (&client_ports);
599 /* send SETUP request with a non-existing stream and check that we get a
601 fail_unless (do_setup (conn, "stream=7", &client_ports, NULL,
602 NULL) == GST_RTSP_STS_NOT_FOUND);
604 /* clean up and iterate so the clean-up can finish */
605 gst_rtsp_connection_free (conn);
609 /* need to unref the server here, otherwise threads will remain
610 * and teardown won't be run */
611 g_object_unref (server);
617 GST_START_TEST (test_play)
619 GstRTSPConnection *conn;
620 GstSDPMessage *sdp_message = NULL;
621 const GstSDPMedia *sdp_media;
622 const gchar *video_control;
623 const gchar *audio_control;
624 GstRTSPRange client_port;
625 gchar *session = NULL;
626 GstRTSPTransport *video_transport = NULL;
627 GstRTSPTransport *audio_transport = NULL;
631 conn = connect_to_server (test_port, TEST_MOUNT_POINT);
633 sdp_message = do_describe (conn, TEST_MOUNT_POINT);
635 /* get control strings from DESCRIBE response */
636 fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
637 sdp_media = gst_sdp_message_get_media (sdp_message, 0);
638 video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
639 sdp_media = gst_sdp_message_get_media (sdp_message, 1);
640 audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
642 get_client_ports (&client_port);
644 /* do SETUP for video and audio */
645 fail_unless (do_setup (conn, video_control, &client_port, &session,
646 &video_transport) == GST_RTSP_STS_OK);
647 fail_unless (do_setup (conn, audio_control, &client_port, &session,
648 &audio_transport) == GST_RTSP_STS_OK);
650 /* send PLAY request and check that we get 200 OK */
651 fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
652 session) == GST_RTSP_STS_OK);
654 /* send TEARDOWN request and check that we get 200 OK */
655 fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
656 session) == GST_RTSP_STS_OK);
658 /* clean up and iterate so the clean-up can finish */
660 gst_rtsp_transport_free (video_transport);
661 gst_rtsp_transport_free (audio_transport);
662 gst_sdp_message_free (sdp_message);
663 gst_rtsp_connection_free (conn);
670 GST_START_TEST (test_play_without_session)
672 GstRTSPConnection *conn;
676 conn = connect_to_server (test_port, TEST_MOUNT_POINT);
678 /* send PLAY request without a session and check that we get a
679 * 454 Session Not Found */
680 fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
681 NULL) == GST_RTSP_STS_SESSION_NOT_FOUND);
683 /* clean up and iterate so the clean-up can finish */
684 gst_rtsp_connection_free (conn);
692 rtspserver_suite (void)
694 Suite *s = suite_create ("rtspserver");
695 TCase *tc = tcase_create ("general");
697 suite_add_tcase (s, tc);
698 tcase_add_checked_fixture (tc, setup, teardown);
699 tcase_set_timeout (tc, 20);
700 tcase_add_test (tc, test_connect);
701 tcase_add_test (tc, test_describe);
702 tcase_add_test (tc, test_describe_non_existing_mount_point);
703 tcase_add_test (tc, test_setup);
704 tcase_add_test (tc, test_setup_non_existing_stream);
705 tcase_add_test (tc, test_play);
706 tcase_add_test (tc, test_play_without_session);
711 GST_CHECK_MAIN (rtspserver);