f0c0eb7848e530a692ddc3ede28db89ea9c115a5
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-client.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #include <sys/ioctl.h>
21
22 #include "rtsp-client.h"
23 #include "rtsp-sdp.h"
24
25 #undef DEBUG
26
27 #define DEFAULT_TIMEOUT  60
28
29 enum
30 {
31   PROP_0,
32   PROP_TIMEOUT,
33   PROP_SESSION_POOL,
34   PROP_MEDIA_MAPPING,
35   PROP_LAST
36 };
37
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);
43
44 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
45
46 static void
47 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
48 {
49   GObjectClass *gobject_class;
50
51   gobject_class = G_OBJECT_CLASS (klass);
52
53   gobject_class->get_property = gst_rtsp_client_get_property;
54   gobject_class->set_property = gst_rtsp_client_set_property;
55   gobject_class->finalize = gst_rtsp_client_finalize;
56
57   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
58       g_param_spec_uint ("timeout", "Timeout", "The client timeout",
59           0, G_MAXUINT, DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
60
61   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
62       g_param_spec_object ("session-pool", "Session Pool",
63           "The session pool to use for client session",
64           GST_TYPE_RTSP_SESSION_POOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
65
66   g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
67       g_param_spec_object ("media-mapping", "Media Mapping",
68           "The media mapping to use for client session",
69           GST_TYPE_RTSP_MEDIA_MAPPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
70 }
71
72 static void
73 gst_rtsp_client_init (GstRTSPClient * client)
74 {
75   client->timeout = DEFAULT_TIMEOUT;
76 }
77
78 /* A client is finalized when the connection is broken */
79 static void
80 gst_rtsp_client_finalize (GObject * obj)
81 {
82   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
83
84   g_message ("finalize client %p", client);
85
86   gst_rtsp_connection_free (client->connection);
87   if (client->session_pool)
88     g_object_unref (client->session_pool);
89   if (client->media_mapping)
90     g_object_unref (client->media_mapping);
91
92   if (client->uri)
93     gst_rtsp_url_free (client->uri);
94   if (client->media)
95     g_object_unref (client->media);
96
97   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
98 }
99
100 static void
101 gst_rtsp_client_get_property (GObject *object, guint propid,
102     GValue *value, GParamSpec *pspec)
103 {
104   GstRTSPClient *client = GST_RTSP_CLIENT (object);
105
106   switch (propid) {
107     case PROP_TIMEOUT:
108       g_value_set_uint (value, gst_rtsp_client_get_timeout (client));
109       break;
110     case PROP_SESSION_POOL:
111       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
112       break;
113     case PROP_MEDIA_MAPPING:
114       g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
115       break;
116     default:
117       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
118   }
119 }
120
121 static void
122 gst_rtsp_client_set_property (GObject *object, guint propid,
123     const GValue *value, GParamSpec *pspec)
124 {
125   GstRTSPClient *client = GST_RTSP_CLIENT (object);
126
127   switch (propid) {
128     case PROP_TIMEOUT:
129       gst_rtsp_client_set_timeout (client, g_value_get_uint (value));
130       break;
131     case PROP_SESSION_POOL:
132       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
133       break;
134     case PROP_MEDIA_MAPPING:
135       gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
136       break;
137     default:
138       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
139   }
140 }
141
142 /**
143  * gst_rtsp_client_new:
144  *
145  * Create a new #GstRTSPClient instance.
146  */
147 GstRTSPClient *
148 gst_rtsp_client_new (void)
149 {
150   GstRTSPClient *result;
151
152   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
153
154   return result;
155 }
156
157 static void
158 send_response (GstRTSPClient *client, GstRTSPSession *session, GstRTSPMessage *response)
159 {
160   GTimeVal timeout;
161
162   gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER, "GStreamer RTSP server");
163
164 #ifdef DEBUG
165   gst_rtsp_message_dump (response);
166 #endif
167
168   timeout.tv_sec = client->timeout;
169   timeout.tv_usec = 0;
170
171   /* add the new session header for new session ids */
172   if (session) {
173     gchar *str;
174
175     if (session->timeout != 60)
176       str = g_strdup_printf ("%s; timeout=%d", session->sessionid, session->timeout);
177     else
178       str = g_strdup (session->sessionid);
179
180     gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
181   }
182   else {
183     /* remove the session id from the response */
184     gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
185   }
186
187   gst_rtsp_connection_send (client->connection, response, &timeout);
188   gst_rtsp_message_unset (response);
189 }
190
191 static void
192 send_generic_response (GstRTSPClient *client, GstRTSPStatusCode code, 
193     GstRTSPMessage *request)
194 {
195   GstRTSPMessage response = { 0 };
196
197   gst_rtsp_message_init_response (&response, code, 
198         gst_rtsp_status_as_text (code), request);
199
200   send_response (client, NULL, &response);
201 }
202
203 static gboolean
204 compare_uri (const GstRTSPUrl *uri1, const GstRTSPUrl *uri2)
205 {
206   if (uri1 == NULL || uri2 == NULL)
207     return FALSE;
208
209   if (strcmp (uri1->abspath, uri2->abspath))
210     return FALSE;
211
212   return TRUE;
213 }
214
215 /* this function is called to initially find the media for the DESCRIBE request
216  * but is cached for when the same client (without breaking the connection) is
217  * doing a setup for the exact same url. */
218 static GstRTSPMedia *
219 find_media (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
220 {
221   GstRTSPMediaFactory *factory;
222   GstRTSPMedia *media;
223
224   if (!compare_uri (client->uri, uri)) {
225     /* remove any previously cached values before we try to construct a new
226      * media for uri */
227     if (client->uri)
228       gst_rtsp_url_free (client->uri);
229     client->uri = NULL;
230     if (client->media)
231       g_object_unref (client->media);
232     client->media = NULL;
233
234     if (!client->media_mapping)
235       goto no_mapping;
236
237     /* find the factory for the uri first */
238     if (!(factory = gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
239       goto no_factory;
240
241     /* prepare the media and add it to the pipeline */
242     if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
243       goto no_media;
244
245     /* prepare the media */
246     if (!(gst_rtsp_media_prepare (media)))
247       goto no_prepare;
248
249     /* now keep track of the uri and the media */
250     client->uri = gst_rtsp_url_copy (uri);
251     client->media = media;
252   }
253   else {
254     /* we have seen this uri before, used cached media */
255     media = client->media;
256     g_message ("reusing cached media %p", media); 
257   }
258
259   if (media)
260     g_object_ref (media);
261
262   return media;
263
264   /* ERRORS */
265 no_mapping:
266   {
267     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
268     return NULL;
269   }
270 no_factory:
271   {
272     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
273     return NULL;
274   }
275 no_media:
276   {
277     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
278     g_object_unref (factory);
279     return NULL;
280   }
281 no_prepare:
282   {
283     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
284     g_object_unref (media);
285     g_object_unref (factory);
286     return NULL;
287   }
288 }
289
290 /* Get the session or NULL when there was no session */
291 static GstRTSPSession *
292 find_session (GstRTSPClient *client, GstRTSPMessage *request)
293 {
294   GstRTSPResult res;
295   GstRTSPSession *session;
296   gchar *sessid;
297
298   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
299   if (res == GST_RTSP_OK) {
300     if (client->session_pool == NULL)
301       goto no_pool;
302
303     /* we had a session in the request, find it again */
304     if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
305       goto session_not_found;
306
307     client->timeout = gst_rtsp_session_get_timeout (session);
308   }
309   else
310     goto service_unavailable;
311
312   return session;
313
314   /* ERRORS */
315 no_pool:
316   {
317     return NULL;
318   }
319 session_not_found:
320   {
321     return NULL;
322   }
323 service_unavailable:
324   {
325     return NULL;
326   }
327 }
328
329 static gboolean
330 handle_teardown_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
331 {
332   GstRTSPSessionMedia *media;
333   GstRTSPMessage response = { 0 };
334   GstRTSPStatusCode code;
335
336   if (!session)
337     goto no_session;
338
339   /* get a handle to the configuration of the media in the session */
340   media = gst_rtsp_session_get_media (session, uri);
341   if (!media)
342     goto not_found;
343
344   gst_rtsp_session_media_stop (media);
345
346   /* unmanage the media in the session, returns false if all media session
347    * are torn down. */
348   if (!gst_rtsp_session_release_media (session, media)) {
349     /* remove the session */
350     gst_rtsp_session_pool_remove (client->session_pool, session);
351   }
352   /* construct the response now */
353   code = GST_RTSP_STS_OK;
354   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
355
356   send_response (client, session, &response);
357
358   return FALSE;
359
360   /* ERRORS */
361 no_session:
362   {
363     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
364     return FALSE;
365   }
366 not_found:
367   {
368     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
369     return FALSE;
370   }
371 }
372
373 static gboolean
374 handle_pause_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
375 {
376   GstRTSPSessionMedia *media;
377   GstRTSPMessage response = { 0 };
378   GstRTSPStatusCode code;
379
380   if (!session)
381     goto no_session;
382
383   /* get a handle to the configuration of the media in the session */
384   media = gst_rtsp_session_get_media (session, uri);
385   if (!media)
386     goto not_found;
387
388   /* the session state must be playing or recording */
389   if (media->state != GST_RTSP_STATE_PLAYING &&
390       media->state != GST_RTSP_STATE_RECORDING)
391     goto invalid_state;
392
393   gst_rtsp_session_media_pause (media);
394
395   /* construct the response now */
396   code = GST_RTSP_STS_OK;
397   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
398
399   send_response (client, session, &response);
400
401   /* the state is now READY */
402   media->state = GST_RTSP_STATE_READY;
403
404   return FALSE;
405
406   /* ERRORS */
407 no_session:
408   {
409     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
410     return FALSE;
411   }
412 not_found:
413   {
414     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
415     return FALSE;
416   }
417 invalid_state:
418   {
419     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
420     return FALSE;
421   }
422 }
423
424 static gboolean
425 handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
426 {
427   GstRTSPSessionMedia *media;
428   GstRTSPMessage response = { 0 };
429   GstRTSPStatusCode code;
430   GString *rtpinfo;
431   guint n_streams, i;
432   guint timestamp, seqnum;
433   gchar *str;
434
435   if (!session)
436     goto no_session;
437
438   /* get a handle to the configuration of the media in the session */
439   media = gst_rtsp_session_get_media (session, uri);
440   if (!media)
441     goto not_found;
442
443   /* the session state must be playing or ready */
444   if (media->state != GST_RTSP_STATE_PLAYING &&
445       media->state != GST_RTSP_STATE_READY)
446     goto invalid_state;
447
448   /* grab RTPInfo from the payloaders now */
449   rtpinfo = g_string_new ("");
450
451   n_streams = gst_rtsp_media_n_streams (media->media);
452   for (i = 0; i < n_streams; i++) {
453     GstRTSPMediaStream *stream;
454     gchar *uristr;
455
456     stream = gst_rtsp_media_get_stream (media->media, i);
457
458     g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL);
459     g_object_get (G_OBJECT (stream->payloader), "timestamp", &timestamp, NULL);
460
461     if (i > 0)
462       g_string_append (rtpinfo, ", ");
463
464     uristr = gst_rtsp_url_get_request_uri (uri);
465     g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u", uristr, i, seqnum, timestamp);
466     g_free (uristr);
467   }
468
469   /* construct the response now */
470   code = GST_RTSP_STS_OK;
471   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
472
473   /* add the RTP-Info header */
474   str = g_string_free (rtpinfo, FALSE);
475   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
476
477   /* add the range */
478   str = gst_rtsp_range_to_string (&media->media->range);
479   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
480
481   send_response (client, session, &response);
482
483   /* start playing after sending the request */
484   gst_rtsp_session_media_play (media);
485
486   media->state = GST_RTSP_STATE_PLAYING;
487
488   return FALSE;
489
490   /* ERRORS */
491 no_session:
492   {
493     /* error was sent */
494     return FALSE;
495   }
496 not_found:
497   {
498     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
499     return FALSE;
500   }
501 invalid_state:
502   {
503     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
504     return FALSE;
505   }
506 }
507
508 static gboolean
509 handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
510 {
511   GstRTSPResult res;
512   gchar *transport;
513   gchar **transports;
514   gboolean have_transport;
515   GstRTSPTransport *ct, *st;
516   gint i;
517   GstRTSPLowerTrans supported;
518   GstRTSPMessage response = { 0 };
519   GstRTSPStatusCode code;
520   GstRTSPSessionStream *stream;
521   gchar *trans_str, *pos;
522   guint streamid;
523   GstRTSPSessionMedia *media;
524   gboolean need_session;
525
526   /* the uri contains the stream number we added in the SDP config, which is
527    * always /stream=%d so we need to strip that off 
528    * parse the stream we need to configure, look for the stream in the abspath
529    * first and then in the query. */
530   if (!(pos = strstr (uri->abspath, "/stream="))) {
531     if (!(pos = strstr (uri->query, "/stream=")))
532       goto bad_request;
533   }
534
535   /* we can mofify the parse uri in place */
536   *pos = '\0';
537
538   pos += strlen ("/stream=");
539   if (sscanf (pos, "%u", &streamid) != 1)
540     goto bad_request;
541
542   /* parse the transport */
543   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
544   if (res != GST_RTSP_OK)
545     goto no_transport;
546
547   transports = g_strsplit (transport, ",", 0);
548   gst_rtsp_transport_new (&ct);  
549
550   /* loop through the transports, try to parse */
551   have_transport = FALSE;
552   for (i = 0; transports[i]; i++) {
553
554     gst_rtsp_transport_init (ct);  
555     res = gst_rtsp_transport_parse (transports[i], ct);
556     if (res == GST_RTSP_OK) {
557       have_transport = TRUE;
558       break;
559     }
560   }
561   g_strfreev (transports);
562
563   /* we have not found anything usable, error out */
564   if (!have_transport) 
565     goto unsupported_transports;
566
567   /* we have a valid transport, check if we can handle it */
568   if (ct->trans != GST_RTSP_TRANS_RTP)
569     goto unsupported_transports;
570   if (ct->profile != GST_RTSP_PROFILE_AVP)
571     goto unsupported_transports;
572
573   supported = GST_RTSP_LOWER_TRANS_UDP |
574         GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP;
575   if (!(ct->lower_transport & supported))
576     goto unsupported_transports;
577
578   if (client->session_pool == NULL)
579     goto no_pool;
580
581   /* we have a valid transport now, set the destination of the client. */
582   g_free (ct->destination);
583   ct->destination = g_strdup (inet_ntoa (client->address.sin_addr));
584
585   if (session) {
586     g_object_ref (session);
587     /* get a handle to the configuration of the media in the session, this can
588      * return NULL if this is a new url to manage in this session. */
589     media = gst_rtsp_session_get_media (session, uri);
590
591     need_session = FALSE;
592   }
593   else {
594     /* create a session if this fails we probably reached our session limit or
595      * something. */
596     if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
597       goto service_unavailable;
598
599     /* we need a new media configuration in this session */
600     media = NULL;
601
602     need_session = TRUE;
603   }
604
605   /* we have no media, find one and manage it */
606   if (media == NULL) {
607     GstRTSPMedia *m;
608
609     /* get a handle to the configuration of the media in the session */
610     if ((m = find_media (client, uri, request))) {
611       /* manage the media in our session now */
612       media = gst_rtsp_session_manage_media (session, uri, m);
613     }
614   }
615
616   /* if we stil have no media, error */
617   if (media == NULL)
618     goto not_found;
619
620   /* get a handle to the stream in the media */
621   if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
622     goto no_stream;
623
624   /* setup the server transport from the client transport */
625   st = gst_rtsp_session_stream_set_transport (stream, ct);
626
627   /* serialize the server transport */
628   trans_str = gst_rtsp_transport_as_text (st);
629   gst_rtsp_transport_free (st);
630
631   /* construct the response now */
632   code = GST_RTSP_STS_OK;
633   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
634
635   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
636   g_free (trans_str);
637
638   send_response (client, session, &response);
639
640   /* update the state */
641   switch (media->state) {
642     case GST_RTSP_STATE_PLAYING:
643     case GST_RTSP_STATE_RECORDING:
644     case GST_RTSP_STATE_READY:
645       /* no state change */
646       break;
647     default:
648       media->state = GST_RTSP_STATE_READY;
649       break;
650   }
651   g_object_unref (session);
652
653   return TRUE;
654
655   /* ERRORS */
656 bad_request:
657   {
658     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
659     return FALSE;
660   }
661 not_found:
662   {
663     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
664     g_object_unref (session);
665     return FALSE;
666   }
667 no_stream:
668   {
669     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
670     g_object_unref (media);
671     g_object_unref (session);
672     return FALSE;
673   }
674 no_transport:
675   {
676     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
677     return FALSE;
678   }
679 unsupported_transports:
680   {
681     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
682     gst_rtsp_transport_free (ct);  
683     return FALSE;
684   }
685 no_pool:
686   {
687     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
688     return FALSE;
689   }
690 service_unavailable:
691   {
692     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
693     return FALSE;
694   }
695 }
696
697 /* for the describe we must generate an SDP */
698 static gboolean
699 handle_describe_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
700 {
701   GstRTSPMessage response = { 0 };
702   GstRTSPResult res;
703   GstSDPMessage *sdp;
704   guint i;
705   gchar *str;
706   GstRTSPMedia *media;
707
708   /* check what kind of format is accepted, we don't really do anything with it
709    * and always return SDP for now. */
710   for (i = 0; i++; ) {
711     gchar *accept;
712
713     res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
714     if (res == GST_RTSP_ENOTIMPL)
715       break;
716
717     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
718       break;
719   }
720
721   /* find the media object for the uri */
722   if (!(media = find_media (client, uri, request)))
723     goto no_media;
724
725   /* create an SDP for the media object */
726   if (!(sdp = gst_rtsp_sdp_from_media (media)))
727     goto no_sdp;
728
729   g_object_unref (media);
730
731   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
732         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
733
734   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "application/sdp");
735
736   /* content base for some clients that might screw up creating the setup uri */
737   str = g_strdup_printf ("rtsp://%s:%u%s/", uri->host, uri->port, uri->abspath);
738   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE, str);
739   g_free (str);
740
741   /* add SDP to the response body */
742   str = gst_sdp_message_as_text (sdp);
743   gst_rtsp_message_take_body (&response, (guint8 *)str, strlen (str));
744   gst_sdp_message_free (sdp);
745
746   send_response (client, NULL, &response);
747
748   return TRUE;
749
750   /* ERRORS */
751 no_media:
752   {
753     /* error reply is already sent */
754     return FALSE;
755   }
756 no_sdp:
757   {
758     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
759     g_object_unref (media);
760     return FALSE;
761   }
762 }
763
764 static void
765 handle_options_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
766 {
767   GstRTSPMessage response = { 0 };
768   GstRTSPMethod options;
769   gchar *str;
770
771   options = GST_RTSP_DESCRIBE |
772             GST_RTSP_OPTIONS |
773     //        GST_RTSP_PAUSE |
774             GST_RTSP_PLAY |
775             GST_RTSP_SETUP |
776             GST_RTSP_TEARDOWN;
777
778   str = gst_rtsp_options_as_text (options);
779
780   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
781         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
782
783   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
784   g_free (str);
785
786   send_response (client, NULL, &response);
787 }
788
789 /* remove duplicate and trailing '/' */
790 static void
791 santize_uri (GstRTSPUrl *uri)
792 {
793   gint i, len;
794   gchar *s, *d;
795   gboolean have_slash, prev_slash;
796
797   s = d = uri->abspath;
798   len = strlen (uri->abspath);
799
800   prev_slash = FALSE;
801
802   for (i = 0; i < len; i++) {
803     have_slash = s[i] == '/';
804     *d = s[i];
805     if (!have_slash || !prev_slash)
806       d++;
807     prev_slash = have_slash;
808   }
809   len = d - uri->abspath;
810   /* don't remove the first slash if that's the only thing left */
811   if (len > 1 && *(d-1) == '/')
812     d--;
813   *d = '\0';
814 }
815
816 /* this function runs in a client specific thread and handles all rtsp messages
817  * with the client */
818 static gpointer
819 handle_client (GstRTSPClient *client)
820 {
821   GstRTSPMessage request = { 0 };
822   GstRTSPResult res;
823   GstRTSPMethod method;
824   const gchar *uristr;
825   GstRTSPUrl *uri;
826   GstRTSPVersion version;
827
828   while (TRUE) {
829     GTimeVal timeout;
830     GstRTSPSession *session;
831
832     timeout.tv_sec = client->timeout;
833     timeout.tv_usec = 0;
834
835     /* start by waiting for a message from the client */
836     res = gst_rtsp_connection_receive (client->connection, &request, &timeout);
837     if (res < 0) {
838       if (res == GST_RTSP_ETIMEOUT)
839         goto timeout;
840
841       goto receive_failed;
842     }
843
844 #ifdef DEBUG
845     gst_rtsp_message_dump (&request);
846 #endif
847
848     gst_rtsp_message_parse_request (&request, &method, &uristr, &version);
849
850     if (version != GST_RTSP_VERSION_1_0) {
851       /* we can only handle 1.0 requests */
852       send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED, &request);
853       continue;
854     }
855
856     /* we always try to parse the url first */
857     if ((res = gst_rtsp_url_parse (uristr, &uri)) != GST_RTSP_OK) {
858       send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, &request);
859       continue;
860     }
861
862     /* sanitize the uri */
863     santize_uri (uri);
864
865     /* get the session if there is any */
866     session = find_session (client, &request);
867
868     /* now see what is asked and dispatch to a dedicated handler */
869     switch (method) {
870       case GST_RTSP_OPTIONS:
871         handle_options_request (client, uri, session, &request);
872         break;
873       case GST_RTSP_DESCRIBE:
874         handle_describe_request (client, uri, session, &request);
875         break;
876       case GST_RTSP_SETUP:
877         handle_setup_request (client, uri, session, &request);
878         break;
879       case GST_RTSP_PLAY:
880         handle_play_request (client, uri, session, &request);
881         break;
882       case GST_RTSP_PAUSE:
883         handle_pause_request (client, uri, session, &request);
884         break;
885       case GST_RTSP_TEARDOWN:
886         handle_teardown_request (client, uri, session, &request);
887         break;
888       case GST_RTSP_ANNOUNCE:
889       case GST_RTSP_GET_PARAMETER:
890       case GST_RTSP_RECORD:
891       case GST_RTSP_REDIRECT:
892       case GST_RTSP_SET_PARAMETER:
893         send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, &request);
894         break;
895       case GST_RTSP_INVALID:
896       default:
897         send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, &request);
898         break;
899     }
900     if (session)
901       g_object_unref (session);
902     gst_rtsp_url_free (uri);
903   }
904   g_object_unref (client);
905   return NULL;
906
907   /* ERRORS */
908 timeout:
909   {
910     g_message ("client timed out");
911     if (client->session_pool)
912       gst_rtsp_session_pool_cleanup (client->session_pool);
913     goto cleanup;
914   }
915 receive_failed:
916   {
917     gchar *str;
918     str = gst_rtsp_strresult (res);
919     g_message ("receive failed %d (%s), disconnect client %p", res, 
920             str, client);
921     g_free (str);
922     goto cleanup;
923   }
924 cleanup:
925   {
926     gst_rtsp_message_unset (&request);
927     gst_rtsp_connection_close (client->connection);
928     g_object_unref (client);
929     return NULL;
930   }
931 }
932
933 /* called when we need to accept a new request from a client */
934 static gboolean
935 client_accept (GstRTSPClient *client, GIOChannel *channel)
936 {
937   /* a new client connected. */
938   int server_sock_fd, fd;
939   unsigned int address_len;
940   GstRTSPConnection *conn;
941
942   server_sock_fd = g_io_channel_unix_get_fd (channel);
943
944   address_len = sizeof (client->address);
945   memset (&client->address, 0, address_len);
946
947   fd = accept (server_sock_fd, (struct sockaddr *) &client->address,
948       &address_len);
949   if (fd == -1)
950     goto accept_failed;
951
952   /* now create the connection object */
953   gst_rtsp_connection_create (NULL, &conn);
954   conn->fd.fd = fd;
955
956   /* FIXME some hackery, we need to have a connection method to accept server
957    * connections */
958   gst_poll_add_fd (conn->fdset, &conn->fd);
959
960   g_message ("added new client %p ip %s with fd %d", client,
961                 inet_ntoa (client->address.sin_addr), conn->fd.fd);
962
963   client->connection = conn;
964
965   return TRUE;
966
967   /* ERRORS */
968 accept_failed:
969   {
970     g_error ("Could not accept client on server socket %d: %s (%d)",
971             server_sock_fd, g_strerror (errno), errno);
972     return FALSE;
973   }
974 }
975
976 /**
977  * gst_rtsp_client_set_timeout:
978  * @client: a #GstRTSPClient
979  * @timeout: a timeout in seconds
980  *
981  * Set the connection timeout to @timeout seconds for @client.
982  */
983 void
984 gst_rtsp_client_set_timeout (GstRTSPClient *client, guint timeout)
985 {
986   client->timeout = timeout;
987 }
988
989 /**
990  * gst_rtsp_client_get_timeout:
991  * @client: a #GstRTSPClient
992  *
993  * Get the connection timeout @client.
994  *
995  * Returns: the connection timeout for @client in seconds.
996  */
997 guint
998 gst_rtsp_client_get_timeout (GstRTSPClient *client)
999 {
1000   return client->timeout;
1001 }
1002
1003 /**
1004  * gst_rtsp_client_set_session_pool:
1005  * @client: a #GstRTSPClient
1006  * @pool: a #GstRTSPSessionPool
1007  *
1008  * Set @pool as the sessionpool for @client which it will use to find
1009  * or allocate sessions. the sessionpool is usually inherited from the server
1010  * that created the client but can be overridden later.
1011  */
1012 void
1013 gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool)
1014 {
1015   GstRTSPSessionPool *old;
1016
1017   old = client->session_pool;
1018   if (old != pool) {
1019     if (pool)
1020       g_object_ref (pool);
1021     client->session_pool = pool;
1022     if (old)
1023       g_object_unref (old);
1024   }
1025 }
1026
1027 /**
1028  * gst_rtsp_client_get_session_pool:
1029  * @client: a #GstRTSPClient
1030  *
1031  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1032  *
1033  * Returns: a #GstRTSPSessionPool, unref after usage.
1034  */
1035 GstRTSPSessionPool *
1036 gst_rtsp_client_get_session_pool (GstRTSPClient *client)
1037 {
1038   GstRTSPSessionPool *result;
1039
1040   if ((result = client->session_pool))
1041     g_object_ref (result);
1042
1043   return result;
1044 }
1045
1046 /**
1047  * gst_rtsp_client_set_media_mapping:
1048  * @client: a #GstRTSPClient
1049  * @mapping: a #GstRTSPMediaMapping
1050  *
1051  * Set @mapping as the media mapping for @client which it will use to map urls
1052  * to media streams. These mapping is usually inherited from the server that
1053  * created the client but can be overriden later.
1054  */
1055 void
1056 gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *mapping)
1057 {
1058   GstRTSPMediaMapping *old;
1059
1060   old = client->media_mapping;
1061
1062   if (old != mapping) {
1063     if (mapping)
1064       g_object_ref (mapping);
1065     client->media_mapping = mapping;
1066     if (old)
1067       g_object_unref (old);
1068   }
1069 }
1070
1071 /**
1072  * gst_rtsp_client_get_media_mapping:
1073  * @client: a #GstRTSPClient
1074  *
1075  * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1076  *
1077  * Returns: a #GstRTSPMediaMapping, unref after usage.
1078  */
1079 GstRTSPMediaMapping *
1080 gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
1081 {
1082   GstRTSPMediaMapping *result;
1083
1084   if ((result = client->media_mapping))
1085     g_object_ref (result);
1086
1087   return result;
1088 }
1089
1090 /**
1091  * gst_rtsp_client_attach:
1092  * @client: a #GstRTSPClient
1093  * @channel: a #GIOChannel
1094  *
1095  * Accept a new connection for @client on the socket in @source. 
1096  *
1097  * This function should be called when the client properties and urls are fully
1098  * configured and the client is ready to start.
1099  *
1100  * Returns: %TRUE if the client could be accepted.
1101  */
1102 gboolean
1103 gst_rtsp_client_accept (GstRTSPClient *client, GIOChannel *channel)
1104 {
1105   GError *error = NULL;
1106
1107   if (!client_accept (client, channel))
1108     goto accept_failed;
1109
1110   /* client accepted, spawn a thread for the client, we don't need to join the
1111    * thread */
1112   g_object_ref (client);
1113   client->thread = g_thread_create ((GThreadFunc)handle_client, client, FALSE, &error);
1114   if (client->thread == NULL)
1115     goto no_thread;
1116
1117   return TRUE;
1118
1119   /* ERRORS */
1120 accept_failed:
1121   {
1122     return FALSE;
1123   }
1124 no_thread:
1125   {
1126     if (error) {
1127       g_warning ("could not create thread for client %p: %s", client, error->message);
1128       g_error_free (error);
1129     }
1130     g_object_unref (client);
1131     return FALSE;
1132   }
1133 }