Merge branch 'master' of git+ssh://git.collabora.co.uk/git/gst-rtsp-server
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-session-pool.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "rtsp-session-pool.h"
21
22 #undef DEBUG
23
24 #define DEFAULT_MAX_SESSIONS 0
25
26 enum
27 {
28   PROP_0,
29   PROP_MAX_SESSIONS,
30   PROP_LAST
31 };
32
33 static void gst_rtsp_session_pool_get_property (GObject *object, guint propid,
34     GValue *value, GParamSpec *pspec);
35 static void gst_rtsp_session_pool_set_property (GObject *object, guint propid,
36     const GValue *value, GParamSpec *pspec);
37 static void gst_rtsp_session_pool_finalize (GObject * object);
38
39 static gchar * create_session_id (GstRTSPSessionPool *pool);
40
41 G_DEFINE_TYPE (GstRTSPSessionPool, gst_rtsp_session_pool, G_TYPE_OBJECT);
42
43 static void
44 gst_rtsp_session_pool_class_init (GstRTSPSessionPoolClass * klass)
45 {
46   GObjectClass *gobject_class;
47
48   gobject_class = G_OBJECT_CLASS (klass);
49
50   gobject_class->get_property = gst_rtsp_session_pool_get_property;
51   gobject_class->set_property = gst_rtsp_session_pool_set_property;
52   gobject_class->finalize = gst_rtsp_session_pool_finalize;
53
54   g_object_class_install_property (gobject_class, PROP_MAX_SESSIONS,
55       g_param_spec_uint ("max-sessions", "Max Sessions",
56           "the maximum amount of sessions (0 = unlimited)",
57           0, G_MAXUINT, DEFAULT_MAX_SESSIONS,
58           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
59
60   klass->create_session_id = create_session_id;
61 }
62
63 static void
64 gst_rtsp_session_pool_init (GstRTSPSessionPool * pool)
65 {
66   pool->lock = g_mutex_new ();
67   pool->sessions = g_hash_table_new_full (g_str_hash, g_str_equal,
68                   NULL, g_object_unref);
69   pool->max_sessions = DEFAULT_MAX_SESSIONS;
70 }
71
72 static void
73 gst_rtsp_session_pool_finalize (GObject * object)
74 {
75   GstRTSPSessionPool * pool = GST_RTSP_SESSION_POOL (object);
76
77   g_mutex_free (pool->lock);
78   g_hash_table_unref (pool->sessions);
79   
80   G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object);
81 }
82
83 static void
84 gst_rtsp_session_pool_get_property (GObject *object, guint propid,
85     GValue *value, GParamSpec *pspec)
86 {
87   GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
88
89   switch (propid) {
90     case PROP_MAX_SESSIONS:
91       g_mutex_lock (pool->lock);
92       g_value_set_uint (value, pool->max_sessions);
93       g_mutex_unlock (pool->lock);
94       break;
95     default:
96       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
97       break;
98   }
99 }
100
101 static void
102 gst_rtsp_session_pool_set_property (GObject *object, guint propid,
103     const GValue *value, GParamSpec *pspec)
104 {
105   GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
106
107   switch (propid) {
108     case PROP_MAX_SESSIONS:
109       g_mutex_lock (pool->lock);
110       pool->max_sessions = g_value_get_uint (value);
111       g_mutex_unlock (pool->lock);
112       break;
113     default:
114       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
115       break;
116   }
117 }
118
119 /**
120  * gst_rtsp_session_pool_new:
121  *
122  * Create a new #GstRTSPSessionPool instance.
123  *
124  * Returns: A new #GstRTSPSessionPool. g_object_unref() after usage.
125  */
126 GstRTSPSessionPool *
127 gst_rtsp_session_pool_new (void)
128 {
129   GstRTSPSessionPool *result;
130
131   result = g_object_new (GST_TYPE_RTSP_SESSION_POOL, NULL);
132
133   return result;
134 }
135
136 /**
137  * gst_rtsp_session_pool_set_max_sessions:
138  * @pool: a #GstRTSPSessionPool
139  * @max: the maximum number of sessions
140  *
141  * Configure the maximum allowed number of sessions in @pool to @max.
142  * A value of 0 means an unlimited amount of sessions.
143  */
144 void
145 gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool *pool, guint max)
146 {
147   g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool));
148
149   g_mutex_lock (pool->lock);
150   pool->max_sessions = max;
151   g_mutex_unlock (pool->lock);
152 }
153
154 /**
155  * gst_rtsp_session_pool_get_max_sessions:
156  * @pool: a #GstRTSPSessionPool
157  *
158  * Get the maximum allowed number of sessions in @pool. 0 means an unlimited
159  * amount of sessions.
160  *
161  * Returns: the maximum allowed number of sessions.
162  */
163 guint
164 gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool *pool)
165 {
166   guint result;
167
168   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
169
170   g_mutex_lock (pool->lock);
171   result = pool->max_sessions;
172   g_mutex_unlock (pool->lock);
173
174   return result;
175 }
176
177 /**
178  * gst_rtsp_session_pool_get_n_sessions:
179  * @pool: a #GstRTSPSessionPool
180  *
181  * Get the amount of active sessions in @pool.
182  *
183  * Returns: the amount of active sessions in @pool.
184  */
185 guint
186 gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool *pool)
187 {
188   guint result;
189
190   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
191
192   g_mutex_lock (pool->lock);
193   result = g_hash_table_size (pool->sessions);
194   g_mutex_unlock (pool->lock);
195
196   return result;
197 }
198
199 /**
200  * gst_rtsp_session_pool_find:
201  * @pool: the pool to search
202  * @sessionid: the session id
203  *
204  * Find the session with @sessionid in @pool. The access time of the session
205  * will be updated with gst_rtsp_session_touch().
206  *
207  * Returns: the #GstRTSPSession with @sessionid or %NULL when the session did
208  * not exist. g_object_unref() after usage.
209  */
210 GstRTSPSession *
211 gst_rtsp_session_pool_find (GstRTSPSessionPool *pool, const gchar *sessionid)
212 {
213   GstRTSPSession *result;
214
215   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
216   g_return_val_if_fail (sessionid != NULL, NULL);
217
218   g_mutex_lock (pool->lock);
219   result = g_hash_table_lookup (pool->sessions, sessionid);
220   if (result) 
221     g_object_ref (result);
222   g_mutex_unlock (pool->lock);
223
224   if (result)
225     gst_rtsp_session_touch (result);
226
227   return result;
228 }
229
230 static gchar *
231 create_session_id (GstRTSPSessionPool *pool)
232 {
233   gchar id[16];
234   gint i;
235
236   for (i = 0; i < 16; i++) {
237     id[i] = g_random_int_range ('a', 'z');
238   }
239
240   return g_strndup (id, 16);
241 }
242
243 /**
244  * gst_rtsp_session_pool_create:
245  * @pool: a #GstRTSPSessionPool
246  *
247  * Create a new #GstRTSPSession object in @pool.
248  *
249  * Returns: a new #GstRTSPSession.
250  */
251 GstRTSPSession *
252 gst_rtsp_session_pool_create (GstRTSPSessionPool *pool)
253 {
254   GstRTSPSession *result = NULL;
255   GstRTSPSessionPoolClass *klass;
256   gchar *id = NULL;
257   guint retry;
258
259   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
260
261   klass = GST_RTSP_SESSION_POOL_GET_CLASS (pool);
262
263   retry = 0;
264   do {
265     /* start by creating a new random session id, we assume that this is random
266      * enough to not cause a collision, which we will check later  */
267     if (klass->create_session_id)
268       id = klass->create_session_id (pool);
269     else
270       goto no_function;
271
272     if (id == NULL)
273       goto no_session;
274
275     g_mutex_lock (pool->lock);
276     /* check session limit */
277     if (pool->max_sessions > 0) {
278       if (g_hash_table_size (pool->sessions) >= pool->max_sessions)
279         goto too_many_sessions;
280     }
281     /* check if the sessionid existed */
282     result = g_hash_table_lookup (pool->sessions, id);
283     if (result) {
284       /* found, retry with a different session id */
285       result = NULL;
286       retry++;
287       if (retry > 100)
288         goto collision;
289     }
290     else {
291       /* not found, create session and insert it in the pool */
292       result = gst_rtsp_session_new (id); 
293       /* take additional ref for the pool */
294       g_object_ref (result);
295       g_hash_table_insert (pool->sessions, result->sessionid, result);
296     }
297     g_mutex_unlock (pool->lock);
298
299     g_free (id);
300   } while (result == NULL);
301
302   return result;
303
304   /* ERRORS */
305 no_function:
306   {
307     g_warning ("no create_session_id vmethod in GstRTSPSessionPool %p", pool);
308     return NULL;
309   }
310 no_session:
311   {
312     g_warning ("can't create session id with GstRTSPSessionPool %p", pool);
313     return NULL;
314   }
315 collision:
316   {
317     g_warning ("can't find unique sessionid for GstRTSPSessionPool %p", pool);
318     g_mutex_unlock (pool->lock);
319     g_free (id);
320     return NULL;
321   }
322 too_many_sessions:
323   {
324     g_warning ("session pool reached max sessions of %d", pool->max_sessions);
325     g_mutex_unlock (pool->lock);
326     g_free (id);
327     return NULL;
328   }
329 }
330
331 /**
332  * gst_rtsp_session_pool_remove:
333  * @pool: a #GstRTSPSessionPool
334  * @sess: a #GstRTSPSession
335  *
336  * Remove @sess from @pool, releasing the ref that the pool has on @sess.
337  *
338  * Returns: %TRUE if the session was found and removed.
339  */
340 gboolean
341 gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool, GstRTSPSession *sess)
342 {
343   gboolean found;
344
345   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), FALSE);
346   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
347
348   g_mutex_lock (pool->lock);
349   found = g_hash_table_remove (pool->sessions, sess->sessionid);
350   g_mutex_unlock (pool->lock);
351
352   return found;
353 }
354
355 static gboolean
356 cleanup_func (gchar *sessionid, GstRTSPSession *sess, GstRTSPSessionPool *pool)
357 {
358   return gst_rtsp_session_is_expired (sess);
359 }
360
361 /**
362  * gst_rtsp_session_pool_cleanup:
363  * @pool: a #GstRTSPSessionPool
364  *
365  * Inspect all the sessions in @pool and remove the sessions that are inactive
366  * for more than their timeout.
367  *
368  * Returns: the amount of sessions that got removed.
369  */
370 guint
371 gst_rtsp_session_pool_cleanup (GstRTSPSessionPool *pool)
372 {
373   guint result;
374
375   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
376
377   g_mutex_lock (pool->lock);
378   result = g_hash_table_foreach_remove (pool->sessions, (GHRFunc) cleanup_func, pool);
379   g_mutex_unlock (pool->lock);
380
381   return result;
382 }