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