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"
27 static GMutex *tunnels_lock;
28 static GHashTable *tunnels;
38 static void gst_rtsp_client_get_property (GObject *object, guint propid,
39 GValue *value, GParamSpec *pspec);
40 static void gst_rtsp_client_set_property (GObject *object, guint propid,
41 const GValue *value, GParamSpec *pspec);
42 static void gst_rtsp_client_finalize (GObject * obj);
44 static void client_session_finalized (GstRTSPClient *client, GstRTSPSession *session);
46 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
49 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
51 GObjectClass *gobject_class;
53 gobject_class = G_OBJECT_CLASS (klass);
55 gobject_class->get_property = gst_rtsp_client_get_property;
56 gobject_class->set_property = gst_rtsp_client_set_property;
57 gobject_class->finalize = gst_rtsp_client_finalize;
59 g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
60 g_param_spec_object ("session-pool", "Session Pool",
61 "The session pool to use for client session",
62 GST_TYPE_RTSP_SESSION_POOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
64 g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
65 g_param_spec_object ("media-mapping", "Media Mapping",
66 "The media mapping to use for client session",
67 GST_TYPE_RTSP_MEDIA_MAPPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
69 tunnels = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
70 tunnels_lock = g_mutex_new ();
74 gst_rtsp_client_init (GstRTSPClient * client)
78 /* A client is finalized when the connection is broken */
80 gst_rtsp_client_finalize (GObject * obj)
82 GstRTSPClient *client = GST_RTSP_CLIENT (obj);
85 g_message ("finalize client %p", client);
87 for (walk = client->sessions; walk; walk = g_list_next (walk))
88 g_object_weak_unref (G_OBJECT (walk->data), (GWeakNotify) client_session_finalized, client);
89 g_list_free (client->sessions);
91 gst_rtsp_connection_free (client->connection);
92 if (client->session_pool)
93 g_object_unref (client->session_pool);
94 if (client->media_mapping)
95 g_object_unref (client->media_mapping);
98 gst_rtsp_url_free (client->uri);
100 g_object_unref (client->media);
102 G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
106 gst_rtsp_client_get_property (GObject *object, guint propid,
107 GValue *value, GParamSpec *pspec)
109 GstRTSPClient *client = GST_RTSP_CLIENT (object);
112 case PROP_SESSION_POOL:
113 g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
115 case PROP_MEDIA_MAPPING:
116 g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
119 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
124 gst_rtsp_client_set_property (GObject *object, guint propid,
125 const GValue *value, GParamSpec *pspec)
127 GstRTSPClient *client = GST_RTSP_CLIENT (object);
130 case PROP_SESSION_POOL:
131 gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
133 case PROP_MEDIA_MAPPING:
134 gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
137 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
142 * gst_rtsp_client_new:
144 * Create a new #GstRTSPClient instance.
147 gst_rtsp_client_new (void)
149 GstRTSPClient *result;
151 result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
157 send_response (GstRTSPClient *client, GstRTSPSession *session, GstRTSPMessage *response)
159 gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER, "GStreamer RTSP server");
161 /* remove any previous header */
162 gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
164 /* add the new session header for new session ids */
168 if (session->timeout != 60)
169 str = g_strdup_printf ("%s; timeout=%d", session->sessionid, session->timeout);
171 str = g_strdup (session->sessionid);
173 gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
177 gst_rtsp_message_dump (response);
180 gst_rtsp_watch_queue_message (client->watch, response);
181 gst_rtsp_message_unset (response);
185 send_generic_response (GstRTSPClient *client, GstRTSPStatusCode code,
186 GstRTSPMessage *request)
188 GstRTSPMessage response = { 0 };
190 gst_rtsp_message_init_response (&response, code,
191 gst_rtsp_status_as_text (code), request);
193 send_response (client, NULL, &response);
197 compare_uri (const GstRTSPUrl *uri1, const GstRTSPUrl *uri2)
199 if (uri1 == NULL || uri2 == NULL)
202 if (strcmp (uri1->abspath, uri2->abspath))
208 /* this function is called to initially find the media for the DESCRIBE request
209 * but is cached for when the same client (without breaking the connection) is
210 * doing a setup for the exact same url. */
211 static GstRTSPMedia *
212 find_media (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
214 GstRTSPMediaFactory *factory;
217 if (!compare_uri (client->uri, uri)) {
218 /* remove any previously cached values before we try to construct a new
221 gst_rtsp_url_free (client->uri);
224 g_object_unref (client->media);
225 client->media = NULL;
227 if (!client->media_mapping)
230 /* find the factory for the uri first */
231 if (!(factory = gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
234 /* prepare the media and add it to the pipeline */
235 if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
238 /* prepare the media */
239 if (!(gst_rtsp_media_prepare (media)))
242 /* now keep track of the uri and the media */
243 client->uri = gst_rtsp_url_copy (uri);
244 client->media = media;
247 /* we have seen this uri before, used cached media */
248 media = client->media;
249 g_message ("reusing cached media %p", media);
253 g_object_ref (media);
260 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
265 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
270 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
271 g_object_unref (factory);
276 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
277 g_object_unref (media);
278 g_object_unref (factory);
284 do_send_data (GstBuffer *buffer, guint8 channel, GstRTSPClient *client)
286 GstRTSPMessage message = { 0 };
290 gst_rtsp_message_init_data (&message, channel);
292 data = GST_BUFFER_DATA (buffer);
293 size = GST_BUFFER_SIZE (buffer);
294 gst_rtsp_message_take_body (&message, data, size);
296 gst_rtsp_watch_queue_message (client->watch, &message);
298 gst_rtsp_message_steal_body (&message, &data, &size);
299 gst_rtsp_message_unset (&message);
305 link_stream (GstRTSPClient *client, GstRTSPSessionStream *stream)
307 gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
308 (GstRTSPSendFunc) do_send_data, client, NULL);
309 client->streams = g_list_prepend (client->streams, stream);
313 unlink_stream (GstRTSPClient *client, GstRTSPSessionStream *stream)
315 gst_rtsp_session_stream_set_callbacks (stream, NULL,
317 client->streams = g_list_remove (client->streams, stream);
321 unlink_streams (GstRTSPClient *client)
325 for (walk = client->streams; walk; walk = g_list_next (walk)) {
326 GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
328 gst_rtsp_session_stream_set_callbacks (stream, NULL,
331 g_list_free (client->streams);
332 client->streams = NULL;
336 unlink_session_streams (GstRTSPClient *client, GstRTSPSessionMedia *media)
340 n_streams = gst_rtsp_media_n_streams (media->media);
341 for (i = 0; i < n_streams; i++) {
342 GstRTSPSessionStream *sstream;
343 GstRTSPTransport *tr;
345 /* get the stream as configured in the session */
346 sstream = gst_rtsp_session_media_get_stream (media, i);
347 /* get the transport, if there is no transport configured, skip this stream */
348 if (!(tr = sstream->trans.transport))
351 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
352 /* for TCP, unlink the stream from the TCP connection of the client */
353 unlink_stream (client, sstream);
359 handle_teardown_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
361 GstRTSPSessionMedia *media;
362 GstRTSPMessage response = { 0 };
363 GstRTSPStatusCode code;
368 /* get a handle to the configuration of the media in the session */
369 media = gst_rtsp_session_get_media (session, uri);
373 /* unlink the all TCP callbacks */
374 unlink_session_streams (client, media);
376 /* remove the session from the watched sessions */
377 g_object_weak_unref (G_OBJECT (session), (GWeakNotify) client_session_finalized, client);
378 client->sessions = g_list_remove (client->sessions, session);
380 gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
382 /* unmanage the media in the session, returns false if all media session
384 if (!gst_rtsp_session_release_media (session, media)) {
385 /* remove the session */
386 gst_rtsp_session_pool_remove (client->session_pool, session);
388 /* construct the response now */
389 code = GST_RTSP_STS_OK;
390 gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
392 send_response (client, session, &response);
399 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
404 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
410 handle_pause_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
412 GstRTSPSessionMedia *media;
413 GstRTSPMessage response = { 0 };
414 GstRTSPStatusCode code;
419 /* get a handle to the configuration of the media in the session */
420 media = gst_rtsp_session_get_media (session, uri);
424 /* the session state must be playing or recording */
425 if (media->state != GST_RTSP_STATE_PLAYING &&
426 media->state != GST_RTSP_STATE_RECORDING)
429 /* unlink the all TCP callbacks */
430 unlink_session_streams (client, media);
432 /* then pause sending */
433 gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
435 /* construct the response now */
436 code = GST_RTSP_STS_OK;
437 gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
439 send_response (client, session, &response);
441 /* the state is now READY */
442 media->state = GST_RTSP_STATE_READY;
449 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
454 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
459 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
465 handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
467 GstRTSPSessionMedia *media;
468 GstRTSPMessage response = { 0 };
469 GstRTSPStatusCode code;
472 guint timestamp, seqnum;
474 GstRTSPTimeRange *range;
480 /* get a handle to the configuration of the media in the session */
481 media = gst_rtsp_session_get_media (session, uri);
485 /* the session state must be playing or ready */
486 if (media->state != GST_RTSP_STATE_PLAYING &&
487 media->state != GST_RTSP_STATE_READY)
490 /* parse the range header if we have one */
491 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_RANGE, &str, 0);
492 if (res == GST_RTSP_OK) {
493 if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
494 /* we have a range, seek to the position */
495 gst_rtsp_media_seek (media->media, range);
496 gst_rtsp_range_free (range);
500 /* grab RTPInfo from the payloaders now */
501 rtpinfo = g_string_new ("");
503 n_streams = gst_rtsp_media_n_streams (media->media);
504 for (i = 0; i < n_streams; i++) {
505 GstRTSPSessionStream *sstream;
506 GstRTSPMediaStream *stream;
507 GstRTSPTransport *tr;
510 /* get the stream as configured in the session */
511 sstream = gst_rtsp_session_media_get_stream (media, i);
512 /* get the transport, if there is no transport configured, skip this stream */
513 if (!(tr = sstream->trans.transport))
516 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
517 /* for TCP, link the stream to the TCP connection of the client */
518 link_stream (client, sstream);
521 stream = sstream->media_stream;
523 g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL);
524 g_object_get (G_OBJECT (stream->payloader), "timestamp", ×tamp, NULL);
527 g_string_append (rtpinfo, ", ");
529 uristr = gst_rtsp_url_get_request_uri (uri);
530 g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u", uristr, i, seqnum, timestamp);
534 /* construct the response now */
535 code = GST_RTSP_STS_OK;
536 gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
538 /* add the RTP-Info header */
539 str = g_string_free (rtpinfo, FALSE);
540 gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
543 str = gst_rtsp_range_to_string (&media->media->range);
544 gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
546 send_response (client, session, &response);
548 /* start playing after sending the request */
549 gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
551 media->state = GST_RTSP_STATE_PLAYING;
558 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
563 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
568 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
574 handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
579 gboolean have_transport;
580 GstRTSPTransport *ct, *st;
582 GstRTSPLowerTrans supported;
583 GstRTSPMessage response = { 0 };
584 GstRTSPStatusCode code;
585 GstRTSPSessionStream *stream;
586 gchar *trans_str, *pos;
588 GstRTSPSessionMedia *media;
589 gboolean need_session;
592 /* the uri contains the stream number we added in the SDP config, which is
593 * always /stream=%d so we need to strip that off
594 * parse the stream we need to configure, look for the stream in the abspath
595 * first and then in the query. */
596 if (!(pos = strstr (uri->abspath, "/stream="))) {
597 if (!(pos = strstr (uri->query, "/stream=")))
601 /* we can mofify the parse uri in place */
604 pos += strlen ("/stream=");
605 if (sscanf (pos, "%u", &streamid) != 1)
608 /* parse the transport */
609 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
610 if (res != GST_RTSP_OK)
613 transports = g_strsplit (transport, ",", 0);
614 gst_rtsp_transport_new (&ct);
616 /* loop through the transports, try to parse */
617 have_transport = FALSE;
618 for (i = 0; transports[i]; i++) {
620 gst_rtsp_transport_init (ct);
621 res = gst_rtsp_transport_parse (transports[i], ct);
622 if (res == GST_RTSP_OK) {
623 have_transport = TRUE;
627 g_strfreev (transports);
629 /* we have not found anything usable, error out */
631 goto unsupported_transports;
633 /* we have a valid transport, check if we can handle it */
634 if (ct->trans != GST_RTSP_TRANS_RTP)
635 goto unsupported_transports;
636 if (ct->profile != GST_RTSP_PROFILE_AVP)
637 goto unsupported_transports;
639 supported = GST_RTSP_LOWER_TRANS_UDP |
640 GST_RTSP_LOWER_TRANS_UDP_MCAST |
641 GST_RTSP_LOWER_TRANS_TCP;
642 if (!(ct->lower_transport & supported))
643 goto unsupported_transports;
645 if (client->session_pool == NULL)
648 /* we have a valid transport now, set the destination of the client. */
649 g_free (ct->destination);
650 url = gst_rtsp_connection_get_url (client->connection);
651 ct->destination = g_strdup (url->host);
654 g_object_ref (session);
655 /* get a handle to the configuration of the media in the session, this can
656 * return NULL if this is a new url to manage in this session. */
657 media = gst_rtsp_session_get_media (session, uri);
659 need_session = FALSE;
662 /* create a session if this fails we probably reached our session limit or
664 if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
665 goto service_unavailable;
667 /* we need a new media configuration in this session */
673 /* we have no media, find one and manage it */
677 /* get a handle to the configuration of the media in the session */
678 if ((m = find_media (client, uri, request))) {
679 /* manage the media in our session now */
680 media = gst_rtsp_session_manage_media (session, uri, m);
684 /* if we stil have no media, error */
688 /* get a handle to the stream in the media */
689 if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
692 st = gst_rtsp_session_stream_set_transport (stream, ct);
694 /* serialize the server transport */
695 trans_str = gst_rtsp_transport_as_text (st);
696 gst_rtsp_transport_free (st);
698 /* construct the response now */
699 code = GST_RTSP_STS_OK;
700 gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
702 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
705 send_response (client, session, &response);
707 /* update the state */
708 switch (media->state) {
709 case GST_RTSP_STATE_PLAYING:
710 case GST_RTSP_STATE_RECORDING:
711 case GST_RTSP_STATE_READY:
712 /* no state change */
715 media->state = GST_RTSP_STATE_READY;
718 g_object_unref (session);
725 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
730 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
731 g_object_unref (session);
736 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
737 g_object_unref (media);
738 g_object_unref (session);
743 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
746 unsupported_transports:
748 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
749 gst_rtsp_transport_free (ct);
754 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
759 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
764 /* for the describe we must generate an SDP */
766 handle_describe_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
768 GstRTSPMessage response = { 0 };
775 /* check what kind of format is accepted, we don't really do anything with it
776 * and always return SDP for now. */
780 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
781 if (res == GST_RTSP_ENOTIMPL)
784 if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
788 /* find the media object for the uri */
789 if (!(media = find_media (client, uri, request)))
792 /* create an SDP for the media object */
793 if (!(sdp = gst_rtsp_sdp_from_media (media)))
796 g_object_unref (media);
798 gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
799 gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
801 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "application/sdp");
803 /* content base for some clients that might screw up creating the setup uri */
804 str = g_strdup_printf ("rtsp://%s:%u%s/", uri->host, uri->port, uri->abspath);
805 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE, str);
808 /* add SDP to the response body */
809 str = gst_sdp_message_as_text (sdp);
810 gst_rtsp_message_take_body (&response, (guint8 *)str, strlen (str));
811 gst_sdp_message_free (sdp);
813 send_response (client, session, &response);
820 /* error reply is already sent */
825 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
826 g_object_unref (media);
832 handle_options_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
834 GstRTSPMessage response = { 0 };
835 GstRTSPMethod options;
838 options = GST_RTSP_DESCRIBE |
845 str = gst_rtsp_options_as_text (options);
847 gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
848 gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
850 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
853 send_response (client, session, &response);
856 /* remove duplicate and trailing '/' */
858 santize_uri (GstRTSPUrl *uri)
862 gboolean have_slash, prev_slash;
864 s = d = uri->abspath;
865 len = strlen (uri->abspath);
869 for (i = 0; i < len; i++) {
870 have_slash = s[i] == '/';
872 if (!have_slash || !prev_slash)
874 prev_slash = have_slash;
876 len = d - uri->abspath;
877 /* don't remove the first slash if that's the only thing left */
878 if (len > 1 && *(d-1) == '/')
884 client_session_finalized (GstRTSPClient *client, GstRTSPSession *session)
886 if (!(client->sessions = g_list_remove (client->sessions, session))) {
887 g_message ("all sessions finalized, close the connection");
888 g_source_destroy ((GSource*)client->watch);
893 client_watch_session (GstRTSPClient *client, GstRTSPSession *session)
897 for (walk = client->sessions; walk; walk = g_list_next (walk)) {
898 GstRTSPSession *msession = (GstRTSPSession *) walk->data;
900 /* we already know about this session */
901 if (msession == session)
905 g_message ("watching session %p", session);
907 g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized, client);
908 client->sessions = g_list_prepend (client->sessions, session);
912 handle_request (GstRTSPClient *client, GstRTSPMessage *request)
914 GstRTSPMethod method;
917 GstRTSPVersion version;
919 GstRTSPSession *session;
923 gst_rtsp_message_dump (request);
926 g_message ("client %p: received a request", client);
928 gst_rtsp_message_parse_request (request, &method, &uristr, &version);
930 if (version != GST_RTSP_VERSION_1_0) {
931 /* we can only handle 1.0 requests */
932 send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED, request);
936 /* we always try to parse the url first */
937 if ((res = gst_rtsp_url_parse (uristr, &uri)) != GST_RTSP_OK) {
938 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
942 /* sanitize the uri */
945 /* get the session if there is any */
946 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
947 if (res == GST_RTSP_OK) {
948 if (client->session_pool == NULL)
951 /* we had a session in the request, find it again */
952 if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
953 goto session_not_found;
955 /* we add the session to the client list of watched sessions. When a session
956 * disappears because it times out, we will be notified. If all sessions are
957 * gone, we will close the connection */
958 client_watch_session (client, session);
963 /* now see what is asked and dispatch to a dedicated handler */
965 case GST_RTSP_OPTIONS:
966 handle_options_request (client, uri, session, request);
968 case GST_RTSP_DESCRIBE:
969 handle_describe_request (client, uri, session, request);
972 handle_setup_request (client, uri, session, request);
975 handle_play_request (client, uri, session, request);
978 handle_pause_request (client, uri, session, request);
980 case GST_RTSP_TEARDOWN:
981 handle_teardown_request (client, uri, session, request);
983 case GST_RTSP_ANNOUNCE:
984 case GST_RTSP_GET_PARAMETER:
985 case GST_RTSP_RECORD:
986 case GST_RTSP_REDIRECT:
987 case GST_RTSP_SET_PARAMETER:
988 send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
990 case GST_RTSP_INVALID:
992 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
996 g_object_unref (session);
998 gst_rtsp_url_free (uri);
1004 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1009 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
1015 handle_data (GstRTSPClient *client, GstRTSPMessage *message)
1024 /* find the stream for this message */
1025 res = gst_rtsp_message_parse_data (message, &channel);
1026 if (res != GST_RTSP_OK)
1029 gst_rtsp_message_steal_body (message, &data, &size);
1031 buffer = gst_buffer_new ();
1032 GST_BUFFER_DATA (buffer) = data;
1033 GST_BUFFER_MALLOCDATA (buffer) = data;
1034 GST_BUFFER_SIZE (buffer) = size;
1036 for (walk = client->streams; walk; walk = g_list_next (walk)) {
1037 GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
1038 GstRTSPMediaStream *mstream;
1039 GstRTSPTransport *tr;
1041 /* get the transport, if there is no transport configured, skip this stream */
1042 if (!(tr = stream->trans.transport))
1045 /* we also need a media stream */
1046 if (!(mstream = stream->media_stream))
1049 /* check for TCP transport */
1050 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1051 /* dispatch to the stream based on the channel number */
1052 if (tr->interleaved.min == channel) {
1053 gst_rtsp_media_stream_rtp (mstream, buffer);
1054 } else if (tr->interleaved.max == channel) {
1055 gst_rtsp_media_stream_rtcp (mstream, buffer);
1059 gst_buffer_unref (buffer);
1063 * gst_rtsp_client_set_session_pool:
1064 * @client: a #GstRTSPClient
1065 * @pool: a #GstRTSPSessionPool
1067 * Set @pool as the sessionpool for @client which it will use to find
1068 * or allocate sessions. the sessionpool is usually inherited from the server
1069 * that created the client but can be overridden later.
1072 gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool)
1074 GstRTSPSessionPool *old;
1076 old = client->session_pool;
1079 g_object_ref (pool);
1080 client->session_pool = pool;
1082 g_object_unref (old);
1087 * gst_rtsp_client_get_session_pool:
1088 * @client: a #GstRTSPClient
1090 * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1092 * Returns: a #GstRTSPSessionPool, unref after usage.
1094 GstRTSPSessionPool *
1095 gst_rtsp_client_get_session_pool (GstRTSPClient *client)
1097 GstRTSPSessionPool *result;
1099 if ((result = client->session_pool))
1100 g_object_ref (result);
1106 * gst_rtsp_client_set_media_mapping:
1107 * @client: a #GstRTSPClient
1108 * @mapping: a #GstRTSPMediaMapping
1110 * Set @mapping as the media mapping for @client which it will use to map urls
1111 * to media streams. These mapping is usually inherited from the server that
1112 * created the client but can be overriden later.
1115 gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *mapping)
1117 GstRTSPMediaMapping *old;
1119 old = client->media_mapping;
1121 if (old != mapping) {
1123 g_object_ref (mapping);
1124 client->media_mapping = mapping;
1126 g_object_unref (old);
1131 * gst_rtsp_client_get_media_mapping:
1132 * @client: a #GstRTSPClient
1134 * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1136 * Returns: a #GstRTSPMediaMapping, unref after usage.
1138 GstRTSPMediaMapping *
1139 gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
1141 GstRTSPMediaMapping *result;
1143 if ((result = client->media_mapping))
1144 g_object_ref (result);
1149 static GstRTSPResult
1150 message_received (GstRTSPWatch *watch, GstRTSPMessage *message, gpointer user_data)
1152 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1154 switch (message->type) {
1155 case GST_RTSP_MESSAGE_REQUEST:
1156 handle_request (client, message);
1158 case GST_RTSP_MESSAGE_RESPONSE:
1160 case GST_RTSP_MESSAGE_DATA:
1161 handle_data (client, message);
1169 static GstRTSPResult
1170 message_sent (GstRTSPWatch *watch, guint cseq, gpointer user_data)
1172 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1174 g_message ("client %p: sent a message with cseq %d", client, cseq);
1179 static GstRTSPResult
1180 closed (GstRTSPWatch *watch, gpointer user_data)
1182 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1183 const gchar *tunnelid;
1185 g_message ("client %p: connection closed", client);
1187 if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1188 g_mutex_lock (tunnels_lock);
1189 g_hash_table_remove (tunnels, tunnelid);
1190 g_mutex_unlock (tunnels_lock);
1193 /* remove all streams that are streaming over this client connection */
1194 unlink_streams (client);
1199 static GstRTSPResult
1200 error (GstRTSPWatch *watch, GstRTSPResult result, gpointer user_data)
1202 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1205 str = gst_rtsp_strresult (result);
1206 g_message ("client %p: received an error %s", client, str);
1212 static GstRTSPStatusCode
1213 tunnel_start (GstRTSPWatch *watch, gpointer user_data)
1215 GstRTSPClient *client;
1216 const gchar *tunnelid;
1218 client = GST_RTSP_CLIENT (user_data);
1220 g_message ("client %p: tunnel start", client);
1222 /* store client in the pending tunnels */
1223 tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1225 g_message ("client %p: inserting %s", client, tunnelid);
1227 /* we can't have two clients connecting with the same tunnelid */
1228 g_mutex_lock (tunnels_lock);
1229 if (g_hash_table_lookup (tunnels, tunnelid))
1230 goto tunnel_existed;
1232 g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1233 g_mutex_unlock (tunnels_lock);
1235 return GST_RTSP_STS_OK;
1240 g_mutex_unlock (tunnels_lock);
1241 g_message ("client %p: tunnel session %s existed", client, tunnelid);
1242 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1246 static GstRTSPResult
1247 tunnel_complete (GstRTSPWatch *watch, gpointer user_data)
1249 const gchar *tunnelid;
1250 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1251 GstRTSPClient *oclient;
1253 g_message ("client %p: tunnel complete", client);
1255 /* find previous tunnel */
1256 tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1258 g_mutex_lock (tunnels_lock);
1259 if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1262 /* remove the old client from the table. ref before because removing it will
1263 * remove the ref to it. */
1264 g_object_ref (oclient);
1265 g_hash_table_remove (tunnels, tunnelid);
1266 g_mutex_unlock (tunnels_lock);
1268 g_message ("client %p: found tunnel %p", client, oclient);
1270 /* merge the tunnels into the first client */
1271 gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1272 gst_rtsp_watch_reset (oclient->watch);
1273 g_object_unref (oclient);
1275 /* we don't need this watch anymore */
1276 g_source_remove (client->watchid);
1283 g_mutex_unlock (tunnels_lock);
1284 g_message ("client %p: tunnel session %s not found", client, tunnelid);
1289 static GstRTSPWatchFuncs watch_funcs = {
1299 * gst_rtsp_client_attach:
1300 * @client: a #GstRTSPClient
1301 * @channel: a #GIOChannel
1303 * Accept a new connection for @client on the socket in @source.
1305 * This function should be called when the client properties and urls are fully
1306 * configured and the client is ready to start.
1308 * Returns: %TRUE if the client could be accepted.
1311 gst_rtsp_client_accept (GstRTSPClient *client, GIOChannel *channel)
1314 GstRTSPConnection *conn;
1317 GMainContext *context;
1320 /* a new client connected. */
1321 sock = g_io_channel_unix_get_fd (channel);
1323 GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1325 url = gst_rtsp_connection_get_url (conn);
1326 g_message ("added new client %p ip %s:%d", client,
1327 url->host, url->port);
1329 client->connection = conn;
1331 /* create watch for the connection and attach */
1332 client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1333 g_object_ref (client), g_object_unref);
1335 /* find the context to add the watch */
1336 if ((source = g_main_current_source ()))
1337 context = g_source_get_context (source);
1341 g_message ("attaching to context %p", context);
1343 client->watchid = gst_rtsp_watch_attach (client->watch, context);
1344 gst_rtsp_watch_unref (client->watch);
1351 gchar *str = gst_rtsp_strresult (res);
1353 g_error ("Could not accept client on server socket %d: %s",