76450a20f1367c528ddf8382b351f6d9c6a506c2
[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 #include "rtsp-params.h"
25
26 /* temporary multicast address until it's configurable somewhere */
27 #define MCAST_ADDRESS "224.2.0.1"
28
29 static GMutex *tunnels_lock;
30 static GHashTable *tunnels;
31
32 enum
33 {
34   PROP_0,
35   PROP_SESSION_POOL,
36   PROP_MEDIA_MAPPING,
37   PROP_LAST
38 };
39
40 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
41 #define GST_CAT_DEFAULT rtsp_client_debug
42
43 static void gst_rtsp_client_get_property (GObject * object, guint propid,
44     GValue * value, GParamSpec * pspec);
45 static void gst_rtsp_client_set_property (GObject * object, guint propid,
46     const GValue * value, GParamSpec * pspec);
47 static void gst_rtsp_client_finalize (GObject * obj);
48
49 static void client_session_finalized (GstRTSPClient * client,
50     GstRTSPSession * session);
51 static void unlink_session_streams (GstRTSPClient * client,
52     GstRTSPSession * session, GstRTSPSessionMedia * media);
53
54 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
55
56 static void
57 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
58 {
59   GObjectClass *gobject_class;
60
61   gobject_class = G_OBJECT_CLASS (klass);
62
63   gobject_class->get_property = gst_rtsp_client_get_property;
64   gobject_class->set_property = gst_rtsp_client_set_property;
65   gobject_class->finalize = gst_rtsp_client_finalize;
66
67   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
68       g_param_spec_object ("session-pool", "Session Pool",
69           "The session pool to use for client session",
70           GST_TYPE_RTSP_SESSION_POOL,
71           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
72
73   g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
74       g_param_spec_object ("media-mapping", "Media Mapping",
75           "The media mapping to use for client session",
76           GST_TYPE_RTSP_MEDIA_MAPPING,
77           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
78
79   tunnels =
80       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
81   tunnels_lock = g_mutex_new ();
82
83   GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
84 }
85
86 static void
87 gst_rtsp_client_init (GstRTSPClient * client)
88 {
89 }
90
91 static void
92 client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
93 {
94   GList *medias;
95
96   /* unlink all media managed in this session */
97   for (medias = session->medias; medias; medias = g_list_next (medias)) {
98     unlink_session_streams (client, session,
99         (GstRTSPSessionMedia *) medias->data);
100   }
101 }
102
103 static void
104 client_cleanup_sessions (GstRTSPClient * client)
105 {
106   GList *sessions;
107
108   /* remove weak-ref from sessions */
109   for (sessions = client->sessions; sessions; sessions = g_list_next (sessions)) {
110     GstRTSPSession *session = (GstRTSPSession *) sessions->data;
111     g_object_weak_unref (G_OBJECT (session),
112         (GWeakNotify) client_session_finalized, client);
113     client_unlink_session (client, session);
114   }
115   g_list_free (client->sessions);
116   client->sessions = NULL;
117 }
118
119 /* A client is finalized when the connection is broken */
120 static void
121 gst_rtsp_client_finalize (GObject * obj)
122 {
123   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
124
125   GST_INFO ("finalize client %p", client);
126
127   client_cleanup_sessions (client);
128
129   gst_rtsp_connection_free (client->connection);
130   if (client->session_pool)
131     g_object_unref (client->session_pool);
132   if (client->media_mapping)
133     g_object_unref (client->media_mapping);
134
135   if (client->uri)
136     gst_rtsp_url_free (client->uri);
137   if (client->media)
138     g_object_unref (client->media);
139
140   g_free (client->server_ip);
141
142   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
143 }
144
145 static void
146 gst_rtsp_client_get_property (GObject * object, guint propid,
147     GValue * value, GParamSpec * pspec)
148 {
149   GstRTSPClient *client = GST_RTSP_CLIENT (object);
150
151   switch (propid) {
152     case PROP_SESSION_POOL:
153       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
154       break;
155     case PROP_MEDIA_MAPPING:
156       g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
160   }
161 }
162
163 static void
164 gst_rtsp_client_set_property (GObject * object, guint propid,
165     const GValue * value, GParamSpec * pspec)
166 {
167   GstRTSPClient *client = GST_RTSP_CLIENT (object);
168
169   switch (propid) {
170     case PROP_SESSION_POOL:
171       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
172       break;
173     case PROP_MEDIA_MAPPING:
174       gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
175       break;
176     default:
177       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
178   }
179 }
180
181 /**
182  * gst_rtsp_client_new:
183  *
184  * Create a new #GstRTSPClient instance.
185  *
186  * Returns: a new #GstRTSPClient
187  */
188 GstRTSPClient *
189 gst_rtsp_client_new (void)
190 {
191   GstRTSPClient *result;
192
193   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
194
195   return result;
196 }
197
198 static void
199 send_response (GstRTSPClient * client, GstRTSPSession * session,
200     GstRTSPMessage * response)
201 {
202   gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER,
203       "GStreamer RTSP server");
204
205   /* remove any previous header */
206   gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
207
208   /* add the new session header for new session ids */
209   if (session) {
210     gchar *str;
211
212     if (session->timeout != 60)
213       str =
214           g_strdup_printf ("%s; timeout=%d", session->sessionid,
215           session->timeout);
216     else
217       str = g_strdup (session->sessionid);
218
219     gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
220   }
221
222   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
223     gst_rtsp_message_dump (response);
224   }
225
226   gst_rtsp_watch_send_message (client->watch, response, NULL);
227   gst_rtsp_message_unset (response);
228 }
229
230 static void
231 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
232     GstRTSPMessage * request)
233 {
234   GstRTSPMessage response = { 0 };
235
236   gst_rtsp_message_init_response (&response, code,
237       gst_rtsp_status_as_text (code), request);
238
239   send_response (client, NULL, &response);
240 }
241
242 static gboolean
243 compare_uri (const GstRTSPUrl * uri1, const GstRTSPUrl * uri2)
244 {
245   if (uri1 == NULL || uri2 == NULL)
246     return FALSE;
247
248   if (strcmp (uri1->abspath, uri2->abspath))
249     return FALSE;
250
251   return TRUE;
252 }
253
254 /* this function is called to initially find the media for the DESCRIBE request
255  * but is cached for when the same client (without breaking the connection) is
256  * doing a setup for the exact same url. */
257 static GstRTSPMedia *
258 find_media (GstRTSPClient * client, GstRTSPUrl * uri, GstRTSPMessage * request)
259 {
260   GstRTSPMediaFactory *factory;
261   GstRTSPMedia *media;
262
263   if (!compare_uri (client->uri, uri)) {
264     /* remove any previously cached values before we try to construct a new
265      * media for uri */
266     if (client->uri)
267       gst_rtsp_url_free (client->uri);
268     client->uri = NULL;
269     if (client->media)
270       g_object_unref (client->media);
271     client->media = NULL;
272
273     if (!client->media_mapping)
274       goto no_mapping;
275
276     /* find the factory for the uri first */
277     if (!(factory =
278             gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
279       goto no_factory;
280
281     /* prepare the media and add it to the pipeline */
282     if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
283       goto no_media;
284
285     /* set ipv6 on the media before preparing */
286     media->is_ipv6 = client->is_ipv6;
287
288     /* prepare the media */
289     if (!(gst_rtsp_media_prepare (media)))
290       goto no_prepare;
291
292     /* now keep track of the uri and the media */
293     client->uri = gst_rtsp_url_copy (uri);
294     client->media = media;
295   } else {
296     /* we have seen this uri before, used cached media */
297     media = client->media;
298     GST_INFO ("reusing cached media %p", media);
299   }
300
301   if (media)
302     g_object_ref (media);
303
304   return media;
305
306   /* ERRORS */
307 no_mapping:
308   {
309     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
310     return NULL;
311   }
312 no_factory:
313   {
314     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
315     return NULL;
316   }
317 no_media:
318   {
319     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
320     g_object_unref (factory);
321     return NULL;
322   }
323 no_prepare:
324   {
325     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
326     g_object_unref (media);
327     g_object_unref (factory);
328     return NULL;
329   }
330 }
331
332 static gboolean
333 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
334 {
335   GstRTSPMessage message = { 0 };
336   guint8 *data;
337   guint size;
338
339   gst_rtsp_message_init_data (&message, channel);
340
341   data = GST_BUFFER_DATA (buffer);
342   size = GST_BUFFER_SIZE (buffer);
343   gst_rtsp_message_take_body (&message, data, size);
344
345   /* FIXME, client->watch could have been finalized here, we need to keep an
346    * extra refcount to the watch.  */
347   gst_rtsp_watch_send_message (client->watch, &message, NULL);
348
349   gst_rtsp_message_steal_body (&message, &data, &size);
350   gst_rtsp_message_unset (&message);
351
352   return TRUE;
353 }
354
355 static gboolean
356 do_send_data_list (GstBufferList * blist, guint8 channel,
357     GstRTSPClient * client)
358 {
359   GstBufferListIterator *it;
360
361   it = gst_buffer_list_iterate (blist);
362   while (gst_buffer_list_iterator_next_group (it)) {
363     GstBuffer *group = gst_buffer_list_iterator_merge_group (it);
364
365     if (group == NULL)
366       continue;
367
368     do_send_data (group, channel, client);
369   }
370   gst_buffer_list_iterator_free (it);
371
372   return TRUE;
373 }
374
375 static void
376 link_stream (GstRTSPClient * client, GstRTSPSession * session,
377     GstRTSPSessionStream * stream)
378 {
379   GST_DEBUG ("client %p: linking stream %p", client, stream);
380   gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
381       (GstRTSPSendFunc) do_send_data, (GstRTSPSendListFunc) do_send_data_list,
382       (GstRTSPSendListFunc) do_send_data_list, client, NULL);
383   client->streams = g_list_prepend (client->streams, stream);
384   /* make sure our session can't expire */
385   gst_rtsp_session_prevent_expire (session);
386 }
387
388 static void
389 unlink_stream (GstRTSPClient * client, GstRTSPSession * session,
390     GstRTSPSessionStream * stream)
391 {
392   GST_DEBUG ("client %p: unlinking stream %p", client, stream);
393   gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL, NULL,
394       NULL);
395   client->streams = g_list_remove (client->streams, stream);
396   /* our session can now expire */
397   gst_rtsp_session_allow_expire (session);
398 }
399
400 static void
401 unlink_session_streams (GstRTSPClient * client, GstRTSPSession * session,
402     GstRTSPSessionMedia * media)
403 {
404   guint n_streams, i;
405
406   n_streams = gst_rtsp_media_n_streams (media->media);
407   for (i = 0; i < n_streams; i++) {
408     GstRTSPSessionStream *sstream;
409     GstRTSPTransport *tr;
410
411     /* get the stream as configured in the session */
412     sstream = gst_rtsp_session_media_get_stream (media, i);
413     /* get the transport, if there is no transport configured, skip this stream */
414     if (!(tr = sstream->trans.transport))
415       continue;
416
417     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
418       /* for TCP, unlink the stream from the TCP connection of the client */
419       unlink_stream (client, session, sstream);
420     }
421   }
422 }
423
424 static void
425 close_connection (GstRTSPClient * client)
426 {
427   const gchar *tunnelid;
428
429   GST_DEBUG ("client %p: closing connection", client);
430
431   if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
432     g_mutex_lock (tunnels_lock);
433     /* remove from tunnelids */
434     g_hash_table_remove (tunnels, tunnelid);
435     g_mutex_unlock (tunnels_lock);
436   }
437
438   gst_rtsp_connection_close (client->connection);
439   if (client->watchid) {
440     g_source_destroy ((GSource *) client->watch);
441     client->watchid = 0;
442     client->watch = NULL;
443   }
444 }
445
446 static gboolean
447 handle_teardown_request (GstRTSPClient * client, GstRTSPUrl * uri,
448     GstRTSPSession * session, GstRTSPMessage * request)
449 {
450   GstRTSPSessionMedia *media;
451   GstRTSPMessage response = { 0 };
452   GstRTSPStatusCode code;
453
454   if (!session)
455     goto no_session;
456
457   /* get a handle to the configuration of the media in the session */
458   media = gst_rtsp_session_get_media (session, uri);
459   if (!media)
460     goto not_found;
461
462   /* unlink the all TCP callbacks */
463   unlink_session_streams (client, session, media);
464
465   /* remove the session from the watched sessions */
466   g_object_weak_unref (G_OBJECT (session),
467       (GWeakNotify) client_session_finalized, client);
468   client->sessions = g_list_remove (client->sessions, session);
469
470   gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
471
472   /* unmanage the media in the session, returns false if all media session
473    * are torn down. */
474   if (!gst_rtsp_session_release_media (session, media)) {
475     /* remove the session */
476     gst_rtsp_session_pool_remove (client->session_pool, session);
477   }
478   /* construct the response now */
479   code = GST_RTSP_STS_OK;
480   gst_rtsp_message_init_response (&response, code,
481       gst_rtsp_status_as_text (code), request);
482
483   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONNECTION, "close");
484
485   send_response (client, session, &response);
486
487   close_connection (client);
488
489   return TRUE;
490
491   /* ERRORS */
492 no_session:
493   {
494     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
495     return FALSE;
496   }
497 not_found:
498   {
499     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
500     return FALSE;
501   }
502 }
503
504 static gboolean
505 handle_get_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
506     GstRTSPSession * session, GstRTSPMessage * request)
507 {
508   GstRTSPResult res;
509   guint8 *data;
510   guint size;
511
512   res = gst_rtsp_message_get_body (request, &data, &size);
513   if (res != GST_RTSP_OK)
514     goto bad_request;
515
516   if (size == 0) {
517     /* no body, keep-alive request */
518     send_generic_response (client, GST_RTSP_STS_OK, request);
519   } else {
520     /* there is a body */
521     GstRTSPMessage response = { 0 };
522
523     /* there is a body, handle the params */
524     res = gst_rtsp_params_get (client, uri, session, request, &response);
525     if (res != GST_RTSP_OK)
526       goto bad_request;
527
528     send_response (client, session, &response);
529   }
530   return TRUE;
531
532   /* ERRORS */
533 bad_request:
534   {
535     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
536     return FALSE;
537   }
538 }
539
540 static gboolean
541 handle_set_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
542     GstRTSPSession * session, GstRTSPMessage * request)
543 {
544   GstRTSPResult res;
545   guint8 *data;
546   guint size;
547
548   res = gst_rtsp_message_get_body (request, &data, &size);
549   if (res != GST_RTSP_OK)
550     goto bad_request;
551
552   if (size == 0) {
553     /* no body, keep-alive request */
554     send_generic_response (client, GST_RTSP_STS_OK, request);
555   } else {
556     GstRTSPMessage response = { 0 };
557
558     /* there is a body, handle the params */
559     res = gst_rtsp_params_set (client, uri, session, request, &response);
560     if (res != GST_RTSP_OK)
561       goto bad_request;
562
563     send_response (client, session, &response);
564   }
565   return TRUE;
566
567   /* ERRORS */
568 bad_request:
569   {
570     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
571     return FALSE;
572   }
573 }
574
575 static gboolean
576 handle_pause_request (GstRTSPClient * client, GstRTSPUrl * uri,
577     GstRTSPSession * session, GstRTSPMessage * request)
578 {
579   GstRTSPSessionMedia *media;
580   GstRTSPMessage response = { 0 };
581   GstRTSPStatusCode code;
582
583   if (!session)
584     goto no_session;
585
586   /* get a handle to the configuration of the media in the session */
587   media = gst_rtsp_session_get_media (session, uri);
588   if (!media)
589     goto not_found;
590
591   /* the session state must be playing or recording */
592   if (media->state != GST_RTSP_STATE_PLAYING &&
593       media->state != GST_RTSP_STATE_RECORDING)
594     goto invalid_state;
595
596   /* unlink the all TCP callbacks */
597   unlink_session_streams (client, session, media);
598
599   /* then pause sending */
600   gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
601
602   /* construct the response now */
603   code = GST_RTSP_STS_OK;
604   gst_rtsp_message_init_response (&response, code,
605       gst_rtsp_status_as_text (code), request);
606
607   send_response (client, session, &response);
608
609   /* the state is now READY */
610   media->state = GST_RTSP_STATE_READY;
611
612   return TRUE;
613
614   /* ERRORS */
615 no_session:
616   {
617     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
618     return FALSE;
619   }
620 not_found:
621   {
622     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
623     return FALSE;
624   }
625 invalid_state:
626   {
627     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
628         request);
629     return FALSE;
630   }
631 }
632
633 static gboolean
634 handle_play_request (GstRTSPClient * client, GstRTSPUrl * uri,
635     GstRTSPSession * session, GstRTSPMessage * request)
636 {
637   GstRTSPSessionMedia *media;
638   GstRTSPMessage response = { 0 };
639   GstRTSPStatusCode code;
640   GString *rtpinfo;
641   guint n_streams, i, infocount;
642   guint timestamp, seqnum;
643   gchar *str;
644   GstRTSPTimeRange *range;
645   GstRTSPResult res;
646
647   if (!session)
648     goto no_session;
649
650   /* get a handle to the configuration of the media in the session */
651   media = gst_rtsp_session_get_media (session, uri);
652   if (!media)
653     goto not_found;
654
655   /* the session state must be playing or ready */
656   if (media->state != GST_RTSP_STATE_PLAYING &&
657       media->state != GST_RTSP_STATE_READY)
658     goto invalid_state;
659
660   /* parse the range header if we have one */
661   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_RANGE, &str, 0);
662   if (res == GST_RTSP_OK) {
663     if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
664       /* we have a range, seek to the position */
665       gst_rtsp_media_seek (media->media, range);
666       gst_rtsp_range_free (range);
667     }
668   }
669
670   /* grab RTPInfo from the payloaders now */
671   rtpinfo = g_string_new ("");
672
673   n_streams = gst_rtsp_media_n_streams (media->media);
674   for (i = 0, infocount = 0; i < n_streams; i++) {
675     GstRTSPSessionStream *sstream;
676     GstRTSPMediaStream *stream;
677     GstRTSPTransport *tr;
678     GObjectClass *payobjclass;
679     gchar *uristr;
680
681     /* get the stream as configured in the session */
682     sstream = gst_rtsp_session_media_get_stream (media, i);
683     /* get the transport, if there is no transport configured, skip this stream */
684     if (!(tr = sstream->trans.transport)) {
685       GST_INFO ("stream %d is not configured", i);
686       continue;
687     }
688
689     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
690       /* for TCP, link the stream to the TCP connection of the client */
691       link_stream (client, session, sstream);
692     }
693
694     stream = sstream->media_stream;
695
696     payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
697
698     if (g_object_class_find_property (payobjclass, "seqnum") &&
699         g_object_class_find_property (payobjclass, "timestamp")) {
700       GObject *payobj;
701
702       payobj = G_OBJECT (stream->payloader);
703
704       /* only add RTP-Info for streams with seqnum and timestamp */
705       g_object_get (payobj, "seqnum", &seqnum, "timestamp", &timestamp, NULL);
706
707       if (infocount > 0)
708         g_string_append (rtpinfo, ", ");
709
710       uristr = gst_rtsp_url_get_request_uri (uri);
711       g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
712           uristr, i, seqnum, timestamp);
713       g_free (uristr);
714
715       infocount++;
716     } else {
717       GST_WARNING ("RTP-Info cannot be determined for stream %d", i);
718     }
719   }
720
721   /* construct the response now */
722   code = GST_RTSP_STS_OK;
723   gst_rtsp_message_init_response (&response, code,
724       gst_rtsp_status_as_text (code), request);
725
726   /* add the RTP-Info header */
727   if (infocount > 0) {
728     str = g_string_free (rtpinfo, FALSE);
729     gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
730   } else {
731     g_string_free (rtpinfo, TRUE);
732   }
733
734   /* add the range */
735   str = gst_rtsp_media_get_range_string (media->media, TRUE);
736   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
737
738   send_response (client, session, &response);
739
740   /* start playing after sending the request */
741   gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
742
743   media->state = GST_RTSP_STATE_PLAYING;
744
745   return TRUE;
746
747   /* ERRORS */
748 no_session:
749   {
750     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
751     return FALSE;
752   }
753 not_found:
754   {
755     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
756     return FALSE;
757   }
758 invalid_state:
759   {
760     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
761         request);
762     return FALSE;
763   }
764 }
765
766 static void
767 do_keepalive (GstRTSPSession * session)
768 {
769   GST_INFO ("keep session %p alive", session);
770   gst_rtsp_session_touch (session);
771 }
772
773 static gboolean
774 handle_setup_request (GstRTSPClient * client, GstRTSPUrl * uri,
775     GstRTSPSession * session, GstRTSPMessage * request)
776 {
777   GstRTSPResult res;
778   gchar *transport;
779   gchar **transports;
780   gboolean have_transport;
781   GstRTSPTransport *ct, *st;
782   gint i;
783   GstRTSPLowerTrans supported;
784   GstRTSPMessage response = { 0 };
785   GstRTSPStatusCode code;
786   GstRTSPSessionStream *stream;
787   gchar *trans_str, *pos;
788   guint streamid;
789   GstRTSPSessionMedia *media;
790   GstRTSPUrl *url;
791
792   /* the uri contains the stream number we added in the SDP config, which is
793    * always /stream=%d so we need to strip that off 
794    * parse the stream we need to configure, look for the stream in the abspath
795    * first and then in the query. */
796   if (uri->abspath == NULL || !(pos = strstr (uri->abspath, "/stream="))) {
797     if (uri->query == NULL || !(pos = strstr (uri->query, "/stream=")))
798       goto bad_request;
799   }
800
801   /* we can mofify the parse uri in place */
802   *pos = '\0';
803
804   pos += strlen ("/stream=");
805   if (sscanf (pos, "%u", &streamid) != 1)
806     goto bad_request;
807
808   /* parse the transport */
809   res =
810       gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport,
811       0);
812   if (res != GST_RTSP_OK)
813     goto no_transport;
814
815   transports = g_strsplit (transport, ",", 0);
816   gst_rtsp_transport_new (&ct);
817
818   /* init transports */
819   have_transport = FALSE;
820   gst_rtsp_transport_init (ct);
821
822   /* our supported transports */
823   supported = GST_RTSP_LOWER_TRANS_UDP |
824       GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP;
825
826   /* loop through the transports, try to parse */
827   for (i = 0; transports[i]; i++) {
828     res = gst_rtsp_transport_parse (transports[i], ct);
829     if (res != GST_RTSP_OK) {
830       /* no valid transport, search some more */
831       GST_WARNING ("could not parse transport %s", transports[i]);
832       goto next;
833     }
834
835     /* we have a transport, see if it's RTP/AVP */
836     if (ct->trans != GST_RTSP_TRANS_RTP || ct->profile != GST_RTSP_PROFILE_AVP) {
837       GST_WARNING ("invalid transport %s", transports[i]);
838       goto next;
839     }
840
841     if (!(ct->lower_transport & supported)) {
842       GST_WARNING ("unsupported transport %s", transports[i]);
843       goto next;
844     }
845
846     /* we have a valid transport */
847     GST_INFO ("found valid transport %s", transports[i]);
848     have_transport = TRUE;
849     break;
850
851   next:
852     gst_rtsp_transport_init (ct);
853   }
854   g_strfreev (transports);
855
856   /* we have not found anything usable, error out */
857   if (!have_transport)
858     goto unsupported_transports;
859
860   if (client->session_pool == NULL)
861     goto no_pool;
862
863   /* we have a valid transport now, set the destination of the client. */
864   g_free (ct->destination);
865   if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
866     ct->destination = g_strdup (MCAST_ADDRESS);
867   } else {
868     url = gst_rtsp_connection_get_url (client->connection);
869     ct->destination = g_strdup (url->host);
870   }
871
872   if (session) {
873     g_object_ref (session);
874     /* get a handle to the configuration of the media in the session, this can
875      * return NULL if this is a new url to manage in this session. */
876     media = gst_rtsp_session_get_media (session, uri);
877   } else {
878     /* create a session if this fails we probably reached our session limit or
879      * something. */
880     if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
881       goto service_unavailable;
882
883     /* we need a new media configuration in this session */
884     media = NULL;
885   }
886
887   /* we have no media, find one and manage it */
888   if (media == NULL) {
889     GstRTSPMedia *m;
890
891     /* get a handle to the configuration of the media in the session */
892     if ((m = find_media (client, uri, request))) {
893       /* manage the media in our session now */
894       media = gst_rtsp_session_manage_media (session, uri, m);
895     }
896   }
897
898   /* if we stil have no media, error */
899   if (media == NULL)
900     goto not_found;
901
902   /* fix the transports */
903   if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
904     /* check if the client selected channels for TCP */
905     if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
906       gst_rtsp_session_media_alloc_channels (media, &ct->interleaved);
907     }
908   }
909
910   /* get a handle to the stream in the media */
911   if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
912     goto no_stream;
913
914   st = gst_rtsp_session_stream_set_transport (stream, ct);
915
916   /* configure keepalive for this transport */
917   gst_rtsp_session_stream_set_keepalive (stream,
918       (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
919
920   /* serialize the server transport */
921   trans_str = gst_rtsp_transport_as_text (st);
922   gst_rtsp_transport_free (st);
923
924   /* construct the response now */
925   code = GST_RTSP_STS_OK;
926   gst_rtsp_message_init_response (&response, code,
927       gst_rtsp_status_as_text (code), request);
928
929   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
930   g_free (trans_str);
931
932   send_response (client, session, &response);
933
934   /* update the state */
935   switch (media->state) {
936     case GST_RTSP_STATE_PLAYING:
937     case GST_RTSP_STATE_RECORDING:
938     case GST_RTSP_STATE_READY:
939       /* no state change */
940       break;
941     default:
942       media->state = GST_RTSP_STATE_READY;
943       break;
944   }
945   g_object_unref (session);
946
947   return TRUE;
948
949   /* ERRORS */
950 bad_request:
951   {
952     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
953     return FALSE;
954   }
955 not_found:
956   {
957     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
958     g_object_unref (session);
959     return FALSE;
960   }
961 no_stream:
962   {
963     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
964     g_object_unref (media);
965     g_object_unref (session);
966     return FALSE;
967   }
968 no_transport:
969   {
970     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
971     return FALSE;
972   }
973 unsupported_transports:
974   {
975     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
976     gst_rtsp_transport_free (ct);
977     return FALSE;
978   }
979 no_pool:
980   {
981     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
982     return FALSE;
983   }
984 service_unavailable:
985   {
986     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
987     return FALSE;
988   }
989 }
990
991 static GstSDPMessage *
992 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
993 {
994   GstSDPMessage *sdp;
995   GstSDPInfo info;
996   const gchar *proto;
997
998   gst_sdp_message_new (&sdp);
999
1000   /* some standard things first */
1001   gst_sdp_message_set_version (sdp, "0");
1002
1003   if (client->is_ipv6)
1004     proto = "IP6";
1005   else
1006     proto = "IP4";
1007
1008   gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", proto,
1009       client->server_ip);
1010
1011   gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
1012   gst_sdp_message_set_information (sdp, "rtsp-server");
1013   gst_sdp_message_add_time (sdp, "0", "0", NULL);
1014   gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
1015   gst_sdp_message_add_attribute (sdp, "type", "broadcast");
1016   gst_sdp_message_add_attribute (sdp, "control", "*");
1017
1018   info.server_proto = proto;
1019   if (media->protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST)
1020     info.server_ip = MCAST_ADDRESS;
1021   else
1022     info.server_ip = client->server_ip;
1023
1024   /* create an SDP for the media object */
1025   if (!gst_rtsp_sdp_from_media (sdp, &info, media))
1026     goto no_sdp;
1027
1028   return sdp;
1029
1030   /* ERRORS */
1031 no_sdp:
1032   {
1033     gst_sdp_message_free (sdp);
1034     return NULL;
1035   }
1036 }
1037
1038 /* for the describe we must generate an SDP */
1039 static gboolean
1040 handle_describe_request (GstRTSPClient * client, GstRTSPUrl * uri,
1041     GstRTSPSession * session, GstRTSPMessage * request)
1042 {
1043   GstRTSPMessage response = { 0 };
1044   GstRTSPResult res;
1045   GstSDPMessage *sdp;
1046   guint i, str_len;
1047   gchar *str, *content_base;
1048   GstRTSPMedia *media;
1049
1050   /* check what kind of format is accepted, we don't really do anything with it
1051    * and always return SDP for now. */
1052   for (i = 0; i++;) {
1053     gchar *accept;
1054
1055     res =
1056         gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
1057     if (res == GST_RTSP_ENOTIMPL)
1058       break;
1059
1060     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
1061       break;
1062   }
1063
1064   /* find the media object for the uri */
1065   if (!(media = find_media (client, uri, request)))
1066     goto no_media;
1067
1068   /* create an SDP for the media object on this client */
1069   if (!(sdp = create_sdp (client, media)))
1070     goto no_sdp;
1071
1072   g_object_unref (media);
1073
1074   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
1075       gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
1076
1077   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE,
1078       "application/sdp");
1079
1080   /* content base for some clients that might screw up creating the setup uri */
1081   str = gst_rtsp_url_get_request_uri (uri);
1082   str_len = strlen (str);
1083
1084   /* check for trailing '/' and append one */
1085   if (str[str_len - 1] != '/') {
1086     content_base = g_malloc (str_len + 2);
1087     memcpy (content_base, str, str_len);
1088     content_base[str_len] = '/';
1089     content_base[str_len + 1] = '\0';
1090     g_free (str);
1091   } else {
1092     content_base = str;
1093   }
1094
1095   GST_INFO ("adding content-base: %s", content_base);
1096
1097   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE,
1098       content_base);
1099   g_free (content_base);
1100
1101   /* add SDP to the response body */
1102   str = gst_sdp_message_as_text (sdp);
1103   gst_rtsp_message_take_body (&response, (guint8 *) str, strlen (str));
1104   gst_sdp_message_free (sdp);
1105
1106   send_response (client, session, &response);
1107
1108   return TRUE;
1109
1110   /* ERRORS */
1111 no_media:
1112   {
1113     /* error reply is already sent */
1114     return FALSE;
1115   }
1116 no_sdp:
1117   {
1118     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1119     g_object_unref (media);
1120     return FALSE;
1121   }
1122 }
1123
1124 static gboolean
1125 handle_options_request (GstRTSPClient * client, GstRTSPUrl * uri,
1126     GstRTSPSession * session, GstRTSPMessage * request)
1127 {
1128   GstRTSPMessage response = { 0 };
1129   GstRTSPMethod options;
1130   gchar *str;
1131
1132   options = GST_RTSP_DESCRIBE |
1133       GST_RTSP_OPTIONS |
1134       GST_RTSP_PAUSE |
1135       GST_RTSP_PLAY |
1136       GST_RTSP_SETUP |
1137       GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
1138
1139   str = gst_rtsp_options_as_text (options);
1140
1141   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
1142       gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
1143
1144   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
1145   g_free (str);
1146
1147   send_response (client, session, &response);
1148
1149   return TRUE;
1150 }
1151
1152 /* remove duplicate and trailing '/' */
1153 static void
1154 sanitize_uri (GstRTSPUrl * uri)
1155 {
1156   gint i, len;
1157   gchar *s, *d;
1158   gboolean have_slash, prev_slash;
1159
1160   s = d = uri->abspath;
1161   len = strlen (uri->abspath);
1162
1163   prev_slash = FALSE;
1164
1165   for (i = 0; i < len; i++) {
1166     have_slash = s[i] == '/';
1167     *d = s[i];
1168     if (!have_slash || !prev_slash)
1169       d++;
1170     prev_slash = have_slash;
1171   }
1172   len = d - uri->abspath;
1173   /* don't remove the first slash if that's the only thing left */
1174   if (len > 1 && *(d - 1) == '/')
1175     d--;
1176   *d = '\0';
1177 }
1178
1179 static void
1180 client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
1181 {
1182   GST_INFO ("client %p: session %p finished", client, session);
1183
1184   /* unlink all media managed in this session */
1185   client_unlink_session (client, session);
1186
1187   /* remove the session */
1188   if (!(client->sessions = g_list_remove (client->sessions, session))) {
1189     GST_INFO ("client %p: all sessions finalized, close the connection",
1190         client);
1191     close_connection (client);
1192   }
1193 }
1194
1195 static void
1196 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
1197 {
1198   GList *walk;
1199
1200   for (walk = client->sessions; walk; walk = g_list_next (walk)) {
1201     GstRTSPSession *msession = (GstRTSPSession *) walk->data;
1202
1203     /* we already know about this session */
1204     if (msession == session)
1205       return;
1206   }
1207
1208   GST_INFO ("watching session %p", session);
1209
1210   g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized,
1211       client);
1212   client->sessions = g_list_prepend (client->sessions, session);
1213 }
1214
1215 static void
1216 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
1217 {
1218   GstRTSPMethod method;
1219   const gchar *uristr;
1220   GstRTSPUrl *uri;
1221   GstRTSPVersion version;
1222   GstRTSPResult res;
1223   GstRTSPSession *session;
1224   gchar *sessid;
1225
1226   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
1227     gst_rtsp_message_dump (request);
1228   }
1229
1230   GST_INFO ("client %p: received a request", client);
1231
1232   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
1233
1234   if (version != GST_RTSP_VERSION_1_0) {
1235     /* we can only handle 1.0 requests */
1236     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
1237         request);
1238     return;
1239   }
1240
1241   /* we always try to parse the url first */
1242   if (gst_rtsp_url_parse (uristr, &uri) != GST_RTSP_OK) {
1243     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1244     return;
1245   }
1246
1247   /* sanitize the uri */
1248   sanitize_uri (uri);
1249
1250   /* get the session if there is any */
1251   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
1252   if (res == GST_RTSP_OK) {
1253     if (client->session_pool == NULL)
1254       goto no_pool;
1255
1256     /* we had a session in the request, find it again */
1257     if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
1258       goto session_not_found;
1259
1260     /* we add the session to the client list of watched sessions. When a session
1261      * disappears because it times out, we will be notified. If all sessions are
1262      * gone, we will close the connection */
1263     client_watch_session (client, session);
1264   } else
1265     session = NULL;
1266
1267   /* now see what is asked and dispatch to a dedicated handler */
1268   switch (method) {
1269     case GST_RTSP_OPTIONS:
1270       handle_options_request (client, uri, session, request);
1271       break;
1272     case GST_RTSP_DESCRIBE:
1273       handle_describe_request (client, uri, session, request);
1274       break;
1275     case GST_RTSP_SETUP:
1276       handle_setup_request (client, uri, session, request);
1277       break;
1278     case GST_RTSP_PLAY:
1279       handle_play_request (client, uri, session, request);
1280       break;
1281     case GST_RTSP_PAUSE:
1282       handle_pause_request (client, uri, session, request);
1283       break;
1284     case GST_RTSP_TEARDOWN:
1285       handle_teardown_request (client, uri, session, request);
1286       break;
1287     case GST_RTSP_SET_PARAMETER:
1288       handle_set_param_request (client, uri, session, request);
1289       break;
1290     case GST_RTSP_GET_PARAMETER:
1291       handle_get_param_request (client, uri, session, request);
1292       break;
1293     case GST_RTSP_ANNOUNCE:
1294     case GST_RTSP_RECORD:
1295     case GST_RTSP_REDIRECT:
1296       send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
1297       break;
1298     case GST_RTSP_INVALID:
1299     default:
1300       send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1301       break;
1302   }
1303   if (session)
1304     g_object_unref (session);
1305
1306   gst_rtsp_url_free (uri);
1307   return;
1308
1309   /* ERRORS */
1310 no_pool:
1311   {
1312     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1313     return;
1314   }
1315 session_not_found:
1316   {
1317     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
1318     return;
1319   }
1320 }
1321
1322 static void
1323 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
1324 {
1325   GstRTSPResult res;
1326   guint8 channel;
1327   GList *walk;
1328   guint8 *data;
1329   guint size;
1330   GstBuffer *buffer;
1331   gboolean handled;
1332
1333   /* find the stream for this message */
1334   res = gst_rtsp_message_parse_data (message, &channel);
1335   if (res != GST_RTSP_OK)
1336     return;
1337
1338   gst_rtsp_message_steal_body (message, &data, &size);
1339
1340   buffer = gst_buffer_new ();
1341   GST_BUFFER_DATA (buffer) = data;
1342   GST_BUFFER_MALLOCDATA (buffer) = data;
1343   GST_BUFFER_SIZE (buffer) = size;
1344
1345   handled = FALSE;
1346   for (walk = client->streams; walk; walk = g_list_next (walk)) {
1347     GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
1348     GstRTSPMediaStream *mstream;
1349     GstRTSPTransport *tr;
1350
1351     /* get the transport, if there is no transport configured, skip this stream */
1352     if (!(tr = stream->trans.transport))
1353       continue;
1354
1355     /* we also need a media stream */
1356     if (!(mstream = stream->media_stream))
1357       continue;
1358
1359     /* check for TCP transport */
1360     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1361       /* dispatch to the stream based on the channel number */
1362       if (tr->interleaved.min == channel) {
1363         gst_rtsp_media_stream_rtp (mstream, buffer);
1364         handled = TRUE;
1365         break;
1366       } else if (tr->interleaved.max == channel) {
1367         gst_rtsp_media_stream_rtcp (mstream, buffer);
1368         handled = TRUE;
1369         break;
1370       }
1371     }
1372   }
1373   if (!handled)
1374     gst_buffer_unref (buffer);
1375 }
1376
1377 /**
1378  * gst_rtsp_client_set_session_pool:
1379  * @client: a #GstRTSPClient
1380  * @pool: a #GstRTSPSessionPool
1381  *
1382  * Set @pool as the sessionpool for @client which it will use to find
1383  * or allocate sessions. the sessionpool is usually inherited from the server
1384  * that created the client but can be overridden later.
1385  */
1386 void
1387 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
1388     GstRTSPSessionPool * pool)
1389 {
1390   GstRTSPSessionPool *old;
1391
1392   old = client->session_pool;
1393   if (old != pool) {
1394     if (pool)
1395       g_object_ref (pool);
1396     client->session_pool = pool;
1397     if (old)
1398       g_object_unref (old);
1399   }
1400 }
1401
1402 /**
1403  * gst_rtsp_client_get_session_pool:
1404  * @client: a #GstRTSPClient
1405  *
1406  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1407  *
1408  * Returns: a #GstRTSPSessionPool, unref after usage.
1409  */
1410 GstRTSPSessionPool *
1411 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
1412 {
1413   GstRTSPSessionPool *result;
1414
1415   if ((result = client->session_pool))
1416     g_object_ref (result);
1417
1418   return result;
1419 }
1420
1421 /**
1422  * gst_rtsp_client_set_media_mapping:
1423  * @client: a #GstRTSPClient
1424  * @mapping: a #GstRTSPMediaMapping
1425  *
1426  * Set @mapping as the media mapping for @client which it will use to map urls
1427  * to media streams. These mapping is usually inherited from the server that
1428  * created the client but can be overriden later.
1429  */
1430 void
1431 gst_rtsp_client_set_media_mapping (GstRTSPClient * client,
1432     GstRTSPMediaMapping * mapping)
1433 {
1434   GstRTSPMediaMapping *old;
1435
1436   old = client->media_mapping;
1437
1438   if (old != mapping) {
1439     if (mapping)
1440       g_object_ref (mapping);
1441     client->media_mapping = mapping;
1442     if (old)
1443       g_object_unref (old);
1444   }
1445 }
1446
1447 /**
1448  * gst_rtsp_client_get_media_mapping:
1449  * @client: a #GstRTSPClient
1450  *
1451  * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1452  *
1453  * Returns: a #GstRTSPMediaMapping, unref after usage.
1454  */
1455 GstRTSPMediaMapping *
1456 gst_rtsp_client_get_media_mapping (GstRTSPClient * client)
1457 {
1458   GstRTSPMediaMapping *result;
1459
1460   if ((result = client->media_mapping))
1461     g_object_ref (result);
1462
1463   return result;
1464 }
1465
1466 static GstRTSPResult
1467 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
1468     gpointer user_data)
1469 {
1470   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1471
1472   switch (message->type) {
1473     case GST_RTSP_MESSAGE_REQUEST:
1474       handle_request (client, message);
1475       break;
1476     case GST_RTSP_MESSAGE_RESPONSE:
1477       break;
1478     case GST_RTSP_MESSAGE_DATA:
1479       handle_data (client, message);
1480       break;
1481     default:
1482       break;
1483   }
1484   return GST_RTSP_OK;
1485 }
1486
1487 static GstRTSPResult
1488 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
1489 {
1490   GstRTSPClient *client;
1491
1492   client = GST_RTSP_CLIENT (user_data);
1493
1494   /* GST_INFO ("client %p: sent a message with cseq %d", client, cseq); */
1495
1496   return GST_RTSP_OK;
1497 }
1498
1499 static GstRTSPResult
1500 closed (GstRTSPWatch * watch, gpointer user_data)
1501 {
1502   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1503   const gchar *tunnelid;
1504
1505   GST_INFO ("client %p: connection closed", client);
1506
1507   if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1508     g_mutex_lock (tunnels_lock);
1509     /* remove from tunnelids */
1510     g_hash_table_remove (tunnels, tunnelid);
1511     g_mutex_unlock (tunnels_lock);
1512   }
1513
1514   return GST_RTSP_OK;
1515 }
1516
1517 static GstRTSPResult
1518 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
1519 {
1520   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1521   gchar *str;
1522
1523   str = gst_rtsp_strresult (result);
1524   GST_INFO ("client %p: received an error %s", client, str);
1525   g_free (str);
1526
1527   return GST_RTSP_OK;
1528 }
1529
1530 static GstRTSPResult
1531 error_full (GstRTSPWatch * watch, GstRTSPResult result,
1532     GstRTSPMessage * message, guint id, gpointer user_data)
1533 {
1534   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1535   gchar *str;
1536
1537   str = gst_rtsp_strresult (result);
1538   GST_INFO
1539       ("client %p: received an error %s when handling message %p with id %d",
1540       client, str, message, id);
1541   g_free (str);
1542
1543   return GST_RTSP_OK;
1544 }
1545
1546 static gboolean
1547 remember_tunnel (GstRTSPClient * client)
1548 {
1549   const gchar *tunnelid;
1550
1551   /* store client in the pending tunnels */
1552   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1553   if (tunnelid == NULL)
1554     goto no_tunnelid;
1555
1556   GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
1557
1558   /* we can't have two clients connecting with the same tunnelid */
1559   g_mutex_lock (tunnels_lock);
1560   if (g_hash_table_lookup (tunnels, tunnelid))
1561     goto tunnel_existed;
1562
1563   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1564   g_mutex_unlock (tunnels_lock);
1565
1566   return TRUE;
1567
1568   /* ERRORS */
1569 no_tunnelid:
1570   {
1571     GST_ERROR ("client %p: no tunnelid provided", client);
1572     return FALSE;
1573   }
1574 tunnel_existed:
1575   {
1576     g_mutex_unlock (tunnels_lock);
1577     GST_ERROR ("client %p: tunnel session %s already existed", client,
1578         tunnelid);
1579     return FALSE;
1580   }
1581 }
1582
1583 static GstRTSPStatusCode
1584 tunnel_start (GstRTSPWatch * watch, gpointer user_data)
1585 {
1586   GstRTSPClient *client;
1587
1588   client = GST_RTSP_CLIENT (user_data);
1589
1590   GST_INFO ("client %p: tunnel start (connection %p)", client,
1591       client->connection);
1592
1593   if (!remember_tunnel (client))
1594     goto tunnel_error;
1595
1596   return GST_RTSP_STS_OK;
1597
1598   /* ERRORS */
1599 tunnel_error:
1600   {
1601     GST_ERROR ("client %p: error starting tunnel", client);
1602     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1603   }
1604 }
1605
1606 static GstRTSPResult
1607 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
1608 {
1609   GstRTSPClient *client;
1610
1611   client = GST_RTSP_CLIENT (user_data);
1612
1613   GST_INFO ("client %p: tunnel lost (connection %p)", client,
1614       client->connection);
1615
1616   /* ignore error, it'll only be a problem when the client does a POST again */
1617   remember_tunnel (client);
1618
1619   return GST_RTSP_OK;
1620 }
1621
1622 static GstRTSPResult
1623 tunnel_complete (GstRTSPWatch * watch, gpointer user_data)
1624 {
1625   const gchar *tunnelid;
1626   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1627   GstRTSPClient *oclient;
1628
1629   GST_INFO ("client %p: tunnel complete", client);
1630
1631   /* find previous tunnel */
1632   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1633   if (tunnelid == NULL)
1634     goto no_tunnelid;
1635
1636   g_mutex_lock (tunnels_lock);
1637   if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1638     goto no_tunnel;
1639
1640   /* remove the old client from the table. ref before because removing it will
1641    * remove the ref to it. */
1642   g_object_ref (oclient);
1643   g_hash_table_remove (tunnels, tunnelid);
1644
1645   if (oclient->watch == NULL)
1646     goto tunnel_closed;
1647   g_mutex_unlock (tunnels_lock);
1648
1649   GST_INFO ("client %p: found tunnel %p (old %p, new %p)", client, oclient,
1650       oclient->connection, client->connection);
1651
1652   /* merge the tunnels into the first client */
1653   gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1654   gst_rtsp_watch_reset (oclient->watch);
1655   g_object_unref (oclient);
1656
1657   /* we don't need this watch anymore */
1658   g_source_destroy ((GSource *) client->watch);
1659   client->watchid = 0;
1660   client->watch = NULL;
1661
1662   return GST_RTSP_OK;
1663
1664   /* ERRORS */
1665 no_tunnelid:
1666   {
1667     GST_INFO ("client %p: no tunnelid provided", client);
1668     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1669   }
1670 no_tunnel:
1671   {
1672     g_mutex_unlock (tunnels_lock);
1673     GST_INFO ("client %p: tunnel session %s not found", client, tunnelid);
1674     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1675   }
1676 tunnel_closed:
1677   {
1678     g_mutex_unlock (tunnels_lock);
1679     GST_INFO ("client %p: tunnel session %s was closed", client, tunnelid);
1680     g_object_unref (oclient);
1681     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1682   }
1683 }
1684
1685 static GstRTSPWatchFuncs watch_funcs = {
1686   message_received,
1687   message_sent,
1688   closed,
1689   error,
1690   tunnel_start,
1691   tunnel_complete,
1692   error_full,
1693   tunnel_lost
1694 };
1695
1696 static void
1697 client_watch_notify (GstRTSPClient * client)
1698 {
1699   GST_INFO ("client %p: watch destroyed", client);
1700   client->watchid = 0;
1701   client->watch = NULL;
1702   g_object_unref (client);
1703 }
1704
1705 /**
1706  * gst_rtsp_client_attach:
1707  * @client: a #GstRTSPClient
1708  * @channel: a #GIOChannel
1709  *
1710  * Accept a new connection for @client on the socket in @channel. 
1711  *
1712  * This function should be called when the client properties and urls are fully
1713  * configured and the client is ready to start.
1714  *
1715  * Returns: %TRUE if the client could be accepted.
1716  */
1717 gboolean
1718 gst_rtsp_client_accept (GstRTSPClient * client, GIOChannel * channel)
1719 {
1720   int sock, fd;
1721   GstRTSPConnection *conn;
1722   GstRTSPResult res;
1723   GSource *source;
1724   GMainContext *context;
1725   GstRTSPUrl *url;
1726   struct sockaddr_storage addr;
1727   socklen_t addrlen;
1728   gchar ip[INET6_ADDRSTRLEN];
1729
1730   /* a new client connected. */
1731   sock = g_io_channel_unix_get_fd (channel);
1732
1733   GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1734
1735   fd = gst_rtsp_connection_get_readfd (conn);
1736
1737   addrlen = sizeof (addr);
1738   if (getsockname (fd, (struct sockaddr *) &addr, &addrlen) < 0)
1739     goto getpeername_failed;
1740
1741   client->is_ipv6 = addr.ss_family == AF_INET6;
1742
1743   if (getnameinfo ((struct sockaddr *) &addr, addrlen, ip, sizeof (ip), NULL, 0,
1744           NI_NUMERICHOST) != 0)
1745     goto getnameinfo_failed;
1746
1747   /* keep the original ip that the client connected to */
1748   g_free (client->server_ip);
1749   client->server_ip = g_strndup (ip, sizeof (ip));
1750
1751   GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
1752       client->server_ip, client->is_ipv6);
1753
1754   url = gst_rtsp_connection_get_url (conn);
1755   GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
1756
1757   client->connection = conn;
1758
1759   /* create watch for the connection and attach */
1760   client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1761       g_object_ref (client), (GDestroyNotify) client_watch_notify);
1762
1763   /* find the context to add the watch */
1764   if ((source = g_main_current_source ()))
1765     context = g_source_get_context (source);
1766   else
1767     context = NULL;
1768
1769   GST_INFO ("attaching to context %p", context);
1770
1771   client->watchid = gst_rtsp_watch_attach (client->watch, context);
1772   gst_rtsp_watch_unref (client->watch);
1773
1774   return TRUE;
1775
1776   /* ERRORS */
1777 accept_failed:
1778   {
1779     gchar *str = gst_rtsp_strresult (res);
1780
1781     GST_ERROR ("Could not accept client on server socket %d: %s", sock, str);
1782     g_free (str);
1783     return FALSE;
1784   }
1785 getpeername_failed:
1786   {
1787     GST_ERROR ("getpeername failed: %s", g_strerror (errno));
1788     return FALSE;
1789   }
1790 getnameinfo_failed:
1791   {
1792     GST_ERROR ("getnameinfo failed: %s", g_strerror (errno));
1793     return FALSE;
1794   }
1795 }