Use getters and setters in property code
[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_value_set_uint (value, gst_rtsp_session_pool_get_max_sessions (pool));
92       break;
93     default:
94       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
95       break;
96   }
97 }
98
99 static void
100 gst_rtsp_session_pool_set_property (GObject *object, guint propid,
101     const GValue *value, GParamSpec *pspec)
102 {
103   GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
104
105   switch (propid) {
106     case PROP_MAX_SESSIONS:
107       gst_rtsp_session_pool_set_max_sessions (pool, g_value_get_uint (value));
108       break;
109     default:
110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
111       break;
112   }
113 }
114
115 /**
116  * gst_rtsp_session_pool_new:
117  *
118  * Create a new #GstRTSPSessionPool instance.
119  *
120  * Returns: A new #GstRTSPSessionPool. g_object_unref() after usage.
121  */
122 GstRTSPSessionPool *
123 gst_rtsp_session_pool_new (void)
124 {
125   GstRTSPSessionPool *result;
126
127   result = g_object_new (GST_TYPE_RTSP_SESSION_POOL, NULL);
128
129   return result;
130 }
131
132 /**
133  * gst_rtsp_session_pool_set_max_sessions:
134  * @pool: a #GstRTSPSessionPool
135  * @max: the maximum number of sessions
136  *
137  * Configure the maximum allowed number of sessions in @pool to @max.
138  * A value of 0 means an unlimited amount of sessions.
139  */
140 void
141 gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool *pool, guint max)
142 {
143   g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool));
144
145   g_mutex_lock (pool->lock);
146   pool->max_sessions = max;
147   g_mutex_unlock (pool->lock);
148 }
149
150 /**
151  * gst_rtsp_session_pool_get_max_sessions:
152  * @pool: a #GstRTSPSessionPool
153  *
154  * Get the maximum allowed number of sessions in @pool. 0 means an unlimited
155  * amount of sessions.
156  *
157  * Returns: the maximum allowed number of sessions.
158  */
159 guint
160 gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool *pool)
161 {
162   guint result;
163
164   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
165
166   g_mutex_lock (pool->lock);
167   result = pool->max_sessions;
168   g_mutex_unlock (pool->lock);
169
170   return result;
171 }
172
173 /**
174  * gst_rtsp_session_pool_get_n_sessions:
175  * @pool: a #GstRTSPSessionPool
176  *
177  * Get the amount of active sessions in @pool.
178  *
179  * Returns: the amount of active sessions in @pool.
180  */
181 guint
182 gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool *pool)
183 {
184   guint result;
185
186   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
187
188   g_mutex_lock (pool->lock);
189   result = g_hash_table_size (pool->sessions);
190   g_mutex_unlock (pool->lock);
191
192   return result;
193 }
194
195 /**
196  * gst_rtsp_session_pool_find:
197  * @pool: the pool to search
198  * @sessionid: the session id
199  *
200  * Find the session with @sessionid in @pool. The access time of the session
201  * will be updated with gst_rtsp_session_touch().
202  *
203  * Returns: the #GstRTSPSession with @sessionid or %NULL when the session did
204  * not exist. g_object_unref() after usage.
205  */
206 GstRTSPSession *
207 gst_rtsp_session_pool_find (GstRTSPSessionPool *pool, const gchar *sessionid)
208 {
209   GstRTSPSession *result;
210
211   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
212   g_return_val_if_fail (sessionid != NULL, NULL);
213
214   g_mutex_lock (pool->lock);
215   result = g_hash_table_lookup (pool->sessions, sessionid);
216   if (result) 
217     g_object_ref (result);
218   g_mutex_unlock (pool->lock);
219
220   if (result)
221     gst_rtsp_session_touch (result);
222
223   return result;
224 }
225
226 static gchar *
227 create_session_id (GstRTSPSessionPool *pool)
228 {
229   gchar id[16];
230   gint i;
231
232   for (i = 0; i < 16; i++) {
233     id[i] = g_random_int_range ('a', 'z');
234   }
235
236   return g_strndup (id, 16);
237 }
238
239 /**
240  * gst_rtsp_session_pool_create:
241  * @pool: a #GstRTSPSessionPool
242  *
243  * Create a new #GstRTSPSession object in @pool.
244  *
245  * Returns: a new #GstRTSPSession.
246  */
247 GstRTSPSession *
248 gst_rtsp_session_pool_create (GstRTSPSessionPool *pool)
249 {
250   GstRTSPSession *result = NULL;
251   GstRTSPSessionPoolClass *klass;
252   gchar *id = NULL;
253   guint retry;
254
255   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
256
257   klass = GST_RTSP_SESSION_POOL_GET_CLASS (pool);
258
259   retry = 0;
260   do {
261     /* start by creating a new random session id, we assume that this is random
262      * enough to not cause a collision, which we will check later  */
263     if (klass->create_session_id)
264       id = klass->create_session_id (pool);
265     else
266       goto no_function;
267
268     if (id == NULL)
269       goto no_session;
270
271     g_mutex_lock (pool->lock);
272     /* check session limit */
273     if (pool->max_sessions > 0) {
274       if (g_hash_table_size (pool->sessions) >= pool->max_sessions)
275         goto too_many_sessions;
276     }
277     /* check if the sessionid existed */
278     result = g_hash_table_lookup (pool->sessions, id);
279     if (result) {
280       /* found, retry with a different session id */
281       result = NULL;
282       retry++;
283       if (retry > 100)
284         goto collision;
285     }
286     else {
287       /* not found, create session and insert it in the pool */
288       result = gst_rtsp_session_new (id); 
289       /* take additional ref for the pool */
290       g_object_ref (result);
291       g_hash_table_insert (pool->sessions, result->sessionid, result);
292     }
293     g_mutex_unlock (pool->lock);
294
295     g_free (id);
296   } while (result == NULL);
297
298   return result;
299
300   /* ERRORS */
301 no_function:
302   {
303     g_warning ("no create_session_id vmethod in GstRTSPSessionPool %p", pool);
304     return NULL;
305   }
306 no_session:
307   {
308     g_warning ("can't create session id with GstRTSPSessionPool %p", pool);
309     return NULL;
310   }
311 collision:
312   {
313     g_warning ("can't find unique sessionid for GstRTSPSessionPool %p", pool);
314     g_mutex_unlock (pool->lock);
315     g_free (id);
316     return NULL;
317   }
318 too_many_sessions:
319   {
320     g_warning ("session pool reached max sessions of %d", pool->max_sessions);
321     g_mutex_unlock (pool->lock);
322     g_free (id);
323     return NULL;
324   }
325 }
326
327 /**
328  * gst_rtsp_session_pool_remove:
329  * @pool: a #GstRTSPSessionPool
330  * @sess: a #GstRTSPSession
331  *
332  * Remove @sess from @pool, releasing the ref that the pool has on @sess.
333  *
334  * Returns: %TRUE if the session was found and removed.
335  */
336 gboolean
337 gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool, GstRTSPSession *sess)
338 {
339   gboolean found;
340
341   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), FALSE);
342   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
343
344   g_mutex_lock (pool->lock);
345   found = g_hash_table_remove (pool->sessions, sess->sessionid);
346   g_mutex_unlock (pool->lock);
347
348   return found;
349 }
350
351 static gboolean
352 cleanup_func (gchar *sessionid, GstRTSPSession *sess, GstRTSPSessionPool *pool)
353 {
354   return gst_rtsp_session_is_expired (sess);
355 }
356
357 /**
358  * gst_rtsp_session_pool_cleanup:
359  * @pool: a #GstRTSPSessionPool
360  *
361  * Inspect all the sessions in @pool and remove the sessions that are inactive
362  * for more than their timeout.
363  *
364  * Returns: the amount of sessions that got removed.
365  */
366 guint
367 gst_rtsp_session_pool_cleanup (GstRTSPSessionPool *pool)
368 {
369   guint result;
370
371   g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
372
373   g_mutex_lock (pool->lock);
374   result = g_hash_table_foreach_remove (pool->sessions, (GHRFunc) cleanup_func, pool);
375   g_mutex_unlock (pool->lock);
376
377   return result;
378 }