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