media: add lock to protect state changes
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-session.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 #include <string.h>
20
21 #include "rtsp-session.h"
22
23 #undef DEBUG
24
25 #define DEFAULT_TIMEOUT 60
26
27 enum
28 {
29   PROP_0,
30   PROP_SESSIONID,
31   PROP_TIMEOUT,
32   PROP_LAST
33 };
34
35 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
36 #define GST_CAT_DEFAULT rtsp_session_debug
37
38 static void gst_rtsp_session_get_property (GObject * object, guint propid,
39     GValue * value, GParamSpec * pspec);
40 static void gst_rtsp_session_set_property (GObject * object, guint propid,
41     const GValue * value, GParamSpec * pspec);
42 static void gst_rtsp_session_finalize (GObject * obj);
43
44 G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
45
46 static void
47 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
48 {
49   GObjectClass *gobject_class;
50
51   gobject_class = G_OBJECT_CLASS (klass);
52
53   gobject_class->get_property = gst_rtsp_session_get_property;
54   gobject_class->set_property = gst_rtsp_session_set_property;
55   gobject_class->finalize = gst_rtsp_session_finalize;
56
57   g_object_class_install_property (gobject_class, PROP_SESSIONID,
58       g_param_spec_string ("sessionid", "Sessionid", "the session id",
59           NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
60           G_PARAM_STATIC_STRINGS));
61
62   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
63       g_param_spec_uint ("timeout", "timeout",
64           "the timeout of the session (0 = never)", 0, G_MAXUINT,
65           DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
66
67   GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
68       "GstRTSPSession");
69 }
70
71 static void
72 gst_rtsp_session_init (GstRTSPSession * session)
73 {
74   g_mutex_init (&session->lock);
75   session->timeout = DEFAULT_TIMEOUT;
76   g_get_current_time (&session->create_time);
77   gst_rtsp_session_touch (session);
78 }
79
80 static void
81 gst_rtsp_session_finalize (GObject * obj)
82 {
83   GstRTSPSession *session;
84
85   session = GST_RTSP_SESSION (obj);
86
87   GST_INFO ("finalize session %p", session);
88
89   /* free all media */
90   g_list_free_full (session->medias, g_object_unref);
91
92   /* free session id */
93   g_free (session->sessionid);
94   g_mutex_clear (&session->lock);
95
96   G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
97 }
98
99 static void
100 gst_rtsp_session_get_property (GObject * object, guint propid,
101     GValue * value, GParamSpec * pspec)
102 {
103   GstRTSPSession *session = GST_RTSP_SESSION (object);
104
105   switch (propid) {
106     case PROP_SESSIONID:
107       g_value_set_string (value, session->sessionid);
108       break;
109     case PROP_TIMEOUT:
110       g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
111       break;
112     default:
113       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
114   }
115 }
116
117 static void
118 gst_rtsp_session_set_property (GObject * object, guint propid,
119     const GValue * value, GParamSpec * pspec)
120 {
121   GstRTSPSession *session = GST_RTSP_SESSION (object);
122
123   switch (propid) {
124     case PROP_SESSIONID:
125       g_free (session->sessionid);
126       session->sessionid = g_value_dup_string (value);
127       break;
128     case PROP_TIMEOUT:
129       gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
130       break;
131     default:
132       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
133   }
134 }
135
136 /**
137  * gst_rtsp_session_manage_media:
138  * @sess: a #GstRTSPSession
139  * @uri: the uri for the media
140  * @media: (transfer full): a #GstRTSPMedia
141  *
142  * Manage the media object @obj in @sess. @uri will be used to retrieve this
143  * media from the session with gst_rtsp_session_get_media().
144  *
145  * Ownership is taken from @media.
146  *
147  * Returns: (transfer none): a new @GstRTSPSessionMedia object.
148  */
149 GstRTSPSessionMedia *
150 gst_rtsp_session_manage_media (GstRTSPSession * sess, const GstRTSPUrl * uri,
151     GstRTSPMedia * media)
152 {
153   GstRTSPSessionMedia *result;
154
155   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
156   g_return_val_if_fail (uri != NULL, NULL);
157   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
158   g_return_val_if_fail (media->status == GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
159
160   result = gst_rtsp_session_media_new (uri, media);
161
162   g_mutex_lock (&sess->lock);
163   sess->medias = g_list_prepend (sess->medias, result);
164   g_mutex_unlock (&sess->lock);
165
166   GST_INFO ("manage new media %p in session %p", media, result);
167
168   return result;
169 }
170
171 /**
172  * gst_rtsp_session_release_media:
173  * @sess: a #GstRTSPSession
174  * @media: a #GstRTSPMedia
175  *
176  * Release the managed @media in @sess, freeing the memory allocated by it.
177  *
178  * Returns: %TRUE if there are more media session left in @sess.
179  */
180 gboolean
181 gst_rtsp_session_release_media (GstRTSPSession * sess,
182     GstRTSPSessionMedia * media)
183 {
184   GList *find;
185   gboolean more;
186
187   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
188   g_return_val_if_fail (media != NULL, FALSE);
189
190   g_mutex_lock (&sess->lock);
191   find = g_list_find (sess->medias, media);
192   if (find)
193     sess->medias = g_list_delete_link (sess->medias, find);
194   more = (sess->medias != NULL);
195   g_mutex_unlock (&sess->lock);
196
197   if (find)
198     g_object_unref (media);
199
200   return more;
201 }
202
203 /**
204  * gst_rtsp_session_get_media:
205  * @sess: a #GstRTSPSession
206  * @url: the url for the media
207  *
208  * Get the session media of the @url.
209  *
210  * Returns: (transfer none): the configuration for @url in @sess.
211  */
212 GstRTSPSessionMedia *
213 gst_rtsp_session_get_media (GstRTSPSession * sess, const GstRTSPUrl * url)
214 {
215   GstRTSPSessionMedia *result;
216   GList *walk;
217
218   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
219   g_return_val_if_fail (url != NULL, NULL);
220
221   result = NULL;
222
223   g_mutex_lock (&sess->lock);
224   for (walk = sess->medias; walk; walk = g_list_next (walk)) {
225     result = (GstRTSPSessionMedia *) walk->data;
226
227     if (g_str_equal (result->url->abspath, url->abspath))
228       break;
229
230     result = NULL;
231   }
232   g_mutex_unlock (&sess->lock);
233
234   return result;
235 }
236
237 /**
238  * gst_rtsp_session_new:
239  * @sessionid: a session id
240  *
241  * Create a new #GstRTSPSession instance with @sessionid.
242  */
243 GstRTSPSession *
244 gst_rtsp_session_new (const gchar * sessionid)
245 {
246   GstRTSPSession *result;
247
248   g_return_val_if_fail (sessionid != NULL, NULL);
249
250   result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
251
252   return result;
253 }
254
255 /**
256  * gst_rtsp_session_get_sessionid:
257  * @session: a #GstRTSPSession
258  *
259  * Get the sessionid of @session.
260  *
261  * Returns: the sessionid of @session. The value remains valid as long as
262  * @session is alive.
263  */
264 const gchar *
265 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
266 {
267   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
268
269   return session->sessionid;
270 }
271
272 /**
273  * gst_rtsp_session_get_header:
274  * @session: a #GstRTSPSession
275  *
276  * Get the string that can be placed in the Session header field.
277  *
278  * Returns: (transfer full): the Session header of @session. g_free() after usage.
279  */
280 gchar *
281 gst_rtsp_session_get_header (GstRTSPSession * session)
282 {
283   gchar *result;
284
285   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
286
287   g_mutex_lock (&session->lock);
288   if (session->timeout != 60)
289     result = g_strdup_printf ("%s; timeout=%d", session->sessionid,
290         session->timeout);
291   else
292     result = g_strdup (session->sessionid);
293   g_mutex_unlock (&session->lock);
294
295   return result;
296 }
297
298 /**
299  * gst_rtsp_session_set_timeout:
300  * @session: a #GstRTSPSession
301  * @timeout: the new timeout
302  *
303  * Configure @session for a timeout of @timeout seconds. The session will be
304  * cleaned up when there is no activity for @timeout seconds.
305  */
306 void
307 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
308 {
309   g_return_if_fail (GST_IS_RTSP_SESSION (session));
310
311   g_mutex_lock (&session->lock);
312   session->timeout = timeout;
313   g_mutex_unlock (&session->lock);
314 }
315
316 /**
317  * gst_rtsp_session_get_timeout:
318  * @session: a #GstRTSPSession
319  *
320  * Get the timeout value of @session.
321  *
322  * Returns: the timeout of @session in seconds.
323  */
324 guint
325 gst_rtsp_session_get_timeout (GstRTSPSession * session)
326 {
327   guint res;
328
329   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
330
331   g_mutex_lock (&session->lock);
332   res = session->timeout;
333   g_mutex_unlock (&session->lock);
334
335   return res;
336 }
337
338 /**
339  * gst_rtsp_session_touch:
340  * @session: a #GstRTSPSession
341  *
342  * Update the last_access time of the session to the current time.
343  */
344 void
345 gst_rtsp_session_touch (GstRTSPSession * session)
346 {
347   g_return_if_fail (GST_IS_RTSP_SESSION (session));
348
349   g_mutex_lock (&session->lock);
350   g_get_current_time (&session->last_access);
351   g_mutex_unlock (&session->lock);
352 }
353
354 /**
355  * gst_rtsp_session_prevent_expire:
356  * @session: a #GstRTSPSession
357  *
358  * Prevent @session from expiring.
359  */
360 void
361 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
362 {
363   g_return_if_fail (GST_IS_RTSP_SESSION (session));
364
365   g_atomic_int_add (&session->expire_count, 1);
366 }
367
368 /**
369  * gst_rtsp_session_allow_expire:
370  * @session: a #GstRTSPSession
371  *
372  * Allow @session to expire. This method must be called an equal
373  * amount of time as gst_rtsp_session_prevent_expire().
374  */
375 void
376 gst_rtsp_session_allow_expire (GstRTSPSession * session)
377 {
378   g_atomic_int_add (&session->expire_count, -1);
379 }
380
381 /**
382  * gst_rtsp_session_next_timeout:
383  * @session: a #GstRTSPSession
384  * @now: the current system time
385  *
386  * Get the amount of milliseconds till the session will expire.
387  *
388  * Returns: the amount of milliseconds since the session will time out.
389  */
390 gint
391 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
392 {
393   gint res;
394   GstClockTime last_access, now_ns;
395
396   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
397   g_return_val_if_fail (now != NULL, -1);
398
399   g_mutex_lock (&session->lock);
400   if (g_atomic_int_get (&session->expire_count) != 0) {
401     /* touch session when the expire count is not 0 */
402     g_get_current_time (&session->last_access);
403   }
404
405   last_access = GST_TIMEVAL_TO_TIME (session->last_access);
406   /* add timeout allow for 5 seconds of extra time */
407   last_access += session->timeout * GST_SECOND + (5 * GST_SECOND);
408   g_mutex_unlock (&session->lock);
409
410   now_ns = GST_TIMEVAL_TO_TIME (*now);
411
412   if (last_access > now_ns)
413     res = GST_TIME_AS_MSECONDS (last_access - now_ns);
414   else
415     res = 0;
416
417   return res;
418 }
419
420 /**
421  * gst_rtsp_session_is_expired:
422  * @session: a #GstRTSPSession
423  * @now: the current system time
424  *
425  * Check if @session timeout out.
426  *
427  * Returns: %TRUE if @session timed out
428  */
429 gboolean
430 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
431 {
432   gboolean res;
433
434   res = (gst_rtsp_session_next_timeout (session, now) == 0);
435
436   return res;
437 }