client: free threadpool
[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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * SECTION:rtsp-client
21  * @short_description: A client connection state
22  * @see_also: #GstRTSPServer, #GstRTSPThreadPool
23  *
24  * The client object handles the connection with a client for as long as a TCP
25  * connection is open.
26  *
27  * A #GstRTSPClient is created by #GstRTSPServer when a new connection is
28  * accepted and it inherits the #GstRTSPMountPoints, #GstRTSPSessionPool,
29  * #GstRTSPAuth and #GstRTSPThreadPool from the server.
30  *
31  * The client connection should be configured with the #GstRTSPConnection using
32  * gst_rtsp_client_set_connection() before it can be attached to a #GMainContext
33  * using gst_rtsp_client_attach(). From then on the client will handle requests
34  * on the connection.
35  *
36  * Use gst_rtsp_client_session_filter() to iterate or modify all the
37  * #GstRTSPSession objects managed by the client object.
38  *
39  * Last reviewed on 2013-07-11 (1.0.0)
40  */
41
42 #include <stdio.h>
43 #include <string.h>
44
45 #include "rtsp-client.h"
46 #include "rtsp-sdp.h"
47 #include "rtsp-params.h"
48
49 #define GST_RTSP_CLIENT_GET_PRIVATE(obj)  \
50    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_CLIENT, GstRTSPClientPrivate))
51
52 /* locking order:
53  * send_lock, lock, tunnels_lock
54  */
55
56 struct _GstRTSPClientPrivate
57 {
58   GMutex lock;                  /* protects everything else */
59   GMutex send_lock;
60   GstRTSPConnection *connection;
61   GstRTSPWatch *watch;
62   guint close_seq;
63   gchar *server_ip;
64   gboolean is_ipv6;
65
66   GstRTSPClientSendFunc send_func;      /* protected by send_lock */
67   gpointer send_data;           /* protected by send_lock */
68   GDestroyNotify send_notify;   /* protected by send_lock */
69
70   GstRTSPSessionPool *session_pool;
71   GstRTSPMountPoints *mount_points;
72   GstRTSPAuth *auth;
73   GstRTSPThreadPool *thread_pool;
74
75   /* used to cache the media in the last requested DESCRIBE so that
76    * we can pick it up in the next SETUP immediately */
77   gchar *path;
78   GstRTSPMedia *media;
79
80   GList *transports;
81   GList *sessions;
82 };
83
84 static GMutex tunnels_lock;
85 static GHashTable *tunnels;     /* protected by tunnels_lock */
86
87 #define DEFAULT_SESSION_POOL            NULL
88 #define DEFAULT_MOUNT_POINTS            NULL
89
90 enum
91 {
92   PROP_0,
93   PROP_SESSION_POOL,
94   PROP_MOUNT_POINTS,
95   PROP_LAST
96 };
97
98 enum
99 {
100   SIGNAL_CLOSED,
101   SIGNAL_NEW_SESSION,
102   SIGNAL_OPTIONS_REQUEST,
103   SIGNAL_DESCRIBE_REQUEST,
104   SIGNAL_SETUP_REQUEST,
105   SIGNAL_PLAY_REQUEST,
106   SIGNAL_PAUSE_REQUEST,
107   SIGNAL_TEARDOWN_REQUEST,
108   SIGNAL_SET_PARAMETER_REQUEST,
109   SIGNAL_GET_PARAMETER_REQUEST,
110   SIGNAL_HANDLE_RESPONSE,
111   SIGNAL_LAST
112 };
113
114 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
115 #define GST_CAT_DEFAULT rtsp_client_debug
116
117 static guint gst_rtsp_client_signals[SIGNAL_LAST] = { 0 };
118
119 static void gst_rtsp_client_get_property (GObject * object, guint propid,
120     GValue * value, GParamSpec * pspec);
121 static void gst_rtsp_client_set_property (GObject * object, guint propid,
122     const GValue * value, GParamSpec * pspec);
123 static void gst_rtsp_client_finalize (GObject * obj);
124
125 static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
126 static void client_session_finalized (GstRTSPClient * client,
127     GstRTSPSession * session);
128 static void unlink_session_transports (GstRTSPClient * client,
129     GstRTSPSession * session, GstRTSPSessionMedia * sessmedia);
130 static gboolean default_configure_client_transport (GstRTSPClient * client,
131     GstRTSPContext * ctx, GstRTSPTransport * ct);
132 static GstRTSPResult default_params_set (GstRTSPClient * client,
133     GstRTSPContext * ctx);
134 static GstRTSPResult default_params_get (GstRTSPClient * client,
135     GstRTSPContext * ctx);
136
137 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
138
139 static void
140 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
141 {
142   GObjectClass *gobject_class;
143
144   g_type_class_add_private (klass, sizeof (GstRTSPClientPrivate));
145
146   gobject_class = G_OBJECT_CLASS (klass);
147
148   gobject_class->get_property = gst_rtsp_client_get_property;
149   gobject_class->set_property = gst_rtsp_client_set_property;
150   gobject_class->finalize = gst_rtsp_client_finalize;
151
152   klass->create_sdp = create_sdp;
153   klass->configure_client_transport = default_configure_client_transport;
154   klass->params_set = default_params_set;
155   klass->params_get = default_params_get;
156
157   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
158       g_param_spec_object ("session-pool", "Session Pool",
159           "The session pool to use for client session",
160           GST_TYPE_RTSP_SESSION_POOL,
161           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
162
163   g_object_class_install_property (gobject_class, PROP_MOUNT_POINTS,
164       g_param_spec_object ("mount-points", "Mount Points",
165           "The mount points to use for client session",
166           GST_TYPE_RTSP_MOUNT_POINTS,
167           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
168
169   gst_rtsp_client_signals[SIGNAL_CLOSED] =
170       g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
171       G_STRUCT_OFFSET (GstRTSPClientClass, closed), NULL, NULL,
172       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
173
174   gst_rtsp_client_signals[SIGNAL_NEW_SESSION] =
175       g_signal_new ("new-session", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
176       G_STRUCT_OFFSET (GstRTSPClientClass, new_session), NULL, NULL,
177       g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_RTSP_SESSION);
178
179   gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST] =
180       g_signal_new ("options-request", G_TYPE_FROM_CLASS (klass),
181       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, options_request),
182       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
183       G_TYPE_POINTER);
184
185   gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST] =
186       g_signal_new ("describe-request", G_TYPE_FROM_CLASS (klass),
187       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, describe_request),
188       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
189       G_TYPE_POINTER);
190
191   gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST] =
192       g_signal_new ("setup-request", G_TYPE_FROM_CLASS (klass),
193       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, setup_request),
194       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
195       G_TYPE_POINTER);
196
197   gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST] =
198       g_signal_new ("play-request", G_TYPE_FROM_CLASS (klass),
199       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, play_request),
200       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
201       G_TYPE_POINTER);
202
203   gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST] =
204       g_signal_new ("pause-request", G_TYPE_FROM_CLASS (klass),
205       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, pause_request),
206       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
207       G_TYPE_POINTER);
208
209   gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST] =
210       g_signal_new ("teardown-request", G_TYPE_FROM_CLASS (klass),
211       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, teardown_request),
212       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
213       G_TYPE_POINTER);
214
215   gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST] =
216       g_signal_new ("set-parameter-request", G_TYPE_FROM_CLASS (klass),
217       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
218           set_parameter_request), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
219       G_TYPE_NONE, 1, G_TYPE_POINTER);
220
221   gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST] =
222       g_signal_new ("get-parameter-request", G_TYPE_FROM_CLASS (klass),
223       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
224           get_parameter_request), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
225       G_TYPE_NONE, 1, G_TYPE_POINTER);
226
227   gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE] =
228       g_signal_new ("handle-response", G_TYPE_FROM_CLASS (klass),
229       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
230           handle_response), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
231       G_TYPE_NONE, 1, G_TYPE_POINTER);
232
233   tunnels =
234       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
235   g_mutex_init (&tunnels_lock);
236
237   GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
238 }
239
240 static void
241 gst_rtsp_client_init (GstRTSPClient * client)
242 {
243   GstRTSPClientPrivate *priv = GST_RTSP_CLIENT_GET_PRIVATE (client);
244
245   client->priv = priv;
246
247   g_mutex_init (&priv->lock);
248   g_mutex_init (&priv->send_lock);
249   priv->close_seq = 0;
250 }
251
252 static GstRTSPFilterResult
253 filter_session (GstRTSPSession * sess, GstRTSPSessionMedia * sessmedia,
254     gpointer user_data)
255 {
256   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
257
258   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
259   unlink_session_transports (client, sess, sessmedia);
260
261   /* unmanage the media in the session */
262   return GST_RTSP_FILTER_REMOVE;
263 }
264
265 static void
266 client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
267 {
268   /* unlink all media managed in this session */
269   gst_rtsp_session_filter (session, filter_session, client);
270 }
271
272 static void
273 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
274 {
275   GstRTSPClientPrivate *priv = client->priv;
276   GList *walk;
277
278   for (walk = priv->sessions; walk; walk = g_list_next (walk)) {
279     GstRTSPSession *msession = (GstRTSPSession *) walk->data;
280
281     /* we already know about this session */
282     if (msession == session)
283       return;
284   }
285
286   GST_INFO ("watching session %p", session);
287
288   g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized,
289       client);
290   priv->sessions = g_list_prepend (priv->sessions, session);
291 }
292
293 static void
294 client_unwatch_session (GstRTSPClient * client, GstRTSPSession * session)
295 {
296   GstRTSPClientPrivate *priv = client->priv;
297
298   GST_INFO ("unwatching session %p", session);
299
300   g_object_weak_unref (G_OBJECT (session),
301       (GWeakNotify) client_session_finalized, client);
302   priv->sessions = g_list_remove (priv->sessions, session);
303 }
304
305 static void
306 client_cleanup_session (GstRTSPClient * client, GstRTSPSession * session)
307 {
308   g_object_weak_unref (G_OBJECT (session),
309       (GWeakNotify) client_session_finalized, client);
310   client_unlink_session (client, session);
311 }
312
313 static void
314 client_cleanup_sessions (GstRTSPClient * client)
315 {
316   GstRTSPClientPrivate *priv = client->priv;
317   GList *sessions;
318
319   /* remove weak-ref from sessions */
320   for (sessions = priv->sessions; sessions; sessions = g_list_next (sessions)) {
321     client_cleanup_session (client, (GstRTSPSession *) sessions->data);
322   }
323   g_list_free (priv->sessions);
324   priv->sessions = NULL;
325 }
326
327 /* A client is finalized when the connection is broken */
328 static void
329 gst_rtsp_client_finalize (GObject * obj)
330 {
331   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
332   GstRTSPClientPrivate *priv = client->priv;
333
334   GST_INFO ("finalize client %p", client);
335
336   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
337
338   if (priv->watch)
339     g_source_destroy ((GSource *) priv->watch);
340
341   client_cleanup_sessions (client);
342
343   if (priv->connection)
344     gst_rtsp_connection_free (priv->connection);
345   if (priv->session_pool)
346     g_object_unref (priv->session_pool);
347   if (priv->mount_points)
348     g_object_unref (priv->mount_points);
349   if (priv->auth)
350     g_object_unref (priv->auth);
351   if (priv->thread_pool)
352     g_object_unref (priv->thread_pool);
353
354   if (priv->path)
355     g_free (priv->path);
356   if (priv->media) {
357     gst_rtsp_media_unprepare (priv->media);
358     g_object_unref (priv->media);
359   }
360
361   g_free (priv->server_ip);
362   g_mutex_clear (&priv->lock);
363   g_mutex_clear (&priv->send_lock);
364
365   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
366 }
367
368 static void
369 gst_rtsp_client_get_property (GObject * object, guint propid,
370     GValue * value, GParamSpec * pspec)
371 {
372   GstRTSPClient *client = GST_RTSP_CLIENT (object);
373
374   switch (propid) {
375     case PROP_SESSION_POOL:
376       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
377       break;
378     case PROP_MOUNT_POINTS:
379       g_value_take_object (value, gst_rtsp_client_get_mount_points (client));
380       break;
381     default:
382       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
383   }
384 }
385
386 static void
387 gst_rtsp_client_set_property (GObject * object, guint propid,
388     const GValue * value, GParamSpec * pspec)
389 {
390   GstRTSPClient *client = GST_RTSP_CLIENT (object);
391
392   switch (propid) {
393     case PROP_SESSION_POOL:
394       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
395       break;
396     case PROP_MOUNT_POINTS:
397       gst_rtsp_client_set_mount_points (client, g_value_get_object (value));
398       break;
399     default:
400       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
401   }
402 }
403
404 /**
405  * gst_rtsp_client_new:
406  *
407  * Create a new #GstRTSPClient instance.
408  *
409  * Returns: a new #GstRTSPClient
410  */
411 GstRTSPClient *
412 gst_rtsp_client_new (void)
413 {
414   GstRTSPClient *result;
415
416   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
417
418   return result;
419 }
420
421 static void
422 send_message (GstRTSPClient * client, GstRTSPSession * session,
423     GstRTSPMessage * message, gboolean close)
424 {
425   GstRTSPClientPrivate *priv = client->priv;
426
427   gst_rtsp_message_add_header (message, GST_RTSP_HDR_SERVER,
428       "GStreamer RTSP server");
429
430   /* remove any previous header */
431   gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
432
433   /* add the new session header for new session ids */
434   if (session) {
435     gst_rtsp_message_take_header (message, GST_RTSP_HDR_SESSION,
436         gst_rtsp_session_get_header (session));
437   }
438
439   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
440     gst_rtsp_message_dump (message);
441   }
442
443   if (close)
444     gst_rtsp_message_add_header (message, GST_RTSP_HDR_CONNECTION, "close");
445
446   g_mutex_lock (&priv->send_lock);
447   if (priv->send_func)
448     priv->send_func (client, message, close, priv->send_data);
449   g_mutex_unlock (&priv->send_lock);
450
451   gst_rtsp_message_unset (message);
452 }
453
454 static void
455 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
456     GstRTSPContext * ctx)
457 {
458   gst_rtsp_message_init_response (ctx->response, code,
459       gst_rtsp_status_as_text (code), ctx->request);
460
461   send_message (client, NULL, ctx->response, FALSE);
462 }
463
464 static gboolean
465 paths_are_equal (const gchar * path1, const gchar * path2, gint len2)
466 {
467   if (path1 == NULL || path2 == NULL)
468     return FALSE;
469
470   if (strlen (path1) != len2)
471     return FALSE;
472
473   if (strncmp (path1, path2, len2))
474     return FALSE;
475
476   return TRUE;
477 }
478
479 /* this function is called to initially find the media for the DESCRIBE request
480  * but is cached for when the same client (without breaking the connection) is
481  * doing a setup for the exact same url. */
482 static GstRTSPMedia *
483 find_media (GstRTSPClient * client, GstRTSPContext * ctx, gint * matched)
484 {
485   GstRTSPClientPrivate *priv = client->priv;
486   GstRTSPMediaFactory *factory;
487   GstRTSPMedia *media;
488   gchar *path;
489   gint path_len;
490
491   if (!priv->mount_points)
492     goto no_mount_points;
493
494   path = ctx->uri->abspath;
495
496   /* find the longest matching factory for the uri first */
497   if (!(factory = gst_rtsp_mount_points_match (priv->mount_points,
498               path, matched)))
499     goto no_factory;
500
501   ctx->factory = factory;
502
503   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS))
504     goto no_factory_access;
505
506   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_CONSTRUCT))
507     goto not_authorized;
508
509   if (matched)
510     path_len = *matched;
511   else
512     path_len = strlen (path);
513
514   if (!paths_are_equal (priv->path, path, path_len)) {
515     GstRTSPThread *thread;
516
517     /* remove any previously cached values before we try to construct a new
518      * media for uri */
519     if (priv->path)
520       g_free (priv->path);
521     priv->path = NULL;
522     if (priv->media) {
523       gst_rtsp_media_unprepare (priv->media);
524       g_object_unref (priv->media);
525     }
526     priv->media = NULL;
527
528     /* prepare the media and add it to the pipeline */
529     if (!(media = gst_rtsp_media_factory_construct (factory, ctx->uri)))
530       goto no_media;
531
532     ctx->media = media;
533
534     thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
535         GST_RTSP_THREAD_TYPE_MEDIA, ctx);
536     if (thread == NULL)
537       goto no_thread;
538
539     /* prepare the media */
540     if (!(gst_rtsp_media_prepare (media, thread)))
541       goto no_prepare;
542
543     /* now keep track of the uri and the media */
544     priv->path = g_strndup (path, path_len);
545     priv->media = media;
546   } else {
547     /* we have seen this path before, used cached media */
548     media = priv->media;
549     ctx->media = media;
550     GST_INFO ("reusing cached media %p for path %s", media, priv->path);
551   }
552
553   g_object_unref (factory);
554   ctx->factory = NULL;
555
556   if (media)
557     g_object_ref (media);
558
559   return media;
560
561   /* ERRORS */
562 no_mount_points:
563   {
564     GST_ERROR ("client %p: no mount points configured", client);
565     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
566     return NULL;
567   }
568 no_factory:
569   {
570     GST_ERROR ("client %p: no factory for uri %s", client, path);
571     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
572     return NULL;
573   }
574 no_factory_access:
575   {
576     GST_ERROR ("client %p: not authorized to see factory uri %s", client, path);
577     return NULL;
578   }
579 not_authorized:
580   {
581     GST_ERROR ("client %p: not authorized for factory uri %s", client, path);
582     return NULL;
583   }
584 no_media:
585   {
586     GST_ERROR ("client %p: can't create media", client);
587     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
588     g_object_unref (factory);
589     ctx->factory = NULL;
590     return NULL;
591   }
592 no_thread:
593   {
594     GST_ERROR ("client %p: can't create thread", client);
595     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
596     g_object_unref (media);
597     ctx->media = NULL;
598     g_object_unref (factory);
599     ctx->factory = NULL;
600     return NULL;
601   }
602 no_prepare:
603   {
604     GST_ERROR ("client %p: can't prepare media", client);
605     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
606     g_object_unref (media);
607     ctx->media = NULL;
608     g_object_unref (factory);
609     ctx->factory = NULL;
610     return NULL;
611   }
612 }
613
614 static gboolean
615 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
616 {
617   GstRTSPClientPrivate *priv = client->priv;
618   GstRTSPMessage message = { 0 };
619   GstMapInfo map_info;
620   guint8 *data;
621   guint usize;
622
623   gst_rtsp_message_init_data (&message, channel);
624
625   /* FIXME, need some sort of iovec RTSPMessage here */
626   if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ))
627     return FALSE;
628
629   gst_rtsp_message_take_body (&message, map_info.data, map_info.size);
630
631   g_mutex_lock (&priv->send_lock);
632   if (priv->send_func)
633     priv->send_func (client, &message, FALSE, priv->send_data);
634   g_mutex_unlock (&priv->send_lock);
635
636   gst_rtsp_message_steal_body (&message, &data, &usize);
637   gst_buffer_unmap (buffer, &map_info);
638
639   gst_rtsp_message_unset (&message);
640
641   return TRUE;
642 }
643
644 static void
645 link_transport (GstRTSPClient * client, GstRTSPSession * session,
646     GstRTSPStreamTransport * trans)
647 {
648   GstRTSPClientPrivate *priv = client->priv;
649
650   GST_DEBUG ("client %p: linking transport %p", client, trans);
651
652   gst_rtsp_stream_transport_set_callbacks (trans,
653       (GstRTSPSendFunc) do_send_data,
654       (GstRTSPSendFunc) do_send_data, client, NULL);
655
656   priv->transports = g_list_prepend (priv->transports, trans);
657
658   /* make sure our session can't expire */
659   gst_rtsp_session_prevent_expire (session);
660 }
661
662 static void
663 unlink_transport (GstRTSPClient * client, GstRTSPSession * session,
664     GstRTSPStreamTransport * trans)
665 {
666   GstRTSPClientPrivate *priv = client->priv;
667
668   GST_DEBUG ("client %p: unlinking transport %p", client, trans);
669
670   gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
671
672   priv->transports = g_list_remove (priv->transports, trans);
673
674   /* our session can now expire */
675   gst_rtsp_session_allow_expire (session);
676 }
677
678 static void
679 unlink_session_transports (GstRTSPClient * client, GstRTSPSession * session,
680     GstRTSPSessionMedia * sessmedia)
681 {
682   guint n_streams, i;
683
684   n_streams =
685       gst_rtsp_media_n_streams (gst_rtsp_session_media_get_media (sessmedia));
686   for (i = 0; i < n_streams; i++) {
687     GstRTSPStreamTransport *trans;
688     const GstRTSPTransport *tr;
689
690     /* get the transport, if there is no transport configured, skip this stream */
691     trans = gst_rtsp_session_media_get_transport (sessmedia, i);
692     if (trans == NULL)
693       continue;
694
695     tr = gst_rtsp_stream_transport_get_transport (trans);
696
697     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
698       /* for TCP, unlink the stream from the TCP connection of the client */
699       unlink_transport (client, session, trans);
700     }
701   }
702 }
703
704 static void
705 close_connection (GstRTSPClient * client)
706 {
707   GstRTSPClientPrivate *priv = client->priv;
708   const gchar *tunnelid;
709
710   GST_DEBUG ("client %p: closing connection", client);
711
712   if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
713     g_mutex_lock (&tunnels_lock);
714     /* remove from tunnelids */
715     g_hash_table_remove (tunnels, tunnelid);
716     g_mutex_unlock (&tunnels_lock);
717   }
718
719   gst_rtsp_connection_close (priv->connection);
720 }
721
722 static gboolean
723 handle_teardown_request (GstRTSPClient * client, GstRTSPContext * ctx)
724 {
725   GstRTSPClientPrivate *priv = client->priv;
726   GstRTSPSession *session;
727   GstRTSPSessionMedia *sessmedia;
728   GstRTSPStatusCode code;
729   const gchar *path;
730   gint matched;
731
732   if (!ctx->session)
733     goto no_session;
734
735   session = ctx->session;
736
737   if (!ctx->uri)
738     goto no_uri;
739
740   path = ctx->uri->abspath;
741
742   /* get a handle to the configuration of the media in the session */
743   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
744   if (!sessmedia)
745     goto not_found;
746
747   /* only aggregate control for now.. */
748   if (path[matched] != '\0')
749     goto no_aggregate;
750
751   ctx->sessmedia = sessmedia;
752
753   /* we emit the signal before closing the connection */
754   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST],
755       0, ctx);
756
757   /* unlink the all TCP callbacks */
758   unlink_session_transports (client, session, sessmedia);
759
760   /* remove the session from the watched sessions */
761   client_unwatch_session (client, session);
762
763   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
764
765   /* unmanage the media in the session, returns false if all media session
766    * are torn down. */
767   if (!gst_rtsp_session_release_media (session, sessmedia)) {
768     /* remove the session */
769     gst_rtsp_session_pool_remove (priv->session_pool, session);
770   }
771   /* construct the response now */
772   code = GST_RTSP_STS_OK;
773   gst_rtsp_message_init_response (ctx->response, code,
774       gst_rtsp_status_as_text (code), ctx->request);
775
776   send_message (client, session, ctx->response, TRUE);
777
778   return TRUE;
779
780   /* ERRORS */
781 no_session:
782   {
783     GST_ERROR ("client %p: no session", client);
784     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
785     return FALSE;
786   }
787 no_uri:
788   {
789     GST_ERROR ("client %p: no uri supplied", client);
790     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
791     return FALSE;
792   }
793 not_found:
794   {
795     GST_ERROR ("client %p: no media for uri", client);
796     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
797     return FALSE;
798   }
799 no_aggregate:
800   {
801     GST_ERROR ("client %p: no aggregate path %s", client, path);
802     send_generic_response (client,
803         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
804     return FALSE;
805   }
806 }
807
808 static GstRTSPResult
809 default_params_set (GstRTSPClient * client, GstRTSPContext * ctx)
810 {
811   GstRTSPResult res;
812
813   res = gst_rtsp_params_set (client, ctx);
814
815   return res;
816 }
817
818 static GstRTSPResult
819 default_params_get (GstRTSPClient * client, GstRTSPContext * ctx)
820 {
821   GstRTSPResult res;
822
823   res = gst_rtsp_params_get (client, ctx);
824
825   return res;
826 }
827
828 static gboolean
829 handle_get_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
830 {
831   GstRTSPResult res;
832   guint8 *data;
833   guint size;
834
835   res = gst_rtsp_message_get_body (ctx->request, &data, &size);
836   if (res != GST_RTSP_OK)
837     goto bad_request;
838
839   if (size == 0) {
840     /* no body, keep-alive request */
841     send_generic_response (client, GST_RTSP_STS_OK, ctx);
842   } else {
843     /* there is a body, handle the params */
844     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_get (client, ctx);
845     if (res != GST_RTSP_OK)
846       goto bad_request;
847
848     send_message (client, ctx->session, ctx->response, FALSE);
849   }
850
851   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST],
852       0, ctx);
853
854   return TRUE;
855
856   /* ERRORS */
857 bad_request:
858   {
859     GST_ERROR ("client %p: bad request", client);
860     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
861     return FALSE;
862   }
863 }
864
865 static gboolean
866 handle_set_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
867 {
868   GstRTSPResult res;
869   guint8 *data;
870   guint size;
871
872   res = gst_rtsp_message_get_body (ctx->request, &data, &size);
873   if (res != GST_RTSP_OK)
874     goto bad_request;
875
876   if (size == 0) {
877     /* no body, keep-alive request */
878     send_generic_response (client, GST_RTSP_STS_OK, ctx);
879   } else {
880     /* there is a body, handle the params */
881     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_set (client, ctx);
882     if (res != GST_RTSP_OK)
883       goto bad_request;
884
885     send_message (client, ctx->session, ctx->response, FALSE);
886   }
887
888   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST],
889       0, ctx);
890
891   return TRUE;
892
893   /* ERRORS */
894 bad_request:
895   {
896     GST_ERROR ("client %p: bad request", client);
897     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
898     return FALSE;
899   }
900 }
901
902 static gboolean
903 handle_pause_request (GstRTSPClient * client, GstRTSPContext * ctx)
904 {
905   GstRTSPSession *session;
906   GstRTSPSessionMedia *sessmedia;
907   GstRTSPStatusCode code;
908   GstRTSPState rtspstate;
909   const gchar *path;
910   gint matched;
911
912   if (!(session = ctx->session))
913     goto no_session;
914
915   if (!ctx->uri)
916     goto no_uri;
917
918   path = ctx->uri->abspath;
919
920   /* get a handle to the configuration of the media in the session */
921   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
922   if (!sessmedia)
923     goto not_found;
924
925   if (path[matched] != '\0')
926     goto no_aggregate;
927
928   ctx->sessmedia = sessmedia;
929
930   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
931   /* the session state must be playing or recording */
932   if (rtspstate != GST_RTSP_STATE_PLAYING &&
933       rtspstate != GST_RTSP_STATE_RECORDING)
934     goto invalid_state;
935
936   /* unlink the all TCP callbacks */
937   unlink_session_transports (client, session, sessmedia);
938
939   /* then pause sending */
940   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PAUSED);
941
942   /* construct the response now */
943   code = GST_RTSP_STS_OK;
944   gst_rtsp_message_init_response (ctx->response, code,
945       gst_rtsp_status_as_text (code), ctx->request);
946
947   send_message (client, session, ctx->response, FALSE);
948
949   /* the state is now READY */
950   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
951
952   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST], 0, ctx);
953
954   return TRUE;
955
956   /* ERRORS */
957 no_session:
958   {
959     GST_ERROR ("client %p: no seesion", client);
960     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
961     return FALSE;
962   }
963 no_uri:
964   {
965     GST_ERROR ("client %p: no uri supplied", client);
966     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
967     return FALSE;
968   }
969 not_found:
970   {
971     GST_ERROR ("client %p: no media for uri", client);
972     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
973     return FALSE;
974   }
975 no_aggregate:
976   {
977     GST_ERROR ("client %p: no aggregate path %s", client, path);
978     send_generic_response (client,
979         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
980     return FALSE;
981   }
982 invalid_state:
983   {
984     GST_ERROR ("client %p: not PLAYING or RECORDING", client);
985     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
986         ctx);
987     return FALSE;
988   }
989 }
990
991 static gboolean
992 handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
993 {
994   GstRTSPSession *session;
995   GstRTSPSessionMedia *sessmedia;
996   GstRTSPMedia *media;
997   GstRTSPStatusCode code;
998   GString *rtpinfo;
999   guint n_streams, i, infocount;
1000   gchar *str;
1001   GstRTSPTimeRange *range;
1002   GstRTSPResult res;
1003   GstRTSPState rtspstate;
1004   GstRTSPRangeUnit unit = GST_RTSP_RANGE_NPT;
1005   const gchar *path;
1006   gint matched;
1007
1008   if (!(session = ctx->session))
1009     goto no_session;
1010
1011   if (!ctx->uri)
1012     goto no_uri;
1013
1014   path = ctx->uri->abspath;
1015
1016   /* get a handle to the configuration of the media in the session */
1017   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1018   if (!sessmedia)
1019     goto not_found;
1020
1021   if (path[matched] != '\0')
1022     goto no_aggregate;
1023
1024   ctx->sessmedia = sessmedia;
1025   ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
1026
1027   /* the session state must be playing or ready */
1028   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1029   if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
1030     goto invalid_state;
1031
1032   /* parse the range header if we have one */
1033   res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RANGE, &str, 0);
1034   if (res == GST_RTSP_OK) {
1035     if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
1036       /* we have a range, seek to the position */
1037       unit = range->unit;
1038       gst_rtsp_media_seek (media, range);
1039       gst_rtsp_range_free (range);
1040     }
1041   }
1042
1043   /* grab RTPInfo from the payloaders now */
1044   rtpinfo = g_string_new ("");
1045
1046   n_streams = gst_rtsp_media_n_streams (media);
1047   for (i = 0, infocount = 0; i < n_streams; i++) {
1048     GstRTSPStreamTransport *trans;
1049     GstRTSPStream *stream;
1050     const GstRTSPTransport *tr;
1051     gchar *uristr;
1052     guint rtptime, seq;
1053
1054     /* get the transport, if there is no transport configured, skip this stream */
1055     trans = gst_rtsp_session_media_get_transport (sessmedia, i);
1056     if (trans == NULL) {
1057       GST_INFO ("stream %d is not configured", i);
1058       continue;
1059     }
1060     tr = gst_rtsp_stream_transport_get_transport (trans);
1061
1062     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1063       /* for TCP, link the stream to the TCP connection of the client */
1064       link_transport (client, session, trans);
1065     }
1066
1067     stream = gst_rtsp_stream_transport_get_stream (trans);
1068     if (gst_rtsp_stream_get_rtpinfo (stream, &rtptime, &seq)) {
1069       if (infocount > 0)
1070         g_string_append (rtpinfo, ", ");
1071
1072       uristr = gst_rtsp_url_get_request_uri (ctx->uri);
1073       g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
1074           uristr, i, seq, rtptime);
1075       g_free (uristr);
1076
1077       infocount++;
1078     } else {
1079       GST_WARNING ("RTP-Info cannot be determined for stream %d", i);
1080     }
1081   }
1082
1083   /* construct the response now */
1084   code = GST_RTSP_STS_OK;
1085   gst_rtsp_message_init_response (ctx->response, code,
1086       gst_rtsp_status_as_text (code), ctx->request);
1087
1088   /* add the RTP-Info header */
1089   if (infocount > 0) {
1090     str = g_string_free (rtpinfo, FALSE);
1091     gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RTP_INFO, str);
1092   } else {
1093     g_string_free (rtpinfo, TRUE);
1094   }
1095
1096   /* add the range */
1097   str = gst_rtsp_media_get_range_string (media, TRUE, unit);
1098   if (str)
1099     gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RANGE, str);
1100
1101   send_message (client, session, ctx->response, FALSE);
1102
1103   /* start playing after sending the request */
1104   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PLAYING);
1105
1106   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_PLAYING);
1107
1108   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST], 0, ctx);
1109
1110   return TRUE;
1111
1112   /* ERRORS */
1113 no_session:
1114   {
1115     GST_ERROR ("client %p: no session", client);
1116     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1117     return FALSE;
1118   }
1119 no_uri:
1120   {
1121     GST_ERROR ("client %p: no uri supplied", client);
1122     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1123     return FALSE;
1124   }
1125 not_found:
1126   {
1127     GST_ERROR ("client %p: media not found", client);
1128     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1129     return FALSE;
1130   }
1131 no_aggregate:
1132   {
1133     GST_ERROR ("client %p: no aggregate path %s", client, path);
1134     send_generic_response (client,
1135         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1136     return FALSE;
1137   }
1138 invalid_state:
1139   {
1140     GST_ERROR ("client %p: not PLAYING or READY", client);
1141     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1142         ctx);
1143     return FALSE;
1144   }
1145 }
1146
1147 static void
1148 do_keepalive (GstRTSPSession * session)
1149 {
1150   GST_INFO ("keep session %p alive", session);
1151   gst_rtsp_session_touch (session);
1152 }
1153
1154 /* parse @transport and return a valid transport in @tr. only transports
1155  * from @supported are returned. Returns FALSE if no valid transport
1156  * was found. */
1157 static gboolean
1158 parse_transport (const char *transport, GstRTSPLowerTrans supported,
1159     GstRTSPTransport * tr)
1160 {
1161   gint i;
1162   gboolean res;
1163   gchar **transports;
1164
1165   res = FALSE;
1166   gst_rtsp_transport_init (tr);
1167
1168   GST_DEBUG ("parsing transports %s", transport);
1169
1170   transports = g_strsplit (transport, ",", 0);
1171
1172   /* loop through the transports, try to parse */
1173   for (i = 0; transports[i]; i++) {
1174     res = gst_rtsp_transport_parse (transports[i], tr);
1175     if (res != GST_RTSP_OK) {
1176       /* no valid transport, search some more */
1177       GST_WARNING ("could not parse transport %s", transports[i]);
1178       goto next;
1179     }
1180
1181     /* we have a transport, see if it's RTP/AVP */
1182     if (tr->trans != GST_RTSP_TRANS_RTP || tr->profile != GST_RTSP_PROFILE_AVP) {
1183       GST_WARNING ("invalid transport %s", transports[i]);
1184       goto next;
1185     }
1186
1187     if (!(tr->lower_transport & supported)) {
1188       GST_WARNING ("unsupported transport %s", transports[i]);
1189       goto next;
1190     }
1191
1192     /* we have a valid transport */
1193     GST_INFO ("found valid transport %s", transports[i]);
1194     res = TRUE;
1195     break;
1196
1197   next:
1198     gst_rtsp_transport_init (tr);
1199   }
1200   g_strfreev (transports);
1201
1202   return res;
1203 }
1204
1205 static gboolean
1206 handle_blocksize (GstRTSPMedia * media, GstRTSPStream * stream,
1207     GstRTSPMessage * request)
1208 {
1209   gchar *blocksize_str;
1210   gboolean ret = TRUE;
1211
1212   if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_BLOCKSIZE,
1213           &blocksize_str, 0) == GST_RTSP_OK) {
1214     guint64 blocksize;
1215     gchar *end;
1216
1217     blocksize = g_ascii_strtoull (blocksize_str, &end, 10);
1218     if (end == blocksize_str) {
1219       GST_ERROR ("failed to parse blocksize");
1220       ret = FALSE;
1221     } else {
1222       /* we don't want to change the mtu when this media
1223        * can be shared because it impacts other clients */
1224       if (gst_rtsp_media_is_shared (media))
1225         return TRUE;
1226
1227       if (blocksize > G_MAXUINT)
1228         blocksize = G_MAXUINT;
1229       gst_rtsp_stream_set_mtu (stream, blocksize);
1230     }
1231   }
1232   return ret;
1233 }
1234
1235 static gboolean
1236 default_configure_client_transport (GstRTSPClient * client,
1237     GstRTSPContext * ctx, GstRTSPTransport * ct)
1238 {
1239   GstRTSPClientPrivate *priv = client->priv;
1240
1241   /* we have a valid transport now, set the destination of the client. */
1242   if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
1243     gboolean use_client_settings;
1244
1245     use_client_settings =
1246         gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS);
1247
1248     if (ct->destination && use_client_settings) {
1249       GstRTSPAddress *addr;
1250
1251       addr = gst_rtsp_stream_reserve_address (ctx->stream, ct->destination,
1252           ct->port.min, ct->port.max - ct->port.min + 1, ct->ttl);
1253
1254       if (addr == NULL)
1255         goto no_address;
1256
1257       gst_rtsp_address_free (addr);
1258     } else {
1259       GstRTSPAddress *addr;
1260       GSocketFamily family;
1261
1262       family = priv->is_ipv6 ? G_SOCKET_FAMILY_IPV6 : G_SOCKET_FAMILY_IPV4;
1263
1264       addr = gst_rtsp_stream_get_multicast_address (ctx->stream, family);
1265       if (addr == NULL)
1266         goto no_address;
1267
1268       g_free (ct->destination);
1269       ct->destination = g_strdup (addr->address);
1270       ct->port.min = addr->port;
1271       ct->port.max = addr->port + addr->n_ports - 1;
1272       ct->ttl = addr->ttl;
1273
1274       gst_rtsp_address_free (addr);
1275     }
1276   } else {
1277     GstRTSPUrl *url;
1278
1279     url = gst_rtsp_connection_get_url (priv->connection);
1280     g_free (ct->destination);
1281     ct->destination = g_strdup (url->host);
1282
1283     if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
1284       /* check if the client selected channels for TCP */
1285       if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
1286         gst_rtsp_session_media_alloc_channels (ctx->sessmedia,
1287             &ct->interleaved);
1288       }
1289     }
1290   }
1291   return TRUE;
1292
1293   /* ERRORS */
1294 no_address:
1295   {
1296     GST_ERROR_OBJECT (client, "failed to acquire address for stream");
1297     return FALSE;
1298   }
1299 }
1300
1301 static GstRTSPTransport *
1302 make_server_transport (GstRTSPClient * client, GstRTSPContext * ctx,
1303     GstRTSPTransport * ct)
1304 {
1305   GstRTSPTransport *st;
1306   GInetAddress *addr;
1307   GSocketFamily family;
1308
1309   /* prepare the server transport */
1310   gst_rtsp_transport_new (&st);
1311
1312   st->trans = ct->trans;
1313   st->profile = ct->profile;
1314   st->lower_transport = ct->lower_transport;
1315
1316   addr = g_inet_address_new_from_string (ct->destination);
1317
1318   if (!addr) {
1319     GST_ERROR ("failed to get inet addr from client destination");
1320     family = G_SOCKET_FAMILY_IPV4;
1321   } else {
1322     family = g_inet_address_get_family (addr);
1323     g_object_unref (addr);
1324     addr = NULL;
1325   }
1326
1327   switch (st->lower_transport) {
1328     case GST_RTSP_LOWER_TRANS_UDP:
1329       st->client_port = ct->client_port;
1330       gst_rtsp_stream_get_server_port (ctx->stream, &st->server_port, family);
1331       break;
1332     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
1333       st->port = ct->port;
1334       st->destination = g_strdup (ct->destination);
1335       st->ttl = ct->ttl;
1336       break;
1337     case GST_RTSP_LOWER_TRANS_TCP:
1338       st->interleaved = ct->interleaved;
1339     default:
1340       break;
1341   }
1342
1343   gst_rtsp_stream_get_ssrc (ctx->stream, &st->ssrc);
1344
1345   return st;
1346 }
1347
1348 static gboolean
1349 handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
1350 {
1351   GstRTSPClientPrivate *priv = client->priv;
1352   GstRTSPResult res;
1353   GstRTSPUrl *uri;
1354   gchar *transport;
1355   GstRTSPTransport *ct, *st;
1356   GstRTSPLowerTrans supported;
1357   GstRTSPStatusCode code;
1358   GstRTSPSession *session;
1359   GstRTSPStreamTransport *trans;
1360   gchar *trans_str;
1361   GstRTSPSessionMedia *sessmedia;
1362   GstRTSPMedia *media;
1363   GstRTSPStream *stream;
1364   GstRTSPState rtspstate;
1365   GstRTSPClientClass *klass;
1366   gchar *path, *control;
1367   gint matched;
1368
1369   if (!ctx->uri)
1370     goto no_uri;
1371
1372   uri = ctx->uri;
1373   path = uri->abspath;
1374
1375   /* parse the transport */
1376   res =
1377       gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_TRANSPORT,
1378       &transport, 0);
1379   if (res != GST_RTSP_OK)
1380     goto no_transport;
1381
1382   /* we create the session after parsing stuff so that we don't make
1383    * a session for malformed requests */
1384   if (priv->session_pool == NULL)
1385     goto no_pool;
1386
1387   session = ctx->session;
1388
1389   if (session) {
1390     g_object_ref (session);
1391     /* get a handle to the configuration of the media in the session, this can
1392      * return NULL if this is a new url to manage in this session. */
1393     sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1394   } else {
1395     /* we need a new media configuration in this session */
1396     sessmedia = NULL;
1397   }
1398
1399   /* we have no session media, find one and manage it */
1400   if (sessmedia == NULL) {
1401     /* get a handle to the configuration of the media in the session */
1402     media = find_media (client, ctx, &matched);
1403   } else {
1404     if ((media = gst_rtsp_session_media_get_media (sessmedia)))
1405       g_object_ref (media);
1406   }
1407   /* no media, not found then */
1408   if (media == NULL)
1409     goto media_not_found;
1410
1411   /* path is what matched. We can modify the parsed uri in place */
1412   path[matched] = '\0';
1413   /* control is remainder */
1414   control = &path[matched + 1];
1415
1416   /* find the stream now using the control part */
1417   stream = gst_rtsp_media_find_stream (media, control);
1418   if (stream == NULL)
1419     goto stream_not_found;
1420
1421   /* now we have a uri identifying a valid media and stream */
1422   ctx->stream = stream;
1423   ctx->media = media;
1424
1425   if (session == NULL) {
1426     /* create a session if this fails we probably reached our session limit or
1427      * something. */
1428     if (!(session = gst_rtsp_session_pool_create (priv->session_pool)))
1429       goto service_unavailable;
1430
1431     /* make sure this client is closed when the session is closed */
1432     client_watch_session (client, session);
1433
1434     /* signal new session */
1435     g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_NEW_SESSION], 0,
1436         session);
1437
1438     ctx->session = session;
1439   }
1440
1441   if (sessmedia == NULL) {
1442     /* manage the media in our session now, if not done already  */
1443     sessmedia = gst_rtsp_session_manage_media (session, path, media);
1444     /* if we stil have no media, error */
1445     if (sessmedia == NULL)
1446       goto sessmedia_unavailable;
1447   } else {
1448     g_object_unref (media);
1449   }
1450
1451   ctx->sessmedia = sessmedia;
1452
1453   /* set blocksize on this stream */
1454   if (!handle_blocksize (media, stream, ctx->request))
1455     goto invalid_blocksize;
1456
1457   gst_rtsp_transport_new (&ct);
1458
1459   /* our supported transports */
1460   supported = gst_rtsp_stream_get_protocols (stream);
1461
1462   /* parse and find a usable supported transport */
1463   if (!parse_transport (transport, supported, ct))
1464     goto unsupported_transports;
1465
1466   /* update the client transport */
1467   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1468   if (!klass->configure_client_transport (client, ctx, ct))
1469     goto unsupported_client_transport;
1470
1471   /* set in the session media transport */
1472   trans = gst_rtsp_session_media_set_transport (sessmedia, stream, ct);
1473
1474   /* configure keepalive for this transport */
1475   gst_rtsp_stream_transport_set_keepalive (trans,
1476       (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
1477
1478   /* create and serialize the server transport */
1479   st = make_server_transport (client, ctx, ct);
1480   trans_str = gst_rtsp_transport_as_text (st);
1481   gst_rtsp_transport_free (st);
1482
1483   /* construct the response now */
1484   code = GST_RTSP_STS_OK;
1485   gst_rtsp_message_init_response (ctx->response, code,
1486       gst_rtsp_status_as_text (code), ctx->request);
1487
1488   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_TRANSPORT,
1489       trans_str);
1490   g_free (trans_str);
1491
1492   send_message (client, session, ctx->response, FALSE);
1493
1494   /* update the state */
1495   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1496   switch (rtspstate) {
1497     case GST_RTSP_STATE_PLAYING:
1498     case GST_RTSP_STATE_RECORDING:
1499     case GST_RTSP_STATE_READY:
1500       /* no state change */
1501       break;
1502     default:
1503       gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
1504       break;
1505   }
1506   g_object_unref (session);
1507
1508   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST], 0, ctx);
1509
1510   return TRUE;
1511
1512   /* ERRORS */
1513 no_uri:
1514   {
1515     GST_ERROR ("client %p: no uri", client);
1516     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1517     return FALSE;
1518   }
1519 no_transport:
1520   {
1521     GST_ERROR ("client %p: no transport", client);
1522     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
1523     return FALSE;
1524   }
1525 no_pool:
1526   {
1527     GST_ERROR ("client %p: no session pool configured", client);
1528     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1529     return FALSE;
1530   }
1531 media_not_found:
1532   {
1533     GST_ERROR ("client %p: media '%s' not found", client, path);
1534     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1535     return FALSE;
1536   }
1537 stream_not_found:
1538   {
1539     GST_ERROR ("client %p: stream '%s' not found", client, control);
1540     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1541     g_object_unref (media);
1542     return FALSE;
1543   }
1544 service_unavailable:
1545   {
1546     GST_ERROR ("client %p: can't create session", client);
1547     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1548     g_object_unref (media);
1549     return FALSE;
1550   }
1551 sessmedia_unavailable:
1552   {
1553     GST_ERROR ("client %p: can't create session media", client);
1554     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1555     g_object_unref (media);
1556     g_object_unref (session);
1557     return FALSE;
1558   }
1559 invalid_blocksize:
1560   {
1561     GST_ERROR ("client %p: invalid blocksize", client);
1562     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1563     g_object_unref (session);
1564     return FALSE;
1565   }
1566 unsupported_transports:
1567   {
1568     GST_ERROR ("client %p: unsupported transports", client);
1569     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
1570     gst_rtsp_transport_free (ct);
1571     g_object_unref (session);
1572     return FALSE;
1573   }
1574 unsupported_client_transport:
1575   {
1576     GST_ERROR ("client %p: unsupported client transport", client);
1577     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
1578     gst_rtsp_transport_free (ct);
1579     g_object_unref (session);
1580     return FALSE;
1581   }
1582 }
1583
1584 static GstSDPMessage *
1585 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
1586 {
1587   GstRTSPClientPrivate *priv = client->priv;
1588   GstSDPMessage *sdp;
1589   GstSDPInfo info;
1590   const gchar *proto;
1591
1592   gst_sdp_message_new (&sdp);
1593
1594   /* some standard things first */
1595   gst_sdp_message_set_version (sdp, "0");
1596
1597   if (priv->is_ipv6)
1598     proto = "IP6";
1599   else
1600     proto = "IP4";
1601
1602   gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", proto,
1603       priv->server_ip);
1604
1605   gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
1606   gst_sdp_message_set_information (sdp, "rtsp-server");
1607   gst_sdp_message_add_time (sdp, "0", "0", NULL);
1608   gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
1609   gst_sdp_message_add_attribute (sdp, "type", "broadcast");
1610   gst_sdp_message_add_attribute (sdp, "control", "*");
1611
1612   info.is_ipv6 = priv->is_ipv6;
1613   info.server_ip = priv->server_ip;
1614
1615   /* create an SDP for the media object */
1616   if (!gst_rtsp_sdp_from_media (sdp, &info, media))
1617     goto no_sdp;
1618
1619   return sdp;
1620
1621   /* ERRORS */
1622 no_sdp:
1623   {
1624     GST_ERROR ("client %p: could not create SDP", client);
1625     gst_sdp_message_free (sdp);
1626     return NULL;
1627   }
1628 }
1629
1630 /* for the describe we must generate an SDP */
1631 static gboolean
1632 handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
1633 {
1634   GstRTSPResult res;
1635   GstSDPMessage *sdp;
1636   guint i, str_len;
1637   gchar *str, *str_query, *content_base;
1638   GstRTSPMedia *media;
1639   GstRTSPClientClass *klass;
1640
1641   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1642
1643   if (!ctx->uri)
1644     goto no_uri;
1645
1646   /* check what kind of format is accepted, we don't really do anything with it
1647    * and always return SDP for now. */
1648   for (i = 0; i++;) {
1649     gchar *accept;
1650
1651     res =
1652         gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_ACCEPT,
1653         &accept, i);
1654     if (res == GST_RTSP_ENOTIMPL)
1655       break;
1656
1657     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
1658       break;
1659   }
1660
1661   /* find the media object for the uri */
1662   if (!(media = find_media (client, ctx, NULL)))
1663     goto no_media;
1664
1665   /* create an SDP for the media object on this client */
1666   if (!(sdp = klass->create_sdp (client, media)))
1667     goto no_sdp;
1668
1669   g_object_unref (media);
1670
1671   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
1672       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
1673
1674   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_CONTENT_TYPE,
1675       "application/sdp");
1676
1677   /* content base for some clients that might screw up creating the setup uri */
1678   str = gst_rtsp_url_get_request_uri (ctx->uri);
1679   str_len = strlen (str);
1680
1681   /* check for query part */
1682   if (ctx->uri->query != NULL) {
1683     str_query = g_strrstr (str, "?");
1684     *str_query = '\0';
1685     str_len = strlen (str);
1686   }
1687
1688   /* check for trailing '/' and append one */
1689   if (str[str_len - 1] != '/') {
1690     content_base = g_malloc (str_len + 2);
1691     memcpy (content_base, str, str_len);
1692     content_base[str_len] = '/';
1693     content_base[str_len + 1] = '\0';
1694     g_free (str);
1695   } else {
1696     content_base = str;
1697   }
1698
1699   GST_INFO ("adding content-base: %s", content_base);
1700
1701   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_CONTENT_BASE,
1702       content_base);
1703   g_free (content_base);
1704
1705   /* add SDP to the response body */
1706   str = gst_sdp_message_as_text (sdp);
1707   gst_rtsp_message_take_body (ctx->response, (guint8 *) str, strlen (str));
1708   gst_sdp_message_free (sdp);
1709
1710   send_message (client, ctx->session, ctx->response, FALSE);
1711
1712   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST],
1713       0, ctx);
1714
1715   return TRUE;
1716
1717   /* ERRORS */
1718 no_uri:
1719   {
1720     GST_ERROR ("client %p: no uri", client);
1721     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1722     return FALSE;
1723   }
1724 no_media:
1725   {
1726     GST_ERROR ("client %p: no media", client);
1727     /* error reply is already sent */
1728     return FALSE;
1729   }
1730 no_sdp:
1731   {
1732     GST_ERROR ("client %p: can't create SDP", client);
1733     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1734     g_object_unref (media);
1735     return FALSE;
1736   }
1737 }
1738
1739 static gboolean
1740 handle_options_request (GstRTSPClient * client, GstRTSPContext * ctx)
1741 {
1742   GstRTSPMethod options;
1743   gchar *str;
1744
1745   options = GST_RTSP_DESCRIBE |
1746       GST_RTSP_OPTIONS |
1747       GST_RTSP_PAUSE |
1748       GST_RTSP_PLAY |
1749       GST_RTSP_SETUP |
1750       GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
1751
1752   str = gst_rtsp_options_as_text (options);
1753
1754   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
1755       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
1756
1757   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_PUBLIC, str);
1758   g_free (str);
1759
1760   send_message (client, ctx->session, ctx->response, FALSE);
1761
1762   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST],
1763       0, ctx);
1764
1765   return TRUE;
1766 }
1767
1768 /* remove duplicate and trailing '/' */
1769 static void
1770 sanitize_uri (GstRTSPUrl * uri)
1771 {
1772   gint i, len;
1773   gchar *s, *d;
1774   gboolean have_slash, prev_slash;
1775
1776   s = d = uri->abspath;
1777   len = strlen (uri->abspath);
1778
1779   prev_slash = FALSE;
1780
1781   for (i = 0; i < len; i++) {
1782     have_slash = s[i] == '/';
1783     *d = s[i];
1784     if (!have_slash || !prev_slash)
1785       d++;
1786     prev_slash = have_slash;
1787   }
1788   len = d - uri->abspath;
1789   /* don't remove the first slash if that's the only thing left */
1790   if (len > 1 && *(d - 1) == '/')
1791     d--;
1792   *d = '\0';
1793 }
1794
1795 static void
1796 client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
1797 {
1798   GstRTSPClientPrivate *priv = client->priv;
1799
1800   GST_INFO ("client %p: session %p finished", client, session);
1801
1802   /* unlink all media managed in this session */
1803   client_unlink_session (client, session);
1804
1805   /* remove the session */
1806   if (!(priv->sessions = g_list_remove (priv->sessions, session))) {
1807     GST_INFO ("client %p: all sessions finalized, close the connection",
1808         client);
1809     close_connection (client);
1810   }
1811 }
1812
1813 static void
1814 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
1815 {
1816   GstRTSPClientPrivate *priv = client->priv;
1817   GstRTSPMethod method;
1818   const gchar *uristr;
1819   GstRTSPUrl *uri = NULL;
1820   GstRTSPVersion version;
1821   GstRTSPResult res;
1822   GstRTSPSession *session = NULL;
1823   GstRTSPContext sctx = { NULL }, *ctx;
1824   GstRTSPMessage response = { 0 };
1825   gchar *sessid;
1826
1827   if (!(ctx = gst_rtsp_context_get_current ())) {
1828     ctx = &sctx;
1829     ctx->auth = priv->auth;
1830     gst_rtsp_context_push_current (ctx);
1831   }
1832
1833   ctx->conn = priv->connection;
1834   ctx->client = client;
1835   ctx->request = request;
1836   ctx->response = &response;
1837
1838   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
1839     gst_rtsp_message_dump (request);
1840   }
1841
1842   GST_INFO ("client %p: received a request", client);
1843
1844   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
1845
1846   /* we can only handle 1.0 requests */
1847   if (version != GST_RTSP_VERSION_1_0)
1848     goto not_supported;
1849
1850   ctx->method = method;
1851
1852   /* we always try to parse the url first */
1853   if (strcmp (uristr, "*") == 0) {
1854     /* special case where we have * as uri, keep uri = NULL */
1855   } else if (gst_rtsp_url_parse (uristr, &uri) != GST_RTSP_OK)
1856     goto bad_request;
1857
1858   /* get the session if there is any */
1859   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
1860   if (res == GST_RTSP_OK) {
1861     if (priv->session_pool == NULL)
1862       goto no_pool;
1863
1864     /* we had a session in the request, find it again */
1865     if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
1866       goto session_not_found;
1867
1868     /* we add the session to the client list of watched sessions. When a session
1869      * disappears because it times out, we will be notified. If all sessions are
1870      * gone, we will close the connection */
1871     client_watch_session (client, session);
1872   }
1873
1874   /* sanitize the uri */
1875   if (uri)
1876     sanitize_uri (uri);
1877   ctx->uri = uri;
1878   ctx->session = session;
1879
1880   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_URL))
1881     goto not_authorized;
1882
1883   /* now see what is asked and dispatch to a dedicated handler */
1884   switch (method) {
1885     case GST_RTSP_OPTIONS:
1886       handle_options_request (client, ctx);
1887       break;
1888     case GST_RTSP_DESCRIBE:
1889       handle_describe_request (client, ctx);
1890       break;
1891     case GST_RTSP_SETUP:
1892       handle_setup_request (client, ctx);
1893       break;
1894     case GST_RTSP_PLAY:
1895       handle_play_request (client, ctx);
1896       break;
1897     case GST_RTSP_PAUSE:
1898       handle_pause_request (client, ctx);
1899       break;
1900     case GST_RTSP_TEARDOWN:
1901       handle_teardown_request (client, ctx);
1902       break;
1903     case GST_RTSP_SET_PARAMETER:
1904       handle_set_param_request (client, ctx);
1905       break;
1906     case GST_RTSP_GET_PARAMETER:
1907       handle_get_param_request (client, ctx);
1908       break;
1909     case GST_RTSP_ANNOUNCE:
1910     case GST_RTSP_RECORD:
1911     case GST_RTSP_REDIRECT:
1912       goto not_implemented;
1913     case GST_RTSP_INVALID:
1914     default:
1915       goto bad_request;
1916   }
1917
1918 done:
1919   if (ctx == &sctx)
1920     gst_rtsp_context_pop_current (ctx);
1921   if (session)
1922     g_object_unref (session);
1923   if (uri)
1924     gst_rtsp_url_free (uri);
1925   return;
1926
1927   /* ERRORS */
1928 not_supported:
1929   {
1930     GST_ERROR ("client %p: version %d not supported", client, version);
1931     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
1932         ctx);
1933     goto done;
1934   }
1935 bad_request:
1936   {
1937     GST_ERROR ("client %p: bad request", client);
1938     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1939     goto done;
1940   }
1941 no_pool:
1942   {
1943     GST_ERROR ("client %p: no pool configured", client);
1944     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1945     goto done;
1946   }
1947 session_not_found:
1948   {
1949     GST_ERROR ("client %p: session not found", client);
1950     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1951     goto done;
1952   }
1953 not_authorized:
1954   {
1955     GST_ERROR ("client %p: not allowed", client);
1956     goto done;
1957   }
1958 not_implemented:
1959   {
1960     GST_ERROR ("client %p: method %d not implemented", client, method);
1961     send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, ctx);
1962     goto done;
1963   }
1964 }
1965
1966
1967 static void
1968 handle_response (GstRTSPClient * client, GstRTSPMessage * response)
1969 {
1970   GstRTSPClientPrivate *priv = client->priv;
1971   GstRTSPResult res;
1972   GstRTSPSession *session = NULL;
1973   GstRTSPContext sctx = { NULL }, *ctx;
1974   gchar *sessid;
1975
1976   if (!(ctx = gst_rtsp_context_get_current ())) {
1977     ctx = &sctx;
1978     ctx->auth = priv->auth;
1979     gst_rtsp_context_push_current (ctx);
1980   }
1981
1982   ctx->conn = priv->connection;
1983   ctx->client = client;
1984   ctx->request = NULL;
1985   ctx->uri = NULL;
1986   ctx->method = GST_RTSP_INVALID;
1987   ctx->response = response;
1988
1989   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
1990     gst_rtsp_message_dump (response);
1991   }
1992
1993   GST_INFO ("client %p: received a response", client);
1994
1995   /* get the session if there is any */
1996   res =
1997       gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &sessid, 0);
1998   if (res == GST_RTSP_OK) {
1999     if (priv->session_pool == NULL)
2000       goto no_pool;
2001
2002     /* we had a session in the request, find it again */
2003     if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
2004       goto session_not_found;
2005
2006     /* we add the session to the client list of watched sessions. When a session
2007      * disappears because it times out, we will be notified. If all sessions are
2008      * gone, we will close the connection */
2009     client_watch_session (client, session);
2010   }
2011
2012   ctx->session = session;
2013
2014   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE],
2015       0, ctx);
2016
2017 done:
2018   if (ctx == &sctx)
2019     gst_rtsp_context_pop_current (ctx);
2020   if (session)
2021     g_object_unref (session);
2022   return;
2023
2024 no_pool:
2025   {
2026     GST_ERROR ("client %p: no pool configured", client);
2027     goto done;
2028   }
2029 session_not_found:
2030   {
2031     GST_ERROR ("client %p: session not found", client);
2032     goto done;
2033   }
2034 }
2035
2036 static void
2037 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
2038 {
2039   GstRTSPClientPrivate *priv = client->priv;
2040   GstRTSPResult res;
2041   guint8 channel;
2042   GList *walk;
2043   guint8 *data;
2044   guint size;
2045   GstBuffer *buffer;
2046   gboolean handled;
2047
2048   /* find the stream for this message */
2049   res = gst_rtsp_message_parse_data (message, &channel);
2050   if (res != GST_RTSP_OK)
2051     return;
2052
2053   gst_rtsp_message_steal_body (message, &data, &size);
2054
2055   buffer = gst_buffer_new_wrapped (data, size);
2056
2057   handled = FALSE;
2058   for (walk = priv->transports; walk; walk = g_list_next (walk)) {
2059     GstRTSPStreamTransport *trans;
2060     GstRTSPStream *stream;
2061     const GstRTSPTransport *tr;
2062
2063     trans = walk->data;
2064
2065     tr = gst_rtsp_stream_transport_get_transport (trans);
2066     stream = gst_rtsp_stream_transport_get_stream (trans);
2067
2068     /* check for TCP transport */
2069     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
2070       /* dispatch to the stream based on the channel number */
2071       if (tr->interleaved.min == channel) {
2072         gst_rtsp_stream_recv_rtp (stream, buffer);
2073         handled = TRUE;
2074         break;
2075       } else if (tr->interleaved.max == channel) {
2076         gst_rtsp_stream_recv_rtcp (stream, buffer);
2077         handled = TRUE;
2078         break;
2079       }
2080     }
2081   }
2082   if (!handled)
2083     gst_buffer_unref (buffer);
2084 }
2085
2086 /**
2087  * gst_rtsp_client_set_session_pool:
2088  * @client: a #GstRTSPClient
2089  * @pool: a #GstRTSPSessionPool
2090  *
2091  * Set @pool as the sessionpool for @client which it will use to find
2092  * or allocate sessions. the sessionpool is usually inherited from the server
2093  * that created the client but can be overridden later.
2094  */
2095 void
2096 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
2097     GstRTSPSessionPool * pool)
2098 {
2099   GstRTSPSessionPool *old;
2100   GstRTSPClientPrivate *priv;
2101
2102   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2103
2104   priv = client->priv;
2105
2106   if (pool)
2107     g_object_ref (pool);
2108
2109   g_mutex_lock (&priv->lock);
2110   old = priv->session_pool;
2111   priv->session_pool = pool;
2112   g_mutex_unlock (&priv->lock);
2113
2114   if (old)
2115     g_object_unref (old);
2116 }
2117
2118 /**
2119  * gst_rtsp_client_get_session_pool:
2120  * @client: a #GstRTSPClient
2121  *
2122  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
2123  *
2124  * Returns: (transfer full): a #GstRTSPSessionPool, unref after usage.
2125  */
2126 GstRTSPSessionPool *
2127 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
2128 {
2129   GstRTSPClientPrivate *priv;
2130   GstRTSPSessionPool *result;
2131
2132   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2133
2134   priv = client->priv;
2135
2136   g_mutex_lock (&priv->lock);
2137   if ((result = priv->session_pool))
2138     g_object_ref (result);
2139   g_mutex_unlock (&priv->lock);
2140
2141   return result;
2142 }
2143
2144 /**
2145  * gst_rtsp_client_set_mount_points:
2146  * @client: a #GstRTSPClient
2147  * @mounts: a #GstRTSPMountPoints
2148  *
2149  * Set @mounts as the mount points for @client which it will use to map urls
2150  * to media streams. These mount points are usually inherited from the server that
2151  * created the client but can be overriden later.
2152  */
2153 void
2154 gst_rtsp_client_set_mount_points (GstRTSPClient * client,
2155     GstRTSPMountPoints * mounts)
2156 {
2157   GstRTSPClientPrivate *priv;
2158   GstRTSPMountPoints *old;
2159
2160   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2161
2162   priv = client->priv;
2163
2164   if (mounts)
2165     g_object_ref (mounts);
2166
2167   g_mutex_lock (&priv->lock);
2168   old = priv->mount_points;
2169   priv->mount_points = mounts;
2170   g_mutex_unlock (&priv->lock);
2171
2172   if (old)
2173     g_object_unref (old);
2174 }
2175
2176 /**
2177  * gst_rtsp_client_get_mount_points:
2178  * @client: a #GstRTSPClient
2179  *
2180  * Get the #GstRTSPMountPoints object that @client uses to manage its sessions.
2181  *
2182  * Returns: (transfer full): a #GstRTSPMountPoints, unref after usage.
2183  */
2184 GstRTSPMountPoints *
2185 gst_rtsp_client_get_mount_points (GstRTSPClient * client)
2186 {
2187   GstRTSPClientPrivate *priv;
2188   GstRTSPMountPoints *result;
2189
2190   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2191
2192   priv = client->priv;
2193
2194   g_mutex_lock (&priv->lock);
2195   if ((result = priv->mount_points))
2196     g_object_ref (result);
2197   g_mutex_unlock (&priv->lock);
2198
2199   return result;
2200 }
2201
2202 /**
2203  * gst_rtsp_client_set_auth:
2204  * @client: a #GstRTSPClient
2205  * @auth: a #GstRTSPAuth
2206  *
2207  * configure @auth to be used as the authentication manager of @client.
2208  */
2209 void
2210 gst_rtsp_client_set_auth (GstRTSPClient * client, GstRTSPAuth * auth)
2211 {
2212   GstRTSPClientPrivate *priv;
2213   GstRTSPAuth *old;
2214
2215   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2216
2217   priv = client->priv;
2218
2219   if (auth)
2220     g_object_ref (auth);
2221
2222   g_mutex_lock (&priv->lock);
2223   old = priv->auth;
2224   priv->auth = auth;
2225   g_mutex_unlock (&priv->lock);
2226
2227   if (old)
2228     g_object_unref (old);
2229 }
2230
2231
2232 /**
2233  * gst_rtsp_client_get_auth:
2234  * @client: a #GstRTSPClient
2235  *
2236  * Get the #GstRTSPAuth used as the authentication manager of @client.
2237  *
2238  * Returns: (transfer full): the #GstRTSPAuth of @client. g_object_unref() after
2239  * usage.
2240  */
2241 GstRTSPAuth *
2242 gst_rtsp_client_get_auth (GstRTSPClient * client)
2243 {
2244   GstRTSPClientPrivate *priv;
2245   GstRTSPAuth *result;
2246
2247   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2248
2249   priv = client->priv;
2250
2251   g_mutex_lock (&priv->lock);
2252   if ((result = priv->auth))
2253     g_object_ref (result);
2254   g_mutex_unlock (&priv->lock);
2255
2256   return result;
2257 }
2258
2259 /**
2260  * gst_rtsp_client_set_thread_pool:
2261  * @client: a #GstRTSPClient
2262  * @pool: a #GstRTSPThreadPool
2263  *
2264  * configure @pool to be used as the thread pool of @client.
2265  */
2266 void
2267 gst_rtsp_client_set_thread_pool (GstRTSPClient * client,
2268     GstRTSPThreadPool * pool)
2269 {
2270   GstRTSPClientPrivate *priv;
2271   GstRTSPThreadPool *old;
2272
2273   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2274
2275   priv = client->priv;
2276
2277   if (pool)
2278     g_object_ref (pool);
2279
2280   g_mutex_lock (&priv->lock);
2281   old = priv->thread_pool;
2282   priv->thread_pool = pool;
2283   g_mutex_unlock (&priv->lock);
2284
2285   if (old)
2286     g_object_unref (old);
2287 }
2288
2289 /**
2290  * gst_rtsp_client_get_thread_pool:
2291  * @client: a #GstRTSPClient
2292  *
2293  * Get the #GstRTSPThreadPool used as the thread pool of @client.
2294  *
2295  * Returns: (transfer full): the #GstRTSPThreadPool of @client. g_object_unref() after
2296  * usage.
2297  */
2298 GstRTSPThreadPool *
2299 gst_rtsp_client_get_thread_pool (GstRTSPClient * client)
2300 {
2301   GstRTSPClientPrivate *priv;
2302   GstRTSPThreadPool *result;
2303
2304   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2305
2306   priv = client->priv;
2307
2308   g_mutex_lock (&priv->lock);
2309   if ((result = priv->thread_pool))
2310     g_object_ref (result);
2311   g_mutex_unlock (&priv->lock);
2312
2313   return result;
2314 }
2315
2316 /**
2317  * gst_rtsp_client_set_connection:
2318  * @client: a #GstRTSPClient
2319  * @conn: (transfer full): a #GstRTSPConnection
2320  *
2321  * Set the #GstRTSPConnection of @client. This function takes ownership of
2322  * @conn.
2323  *
2324  * Returns: %TRUE on success.
2325  */
2326 gboolean
2327 gst_rtsp_client_set_connection (GstRTSPClient * client,
2328     GstRTSPConnection * conn)
2329 {
2330   GstRTSPClientPrivate *priv;
2331   GSocket *read_socket;
2332   GSocketAddress *address;
2333   GstRTSPUrl *url;
2334   GError *error = NULL;
2335
2336   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
2337   g_return_val_if_fail (conn != NULL, FALSE);
2338
2339   priv = client->priv;
2340
2341   read_socket = gst_rtsp_connection_get_read_socket (conn);
2342
2343   if (!(address = g_socket_get_local_address (read_socket, &error)))
2344     goto no_address;
2345
2346   g_free (priv->server_ip);
2347   /* keep the original ip that the client connected to */
2348   if (G_IS_INET_SOCKET_ADDRESS (address)) {
2349     GInetAddress *iaddr;
2350
2351     iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address));
2352
2353     /* socket might be ipv6 but adress still ipv4 */
2354     priv->is_ipv6 = g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV6;
2355     priv->server_ip = g_inet_address_to_string (iaddr);
2356     g_object_unref (address);
2357   } else {
2358     priv->is_ipv6 = g_socket_get_family (read_socket) == G_SOCKET_FAMILY_IPV6;
2359     priv->server_ip = g_strdup ("unknown");
2360   }
2361
2362   GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
2363       priv->server_ip, priv->is_ipv6);
2364
2365   url = gst_rtsp_connection_get_url (conn);
2366   GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
2367
2368   priv->connection = conn;
2369
2370   return TRUE;
2371
2372   /* ERRORS */
2373 no_address:
2374   {
2375     GST_ERROR ("could not get local address %s", error->message);
2376     g_error_free (error);
2377     return FALSE;
2378   }
2379 }
2380
2381 /**
2382  * gst_rtsp_client_get_connection:
2383  * @client: a #GstRTSPClient
2384  *
2385  * Get the #GstRTSPConnection of @client.
2386  *
2387  * Returns: (transfer none): the #GstRTSPConnection of @client.
2388  * The connection object returned remains valid until the client is freed.
2389  */
2390 GstRTSPConnection *
2391 gst_rtsp_client_get_connection (GstRTSPClient * client)
2392 {
2393   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2394
2395   return client->priv->connection;
2396 }
2397
2398 /**
2399  * gst_rtsp_client_set_send_func:
2400  * @client: a #GstRTSPClient
2401  * @func: a #GstRTSPClientSendFunc
2402  * @user_data: user data passed to @func
2403  * @notify: called when @user_data is no longer in use
2404  *
2405  * Set @func as the callback that will be called when a new message needs to be
2406  * sent to the client. @user_data is passed to @func and @notify is called when
2407  * @user_data is no longer in use.
2408  *
2409  * By default, the client will send the messages on the #GstRTSPConnection that
2410  * was configured with gst_rtsp_client_attach() was called.
2411  */
2412 void
2413 gst_rtsp_client_set_send_func (GstRTSPClient * client,
2414     GstRTSPClientSendFunc func, gpointer user_data, GDestroyNotify notify)
2415 {
2416   GstRTSPClientPrivate *priv;
2417   GDestroyNotify old_notify;
2418   gpointer old_data;
2419
2420   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2421
2422   priv = client->priv;
2423
2424   g_mutex_lock (&priv->send_lock);
2425   priv->send_func = func;
2426   old_notify = priv->send_notify;
2427   old_data = priv->send_data;
2428   priv->send_notify = notify;
2429   priv->send_data = user_data;
2430   g_mutex_unlock (&priv->send_lock);
2431
2432   if (old_notify)
2433     old_notify (old_data);
2434 }
2435
2436 /**
2437  * gst_rtsp_client_handle_message:
2438  * @client: a #GstRTSPClient
2439  * @message: an #GstRTSPMessage
2440  *
2441  * Let the client handle @message.
2442  *
2443  * Returns: a #GstRTSPResult.
2444  */
2445 GstRTSPResult
2446 gst_rtsp_client_handle_message (GstRTSPClient * client,
2447     GstRTSPMessage * message)
2448 {
2449   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
2450   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2451
2452   switch (message->type) {
2453     case GST_RTSP_MESSAGE_REQUEST:
2454       handle_request (client, message);
2455       break;
2456     case GST_RTSP_MESSAGE_RESPONSE:
2457       handle_response (client, message);
2458       break;
2459     case GST_RTSP_MESSAGE_DATA:
2460       handle_data (client, message);
2461       break;
2462     default:
2463       break;
2464   }
2465   return GST_RTSP_OK;
2466 }
2467
2468 /**
2469  * gst_rtsp_client_send_message:
2470  * @client: a #GstRTSPClient
2471  * @session: a #GstRTSPSession to send the message to or %NULL
2472  * @message: The #GstRTSPMessage to send
2473  *
2474  * Send a message message to the remote end. @message must be a
2475  * #GST_RTSP_MESSAGE_REQUEST or a #GST_RTSP_MESSAGE_RESPONSE.
2476  */
2477 GstRTSPResult
2478 gst_rtsp_client_send_message (GstRTSPClient * client, GstRTSPSession * session,
2479     GstRTSPMessage * message)
2480 {
2481   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
2482   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2483   g_return_val_if_fail (message->type == GST_RTSP_MESSAGE_REQUEST ||
2484       message->type == GST_RTSP_MESSAGE_RESPONSE, GST_RTSP_EINVAL);
2485
2486   send_message (client, session, message, FALSE);
2487
2488   return GST_RTSP_OK;
2489 }
2490
2491 static GstRTSPResult
2492 do_send_message (GstRTSPClient * client, GstRTSPMessage * message,
2493     gboolean close, gpointer user_data)
2494 {
2495   GstRTSPClientPrivate *priv = client->priv;
2496
2497   /* send the response and store the seq number so we can wait until it's
2498    * written to the client to close the connection */
2499   return gst_rtsp_watch_send_message (priv->watch, message, close ?
2500       &priv->close_seq : NULL);
2501 }
2502
2503 static GstRTSPResult
2504 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
2505     gpointer user_data)
2506 {
2507   return gst_rtsp_client_handle_message (GST_RTSP_CLIENT (user_data), message);
2508 }
2509
2510 static GstRTSPResult
2511 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
2512 {
2513   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2514   GstRTSPClientPrivate *priv = client->priv;
2515
2516   if (priv->close_seq && priv->close_seq == cseq) {
2517     priv->close_seq = 0;
2518     close_connection (client);
2519   }
2520
2521   return GST_RTSP_OK;
2522 }
2523
2524 static GstRTSPResult
2525 closed (GstRTSPWatch * watch, gpointer user_data)
2526 {
2527   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2528   GstRTSPClientPrivate *priv = client->priv;
2529   const gchar *tunnelid;
2530
2531   GST_INFO ("client %p: connection closed", client);
2532
2533   if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
2534     g_mutex_lock (&tunnels_lock);
2535     /* remove from tunnelids */
2536     g_hash_table_remove (tunnels, tunnelid);
2537     g_mutex_unlock (&tunnels_lock);
2538   }
2539
2540   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
2541
2542   return GST_RTSP_OK;
2543 }
2544
2545 static GstRTSPResult
2546 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
2547 {
2548   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2549   gchar *str;
2550
2551   str = gst_rtsp_strresult (result);
2552   GST_INFO ("client %p: received an error %s", client, str);
2553   g_free (str);
2554
2555   return GST_RTSP_OK;
2556 }
2557
2558 static GstRTSPResult
2559 error_full (GstRTSPWatch * watch, GstRTSPResult result,
2560     GstRTSPMessage * message, guint id, gpointer user_data)
2561 {
2562   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2563   gchar *str;
2564
2565   str = gst_rtsp_strresult (result);
2566   GST_INFO
2567       ("client %p: error when handling message %p with id %d: %s",
2568       client, message, id, str);
2569   g_free (str);
2570
2571   return GST_RTSP_OK;
2572 }
2573
2574 static gboolean
2575 remember_tunnel (GstRTSPClient * client)
2576 {
2577   GstRTSPClientPrivate *priv = client->priv;
2578   const gchar *tunnelid;
2579
2580   /* store client in the pending tunnels */
2581   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
2582   if (tunnelid == NULL)
2583     goto no_tunnelid;
2584
2585   GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
2586
2587   /* we can't have two clients connecting with the same tunnelid */
2588   g_mutex_lock (&tunnels_lock);
2589   if (g_hash_table_lookup (tunnels, tunnelid))
2590     goto tunnel_existed;
2591
2592   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
2593   g_mutex_unlock (&tunnels_lock);
2594
2595   return TRUE;
2596
2597   /* ERRORS */
2598 no_tunnelid:
2599   {
2600     GST_ERROR ("client %p: no tunnelid provided", client);
2601     return FALSE;
2602   }
2603 tunnel_existed:
2604   {
2605     g_mutex_unlock (&tunnels_lock);
2606     GST_ERROR ("client %p: tunnel session %s already existed", client,
2607         tunnelid);
2608     return FALSE;
2609   }
2610 }
2611
2612 static GstRTSPStatusCode
2613 tunnel_start (GstRTSPWatch * watch, gpointer user_data)
2614 {
2615   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2616   GstRTSPClientPrivate *priv = client->priv;
2617
2618   GST_INFO ("client %p: tunnel start (connection %p)", client,
2619       priv->connection);
2620
2621   if (!remember_tunnel (client))
2622     goto tunnel_error;
2623
2624   return GST_RTSP_STS_OK;
2625
2626   /* ERRORS */
2627 tunnel_error:
2628   {
2629     GST_ERROR ("client %p: error starting tunnel", client);
2630     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
2631   }
2632 }
2633
2634 static GstRTSPResult
2635 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
2636 {
2637   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2638   GstRTSPClientPrivate *priv = client->priv;
2639
2640   GST_WARNING ("client %p: tunnel lost (connection %p)", client,
2641       priv->connection);
2642
2643   /* ignore error, it'll only be a problem when the client does a POST again */
2644   remember_tunnel (client);
2645
2646   return GST_RTSP_OK;
2647 }
2648
2649 static GstRTSPResult
2650 tunnel_complete (GstRTSPWatch * watch, gpointer user_data)
2651 {
2652   const gchar *tunnelid;
2653   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2654   GstRTSPClientPrivate *priv = client->priv;
2655   GstRTSPClient *oclient;
2656   GstRTSPClientPrivate *opriv;
2657
2658   GST_INFO ("client %p: tunnel complete", client);
2659
2660   /* find previous tunnel */
2661   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
2662   if (tunnelid == NULL)
2663     goto no_tunnelid;
2664
2665   g_mutex_lock (&tunnels_lock);
2666   if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
2667     goto no_tunnel;
2668
2669   /* remove the old client from the table. ref before because removing it will
2670    * remove the ref to it. */
2671   g_object_ref (oclient);
2672   g_hash_table_remove (tunnels, tunnelid);
2673
2674   opriv = oclient->priv;
2675
2676   if (opriv->watch == NULL)
2677     goto tunnel_closed;
2678   g_mutex_unlock (&tunnels_lock);
2679
2680   GST_INFO ("client %p: found tunnel %p (old %p, new %p)", client, oclient,
2681       opriv->connection, priv->connection);
2682
2683   /* merge the tunnels into the first client */
2684   gst_rtsp_connection_do_tunnel (opriv->connection, priv->connection);
2685   gst_rtsp_watch_reset (opriv->watch);
2686   g_object_unref (oclient);
2687
2688   return GST_RTSP_OK;
2689
2690   /* ERRORS */
2691 no_tunnelid:
2692   {
2693     GST_ERROR ("client %p: no tunnelid provided", client);
2694     return GST_RTSP_ERROR;
2695   }
2696 no_tunnel:
2697   {
2698     g_mutex_unlock (&tunnels_lock);
2699     GST_ERROR ("client %p: tunnel session %s not found", client, tunnelid);
2700     return GST_RTSP_ERROR;
2701   }
2702 tunnel_closed:
2703   {
2704     g_mutex_unlock (&tunnels_lock);
2705     GST_ERROR ("client %p: tunnel session %s was closed", client, tunnelid);
2706     g_object_unref (oclient);
2707     return GST_RTSP_ERROR;
2708   }
2709 }
2710
2711 static GstRTSPWatchFuncs watch_funcs = {
2712   message_received,
2713   message_sent,
2714   closed,
2715   error,
2716   tunnel_start,
2717   tunnel_complete,
2718   error_full,
2719   tunnel_lost
2720 };
2721
2722 static void
2723 client_watch_notify (GstRTSPClient * client)
2724 {
2725   GstRTSPClientPrivate *priv = client->priv;
2726
2727   GST_INFO ("client %p: watch destroyed", client);
2728   priv->watch = NULL;
2729   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL);
2730   g_object_unref (client);
2731 }
2732
2733 /**
2734  * gst_rtsp_client_attach:
2735  * @client: a #GstRTSPClient
2736  * @context: (allow-none): a #GMainContext
2737  *
2738  * Attaches @client to @context. When the mainloop for @context is run, the
2739  * client will be dispatched. When @context is NULL, the default context will be
2740  * used).
2741  *
2742  * This function should be called when the client properties and urls are fully
2743  * configured and the client is ready to start.
2744  *
2745  * Returns: the ID (greater than 0) for the source within the GMainContext.
2746  */
2747 guint
2748 gst_rtsp_client_attach (GstRTSPClient * client, GMainContext * context)
2749 {
2750   GstRTSPClientPrivate *priv;
2751   guint res;
2752
2753   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), 0);
2754   priv = client->priv;
2755   g_return_val_if_fail (priv->connection != NULL, 0);
2756   g_return_val_if_fail (priv->watch == NULL, 0);
2757
2758   /* create watch for the connection and attach */
2759   priv->watch = gst_rtsp_watch_new (priv->connection, &watch_funcs,
2760       g_object_ref (client), (GDestroyNotify) client_watch_notify);
2761   gst_rtsp_client_set_send_func (client, do_send_message, priv->watch,
2762       (GDestroyNotify) gst_rtsp_watch_unref);
2763
2764   /* FIXME make this configurable. We don't want to do this yet because it will
2765    * be superceeded by a cache object later */
2766   gst_rtsp_watch_set_send_backlog (priv->watch, 0, 100);
2767
2768   GST_INFO ("attaching to context %p", context);
2769   res = gst_rtsp_watch_attach (priv->watch, context);
2770
2771   return res;
2772 }
2773
2774 /**
2775  * gst_rtsp_client_session_filter:
2776  * @client: a #GstRTSPClient
2777  * @func: (scope call): a callback
2778  * @user_data: user data passed to @func
2779  *
2780  * Call @func for each session managed by @client. The result value of @func
2781  * determines what happens to the session. @func will be called with @client
2782  * locked so no further actions on @client can be performed from @func.
2783  *
2784  * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from
2785  * @client.
2786  *
2787  * If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @client.
2788  *
2789  * If @func returns #GST_RTSP_FILTER_REF, the session will remain in @client but
2790  * will also be added with an additional ref to the result #GList of this
2791  * function..
2792  *
2793  * Returns: (element-type GstRTSPSession) (transfer full): a #GList with all
2794  * sessions for which @func returned #GST_RTSP_FILTER_REF. After usage, each
2795  * element in the #GList should be unreffed before the list is freed.
2796  */
2797 GList *
2798 gst_rtsp_client_session_filter (GstRTSPClient * client,
2799     GstRTSPClientSessionFilterFunc func, gpointer user_data)
2800 {
2801   GstRTSPClientPrivate *priv;
2802   GList *result, *walk, *next;
2803
2804   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2805   g_return_val_if_fail (func != NULL, NULL);
2806
2807   priv = client->priv;
2808
2809   result = NULL;
2810
2811   g_mutex_lock (&priv->lock);
2812   for (walk = priv->sessions; walk; walk = next) {
2813     GstRTSPSession *sess = walk->data;
2814
2815     next = g_list_next (walk);
2816
2817     switch (func (client, sess, user_data)) {
2818       case GST_RTSP_FILTER_REMOVE:
2819         /* stop watching the session and pretent it went away */
2820         client_cleanup_session (client, sess);
2821         break;
2822       case GST_RTSP_FILTER_REF:
2823         result = g_list_prepend (result, g_object_ref (sess));
2824         break;
2825       case GST_RTSP_FILTER_KEEP:
2826       default:
2827         break;
2828     }
2829   }
2830   g_mutex_unlock (&priv->lock);
2831
2832   return result;
2833 }