2 * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
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.
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.
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.
20 * SECTION:rtsp-session
21 * @short_description: An object to manage media
22 * @see_also: #GstRTSPSessionPool, #GstRTSPSessionMedia, #GstRTSPMedia
24 * The #GstRTSPSession is identified by an id, unique in the
25 * #GstRTSPSessionPool that created the session and manages media and its
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.
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.
41 * The media in a session can be iterated with gst_rtsp_session_filter().
43 * Last reviewed on 2013-07-11 (1.0.0)
48 #include "rtsp-session.h"
50 #define GST_RTSP_SESSION_GET_PRIVATE(obj) \
51 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_SESSION, GstRTSPSessionPrivate))
53 struct _GstRTSPSessionPrivate
55 GMutex lock; /* protects everything but sessionid and create_time */
59 gboolean timeout_always_visible;
60 GMutex last_access_lock;
61 gint64 last_access_monotonic_time;
62 gint64 last_access_real_time;
71 #define DEFAULT_TIMEOUT 60
73 #define DEFAULT_ALWAYS_VISIBLE FALSE
80 PROP_TIMEOUT_ALWAYS_VISIBLE,
84 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
85 #define GST_CAT_DEFAULT rtsp_session_debug
87 static void gst_rtsp_session_get_property (GObject * object, guint propid,
88 GValue * value, GParamSpec * pspec);
89 static void gst_rtsp_session_set_property (GObject * object, guint propid,
90 const GValue * value, GParamSpec * pspec);
91 static void gst_rtsp_session_finalize (GObject * obj);
93 G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
96 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
98 GObjectClass *gobject_class;
100 g_type_class_add_private (klass, sizeof (GstRTSPSessionPrivate));
102 gobject_class = G_OBJECT_CLASS (klass);
104 gobject_class->get_property = gst_rtsp_session_get_property;
105 gobject_class->set_property = gst_rtsp_session_set_property;
106 gobject_class->finalize = gst_rtsp_session_finalize;
108 g_object_class_install_property (gobject_class, PROP_SESSIONID,
109 g_param_spec_string ("sessionid", "Sessionid", "the session id",
110 NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
111 G_PARAM_STATIC_STRINGS));
113 g_object_class_install_property (gobject_class, PROP_TIMEOUT,
114 g_param_spec_uint ("timeout", "timeout",
115 "the timeout of the session (0 = never)", 0, G_MAXUINT,
116 DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
118 g_object_class_install_property (gobject_class, PROP_TIMEOUT_ALWAYS_VISIBLE,
119 g_param_spec_boolean ("timeout-always-visible", "Timeout Always Visible ",
120 "timeout always visible in header",
121 DEFAULT_ALWAYS_VISIBLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
123 GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
128 gst_rtsp_session_init (GstRTSPSession * session)
130 GstRTSPSessionPrivate *priv = GST_RTSP_SESSION_GET_PRIVATE (session);
132 session->priv = priv;
134 GST_INFO ("init session %p", session);
136 g_mutex_init (&priv->lock);
137 g_mutex_init (&priv->last_access_lock);
138 priv->timeout = DEFAULT_TIMEOUT;
140 gst_rtsp_session_touch (session);
144 gst_rtsp_session_finalize (GObject * obj)
146 GstRTSPSession *session;
147 GstRTSPSessionPrivate *priv;
149 session = GST_RTSP_SESSION (obj);
150 priv = session->priv;
152 GST_INFO ("finalize session %p", session);
155 g_list_free_full (priv->medias, g_object_unref);
157 /* free session id */
158 g_free (priv->sessionid);
159 g_mutex_clear (&priv->last_access_lock);
160 g_mutex_clear (&priv->lock);
162 G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
166 gst_rtsp_session_get_property (GObject * object, guint propid,
167 GValue * value, GParamSpec * pspec)
169 GstRTSPSession *session = GST_RTSP_SESSION (object);
170 GstRTSPSessionPrivate *priv = session->priv;
174 g_value_set_string (value, priv->sessionid);
177 g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
179 case PROP_TIMEOUT_ALWAYS_VISIBLE:
180 g_value_set_boolean (value, priv->timeout_always_visible);
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
188 gst_rtsp_session_set_property (GObject * object, guint propid,
189 const GValue * value, GParamSpec * pspec)
191 GstRTSPSession *session = GST_RTSP_SESSION (object);
192 GstRTSPSessionPrivate *priv = session->priv;
196 g_free (priv->sessionid);
197 priv->sessionid = g_value_dup_string (value);
200 gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
202 case PROP_TIMEOUT_ALWAYS_VISIBLE:
203 g_mutex_lock (&priv->lock);
204 priv->timeout_always_visible = g_value_get_boolean (value);
205 g_mutex_unlock (&priv->lock);
208 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
213 * gst_rtsp_session_manage_media:
214 * @sess: a #GstRTSPSession
215 * @path: the path for the media
216 * @media: (transfer full): a #GstRTSPMedia
218 * Manage the media object @obj in @sess. @path will be used to retrieve this
219 * media from the session with gst_rtsp_session_get_media().
221 * Ownership is taken from @media.
223 * Returns: (transfer none): a new @GstRTSPSessionMedia object.
225 GstRTSPSessionMedia *
226 gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
227 GstRTSPMedia * media)
229 GstRTSPSessionPrivate *priv;
230 GstRTSPSessionMedia *result;
231 GstRTSPMediaStatus status;
233 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
234 g_return_val_if_fail (path != NULL, NULL);
235 g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
236 status = gst_rtsp_media_get_status (media);
237 g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
238 GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
242 result = gst_rtsp_session_media_new (path, media);
244 g_mutex_lock (&priv->lock);
245 priv->medias = g_list_prepend (priv->medias, result);
246 priv->medias_cookie++;
247 g_mutex_unlock (&priv->lock);
249 GST_INFO ("manage new media %p in session %p", media, result);
255 gst_rtsp_session_unset_transport_keepalive (GstRTSPSessionMedia * sessmedia)
260 media = gst_rtsp_session_media_get_media (sessmedia);
261 n_streams = gst_rtsp_media_n_streams (media);
263 for (i = 0; i < n_streams; i++) {
264 GstRTSPStreamTransport *transport =
265 gst_rtsp_session_media_get_transport (sessmedia, i);
270 gst_rtsp_stream_transport_set_keepalive (transport, NULL, NULL, NULL);
275 * gst_rtsp_session_release_media:
276 * @sess: a #GstRTSPSession
277 * @media: (transfer none): a #GstRTSPMedia
279 * Release the managed @media in @sess, freeing the memory allocated by it.
281 * Returns: %TRUE if there are more media session left in @sess.
284 gst_rtsp_session_release_media (GstRTSPSession * sess,
285 GstRTSPSessionMedia * media)
287 GstRTSPSessionPrivate *priv;
291 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
292 g_return_val_if_fail (media != NULL, FALSE);
296 g_mutex_lock (&priv->lock);
297 find = g_list_find (priv->medias, media);
299 priv->medias = g_list_delete_link (priv->medias, find);
300 priv->medias_cookie++;
302 more = (priv->medias != NULL);
303 g_mutex_unlock (&priv->lock);
306 gst_rtsp_session_unset_transport_keepalive (media);
309 g_object_unref (media);
315 * gst_rtsp_session_get_media:
316 * @sess: a #GstRTSPSession
317 * @path: the path for the media
318 * @matched: (out): the amount of matched characters
320 * Get the session media for @path. @matched will contain the number of matched
321 * characters of @path.
323 * Returns: (transfer none): the configuration for @path in @sess.
325 GstRTSPSessionMedia *
326 gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
329 GstRTSPSessionPrivate *priv;
330 GstRTSPSessionMedia *result;
334 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
335 g_return_val_if_fail (path != NULL, NULL);
341 g_mutex_lock (&priv->lock);
342 for (walk = priv->medias; walk; walk = g_list_next (walk)) {
343 GstRTSPSessionMedia *test;
345 test = (GstRTSPSessionMedia *) walk->data;
347 /* find largest match */
348 if (gst_rtsp_session_media_matches (test, path, matched)) {
349 if (best < *matched) {
355 g_mutex_unlock (&priv->lock);
363 * gst_rtsp_session_filter:
364 * @sess: a #GstRTSPSession
365 * @func: (scope call) (allow-none): a callback
366 * @user_data: (closure): user data passed to @func
368 * Call @func for each media in @sess. The result value of @func determines
369 * what happens to the media. @func will be called with @sess
370 * locked so no further actions on @sess can be performed from @func.
372 * If @func returns #GST_RTSP_FILTER_REMOVE, the media will be removed from
375 * If @func returns #GST_RTSP_FILTER_KEEP, the media will remain in @sess.
377 * If @func returns #GST_RTSP_FILTER_REF, the media will remain in @sess but
378 * will also be added with an additional ref to the result #GList of this
381 * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all media.
383 * Returns: (element-type GstRTSPSessionMedia) (transfer full): a GList with all
384 * media for which @func returned #GST_RTSP_FILTER_REF. After usage, each
385 * element in the #GList should be unreffed before the list is freed.
388 gst_rtsp_session_filter (GstRTSPSession * sess,
389 GstRTSPSessionFilterFunc func, gpointer user_data)
391 GstRTSPSessionPrivate *priv;
392 GList *result, *walk, *next;
396 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
402 visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
404 g_mutex_lock (&priv->lock);
406 cookie = priv->medias_cookie;
407 for (walk = priv->medias; walk; walk = next) {
408 GstRTSPSessionMedia *media = walk->data;
409 GstRTSPFilterResult res;
412 next = g_list_next (walk);
415 /* only visit each media once */
416 if (g_hash_table_contains (visited, media))
419 g_hash_table_add (visited, g_object_ref (media));
420 g_mutex_unlock (&priv->lock);
422 res = func (sess, media, user_data);
424 g_mutex_lock (&priv->lock);
426 res = GST_RTSP_FILTER_REF;
428 changed = (cookie != priv->medias_cookie);
431 case GST_RTSP_FILTER_REMOVE:
433 priv->medias = g_list_remove (priv->medias, media);
435 priv->medias = g_list_delete_link (priv->medias, walk);
436 cookie = ++priv->medias_cookie;
437 g_object_unref (media);
439 case GST_RTSP_FILTER_REF:
440 result = g_list_prepend (result, g_object_ref (media));
442 case GST_RTSP_FILTER_KEEP:
449 g_mutex_unlock (&priv->lock);
452 g_hash_table_unref (visited);
458 * gst_rtsp_session_new:
459 * @sessionid: a session id
461 * Create a new #GstRTSPSession instance with @sessionid.
463 * Returns: (transfer full): a new #GstRTSPSession
466 gst_rtsp_session_new (const gchar * sessionid)
468 GstRTSPSession *result;
470 g_return_val_if_fail (sessionid != NULL, NULL);
472 result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
478 * gst_rtsp_session_get_sessionid:
479 * @session: a #GstRTSPSession
481 * Get the sessionid of @session.
483 * Returns: (transfer none): the sessionid of @session. The value remains valid
484 * as long as @session is alive.
487 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
489 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
491 return session->priv->sessionid;
495 * gst_rtsp_session_get_header:
496 * @session: a #GstRTSPSession
498 * Get the string that can be placed in the Session header field.
500 * Returns: (transfer full): the Session header of @session. g_free() after usage.
503 gst_rtsp_session_get_header (GstRTSPSession * session)
505 GstRTSPSessionPrivate *priv;
508 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
510 priv = session->priv;
513 g_mutex_lock (&priv->lock);
514 if (priv->timeout_always_visible || priv->timeout != 60)
515 result = g_strdup_printf ("%s;timeout=%d", priv->sessionid, priv->timeout);
517 result = g_strdup (priv->sessionid);
518 g_mutex_unlock (&priv->lock);
524 * gst_rtsp_session_set_timeout:
525 * @session: a #GstRTSPSession
526 * @timeout: the new timeout
528 * Configure @session for a timeout of @timeout seconds. The session will be
529 * cleaned up when there is no activity for @timeout seconds.
532 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
534 GstRTSPSessionPrivate *priv;
536 g_return_if_fail (GST_IS_RTSP_SESSION (session));
538 priv = session->priv;
540 g_mutex_lock (&priv->lock);
541 priv->timeout = timeout;
542 g_mutex_unlock (&priv->lock);
546 * gst_rtsp_session_get_timeout:
547 * @session: a #GstRTSPSession
549 * Get the timeout value of @session.
551 * Returns: the timeout of @session in seconds.
554 gst_rtsp_session_get_timeout (GstRTSPSession * session)
556 GstRTSPSessionPrivate *priv;
559 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
561 priv = session->priv;
563 g_mutex_lock (&priv->lock);
565 g_mutex_unlock (&priv->lock);
571 * gst_rtsp_session_touch:
572 * @session: a #GstRTSPSession
574 * Update the last_access time of the session to the current time.
577 gst_rtsp_session_touch (GstRTSPSession * session)
579 GstRTSPSessionPrivate *priv;
581 g_return_if_fail (GST_IS_RTSP_SESSION (session));
583 priv = session->priv;
585 g_mutex_lock (&priv->last_access_lock);
586 priv->last_access_monotonic_time = g_get_monotonic_time ();
587 priv->last_access_real_time = g_get_real_time ();
588 g_mutex_unlock (&priv->last_access_lock);
592 * gst_rtsp_session_prevent_expire:
593 * @session: a #GstRTSPSession
595 * Prevent @session from expiring.
598 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
600 g_return_if_fail (GST_IS_RTSP_SESSION (session));
602 g_atomic_int_add (&session->priv->expire_count, 1);
606 * gst_rtsp_session_allow_expire:
607 * @session: a #GstRTSPSession
609 * Allow @session to expire. This method must be called an equal
610 * amount of time as gst_rtsp_session_prevent_expire().
613 gst_rtsp_session_allow_expire (GstRTSPSession * session)
615 g_atomic_int_add (&session->priv->expire_count, -1);
619 * gst_rtsp_session_next_timeout_usec:
620 * @session: a #GstRTSPSession
621 * @now: the current monotonic time
623 * Get the amount of milliseconds till the session will expire.
625 * Returns: the amount of milliseconds since the session will time out.
628 gst_rtsp_session_next_timeout_usec (GstRTSPSession * session, gint64 now)
630 GstRTSPSessionPrivate *priv;
632 GstClockTime last_access, now_ns;
634 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
636 priv = session->priv;
638 g_mutex_lock (&priv->lock);
639 /* If timeout is set to 0, we never timeout */
640 if (priv->timeout == 0) {
641 g_mutex_unlock (&priv->lock);
644 g_mutex_unlock (&priv->lock);
646 g_mutex_lock (&priv->last_access_lock);
647 if (g_atomic_int_get (&priv->expire_count) != 0) {
648 /* touch session when the expire count is not 0 */
649 priv->last_access_monotonic_time = g_get_monotonic_time ();
650 priv->last_access_real_time = g_get_real_time ();
653 last_access = GST_USECOND * (priv->last_access_monotonic_time);
655 /* add timeout allow for 5 seconds of extra time */
656 last_access += priv->timeout * GST_SECOND + (5 * GST_SECOND);
657 g_mutex_unlock (&priv->last_access_lock);
659 now_ns = GST_USECOND * now;
661 if (last_access > now_ns) {
662 res = GST_TIME_AS_MSECONDS (last_access - now_ns);
670 /****** Deprecated API *******/
673 * gst_rtsp_session_next_timeout:
674 * @session: a #GstRTSPSession
675 * @now: (transfer none): the current system time
677 * Get the amount of milliseconds till the session will expire.
679 * Returns: the amount of milliseconds since the session will time out.
681 * Deprecated: Use gst_rtsp_session_next_timeout_usec() instead.
683 #ifndef GST_REMOVE_DEPRECATED
684 #ifdef GST_DISABLE_DEPRECATED
685 gint gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now);
689 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
691 GstRTSPSessionPrivate *priv;
693 GstClockTime last_access, now_ns;
695 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
696 g_return_val_if_fail (now != NULL, -1);
698 priv = session->priv;
700 g_mutex_lock (&priv->last_access_lock);
701 if (g_atomic_int_get (&priv->expire_count) != 0) {
702 /* touch session when the expire count is not 0 */
703 priv->last_access_monotonic_time = g_get_monotonic_time ();
704 priv->last_access_real_time = g_get_real_time ();
707 last_access = GST_USECOND * (priv->last_access_real_time);
709 /* add timeout allow for 5 seconds of extra time */
710 last_access += priv->timeout * GST_SECOND + (5 * GST_SECOND);
711 g_mutex_unlock (&priv->last_access_lock);
713 now_ns = GST_TIMEVAL_TO_TIME (*now);
715 if (last_access > now_ns) {
716 res = GST_TIME_AS_MSECONDS (last_access - now_ns);
726 * gst_rtsp_session_is_expired_usec:
727 * @session: a #GstRTSPSession
728 * @now: the current monotonic time
730 * Check if @session timeout out.
732 * Returns: %TRUE if @session timed out
735 gst_rtsp_session_is_expired_usec (GstRTSPSession * session, gint64 now)
739 res = (gst_rtsp_session_next_timeout_usec (session, now) == 0);
745 /****** Deprecated API *******/
748 * gst_rtsp_session_is_expired:
749 * @session: a #GstRTSPSession
750 * @now: (transfer none): the current system time
752 * Check if @session timeout out.
754 * Returns: %TRUE if @session timed out
756 * Deprecated: Use gst_rtsp_session_is_expired_usec() instead.
758 #ifndef GST_REMOVE_DEPRECATED
759 #ifdef GST_DISABLE_DEPRECATED
760 gboolean gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now);
763 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
767 res = (gst_rtsp_session_next_timeout (session, now) == 0);