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 static GMutex *tunnels_lock;
27 static GHashTable *tunnels;
37 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
38 #define GST_CAT_DEFAULT rtsp_client_debug
40 static void gst_rtsp_client_get_property (GObject * object, guint propid,
41 GValue * value, GParamSpec * pspec);
42 static void gst_rtsp_client_set_property (GObject * object, guint propid,
43 const GValue * value, GParamSpec * pspec);
44 static void gst_rtsp_client_finalize (GObject * obj);
46 static void client_session_finalized (GstRTSPClient * client,
47 GstRTSPSession * session);
49 static void unlink_streams (GstRTSPClient * client);
51 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
54 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
56 GObjectClass *gobject_class;
58 gobject_class = G_OBJECT_CLASS (klass);
60 gobject_class->get_property = gst_rtsp_client_get_property;
61 gobject_class->set_property = gst_rtsp_client_set_property;
62 gobject_class->finalize = gst_rtsp_client_finalize;
64 g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
65 g_param_spec_object ("session-pool", "Session Pool",
66 "The session pool to use for client session",
67 GST_TYPE_RTSP_SESSION_POOL,
68 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
70 g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
71 g_param_spec_object ("media-mapping", "Media Mapping",
72 "The media mapping to use for client session",
73 GST_TYPE_RTSP_MEDIA_MAPPING,
74 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
77 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
78 tunnels_lock = g_mutex_new ();
80 GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
84 gst_rtsp_client_init (GstRTSPClient * client)
88 /* A client is finalized when the connection is broken */
90 gst_rtsp_client_finalize (GObject * obj)
92 GstRTSPClient *client = GST_RTSP_CLIENT (obj);
95 GST_INFO ("finalize client %p", client);
97 /* remove weak-ref from sessions */
98 for (walk = client->sessions; walk; walk = g_list_next (walk)) {
99 GstRTSPSession *msession = (GstRTSPSession *) walk->data;
100 g_object_weak_unref (G_OBJECT (msession),
101 (GWeakNotify) client_session_finalized, client);
104 unlink_streams (client);
106 g_list_free (client->sessions);
108 gst_rtsp_connection_free (client->connection);
109 if (client->session_pool)
110 g_object_unref (client->session_pool);
111 if (client->media_mapping)
112 g_object_unref (client->media_mapping);
115 gst_rtsp_url_free (client->uri);
117 g_object_unref (client->media);
119 G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
123 gst_rtsp_client_get_property (GObject * object, guint propid,
124 GValue * value, GParamSpec * pspec)
126 GstRTSPClient *client = GST_RTSP_CLIENT (object);
129 case PROP_SESSION_POOL:
130 g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
132 case PROP_MEDIA_MAPPING:
133 g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
136 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
141 gst_rtsp_client_set_property (GObject * object, guint propid,
142 const GValue * value, GParamSpec * pspec)
144 GstRTSPClient *client = GST_RTSP_CLIENT (object);
147 case PROP_SESSION_POOL:
148 gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
150 case PROP_MEDIA_MAPPING:
151 gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
159 * gst_rtsp_client_new:
161 * Create a new #GstRTSPClient instance.
164 gst_rtsp_client_new (void)
166 GstRTSPClient *result;
168 result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
174 send_response (GstRTSPClient * client, GstRTSPSession * session,
175 GstRTSPMessage * response)
177 gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER,
178 "GStreamer RTSP server");
180 /* remove any previous header */
181 gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
183 /* add the new session header for new session ids */
187 if (session->timeout != 60)
189 g_strdup_printf ("%s; timeout=%d", session->sessionid,
192 str = g_strdup (session->sessionid);
194 gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
197 if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
198 gst_rtsp_message_dump (response);
201 gst_rtsp_watch_send_message (client->watch, response, NULL);
202 gst_rtsp_message_unset (response);
206 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
207 GstRTSPMessage * request)
209 GstRTSPMessage response = { 0 };
211 gst_rtsp_message_init_response (&response, code,
212 gst_rtsp_status_as_text (code), request);
214 send_response (client, NULL, &response);
218 compare_uri (const GstRTSPUrl * uri1, const GstRTSPUrl * uri2)
220 if (uri1 == NULL || uri2 == NULL)
223 if (strcmp (uri1->abspath, uri2->abspath))
229 /* this function is called to initially find the media for the DESCRIBE request
230 * but is cached for when the same client (without breaking the connection) is
231 * doing a setup for the exact same url. */
232 static GstRTSPMedia *
233 find_media (GstRTSPClient * client, GstRTSPUrl * uri, GstRTSPMessage * request)
235 GstRTSPMediaFactory *factory;
238 if (!compare_uri (client->uri, uri)) {
239 /* remove any previously cached values before we try to construct a new
242 gst_rtsp_url_free (client->uri);
245 g_object_unref (client->media);
246 client->media = NULL;
248 if (!client->media_mapping)
251 /* find the factory for the uri first */
253 gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
256 /* prepare the media and add it to the pipeline */
257 if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
260 /* prepare the media */
261 if (!(gst_rtsp_media_prepare (media)))
264 /* now keep track of the uri and the media */
265 client->uri = gst_rtsp_url_copy (uri);
266 client->media = media;
268 /* we have seen this uri before, used cached media */
269 media = client->media;
270 GST_INFO ("reusing cached media %p", media);
274 g_object_ref (media);
281 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
286 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
291 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
292 g_object_unref (factory);
297 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
298 g_object_unref (media);
299 g_object_unref (factory);
305 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
307 GstRTSPMessage message = { 0 };
311 gst_rtsp_message_init_data (&message, channel);
313 data = GST_BUFFER_DATA (buffer);
314 size = GST_BUFFER_SIZE (buffer);
315 gst_rtsp_message_take_body (&message, data, size);
317 gst_rtsp_watch_send_message (client->watch, &message, NULL);
319 gst_rtsp_message_steal_body (&message, &data, &size);
320 gst_rtsp_message_unset (&message);
326 link_stream (GstRTSPClient * client, GstRTSPSessionStream * stream)
328 gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
329 (GstRTSPSendFunc) do_send_data, client, NULL);
330 client->streams = g_list_prepend (client->streams, stream);
334 unlink_stream (GstRTSPClient * client, GstRTSPSessionStream * stream)
336 gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
337 client->streams = g_list_remove (client->streams, stream);
341 unlink_streams (GstRTSPClient * client)
345 for (walk = client->streams; walk; walk = g_list_next (walk)) {
346 GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
348 gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
350 g_list_free (client->streams);
351 client->streams = NULL;
355 unlink_session_streams (GstRTSPClient * client, GstRTSPSessionMedia * media)
359 n_streams = gst_rtsp_media_n_streams (media->media);
360 for (i = 0; i < n_streams; i++) {
361 GstRTSPSessionStream *sstream;
362 GstRTSPTransport *tr;
364 /* get the stream as configured in the session */
365 sstream = gst_rtsp_session_media_get_stream (media, i);
366 /* get the transport, if there is no transport configured, skip this stream */
367 if (!(tr = sstream->trans.transport))
370 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
371 /* for TCP, unlink the stream from the TCP connection of the client */
372 unlink_stream (client, sstream);
378 handle_teardown_request (GstRTSPClient * client, GstRTSPUrl * uri,
379 GstRTSPSession * session, GstRTSPMessage * request)
381 GstRTSPSessionMedia *media;
382 GstRTSPMessage response = { 0 };
383 GstRTSPStatusCode code;
388 /* get a handle to the configuration of the media in the session */
389 media = gst_rtsp_session_get_media (session, uri);
393 /* unlink the all TCP callbacks */
394 unlink_session_streams (client, media);
396 /* remove the session from the watched sessions */
397 g_object_weak_unref (G_OBJECT (session),
398 (GWeakNotify) client_session_finalized, client);
399 client->sessions = g_list_remove (client->sessions, session);
401 gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
403 /* unmanage the media in the session, returns false if all media session
405 if (!gst_rtsp_session_release_media (session, media)) {
406 /* remove the session */
407 gst_rtsp_session_pool_remove (client->session_pool, session);
409 /* construct the response now */
410 code = GST_RTSP_STS_OK;
411 gst_rtsp_message_init_response (&response, code,
412 gst_rtsp_status_as_text (code), request);
414 send_response (client, session, &response);
421 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
426 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
432 handle_get_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
433 GstRTSPSession * session, GstRTSPMessage * request)
439 res = gst_rtsp_message_get_body (request, &data, &size);
440 if (res != GST_RTSP_OK)
444 /* no body, keep-alive request */
445 send_generic_response (client, GST_RTSP_STS_OK, request);
447 /* there is a body */
448 GstRTSPMessage response = { 0 };
450 /* there is a body, handle the params */
451 res = gst_rtsp_params_get (client, uri, session, request, &response);
452 if (res != GST_RTSP_OK)
455 send_response (client, session, &response);
462 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
468 handle_set_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
469 GstRTSPSession * session, GstRTSPMessage * request)
475 res = gst_rtsp_message_get_body (request, &data, &size);
476 if (res != GST_RTSP_OK)
480 /* no body, keep-alive request */
481 send_generic_response (client, GST_RTSP_STS_OK, request);
483 GstRTSPMessage response = { 0 };
485 /* there is a body, handle the params */
486 res = gst_rtsp_params_set (client, uri, session, request, &response);
487 if (res != GST_RTSP_OK)
490 send_response (client, session, &response);
497 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
503 handle_pause_request (GstRTSPClient * client, GstRTSPUrl * uri,
504 GstRTSPSession * session, GstRTSPMessage * request)
506 GstRTSPSessionMedia *media;
507 GstRTSPMessage response = { 0 };
508 GstRTSPStatusCode code;
513 /* get a handle to the configuration of the media in the session */
514 media = gst_rtsp_session_get_media (session, uri);
518 /* the session state must be playing or recording */
519 if (media->state != GST_RTSP_STATE_PLAYING &&
520 media->state != GST_RTSP_STATE_RECORDING)
523 /* unlink the all TCP callbacks */
524 unlink_session_streams (client, media);
526 /* then pause sending */
527 gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
529 /* construct the response now */
530 code = GST_RTSP_STS_OK;
531 gst_rtsp_message_init_response (&response, code,
532 gst_rtsp_status_as_text (code), request);
534 send_response (client, session, &response);
536 /* the state is now READY */
537 media->state = GST_RTSP_STATE_READY;
544 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
549 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
554 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
561 handle_play_request (GstRTSPClient * client, GstRTSPUrl * uri,
562 GstRTSPSession * session, GstRTSPMessage * request)
564 GstRTSPSessionMedia *media;
565 GstRTSPMessage response = { 0 };
566 GstRTSPStatusCode code;
568 guint n_streams, i, infocount;
569 guint timestamp, seqnum;
571 GstRTSPTimeRange *range;
577 /* get a handle to the configuration of the media in the session */
578 media = gst_rtsp_session_get_media (session, uri);
582 /* the session state must be playing or ready */
583 if (media->state != GST_RTSP_STATE_PLAYING &&
584 media->state != GST_RTSP_STATE_READY)
587 /* parse the range header if we have one */
588 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_RANGE, &str, 0);
589 if (res == GST_RTSP_OK) {
590 if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
591 /* we have a range, seek to the position */
592 gst_rtsp_media_seek (media->media, range);
593 gst_rtsp_range_free (range);
597 /* grab RTPInfo from the payloaders now */
598 rtpinfo = g_string_new ("");
600 n_streams = gst_rtsp_media_n_streams (media->media);
601 for (i = 0, infocount = 0; i < n_streams; i++) {
602 GstRTSPSessionStream *sstream;
603 GstRTSPMediaStream *stream;
604 GstRTSPTransport *tr;
605 GObjectClass *payobjclass;
608 /* get the stream as configured in the session */
609 sstream = gst_rtsp_session_media_get_stream (media, i);
610 /* get the transport, if there is no transport configured, skip this stream */
611 if (!(tr = sstream->trans.transport)) {
612 GST_INFO ("stream %d is not configured", i);
616 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
617 /* for TCP, link the stream to the TCP connection of the client */
618 link_stream (client, sstream);
621 stream = sstream->media_stream;
623 payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
625 if (g_object_class_find_property (payobjclass, "seqnum") &&
626 g_object_class_find_property (payobjclass, "timestamp")) {
629 payobj = G_OBJECT (stream->payloader);
631 /* only add RTP-Info for streams with seqnum and timestamp */
632 g_object_get (payobj, "seqnum", &seqnum, "timestamp", ×tamp, NULL);
635 g_string_append (rtpinfo, ", ");
637 uristr = gst_rtsp_url_get_request_uri (uri);
638 g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
639 uristr, i, seqnum, timestamp);
644 GST_WARNING ("RTP-Info cannot be determined for stream %d", i);
648 /* construct the response now */
649 code = GST_RTSP_STS_OK;
650 gst_rtsp_message_init_response (&response, code,
651 gst_rtsp_status_as_text (code), request);
653 /* add the RTP-Info header */
655 str = g_string_free (rtpinfo, FALSE);
656 gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
658 g_string_free (rtpinfo, TRUE);
662 str = gst_rtsp_range_to_string (&media->media->range);
663 gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
665 send_response (client, session, &response);
667 /* start playing after sending the request */
668 gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
670 media->state = GST_RTSP_STATE_PLAYING;
677 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
682 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
687 send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
694 do_keepalive (GstRTSPSession * session)
696 GST_INFO ("keep session %p alive", session);
697 gst_rtsp_session_touch (session);
701 handle_setup_request (GstRTSPClient * client, GstRTSPUrl * uri,
702 GstRTSPSession * session, GstRTSPMessage * request)
707 gboolean have_transport;
708 GstRTSPTransport *ct, *st;
710 GstRTSPLowerTrans supported;
711 GstRTSPMessage response = { 0 };
712 GstRTSPStatusCode code;
713 GstRTSPSessionStream *stream;
714 gchar *trans_str, *pos;
716 GstRTSPSessionMedia *media;
717 gboolean need_session;
720 /* the uri contains the stream number we added in the SDP config, which is
721 * always /stream=%d so we need to strip that off
722 * parse the stream we need to configure, look for the stream in the abspath
723 * first and then in the query. */
724 if (uri->abspath == NULL || !(pos = strstr (uri->abspath, "/stream="))) {
725 if (uri->query == NULL || !(pos = strstr (uri->query, "/stream=")))
729 /* we can mofify the parse uri in place */
732 pos += strlen ("/stream=");
733 if (sscanf (pos, "%u", &streamid) != 1)
736 /* parse the transport */
738 gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport,
740 if (res != GST_RTSP_OK)
743 transports = g_strsplit (transport, ",", 0);
744 gst_rtsp_transport_new (&ct);
746 /* init transports */
747 have_transport = FALSE;
748 gst_rtsp_transport_init (ct);
750 /* our supported transports */
751 supported = GST_RTSP_LOWER_TRANS_UDP |
752 GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP;
754 /* loop through the transports, try to parse */
755 for (i = 0; transports[i]; i++) {
756 res = gst_rtsp_transport_parse (transports[i], ct);
757 if (res != GST_RTSP_OK) {
758 /* no valid transport, search some more */
759 GST_WARNING ("could not parse transport %s", transports[i]);
763 /* we have a transport, see if it's RTP/AVP */
764 if (ct->trans != GST_RTSP_TRANS_RTP ||
765 ct->profile != GST_RTSP_PROFILE_AVP) {
766 GST_WARNING ("invalid transport %s", transports[i]);
770 if (!(ct->lower_transport & supported)) {
771 GST_WARNING ("unsupported transport %s", transports[i]);
775 /* we have a valid transport */
776 GST_INFO ("found valid transport %s", transports[i]);
777 have_transport = TRUE;
781 gst_rtsp_transport_init (ct);
783 g_strfreev (transports);
785 /* we have not found anything usable, error out */
787 goto unsupported_transports;
789 if (client->session_pool == NULL)
792 /* we have a valid transport now, set the destination of the client. */
793 g_free (ct->destination);
794 if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
795 ct->destination = g_strdup ("224.2.0.1");
797 url = gst_rtsp_connection_get_url (client->connection);
798 ct->destination = g_strdup (url->host);
802 g_object_ref (session);
803 /* get a handle to the configuration of the media in the session, this can
804 * return NULL if this is a new url to manage in this session. */
805 media = gst_rtsp_session_get_media (session, uri);
807 need_session = FALSE;
809 /* create a session if this fails we probably reached our session limit or
811 if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
812 goto service_unavailable;
814 /* we need a new media configuration in this session */
820 /* we have no media, find one and manage it */
824 /* get a handle to the configuration of the media in the session */
825 if ((m = find_media (client, uri, request))) {
826 /* manage the media in our session now */
827 media = gst_rtsp_session_manage_media (session, uri, m);
831 /* if we stil have no media, error */
835 /* fix the transports */
836 if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
837 /* check if the client selected channels for TCP */
838 if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
839 gst_rtsp_session_media_alloc_channels (media, &ct->interleaved);
843 /* get a handle to the stream in the media */
844 if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
847 st = gst_rtsp_session_stream_set_transport (stream, ct);
849 /* configure keepalive for this transport */
850 gst_rtsp_session_stream_set_keepalive (stream,
851 (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
853 /* serialize the server transport */
854 trans_str = gst_rtsp_transport_as_text (st);
855 gst_rtsp_transport_free (st);
857 /* construct the response now */
858 code = GST_RTSP_STS_OK;
859 gst_rtsp_message_init_response (&response, code,
860 gst_rtsp_status_as_text (code), request);
862 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
865 send_response (client, session, &response);
867 /* update the state */
868 switch (media->state) {
869 case GST_RTSP_STATE_PLAYING:
870 case GST_RTSP_STATE_RECORDING:
871 case GST_RTSP_STATE_READY:
872 /* no state change */
875 media->state = GST_RTSP_STATE_READY;
878 g_object_unref (session);
885 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
890 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
891 g_object_unref (session);
896 send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
897 g_object_unref (media);
898 g_object_unref (session);
903 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
906 unsupported_transports:
908 send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
909 gst_rtsp_transport_free (ct);
914 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
919 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
924 /* for the describe we must generate an SDP */
926 handle_describe_request (GstRTSPClient * client, GstRTSPUrl * uri,
927 GstRTSPSession * session, GstRTSPMessage * request)
929 GstRTSPMessage response = { 0 };
936 /* check what kind of format is accepted, we don't really do anything with it
937 * and always return SDP for now. */
942 gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
943 if (res == GST_RTSP_ENOTIMPL)
946 if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
950 /* find the media object for the uri */
951 if (!(media = find_media (client, uri, request)))
954 /* create an SDP for the media object */
955 if (!(sdp = gst_rtsp_sdp_from_media (media)))
958 g_object_unref (media);
960 gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
961 gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
963 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE,
966 /* content base for some clients that might screw up creating the setup uri */
967 str = g_strdup_printf ("rtsp://%s:%u%s/", uri->host, uri->port, uri->abspath);
968 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE, str);
971 /* add SDP to the response body */
972 str = gst_sdp_message_as_text (sdp);
973 gst_rtsp_message_take_body (&response, (guint8 *) str, strlen (str));
974 gst_sdp_message_free (sdp);
976 send_response (client, session, &response);
983 /* error reply is already sent */
988 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
989 g_object_unref (media);
995 handle_options_request (GstRTSPClient * client, GstRTSPUrl * uri,
996 GstRTSPSession * session, GstRTSPMessage * request)
998 GstRTSPMessage response = { 0 };
999 GstRTSPMethod options;
1002 options = GST_RTSP_DESCRIBE |
1007 GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
1009 str = gst_rtsp_options_as_text (options);
1011 gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
1012 gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
1014 gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
1017 send_response (client, session, &response);
1022 /* remove duplicate and trailing '/' */
1024 santize_uri (GstRTSPUrl * uri)
1028 gboolean have_slash, prev_slash;
1030 s = d = uri->abspath;
1031 len = strlen (uri->abspath);
1035 for (i = 0; i < len; i++) {
1036 have_slash = s[i] == '/';
1038 if (!have_slash || !prev_slash)
1040 prev_slash = have_slash;
1042 len = d - uri->abspath;
1043 /* don't remove the first slash if that's the only thing left */
1044 if (len > 1 && *(d - 1) == '/')
1050 client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
1052 if (!(client->sessions = g_list_remove (client->sessions, session))) {
1053 GST_INFO ("all sessions finalized, close the connection");
1054 g_source_destroy ((GSource *) client->watch);
1059 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
1063 for (walk = client->sessions; walk; walk = g_list_next (walk)) {
1064 GstRTSPSession *msession = (GstRTSPSession *) walk->data;
1066 /* we already know about this session */
1067 if (msession == session)
1071 GST_INFO ("watching session %p", session);
1073 g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized,
1075 client->sessions = g_list_prepend (client->sessions, session);
1079 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
1081 GstRTSPMethod method;
1082 const gchar *uristr;
1084 GstRTSPVersion version;
1086 GstRTSPSession *session;
1089 if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
1090 gst_rtsp_message_dump (request);
1093 GST_INFO ("client %p: received a request", client);
1095 gst_rtsp_message_parse_request (request, &method, &uristr, &version);
1097 if (version != GST_RTSP_VERSION_1_0) {
1098 /* we can only handle 1.0 requests */
1099 send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
1104 /* we always try to parse the url first */
1105 if ((res = gst_rtsp_url_parse (uristr, &uri)) != GST_RTSP_OK) {
1106 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1110 /* sanitize the uri */
1113 /* get the session if there is any */
1114 res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
1115 if (res == GST_RTSP_OK) {
1116 if (client->session_pool == NULL)
1119 /* we had a session in the request, find it again */
1120 if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
1121 goto session_not_found;
1123 /* we add the session to the client list of watched sessions. When a session
1124 * disappears because it times out, we will be notified. If all sessions are
1125 * gone, we will close the connection */
1126 client_watch_session (client, session);
1130 /* now see what is asked and dispatch to a dedicated handler */
1132 case GST_RTSP_OPTIONS:
1133 handle_options_request (client, uri, session, request);
1135 case GST_RTSP_DESCRIBE:
1136 handle_describe_request (client, uri, session, request);
1138 case GST_RTSP_SETUP:
1139 handle_setup_request (client, uri, session, request);
1142 handle_play_request (client, uri, session, request);
1144 case GST_RTSP_PAUSE:
1145 handle_pause_request (client, uri, session, request);
1147 case GST_RTSP_TEARDOWN:
1148 handle_teardown_request (client, uri, session, request);
1150 case GST_RTSP_SET_PARAMETER:
1151 handle_set_param_request (client, uri, session, request);
1153 case GST_RTSP_GET_PARAMETER:
1154 handle_get_param_request (client, uri, session, request);
1156 case GST_RTSP_ANNOUNCE:
1157 case GST_RTSP_RECORD:
1158 case GST_RTSP_REDIRECT:
1159 send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
1161 case GST_RTSP_INVALID:
1163 send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1167 g_object_unref (session);
1169 gst_rtsp_url_free (uri);
1175 send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1180 send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
1186 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
1196 /* find the stream for this message */
1197 res = gst_rtsp_message_parse_data (message, &channel);
1198 if (res != GST_RTSP_OK)
1201 gst_rtsp_message_steal_body (message, &data, &size);
1203 buffer = gst_buffer_new ();
1204 GST_BUFFER_DATA (buffer) = data;
1205 GST_BUFFER_MALLOCDATA (buffer) = data;
1206 GST_BUFFER_SIZE (buffer) = size;
1209 for (walk = client->streams; walk; walk = g_list_next (walk)) {
1210 GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
1211 GstRTSPMediaStream *mstream;
1212 GstRTSPTransport *tr;
1214 /* get the transport, if there is no transport configured, skip this stream */
1215 if (!(tr = stream->trans.transport))
1218 /* we also need a media stream */
1219 if (!(mstream = stream->media_stream))
1222 /* check for TCP transport */
1223 if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1224 /* dispatch to the stream based on the channel number */
1225 if (tr->interleaved.min == channel) {
1226 gst_rtsp_media_stream_rtp (mstream, buffer);
1229 } else if (tr->interleaved.max == channel) {
1230 gst_rtsp_media_stream_rtcp (mstream, buffer);
1237 gst_buffer_unref (buffer);
1241 * gst_rtsp_client_set_session_pool:
1242 * @client: a #GstRTSPClient
1243 * @pool: a #GstRTSPSessionPool
1245 * Set @pool as the sessionpool for @client which it will use to find
1246 * or allocate sessions. the sessionpool is usually inherited from the server
1247 * that created the client but can be overridden later.
1250 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
1251 GstRTSPSessionPool * pool)
1253 GstRTSPSessionPool *old;
1255 old = client->session_pool;
1258 g_object_ref (pool);
1259 client->session_pool = pool;
1261 g_object_unref (old);
1266 * gst_rtsp_client_get_session_pool:
1267 * @client: a #GstRTSPClient
1269 * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1271 * Returns: a #GstRTSPSessionPool, unref after usage.
1273 GstRTSPSessionPool *
1274 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
1276 GstRTSPSessionPool *result;
1278 if ((result = client->session_pool))
1279 g_object_ref (result);
1285 * gst_rtsp_client_set_media_mapping:
1286 * @client: a #GstRTSPClient
1287 * @mapping: a #GstRTSPMediaMapping
1289 * Set @mapping as the media mapping for @client which it will use to map urls
1290 * to media streams. These mapping is usually inherited from the server that
1291 * created the client but can be overriden later.
1294 gst_rtsp_client_set_media_mapping (GstRTSPClient * client,
1295 GstRTSPMediaMapping * mapping)
1297 GstRTSPMediaMapping *old;
1299 old = client->media_mapping;
1301 if (old != mapping) {
1303 g_object_ref (mapping);
1304 client->media_mapping = mapping;
1306 g_object_unref (old);
1311 * gst_rtsp_client_get_media_mapping:
1312 * @client: a #GstRTSPClient
1314 * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1316 * Returns: a #GstRTSPMediaMapping, unref after usage.
1318 GstRTSPMediaMapping *
1319 gst_rtsp_client_get_media_mapping (GstRTSPClient * client)
1321 GstRTSPMediaMapping *result;
1323 if ((result = client->media_mapping))
1324 g_object_ref (result);
1329 static GstRTSPResult
1330 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
1333 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1335 switch (message->type) {
1336 case GST_RTSP_MESSAGE_REQUEST:
1337 handle_request (client, message);
1339 case GST_RTSP_MESSAGE_RESPONSE:
1341 case GST_RTSP_MESSAGE_DATA:
1342 handle_data (client, message);
1350 static GstRTSPResult
1351 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
1353 GstRTSPClient *client;
1355 client = GST_RTSP_CLIENT (user_data);
1357 /* GST_INFO ("client %p: sent a message with cseq %d", client, cseq); */
1362 static GstRTSPResult
1363 closed (GstRTSPWatch * watch, gpointer user_data)
1365 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1366 const gchar *tunnelid;
1368 GST_INFO ("client %p: connection closed", client);
1370 if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1371 g_mutex_lock (tunnels_lock);
1372 g_hash_table_remove (tunnels, tunnelid);
1373 g_mutex_unlock (tunnels_lock);
1376 /* remove all streams that are streaming over this client connection */
1377 unlink_streams (client);
1382 static GstRTSPResult
1383 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
1385 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1388 str = gst_rtsp_strresult (result);
1389 GST_INFO ("client %p: received an error %s", client, str);
1395 static GstRTSPResult
1396 error_full (GstRTSPWatch *watch, GstRTSPResult result,
1397 GstRTSPMessage *message, guint id, gpointer user_data)
1399 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1402 str = gst_rtsp_strresult (result);
1403 GST_INFO ("client %p: received an error %s when handling message %p with id %d",
1404 client, str, message, id);
1410 static GstRTSPStatusCode
1411 tunnel_start (GstRTSPWatch * watch, gpointer user_data)
1413 GstRTSPClient *client;
1414 const gchar *tunnelid;
1416 client = GST_RTSP_CLIENT (user_data);
1418 GST_INFO ("client %p: tunnel start", client);
1420 /* store client in the pending tunnels */
1421 tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1422 if (tunnelid == NULL)
1425 GST_INFO ("client %p: inserting %s", client, tunnelid);
1427 /* we can't have two clients connecting with the same tunnelid */
1428 g_mutex_lock (tunnels_lock);
1429 if (g_hash_table_lookup (tunnels, tunnelid))
1430 goto tunnel_existed;
1432 g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1433 g_mutex_unlock (tunnels_lock);
1435 return GST_RTSP_STS_OK;
1440 GST_INFO ("client %p: no tunnelid provided", client);
1441 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1445 g_mutex_unlock (tunnels_lock);
1446 GST_INFO ("client %p: tunnel session %s existed", client, tunnelid);
1447 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1451 static GstRTSPResult
1452 tunnel_complete (GstRTSPWatch * watch, gpointer user_data)
1454 const gchar *tunnelid;
1455 GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1456 GstRTSPClient *oclient;
1458 GST_INFO ("client %p: tunnel complete", client);
1460 /* find previous tunnel */
1461 tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1462 if (tunnelid == NULL)
1465 g_mutex_lock (tunnels_lock);
1466 if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1469 /* remove the old client from the table. ref before because removing it will
1470 * remove the ref to it. */
1471 g_object_ref (oclient);
1472 g_hash_table_remove (tunnels, tunnelid);
1473 g_mutex_unlock (tunnels_lock);
1475 GST_INFO ("client %p: found tunnel %p", client, oclient);
1477 /* merge the tunnels into the first client */
1478 gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1479 gst_rtsp_watch_reset (oclient->watch);
1480 g_object_unref (oclient);
1482 /* we don't need this watch anymore */
1483 g_source_destroy ((GSource *) client->watch);
1484 client->watchid = 0;
1491 GST_INFO ("client %p: no tunnelid provided", client);
1492 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1496 g_mutex_unlock (tunnels_lock);
1497 GST_INFO ("client %p: tunnel session %s not found", client, tunnelid);
1498 return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1502 static GstRTSPWatchFuncs watch_funcs = {
1513 * gst_rtsp_client_attach:
1514 * @client: a #GstRTSPClient
1515 * @channel: a #GIOChannel
1517 * Accept a new connection for @client on the socket in @channel.
1519 * This function should be called when the client properties and urls are fully
1520 * configured and the client is ready to start.
1522 * Returns: %TRUE if the client could be accepted.
1525 gst_rtsp_client_accept (GstRTSPClient * client, GIOChannel * channel)
1528 GstRTSPConnection *conn;
1531 GMainContext *context;
1534 /* a new client connected. */
1535 sock = g_io_channel_unix_get_fd (channel);
1537 GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1539 url = gst_rtsp_connection_get_url (conn);
1540 GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
1542 client->connection = conn;
1544 /* create watch for the connection and attach */
1545 client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1546 g_object_ref (client), g_object_unref);
1548 /* find the context to add the watch */
1549 if ((source = g_main_current_source ()))
1550 context = g_source_get_context (source);
1554 GST_INFO ("attaching to context %p", context);
1556 client->watchid = gst_rtsp_watch_attach (client->watch, context);
1557 gst_rtsp_watch_unref (client->watch);
1564 gchar *str = gst_rtsp_strresult (res);
1566 GST_ERROR ("Could not accept client on server socket %d: %s", sock, str);