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