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