X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Frtsp-server%2Frtsp-session-pool.c;h=772390ffc1a0579727336a802c8e95e854775bc1;hb=94f3e18c5bb2d8a054ab3b3d4828b9c77fd7f398;hp=1d00dc448d65f0334083f803c8ab27a1ad4fc0b6;hpb=37bbf2317b0a485d1bb530e37f5fb6480b9e60a9;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/rtsp-server/rtsp-session-pool.c b/gst/rtsp-server/rtsp-session-pool.c index 1d00dc4..772390f 100644 --- a/gst/rtsp-server/rtsp-session-pool.c +++ b/gst/rtsp-server/rtsp-session-pool.c @@ -13,13 +13,45 @@ * * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/** + * SECTION:rtsp-session-pool + * @short_description: An object for managing sessions + * @see_also: #GstRTSPSession + * + * The #GstRTSPSessionPool object manages a list of #GstRTSPSession objects. + * + * The maximum number of sessions can be configured with + * gst_rtsp_session_pool_set_max_sessions(). The current number of sessions can + * be retrieved with gst_rtsp_session_pool_get_n_sessions(). + * + * Use gst_rtsp_session_pool_create() to create a new #GstRTSPSession object. + * The session object can be found again with its id and + * gst_rtsp_session_pool_find(). + * + * All sessions can be iterated with gst_rtsp_session_pool_filter(). + * + * Run gst_rtsp_session_pool_cleanup() periodically to remove timed out sessions + * or use gst_rtsp_session_pool_create_watch() to be notified when session + * cleanup should be performed. + * + * Last reviewed on 2013-07-11 (1.0.0) */ #include "rtsp-session-pool.h" -#undef DEBUG +#define GST_RTSP_SESSION_POOL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_SESSION_POOL, GstRTSPSessionPoolPrivate)) + +struct _GstRTSPSessionPoolPrivate +{ + GMutex lock; /* protects everything in this struct */ + guint max_sessions; + GHashTable *sessions; + guint sessions_cookie; +}; #define DEFAULT_MAX_SESSIONS 0 @@ -38,6 +70,14 @@ static const gchar session_id_charset[] = '8', '9', '$', '-', '_', '.', '+' }; +enum +{ + SIGNAL_SESSION_REMOVED, + SIGNAL_LAST +}; + +static guint gst_rtsp_session_pool_signals[SIGNAL_LAST] = { 0 }; + GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug); #define GST_CAT_DEFAULT rtsp_session_debug @@ -48,6 +88,8 @@ static void gst_rtsp_session_pool_set_property (GObject * object, guint propid, static void gst_rtsp_session_pool_finalize (GObject * object); static gchar *create_session_id (GstRTSPSessionPool * pool); +static GstRTSPSession *create_session (GstRTSPSessionPool * pool, + const gchar * id); G_DEFINE_TYPE (GstRTSPSessionPool, gst_rtsp_session_pool, G_TYPE_OBJECT); @@ -56,6 +98,8 @@ gst_rtsp_session_pool_class_init (GstRTSPSessionPoolClass * klass) { GObjectClass *gobject_class; + g_type_class_add_private (klass, sizeof (GstRTSPSessionPoolPrivate)); + gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = gst_rtsp_session_pool_get_property; @@ -68,7 +112,14 @@ gst_rtsp_session_pool_class_init (GstRTSPSessionPoolClass * klass) 0, G_MAXUINT, DEFAULT_MAX_SESSIONS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED] = + g_signal_new ("session-removed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPSessionPoolClass, + session_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, + 1, GST_TYPE_RTSP_SESSION); + klass->create_session_id = create_session_id; + klass->create_session = create_session; GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsessionpool", 0, "GstRTSPSessionPool"); @@ -77,19 +128,32 @@ gst_rtsp_session_pool_class_init (GstRTSPSessionPoolClass * klass) static void gst_rtsp_session_pool_init (GstRTSPSessionPool * pool) { - pool->lock = g_mutex_new (); - pool->sessions = g_hash_table_new_full (g_str_hash, g_str_equal, + GstRTSPSessionPoolPrivate *priv = GST_RTSP_SESSION_POOL_GET_PRIVATE (pool); + + pool->priv = priv; + + g_mutex_init (&priv->lock); + priv->sessions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); - pool->max_sessions = DEFAULT_MAX_SESSIONS; + priv->max_sessions = DEFAULT_MAX_SESSIONS; +} + +static GstRTSPFilterResult +remove_sessions_func (GstRTSPSessionPool * pool, GstRTSPSession * session, + gpointer user_data) +{ + return GST_RTSP_FILTER_REMOVE; } static void gst_rtsp_session_pool_finalize (GObject * object) { GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object); + GstRTSPSessionPoolPrivate *priv = pool->priv; - g_mutex_free (pool->lock); - g_hash_table_unref (pool->sessions); + gst_rtsp_session_pool_filter (pool, remove_sessions_func, NULL); + g_hash_table_unref (priv->sessions); + g_mutex_clear (&priv->lock); G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object); } @@ -131,7 +195,8 @@ gst_rtsp_session_pool_set_property (GObject * object, guint propid, * * Create a new #GstRTSPSessionPool instance. * - * Returns: A new #GstRTSPSessionPool. g_object_unref() after usage. + * Returns: (transfer full): A new #GstRTSPSessionPool. g_object_unref() after + * usage. */ GstRTSPSessionPool * gst_rtsp_session_pool_new (void) @@ -154,11 +219,15 @@ gst_rtsp_session_pool_new (void) void gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool * pool, guint max) { + GstRTSPSessionPoolPrivate *priv; + g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool)); - g_mutex_lock (pool->lock); - pool->max_sessions = max; - g_mutex_unlock (pool->lock); + priv = pool->priv; + + g_mutex_lock (&priv->lock); + priv->max_sessions = max; + g_mutex_unlock (&priv->lock); } /** @@ -173,13 +242,16 @@ gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool * pool, guint max) guint gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool * pool) { + GstRTSPSessionPoolPrivate *priv; 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); + priv = pool->priv; + + g_mutex_lock (&priv->lock); + result = priv->max_sessions; + g_mutex_unlock (&priv->lock); return result; } @@ -195,13 +267,16 @@ gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool * pool) guint gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool * pool) { + GstRTSPSessionPoolPrivate *priv; 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); + priv = pool->priv; + + g_mutex_lock (&priv->lock); + result = g_hash_table_size (priv->sessions); + g_mutex_unlock (&priv->lock); return result; } @@ -214,24 +289,27 @@ gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool * 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. + * Returns: (transfer full) (nullable): the #GstRTSPSession with @sessionid + * or %NULL when the session did not exist. g_object_unref() after usage. */ GstRTSPSession * gst_rtsp_session_pool_find (GstRTSPSessionPool * pool, const gchar * sessionid) { + GstRTSPSessionPoolPrivate *priv; GstRTSPSession *result; g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL); g_return_val_if_fail (sessionid != NULL, NULL); - g_mutex_lock (pool->lock); - result = g_hash_table_lookup (pool->sessions, sessionid); + priv = pool->priv; + + g_mutex_lock (&priv->lock); + result = g_hash_table_lookup (priv->sessions, sessionid); if (result) { g_object_ref (result); gst_rtsp_session_touch (result); } - g_mutex_unlock (pool->lock); + g_mutex_unlock (&priv->lock); return result; } @@ -251,17 +329,24 @@ create_session_id (GstRTSPSessionPool * pool) return g_strndup (id, 16); } +static GstRTSPSession * +create_session (GstRTSPSessionPool * pool, const gchar * id) +{ + return gst_rtsp_session_new (id); +} + /** * gst_rtsp_session_pool_create: * @pool: a #GstRTSPSessionPool * * Create a new #GstRTSPSession object in @pool. * - * Returns: a new #GstRTSPSession. + * Returns: (transfer full): a new #GstRTSPSession. */ GstRTSPSession * gst_rtsp_session_pool_create (GstRTSPSessionPool * pool) { + GstRTSPSessionPoolPrivate *priv; GstRTSPSession *result = NULL; GstRTSPSessionPoolClass *klass; gchar *id = NULL; @@ -269,6 +354,8 @@ gst_rtsp_session_pool_create (GstRTSPSessionPool * pool) g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL); + priv = pool->priv; + klass = GST_RTSP_SESSION_POOL_GET_CLASS (pool); retry = 0; @@ -283,14 +370,14 @@ gst_rtsp_session_pool_create (GstRTSPSessionPool * pool) if (id == NULL) goto no_session; - g_mutex_lock (pool->lock); + g_mutex_lock (&priv->lock); /* check session limit */ - if (pool->max_sessions > 0) { - if (g_hash_table_size (pool->sessions) >= pool->max_sessions) + if (priv->max_sessions > 0) { + if (g_hash_table_size (priv->sessions) >= priv->max_sessions) goto too_many_sessions; } /* check if the sessionid existed */ - result = g_hash_table_lookup (pool->sessions, id); + result = g_hash_table_lookup (priv->sessions, id); if (result) { /* found, retry with a different session id */ result = NULL; @@ -299,12 +386,17 @@ gst_rtsp_session_pool_create (GstRTSPSessionPool * pool) goto collision; } else { /* not found, create session and insert it in the pool */ - result = gst_rtsp_session_new (id); + if (klass->create_session) + result = create_session (pool, id); + if (result == NULL) + goto too_many_sessions; /* take additional ref for the pool */ g_object_ref (result); - g_hash_table_insert (pool->sessions, result->sessionid, result); + g_hash_table_insert (priv->sessions, + (gchar *) gst_rtsp_session_get_sessionid (result), result); + priv->sessions_cookie++; } - g_mutex_unlock (pool->lock); + g_mutex_unlock (&priv->lock); g_free (id); } while (result == NULL); @@ -325,14 +417,14 @@ no_session: collision: { GST_WARNING ("can't find unique sessionid for GstRTSPSessionPool %p", pool); - g_mutex_unlock (pool->lock); + g_mutex_unlock (&priv->lock); g_free (id); return NULL; } too_many_sessions: { - GST_WARNING ("session pool reached max sessions of %d", pool->max_sessions); - g_mutex_unlock (pool->lock); + GST_WARNING ("session pool reached max sessions of %d", priv->max_sessions); + g_mutex_unlock (&priv->lock); g_free (id); return NULL; } @@ -341,7 +433,7 @@ too_many_sessions: /** * gst_rtsp_session_pool_remove: * @pool: a #GstRTSPSessionPool - * @sess: a #GstRTSPSession + * @sess: (transfer none): a #GstRTSPSession * * Remove @sess from @pool, releasing the ref that the pool has on @sess. * @@ -350,22 +442,50 @@ too_many_sessions: gboolean gst_rtsp_session_pool_remove (GstRTSPSessionPool * pool, GstRTSPSession * sess) { + GstRTSPSessionPoolPrivate *priv; gboolean found; 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); + priv = pool->priv; + + g_mutex_lock (&priv->lock); + g_object_ref (sess); + found = + g_hash_table_remove (priv->sessions, + gst_rtsp_session_get_sessionid (sess)); + if (found) + priv->sessions_cookie++; + g_mutex_unlock (&priv->lock); + + if (found) + g_signal_emit (pool, gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], + 0, sess); + + g_object_unref (sess); return found; } +typedef struct +{ + GTimeVal now; + GstRTSPSessionPool *pool; + GList *removed; +} CleanupData; + static gboolean -cleanup_func (gchar * sessionid, GstRTSPSession * sess, GTimeVal * now) +cleanup_func (gchar * sessionid, GstRTSPSession * sess, CleanupData * data) { - return gst_rtsp_session_is_expired (sess, now); + gboolean expired; + + expired = gst_rtsp_session_is_expired (sess, &data->now); + if (expired) { + GST_DEBUG ("session expired"); + data->removed = g_list_prepend (data->removed, g_object_ref (sess)); + } + return expired; } /** @@ -380,57 +500,52 @@ cleanup_func (gchar * sessionid, GstRTSPSession * sess, GTimeVal * now) guint gst_rtsp_session_pool_cleanup (GstRTSPSessionPool * pool) { + GstRTSPSessionPoolPrivate *priv; guint result; - GTimeVal now; + CleanupData data; + GList *walk; g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0); - g_get_current_time (&now); + priv = pool->priv; + + g_get_current_time (&data.now); + data.pool = pool; + data.removed = NULL; - g_mutex_lock (pool->lock); + g_mutex_lock (&priv->lock); result = - g_hash_table_foreach_remove (pool->sessions, (GHRFunc) cleanup_func, - &now); - g_mutex_unlock (pool->lock); + g_hash_table_foreach_remove (priv->sessions, (GHRFunc) cleanup_func, + &data); + if (result > 0) + priv->sessions_cookie++; + g_mutex_unlock (&priv->lock); - return result; -} + for (walk = data.removed; walk; walk = walk->next) { + GstRTSPSession *sess = walk->data; -typedef struct -{ - GstRTSPSessionPool *pool; - GstRTSPSessionFilterFunc func; - gpointer user_data; - GList *list; -} FilterData; + g_signal_emit (pool, + gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0, sess); -static gboolean -filter_func (gchar * sessionid, GstRTSPSession * sess, FilterData * data) -{ - switch (data->func (data->pool, sess, data->user_data)) { - case GST_RTSP_FILTER_REMOVE: - return TRUE; - case GST_RTSP_FILTER_REF: - /* keep ref */ - data->list = g_list_prepend (data->list, g_object_ref (sess)); - /* fallthrough */ - default: - case GST_RTSP_FILTER_KEEP: - return FALSE; + g_object_unref (sess); } + g_list_free (data.removed); + + return result; } /** * gst_rtsp_session_pool_filter: * @pool: a #GstRTSPSessionPool - * @func: a callback - * @user_data: user data passed to @func + * @func: (scope call) (allow-none): a callback + * @user_data: (closure): user data passed to @func * * Call @func for each session in @pool. The result value of @func determines * what happens to the session. @func will be called with the session pool * locked so no further actions on @pool can be performed from @func. * - * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from + * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be set to the + * expired state with gst_rtsp_session_set_expired() and removed from * @pool. * * If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @pool. @@ -439,29 +554,100 @@ filter_func (gchar * sessionid, GstRTSPSession * sess, FilterData * data) * will also be added with an additional ref to the result GList of this * function.. * - * Returns: a GList with all sessions for which @func returned - * #GST_RTSP_FILTER_REF. After usage, each element in the GList should be unreffed - * before the list is freed. + * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all sessions. + * + * Returns: (element-type GstRTSPSession) (transfer full): a GList with all + * sessions 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_pool_filter (GstRTSPSessionPool * pool, - GstRTSPSessionFilterFunc func, gpointer user_data) + GstRTSPSessionPoolFilterFunc func, gpointer user_data) { - FilterData data; + GstRTSPSessionPoolPrivate *priv; + GHashTableIter iter; + gpointer key, value; + GList *result; + GHashTable *visited; + guint cookie; g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL); - g_return_val_if_fail (func != NULL, NULL); - data.pool = pool; - data.func = func; - data.user_data = user_data; - data.list = NULL; + priv = pool->priv; + + result = NULL; + if (func) + visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL); + + g_mutex_lock (&priv->lock); +restart: + g_hash_table_iter_init (&iter, priv->sessions); + cookie = priv->sessions_cookie; + while (g_hash_table_iter_next (&iter, &key, &value)) { + GstRTSPSession *session = value; + GstRTSPFilterResult res; + gboolean changed; + + if (func) { + /* only visit each session once */ + if (g_hash_table_contains (visited, session)) + continue; + + g_hash_table_add (visited, g_object_ref (session)); + g_mutex_unlock (&priv->lock); + + res = func (pool, session, user_data); + + g_mutex_lock (&priv->lock); + } else + res = GST_RTSP_FILTER_REF; + + changed = (cookie != priv->sessions_cookie); + + switch (res) { + case GST_RTSP_FILTER_REMOVE: + { + gboolean removed = TRUE; + + if (changed) + /* something changed, check if we still have the session */ + removed = g_hash_table_remove (priv->sessions, key); + else + g_hash_table_iter_remove (&iter); + + if (removed) { + /* if we managed to remove the session, update the cookie and + * signal */ + cookie = ++priv->sessions_cookie; + g_mutex_unlock (&priv->lock); + + g_signal_emit (pool, + gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0, + session); + + g_mutex_lock (&priv->lock); + /* cookie could have changed again, make sure we restart */ + changed |= (cookie != priv->sessions_cookie); + } + break; + } + case GST_RTSP_FILTER_REF: + /* keep ref */ + result = g_list_prepend (result, g_object_ref (session)); + break; + case GST_RTSP_FILTER_KEEP: + default: + break; + } + if (changed) + goto restart; + } + g_mutex_unlock (&priv->lock); - g_mutex_lock (pool->lock); - g_hash_table_foreach_remove (pool->sessions, (GHRFunc) filter_func, &data); - g_mutex_unlock (pool->lock); + if (func) + g_hash_table_unref (visited); - return data.list; + return result; } typedef struct @@ -488,15 +674,17 @@ collect_timeout (gchar * sessionid, GstRTSPSession * sess, GstPoolSource * psrc) static gboolean gst_pool_source_prepare (GSource * source, gint * timeout) { + GstRTSPSessionPoolPrivate *priv; GstPoolSource *psrc; gboolean result; psrc = (GstPoolSource *) source; psrc->timeout = -1; + priv = psrc->pool->priv; - g_mutex_lock (psrc->pool->lock); - g_hash_table_foreach (psrc->pool->sessions, (GHFunc) collect_timeout, psrc); - g_mutex_unlock (psrc->pool->lock); + g_mutex_lock (&priv->lock); + g_hash_table_foreach (priv->sessions, (GHFunc) collect_timeout, psrc); + g_mutex_unlock (&priv->lock); if (timeout) *timeout = psrc->timeout; @@ -556,7 +744,10 @@ static GSourceFuncs gst_pool_source_funcs = { * gst_rtsp_session_pool_create_watch: * @pool: a #GstRTSPSessionPool * - * A GSource that will be dispatched when the session should be cleaned up. + * Create a #GSource that will be dispatched when the session should be cleaned + * up. + * + * Returns: (transfer full): a #GSource */ GSource * gst_rtsp_session_pool_create_watch (GstRTSPSessionPool * pool)