Fix rtsp client refcount management in TCP mode.
[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, NULL, NULL);
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   /* remove the session from the watched sessions */
377   g_object_weak_unref (G_OBJECT (session), (GWeakNotify) client_session_finalized, client);
378   client->sessions = g_list_remove (client->sessions, session);
379
380   gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
381
382   /* unmanage the media in the session, returns false if all media session
383    * are torn down. */
384   if (!gst_rtsp_session_release_media (session, media)) {
385     /* remove the session */
386     gst_rtsp_session_pool_remove (client->session_pool, session);
387   }
388   /* construct the response now */
389   code = GST_RTSP_STS_OK;
390   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
391
392   send_response (client, session, &response);
393
394   return FALSE;
395
396   /* ERRORS */
397 no_session:
398   {
399     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
400     return FALSE;
401   }
402 not_found:
403   {
404     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
405     return FALSE;
406   }
407 }
408
409 static gboolean
410 handle_pause_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
411 {
412   GstRTSPSessionMedia *media;
413   GstRTSPMessage response = { 0 };
414   GstRTSPStatusCode code;
415
416   if (!session)
417     goto no_session;
418
419   /* get a handle to the configuration of the media in the session */
420   media = gst_rtsp_session_get_media (session, uri);
421   if (!media)
422     goto not_found;
423
424   /* the session state must be playing or recording */
425   if (media->state != GST_RTSP_STATE_PLAYING &&
426       media->state != GST_RTSP_STATE_RECORDING)
427     goto invalid_state;
428
429   /* unlink the all TCP callbacks */
430   unlink_session_streams (client, media);
431
432   /* then pause sending */
433   gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
434
435   /* construct the response now */
436   code = GST_RTSP_STS_OK;
437   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
438
439   send_response (client, session, &response);
440
441   /* the state is now READY */
442   media->state = GST_RTSP_STATE_READY;
443
444   return FALSE;
445
446   /* ERRORS */
447 no_session:
448   {
449     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
450     return FALSE;
451   }
452 not_found:
453   {
454     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
455     return FALSE;
456   }
457 invalid_state:
458   {
459     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
460     return FALSE;
461   }
462 }
463
464 static gboolean
465 handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
466 {
467   GstRTSPSessionMedia *media;
468   GstRTSPMessage response = { 0 };
469   GstRTSPStatusCode code;
470   GString *rtpinfo;
471   guint n_streams, i;
472   guint timestamp, seqnum;
473   gchar *str;
474   GstRTSPTimeRange *range;
475   GstRTSPResult res;
476
477   if (!session)
478     goto no_session;
479
480   /* get a handle to the configuration of the media in the session */
481   media = gst_rtsp_session_get_media (session, uri);
482   if (!media)
483     goto not_found;
484
485   /* the session state must be playing or ready */
486   if (media->state != GST_RTSP_STATE_PLAYING &&
487       media->state != GST_RTSP_STATE_READY)
488     goto invalid_state;
489
490   /* parse the range header if we have one */
491   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_RANGE, &str, 0);
492   if (res == GST_RTSP_OK) {
493     if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
494       /* we have a range, seek to the position */
495       gst_rtsp_media_seek (media->media, range);
496       gst_rtsp_range_free (range);
497     }
498   }
499
500   /* grab RTPInfo from the payloaders now */
501   rtpinfo = g_string_new ("");
502
503   n_streams = gst_rtsp_media_n_streams (media->media);
504   for (i = 0; i < n_streams; i++) {
505     GstRTSPSessionStream *sstream;
506     GstRTSPMediaStream *stream;
507     GstRTSPTransport *tr;
508     gchar *uristr;
509
510     /* get the stream as configured in the session */
511     sstream = gst_rtsp_session_media_get_stream (media, i);
512     /* get the transport, if there is no transport configured, skip this stream */
513     if (!(tr = sstream->trans.transport))
514       continue;
515
516     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
517       /* for TCP, link the stream to the TCP connection of the client */
518       link_stream (client, sstream);
519     }
520
521     stream = sstream->media_stream;
522
523     g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL);
524     g_object_get (G_OBJECT (stream->payloader), "timestamp", &timestamp, NULL);
525
526     if (i > 0)
527       g_string_append (rtpinfo, ", ");
528
529     uristr = gst_rtsp_url_get_request_uri (uri);
530     g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u", uristr, i, seqnum, timestamp);
531     g_free (uristr);
532   }
533
534   /* construct the response now */
535   code = GST_RTSP_STS_OK;
536   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
537
538   /* add the RTP-Info header */
539   str = g_string_free (rtpinfo, FALSE);
540   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
541
542   /* add the range */
543   str = gst_rtsp_range_to_string (&media->media->range);
544   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
545
546   send_response (client, session, &response);
547
548   /* start playing after sending the request */
549   gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
550
551   media->state = GST_RTSP_STATE_PLAYING;
552
553   return FALSE;
554
555   /* ERRORS */
556 no_session:
557   {
558     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
559     return FALSE;
560   }
561 not_found:
562   {
563     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
564     return FALSE;
565   }
566 invalid_state:
567   {
568     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
569     return FALSE;
570   }
571 }
572
573 static gboolean
574 handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
575 {
576   GstRTSPResult res;
577   gchar *transport;
578   gchar **transports;
579   gboolean have_transport;
580   GstRTSPTransport *ct, *st;
581   gint i;
582   GstRTSPLowerTrans supported;
583   GstRTSPMessage response = { 0 };
584   GstRTSPStatusCode code;
585   GstRTSPSessionStream *stream;
586   gchar *trans_str, *pos;
587   guint streamid;
588   GstRTSPSessionMedia *media;
589   gboolean need_session;
590   GstRTSPUrl *url;
591
592   /* the uri contains the stream number we added in the SDP config, which is
593    * always /stream=%d so we need to strip that off 
594    * parse the stream we need to configure, look for the stream in the abspath
595    * first and then in the query. */
596   if (!(pos = strstr (uri->abspath, "/stream="))) {
597     if (!(pos = strstr (uri->query, "/stream=")))
598       goto bad_request;
599   }
600
601   /* we can mofify the parse uri in place */
602   *pos = '\0';
603
604   pos += strlen ("/stream=");
605   if (sscanf (pos, "%u", &streamid) != 1)
606     goto bad_request;
607
608   /* parse the transport */
609   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
610   if (res != GST_RTSP_OK)
611     goto no_transport;
612
613   transports = g_strsplit (transport, ",", 0);
614   gst_rtsp_transport_new (&ct);  
615
616   /* loop through the transports, try to parse */
617   have_transport = FALSE;
618   for (i = 0; transports[i]; i++) {
619
620     gst_rtsp_transport_init (ct);  
621     res = gst_rtsp_transport_parse (transports[i], ct);
622     if (res == GST_RTSP_OK) {
623       have_transport = TRUE;
624       break;
625     }
626   }
627   g_strfreev (transports);
628
629   /* we have not found anything usable, error out */
630   if (!have_transport) 
631     goto unsupported_transports;
632
633   /* we have a valid transport, check if we can handle it */
634   if (ct->trans != GST_RTSP_TRANS_RTP)
635     goto unsupported_transports;
636   if (ct->profile != GST_RTSP_PROFILE_AVP)
637     goto unsupported_transports;
638
639   supported = GST_RTSP_LOWER_TRANS_UDP |
640         GST_RTSP_LOWER_TRANS_UDP_MCAST |
641         GST_RTSP_LOWER_TRANS_TCP;
642   if (!(ct->lower_transport & supported))
643     goto unsupported_transports;
644
645   if (client->session_pool == NULL)
646     goto no_pool;
647
648   /* we have a valid transport now, set the destination of the client. */
649   g_free (ct->destination);
650   url = gst_rtsp_connection_get_url (client->connection);
651   ct->destination = g_strdup (url->host);
652
653   if (session) {
654     g_object_ref (session);
655     /* get a handle to the configuration of the media in the session, this can
656      * return NULL if this is a new url to manage in this session. */
657     media = gst_rtsp_session_get_media (session, uri);
658
659     need_session = FALSE;
660   }
661   else {
662     /* create a session if this fails we probably reached our session limit or
663      * something. */
664     if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
665       goto service_unavailable;
666
667     /* we need a new media configuration in this session */
668     media = NULL;
669
670     need_session = TRUE;
671   }
672
673   /* we have no media, find one and manage it */
674   if (media == NULL) {
675     GstRTSPMedia *m;
676
677     /* get a handle to the configuration of the media in the session */
678     if ((m = find_media (client, uri, request))) {
679       /* manage the media in our session now */
680       media = gst_rtsp_session_manage_media (session, uri, m);
681     }
682   }
683
684   /* if we stil have no media, error */
685   if (media == NULL)
686     goto not_found;
687
688   /* get a handle to the stream in the media */
689   if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
690     goto no_stream;
691
692   st = gst_rtsp_session_stream_set_transport (stream, ct);
693
694   /* serialize the server transport */
695   trans_str = gst_rtsp_transport_as_text (st);
696   gst_rtsp_transport_free (st);
697
698   /* construct the response now */
699   code = GST_RTSP_STS_OK;
700   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
701
702   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
703   g_free (trans_str);
704
705   send_response (client, session, &response);
706
707   /* update the state */
708   switch (media->state) {
709     case GST_RTSP_STATE_PLAYING:
710     case GST_RTSP_STATE_RECORDING:
711     case GST_RTSP_STATE_READY:
712       /* no state change */
713       break;
714     default:
715       media->state = GST_RTSP_STATE_READY;
716       break;
717   }
718   g_object_unref (session);
719
720   return TRUE;
721
722   /* ERRORS */
723 bad_request:
724   {
725     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
726     return FALSE;
727   }
728 not_found:
729   {
730     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
731     g_object_unref (session);
732     return FALSE;
733   }
734 no_stream:
735   {
736     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
737     g_object_unref (media);
738     g_object_unref (session);
739     return FALSE;
740   }
741 no_transport:
742   {
743     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
744     return FALSE;
745   }
746 unsupported_transports:
747   {
748     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
749     gst_rtsp_transport_free (ct);  
750     return FALSE;
751   }
752 no_pool:
753   {
754     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
755     return FALSE;
756   }
757 service_unavailable:
758   {
759     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
760     return FALSE;
761   }
762 }
763
764 /* for the describe we must generate an SDP */
765 static gboolean
766 handle_describe_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
767 {
768   GstRTSPMessage response = { 0 };
769   GstRTSPResult res;
770   GstSDPMessage *sdp;
771   guint i;
772   gchar *str;
773   GstRTSPMedia *media;
774
775   /* check what kind of format is accepted, we don't really do anything with it
776    * and always return SDP for now. */
777   for (i = 0; i++; ) {
778     gchar *accept;
779
780     res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
781     if (res == GST_RTSP_ENOTIMPL)
782       break;
783
784     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
785       break;
786   }
787
788   /* find the media object for the uri */
789   if (!(media = find_media (client, uri, request)))
790     goto no_media;
791
792   /* create an SDP for the media object */
793   if (!(sdp = gst_rtsp_sdp_from_media (media)))
794     goto no_sdp;
795
796   g_object_unref (media);
797
798   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
799         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
800
801   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "application/sdp");
802
803   /* content base for some clients that might screw up creating the setup uri */
804   str = g_strdup_printf ("rtsp://%s:%u%s/", uri->host, uri->port, uri->abspath);
805   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE, str);
806   g_free (str);
807
808   /* add SDP to the response body */
809   str = gst_sdp_message_as_text (sdp);
810   gst_rtsp_message_take_body (&response, (guint8 *)str, strlen (str));
811   gst_sdp_message_free (sdp);
812
813   send_response (client, session, &response);
814
815   return TRUE;
816
817   /* ERRORS */
818 no_media:
819   {
820     /* error reply is already sent */
821     return FALSE;
822   }
823 no_sdp:
824   {
825     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
826     g_object_unref (media);
827     return FALSE;
828   }
829 }
830
831 static void
832 handle_options_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
833 {
834   GstRTSPMessage response = { 0 };
835   GstRTSPMethod options;
836   gchar *str;
837
838   options = GST_RTSP_DESCRIBE |
839             GST_RTSP_OPTIONS |
840             GST_RTSP_PAUSE |
841             GST_RTSP_PLAY |
842             GST_RTSP_SETUP |
843             GST_RTSP_TEARDOWN;
844
845   str = gst_rtsp_options_as_text (options);
846
847   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
848         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
849
850   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
851   g_free (str);
852
853   send_response (client, session, &response);
854 }
855
856 /* remove duplicate and trailing '/' */
857 static void
858 santize_uri (GstRTSPUrl *uri)
859 {
860   gint i, len;
861   gchar *s, *d;
862   gboolean have_slash, prev_slash;
863
864   s = d = uri->abspath;
865   len = strlen (uri->abspath);
866
867   prev_slash = FALSE;
868
869   for (i = 0; i < len; i++) {
870     have_slash = s[i] == '/';
871     *d = s[i];
872     if (!have_slash || !prev_slash)
873       d++;
874     prev_slash = have_slash;
875   }
876   len = d - uri->abspath;
877   /* don't remove the first slash if that's the only thing left */
878   if (len > 1 && *(d-1) == '/')
879     d--;
880   *d = '\0';
881 }
882
883 static void
884 client_session_finalized (GstRTSPClient *client, GstRTSPSession *session)
885 {
886   if (!(client->sessions = g_list_remove (client->sessions, session))) {
887     g_message ("all sessions finalized, close the connection");
888     g_source_destroy ((GSource*)client->watch);
889   }
890 }
891
892 static void
893 client_watch_session (GstRTSPClient *client, GstRTSPSession *session)
894 {
895   GList *walk;
896
897   for (walk = client->sessions; walk; walk = g_list_next (walk)) {
898     GstRTSPSession *msession = (GstRTSPSession *) walk->data;
899
900     /* we already know about this session */
901     if (msession == session)
902       return;
903   }
904
905   g_message ("watching session %p", session);
906
907   g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized, client);
908   client->sessions = g_list_prepend (client->sessions, session);
909 }
910
911 static void
912 handle_request (GstRTSPClient *client, GstRTSPMessage *request)
913 {
914   GstRTSPMethod method;
915   const gchar *uristr;
916   GstRTSPUrl *uri;
917   GstRTSPVersion version;
918   GstRTSPResult res;
919   GstRTSPSession *session;
920   gchar *sessid;
921
922 #ifdef DEBUG
923   gst_rtsp_message_dump (request);
924 #endif
925
926   g_message ("client %p: received a request", client);
927
928   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
929
930   if (version != GST_RTSP_VERSION_1_0) {
931     /* we can only handle 1.0 requests */
932     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED, request);
933     return;
934   }
935
936   /* we always try to parse the url first */
937   if ((res = gst_rtsp_url_parse (uristr, &uri)) != GST_RTSP_OK) {
938     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
939     return;
940   }
941
942   /* sanitize the uri */
943   santize_uri (uri);
944
945   /* get the session if there is any */
946   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
947   if (res == GST_RTSP_OK) {
948     if (client->session_pool == NULL)
949       goto no_pool;
950
951     /* we had a session in the request, find it again */
952     if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
953       goto session_not_found;
954
955     /* we add the session to the client list of watched sessions. When a session
956      * disappears because it times out, we will be notified. If all sessions are
957      * gone, we will close the connection */
958     client_watch_session (client, session);
959   }
960   else
961     session = NULL;
962
963   /* now see what is asked and dispatch to a dedicated handler */
964   switch (method) {
965     case GST_RTSP_OPTIONS:
966       handle_options_request (client, uri, session, request);
967       break;
968     case GST_RTSP_DESCRIBE:
969       handle_describe_request (client, uri, session, request);
970       break;
971     case GST_RTSP_SETUP:
972       handle_setup_request (client, uri, session, request);
973       break;
974     case GST_RTSP_PLAY:
975       handle_play_request (client, uri, session, request);
976       break;
977     case GST_RTSP_PAUSE:
978       handle_pause_request (client, uri, session, request);
979       break;
980     case GST_RTSP_TEARDOWN:
981       handle_teardown_request (client, uri, session, request);
982       break;
983     case GST_RTSP_ANNOUNCE:
984     case GST_RTSP_GET_PARAMETER:
985     case GST_RTSP_RECORD:
986     case GST_RTSP_REDIRECT:
987     case GST_RTSP_SET_PARAMETER:
988       send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
989       break;
990     case GST_RTSP_INVALID:
991     default:
992       send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
993       break;
994   }
995   if (session)
996     g_object_unref (session);
997
998   gst_rtsp_url_free (uri);
999   return;
1000
1001   /* ERRORS */
1002 no_pool:
1003   {
1004     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1005     return;
1006   }
1007 session_not_found:
1008   {
1009     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
1010     return;
1011   }
1012 }
1013
1014 static void
1015 handle_data (GstRTSPClient *client, GstRTSPMessage *message)
1016 {
1017   GstRTSPResult res;
1018   guint8 channel;
1019   GList *walk;
1020   guint8 *data;
1021   guint size;
1022   GstBuffer *buffer;
1023
1024   /* find the stream for this message */ 
1025   res = gst_rtsp_message_parse_data (message, &channel);
1026   if (res != GST_RTSP_OK)
1027     return;
1028
1029   gst_rtsp_message_steal_body (message, &data, &size);
1030
1031   buffer = gst_buffer_new ();
1032   GST_BUFFER_DATA (buffer) = data;
1033   GST_BUFFER_MALLOCDATA (buffer) = data;
1034   GST_BUFFER_SIZE (buffer) = size;
1035
1036   for (walk = client->streams; walk; walk = g_list_next (walk)) {
1037     GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
1038     GstRTSPMediaStream *mstream;
1039     GstRTSPTransport *tr;
1040
1041     /* get the transport, if there is no transport configured, skip this stream */
1042     if (!(tr = stream->trans.transport))
1043       continue;
1044
1045     /* we also need a media stream */
1046     if (!(mstream = stream->media_stream))
1047       continue;
1048
1049     /* check for TCP transport */
1050     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1051       /* dispatch to the stream based on the channel number */
1052       if (tr->interleaved.min == channel) {
1053         gst_rtsp_media_stream_rtp (mstream, buffer);
1054       } else if (tr->interleaved.max == channel) {
1055         gst_rtsp_media_stream_rtcp (mstream, buffer);
1056       }
1057     }
1058   }
1059   gst_buffer_unref (buffer);
1060 }
1061
1062 /**
1063  * gst_rtsp_client_set_session_pool:
1064  * @client: a #GstRTSPClient
1065  * @pool: a #GstRTSPSessionPool
1066  *
1067  * Set @pool as the sessionpool for @client which it will use to find
1068  * or allocate sessions. the sessionpool is usually inherited from the server
1069  * that created the client but can be overridden later.
1070  */
1071 void
1072 gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool)
1073 {
1074   GstRTSPSessionPool *old;
1075
1076   old = client->session_pool;
1077   if (old != pool) {
1078     if (pool)
1079       g_object_ref (pool);
1080     client->session_pool = pool;
1081     if (old)
1082       g_object_unref (old);
1083   }
1084 }
1085
1086 /**
1087  * gst_rtsp_client_get_session_pool:
1088  * @client: a #GstRTSPClient
1089  *
1090  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1091  *
1092  * Returns: a #GstRTSPSessionPool, unref after usage.
1093  */
1094 GstRTSPSessionPool *
1095 gst_rtsp_client_get_session_pool (GstRTSPClient *client)
1096 {
1097   GstRTSPSessionPool *result;
1098
1099   if ((result = client->session_pool))
1100     g_object_ref (result);
1101
1102   return result;
1103 }
1104
1105 /**
1106  * gst_rtsp_client_set_media_mapping:
1107  * @client: a #GstRTSPClient
1108  * @mapping: a #GstRTSPMediaMapping
1109  *
1110  * Set @mapping as the media mapping for @client which it will use to map urls
1111  * to media streams. These mapping is usually inherited from the server that
1112  * created the client but can be overriden later.
1113  */
1114 void
1115 gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *mapping)
1116 {
1117   GstRTSPMediaMapping *old;
1118
1119   old = client->media_mapping;
1120
1121   if (old != mapping) {
1122     if (mapping)
1123       g_object_ref (mapping);
1124     client->media_mapping = mapping;
1125     if (old)
1126       g_object_unref (old);
1127   }
1128 }
1129
1130 /**
1131  * gst_rtsp_client_get_media_mapping:
1132  * @client: a #GstRTSPClient
1133  *
1134  * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1135  *
1136  * Returns: a #GstRTSPMediaMapping, unref after usage.
1137  */
1138 GstRTSPMediaMapping *
1139 gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
1140 {
1141   GstRTSPMediaMapping *result;
1142
1143   if ((result = client->media_mapping))
1144     g_object_ref (result);
1145
1146   return result;
1147 }
1148
1149 static GstRTSPResult
1150 message_received (GstRTSPWatch *watch, GstRTSPMessage *message, gpointer user_data)
1151 {
1152   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1153
1154   switch (message->type) {
1155     case GST_RTSP_MESSAGE_REQUEST:
1156       handle_request (client, message);
1157       break;
1158     case GST_RTSP_MESSAGE_RESPONSE:
1159       break;
1160     case GST_RTSP_MESSAGE_DATA:
1161       handle_data (client, message);
1162       break;
1163     default:
1164       break;
1165   }
1166   return GST_RTSP_OK;
1167 }
1168
1169 static GstRTSPResult
1170 message_sent (GstRTSPWatch *watch, guint cseq, gpointer user_data)
1171 {
1172   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1173
1174   g_message ("client %p: sent a message with cseq %d", client, cseq);
1175
1176   return GST_RTSP_OK;
1177 }
1178
1179 static GstRTSPResult
1180 closed (GstRTSPWatch *watch, gpointer user_data)
1181 {
1182   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1183   const gchar *tunnelid;
1184
1185   g_message ("client %p: connection closed", client);
1186
1187   if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1188     g_mutex_lock (tunnels_lock);
1189     g_hash_table_remove (tunnels, tunnelid);
1190     g_mutex_unlock (tunnels_lock);
1191   }
1192
1193   /* remove all streams that are streaming over this client connection */
1194   unlink_streams (client);
1195
1196   return GST_RTSP_OK;
1197 }
1198
1199 static GstRTSPResult
1200 error (GstRTSPWatch *watch, GstRTSPResult result, gpointer user_data)
1201 {
1202   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1203   gchar *str;
1204
1205   str = gst_rtsp_strresult (result);
1206   g_message ("client %p: received an error %s", client, str);
1207   g_free (str);
1208
1209   return GST_RTSP_OK;
1210 }
1211
1212 static GstRTSPStatusCode
1213 tunnel_start (GstRTSPWatch *watch, gpointer user_data)
1214 {
1215   GstRTSPClient *client;
1216   const gchar *tunnelid;
1217
1218   client = GST_RTSP_CLIENT (user_data);
1219
1220   g_message ("client %p: tunnel start", client);
1221
1222   /* store client in the pending tunnels */
1223   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1224
1225   g_message ("client %p: inserting %s", client, tunnelid);
1226
1227   /* we can't have two clients connecting with the same tunnelid */
1228   g_mutex_lock (tunnels_lock);
1229   if (g_hash_table_lookup (tunnels, tunnelid))
1230     goto tunnel_existed;
1231
1232   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1233   g_mutex_unlock (tunnels_lock);
1234
1235   return GST_RTSP_STS_OK;
1236
1237   /* ERRORS */
1238 tunnel_existed:
1239   {
1240     g_mutex_unlock (tunnels_lock);
1241     g_message ("client %p: tunnel session %s existed", client, tunnelid);
1242     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1243   }
1244 }
1245
1246 static GstRTSPResult
1247 tunnel_complete (GstRTSPWatch *watch, gpointer user_data)
1248 {
1249   const gchar *tunnelid;
1250   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1251   GstRTSPClient *oclient;
1252
1253   g_message ("client %p: tunnel complete", client);
1254
1255   /* find previous tunnel */
1256   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1257
1258   g_mutex_lock (tunnels_lock);
1259   if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1260     goto no_tunnel;
1261
1262   /* remove the old client from the table. ref before because removing it will
1263    * remove the ref to it. */
1264   g_object_ref (oclient);
1265   g_hash_table_remove (tunnels, tunnelid);
1266   g_mutex_unlock (tunnels_lock);
1267
1268   g_message ("client %p: found tunnel %p", client, oclient);
1269
1270   /* merge the tunnels into the first client */
1271   gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1272   gst_rtsp_watch_reset (oclient->watch);
1273   g_object_unref (oclient);
1274
1275   /* we don't need this watch anymore */
1276   g_source_remove (client->watchid);
1277
1278   return GST_RTSP_OK;
1279
1280   /* ERRORS */
1281 no_tunnel:
1282   {
1283     g_mutex_unlock (tunnels_lock);
1284     g_message ("client %p: tunnel session %s not found", client, tunnelid);
1285     return GST_RTSP_OK;
1286   }
1287 }
1288
1289 static GstRTSPWatchFuncs watch_funcs = {
1290   message_received,
1291   message_sent,
1292   closed,
1293   error,
1294   tunnel_start,
1295   tunnel_complete
1296 };
1297
1298 /**
1299  * gst_rtsp_client_attach:
1300  * @client: a #GstRTSPClient
1301  * @channel: a #GIOChannel
1302  *
1303  * Accept a new connection for @client on the socket in @source. 
1304  *
1305  * This function should be called when the client properties and urls are fully
1306  * configured and the client is ready to start.
1307  *
1308  * Returns: %TRUE if the client could be accepted.
1309  */
1310 gboolean
1311 gst_rtsp_client_accept (GstRTSPClient *client, GIOChannel *channel)
1312 {
1313   int sock;
1314   GstRTSPConnection *conn;
1315   GstRTSPResult res;
1316   GSource *source;
1317   GMainContext *context;
1318   GstRTSPUrl *url;
1319
1320   /* a new client connected. */
1321   sock = g_io_channel_unix_get_fd (channel);
1322
1323   GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1324
1325   url = gst_rtsp_connection_get_url (conn);
1326   g_message ("added new client %p ip %s:%d", client,
1327                 url->host, url->port);
1328
1329   client->connection = conn;
1330
1331   /* create watch for the connection and attach */
1332   client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1333                   g_object_ref (client), g_object_unref);
1334
1335   /* find the context to add the watch */
1336   if ((source = g_main_current_source ()))
1337     context = g_source_get_context (source);
1338   else
1339     context = NULL;
1340
1341   g_message ("attaching to context %p", context);
1342
1343   client->watchid = gst_rtsp_watch_attach (client->watch, context);
1344   gst_rtsp_watch_unref (client->watch);
1345
1346   return TRUE;
1347
1348   /* ERRORS */
1349 accept_failed:
1350   {
1351     gchar *str = gst_rtsp_strresult (res);
1352
1353     g_error ("Could not accept client on server socket %d: %s",
1354             sock, str);
1355     g_free (str);
1356     return FALSE;
1357   }
1358 }