Fixed several GIR warnings
[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 /**
20  * SECTION:rtsp-session
21  * @short_description: An object to manage media
22  * @see_also: #GstRTSPSessionPool, #GstRTSPSessionMedia, #GstRTSPMedia
23  *
24  * The #GstRTSPSession is identified by an id, unique in the
25  * #GstRTSPSessionPool that created the session and manages media and its
26  * configuration.
27  *
28  * A #GstRTSPSession has a timeout that can be retrieved with
29  * gst_rtsp_session_get_timeout(). You can check if the sessions is expired with
30  * gst_rtsp_session_is_expired(). gst_rtsp_session_touch() will reset the
31  * expiration counter of the session.
32  *
33  * When a client configures a media with SETUP, a session will be created to
34  * keep track of the configuration of that media. With
35  * gst_rtsp_session_manage_media(), the media is added to the managed media
36  * in the session. With gst_rtsp_session_release_media() the media can be
37  * released again from the session. Managed media is identified in the sessions
38  * with a url. Use gst_rtsp_session_get_media() to get the media that matches
39  * (part of) the given url.
40  *
41  * The media in a session can be iterated with gst_rtsp_session_filter().
42  *
43  * Last reviewed on 2013-07-11 (1.0.0)
44  */
45
46 #include <string.h>
47
48 #include "rtsp-session.h"
49
50 #define GST_RTSP_SESSION_GET_PRIVATE(obj)  \
51        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_SESSION, GstRTSPSessionPrivate))
52
53 struct _GstRTSPSessionPrivate
54 {
55   GMutex lock;                  /* protects everything but sessionid and create_time */
56   gchar *sessionid;
57
58   guint timeout;
59   GTimeVal create_time;         /* immutable */
60   GTimeVal last_access;
61   gint expire_count;
62
63   GList *medias;
64 };
65
66 #undef DEBUG
67
68 #define DEFAULT_TIMEOUT 60
69
70 enum
71 {
72   PROP_0,
73   PROP_SESSIONID,
74   PROP_TIMEOUT,
75   PROP_LAST
76 };
77
78 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
79 #define GST_CAT_DEFAULT rtsp_session_debug
80
81 static void gst_rtsp_session_get_property (GObject * object, guint propid,
82     GValue * value, GParamSpec * pspec);
83 static void gst_rtsp_session_set_property (GObject * object, guint propid,
84     const GValue * value, GParamSpec * pspec);
85 static void gst_rtsp_session_finalize (GObject * obj);
86
87 G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
88
89 static void
90 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
91 {
92   GObjectClass *gobject_class;
93
94   g_type_class_add_private (klass, sizeof (GstRTSPSessionPrivate));
95
96   gobject_class = G_OBJECT_CLASS (klass);
97
98   gobject_class->get_property = gst_rtsp_session_get_property;
99   gobject_class->set_property = gst_rtsp_session_set_property;
100   gobject_class->finalize = gst_rtsp_session_finalize;
101
102   g_object_class_install_property (gobject_class, PROP_SESSIONID,
103       g_param_spec_string ("sessionid", "Sessionid", "the session id",
104           NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
105           G_PARAM_STATIC_STRINGS));
106
107   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
108       g_param_spec_uint ("timeout", "timeout",
109           "the timeout of the session (0 = never)", 0, G_MAXUINT,
110           DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
111
112   GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
113       "GstRTSPSession");
114 }
115
116 static void
117 gst_rtsp_session_init (GstRTSPSession * session)
118 {
119   GstRTSPSessionPrivate *priv = GST_RTSP_SESSION_GET_PRIVATE (session);
120
121   session->priv = priv;
122
123   g_mutex_init (&priv->lock);
124   priv->timeout = DEFAULT_TIMEOUT;
125   g_get_current_time (&priv->create_time);
126   gst_rtsp_session_touch (session);
127 }
128
129 static void
130 gst_rtsp_session_finalize (GObject * obj)
131 {
132   GstRTSPSession *session;
133   GstRTSPSessionPrivate *priv;
134
135   session = GST_RTSP_SESSION (obj);
136   priv = session->priv;
137
138   GST_INFO ("finalize session %p", session);
139
140   /* free all media */
141   g_list_free_full (priv->medias, g_object_unref);
142
143   /* free session id */
144   g_free (priv->sessionid);
145   g_mutex_clear (&priv->lock);
146
147   G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
148 }
149
150 static void
151 gst_rtsp_session_get_property (GObject * object, guint propid,
152     GValue * value, GParamSpec * pspec)
153 {
154   GstRTSPSession *session = GST_RTSP_SESSION (object);
155   GstRTSPSessionPrivate *priv = session->priv;
156
157   switch (propid) {
158     case PROP_SESSIONID:
159       g_value_set_string (value, priv->sessionid);
160       break;
161     case PROP_TIMEOUT:
162       g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
163       break;
164     default:
165       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
166   }
167 }
168
169 static void
170 gst_rtsp_session_set_property (GObject * object, guint propid,
171     const GValue * value, GParamSpec * pspec)
172 {
173   GstRTSPSession *session = GST_RTSP_SESSION (object);
174   GstRTSPSessionPrivate *priv = session->priv;
175
176   switch (propid) {
177     case PROP_SESSIONID:
178       g_free (priv->sessionid);
179       priv->sessionid = g_value_dup_string (value);
180       break;
181     case PROP_TIMEOUT:
182       gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
183       break;
184     default:
185       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
186   }
187 }
188
189 /**
190  * gst_rtsp_session_manage_media:
191  * @sess: a #GstRTSPSession
192  * @path: the path for the media
193  * @media: (transfer full): a #GstRTSPMedia
194  *
195  * Manage the media object @obj in @sess. @path will be used to retrieve this
196  * media from the session with gst_rtsp_session_get_media().
197  *
198  * Ownership is taken from @media.
199  *
200  * Returns: (transfer none): a new @GstRTSPSessionMedia object.
201  */
202 GstRTSPSessionMedia *
203 gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
204     GstRTSPMedia * media)
205 {
206   GstRTSPSessionPrivate *priv;
207   GstRTSPSessionMedia *result;
208
209   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
210   g_return_val_if_fail (path != NULL, NULL);
211   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
212   g_return_val_if_fail (gst_rtsp_media_get_status (media) ==
213       GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
214
215   priv = sess->priv;
216
217   result = gst_rtsp_session_media_new (path, media);
218
219   g_mutex_lock (&priv->lock);
220   priv->medias = g_list_prepend (priv->medias, result);
221   g_mutex_unlock (&priv->lock);
222
223   GST_INFO ("manage new media %p in session %p", media, result);
224
225   return result;
226 }
227
228 /**
229  * gst_rtsp_session_release_media:
230  * @sess: a #GstRTSPSession
231  * @media: a #GstRTSPMedia
232  *
233  * Release the managed @media in @sess, freeing the memory allocated by it.
234  *
235  * Returns: %TRUE if there are more media session left in @sess.
236  */
237 gboolean
238 gst_rtsp_session_release_media (GstRTSPSession * sess,
239     GstRTSPSessionMedia * media)
240 {
241   GstRTSPSessionPrivate *priv;
242   GList *find;
243   gboolean more;
244
245   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
246   g_return_val_if_fail (media != NULL, FALSE);
247
248   priv = sess->priv;
249
250   g_mutex_lock (&priv->lock);
251   find = g_list_find (priv->medias, media);
252   if (find)
253     priv->medias = g_list_delete_link (priv->medias, find);
254   more = (priv->medias != NULL);
255   g_mutex_unlock (&priv->lock);
256
257   if (find)
258     g_object_unref (media);
259
260   return more;
261 }
262
263 /**
264  * gst_rtsp_session_get_media:
265  * @sess: a #GstRTSPSession
266  * @path: the path for the media
267  * @matched: (out): the amount of matched characters
268  *
269  * Get the session media for @path. @matched will contain the number of matched
270  * characters of @path.
271  *
272  * Returns: (transfer none): the configuration for @path in @sess.
273  */
274 GstRTSPSessionMedia *
275 gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
276     gint * matched)
277 {
278   GstRTSPSessionPrivate *priv;
279   GstRTSPSessionMedia *result;
280   GList *walk;
281   gint best;
282
283   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
284   g_return_val_if_fail (path != NULL, NULL);
285
286   priv = sess->priv;
287   result = NULL;
288   best = 0;
289
290   g_mutex_lock (&priv->lock);
291   for (walk = priv->medias; walk; walk = g_list_next (walk)) {
292     GstRTSPSessionMedia *test;
293
294     test = (GstRTSPSessionMedia *) walk->data;
295
296     /* find largest match */
297     if (gst_rtsp_session_media_matches (test, path, matched)) {
298       if (best < *matched) {
299         result = test;
300         best = *matched;
301       }
302     }
303   }
304   g_mutex_unlock (&priv->lock);
305
306   *matched = best;
307
308   return result;
309 }
310
311 /**
312  * gst_rtsp_session_filter:
313  * @sess: a #GstRTSPSession
314  * @func: (scope call): a callback
315  * @user_data: user data passed to @func
316  *
317  * Call @func for each media in @sess. The result value of @func determines
318  * what happens to the media. @func will be called with @sess
319  * locked so no further actions on @sess can be performed from @func.
320  *
321  * If @func returns #GST_RTSP_FILTER_REMOVE, the media will be removed from
322  * @sess.
323  *
324  * If @func returns #GST_RTSP_FILTER_KEEP, the media will remain in @sess.
325  *
326  * If @func returns #GST_RTSP_FILTER_REF, the media will remain in @sess but
327  * will also be added with an additional ref to the result #GList of this
328  * function..
329  *
330  * Returns: (element-type GstRTSPSessionMedia) (transfer full): a GList with all
331  * media for which @func returned #GST_RTSP_FILTER_REF. After usage, each
332  * element in the #GList should be unreffed before the list is freed.
333  */
334 GList *
335 gst_rtsp_session_filter (GstRTSPSession * sess,
336     GstRTSPSessionFilterFunc func, gpointer user_data)
337 {
338   GstRTSPSessionPrivate *priv;
339   GList *result, *walk, *next;
340
341   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
342   g_return_val_if_fail (func != NULL, NULL);
343
344   priv = sess->priv;
345
346   result = NULL;
347
348   g_mutex_lock (&priv->lock);
349   for (walk = priv->medias; walk; walk = next) {
350     GstRTSPSessionMedia *media = walk->data;
351
352     next = g_list_next (walk);
353
354     switch (func (sess, media, user_data)) {
355       case GST_RTSP_FILTER_REMOVE:
356         g_object_unref (media);
357         priv->medias = g_list_delete_link (priv->medias, walk);
358         break;
359       case GST_RTSP_FILTER_REF:
360         result = g_list_prepend (result, g_object_ref (media));
361         break;
362       case GST_RTSP_FILTER_KEEP:
363       default:
364         break;
365     }
366   }
367   g_mutex_unlock (&priv->lock);
368
369   return result;
370 }
371
372 /**
373  * gst_rtsp_session_new:
374  * @sessionid: a session id
375  *
376  * Create a new #GstRTSPSession instance with @sessionid.
377  */
378 GstRTSPSession *
379 gst_rtsp_session_new (const gchar * sessionid)
380 {
381   GstRTSPSession *result;
382
383   g_return_val_if_fail (sessionid != NULL, NULL);
384
385   result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
386
387   return result;
388 }
389
390 /**
391  * gst_rtsp_session_get_sessionid:
392  * @session: a #GstRTSPSession
393  *
394  * Get the sessionid of @session.
395  *
396  * Returns: the sessionid of @session. The value remains valid as long as
397  * @session is alive.
398  */
399 const gchar *
400 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
401 {
402   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
403
404   return session->priv->sessionid;
405 }
406
407 /**
408  * gst_rtsp_session_get_header:
409  * @session: a #GstRTSPSession
410  *
411  * Get the string that can be placed in the Session header field.
412  *
413  * Returns: (transfer full): the Session header of @session. g_free() after usage.
414  */
415 gchar *
416 gst_rtsp_session_get_header (GstRTSPSession * session)
417 {
418   GstRTSPSessionPrivate *priv;
419   gchar *result;
420
421   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
422
423   priv = session->priv;
424
425   g_mutex_lock (&priv->lock);
426   if (priv->timeout != 60)
427     result = g_strdup_printf ("%s; timeout=%d", priv->sessionid, priv->timeout);
428   else
429     result = g_strdup (priv->sessionid);
430   g_mutex_unlock (&priv->lock);
431
432   return result;
433 }
434
435 /**
436  * gst_rtsp_session_set_timeout:
437  * @session: a #GstRTSPSession
438  * @timeout: the new timeout
439  *
440  * Configure @session for a timeout of @timeout seconds. The session will be
441  * cleaned up when there is no activity for @timeout seconds.
442  */
443 void
444 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
445 {
446   GstRTSPSessionPrivate *priv;
447
448   g_return_if_fail (GST_IS_RTSP_SESSION (session));
449
450   priv = session->priv;
451
452   g_mutex_lock (&priv->lock);
453   priv->timeout = timeout;
454   g_mutex_unlock (&priv->lock);
455 }
456
457 /**
458  * gst_rtsp_session_get_timeout:
459  * @session: a #GstRTSPSession
460  *
461  * Get the timeout value of @session.
462  *
463  * Returns: the timeout of @session in seconds.
464  */
465 guint
466 gst_rtsp_session_get_timeout (GstRTSPSession * session)
467 {
468   GstRTSPSessionPrivate *priv;
469   guint res;
470
471   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
472
473   priv = session->priv;
474
475   g_mutex_lock (&priv->lock);
476   res = priv->timeout;
477   g_mutex_unlock (&priv->lock);
478
479   return res;
480 }
481
482 /**
483  * gst_rtsp_session_touch:
484  * @session: a #GstRTSPSession
485  *
486  * Update the last_access time of the session to the current time.
487  */
488 void
489 gst_rtsp_session_touch (GstRTSPSession * session)
490 {
491   GstRTSPSessionPrivate *priv;
492
493   g_return_if_fail (GST_IS_RTSP_SESSION (session));
494
495   priv = session->priv;
496
497   g_mutex_lock (&priv->lock);
498   g_get_current_time (&priv->last_access);
499   g_mutex_unlock (&priv->lock);
500 }
501
502 /**
503  * gst_rtsp_session_prevent_expire:
504  * @session: a #GstRTSPSession
505  *
506  * Prevent @session from expiring.
507  */
508 void
509 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
510 {
511   g_return_if_fail (GST_IS_RTSP_SESSION (session));
512
513   g_atomic_int_add (&session->priv->expire_count, 1);
514 }
515
516 /**
517  * gst_rtsp_session_allow_expire:
518  * @session: a #GstRTSPSession
519  *
520  * Allow @session to expire. This method must be called an equal
521  * amount of time as gst_rtsp_session_prevent_expire().
522  */
523 void
524 gst_rtsp_session_allow_expire (GstRTSPSession * session)
525 {
526   g_atomic_int_add (&session->priv->expire_count, -1);
527 }
528
529 /**
530  * gst_rtsp_session_next_timeout:
531  * @session: a #GstRTSPSession
532  * @now: the current system time
533  *
534  * Get the amount of milliseconds till the session will expire.
535  *
536  * Returns: the amount of milliseconds since the session will time out.
537  */
538 gint
539 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
540 {
541   GstRTSPSessionPrivate *priv;
542   gint res;
543   GstClockTime last_access, now_ns;
544
545   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
546   g_return_val_if_fail (now != NULL, -1);
547
548   priv = session->priv;
549
550   g_mutex_lock (&priv->lock);
551   if (g_atomic_int_get (&priv->expire_count) != 0) {
552     /* touch session when the expire count is not 0 */
553     g_get_current_time (&priv->last_access);
554   }
555
556   last_access = GST_TIMEVAL_TO_TIME (priv->last_access);
557   /* add timeout allow for 5 seconds of extra time */
558   last_access += priv->timeout * GST_SECOND + (5 * GST_SECOND);
559   g_mutex_unlock (&priv->lock);
560
561   now_ns = GST_TIMEVAL_TO_TIME (*now);
562
563   if (last_access > now_ns)
564     res = GST_TIME_AS_MSECONDS (last_access - now_ns);
565   else
566     res = 0;
567
568   return res;
569 }
570
571 /**
572  * gst_rtsp_session_is_expired:
573  * @session: a #GstRTSPSession
574  * @now: the current system time
575  *
576  * Check if @session timeout out.
577  *
578  * Returns: %TRUE if @session timed out
579  */
580 gboolean
581 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
582 {
583   gboolean res;
584
585   res = (gst_rtsp_session_next_timeout (session, now) == 0);
586
587   return res;
588 }