Add the timeout value to the Session header for unusual timeout values.
Allow us to configure a limit to the amount of active sessions in a pool. Set a
limit on the amount of retry we do after a sessionid collision.
Add properties to the sessionid and the timeout of a session. Keep track of
creation time and last access time for sessions.
gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
/* add the new session header for new session ids */
- if (need_session)
- gst_rtsp_message_add_header (&response, GST_RTSP_HDR_SESSION, session->sessionid);
+ if (need_session) {
+ gchar *str;
+
+ if (session->timeout != 60)
+ str = g_strdup_printf ("%s; timeout=%d", session->sessionid, session->timeout);
+ else
+ str = g_strdup (session->sessionid);
+
+ gst_rtsp_message_take_header (&response, GST_RTSP_HDR_SESSION, str);
+ }
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
g_free (trans_str);
#undef DEBUG
+#define DEFAULT_MAX_SESSIONS 0
+
+enum
+{
+ PROP_0,
+ PROP_MAX_SESSIONS,
+ PROP_LAST
+};
+
+static void gst_rtsp_session_pool_get_property (GObject *object, guint propid,
+ GValue *value, GParamSpec *pspec);
+static void gst_rtsp_session_pool_set_property (GObject *object, guint propid,
+ const GValue *value, GParamSpec *pspec);
static void gst_rtsp_session_pool_finalize (GObject * object);
static gchar * create_session_id (GstRTSPSessionPool *pool);
gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->get_property = gst_rtsp_session_pool_get_property;
+ gobject_class->set_property = gst_rtsp_session_pool_set_property;
gobject_class->finalize = gst_rtsp_session_pool_finalize;
+ g_object_class_install_property (gobject_class, PROP_MAX_SESSIONS,
+ g_param_spec_uint ("max-sessions", "Max Sessions",
+ "the maximum amount of sessions (0 = unlimited)",
+ 0, G_MAXUINT, DEFAULT_MAX_SESSIONS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
klass->create_session_id = create_session_id;
}
pool->lock = g_mutex_new ();
pool->sessions = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, g_object_unref);
+ pool->max_sessions = DEFAULT_MAX_SESSIONS;
}
static void
G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object);
}
+static void
+gst_rtsp_session_pool_get_property (GObject *object, guint propid,
+ GValue *value, GParamSpec *pspec)
+{
+ GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
+
+ switch (propid) {
+ case PROP_MAX_SESSIONS:
+ g_mutex_lock (pool->lock);
+ g_value_set_uint (value, pool->max_sessions);
+ g_mutex_unlock (pool->lock);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
+ break;
+ }
+}
+
+static void
+gst_rtsp_session_pool_set_property (GObject *object, guint propid,
+ const GValue *value, GParamSpec *pspec)
+{
+ GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
+
+ switch (propid) {
+ case PROP_MAX_SESSIONS:
+ g_mutex_lock (pool->lock);
+ pool->max_sessions = g_value_get_uint (value);
+ g_mutex_unlock (pool->lock);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
+ break;
+ }
+}
+
/**
* gst_rtsp_session_pool_new:
*
}
/**
+ * gst_rtsp_session_pool_set_max_sessions:
+ * @pool: a #GstRTSPSessionPool
+ * @max: the maximum number of sessions
+ *
+ * Configure the maximum allowed number of sessions in @pool to @max.
+ * A value of 0 means an unlimited amount of sessions.
+ */
+void
+gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool *pool, guint max)
+{
+ g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool));
+
+ g_mutex_lock (pool->lock);
+ pool->max_sessions = max;
+ g_mutex_unlock (pool->lock);
+}
+
+/**
+ * gst_rtsp_session_pool_get_max_sessions:
+ * @pool: a #GstRTSPSessionPool
+ *
+ * Get the maximum allowed number of sessions in @pool. 0 means an unlimited
+ * amount of sessions.
+ *
+ * Returns: the maximum allowed number of sessions.
+ */
+guint
+gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool *pool)
+{
+ guint result;
+
+ g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
+
+ g_mutex_lock (pool->lock);
+ result = pool->max_sessions;
+ g_mutex_unlock (pool->lock);
+
+ return result;
+}
+
+/**
+ * gst_rtsp_session_pool_get_n_sessions:
+ * @pool: a #GstRTSPSessionPool
+ *
+ * Get the amount of active sessions in @pool.
+ *
+ * Returns: the amount of active sessions in @pool.
+ */
+guint
+gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool *pool)
+{
+ guint result;
+
+ g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
+
+ g_mutex_lock (pool->lock);
+ result = g_hash_table_size (pool->sessions);
+ g_mutex_unlock (pool->lock);
+
+ return result;
+}
+
+/**
* gst_rtsp_session_pool_find:
* @pool: the pool to search
* @sessionid: the session id
*
- * Find the session with @sessionid in @pool.
+ * Find the session with @sessionid in @pool. The access time of the session
+ * will be updated with gst_rtsp_session_touch().
*
* Returns: the #GstRTSPSession with @sessionid or %NULL when the session did
* not exist. g_object_unref() after usage.
g_mutex_lock (pool->lock);
result = g_hash_table_lookup (pool->sessions, sessionid);
- if (result)
+ if (result)
g_object_ref (result);
g_mutex_unlock (pool->lock);
+ if (result)
+ gst_rtsp_session_touch (result);
+
return result;
}
GstRTSPSession *result = NULL;
GstRTSPSessionPoolClass *klass;
gchar *id = NULL;
+ guint retry;
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
klass = GST_RTSP_SESSION_POOL_GET_CLASS (pool);
+ retry = 0;
do {
/* start by creating a new random session id, we assume that this is random
* enough to not cause a collision, which we will check later */
goto no_session;
g_mutex_lock (pool->lock);
+ /* check session limit */
+ if (pool->max_sessions > 0) {
+ if (g_hash_table_size (pool->sessions) >= pool->max_sessions)
+ goto too_many_sessions;
+ }
/* check if the sessionid existed */
result = g_hash_table_lookup (pool->sessions, id);
if (result) {
- /* found, retry with a different session id*/
+ /* found, retry with a different session id */
result = NULL;
+ retry++;
+ if (retry > 100)
+ goto collision;
}
else {
/* not found, create session and insert it in the pool */
g_warning ("can't create session id with GstRTSPSessionPool %p", pool);
return NULL;
}
+collision:
+ {
+ g_warning ("can't find unique sessionid for GstRTSPSessionPool %p", pool);
+ g_mutex_unlock (pool->lock);
+ g_free (id);
+ return NULL;
+ }
+too_many_sessions:
+ {
+ g_warning ("session pool reached max sessions of %d", pool->max_sessions);
+ g_mutex_unlock (pool->lock);
+ g_free (id);
+ return NULL;
+ }
}
/**
* @pool: a #GstRTSPSessionPool
* @sess: a #GstRTSPSession
*
- * Remove @sess from @pool and Clean it up.
+ * Remove @sess from @pool, releasing the ref that the pool has on @sess.
*
- * Returns: a new #GstRTSPSession.
+ * Returns: %TRUE if the session was found and removed.
*/
-void
+gboolean
gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool, GstRTSPSession *sess)
{
gboolean found;
- g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool));
- g_return_if_fail (GST_IS_RTSP_SESSION (sess));
+ g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), FALSE);
+ g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
g_mutex_lock (pool->lock);
found = g_hash_table_remove (pool->sessions, sess->sessionid);
g_mutex_unlock (pool->lock);
+
+ return found;
}
/**
* GstRTSPSessionPool:
- *
+ * @max_sessions: the maximum number of sessions.
* @lock: locking the session hashtable
* @session: hashtable of sessions indexed by the session id.
*
struct _GstRTSPSessionPool {
GObject parent;
+ guint max_sessions;
+
GMutex *lock;
GHashTable *sessions;
};
/**
* GstRTSPSessionPoolClass:
* @create_session_id: create a new random session id. Subclasses can create
- * custom session ids and should not check if the session exists.
+ * custom session ids and should not check if the session exists.
*/
struct _GstRTSPSessionPoolClass {
GObjectClass parent_class;
GType gst_rtsp_session_pool_get_type (void);
+/* creating a session pool */
GstRTSPSessionPool * gst_rtsp_session_pool_new (void);
+/* counting sessionss */
+void gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool *pool, guint max);
+guint gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool *pool);
+
+guint gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool *pool);
+
+/* managing sessions */
+GstRTSPSession * gst_rtsp_session_pool_create (GstRTSPSessionPool *pool);
GstRTSPSession * gst_rtsp_session_pool_find (GstRTSPSessionPool *pool,
const gchar *sessionid);
-GstRTSPSession * gst_rtsp_session_pool_create (GstRTSPSessionPool *pool);
-void gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool,
+gboolean gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool,
GstRTSPSession *sess);
G_END_DECLS
#undef DEBUG
+#define DEFAULT_TIMEOUT 60
+
+enum
+{
+ PROP_0,
+ PROP_SESSIONID,
+ PROP_TIMEOUT,
+ PROP_LAST
+};
+
+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 (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
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));
}
static void
gst_rtsp_session_init (GstRTSPSession * session)
{
+ session->timeout = DEFAULT_TIMEOUT;
+ g_get_current_time (&session->create_time);
+ gst_rtsp_session_touch (session);
}
static void
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);
+
+ switch (propid) {
+ case PROP_SESSIONID:
+ g_value_set_string (value, session->sessionid);
+ break;
+ case PROP_TIMEOUT:
+ g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
+ 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);
+
+ switch (propid) {
+ case PROP_SESSIONID:
+ g_free (session->sessionid);
+ session->sessionid = g_value_dup_string (value);
+ break;
+ case PROP_TIMEOUT:
+ gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
+ }
+}
+
/**
* gst_rtsp_session_manage_media:
* @sess: a #GstRTSPSession
{
GstRTSPSession *result;
- result = g_object_new (GST_TYPE_RTSP_SESSION, NULL);
- result->sessionid = g_strdup (sessionid);
+ 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: 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->sessionid;
+}
+
+/**
+ * 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)
+{
+ g_return_if_fail (GST_IS_RTSP_SESSION (session));
+
+ session->timeout = timeout;
+}
+
+/**
+ * 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)
+{
+ g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
+
+ return session->timeout;
+}
+
+/**
+ * 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)
+{
+ g_return_if_fail (GST_IS_RTSP_SESSION (session));
+
+ g_get_current_time (&session->last_access);
+}
+
+/**
* gst_rtsp_session_stream_init_udp:
* @stream: a #GstRTSPSessionStream
* @ct: a client #GstRTSPTransport
{
GstRTSPTransport *st;
+ g_return_val_if_fail (stream != NULL, NULL);
+ g_return_val_if_fail (ct != NULL, NULL);
+
/* prepare the server transport */
gst_rtsp_transport_new (&st);
{
gboolean ret;
+ g_return_val_if_fail (media != NULL, FALSE);
+
ret = gst_rtsp_media_play (media->media, media->streams);
return ret;
{
gboolean ret;
+ g_return_val_if_fail (media != NULL, FALSE);
+
ret = gst_rtsp_media_pause (media->media, media->streams);
return ret;
{
gboolean ret;
+ g_return_val_if_fail (media != NULL, FALSE);
+
ret = gst_rtsp_media_stop (media->media, media->streams);
return ret;
/**
* GstRTSPSession:
+ * @sessionid: the session id of the session
+ * @timeout: the timeout of the session
+ * @create_time: the time when the session was created
+ * @last_access: the time the session was last accessed
+ * @media: a list of #GstRTSPSessionMedia managed in this session
*
* Session information kept by the server for a specific client.
* One client session, identified with a session id, can handle multiple medias
- * identified with the media object.
+ * identified with the url of a media.
*/
struct _GstRTSPSession {
GObject parent;
gchar *sessionid;
+ guint timeout;
+ GTimeVal create_time;
+ GTimeVal last_access;
+
GList *medias;
};
/* create a new session */
GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid);
+const gchar * gst_rtsp_session_get_sessionid (GstRTSPSession *session);
+
+void gst_rtsp_session_set_timeout (GstRTSPSession *session, guint timeout);
+guint gst_rtsp_session_get_timeout (GstRTSPSession *session);
+
+/* touch the session, update last_access */
+void gst_rtsp_session_touch (GstRTSPSession *session);
+
/* handle media in a session */
GstRTSPSessionMedia * gst_rtsp_session_manage_media (GstRTSPSession *sess,
const GstRTSPUrl *uri,