mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +00:00
7ba665995f
handle_*_request() functions were all retrieving the session media from the session by calling gst_rtsp_session_get_media () which is a transfer-none call. If a session timeout happens at that time, the session media may get freed making the pointer invalid.. Fixes #757 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1053>
857 lines
23 KiB
C
857 lines
23 KiB
C
/* GStreamer
|
|
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
/**
|
|
* SECTION:rtsp-session
|
|
* @short_description: An object to manage media
|
|
* @see_also: #GstRTSPSessionPool, #GstRTSPSessionMedia, #GstRTSPMedia
|
|
*
|
|
* The #GstRTSPSession is identified by an id, unique in the
|
|
* #GstRTSPSessionPool that created the session and manages media and its
|
|
* configuration.
|
|
*
|
|
* A #GstRTSPSession has a timeout that can be retrieved with
|
|
* gst_rtsp_session_get_timeout(). You can check if the sessions is expired with
|
|
* gst_rtsp_session_is_expired(). gst_rtsp_session_touch() will reset the
|
|
* expiration counter of the session.
|
|
*
|
|
* When a client configures a media with SETUP, a session will be created to
|
|
* keep track of the configuration of that media. With
|
|
* gst_rtsp_session_manage_media(), the media is added to the managed media
|
|
* in the session. With gst_rtsp_session_release_media() the media can be
|
|
* released again from the session. Managed media is identified in the sessions
|
|
* with a url. Use gst_rtsp_session_get_media() to get the media that matches
|
|
* (part of) the given url.
|
|
*
|
|
* The media in a session can be iterated with gst_rtsp_session_filter().
|
|
*
|
|
* Last reviewed on 2013-07-11 (1.0.0)
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "rtsp-session.h"
|
|
|
|
struct _GstRTSPSessionPrivate
|
|
{
|
|
GMutex lock; /* protects everything but sessionid and create_time */
|
|
gchar *sessionid;
|
|
|
|
guint timeout;
|
|
gboolean timeout_always_visible;
|
|
GMutex last_access_lock;
|
|
gint64 last_access_monotonic_time;
|
|
gint64 last_access_real_time;
|
|
gint expire_count;
|
|
|
|
GList *medias;
|
|
guint medias_cookie;
|
|
guint extra_time_timeout;
|
|
};
|
|
|
|
#undef DEBUG
|
|
|
|
#define DEFAULT_TIMEOUT 60
|
|
#define NO_TIMEOUT -1
|
|
#define DEFAULT_ALWAYS_VISIBLE FALSE
|
|
#define DEFAULT_EXTRA_TIMEOUT 5
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_SESSIONID,
|
|
PROP_TIMEOUT,
|
|
PROP_TIMEOUT_ALWAYS_VISIBLE,
|
|
PROP_EXTRA_TIME_TIMEOUT,
|
|
PROP_LAST
|
|
};
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
|
|
#define GST_CAT_DEFAULT rtsp_session_debug
|
|
|
|
static void gst_rtsp_session_get_property (GObject * object, guint propid,
|
|
GValue * value, GParamSpec * pspec);
|
|
static void gst_rtsp_session_set_property (GObject * object, guint propid,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_rtsp_session_finalize (GObject * obj);
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->get_property = gst_rtsp_session_get_property;
|
|
gobject_class->set_property = gst_rtsp_session_set_property;
|
|
gobject_class->finalize = gst_rtsp_session_finalize;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_SESSIONID,
|
|
g_param_spec_string ("sessionid", "Sessionid", "the session id",
|
|
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_TIMEOUT,
|
|
g_param_spec_uint ("timeout", "timeout",
|
|
"the timeout of the session (0 = never)", 0, G_MAXUINT,
|
|
DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_TIMEOUT_ALWAYS_VISIBLE,
|
|
g_param_spec_boolean ("timeout-always-visible", "Timeout Always Visible ",
|
|
"timeout always visible in header",
|
|
DEFAULT_ALWAYS_VISIBLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GstRTSPSession::extra-timeout:
|
|
*
|
|
* Extra time to add to the timeout, in seconds. This only affects the
|
|
* time until a session is considered timed out and is not signalled
|
|
* in the RTSP request responses. Only the value of the timeout
|
|
* property is signalled in the request responses.
|
|
*
|
|
* Default value is 5 seconds.
|
|
* If the application is using a buffer that is configured to hold
|
|
* amount of data equal to the sessiontimeout, extra-timeout can be
|
|
* set to zero to prevent loss of data
|
|
*
|
|
* Since: 1.18
|
|
*/
|
|
g_object_class_install_property (gobject_class, PROP_EXTRA_TIME_TIMEOUT,
|
|
g_param_spec_uint ("extra-timeout",
|
|
"Add extra time to timeout ", "Add extra time to timeout", 0,
|
|
G_MAXUINT, DEFAULT_EXTRA_TIMEOUT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
|
|
"GstRTSPSession");
|
|
}
|
|
|
|
static void
|
|
gst_rtsp_session_init (GstRTSPSession * session)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
|
|
session->priv = priv = gst_rtsp_session_get_instance_private (session);
|
|
|
|
GST_INFO ("init session %p", session);
|
|
|
|
g_mutex_init (&priv->lock);
|
|
g_mutex_init (&priv->last_access_lock);
|
|
priv->timeout = DEFAULT_TIMEOUT;
|
|
priv->extra_time_timeout = DEFAULT_EXTRA_TIMEOUT;
|
|
|
|
gst_rtsp_session_touch (session);
|
|
}
|
|
|
|
static void
|
|
gst_rtsp_session_finalize (GObject * obj)
|
|
{
|
|
GstRTSPSession *session;
|
|
GstRTSPSessionPrivate *priv;
|
|
|
|
session = GST_RTSP_SESSION (obj);
|
|
priv = session->priv;
|
|
|
|
GST_INFO ("finalize session %p", session);
|
|
|
|
/* free all media */
|
|
g_list_free_full (priv->medias, g_object_unref);
|
|
|
|
/* free session id */
|
|
g_free (priv->sessionid);
|
|
g_mutex_clear (&priv->last_access_lock);
|
|
g_mutex_clear (&priv->lock);
|
|
|
|
G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
|
|
}
|
|
|
|
static void
|
|
gst_rtsp_session_get_property (GObject * object, guint propid,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstRTSPSession *session = GST_RTSP_SESSION (object);
|
|
GstRTSPSessionPrivate *priv = session->priv;
|
|
|
|
switch (propid) {
|
|
case PROP_SESSIONID:
|
|
g_value_set_string (value, priv->sessionid);
|
|
break;
|
|
case PROP_TIMEOUT:
|
|
g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
|
|
break;
|
|
case PROP_TIMEOUT_ALWAYS_VISIBLE:
|
|
g_value_set_boolean (value, priv->timeout_always_visible);
|
|
break;
|
|
case PROP_EXTRA_TIME_TIMEOUT:
|
|
g_value_set_uint (value, priv->extra_time_timeout);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_rtsp_session_set_property (GObject * object, guint propid,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstRTSPSession *session = GST_RTSP_SESSION (object);
|
|
GstRTSPSessionPrivate *priv = session->priv;
|
|
|
|
switch (propid) {
|
|
case PROP_SESSIONID:
|
|
g_free (priv->sessionid);
|
|
priv->sessionid = g_value_dup_string (value);
|
|
break;
|
|
case PROP_TIMEOUT:
|
|
gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
|
|
break;
|
|
case PROP_TIMEOUT_ALWAYS_VISIBLE:
|
|
g_mutex_lock (&priv->lock);
|
|
priv->timeout_always_visible = g_value_get_boolean (value);
|
|
g_mutex_unlock (&priv->lock);
|
|
break;
|
|
case PROP_EXTRA_TIME_TIMEOUT:
|
|
g_mutex_lock (&priv->lock);
|
|
priv->extra_time_timeout = g_value_get_uint (value);
|
|
g_mutex_unlock (&priv->lock);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_manage_media:
|
|
* @sess: a #GstRTSPSession
|
|
* @path: the path for the media
|
|
* @media: (transfer full): a #GstRTSPMedia
|
|
*
|
|
* Manage the media object @obj in @sess. @path will be used to retrieve this
|
|
* media from the session with gst_rtsp_session_get_media().
|
|
*
|
|
* Ownership is taken from @media.
|
|
*
|
|
* Returns: (transfer none): a new @GstRTSPSessionMedia object.
|
|
*/
|
|
GstRTSPSessionMedia *
|
|
gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
|
|
GstRTSPMedia * media)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
GstRTSPSessionMedia *result;
|
|
GstRTSPMediaStatus status;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
|
|
status = gst_rtsp_media_get_status (media);
|
|
g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
|
|
GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
|
|
|
|
priv = sess->priv;
|
|
|
|
result = gst_rtsp_session_media_new (path, media);
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
priv->medias = g_list_prepend (priv->medias, result);
|
|
priv->medias_cookie++;
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
GST_INFO ("manage new media %p in session %p", media, result);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
gst_rtsp_session_unset_transport_keepalive (GstRTSPSessionMedia * sessmedia)
|
|
{
|
|
GstRTSPMedia *media;
|
|
guint i, n_streams;
|
|
|
|
media = gst_rtsp_session_media_get_media (sessmedia);
|
|
n_streams = gst_rtsp_media_n_streams (media);
|
|
|
|
for (i = 0; i < n_streams; i++) {
|
|
GstRTSPStreamTransport *transport =
|
|
gst_rtsp_session_media_get_transport (sessmedia, i);
|
|
|
|
if (!transport)
|
|
continue;
|
|
|
|
gst_rtsp_stream_transport_set_keepalive (transport, NULL, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_release_media:
|
|
* @sess: a #GstRTSPSession
|
|
* @media: (transfer none): a #GstRTSPMedia
|
|
*
|
|
* Release the managed @media in @sess, freeing the memory allocated by it.
|
|
*
|
|
* Returns: %TRUE if there are more media session left in @sess.
|
|
*/
|
|
gboolean
|
|
gst_rtsp_session_release_media (GstRTSPSession * sess,
|
|
GstRTSPSessionMedia * media)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
GList *find;
|
|
gboolean more;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
|
|
g_return_val_if_fail (media != NULL, FALSE);
|
|
|
|
priv = sess->priv;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
find = g_list_find (priv->medias, media);
|
|
if (find) {
|
|
priv->medias = g_list_delete_link (priv->medias, find);
|
|
priv->medias_cookie++;
|
|
}
|
|
more = (priv->medias != NULL);
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
if (find && !more)
|
|
gst_rtsp_session_unset_transport_keepalive (media);
|
|
|
|
if (find)
|
|
g_object_unref (media);
|
|
|
|
return more;
|
|
}
|
|
|
|
static GstRTSPSessionMedia *
|
|
_gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
|
|
gint * matched, gboolean dup)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
GstRTSPSessionMedia *result;
|
|
GList *walk;
|
|
gint best;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
|
|
priv = sess->priv;
|
|
result = NULL;
|
|
best = 0;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
for (walk = priv->medias; walk; walk = g_list_next (walk)) {
|
|
GstRTSPSessionMedia *test;
|
|
|
|
test = (GstRTSPSessionMedia *) walk->data;
|
|
|
|
/* find largest match */
|
|
if (gst_rtsp_session_media_matches (test, path, matched)) {
|
|
if (best < *matched) {
|
|
result = test;
|
|
best = *matched;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result && dup)
|
|
result = g_object_ref (result);
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
*matched = best;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_get_media:
|
|
* @sess: a #GstRTSPSession
|
|
* @path: the path for the media
|
|
* @matched: (out): the amount of matched characters
|
|
*
|
|
* Gets the session media for @path. @matched will contain the number of matched
|
|
* characters of @path.
|
|
*
|
|
* Returns: (transfer none) (nullable): the configuration for @path in @sess.
|
|
*/
|
|
GstRTSPSessionMedia *
|
|
gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
|
|
gint * matched)
|
|
{
|
|
return _gst_rtsp_session_get_media (sess, path, matched, FALSE);
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_dup_media:
|
|
* @sess: a #GstRTSPSession
|
|
* @path: the path for the media
|
|
* @matched: (out): the amount of matched characters
|
|
*
|
|
* Gets the session media for @path, increasing its reference count. @matched
|
|
* will contain the number of matched characters of @path.
|
|
*
|
|
* Returns: (transfer full) (nullable): the configuration for @path in @sess,
|
|
* should be unreferenced when no longer needed.
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
GstRTSPSessionMedia *
|
|
gst_rtsp_session_dup_media (GstRTSPSession * sess, const gchar * path,
|
|
gint * matched)
|
|
{
|
|
return _gst_rtsp_session_get_media (sess, path, matched, TRUE);
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_filter:
|
|
* @sess: a #GstRTSPSession
|
|
* @func: (scope call) (allow-none): a callback
|
|
* @user_data: (closure): user data passed to @func
|
|
*
|
|
* Call @func for each media in @sess. The result value of @func determines
|
|
* what happens to the media. @func will be called with @sess
|
|
* locked so no further actions on @sess can be performed from @func.
|
|
*
|
|
* If @func returns #GST_RTSP_FILTER_REMOVE, the media will be removed from
|
|
* @sess.
|
|
*
|
|
* If @func returns #GST_RTSP_FILTER_KEEP, the media will remain in @sess.
|
|
*
|
|
* If @func returns #GST_RTSP_FILTER_REF, the media will remain in @sess but
|
|
* will also be added with an additional ref to the result #GList of this
|
|
* function..
|
|
*
|
|
* When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all media.
|
|
*
|
|
* Returns: (element-type GstRTSPSessionMedia) (transfer full): a GList with all
|
|
* media for which @func returned #GST_RTSP_FILTER_REF. After usage, each
|
|
* element in the #GList should be unreffed before the list is freed.
|
|
*/
|
|
GList *
|
|
gst_rtsp_session_filter (GstRTSPSession * sess,
|
|
GstRTSPSessionFilterFunc func, gpointer user_data)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
GList *result, *walk, *next;
|
|
GHashTable *visited;
|
|
guint cookie;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
|
|
|
|
priv = sess->priv;
|
|
|
|
result = NULL;
|
|
if (func)
|
|
visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
restart:
|
|
cookie = priv->medias_cookie;
|
|
for (walk = priv->medias; walk; walk = next) {
|
|
GstRTSPSessionMedia *media = walk->data;
|
|
GstRTSPFilterResult res;
|
|
gboolean changed;
|
|
|
|
next = g_list_next (walk);
|
|
|
|
if (func) {
|
|
/* only visit each media once */
|
|
if (g_hash_table_contains (visited, media))
|
|
continue;
|
|
|
|
g_hash_table_add (visited, g_object_ref (media));
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
res = func (sess, media, user_data);
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
} else {
|
|
res = GST_RTSP_FILTER_REF;
|
|
}
|
|
|
|
changed = (cookie != priv->medias_cookie);
|
|
|
|
switch (res) {
|
|
case GST_RTSP_FILTER_REMOVE:
|
|
if (changed) {
|
|
GList *l;
|
|
|
|
walk = NULL;
|
|
|
|
for (l = priv->medias; l; l = l->next) {
|
|
if (l->data == media) {
|
|
walk = l;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The media might have been removed from the list while the mutex was
|
|
* unlocked above. In that case there's nothing else to do here as the
|
|
* only reference to the media owned by this function is in the
|
|
* visited hash table and that is released in the end
|
|
*/
|
|
if (walk) {
|
|
priv->medias = g_list_delete_link (priv->medias, walk);
|
|
g_object_unref (media);
|
|
}
|
|
|
|
cookie = ++priv->medias_cookie;
|
|
break;
|
|
case GST_RTSP_FILTER_REF:
|
|
result = g_list_prepend (result, g_object_ref (media));
|
|
break;
|
|
case GST_RTSP_FILTER_KEEP:
|
|
default:
|
|
break;
|
|
}
|
|
if (changed)
|
|
goto restart;
|
|
}
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
if (func)
|
|
g_hash_table_unref (visited);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_new:
|
|
* @sessionid: a session id
|
|
*
|
|
* Create a new #GstRTSPSession instance with @sessionid.
|
|
*
|
|
* Returns: (transfer full): a new #GstRTSPSession
|
|
*/
|
|
GstRTSPSession *
|
|
gst_rtsp_session_new (const gchar * sessionid)
|
|
{
|
|
GstRTSPSession *result;
|
|
|
|
g_return_val_if_fail (sessionid != NULL, NULL);
|
|
|
|
result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_get_sessionid:
|
|
* @session: a #GstRTSPSession
|
|
*
|
|
* Get the sessionid of @session.
|
|
*
|
|
* Returns: (transfer none) (nullable): the sessionid of @session.
|
|
* The value remains valid as long as @session is alive.
|
|
*/
|
|
const gchar *
|
|
gst_rtsp_session_get_sessionid (GstRTSPSession * session)
|
|
{
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
|
|
|
|
return session->priv->sessionid;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_get_header:
|
|
* @session: a #GstRTSPSession
|
|
*
|
|
* Get the string that can be placed in the Session header field.
|
|
*
|
|
* Returns: (transfer full) (nullable): the Session header of @session.
|
|
* g_free() after usage.
|
|
*/
|
|
gchar *
|
|
gst_rtsp_session_get_header (GstRTSPSession * session)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
gchar *result;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
|
|
|
|
priv = session->priv;
|
|
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
if (priv->timeout_always_visible || priv->timeout != 60)
|
|
result = g_strdup_printf ("%s;timeout=%d", priv->sessionid, priv->timeout);
|
|
else
|
|
result = g_strdup (priv->sessionid);
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_set_timeout:
|
|
* @session: a #GstRTSPSession
|
|
* @timeout: the new timeout
|
|
*
|
|
* Configure @session for a timeout of @timeout seconds. The session will be
|
|
* cleaned up when there is no activity for @timeout seconds.
|
|
*/
|
|
void
|
|
gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
|
|
g_return_if_fail (GST_IS_RTSP_SESSION (session));
|
|
|
|
priv = session->priv;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
priv->timeout = timeout;
|
|
g_mutex_unlock (&priv->lock);
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_get_timeout:
|
|
* @session: a #GstRTSPSession
|
|
*
|
|
* Get the timeout value of @session.
|
|
*
|
|
* Returns: the timeout of @session in seconds.
|
|
*/
|
|
guint
|
|
gst_rtsp_session_get_timeout (GstRTSPSession * session)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
guint res;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
|
|
|
|
priv = session->priv;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
res = priv->timeout;
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_touch:
|
|
* @session: a #GstRTSPSession
|
|
*
|
|
* Update the last_access time of the session to the current time.
|
|
*/
|
|
void
|
|
gst_rtsp_session_touch (GstRTSPSession * session)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
|
|
g_return_if_fail (GST_IS_RTSP_SESSION (session));
|
|
|
|
priv = session->priv;
|
|
|
|
g_mutex_lock (&priv->last_access_lock);
|
|
priv->last_access_monotonic_time = g_get_monotonic_time ();
|
|
priv->last_access_real_time = g_get_real_time ();
|
|
g_mutex_unlock (&priv->last_access_lock);
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_prevent_expire:
|
|
* @session: a #GstRTSPSession
|
|
*
|
|
* Prevent @session from expiring.
|
|
*/
|
|
void
|
|
gst_rtsp_session_prevent_expire (GstRTSPSession * session)
|
|
{
|
|
g_return_if_fail (GST_IS_RTSP_SESSION (session));
|
|
|
|
g_atomic_int_add (&session->priv->expire_count, 1);
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_allow_expire:
|
|
* @session: a #GstRTSPSession
|
|
*
|
|
* Allow @session to expire. This method must be called an equal
|
|
* amount of time as gst_rtsp_session_prevent_expire().
|
|
*/
|
|
void
|
|
gst_rtsp_session_allow_expire (GstRTSPSession * session)
|
|
{
|
|
g_atomic_int_add (&session->priv->expire_count, -1);
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_session_next_timeout_usec:
|
|
* @session: a #GstRTSPSession
|
|
* @now: the current monotonic time
|
|
*
|
|
* Get the amount of milliseconds till the session will expire.
|
|
*
|
|
* Returns: the amount of milliseconds since the session will time out.
|
|
*/
|
|
gint
|
|
gst_rtsp_session_next_timeout_usec (GstRTSPSession * session, gint64 now)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
gint res;
|
|
GstClockTime last_access, now_ns;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
|
|
|
|
priv = session->priv;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
/* If timeout is set to 0, we never timeout */
|
|
if (priv->timeout == 0) {
|
|
g_mutex_unlock (&priv->lock);
|
|
return NO_TIMEOUT;
|
|
}
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
g_mutex_lock (&priv->last_access_lock);
|
|
if (g_atomic_int_get (&priv->expire_count) != 0) {
|
|
/* touch session when the expire count is not 0 */
|
|
priv->last_access_monotonic_time = g_get_monotonic_time ();
|
|
priv->last_access_real_time = g_get_real_time ();
|
|
}
|
|
|
|
last_access = GST_USECOND * (priv->last_access_monotonic_time);
|
|
|
|
/* add timeout allow for priv->extra_time_timeout
|
|
* seconds of extra time */
|
|
last_access += priv->timeout * GST_SECOND +
|
|
(priv->extra_time_timeout * GST_SECOND);
|
|
|
|
g_mutex_unlock (&priv->last_access_lock);
|
|
|
|
now_ns = GST_USECOND * now;
|
|
|
|
if (last_access > now_ns) {
|
|
res = GST_TIME_AS_MSECONDS (last_access - now_ns);
|
|
} else {
|
|
res = 0;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/****** Deprecated API *******/
|
|
|
|
/**
|
|
* gst_rtsp_session_next_timeout:
|
|
* @session: a #GstRTSPSession
|
|
* @now: (transfer none): the current system time
|
|
*
|
|
* Get the amount of milliseconds till the session will expire.
|
|
*
|
|
* Returns: the amount of milliseconds since the session will time out.
|
|
*
|
|
* Deprecated: Use gst_rtsp_session_next_timeout_usec() instead.
|
|
*/
|
|
#ifndef GST_REMOVE_DEPRECATED
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS gint
|
|
gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
|
|
{
|
|
GstRTSPSessionPrivate *priv;
|
|
gint res;
|
|
GstClockTime last_access, now_ns;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
|
|
g_return_val_if_fail (now != NULL, -1);
|
|
|
|
priv = session->priv;
|
|
|
|
g_mutex_lock (&priv->last_access_lock);
|
|
if (g_atomic_int_get (&priv->expire_count) != 0) {
|
|
/* touch session when the expire count is not 0 */
|
|
priv->last_access_monotonic_time = g_get_monotonic_time ();
|
|
priv->last_access_real_time = g_get_real_time ();
|
|
}
|
|
|
|
last_access = GST_USECOND * (priv->last_access_real_time);
|
|
|
|
/* add timeout allow for priv->extra_time_timeout
|
|
* seconds of extra time */
|
|
last_access += priv->timeout * GST_SECOND +
|
|
(priv->extra_time_timeout * GST_SECOND);
|
|
|
|
g_mutex_unlock (&priv->last_access_lock);
|
|
|
|
now_ns = GST_TIMEVAL_TO_TIME (*now);
|
|
|
|
if (last_access > now_ns) {
|
|
res = GST_TIME_AS_MSECONDS (last_access - now_ns);
|
|
} else {
|
|
res = 0;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
|
#endif
|
|
/**
|
|
* gst_rtsp_session_is_expired_usec:
|
|
* @session: a #GstRTSPSession
|
|
* @now: the current monotonic time
|
|
*
|
|
* Check if @session timeout out.
|
|
*
|
|
* Returns: %TRUE if @session timed out
|
|
*/
|
|
gboolean
|
|
gst_rtsp_session_is_expired_usec (GstRTSPSession * session, gint64 now)
|
|
{
|
|
gboolean res;
|
|
|
|
res = (gst_rtsp_session_next_timeout_usec (session, now) == 0);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/****** Deprecated API *******/
|
|
|
|
/**
|
|
* gst_rtsp_session_is_expired:
|
|
* @session: a #GstRTSPSession
|
|
* @now: (transfer none): the current system time
|
|
*
|
|
* Check if @session timeout out.
|
|
*
|
|
* Returns: %TRUE if @session timed out
|
|
*
|
|
* Deprecated: Use gst_rtsp_session_is_expired_usec() instead.
|
|
*/
|
|
#ifndef GST_REMOVE_DEPRECATED
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS gboolean
|
|
gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
|
|
{
|
|
gboolean res;
|
|
|
|
res = gst_rtsp_session_next_timeout_usec (session,
|
|
(now->tv_sec * G_USEC_PER_SEC) + (now->tv_usec));
|
|
|
|
return res;
|
|
}
|
|
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
|
#endif
|