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)
51 #include "rtsp-session.h"
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;
67 guint extra_time_timeout;
72 #define DEFAULT_TIMEOUT 60
74 #define DEFAULT_ALWAYS_VISIBLE FALSE
75 #define DEFAULT_EXTRA_TIMEOUT 5
82 PROP_TIMEOUT_ALWAYS_VISIBLE,
83 PROP_EXTRA_TIME_TIMEOUT,
87 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
88 #define GST_CAT_DEFAULT rtsp_session_debug
90 static void gst_rtsp_session_get_property (GObject * object, guint propid,
91 GValue * value, GParamSpec * pspec);
92 static void gst_rtsp_session_set_property (GObject * object, guint propid,
93 const GValue * value, GParamSpec * pspec);
94 static void gst_rtsp_session_finalize (GObject * obj);
96 G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
99 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
101 GObjectClass *gobject_class;
103 gobject_class = G_OBJECT_CLASS (klass);
105 gobject_class->get_property = gst_rtsp_session_get_property;
106 gobject_class->set_property = gst_rtsp_session_set_property;
107 gobject_class->finalize = gst_rtsp_session_finalize;
109 g_object_class_install_property (gobject_class, PROP_SESSIONID,
110 g_param_spec_string ("sessionid", "Sessionid", "the session id",
111 NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
112 G_PARAM_STATIC_STRINGS));
114 g_object_class_install_property (gobject_class, PROP_TIMEOUT,
115 g_param_spec_uint ("timeout", "timeout",
116 "the timeout of the session (0 = never)", 0, G_MAXUINT,
117 DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
119 g_object_class_install_property (gobject_class, PROP_TIMEOUT_ALWAYS_VISIBLE,
120 g_param_spec_boolean ("timeout-always-visible", "Timeout Always Visible ",
121 "timeout always visible in header",
122 DEFAULT_ALWAYS_VISIBLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
125 * GstRTSPSession::extra-timeout:
127 * Extra time to add to the timeout, in seconds. This only affects the
128 * time until a session is considered timed out and is not signalled
129 * in the RTSP request responses. Only the value of the timeout
130 * property is signalled in the request responses.
132 * Default value is 5 seconds.
133 * If the application is using a buffer that is configured to hold
134 * amount of data equal to the sessiontimeout, extra-timeout can be
135 * set to zero to prevent loss of data
139 g_object_class_install_property (gobject_class, PROP_EXTRA_TIME_TIMEOUT,
140 g_param_spec_uint ("extra-timeout",
141 "Add extra time to timeout ", "Add extra time to timeout", 0,
142 G_MAXUINT, DEFAULT_EXTRA_TIMEOUT,
143 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146 GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
151 gst_rtsp_session_init (GstRTSPSession * session)
153 GstRTSPSessionPrivate *priv;
155 session->priv = priv = gst_rtsp_session_get_instance_private (session);
157 GST_INFO ("init session %p", session);
159 g_mutex_init (&priv->lock);
160 g_mutex_init (&priv->last_access_lock);
161 priv->timeout = DEFAULT_TIMEOUT;
162 priv->extra_time_timeout = DEFAULT_EXTRA_TIMEOUT;
164 gst_rtsp_session_touch (session);
168 gst_rtsp_session_finalize (GObject * obj)
170 GstRTSPSession *session;
171 GstRTSPSessionPrivate *priv;
173 session = GST_RTSP_SESSION (obj);
174 priv = session->priv;
176 GST_INFO ("finalize session %p", session);
179 g_list_free_full (priv->medias, g_object_unref);
181 /* free session id */
182 g_free (priv->sessionid);
183 g_mutex_clear (&priv->last_access_lock);
184 g_mutex_clear (&priv->lock);
186 G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
190 gst_rtsp_session_get_property (GObject * object, guint propid,
191 GValue * value, GParamSpec * pspec)
193 GstRTSPSession *session = GST_RTSP_SESSION (object);
194 GstRTSPSessionPrivate *priv = session->priv;
198 g_value_set_string (value, priv->sessionid);
201 g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
203 case PROP_TIMEOUT_ALWAYS_VISIBLE:
204 g_value_set_boolean (value, priv->timeout_always_visible);
206 case PROP_EXTRA_TIME_TIMEOUT:
207 g_value_set_uint (value, priv->extra_time_timeout);
210 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
215 gst_rtsp_session_set_property (GObject * object, guint propid,
216 const GValue * value, GParamSpec * pspec)
218 GstRTSPSession *session = GST_RTSP_SESSION (object);
219 GstRTSPSessionPrivate *priv = session->priv;
223 g_free (priv->sessionid);
224 priv->sessionid = g_value_dup_string (value);
227 gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
229 case PROP_TIMEOUT_ALWAYS_VISIBLE:
230 g_mutex_lock (&priv->lock);
231 priv->timeout_always_visible = g_value_get_boolean (value);
232 g_mutex_unlock (&priv->lock);
234 case PROP_EXTRA_TIME_TIMEOUT:
235 g_mutex_lock (&priv->lock);
236 priv->extra_time_timeout = g_value_get_uint (value);
237 g_mutex_unlock (&priv->lock);
240 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
245 * gst_rtsp_session_manage_media:
246 * @sess: a #GstRTSPSession
247 * @path: the path for the media
248 * @media: (transfer full): a #GstRTSPMedia
250 * Manage the media object @obj in @sess. @path will be used to retrieve this
251 * media from the session with gst_rtsp_session_get_media().
253 * Ownership is taken from @media.
255 * Returns: (transfer none): a new @GstRTSPSessionMedia object.
257 GstRTSPSessionMedia *
258 gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
259 GstRTSPMedia * media)
261 GstRTSPSessionPrivate *priv;
262 GstRTSPSessionMedia *result;
263 GstRTSPMediaStatus status;
265 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
266 g_return_val_if_fail (path != NULL, NULL);
267 g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
268 status = gst_rtsp_media_get_status (media);
269 g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
270 GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
274 result = gst_rtsp_session_media_new (path, media);
276 g_mutex_lock (&priv->lock);
277 priv->medias = g_list_prepend (priv->medias, result);
278 priv->medias_cookie++;
279 g_mutex_unlock (&priv->lock);
281 GST_INFO ("manage new media %p in session %p", media, result);
287 gst_rtsp_session_unset_transport_keepalive (GstRTSPSessionMedia * sessmedia)
292 media = gst_rtsp_session_media_get_media (sessmedia);
293 n_streams = gst_rtsp_media_n_streams (media);
295 for (i = 0; i < n_streams; i++) {
296 GstRTSPStreamTransport *transport =
297 gst_rtsp_session_media_get_transport (sessmedia, i);
302 gst_rtsp_stream_transport_set_keepalive (transport, NULL, NULL, NULL);
307 * gst_rtsp_session_release_media:
308 * @sess: a #GstRTSPSession
309 * @media: (transfer none): a #GstRTSPMedia
311 * Release the managed @media in @sess, freeing the memory allocated by it.
313 * Returns: %TRUE if there are more media session left in @sess.
316 gst_rtsp_session_release_media (GstRTSPSession * sess,
317 GstRTSPSessionMedia * media)
319 GstRTSPSessionPrivate *priv;
323 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
324 g_return_val_if_fail (media != NULL, FALSE);
328 g_mutex_lock (&priv->lock);
329 find = g_list_find (priv->medias, media);
331 priv->medias = g_list_delete_link (priv->medias, find);
332 priv->medias_cookie++;
334 more = (priv->medias != NULL);
335 g_mutex_unlock (&priv->lock);
338 gst_rtsp_session_unset_transport_keepalive (media);
341 g_object_unref (media);
346 static GstRTSPSessionMedia *
347 _gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
348 gint * matched, gboolean dup)
350 GstRTSPSessionPrivate *priv;
351 GstRTSPSessionMedia *result;
355 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
356 g_return_val_if_fail (path != NULL, NULL);
362 g_mutex_lock (&priv->lock);
363 for (walk = priv->medias; walk; walk = g_list_next (walk)) {
364 GstRTSPSessionMedia *test;
366 test = (GstRTSPSessionMedia *) walk->data;
368 /* find largest match */
369 if (gst_rtsp_session_media_matches (test, path, matched)) {
370 if (best < *matched) {
378 result = g_object_ref (result);
379 g_mutex_unlock (&priv->lock);
387 * gst_rtsp_session_get_media:
388 * @sess: a #GstRTSPSession
389 * @path: the path for the media
390 * @matched: (out): the amount of matched characters
392 * Gets the session media for @path. @matched will contain the number of matched
393 * characters of @path.
395 * Returns: (transfer none) (nullable): the configuration for @path in @sess.
397 GstRTSPSessionMedia *
398 gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
401 return _gst_rtsp_session_get_media (sess, path, matched, FALSE);
405 * gst_rtsp_session_dup_media:
406 * @sess: a #GstRTSPSession
407 * @path: the path for the media
408 * @matched: (out): the amount of matched characters
410 * Gets the session media for @path, increasing its reference count. @matched
411 * will contain the number of matched characters of @path.
413 * Returns: (transfer full) (nullable): the configuration for @path in @sess,
414 * should be unreferenced when no longer needed.
418 GstRTSPSessionMedia *
419 gst_rtsp_session_dup_media (GstRTSPSession * sess, const gchar * path,
422 return _gst_rtsp_session_get_media (sess, path, matched, TRUE);
426 * gst_rtsp_session_filter:
427 * @sess: a #GstRTSPSession
428 * @func: (scope call) (allow-none): a callback
429 * @user_data: (closure): user data passed to @func
431 * Call @func for each media in @sess. The result value of @func determines
432 * what happens to the media. @func will be called with @sess
433 * locked so no further actions on @sess can be performed from @func.
435 * If @func returns #GST_RTSP_FILTER_REMOVE, the media will be removed from
438 * If @func returns #GST_RTSP_FILTER_KEEP, the media will remain in @sess.
440 * If @func returns #GST_RTSP_FILTER_REF, the media will remain in @sess but
441 * will also be added with an additional ref to the result #GList of this
444 * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all media.
446 * Returns: (element-type GstRTSPSessionMedia) (transfer full): a GList with all
447 * media for which @func returned #GST_RTSP_FILTER_REF. After usage, each
448 * element in the #GList should be unreffed before the list is freed.
451 gst_rtsp_session_filter (GstRTSPSession * sess,
452 GstRTSPSessionFilterFunc func, gpointer user_data)
454 GstRTSPSessionPrivate *priv;
455 GList *result, *walk, *next;
459 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
465 visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
467 g_mutex_lock (&priv->lock);
469 cookie = priv->medias_cookie;
470 for (walk = priv->medias; walk; walk = next) {
471 GstRTSPSessionMedia *media = walk->data;
472 GstRTSPFilterResult res;
475 next = g_list_next (walk);
478 /* only visit each media once */
479 if (g_hash_table_contains (visited, media))
482 g_hash_table_add (visited, g_object_ref (media));
483 g_mutex_unlock (&priv->lock);
485 res = func (sess, media, user_data);
487 g_mutex_lock (&priv->lock);
489 res = GST_RTSP_FILTER_REF;
492 changed = (cookie != priv->medias_cookie);
495 case GST_RTSP_FILTER_REMOVE:
501 for (l = priv->medias; l; l = l->next) {
502 if (l->data == media) {
509 /* The media might have been removed from the list while the mutex was
510 * unlocked above. In that case there's nothing else to do here as the
511 * only reference to the media owned by this function is in the
512 * visited hash table and that is released in the end
515 priv->medias = g_list_delete_link (priv->medias, walk);
516 g_object_unref (media);
519 cookie = ++priv->medias_cookie;
521 case GST_RTSP_FILTER_REF:
522 result = g_list_prepend (result, g_object_ref (media));
524 case GST_RTSP_FILTER_KEEP:
531 g_mutex_unlock (&priv->lock);
534 g_hash_table_unref (visited);
540 * gst_rtsp_session_new:
541 * @sessionid: a session id
543 * Create a new #GstRTSPSession instance with @sessionid.
545 * Returns: (transfer full): a new #GstRTSPSession
548 gst_rtsp_session_new (const gchar * sessionid)
550 GstRTSPSession *result;
552 g_return_val_if_fail (sessionid != NULL, NULL);
554 result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
560 * gst_rtsp_session_get_sessionid:
561 * @session: a #GstRTSPSession
563 * Get the sessionid of @session.
565 * Returns: (transfer none) (nullable): the sessionid of @session.
566 * The value remains valid as long as @session is alive.
569 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
571 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
573 return session->priv->sessionid;
577 * gst_rtsp_session_get_header:
578 * @session: a #GstRTSPSession
580 * Get the string that can be placed in the Session header field.
582 * Returns: (transfer full) (nullable): the Session header of @session.
583 * g_free() after usage.
586 gst_rtsp_session_get_header (GstRTSPSession * session)
588 GstRTSPSessionPrivate *priv;
591 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
593 priv = session->priv;
596 g_mutex_lock (&priv->lock);
597 if (priv->timeout_always_visible || priv->timeout != 60)
598 result = g_strdup_printf ("%s;timeout=%d", priv->sessionid, priv->timeout);
600 result = g_strdup (priv->sessionid);
601 g_mutex_unlock (&priv->lock);
607 * gst_rtsp_session_set_timeout:
608 * @session: a #GstRTSPSession
609 * @timeout: the new timeout
611 * Configure @session for a timeout of @timeout seconds. The session will be
612 * cleaned up when there is no activity for @timeout seconds.
615 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
617 GstRTSPSessionPrivate *priv;
619 g_return_if_fail (GST_IS_RTSP_SESSION (session));
621 priv = session->priv;
623 g_mutex_lock (&priv->lock);
624 priv->timeout = timeout;
625 g_mutex_unlock (&priv->lock);
629 * gst_rtsp_session_get_timeout:
630 * @session: a #GstRTSPSession
632 * Get the timeout value of @session.
634 * Returns: the timeout of @session in seconds.
637 gst_rtsp_session_get_timeout (GstRTSPSession * session)
639 GstRTSPSessionPrivate *priv;
642 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
644 priv = session->priv;
646 g_mutex_lock (&priv->lock);
648 g_mutex_unlock (&priv->lock);
654 * gst_rtsp_session_touch:
655 * @session: a #GstRTSPSession
657 * Update the last_access time of the session to the current time.
660 gst_rtsp_session_touch (GstRTSPSession * session)
662 GstRTSPSessionPrivate *priv;
664 g_return_if_fail (GST_IS_RTSP_SESSION (session));
666 priv = session->priv;
668 g_mutex_lock (&priv->last_access_lock);
669 priv->last_access_monotonic_time = g_get_monotonic_time ();
670 priv->last_access_real_time = g_get_real_time ();
671 g_mutex_unlock (&priv->last_access_lock);
675 * gst_rtsp_session_prevent_expire:
676 * @session: a #GstRTSPSession
678 * Prevent @session from expiring.
681 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
683 g_return_if_fail (GST_IS_RTSP_SESSION (session));
685 g_atomic_int_add (&session->priv->expire_count, 1);
689 * gst_rtsp_session_allow_expire:
690 * @session: a #GstRTSPSession
692 * Allow @session to expire. This method must be called an equal
693 * amount of time as gst_rtsp_session_prevent_expire().
696 gst_rtsp_session_allow_expire (GstRTSPSession * session)
698 g_atomic_int_add (&session->priv->expire_count, -1);
702 * gst_rtsp_session_next_timeout_usec:
703 * @session: a #GstRTSPSession
704 * @now: the current monotonic time
706 * Get the amount of milliseconds till the session will expire.
708 * Returns: the amount of milliseconds since the session will time out.
711 gst_rtsp_session_next_timeout_usec (GstRTSPSession * session, gint64 now)
713 GstRTSPSessionPrivate *priv;
715 GstClockTime last_access, now_ns;
717 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
719 priv = session->priv;
721 g_mutex_lock (&priv->lock);
722 /* If timeout is set to 0, we never timeout */
723 if (priv->timeout == 0) {
724 g_mutex_unlock (&priv->lock);
727 g_mutex_unlock (&priv->lock);
729 g_mutex_lock (&priv->last_access_lock);
730 if (g_atomic_int_get (&priv->expire_count) != 0) {
731 /* touch session when the expire count is not 0 */
732 priv->last_access_monotonic_time = g_get_monotonic_time ();
733 priv->last_access_real_time = g_get_real_time ();
736 last_access = GST_USECOND * (priv->last_access_monotonic_time);
738 /* add timeout allow for priv->extra_time_timeout
739 * seconds of extra time */
740 last_access += priv->timeout * GST_SECOND +
741 (priv->extra_time_timeout * GST_SECOND);
743 g_mutex_unlock (&priv->last_access_lock);
745 now_ns = GST_USECOND * now;
747 if (last_access > now_ns) {
748 res = GST_TIME_AS_MSECONDS (last_access - now_ns);
756 /****** Deprecated API *******/
759 * gst_rtsp_session_next_timeout:
760 * @session: a #GstRTSPSession
761 * @now: (transfer none): the current system time
763 * Get the amount of milliseconds till the session will expire.
765 * Returns: the amount of milliseconds since the session will time out.
767 * Deprecated: Use gst_rtsp_session_next_timeout_usec() instead.
769 #ifndef GST_REMOVE_DEPRECATED
770 G_GNUC_BEGIN_IGNORE_DEPRECATIONS gint
771 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
773 GstRTSPSessionPrivate *priv;
775 GstClockTime last_access, now_ns;
777 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
778 g_return_val_if_fail (now != NULL, -1);
780 priv = session->priv;
782 g_mutex_lock (&priv->last_access_lock);
783 if (g_atomic_int_get (&priv->expire_count) != 0) {
784 /* touch session when the expire count is not 0 */
785 priv->last_access_monotonic_time = g_get_monotonic_time ();
786 priv->last_access_real_time = g_get_real_time ();
789 last_access = GST_USECOND * (priv->last_access_real_time);
791 /* add timeout allow for priv->extra_time_timeout
792 * seconds of extra time */
793 last_access += priv->timeout * GST_SECOND +
794 (priv->extra_time_timeout * GST_SECOND);
796 g_mutex_unlock (&priv->last_access_lock);
798 now_ns = GST_TIMEVAL_TO_TIME (*now);
800 if (last_access > now_ns) {
801 res = GST_TIME_AS_MSECONDS (last_access - now_ns);
809 G_GNUC_END_IGNORE_DEPRECATIONS
812 * gst_rtsp_session_is_expired_usec:
813 * @session: a #GstRTSPSession
814 * @now: the current monotonic time
816 * Check if @session timeout out.
818 * Returns: %TRUE if @session timed out
821 gst_rtsp_session_is_expired_usec (GstRTSPSession * session, gint64 now)
825 res = (gst_rtsp_session_next_timeout_usec (session, now) == 0);
831 /****** Deprecated API *******/
834 * gst_rtsp_session_is_expired:
835 * @session: a #GstRTSPSession
836 * @now: (transfer none): the current system time
838 * Check if @session timeout out.
840 * Returns: %TRUE if @session timed out
842 * Deprecated: Use gst_rtsp_session_is_expired_usec() instead.
844 #ifndef GST_REMOVE_DEPRECATED
845 G_GNUC_BEGIN_IGNORE_DEPRECATIONS gboolean
846 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
850 res = gst_rtsp_session_next_timeout_usec (session,
851 (now->tv_sec * G_USEC_PER_SEC) + (now->tv_usec));
856 G_GNUC_END_IGNORE_DEPRECATIONS