2476db7a9be85b420e8eef4494e1bf56d6d3d88f
[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 #define DEBUG
26
27 static GMutex *tunnels_lock;
28 static GHashTable *tunnels;
29
30 enum
31 {
32   PROP_0,
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 static void client_session_finalized (GstRTSPClient *client, GstRTSPSession *session);
45
46 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
47
48 static void
49 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
50 {
51   GObjectClass *gobject_class;
52
53   gobject_class = G_OBJECT_CLASS (klass);
54
55   gobject_class->get_property = gst_rtsp_client_get_property;
56   gobject_class->set_property = gst_rtsp_client_set_property;
57   gobject_class->finalize = gst_rtsp_client_finalize;
58
59   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
60       g_param_spec_object ("session-pool", "Session Pool",
61           "The session pool to use for client session",
62           GST_TYPE_RTSP_SESSION_POOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
63
64   g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
65       g_param_spec_object ("media-mapping", "Media Mapping",
66           "The media mapping to use for client session",
67           GST_TYPE_RTSP_MEDIA_MAPPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
68
69   tunnels = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
70   tunnels_lock = g_mutex_new ();
71 }
72
73 static void
74 gst_rtsp_client_init (GstRTSPClient * client)
75 {
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   GList *walk;
84
85   g_message ("finalize client %p", client);
86
87   for (walk = client->sessions; walk; walk = g_list_next (walk))
88     g_object_weak_unref (G_OBJECT (walk->data), (GWeakNotify) client_session_finalized, client);
89   g_list_free (client->sessions);
90
91   gst_rtsp_connection_free (client->connection);
92   if (client->session_pool)
93     g_object_unref (client->session_pool);
94   if (client->media_mapping)
95     g_object_unref (client->media_mapping);
96
97   if (client->uri)
98     gst_rtsp_url_free (client->uri);
99   if (client->media)
100     g_object_unref (client->media);
101
102   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
103 }
104
105 static void
106 gst_rtsp_client_get_property (GObject *object, guint propid,
107     GValue *value, GParamSpec *pspec)
108 {
109   GstRTSPClient *client = GST_RTSP_CLIENT (object);
110
111   switch (propid) {
112     case PROP_SESSION_POOL:
113       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
114       break;
115     case PROP_MEDIA_MAPPING:
116       g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
117       break;
118     default:
119       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
120   }
121 }
122
123 static void
124 gst_rtsp_client_set_property (GObject *object, guint propid,
125     const GValue *value, GParamSpec *pspec)
126 {
127   GstRTSPClient *client = GST_RTSP_CLIENT (object);
128
129   switch (propid) {
130     case PROP_SESSION_POOL:
131       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
132       break;
133     case PROP_MEDIA_MAPPING:
134       gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
135       break;
136     default:
137       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
138   }
139 }
140
141 /**
142  * gst_rtsp_client_new:
143  *
144  * Create a new #GstRTSPClient instance.
145  */
146 GstRTSPClient *
147 gst_rtsp_client_new (void)
148 {
149   GstRTSPClient *result;
150
151   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
152
153   return result;
154 }
155
156 static void
157 send_response (GstRTSPClient *client, GstRTSPSession *session, GstRTSPMessage *response)
158 {
159   gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER, "GStreamer RTSP server");
160
161   /* remove any previous header */
162   gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
163
164   /* add the new session header for new session ids */
165   if (session) {
166     gchar *str;
167
168     if (session->timeout != 60)
169       str = g_strdup_printf ("%s; timeout=%d", session->sessionid, session->timeout);
170     else
171       str = g_strdup (session->sessionid);
172
173     gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
174   }
175
176 #ifdef DEBUG
177   gst_rtsp_message_dump (response);
178 #endif
179
180   gst_rtsp_watch_queue_message (client->watch, response);
181   gst_rtsp_message_unset (response);
182 }
183
184 static void
185 send_generic_response (GstRTSPClient *client, GstRTSPStatusCode code, 
186     GstRTSPMessage *request)
187 {
188   GstRTSPMessage response = { 0 };
189
190   gst_rtsp_message_init_response (&response, code, 
191         gst_rtsp_status_as_text (code), request);
192
193   send_response (client, NULL, &response);
194 }
195
196 static gboolean
197 compare_uri (const GstRTSPUrl *uri1, const GstRTSPUrl *uri2)
198 {
199   if (uri1 == NULL || uri2 == NULL)
200     return FALSE;
201
202   if (strcmp (uri1->abspath, uri2->abspath))
203     return FALSE;
204
205   return TRUE;
206 }
207
208 /* this function is called to initially find the media for the DESCRIBE request
209  * but is cached for when the same client (without breaking the connection) is
210  * doing a setup for the exact same url. */
211 static GstRTSPMedia *
212 find_media (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
213 {
214   GstRTSPMediaFactory *factory;
215   GstRTSPMedia *media;
216
217   if (!compare_uri (client->uri, uri)) {
218     /* remove any previously cached values before we try to construct a new
219      * media for uri */
220     if (client->uri)
221       gst_rtsp_url_free (client->uri);
222     client->uri = NULL;
223     if (client->media)
224       g_object_unref (client->media);
225     client->media = NULL;
226
227     if (!client->media_mapping)
228       goto no_mapping;
229
230     /* find the factory for the uri first */
231     if (!(factory = gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
232       goto no_factory;
233
234     /* prepare the media and add it to the pipeline */
235     if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
236       goto no_media;
237
238     /* prepare the media */
239     if (!(gst_rtsp_media_prepare (media)))
240       goto no_prepare;
241
242     /* now keep track of the uri and the media */
243     client->uri = gst_rtsp_url_copy (uri);
244     client->media = media;
245   }
246   else {
247     /* we have seen this uri before, used cached media */
248     media = client->media;
249     g_message ("reusing cached media %p", media); 
250   }
251
252   if (media)
253     g_object_ref (media);
254
255   return media;
256
257   /* ERRORS */
258 no_mapping:
259   {
260     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
261     return NULL;
262   }
263 no_factory:
264   {
265     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
266     return NULL;
267   }
268 no_media:
269   {
270     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
271     g_object_unref (factory);
272     return NULL;
273   }
274 no_prepare:
275   {
276     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
277     g_object_unref (media);
278     g_object_unref (factory);
279     return NULL;
280   }
281 }
282
283 static gboolean
284 do_send_data (GstBuffer *buffer, guint8 channel, GstRTSPClient *client)
285 {
286   GstRTSPMessage message = { 0 };
287   guint8 *data;
288   guint size;
289
290   gst_rtsp_message_init_data (&message, channel);
291
292   data = GST_BUFFER_DATA (buffer);
293   size = GST_BUFFER_SIZE (buffer);
294   gst_rtsp_message_take_body (&message, data, size);
295
296   gst_rtsp_watch_queue_message (client->watch, &message);
297
298   gst_rtsp_message_steal_body (&message, &data, &size);
299   gst_rtsp_message_unset (&message);
300
301   return TRUE;
302 }
303
304 static void
305 link_stream (GstRTSPClient *client, GstRTSPSessionStream *stream)
306 {
307   gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
308        (GstRTSPSendFunc) do_send_data, client, NULL);
309   client->streams = g_list_prepend (client->streams, stream);
310 }
311
312 static void
313 unlink_stream (GstRTSPClient *client, GstRTSPSessionStream *stream)
314 {
315   gst_rtsp_session_stream_set_callbacks (stream, NULL,
316        NULL, client, g_object_unref);
317   client->streams = g_list_remove (client->streams, stream);
318 }
319
320 static void
321 unlink_streams (GstRTSPClient *client)
322 {
323   GList *walk;
324
325   for (walk = client->streams; walk; walk = g_list_next (walk)) {
326     GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
327
328     gst_rtsp_session_stream_set_callbacks (stream, NULL,
329          NULL, NULL, NULL);
330   }
331   g_list_free (client->streams);
332   client->streams = NULL;
333 }
334
335 static void
336 unlink_session_streams (GstRTSPClient *client, GstRTSPSessionMedia *media)
337 {
338   guint n_streams, i;
339
340   n_streams = gst_rtsp_media_n_streams (media->media);
341   for (i = 0; i < n_streams; i++) {
342     GstRTSPSessionStream *sstream;
343     GstRTSPTransport *tr;
344
345     /* get the stream as configured in the session */
346     sstream = gst_rtsp_session_media_get_stream (media, i);
347     /* get the transport, if there is no transport configured, skip this stream */
348     if (!(tr = sstream->trans.transport))
349       continue;
350
351     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
352       /* for TCP, unlink the stream from the TCP connection of the client */
353       unlink_stream (client, sstream);
354     }
355   }
356 }
357
358 static gboolean
359 handle_teardown_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
360 {
361   GstRTSPSessionMedia *media;
362   GstRTSPMessage response = { 0 };
363   GstRTSPStatusCode code;
364
365   if (!session)
366     goto no_session;
367
368   /* get a handle to the configuration of the media in the session */
369   media = gst_rtsp_session_get_media (session, uri);
370   if (!media)
371     goto not_found;
372
373   /* unlink the all TCP callbacks */
374   unlink_session_streams (client, media);
375
376   gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
377
378   /* unmanage the media in the session, returns false if all media session
379    * are torn down. */
380   if (!gst_rtsp_session_release_media (session, media)) {
381     /* remove the session */
382     gst_rtsp_session_pool_remove (client->session_pool, session);
383   }
384   /* construct the response now */
385   code = GST_RTSP_STS_OK;
386   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
387
388   send_response (client, session, &response);
389
390   return FALSE;
391
392   /* ERRORS */
393 no_session:
394   {
395     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
396     return FALSE;
397   }
398 not_found:
399   {
400     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
401     return FALSE;
402   }
403 }
404
405 static gboolean
406 handle_pause_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
407 {
408   GstRTSPSessionMedia *media;
409   GstRTSPMessage response = { 0 };
410   GstRTSPStatusCode code;
411
412   if (!session)
413     goto no_session;
414
415   /* get a handle to the configuration of the media in the session */
416   media = gst_rtsp_session_get_media (session, uri);
417   if (!media)
418     goto not_found;
419
420   /* the session state must be playing or recording */
421   if (media->state != GST_RTSP_STATE_PLAYING &&
422       media->state != GST_RTSP_STATE_RECORDING)
423     goto invalid_state;
424
425   /* unlink the all TCP callbacks */
426   unlink_session_streams (client, media);
427
428   /* then pause sending */
429   gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
430
431   /* construct the response now */
432   code = GST_RTSP_STS_OK;
433   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
434
435   send_response (client, session, &response);
436
437   /* the state is now READY */
438   media->state = GST_RTSP_STATE_READY;
439
440   return FALSE;
441
442   /* ERRORS */
443 no_session:
444   {
445     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
446     return FALSE;
447   }
448 not_found:
449   {
450     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
451     return FALSE;
452   }
453 invalid_state:
454   {
455     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
456     return FALSE;
457   }
458 }
459
460 static gboolean
461 handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
462 {
463   GstRTSPSessionMedia *media;
464   GstRTSPMessage response = { 0 };
465   GstRTSPStatusCode code;
466   GString *rtpinfo;
467   guint n_streams, i;
468   guint timestamp, seqnum;
469   gchar *str;
470
471   if (!session)
472     goto no_session;
473
474   /* get a handle to the configuration of the media in the session */
475   media = gst_rtsp_session_get_media (session, uri);
476   if (!media)
477     goto not_found;
478
479   /* the session state must be playing or ready */
480   if (media->state != GST_RTSP_STATE_PLAYING &&
481       media->state != GST_RTSP_STATE_READY)
482     goto invalid_state;
483
484   /* grab RTPInfo from the payloaders now */
485   rtpinfo = g_string_new ("");
486
487   n_streams = gst_rtsp_media_n_streams (media->media);
488   for (i = 0; i < n_streams; i++) {
489     GstRTSPSessionStream *sstream;
490     GstRTSPMediaStream *stream;
491     GstRTSPTransport *tr;
492     gchar *uristr;
493
494     /* get the stream as configured in the session */
495     sstream = gst_rtsp_session_media_get_stream (media, i);
496     /* get the transport, if there is no transport configured, skip this stream */
497     if (!(tr = sstream->trans.transport))
498       continue;
499
500     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
501       /* for TCP, link the stream to the TCP connection of the client */
502       link_stream (client, sstream);
503     }
504
505     stream = sstream->media_stream;
506
507     g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL);
508     g_object_get (G_OBJECT (stream->payloader), "timestamp", &timestamp, NULL);
509
510     if (i > 0)
511       g_string_append (rtpinfo, ", ");
512
513     uristr = gst_rtsp_url_get_request_uri (uri);
514     g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u", uristr, i, seqnum, timestamp);
515     g_free (uristr);
516   }
517
518   /* construct the response now */
519   code = GST_RTSP_STS_OK;
520   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
521
522   /* add the RTP-Info header */
523   str = g_string_free (rtpinfo, FALSE);
524   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
525
526   /* add the range */
527   str = gst_rtsp_range_to_string (&media->media->range);
528   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
529
530   send_response (client, session, &response);
531
532   /* start playing after sending the request */
533   gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
534
535   media->state = GST_RTSP_STATE_PLAYING;
536
537   return FALSE;
538
539   /* ERRORS */
540 no_session:
541   {
542     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
543     return FALSE;
544   }
545 not_found:
546   {
547     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
548     return FALSE;
549   }
550 invalid_state:
551   {
552     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
553     return FALSE;
554   }
555 }
556
557 static gboolean
558 handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
559 {
560   GstRTSPResult res;
561   gchar *transport;
562   gchar **transports;
563   gboolean have_transport;
564   GstRTSPTransport *ct, *st;
565   gint i;
566   GstRTSPLowerTrans supported;
567   GstRTSPMessage response = { 0 };
568   GstRTSPStatusCode code;
569   GstRTSPSessionStream *stream;
570   gchar *trans_str, *pos;
571   guint streamid;
572   GstRTSPSessionMedia *media;
573   gboolean need_session;
574   GstRTSPUrl *url;
575
576   /* the uri contains the stream number we added in the SDP config, which is
577    * always /stream=%d so we need to strip that off 
578    * parse the stream we need to configure, look for the stream in the abspath
579    * first and then in the query. */
580   if (!(pos = strstr (uri->abspath, "/stream="))) {
581     if (!(pos = strstr (uri->query, "/stream=")))
582       goto bad_request;
583   }
584
585   /* we can mofify the parse uri in place */
586   *pos = '\0';
587
588   pos += strlen ("/stream=");
589   if (sscanf (pos, "%u", &streamid) != 1)
590     goto bad_request;
591
592   /* parse the transport */
593   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
594   if (res != GST_RTSP_OK)
595     goto no_transport;
596
597   transports = g_strsplit (transport, ",", 0);
598   gst_rtsp_transport_new (&ct);  
599
600   /* loop through the transports, try to parse */
601   have_transport = FALSE;
602   for (i = 0; transports[i]; i++) {
603
604     gst_rtsp_transport_init (ct);  
605     res = gst_rtsp_transport_parse (transports[i], ct);
606     if (res == GST_RTSP_OK) {
607       have_transport = TRUE;
608       break;
609     }
610   }
611   g_strfreev (transports);
612
613   /* we have not found anything usable, error out */
614   if (!have_transport) 
615     goto unsupported_transports;
616
617   /* we have a valid transport, check if we can handle it */
618   if (ct->trans != GST_RTSP_TRANS_RTP)
619     goto unsupported_transports;
620   if (ct->profile != GST_RTSP_PROFILE_AVP)
621     goto unsupported_transports;
622
623   supported = GST_RTSP_LOWER_TRANS_UDP |
624         GST_RTSP_LOWER_TRANS_UDP_MCAST |
625         GST_RTSP_LOWER_TRANS_TCP;
626   if (!(ct->lower_transport & supported))
627     goto unsupported_transports;
628
629   if (client->session_pool == NULL)
630     goto no_pool;
631
632   /* we have a valid transport now, set the destination of the client. */
633   g_free (ct->destination);
634   url = gst_rtsp_connection_get_url (client->connection);
635   ct->destination = g_strdup (url->host);
636
637   if (session) {
638     g_object_ref (session);
639     /* get a handle to the configuration of the media in the session, this can
640      * return NULL if this is a new url to manage in this session. */
641     media = gst_rtsp_session_get_media (session, uri);
642
643     need_session = FALSE;
644   }
645   else {
646     /* create a session if this fails we probably reached our session limit or
647      * something. */
648     if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
649       goto service_unavailable;
650
651     /* we need a new media configuration in this session */
652     media = NULL;
653
654     need_session = TRUE;
655   }
656
657   /* we have no media, find one and manage it */
658   if (media == NULL) {
659     GstRTSPMedia *m;
660
661     /* get a handle to the configuration of the media in the session */
662     if ((m = find_media (client, uri, request))) {
663       /* manage the media in our session now */
664       media = gst_rtsp_session_manage_media (session, uri, m);
665     }
666   }
667
668   /* if we stil have no media, error */
669   if (media == NULL)
670     goto not_found;
671
672   /* get a handle to the stream in the media */
673   if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
674     goto no_stream;
675
676   st = gst_rtsp_session_stream_set_transport (stream, ct);
677
678   /* serialize the server transport */
679   trans_str = gst_rtsp_transport_as_text (st);
680   gst_rtsp_transport_free (st);
681
682   /* construct the response now */
683   code = GST_RTSP_STS_OK;
684   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
685
686   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
687   g_free (trans_str);
688
689   send_response (client, session, &response);
690
691   /* update the state */
692   switch (media->state) {
693     case GST_RTSP_STATE_PLAYING:
694     case GST_RTSP_STATE_RECORDING:
695     case GST_RTSP_STATE_READY:
696       /* no state change */
697       break;
698     default:
699       media->state = GST_RTSP_STATE_READY;
700       break;
701   }
702   g_object_unref (session);
703
704   return TRUE;
705
706   /* ERRORS */
707 bad_request:
708   {
709     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
710     return FALSE;
711   }
712 not_found:
713   {
714     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
715     g_object_unref (session);
716     return FALSE;
717   }
718 no_stream:
719   {
720     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
721     g_object_unref (media);
722     g_object_unref (session);
723     return FALSE;
724   }
725 no_transport:
726   {
727     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
728     return FALSE;
729   }
730 unsupported_transports:
731   {
732     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
733     gst_rtsp_transport_free (ct);  
734     return FALSE;
735   }
736 no_pool:
737   {
738     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
739     return FALSE;
740   }
741 service_unavailable:
742   {
743     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
744     return FALSE;
745   }
746 }
747
748 /* for the describe we must generate an SDP */
749 static gboolean
750 handle_describe_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
751 {
752   GstRTSPMessage response = { 0 };
753   GstRTSPResult res;
754   GstSDPMessage *sdp;
755   guint i;
756   gchar *str;
757   GstRTSPMedia *media;
758
759   /* check what kind of format is accepted, we don't really do anything with it
760    * and always return SDP for now. */
761   for (i = 0; i++; ) {
762     gchar *accept;
763
764     res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
765     if (res == GST_RTSP_ENOTIMPL)
766       break;
767
768     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
769       break;
770   }
771
772   /* find the media object for the uri */
773   if (!(media = find_media (client, uri, request)))
774     goto no_media;
775
776   /* create an SDP for the media object */
777   if (!(sdp = gst_rtsp_sdp_from_media (media)))
778     goto no_sdp;
779
780   g_object_unref (media);
781
782   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
783         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
784
785   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "application/sdp");
786
787   /* content base for some clients that might screw up creating the setup uri */
788   str = g_strdup_printf ("rtsp://%s:%u%s/", uri->host, uri->port, uri->abspath);
789   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE, str);
790   g_free (str);
791
792   /* add SDP to the response body */
793   str = gst_sdp_message_as_text (sdp);
794   gst_rtsp_message_take_body (&response, (guint8 *)str, strlen (str));
795   gst_sdp_message_free (sdp);
796
797   send_response (client, session, &response);
798
799   return TRUE;
800
801   /* ERRORS */
802 no_media:
803   {
804     /* error reply is already sent */
805     return FALSE;
806   }
807 no_sdp:
808   {
809     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
810     g_object_unref (media);
811     return FALSE;
812   }
813 }
814
815 static void
816 handle_options_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
817 {
818   GstRTSPMessage response = { 0 };
819   GstRTSPMethod options;
820   gchar *str;
821
822   options = GST_RTSP_DESCRIBE |
823             GST_RTSP_OPTIONS |
824     //        GST_RTSP_PAUSE |
825             GST_RTSP_PLAY |
826             GST_RTSP_SETUP |
827             GST_RTSP_TEARDOWN;
828
829   str = gst_rtsp_options_as_text (options);
830
831   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
832         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
833
834   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
835   g_free (str);
836
837   send_response (client, session, &response);
838 }
839
840 /* remove duplicate and trailing '/' */
841 static void
842 santize_uri (GstRTSPUrl *uri)
843 {
844   gint i, len;
845   gchar *s, *d;
846   gboolean have_slash, prev_slash;
847
848   s = d = uri->abspath;
849   len = strlen (uri->abspath);
850
851   prev_slash = FALSE;
852
853   for (i = 0; i < len; i++) {
854     have_slash = s[i] == '/';
855     *d = s[i];
856     if (!have_slash || !prev_slash)
857       d++;
858     prev_slash = have_slash;
859   }
860   len = d - uri->abspath;
861   /* don't remove the first slash if that's the only thing left */
862   if (len > 1 && *(d-1) == '/')
863     d--;
864   *d = '\0';
865 }
866
867 static void
868 client_session_finalized (GstRTSPClient *client, GstRTSPSession *session)
869 {
870   g_message ("session %p finalized", session);
871
872   client->sessions = g_list_remove (client->sessions, session);
873   if (client->sessions == NULL) {
874     g_message ("all sessions finalized");
875     g_source_destroy ((GSource*)client->watch);
876   }
877 }
878
879 static void
880 client_watch_session (GstRTSPClient *client, GstRTSPSession *session)
881 {
882   GList *walk;
883
884   for (walk = client->sessions; walk; walk = g_list_next (walk)) {
885     GstRTSPSession *msession = (GstRTSPSession *) walk->data;
886
887     /* we already know about this session */
888     if (msession == session)
889       return;
890   }
891
892   g_message ("watching session %p", session);
893
894   g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized, client);
895   client->sessions = g_list_prepend (client->sessions, session);
896 }
897
898 static void
899 handle_request (GstRTSPClient *client, GstRTSPMessage *request)
900 {
901   GstRTSPMethod method;
902   const gchar *uristr;
903   GstRTSPUrl *uri;
904   GstRTSPVersion version;
905   GstRTSPResult res;
906   GstRTSPSession *session;
907   gchar *sessid;
908
909 #ifdef DEBUG
910   gst_rtsp_message_dump (request);
911 #endif
912
913   g_message ("client %p: received a request", client);
914
915   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
916
917   if (version != GST_RTSP_VERSION_1_0) {
918     /* we can only handle 1.0 requests */
919     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED, request);
920     return;
921   }
922
923   /* we always try to parse the url first */
924   if ((res = gst_rtsp_url_parse (uristr, &uri)) != GST_RTSP_OK) {
925     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
926     return;
927   }
928
929   /* sanitize the uri */
930   santize_uri (uri);
931
932   /* get the session if there is any */
933   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
934   if (res == GST_RTSP_OK) {
935     if (client->session_pool == NULL)
936       goto no_pool;
937
938     /* we had a session in the request, find it again */
939     if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
940       goto session_not_found;
941
942     client_watch_session (client, session);
943   }
944   else
945     session = NULL;
946
947   /* now see what is asked and dispatch to a dedicated handler */
948   switch (method) {
949     case GST_RTSP_OPTIONS:
950       handle_options_request (client, uri, session, request);
951       break;
952     case GST_RTSP_DESCRIBE:
953       handle_describe_request (client, uri, session, request);
954       break;
955     case GST_RTSP_SETUP:
956       handle_setup_request (client, uri, session, request);
957       break;
958     case GST_RTSP_PLAY:
959       handle_play_request (client, uri, session, request);
960       break;
961     case GST_RTSP_PAUSE:
962       handle_pause_request (client, uri, session, request);
963       break;
964     case GST_RTSP_TEARDOWN:
965       handle_teardown_request (client, uri, session, request);
966       break;
967     case GST_RTSP_ANNOUNCE:
968     case GST_RTSP_GET_PARAMETER:
969     case GST_RTSP_RECORD:
970     case GST_RTSP_REDIRECT:
971     case GST_RTSP_SET_PARAMETER:
972       send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
973       break;
974     case GST_RTSP_INVALID:
975     default:
976       send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
977       break;
978   }
979   if (session)
980     g_object_unref (session);
981
982   gst_rtsp_url_free (uri);
983   return;
984
985   /* ERRORS */
986 no_pool:
987   {
988     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
989     return;
990   }
991 session_not_found:
992   {
993     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
994     return;
995   }
996 }
997
998 static void
999 handle_data (GstRTSPClient *client, GstRTSPMessage *message)
1000 {
1001   GstRTSPResult res;
1002   guint8 channel;
1003   GList *walk;
1004   guint8 *data;
1005   guint size;
1006   GstBuffer *buffer;
1007
1008   /* find the stream for this message */ 
1009   res = gst_rtsp_message_parse_data (message, &channel);
1010   if (res != GST_RTSP_OK)
1011     return;
1012
1013   gst_rtsp_message_steal_body (message, &data, &size);
1014
1015   buffer = gst_buffer_new ();
1016   GST_BUFFER_DATA (buffer) = data;
1017   GST_BUFFER_MALLOCDATA (buffer) = data;
1018   GST_BUFFER_SIZE (buffer) = size;
1019
1020   for (walk = client->streams; walk; walk = g_list_next (walk)) {
1021     GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
1022     GstRTSPMediaStream *mstream;
1023     GstRTSPTransport *tr;
1024
1025     /* get the transport, if there is no transport configured, skip this stream */
1026     if (!(tr = stream->trans.transport))
1027       continue;
1028
1029     /* we also need a media stream */
1030     if (!(mstream = stream->media_stream))
1031       continue;
1032
1033     /* check for TCP transport */
1034     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1035       /* dispatch to the stream based on the channel number */
1036       if (tr->interleaved.min == channel) {
1037         gst_rtsp_media_stream_rtp (mstream, buffer);
1038       } else if (tr->interleaved.max == channel) {
1039         gst_rtsp_media_stream_rtcp (mstream, buffer);
1040       }
1041     }
1042   }
1043   gst_buffer_unref (buffer);
1044 }
1045
1046 /**
1047  * gst_rtsp_client_set_session_pool:
1048  * @client: a #GstRTSPClient
1049  * @pool: a #GstRTSPSessionPool
1050  *
1051  * Set @pool as the sessionpool for @client which it will use to find
1052  * or allocate sessions. the sessionpool is usually inherited from the server
1053  * that created the client but can be overridden later.
1054  */
1055 void
1056 gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool)
1057 {
1058   GstRTSPSessionPool *old;
1059
1060   old = client->session_pool;
1061   if (old != pool) {
1062     if (pool)
1063       g_object_ref (pool);
1064     client->session_pool = pool;
1065     if (old)
1066       g_object_unref (old);
1067   }
1068 }
1069
1070 /**
1071  * gst_rtsp_client_get_session_pool:
1072  * @client: a #GstRTSPClient
1073  *
1074  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1075  *
1076  * Returns: a #GstRTSPSessionPool, unref after usage.
1077  */
1078 GstRTSPSessionPool *
1079 gst_rtsp_client_get_session_pool (GstRTSPClient *client)
1080 {
1081   GstRTSPSessionPool *result;
1082
1083   if ((result = client->session_pool))
1084     g_object_ref (result);
1085
1086   return result;
1087 }
1088
1089 /**
1090  * gst_rtsp_client_set_media_mapping:
1091  * @client: a #GstRTSPClient
1092  * @mapping: a #GstRTSPMediaMapping
1093  *
1094  * Set @mapping as the media mapping for @client which it will use to map urls
1095  * to media streams. These mapping is usually inherited from the server that
1096  * created the client but can be overriden later.
1097  */
1098 void
1099 gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *mapping)
1100 {
1101   GstRTSPMediaMapping *old;
1102
1103   old = client->media_mapping;
1104
1105   if (old != mapping) {
1106     if (mapping)
1107       g_object_ref (mapping);
1108     client->media_mapping = mapping;
1109     if (old)
1110       g_object_unref (old);
1111   }
1112 }
1113
1114 /**
1115  * gst_rtsp_client_get_media_mapping:
1116  * @client: a #GstRTSPClient
1117  *
1118  * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1119  *
1120  * Returns: a #GstRTSPMediaMapping, unref after usage.
1121  */
1122 GstRTSPMediaMapping *
1123 gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
1124 {
1125   GstRTSPMediaMapping *result;
1126
1127   if ((result = client->media_mapping))
1128     g_object_ref (result);
1129
1130   return result;
1131 }
1132
1133 static GstRTSPResult
1134 message_received (GstRTSPWatch *watch, GstRTSPMessage *message, gpointer user_data)
1135 {
1136   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1137
1138   switch (message->type) {
1139     case GST_RTSP_MESSAGE_REQUEST:
1140       handle_request (client, message);
1141       break;
1142     case GST_RTSP_MESSAGE_RESPONSE:
1143       break;
1144     case GST_RTSP_MESSAGE_DATA:
1145       handle_data (client, message);
1146       break;
1147     default:
1148       break;
1149   }
1150   return GST_RTSP_OK;
1151 }
1152
1153 static GstRTSPResult
1154 message_sent (GstRTSPWatch *watch, guint cseq, gpointer user_data)
1155 {
1156   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1157
1158   g_message ("client %p: sent a message with cseq %d", client, cseq);
1159
1160   return GST_RTSP_OK;
1161 }
1162
1163 static GstRTSPResult
1164 closed (GstRTSPWatch *watch, gpointer user_data)
1165 {
1166   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1167   const gchar *tunnelid;
1168
1169   g_message ("client %p: connection closed", client);
1170
1171   if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1172     g_mutex_lock (tunnels_lock);
1173     g_hash_table_remove (tunnels, tunnelid);
1174     g_mutex_unlock (tunnels_lock);
1175   }
1176
1177   /* remove all streams that are streaming over this client connection */
1178   unlink_streams (client);
1179
1180   return GST_RTSP_OK;
1181 }
1182
1183 static GstRTSPResult
1184 error (GstRTSPWatch *watch, GstRTSPResult result, gpointer user_data)
1185 {
1186   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1187   gchar *str;
1188
1189   str = gst_rtsp_strresult (result);
1190   g_message ("client %p: received an error %s", client, str);
1191   g_free (str);
1192
1193   return GST_RTSP_OK;
1194 }
1195
1196 static GstRTSPStatusCode
1197 tunnel_start (GstRTSPWatch *watch, gpointer user_data)
1198 {
1199   GstRTSPClient *client;
1200   const gchar *tunnelid;
1201
1202   client = GST_RTSP_CLIENT (user_data);
1203
1204   g_message ("client %p: tunnel start", client);
1205
1206   /* store client in the pending tunnels */
1207   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1208
1209   g_message ("client %p: inserting %s", client, tunnelid);
1210
1211   /* we can't have two clients connecting with the same tunnelid */
1212   g_mutex_lock (tunnels_lock);
1213   if (g_hash_table_lookup (tunnels, tunnelid))
1214     goto tunnel_existed;
1215
1216   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1217   g_mutex_unlock (tunnels_lock);
1218
1219   return GST_RTSP_STS_OK;
1220
1221   /* ERRORS */
1222 tunnel_existed:
1223   {
1224     g_mutex_unlock (tunnels_lock);
1225     g_message ("client %p: tunnel session %s existed", client, tunnelid);
1226     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1227   }
1228 }
1229
1230 static GstRTSPResult
1231 tunnel_complete (GstRTSPWatch *watch, gpointer user_data)
1232 {
1233   const gchar *tunnelid;
1234   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1235   GstRTSPClient *oclient;
1236
1237   g_message ("client %p: tunnel complete", client);
1238
1239   /* find previous tunnel */
1240   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1241
1242   g_mutex_lock (tunnels_lock);
1243   if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1244     goto no_tunnel;
1245
1246   /* remove the old client from the table. ref before because removing it will
1247    * remove the ref to it. */
1248   g_object_ref (oclient);
1249   g_hash_table_remove (tunnels, tunnelid);
1250   g_mutex_unlock (tunnels_lock);
1251
1252   g_message ("client %p: found tunnel %p", client, oclient);
1253
1254   /* merge the tunnels into the first client */
1255   gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1256   gst_rtsp_watch_reset (oclient->watch);
1257   g_object_unref (oclient);
1258
1259   /* we don't need this watch anymore */
1260   g_source_remove (client->watchid);
1261
1262   return GST_RTSP_OK;
1263
1264   /* ERRORS */
1265 no_tunnel:
1266   {
1267     g_mutex_unlock (tunnels_lock);
1268     g_message ("client %p: tunnel session %s not found", client, tunnelid);
1269     return GST_RTSP_OK;
1270   }
1271 }
1272
1273 static GstRTSPWatchFuncs watch_funcs = {
1274   message_received,
1275   message_sent,
1276   closed,
1277   error,
1278   tunnel_start,
1279   tunnel_complete
1280 };
1281
1282 /**
1283  * gst_rtsp_client_attach:
1284  * @client: a #GstRTSPClient
1285  * @channel: a #GIOChannel
1286  *
1287  * Accept a new connection for @client on the socket in @source. 
1288  *
1289  * This function should be called when the client properties and urls are fully
1290  * configured and the client is ready to start.
1291  *
1292  * Returns: %TRUE if the client could be accepted.
1293  */
1294 gboolean
1295 gst_rtsp_client_accept (GstRTSPClient *client, GIOChannel *channel)
1296 {
1297   int sock;
1298   GstRTSPConnection *conn;
1299   GstRTSPResult res;
1300   GSource *source;
1301   GMainContext *context;
1302   GstRTSPUrl *url;
1303
1304   /* a new client connected. */
1305   sock = g_io_channel_unix_get_fd (channel);
1306
1307   GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1308
1309   url = gst_rtsp_connection_get_url (conn);
1310   g_message ("added new client %p ip %s:%d", client,
1311                 url->host, url->port);
1312
1313   client->connection = conn;
1314
1315   /* create watch for the connection and attach */
1316   client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1317                   g_object_ref (client), g_object_unref);
1318
1319   /* find the context to add the watch */
1320   if ((source = g_main_current_source ()))
1321     context = g_source_get_context (source);
1322   else
1323     context = NULL;
1324
1325   g_message ("attaching to context %p", context);
1326
1327   client->watchid = gst_rtsp_watch_attach (client->watch, context);
1328   gst_rtsp_watch_unref (client->watch);
1329
1330   return TRUE;
1331
1332   /* ERRORS */
1333 accept_failed:
1334   {
1335     gchar *str = gst_rtsp_strresult (res);
1336
1337     g_error ("Could not accept client on server socket %d: %s",
1338             sock, str);
1339     g_free (str);
1340     return FALSE;
1341   }
1342 }