2 * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include <sys/ioctl.h>
22 #include "rtsp-client.h"
24 #include "rtsp-params.h"
26 /* temporary multicast address until it's configurable somewhere */
27 #define MCAST_ADDRESS "224.2.0.1"
29 static GMutex *tunnels_lock;
30 static GHashTable *tunnels;
40 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
41 #define GST_CAT_DEFAULT rtsp_client_debug
43 static void gst_rtsp_client_get_property (GObject * object, guint propid,
44 GValue * value, GParamSpec * pspec);
45 static void gst_rtsp_client_set_property (GObject * object, guint propid,
46 const GValue * value, GParamSpec * pspec);
47 static void gst_rtsp_client_finalize (GObject * obj);
49 static void client_session_finalized (GstRTSPClient * client,
50 GstRTSPSession * session);
51 static void unlink_session_streams (GstRTSPClient * client,
52 GstRTSPSession * session, GstRTSPSessionMedia * media);
54 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
57 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
59 GObjectClass *gobject_class;
61 gobject_class = G_OBJECT_CLASS (klass);
63 gobject_class->get_property = gst_rtsp_client_get_property;
64 gobject_class->set_property = gst_rtsp_client_set_property;
65 gobject_class->finalize = gst_rtsp_client_finalize;
67 g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
68 g_param_spec_object ("session-pool", "Session Pool",
69 "The session pool to use for client session",
70 GST_TYPE_RTSP_SESSION_POOL,
71 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
73 g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
74 g_param_spec_object ("media-mapping", "Media Mapping",
75 "The media mapping to use for client session",
76 GST_TYPE_RTSP_MEDIA_MAPPING,
77 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
80 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
81 tunnels_lock = g_mutex_new ();
83 GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
87 gst_rtsp_client_init (GstRTSPClient * client)
92 client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
96 /* unlink all media managed in this session */
97 for (medias = session->medias; medias; medias = g_list_next (medias)) {
98 unlink_session_streams (client, session,
99 (GstRTSPSessionMedia *) medias->data);
104 client_cleanup_sessions (GstRTSPClient * client)
108 /* remove weak-ref from sessions */
109 for (sessions = client->sessions; sessions; sessions = g_list_next (sessions)) {
110 GstRTSPSession *session = (GstRTSPSession *) sessions->data;
111 g_object_weak_unref (G_OBJECT (session),
112 (GWeakNotify) client_session_finalized, client);
113 client_unlink_session (client, session);
115 g_list_free (client->sessions);
116 client->sessions = NULL;
119 /* A client is finalized when the connection is broken */
121 gst_rtsp_client_finalize (GObject * obj)
123 GstRTSPClient *client = GST_RTSP_CLIENT (obj);
125 GST_INFO ("finalize client %p", client);
127 client_cleanup_sessions (client);
129 gst_rtsp_connection_free (client->connection);
130 if (client->session_pool)
131 g_object_unref (client->session_pool);
132 if (client->media_mapping)
133 g_object_unref (client->media_mapping);
136 gst_rtsp_url_free (client->uri);
138 g_object_unref (client->media);
140 g_free (client->server_ip);
142 G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
146 gst_rtsp_client_get_property (GObject * object, guint propid,
147 GValue * value, GParamSpec * pspec)
149 GstRTSPClient *client = GST_RTSP_CLIENT (object);
152 case PROP_SESSION_POOL:
153 g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
155 case PROP_MEDIA_MAPPING:
156 g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
159 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
164 gst_rtsp_client_set_property (GObject * object, guint propid,
165 const GValue * value, GParamSpec * pspec)
167 GstRTSPClient *client = GST_RTSP_CLIENT (object);
170 case PROP_SESSION_POOL:
171 gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
173 case PROP_MEDIA_MAPPING:
174 gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
177 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
182 * gst_rtsp_client_new:
184 * Create a new #GstRTSPClient instance.
186 * Returns: a new #GstRTSPClient
189 gst_rtsp_client_new (void)
191 GstRTSPClient *result;
193 result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
199 send_response (GstRTSPClient * client, GstRTSPSession * session,
200 GstRTSPMessage * response)
202 gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER,
203 "GStreamer RTSP server");
205 /* remove any previous header */
206 gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
208 /* add the new session header for new session ids */
212 if (session->timeout != 60)
214 g_strdup_printf ("%s; timeout=%d", session->sessionid,
217 str = g_strdup (session->sessionid);
219 gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
222 if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
223 gst_rtsp_message_dump (response);
226 gst_rtsp_watch_send_message (client->watch, response, NULL);
227 gst_rtsp_message_unset (response);
231 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
232 GstRTSPMessage * request)
234 GstRTSPMessage response = { 0 };
236 gst_rtsp_message_init_response (&response, code,
237 gst_rtsp_status_as_text (code), request);
239 send_response (client, NULL, &response);
243 compare_uri (const GstRTSPUrl * uri1, const GstRTSPUrl * uri2)
245 if (uri1 == NULL || uri2 == NULL)
248 if (strcmp (uri1->abspath, uri2->abspath))
254 /* this function is called to initially find the media for the DESCRIBE request
255 * but is cached for when the same client (without breaking the connection) is
256 * doing a setup for the exact same url. */
257 static GstRTSPMedia *
258 find_media (GstRTSPClient * client, GstRTSPUrl * uri, GstRTSPMessage * request)
260 GstRTSPMediaFactory *factory;
263 if (!compare_uri (client->uri, uri)) {
264 /* remove any previously cached values before we try to construct a new
267 gst_rtsp_url_free (client->uri);
270 g_object_unref (client->media);
271 client->media = NULL;
273 if (!client->media_mapping)
276 /* find the factory for the uri first */
278 gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
281 /* prepare the media and add it to the pipeline */
282 if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
285 /* set ipv6 on the media before preparing */
286 media->is_ipv6 = client->is_ipv6;
288 /* prepare the media */
289 if (!(gst_rtsp_media_prepare (media)))
292 /* now keep track of the uri and the media */
293 client->uri = gst_rtsp_url_copy (uri);
294 client->media = media;
296 /* we have seen this uri before, used cached media */
297 media = client->media;
298 GST_INFO ("reusing cached media %p", media);
302 g_object_ref (media);
309 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
314 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
319 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
320 g_object_unref (factory);
325 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
326 g_object_unref (media);
327 g_object_unref (factory);
333 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
335 GstRTSPMessage message = { 0 };
339 gst_rtsp_message_init_data (&message, channel);
341 data = GST_BUFFER_DATA (buffer);
342 size = GST_BUFFER_SIZE (buffer);
343 gst_rtsp_message_take_body (&message, data, size);
345 /* FIXME, client->watch could have been finalized here, we need to keep an
346 * extra refcount to the watch. */
347 gst_rtsp_watch_send_message (client->watch, &message, NULL);
349 gst_rtsp_message_steal_body (&message, &data, &size);
350 gst_rtsp_message_unset (&message);
356 do_send_data_list (GstBufferList * blist, guint8 channel,
357 GstRTSPClient * client)
359 GstBufferListIterator *it;
361 it = gst_buffer_list_iterate (blist);
362 while (gst_buffer_list_iterator_next_group (it)) {
363 GstBuffer *group = gst_buffer_list_iterator_merge_group (it);
368 do_send_data (group, channel, client);
370 gst_buffer_list_iterator_free (it);
376 link_stream (GstRTSPClient * client, GstRTSPSession * session,
377 GstRTSPSessionStream * stream)
379 GST_DEBUG ("client %p: linking stream %p", client, stream);
380 gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
381 (GstRTSPSendFunc) do_send_data, (GstRTSPSendListFunc) do_send_data_list,
382 (GstRTSPSendListFunc) do_send_data_list, client, NULL);
383 client->streams = g_list_prepend (client->streams, stream);
384 /* make sure our session can't expire */
385 gst_rtsp_session_prevent_expire (session);
389 unlink_stream (GstRTSPClient * client, GstRTSPSession * session,
390 GstRTSPSessionStream * stream)
392 GST_DEBUG ("client %p: unlinking stream %p", client, stream);
393 gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL, NULL,
395 client->streams = g_list_remove (client->streams, stream);
396 /* our session can now expire */
397 gst_rtsp_session_allow_expire (session);
401 unlink_session_streams (GstRTSPClient * client, GstRTSPSession * session,
402 GstRTSPSessionMedia * media)
406 n_streams = gst_rtsp_media_n_streams (media->media);
407 for (i = 0; i < n_streams; i++) {
408 GstRTSPSessionStream *sstream;
409 GstRTSPTransport *tr;
411 /* get the stream as configured in the session */
412 sstream = gst_rtsp_session_media_get_stream (media, i);
413 /* get the transport, if there is no transport configured, skip this stream */
414 if (!(tr = sstream->trans.transport))
417 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
418 /* for TCP, unlink the stream from the TCP connection of the client */
419 unlink_stream (client, session, sstream);
425 close_connection (GstRTSPClient * client)
427 const gchar *tunnelid;
429 GST_DEBUG ("client %p: closing connection", client);
431 if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
432 g_mutex_lock (tunnels_lock);
433 /* remove from tunnelids */
434 g_hash_table_remove (tunnels, tunnelid);
435 g_mutex_unlock (tunnels_lock);
438 gst_rtsp_connection_close (client->connection);
439 if (client->watchid) {
440 g_source_destroy ((GSource *) client->watch);
442 client->watch = NULL;
447 handle_teardown_request (GstRTSPClient * client, GstRTSPUrl * uri,
448 GstRTSPSession * session, GstRTSPMessage * request)
450 GstRTSPSessionMedia *media;
451 GstRTSPMessage response = { 0 };
452 GstRTSPStatusCode code;
457 /* get a handle to the configuration of the media in the session */
458 media = gst_rtsp_session_get_media (session, uri);
462 /* unlink the all TCP callbacks */
463 unlink_session_streams (client, session, media);
465 /* remove the session from the watched sessions */
466 g_object_weak_unref (G_OBJECT (session),
467 (GWeakNotify) client_session_finalized, client);
468 client->sessions = g_list_remove (client->sessions, session);
470 gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
472 /* unmanage the media in the session, returns false if all media session
474 if (!gst_rtsp_session_release_media (session, media)) {
475 /* remove the session */
476 gst_rtsp_session_pool_remove (client->session_pool, session);
478 /* construct the response now */
479 code = GST_RTSP_STS_OK;
480 gst_rtsp_message_init_response (&response, code,
481 gst_rtsp_status_as_text (code), request);
483 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONNECTION, "close");
485 send_response (client, session, &response);
487 close_connection (client);
494 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
499 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
505 handle_get_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
506 GstRTSPSession * session, GstRTSPMessage * request)
512 res = gst_rtsp_message_get_body (request, &data, &size);
513 if (res != GST_RTSP_OK)
517 /* no body, keep-alive request */
518 send_generic_response (client, GST_RTSP_STS_OK, request);
520 /* there is a body */
521 GstRTSPMessage response = { 0 };
523 /* there is a body, handle the params */
524 res = gst_rtsp_params_get (client, uri, session, request, &response);
525 if (res != GST_RTSP_OK)
528 send_response (client, session, &response);
535 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
541 handle_set_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
542 GstRTSPSession * session, GstRTSPMessage * request)
548 res = gst_rtsp_message_get_body (request, &data, &size);
549 if (res != GST_RTSP_OK)
553 /* no body, keep-alive request */
554 send_generic_response (client, GST_RTSP_STS_OK, request);
556 GstRTSPMessage response = { 0 };
558 /* there is a body, handle the params */
559 res = gst_rtsp_params_set (client, uri, session, request, &response);
560 if (res != GST_RTSP_OK)
563 send_response (client, session, &response);
570 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
576 handle_pause_request (GstRTSPClient * client, GstRTSPUrl * uri,
577 GstRTSPSession * session, GstRTSPMessage * request)
579 GstRTSPSessionMedia *media;
580 GstRTSPMessage response = { 0 };
581 GstRTSPStatusCode code;
586 /* get a handle to the configuration of the media in the session */
587 media = gst_rtsp_session_get_media (session, uri);
591 /* the session state must be playing or recording */
592 if (media->state != GST_RTSP_STATE_PLAYING &&
593 media->state != GST_RTSP_STATE_RECORDING)
596 /* unlink the all TCP callbacks */
597 unlink_session_streams (client, session, media);
599 /* then pause sending */
600 gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
602 /* construct the response now */
603 code = GST_RTSP_STS_OK;
604 gst_rtsp_message_init_response (&response, code,
605 gst_rtsp_status_as_text (code), request);
607 send_response (client, session, &response);
609 /* the state is now READY */
610 media->state = GST_RTSP_STATE_READY;
617 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
622 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
627 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
634 handle_play_request (GstRTSPClient * client, GstRTSPUrl * uri,
635 GstRTSPSession * session, GstRTSPMessage * request)
637 GstRTSPSessionMedia *media;
638 GstRTSPMessage response = { 0 };
639 GstRTSPStatusCode code;
641 guint n_streams, i, infocount;
642 guint timestamp, seqnum;
644 GstRTSPTimeRange *range;
650 /* get a handle to the configuration of the media in the session */
651 media = gst_rtsp_session_get_media (session, uri);
655 /* the session state must be playing or ready */
656 if (media->state != GST_RTSP_STATE_PLAYING &&
657 media->state != GST_RTSP_STATE_READY)
660 /* parse the range header if we have one */
661 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_RANGE, &str, 0);
662 if (res == GST_RTSP_OK) {
663 if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
664 /* we have a range, seek to the position */
665 gst_rtsp_media_seek (media->media, range);
666 gst_rtsp_range_free (range);
670 /* grab RTPInfo from the payloaders now */
671 rtpinfo = g_string_new ("");
673 n_streams = gst_rtsp_media_n_streams (media->media);
674 for (i = 0, infocount = 0; i < n_streams; i++) {
675 GstRTSPSessionStream *sstream;
676 GstRTSPMediaStream *stream;
677 GstRTSPTransport *tr;
678 GObjectClass *payobjclass;
681 /* get the stream as configured in the session */
682 sstream = gst_rtsp_session_media_get_stream (media, i);
683 /* get the transport, if there is no transport configured, skip this stream */
684 if (!(tr = sstream->trans.transport)) {
685 GST_INFO ("stream %d is not configured", i);
689 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
690 /* for TCP, link the stream to the TCP connection of the client */
691 link_stream (client, session, sstream);
694 stream = sstream->media_stream;
696 payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
698 if (g_object_class_find_property (payobjclass, "seqnum") &&
699 g_object_class_find_property (payobjclass, "timestamp")) {
702 payobj = G_OBJECT (stream->payloader);
704 /* only add RTP-Info for streams with seqnum and timestamp */
705 g_object_get (payobj, "seqnum", &seqnum, "timestamp", ×tamp, NULL);
708 g_string_append (rtpinfo, ", ");
710 uristr = gst_rtsp_url_get_request_uri (uri);
711 g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
712 uristr, i, seqnum, timestamp);
717 GST_WARNING ("RTP-Info cannot be determined for stream %d", i);
721 /* construct the response now */
722 code = GST_RTSP_STS_OK;
723 gst_rtsp_message_init_response (&response, code,
724 gst_rtsp_status_as_text (code), request);
726 /* add the RTP-Info header */
728 str = g_string_free (rtpinfo, FALSE);
729 gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
731 g_string_free (rtpinfo, TRUE);
735 str = gst_rtsp_media_get_range_string (media->media, TRUE);
736 gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
738 send_response (client, session, &response);
740 /* start playing after sending the request */
741 gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
743 media->state = GST_RTSP_STATE_PLAYING;
750 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
755 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
760 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
767 do_keepalive (GstRTSPSession * session)
769 GST_INFO ("keep session %p alive", session);
770 gst_rtsp_session_touch (session);
774 handle_setup_request (GstRTSPClient * client, GstRTSPUrl * uri,
775 GstRTSPSession * session, GstRTSPMessage * request)
780 gboolean have_transport;
781 GstRTSPTransport *ct, *st;
783 GstRTSPLowerTrans supported;
784 GstRTSPMessage response = { 0 };
785 GstRTSPStatusCode code;
786 GstRTSPSessionStream *stream;
787 gchar *trans_str, *pos;
789 GstRTSPSessionMedia *media;
792 /* the uri contains the stream number we added in the SDP config, which is
793 * always /stream=%d so we need to strip that off
794 * parse the stream we need to configure, look for the stream in the abspath
795 * first and then in the query. */
796 if (uri->abspath == NULL || !(pos = strstr (uri->abspath, "/stream="))) {
797 if (uri->query == NULL || !(pos = strstr (uri->query, "/stream=")))
801 /* we can mofify the parse uri in place */
804 pos += strlen ("/stream=");
805 if (sscanf (pos, "%u", &streamid) != 1)
808 /* parse the transport */
810 gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport,
812 if (res != GST_RTSP_OK)
815 transports = g_strsplit (transport, ",", 0);
816 gst_rtsp_transport_new (&ct);
818 /* init transports */
819 have_transport = FALSE;
820 gst_rtsp_transport_init (ct);
822 /* our supported transports */
823 supported = GST_RTSP_LOWER_TRANS_UDP |
824 GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP;
826 /* loop through the transports, try to parse */
827 for (i = 0; transports[i]; i++) {
828 res = gst_rtsp_transport_parse (transports[i], ct);
829 if (res != GST_RTSP_OK) {
830 /* no valid transport, search some more */
831 GST_WARNING ("could not parse transport %s", transports[i]);
835 /* we have a transport, see if it's RTP/AVP */
836 if (ct->trans != GST_RTSP_TRANS_RTP || ct->profile != GST_RTSP_PROFILE_AVP) {
837 GST_WARNING ("invalid transport %s", transports[i]);
841 if (!(ct->lower_transport & supported)) {
842 GST_WARNING ("unsupported transport %s", transports[i]);
846 /* we have a valid transport */
847 GST_INFO ("found valid transport %s", transports[i]);
848 have_transport = TRUE;
852 gst_rtsp_transport_init (ct);
854 g_strfreev (transports);
856 /* we have not found anything usable, error out */
858 goto unsupported_transports;
860 if (client->session_pool == NULL)
863 /* we have a valid transport now, set the destination of the client. */
864 g_free (ct->destination);
865 if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
866 ct->destination = g_strdup (MCAST_ADDRESS);
868 url = gst_rtsp_connection_get_url (client->connection);
869 ct->destination = g_strdup (url->host);
873 g_object_ref (session);
874 /* get a handle to the configuration of the media in the session, this can
875 * return NULL if this is a new url to manage in this session. */
876 media = gst_rtsp_session_get_media (session, uri);
878 /* create a session if this fails we probably reached our session limit or
880 if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
881 goto service_unavailable;
883 /* we need a new media configuration in this session */
887 /* we have no media, find one and manage it */
891 /* get a handle to the configuration of the media in the session */
892 if ((m = find_media (client, uri, request))) {
893 /* manage the media in our session now */
894 media = gst_rtsp_session_manage_media (session, uri, m);
898 /* if we stil have no media, error */
902 /* fix the transports */
903 if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
904 /* check if the client selected channels for TCP */
905 if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
906 gst_rtsp_session_media_alloc_channels (media, &ct->interleaved);
910 /* get a handle to the stream in the media */
911 if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
914 st = gst_rtsp_session_stream_set_transport (stream, ct);
916 /* configure keepalive for this transport */
917 gst_rtsp_session_stream_set_keepalive (stream,
918 (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
920 /* serialize the server transport */
921 trans_str = gst_rtsp_transport_as_text (st);
922 gst_rtsp_transport_free (st);
924 /* construct the response now */
925 code = GST_RTSP_STS_OK;
926 gst_rtsp_message_init_response (&response, code,
927 gst_rtsp_status_as_text (code), request);
929 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
932 send_response (client, session, &response);
934 /* update the state */
935 switch (media->state) {
936 case GST_RTSP_STATE_PLAYING:
937 case GST_RTSP_STATE_RECORDING:
938 case GST_RTSP_STATE_READY:
939 /* no state change */
942 media->state = GST_RTSP_STATE_READY;
945 g_object_unref (session);
952 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
957 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
958 g_object_unref (session);
963 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
964 g_object_unref (media);
965 g_object_unref (session);
970 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
973 unsupported_transports:
975 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
976 gst_rtsp_transport_free (ct);
981 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
986 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
991 static GstSDPMessage *
992 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
998 gst_sdp_message_new (&sdp);
1000 /* some standard things first */
1001 gst_sdp_message_set_version (sdp, "0");
1003 if (client->is_ipv6)
1008 gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", proto,
1011 gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
1012 gst_sdp_message_set_information (sdp, "rtsp-server");
1013 gst_sdp_message_add_time (sdp, "0", "0", NULL);
1014 gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
1015 gst_sdp_message_add_attribute (sdp, "type", "broadcast");
1016 gst_sdp_message_add_attribute (sdp, "control", "*");
1018 info.server_proto = proto;
1019 if (media->protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST)
1020 info.server_ip = MCAST_ADDRESS;
1022 info.server_ip = client->server_ip;
1024 /* create an SDP for the media object */
1025 if (!gst_rtsp_sdp_from_media (sdp, &info, media))
1033 gst_sdp_message_free (sdp);
1038 /* for the describe we must generate an SDP */
1040 handle_describe_request (GstRTSPClient * client, GstRTSPUrl * uri,
1041 GstRTSPSession * session, GstRTSPMessage * request)
1043 GstRTSPMessage response = { 0 };
1047 gchar *str, *content_base;
1048 GstRTSPMedia *media;
1050 /* check what kind of format is accepted, we don't really do anything with it
1051 * and always return SDP for now. */
1056 gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
1057 if (res == GST_RTSP_ENOTIMPL)
1060 if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
1064 /* find the media object for the uri */
1065 if (!(media = find_media (client, uri, request)))
1068 /* create an SDP for the media object on this client */
1069 if (!(sdp = create_sdp (client, media)))
1072 g_object_unref (media);
1074 gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
1075 gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
1077 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE,
1080 /* content base for some clients that might screw up creating the setup uri */
1081 str = gst_rtsp_url_get_request_uri (uri);
1082 str_len = strlen (str);
1084 /* check for trailing '/' and append one */
1085 if (str[str_len - 1] != '/') {
1086 content_base = g_malloc (str_len + 2);
1087 memcpy (content_base, str, str_len);
1088 content_base[str_len] = '/';
1089 content_base[str_len + 1] = '\0';
1095 GST_INFO ("adding content-base: %s", content_base);
1097 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE,
1099 g_free (content_base);
1101 /* add SDP to the response body */
1102 str = gst_sdp_message_as_text (sdp);
1103 gst_rtsp_message_take_body (&response, (guint8 *) str, strlen (str));
1104 gst_sdp_message_free (sdp);
1106 send_response (client, session, &response);
1113 /* error reply is already sent */
1118 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1119 g_object_unref (media);
1125 handle_options_request (GstRTSPClient * client, GstRTSPUrl * uri,
1126 GstRTSPSession * session, GstRTSPMessage * request)
1128 GstRTSPMessage response = { 0 };
1129 GstRTSPMethod options;
1132 options = GST_RTSP_DESCRIBE |
1137 GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
1139 str = gst_rtsp_options_as_text (options);
1141 gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
1142 gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
1144 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
1147 send_response (client, session, &response);
1152 /* remove duplicate and trailing '/' */
1154 sanitize_uri (GstRTSPUrl * uri)
1158 gboolean have_slash, prev_slash;
1160 s = d = uri->abspath;
1161 len = strlen (uri->abspath);
1165 for (i = 0; i < len; i++) {
1166 have_slash = s[i] == '/';
1168 if (!have_slash || !prev_slash)
1170 prev_slash = have_slash;
1172 len = d - uri->abspath;
1173 /* don't remove the first slash if that's the only thing left */
1174 if (len > 1 && *(d - 1) == '/')
1180 client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
1182 GST_INFO ("client %p: session %p finished", client, session);
1184 /* unlink all media managed in this session */
1185 client_unlink_session (client, session);
1187 /* remove the session */
1188 if (!(client->sessions = g_list_remove (client->sessions, session))) {
1189 GST_INFO ("client %p: all sessions finalized, close the connection",
1191 close_connection (client);
1196 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
1200 for (walk = client->sessions; walk; walk = g_list_next (walk)) {
1201 GstRTSPSession *msession = (GstRTSPSession *) walk->data;
1203 /* we already know about this session */
1204 if (msession == session)
1208 GST_INFO ("watching session %p", session);
1210 g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized,
1212 client->sessions = g_list_prepend (client->sessions, session);
1216 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
1218 GstRTSPMethod method;
1219 const gchar *uristr;
1221 GstRTSPVersion version;
1223 GstRTSPSession *session;
1226 if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
1227 gst_rtsp_message_dump (request);
1230 GST_INFO ("client %p: received a request", client);
1232 gst_rtsp_message_parse_request (request, &method, &uristr, &version);
1234 if (version != GST_RTSP_VERSION_1_0) {
1235 /* we can only handle 1.0 requests */
1236 send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
1241 /* we always try to parse the url first */
1242 if (gst_rtsp_url_parse (uristr, &uri) != GST_RTSP_OK) {
1243 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1247 /* sanitize the uri */
1250 /* get the session if there is any */
1251 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
1252 if (res == GST_RTSP_OK) {
1253 if (client->session_pool == NULL)
1256 /* we had a session in the request, find it again */
1257 if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
1258 goto session_not_found;
1260 /* we add the session to the client list of watched sessions. When a session
1261 * disappears because it times out, we will be notified. If all sessions are
1262 * gone, we will close the connection */
1263 client_watch_session (client, session);
1267 /* now see what is asked and dispatch to a dedicated handler */
1269 case GST_RTSP_OPTIONS:
1270 handle_options_request (client, uri, session, request);
1272 case GST_RTSP_DESCRIBE:
1273 handle_describe_request (client, uri, session, request);
1275 case GST_RTSP_SETUP:
1276 handle_setup_request (client, uri, session, request);
1279 handle_play_request (client, uri, session, request);
1281 case GST_RTSP_PAUSE:
1282 handle_pause_request (client, uri, session, request);
1284 case GST_RTSP_TEARDOWN:
1285 handle_teardown_request (client, uri, session, request);
1287 case GST_RTSP_SET_PARAMETER:
1288 handle_set_param_request (client, uri, session, request);
1290 case GST_RTSP_GET_PARAMETER:
1291 handle_get_param_request (client, uri, session, request);
1293 case GST_RTSP_ANNOUNCE:
1294 case GST_RTSP_RECORD:
1295 case GST_RTSP_REDIRECT:
1296 send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
1298 case GST_RTSP_INVALID:
1300 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1304 g_object_unref (session);
1306 gst_rtsp_url_free (uri);
1312 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1317 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
1323 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
1333 /* find the stream for this message */
1334 res = gst_rtsp_message_parse_data (message, &channel);
1335 if (res != GST_RTSP_OK)
1338 gst_rtsp_message_steal_body (message, &data, &size);
1340 buffer = gst_buffer_new ();
1341 GST_BUFFER_DATA (buffer) = data;
1342 GST_BUFFER_MALLOCDATA (buffer) = data;
1343 GST_BUFFER_SIZE (buffer) = size;
1346 for (walk = client->streams; walk; walk = g_list_next (walk)) {
1347 GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
1348 GstRTSPMediaStream *mstream;
1349 GstRTSPTransport *tr;
1351 /* get the transport, if there is no transport configured, skip this stream */
1352 if (!(tr = stream->trans.transport))
1355 /* we also need a media stream */
1356 if (!(mstream = stream->media_stream))
1359 /* check for TCP transport */
1360 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1361 /* dispatch to the stream based on the channel number */
1362 if (tr->interleaved.min == channel) {
1363 gst_rtsp_media_stream_rtp (mstream, buffer);
1366 } else if (tr->interleaved.max == channel) {
1367 gst_rtsp_media_stream_rtcp (mstream, buffer);
1374 gst_buffer_unref (buffer);
1378 * gst_rtsp_client_set_session_pool:
1379 * @client: a #GstRTSPClient
1380 * @pool: a #GstRTSPSessionPool
1382 * Set @pool as the sessionpool for @client which it will use to find
1383 * or allocate sessions. the sessionpool is usually inherited from the server
1384 * that created the client but can be overridden later.
1387 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
1388 GstRTSPSessionPool * pool)
1390 GstRTSPSessionPool *old;
1392 old = client->session_pool;
1395 g_object_ref (pool);
1396 client->session_pool = pool;
1398 g_object_unref (old);
1403 * gst_rtsp_client_get_session_pool:
1404 * @client: a #GstRTSPClient
1406 * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1408 * Returns: a #GstRTSPSessionPool, unref after usage.
1410 GstRTSPSessionPool *
1411 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
1413 GstRTSPSessionPool *result;
1415 if ((result = client->session_pool))
1416 g_object_ref (result);
1422 * gst_rtsp_client_set_media_mapping:
1423 * @client: a #GstRTSPClient
1424 * @mapping: a #GstRTSPMediaMapping
1426 * Set @mapping as the media mapping for @client which it will use to map urls
1427 * to media streams. These mapping is usually inherited from the server that
1428 * created the client but can be overriden later.
1431 gst_rtsp_client_set_media_mapping (GstRTSPClient * client,
1432 GstRTSPMediaMapping * mapping)
1434 GstRTSPMediaMapping *old;
1436 old = client->media_mapping;
1438 if (old != mapping) {
1440 g_object_ref (mapping);
1441 client->media_mapping = mapping;
1443 g_object_unref (old);
1448 * gst_rtsp_client_get_media_mapping:
1449 * @client: a #GstRTSPClient
1451 * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1453 * Returns: a #GstRTSPMediaMapping, unref after usage.
1455 GstRTSPMediaMapping *
1456 gst_rtsp_client_get_media_mapping (GstRTSPClient * client)
1458 GstRTSPMediaMapping *result;
1460 if ((result = client->media_mapping))
1461 g_object_ref (result);
1466 static GstRTSPResult
1467 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
1470 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1472 switch (message->type) {
1473 case GST_RTSP_MESSAGE_REQUEST:
1474 handle_request (client, message);
1476 case GST_RTSP_MESSAGE_RESPONSE:
1478 case GST_RTSP_MESSAGE_DATA:
1479 handle_data (client, message);
1487 static GstRTSPResult
1488 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
1490 GstRTSPClient *client;
1492 client = GST_RTSP_CLIENT (user_data);
1494 /* GST_INFO ("client %p: sent a message with cseq %d", client, cseq); */
1499 static GstRTSPResult
1500 closed (GstRTSPWatch * watch, gpointer user_data)
1502 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1503 const gchar *tunnelid;
1505 GST_INFO ("client %p: connection closed", client);
1507 if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1508 g_mutex_lock (tunnels_lock);
1509 /* remove from tunnelids */
1510 g_hash_table_remove (tunnels, tunnelid);
1511 g_mutex_unlock (tunnels_lock);
1517 static GstRTSPResult
1518 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
1520 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1523 str = gst_rtsp_strresult (result);
1524 GST_INFO ("client %p: received an error %s", client, str);
1530 static GstRTSPResult
1531 error_full (GstRTSPWatch * watch, GstRTSPResult result,
1532 GstRTSPMessage * message, guint id, gpointer user_data)
1534 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1537 str = gst_rtsp_strresult (result);
1539 ("client %p: received an error %s when handling message %p with id %d",
1540 client, str, message, id);
1547 remember_tunnel (GstRTSPClient * client)
1549 const gchar *tunnelid;
1551 /* store client in the pending tunnels */
1552 tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1553 if (tunnelid == NULL)
1556 GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
1558 /* we can't have two clients connecting with the same tunnelid */
1559 g_mutex_lock (tunnels_lock);
1560 if (g_hash_table_lookup (tunnels, tunnelid))
1561 goto tunnel_existed;
1563 g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1564 g_mutex_unlock (tunnels_lock);
1571 GST_ERROR ("client %p: no tunnelid provided", client);
1576 g_mutex_unlock (tunnels_lock);
1577 GST_ERROR ("client %p: tunnel session %s already existed", client,
1583 static GstRTSPStatusCode
1584 tunnel_start (GstRTSPWatch * watch, gpointer user_data)
1586 GstRTSPClient *client;
1588 client = GST_RTSP_CLIENT (user_data);
1590 GST_INFO ("client %p: tunnel start (connection %p)", client,
1591 client->connection);
1593 if (!remember_tunnel (client))
1596 return GST_RTSP_STS_OK;
1601 GST_ERROR ("client %p: error starting tunnel", client);
1602 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1606 static GstRTSPResult
1607 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
1609 GstRTSPClient *client;
1611 client = GST_RTSP_CLIENT (user_data);
1613 GST_INFO ("client %p: tunnel lost (connection %p)", client,
1614 client->connection);
1616 /* ignore error, it'll only be a problem when the client does a POST again */
1617 remember_tunnel (client);
1622 static GstRTSPResult
1623 tunnel_complete (GstRTSPWatch * watch, gpointer user_data)
1625 const gchar *tunnelid;
1626 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1627 GstRTSPClient *oclient;
1629 GST_INFO ("client %p: tunnel complete", client);
1631 /* find previous tunnel */
1632 tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1633 if (tunnelid == NULL)
1636 g_mutex_lock (tunnels_lock);
1637 if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1640 /* remove the old client from the table. ref before because removing it will
1641 * remove the ref to it. */
1642 g_object_ref (oclient);
1643 g_hash_table_remove (tunnels, tunnelid);
1645 if (oclient->watch == NULL)
1647 g_mutex_unlock (tunnels_lock);
1649 GST_INFO ("client %p: found tunnel %p (old %p, new %p)", client, oclient,
1650 oclient->connection, client->connection);
1652 /* merge the tunnels into the first client */
1653 gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1654 gst_rtsp_watch_reset (oclient->watch);
1655 g_object_unref (oclient);
1657 /* we don't need this watch anymore */
1658 g_source_destroy ((GSource *) client->watch);
1659 client->watchid = 0;
1660 client->watch = NULL;
1667 GST_INFO ("client %p: no tunnelid provided", client);
1668 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1672 g_mutex_unlock (tunnels_lock);
1673 GST_INFO ("client %p: tunnel session %s not found", client, tunnelid);
1674 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1678 g_mutex_unlock (tunnels_lock);
1679 GST_INFO ("client %p: tunnel session %s was closed", client, tunnelid);
1680 g_object_unref (oclient);
1681 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1685 static GstRTSPWatchFuncs watch_funcs = {
1697 client_watch_notify (GstRTSPClient * client)
1699 GST_INFO ("client %p: watch destroyed", client);
1700 client->watchid = 0;
1701 client->watch = NULL;
1702 g_object_unref (client);
1706 * gst_rtsp_client_attach:
1707 * @client: a #GstRTSPClient
1708 * @channel: a #GIOChannel
1710 * Accept a new connection for @client on the socket in @channel.
1712 * This function should be called when the client properties and urls are fully
1713 * configured and the client is ready to start.
1715 * Returns: %TRUE if the client could be accepted.
1718 gst_rtsp_client_accept (GstRTSPClient * client, GIOChannel * channel)
1721 GstRTSPConnection *conn;
1724 GMainContext *context;
1726 struct sockaddr_storage addr;
1728 gchar ip[INET6_ADDRSTRLEN];
1730 /* a new client connected. */
1731 sock = g_io_channel_unix_get_fd (channel);
1733 GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1735 fd = gst_rtsp_connection_get_readfd (conn);
1737 addrlen = sizeof (addr);
1738 if (getsockname (fd, (struct sockaddr *) &addr, &addrlen) < 0)
1739 goto getpeername_failed;
1741 client->is_ipv6 = addr.ss_family == AF_INET6;
1743 if (getnameinfo ((struct sockaddr *) &addr, addrlen, ip, sizeof (ip), NULL, 0,
1744 NI_NUMERICHOST) != 0)
1745 goto getnameinfo_failed;
1747 /* keep the original ip that the client connected to */
1748 g_free (client->server_ip);
1749 client->server_ip = g_strndup (ip, sizeof (ip));
1751 GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
1752 client->server_ip, client->is_ipv6);
1754 url = gst_rtsp_connection_get_url (conn);
1755 GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
1757 client->connection = conn;
1759 /* create watch for the connection and attach */
1760 client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1761 g_object_ref (client), (GDestroyNotify) client_watch_notify);
1763 /* find the context to add the watch */
1764 if ((source = g_main_current_source ()))
1765 context = g_source_get_context (source);
1769 GST_INFO ("attaching to context %p", context);
1771 client->watchid = gst_rtsp_watch_attach (client->watch, context);
1772 gst_rtsp_watch_unref (client->watch);
1779 gchar *str = gst_rtsp_strresult (res);
1781 GST_ERROR ("Could not accept client on server socket %d: %s", sock, str);
1787 GST_ERROR ("getpeername failed: %s", g_strerror (errno));
1792 GST_ERROR ("getnameinfo failed: %s", g_strerror (errno));