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