Make the server handle arbitrary pipelines
[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 <gst/sdp/gstsdpmessage.h>
23
24 #include "rtsp-client.h"
25
26 #undef DEBUG
27
28 static void gst_rtsp_client_finalize (GObject * obj);
29
30 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
31
32 static void
33 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
34 {
35   GObjectClass *gobject_class;
36
37   gobject_class = G_OBJECT_CLASS (klass);
38
39   gobject_class->finalize = gst_rtsp_client_finalize;
40 }
41
42 static void
43 gst_rtsp_client_init (GstRTSPClient * client)
44 {
45 }
46
47 static void
48 gst_rtsp_client_finalize (GObject * obj)
49 {
50   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
51 }
52
53 /**
54  * gst_rtsp_client_new:
55  *
56  * Create a new #GstRTSPClient instance.
57  */
58 GstRTSPClient *
59 gst_rtsp_client_new (void)
60 {
61   GstRTSPClient *result;
62
63   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
64
65   return result;
66 }
67
68 static void
69 handle_generic_response (GstRTSPClient *client, GstRTSPStatusCode code, 
70     GstRTSPMessage *request)
71 {
72   GstRTSPMessage response = { 0 };
73
74   gst_rtsp_message_init_response (&response, code, 
75         gst_rtsp_status_as_text (code), request);
76
77   gst_rtsp_connection_send (client->connection, &response, NULL);
78 }
79
80 static gboolean
81 handle_teardown_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *request)
82 {
83   GstRTSPResult res;
84   GstRTSPSessionMedia *media;
85   GstRTSPSession *session;
86   gchar *sessid;
87   GstRTSPMessage response = { 0 };
88   GstRTSPStatusCode code;
89
90   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
91   if (res == GST_RTSP_OK) {
92     /* we had a session in the request, find it again */
93     if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
94       goto session_not_found;
95   }
96   else
97     goto service_unavailable;
98
99   /* get a handle to the configuration of the media in the session */
100   media = gst_rtsp_session_get_media (session, uri, client->factory);
101   if (!media)
102     goto not_found;
103
104   gst_rtsp_session_media_stop (media);
105
106   gst_rtsp_session_pool_remove (client->pool, session);
107   g_object_unref (session);
108
109   /* remove the session id from the request, which will also remove it from the
110    * response */
111   gst_rtsp_message_remove_header (request, GST_RTSP_HDR_SESSION, -1);
112
113   /* construct the response now */
114   code = GST_RTSP_STS_OK;
115   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
116
117   gst_rtsp_connection_send (client->connection, &response, NULL);
118
119   return FALSE;
120
121   /* ERRORS */
122 session_not_found:
123   {
124     handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
125     return FALSE;
126   }
127 service_unavailable:
128   {
129     handle_generic_response (client, GST_RTSP_STS_OK, request);
130     return FALSE;
131   }
132 not_found:
133   {
134     handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
135     return FALSE;
136   }
137 }
138
139 static gboolean
140 handle_pause_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *request)
141 {
142   GstRTSPResult res;
143   GstRTSPSessionMedia *media;
144   GstRTSPSession *session;
145   gchar *sessid;
146   GstRTSPMessage response = { 0 };
147   GstRTSPStatusCode code;
148
149   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
150   if (res == GST_RTSP_OK) {
151     /* we had a session in the request, find it again */
152     if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
153       goto session_not_found;
154   }
155   else
156     goto service_unavailable;
157
158   /* get a handle to the configuration of the media in the session */
159   media = gst_rtsp_session_get_media (session, uri, client->factory);
160   if (!media)
161     goto not_found;
162
163   gst_rtsp_session_media_pause (media);
164   g_object_unref (session);
165
166   /* construct the response now */
167   code = GST_RTSP_STS_OK;
168   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
169
170   gst_rtsp_connection_send (client->connection, &response, NULL);
171
172   return FALSE;
173
174   /* ERRORS */
175 session_not_found:
176   {
177     handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
178     return FALSE;
179   }
180 service_unavailable:
181   {
182     return FALSE;
183   }
184 not_found:
185   {
186     handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
187     return FALSE;
188   }
189 }
190
191 static gboolean
192 handle_play_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *request)
193 {
194   GstRTSPResult res;
195   GstRTSPSessionMedia *media;
196   GstRTSPSession *session;
197   gchar *sessid;
198   GstRTSPMessage response = { 0 };
199   GstRTSPStatusCode code;
200   GstStateChangeReturn ret;
201   GString *rtpinfo;
202   guint n_streams, i;
203   guint timestamp, seqnum;
204
205   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
206   if (res == GST_RTSP_OK) {
207     /* we had a session in the request, find it again */
208     if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
209       goto session_not_found;
210   }
211   else
212     goto service_unavailable;
213
214   /* get a handle to the configuration of the media in the session */
215   media = gst_rtsp_session_get_media (session, uri, client->factory);
216   if (!media)
217     goto not_found;
218
219   /* wait for paused to get the caps */
220   ret = gst_rtsp_session_media_pause (media);
221   switch (ret) {
222     case GST_STATE_CHANGE_NO_PREROLL:
223       break;
224     case GST_STATE_CHANGE_SUCCESS:
225       break;
226     case GST_STATE_CHANGE_FAILURE:
227       goto service_unavailable;
228     case GST_STATE_CHANGE_ASYNC:
229       /* wait for paused state change to complete */
230       ret = gst_element_get_state (media->pipeline, NULL, NULL, -1);
231       break;
232   }
233
234   /* grab RTPInfo from the payloaders now */
235   rtpinfo = g_string_new ("");
236   n_streams = gst_rtsp_media_bin_n_streams (media->mediabin);
237   for (i = 0; i < n_streams; i++) {
238     GstRTSPMediaStream *stream;
239
240     stream = gst_rtsp_media_bin_get_stream (media->mediabin, i);
241
242     g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL);
243     g_object_get (G_OBJECT (stream->payloader), "timestamp", &timestamp, NULL);
244
245     if (i > 0)
246       g_string_append (rtpinfo, ", ");
247     g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u", uri, i, seqnum, timestamp);
248   }
249
250   /* construct the response now */
251   code = GST_RTSP_STS_OK;
252   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
253
254   /* add the RTP-Info header */
255   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_RTP_INFO, rtpinfo->str);
256   g_string_free (rtpinfo, TRUE);
257
258   gst_rtsp_connection_send (client->connection, &response, NULL);
259
260   /* start playing after sending the request */
261   gst_rtsp_session_media_play (media);
262   g_object_unref (session);
263
264   return FALSE;
265
266   /* ERRORS */
267 session_not_found:
268   {
269     handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
270     return FALSE;
271   }
272 service_unavailable:
273   {
274     handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
275     return FALSE;
276   }
277 not_found:
278   {
279     handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
280     return FALSE;
281   }
282 }
283
284 static gboolean
285 handle_setup_response (GstRTSPClient *client, const gchar *location, GstRTSPMessage *request)
286 {
287   GstRTSPResult res;
288   gchar *sessid;
289   gchar *transport;
290   gchar **transports;
291   gboolean have_transport;
292   GstRTSPTransport *ct, *st;
293   GstRTSPUrl *uri;
294   GstRTSPSession *session;
295   gint i;
296   GstRTSPLowerTrans supported;
297   GstRTSPMessage response = { 0 };
298   GstRTSPStatusCode code;
299   GstRTSPSessionStream *stream;
300   gchar *trans_str, *pos;
301   guint streamid;
302   GstRTSPSessionMedia *media;
303   gboolean need_session;
304
305   /* the uri contains the stream number we added in the SDP config, which is
306    * always /stream=%d so we need to strip that off */
307   if ((res = gst_rtsp_url_parse (location, &uri)) != GST_RTSP_OK)
308     goto bad_url;
309
310   /* parse the stream we need to configure, look for the stream in the abspath
311    * first and then in the query. */
312   if (!(pos = strstr (uri->abspath, "/stream="))) {
313     if (!(pos = strstr (uri->query, "/stream=")))
314       goto bad_request;
315   }
316
317   /* we can mofify the parse uri in place */
318   *pos = '\0';
319
320   pos += strlen ("/stream=");
321   if (sscanf (pos, "%u", &streamid) != 1)
322     goto bad_request;
323
324   /* find the media associated with the uri */
325   if (client->factory == NULL) {
326     if ((client->factory = gst_rtsp_media_mapping_find_factory (client->mapping, uri)) == NULL)
327       goto not_found;
328   }
329
330   /* parse the transport */
331   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
332   if (res != GST_RTSP_OK)
333     goto unsupported_transports;
334
335   transports = g_strsplit (transport, ",", 0);
336   gst_rtsp_transport_new (&ct);  
337
338   /* loop through the transports, try to parse */
339   have_transport = FALSE;
340   for (i = 0; transports[i]; i++) {
341
342     gst_rtsp_transport_init (ct);  
343     res = gst_rtsp_transport_parse (transports[i], ct);
344     if (res == GST_RTSP_OK) {
345       have_transport = TRUE;
346       break;
347     }
348   }
349   g_strfreev (transports);
350
351   /* we have not found anything usable, error out */
352   if (!have_transport) {
353     gst_rtsp_transport_free (ct);  
354     goto unsupported_transports;
355   }
356
357   /* we have a valid transport, check if we can handle it */
358   if (ct->trans != GST_RTSP_TRANS_RTP)
359     goto unsupported_transports;
360   if (ct->profile != GST_RTSP_PROFILE_AVP)
361     goto unsupported_transports;
362   supported = GST_RTSP_LOWER_TRANS_UDP |
363         GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP;
364   if (!(ct->lower_transport & supported))
365     goto unsupported_transports;
366
367   /* a setup request creates a session for a client, check if the client already
368    * sent a session id to us */
369   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
370   if (res == GST_RTSP_OK) {
371     /* we had a session in the request, find it again */
372     if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
373       goto session_not_found;
374     need_session = FALSE;
375   }
376   else {
377     /* create a session if this fails we probably reached our session limit or
378      * something. */
379     if (!(session = gst_rtsp_session_pool_create (client->pool)))
380       goto service_unavailable;
381     need_session = TRUE;
382   }
383
384   /* get a handle to the configuration of the media in the session */
385   media = gst_rtsp_session_get_media (session, uri->abspath, client->factory);
386   if (!media)
387     goto not_found;
388
389   /* get a handle to the stream in the media */
390   stream = gst_rtsp_session_media_get_stream (media, streamid);
391
392   /* setup the server transport from the client transport */
393   st = gst_rtsp_session_stream_set_transport (stream, inet_ntoa (client->address.sin_addr), ct);
394
395   /* serialize the server transport */
396   trans_str = gst_rtsp_transport_as_text (st);
397
398   /* construct the response now */
399   code = GST_RTSP_STS_OK;
400   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
401
402   if (need_session)
403     gst_rtsp_message_add_header (&response, GST_RTSP_HDR_SESSION, session->sessionid);
404   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
405   g_free (trans_str);
406   g_object_unref (session);
407
408   gst_rtsp_connection_send (client->connection, &response, NULL);
409
410   return TRUE;
411
412   /* ERRORS */
413 bad_url:
414   {
415     handle_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
416     return FALSE;
417   }
418 bad_request:
419   {
420     handle_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
421     return FALSE;
422   }
423 not_found:
424   {
425     handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
426     return FALSE;
427   }
428 session_not_found:
429   {
430     handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
431     return FALSE;
432   }
433 unsupported_transports:
434   {
435     handle_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
436     return FALSE;
437   }
438 service_unavailable:
439   {
440     handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
441     return FALSE;
442   }
443 }
444
445 /* for the describe we must generate an SDP */
446 static gboolean
447 handle_describe_response (GstRTSPClient *client, const gchar *location, GstRTSPMessage *request)
448 {
449   GstRTSPMessage response = { 0 };
450   GstRTSPResult res;
451   GstSDPMessage *sdp;
452   GstRTSPUrl *uri;
453   guint n_streams, i;
454   gchar *sdptext;
455   GstRTSPMediaFactory *factory;
456   GstRTSPMediaBin *mediabin;
457   GstElement *pipeline;
458
459   /* the uri contains the stream number we added in the SDP config, which is
460    * always /stream=%d so we need to strip that off */
461   if ((res = gst_rtsp_url_parse (location, &uri)) != GST_RTSP_OK)
462     goto bad_url;
463
464   /* find the factory for the uri first */
465   if (!(factory = gst_rtsp_media_mapping_find_factory (client->mapping, uri)))
466     goto no_factory;
467
468   /* check what kind of format is accepted */
469
470   /* create a pipeline to preroll the media */
471   pipeline = gst_pipeline_new ("client-describe-pipeline");
472
473   /* prepare the media and add it to the pipeline */
474   if (!(mediabin = gst_rtsp_media_factory_construct (factory, uri->abspath)))
475     goto no_media_bin;
476   
477   gst_bin_add (GST_BIN_CAST (pipeline), mediabin->element);
478
479   /* link fakesink to all stream pads and set the pipeline to PLAYING */
480   n_streams = gst_rtsp_media_bin_n_streams (mediabin);
481   for (i = 0; i < n_streams; i++) {
482     GstRTSPMediaStream *stream;
483     GstElement *sink;
484     GstPad *sinkpad;
485     GstPadLinkReturn lret;
486
487     stream = gst_rtsp_media_bin_get_stream (mediabin, i);
488
489     sink = gst_element_factory_make ("fakesink", NULL);
490     gst_bin_add (GST_BIN (pipeline), sink);
491
492     sinkpad = gst_element_get_static_pad (sink, "sink");
493     lret = gst_pad_link (stream->srcpad, sinkpad);
494     if (lret != GST_PAD_LINK_OK) {
495       g_warning ("failed to link pad to sink: %d", lret);
496     }
497     gst_object_unref (sinkpad);
498   }
499
500   /* now play and wait till we get the pads blocked. At that time the pipeline
501    * is prerolled and we have the caps on the streams too. */
502   gst_element_set_state (pipeline, GST_STATE_PLAYING);
503
504   /* wait for state change to complete */
505   gst_element_get_state (pipeline, NULL, NULL, -1);
506
507   /* we should now be able to construct the SDP message */
508   gst_sdp_message_new (&sdp);
509
510   /* some standard things first */
511   gst_sdp_message_set_version (sdp, "0");
512   gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", "IP4", "127.0.0.1");
513   gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
514   gst_sdp_message_set_information (sdp, "rtsp-server");
515   gst_sdp_message_add_time (sdp, "0", "0", NULL);
516   gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
517   gst_sdp_message_add_attribute (sdp, "type", "broadcast");
518
519   for (i = 0; i < n_streams; i++) {
520     GstRTSPMediaStream *stream;
521     GstSDPMedia *smedia;
522     GstStructure *s;
523     const gchar *caps_str, *caps_enc, *caps_params;
524     gchar *tmp;
525     gint caps_pt, caps_rate;
526     guint n_fields, j;
527     gboolean first;
528     GString *fmtp;
529
530     stream = gst_rtsp_media_bin_get_stream (mediabin, i);
531     gst_sdp_media_new (&smedia);
532
533     s = gst_caps_get_structure (stream->caps, 0);
534
535     /* get media type and payload for the m= line */
536     caps_str = gst_structure_get_string (s, "media");
537     gst_sdp_media_set_media (smedia, caps_str);
538
539     gst_structure_get_int (s, "payload", &caps_pt);
540     tmp = g_strdup_printf ("%d", caps_pt);
541     gst_sdp_media_add_format (smedia, tmp);
542     g_free (tmp);
543
544     gst_sdp_media_set_port_info (smedia, 0, 1);
545     gst_sdp_media_set_proto (smedia, "RTP/AVP");
546
547     /* for the c= line */
548     gst_sdp_media_add_connection (smedia, "IN", "IP4", "127.0.0.1", 0, 0);
549
550     /* get clock-rate, media type and params for the rtpmap attribute */
551     gst_structure_get_int (s, "clock-rate", &caps_rate);
552     caps_enc = gst_structure_get_string (s, "encoding-name");
553     caps_params = gst_structure_get_string (s, "encoding-params");
554
555     if (caps_params)
556       tmp = g_strdup_printf ("%d %s/%d/%s", caps_pt, caps_enc, caps_rate,
557                       caps_params);
558     else
559       tmp = g_strdup_printf ("%d %s/%d", caps_pt, caps_enc, caps_rate);
560
561     gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
562     g_free (tmp);
563
564     /* the config uri */
565     tmp = g_strdup_printf ("stream=%d", i);
566     gst_sdp_media_add_attribute (smedia, "control", tmp);
567     g_free (tmp);
568
569     /* collect all other properties and add them to fmtp */
570     fmtp = g_string_new ("");
571     g_string_append_printf (fmtp, "%d ", caps_pt);
572     first = TRUE;
573     n_fields = gst_structure_n_fields (s);
574     for (j = 0; j < n_fields; j++) {
575       const gchar *fname, *fval;
576
577       fname = gst_structure_nth_field_name (s, j);
578
579       /* filter out standard properties */
580       if (!strcmp (fname, "media")) 
581         continue;
582       if (!strcmp (fname, "payload")) 
583         continue;
584       if (!strcmp (fname, "clock-rate")) 
585         continue;
586       if (!strcmp (fname, "encoding-name")) 
587         continue;
588       if (!strcmp (fname, "encoding-params")) 
589         continue;
590       if (!strcmp (fname, "ssrc")) 
591         continue;
592       if (!strcmp (fname, "clock-base")) 
593         continue;
594       if (!strcmp (fname, "seqnum-base")) 
595         continue;
596
597       if ((fval = gst_structure_get_string (s, fname))) {
598         g_string_append_printf (fmtp, "%s%s=%s", first ? "":";", fname, fval);
599         first = FALSE;
600       }
601     }
602     if (!first) {
603       tmp = g_string_free (fmtp, FALSE);
604       gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
605       g_free (tmp);
606     }
607     else {
608       g_string_free (fmtp, TRUE);
609     }
610     gst_sdp_message_add_media (sdp, smedia);
611   }
612   /* go back to NULL */
613   gst_element_set_state (pipeline, GST_STATE_NULL);
614
615   g_object_unref (factory);
616
617   gst_object_unref (pipeline);
618   pipeline = NULL;
619
620   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
621         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
622
623   /* add SDP to the response body */
624   sdptext = gst_sdp_message_as_text (sdp);
625   gst_rtsp_message_take_body (&response, (guint8 *)sdptext, strlen (sdptext));
626   gst_sdp_message_free (sdp);
627
628   gst_rtsp_connection_send (client->connection, &response, NULL);
629
630   return TRUE;
631
632   /* ERRORS */
633 bad_url:
634   {
635     handle_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
636     return FALSE;
637   }
638 no_factory:
639   {
640     handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
641     return FALSE;
642   }
643 no_media_bin:
644   {
645     handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
646     g_object_unref (factory);
647     return FALSE;
648   }
649 }
650
651 static void
652 handle_options_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *request)
653 {
654   GstRTSPMessage response = { 0 };
655   GstRTSPMethod options;
656   GString *str;
657
658   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
659         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
660
661   options = GST_RTSP_DESCRIBE |
662             GST_RTSP_OPTIONS |
663     //        GST_RTSP_PAUSE |
664             GST_RTSP_PLAY |
665             GST_RTSP_SETUP |
666             GST_RTSP_TEARDOWN;
667
668   /* always return options.. */
669   str = g_string_new ("OPTIONS");
670
671   if (options & GST_RTSP_DESCRIBE)
672     g_string_append (str, ", DESCRIBE");
673   if (options & GST_RTSP_ANNOUNCE)
674     g_string_append (str, ", ANNOUNCE");
675   if (options & GST_RTSP_GET_PARAMETER)
676     g_string_append (str, ", GET_PARAMETER");
677   if (options & GST_RTSP_PAUSE)
678     g_string_append (str, ", PAUSE");
679   if (options & GST_RTSP_PLAY)
680     g_string_append (str, ", PLAY");
681   if (options & GST_RTSP_RECORD)
682     g_string_append (str, ", RECORD");
683   if (options & GST_RTSP_REDIRECT)
684     g_string_append (str, ", REDIRECT");
685   if (options & GST_RTSP_SETUP)
686     g_string_append (str, ", SETUP");
687   if (options & GST_RTSP_SET_PARAMETER)
688     g_string_append (str, ", SET_PARAMETER");
689   if (options & GST_RTSP_TEARDOWN)
690     g_string_append (str, ", TEARDOWN");
691
692   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str->str);
693
694   g_string_free (str, TRUE);
695
696   gst_rtsp_connection_send (client->connection, &response, NULL);
697 }
698
699 /* this function runs in a client specific thread and handles all rtsp messages
700  * with the client */
701 static gpointer
702 handle_client (GstRTSPClient *client)
703 {
704   GstRTSPMessage request = { 0 };
705   GstRTSPResult res;
706   GstRTSPMethod method;
707   const gchar *uri;
708   GstRTSPVersion version;
709
710   while (TRUE) {
711     /* start by waiting for a message from the client */
712     res = gst_rtsp_connection_receive (client->connection, &request, NULL);
713     if (res < 0)
714       goto receive_failed;
715
716 #ifdef DEBUG
717     gst_rtsp_message_dump (&request);
718 #endif
719
720     gst_rtsp_message_parse_request (&request, &method, &uri, &version);
721
722     if (version != GST_RTSP_VERSION_1_0) {
723       /* we can only handle 1.0 requests */
724       handle_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED, &request);
725       continue;
726     }
727
728     /* now see what is asked and dispatch to a dedicated handler */
729     switch (method) {
730       case GST_RTSP_OPTIONS:
731         handle_options_response (client, uri, &request);
732         break;
733       case GST_RTSP_DESCRIBE:
734         handle_describe_response (client, uri, &request);
735         break;
736       case GST_RTSP_SETUP:
737         handle_setup_response (client, uri, &request);
738         break;
739       case GST_RTSP_PLAY:
740         handle_play_response (client, uri, &request);
741         break;
742       case GST_RTSP_PAUSE:
743         handle_pause_response (client, uri, &request);
744         break;
745       case GST_RTSP_TEARDOWN:
746         handle_teardown_response (client, uri, &request);
747         break;
748       case GST_RTSP_ANNOUNCE:
749       case GST_RTSP_GET_PARAMETER:
750       case GST_RTSP_RECORD:
751       case GST_RTSP_REDIRECT:
752       case GST_RTSP_SET_PARAMETER:
753         handle_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, &request);
754         break;
755       case GST_RTSP_INVALID:
756       default:
757         handle_generic_response (client, GST_RTSP_STS_BAD_REQUEST, &request);
758         break;
759     }
760   }
761   g_object_unref (client);
762   return NULL;
763
764   /* ERRORS */
765 receive_failed:
766   {
767     g_message ("receive failed %d (%s), disconnect client %p", res, 
768             gst_rtsp_strresult (res), client);
769     gst_rtsp_connection_close (client->connection);
770     g_object_unref (client);
771     return NULL;
772   }
773 }
774
775 /* called when we need to accept a new request from a client */
776 static gboolean
777 client_accept (GstRTSPClient *client, GIOChannel *channel)
778 {
779   /* a new client connected. */
780   int server_sock_fd, fd;
781   unsigned int address_len;
782   GstRTSPConnection *conn;
783
784   server_sock_fd = g_io_channel_unix_get_fd (channel);
785
786   address_len = sizeof (client->address);
787   memset (&client->address, 0, address_len);
788
789   fd = accept (server_sock_fd, (struct sockaddr *) &client->address,
790       &address_len);
791   if (fd == -1)
792     goto accept_failed;
793
794   /* now create the connection object */
795   gst_rtsp_connection_create (NULL, &conn);
796   conn->fd.fd = fd;
797
798   /* FIXME some hackery, we need to have a connection method to accept server
799    * connections */
800   gst_poll_add_fd (conn->fdset, &conn->fd);
801
802   g_message ("added new client %p ip %s with fd %d", client,
803                 inet_ntoa (client->address.sin_addr), conn->fd.fd);
804
805   client->connection = conn;
806
807   return TRUE;
808
809   /* ERRORS */
810 accept_failed:
811   {
812     g_error ("Could not accept client on server socket %d: %s (%d)",
813             server_sock_fd, g_strerror (errno), errno);
814     return FALSE;
815   }
816 }
817
818 /**
819  * gst_rtsp_client_set_session_pool:
820  * @client: a #GstRTSPClient
821  * @pool: a #GstRTSPSessionPool
822  *
823  * Set @pool as the sessionpool for @client which it will use to find
824  * or allocate sessions. the sessionpool is usually inherited from the server
825  * that created the client but can be overridden later.
826  */
827 void
828 gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool)
829 {
830   GstRTSPSessionPool *old;
831
832   old = client->pool;
833   if (old != pool) {
834     if (pool)
835       g_object_ref (pool);
836     client->pool = pool;
837     if (old)
838       g_object_unref (old);
839   }
840 }
841
842 /**
843  * gst_rtsp_client_get_session_pool:
844  * @client: a #GstRTSPClient
845  *
846  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
847  *
848  * Returns: a #GstRTSPSessionPool, unref after usage.
849  */
850 GstRTSPSessionPool *
851 gst_rtsp_client_get_session_pool (GstRTSPClient *client)
852 {
853   GstRTSPSessionPool *result;
854
855   if ((result = client->pool))
856     g_object_ref (result);
857
858   return result;
859 }
860
861 /**
862  * gst_rtsp_client_set_media_mapping:
863  * @client: a #GstRTSPClient
864  * @mapping: a #GstRTSPMediaMapping
865  *
866  * Set @mapping as the media mapping for @client which it will use to map urls
867  * to media streams. These mapping is usually inherited from the server that
868  * created the client but can be overriden later.
869  */
870 void
871 gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *mapping)
872 {
873   GstRTSPMediaMapping *old;
874
875   old = client->mapping;
876
877   if (old != mapping) {
878     if (mapping)
879       g_object_ref (mapping);
880     client->mapping = mapping;
881     if (old)
882       g_object_unref (old);
883   }
884 }
885
886 /**
887  * gst_rtsp_client_get_media_mapping:
888  * @client: a #GstRTSPClient
889  *
890  * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
891  *
892  * Returns: a #GstRTSPMediaMapping, unref after usage.
893  */
894 GstRTSPMediaMapping *
895 gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
896 {
897   GstRTSPMediaMapping *result;
898
899   if ((result = client->mapping))
900     g_object_ref (result);
901
902   return result;
903 }
904
905
906 /**
907  * gst_rtsp_client_attach:
908  * @client: a #GstRTSPClient
909  * @channel: a #GIOChannel
910  *
911  * Accept a new connection for @client on the socket in @source. 
912  *
913  * This function should be called when the client properties and urls are fully
914  * configured and the client is ready to start.
915  *
916  * Returns: %TRUE if the client could be accepted.
917  */
918 gboolean
919 gst_rtsp_client_accept (GstRTSPClient *client, GIOChannel *channel)
920 {
921   if (!client_accept (client, channel))
922     goto accept_failed;
923
924   /* client accepted, spawn a thread for the client */
925   g_object_ref (client);
926   client->thread = g_thread_create ((GThreadFunc)handle_client, client, TRUE, NULL);
927
928   return TRUE;
929
930   /* ERRORS */
931 accept_failed:
932   {
933     return FALSE;
934   }
935 }