From df08a2dd9e70c490e03e45b5dbc7ef69a1b423c9 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 3 Jul 2013 10:25:46 +0200 Subject: [PATCH] mount-points: improve mount point searching Use a GSequence to keep track of the mount points. Match a URL to the longest matching registered mount point. This should be the URL to perform aggreagate control and the remainder is the stream specific control part. Add some unit tests for this. --- gst/rtsp-server/rtsp-mount-points.c | 185 ++++++++++++++++++++++++++++++++---- gst/rtsp-server/rtsp-mount-points.h | 17 +++- tests/check/gst/mountpoints.c | 61 ++++++++++++ 3 files changed, 238 insertions(+), 25 deletions(-) diff --git a/gst/rtsp-server/rtsp-mount-points.c b/gst/rtsp-server/rtsp-mount-points.c index 3d3b61c..a8db8b3 100644 --- a/gst/rtsp-server/rtsp-mount-points.c +++ b/gst/rtsp-server/rtsp-mount-points.c @@ -17,15 +17,67 @@ * Boston, MA 02110-1301, USA. */ +#include + #include "rtsp-mount-points.h" #define GST_RTSP_MOUNT_POINTS_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_MOUNT_POINTS, GstRTSPMountPointsPrivate)) +typedef struct +{ + gchar *path; + gint len; + GstRTSPMediaFactory *factory; +} DataItem; + +static DataItem * +data_item_new (gchar * path, gint len, GstRTSPMediaFactory * factory) +{ + DataItem *item; + + item = g_slice_alloc (sizeof (DataItem)); + item->path = path; + item->len = len; + item->factory = factory; + + return item; +} + +static void +data_item_free (gpointer data) +{ + DataItem *item = data; + + g_free (item->path); + g_object_unref (item->factory); + g_slice_free1 (sizeof (DataItem), item); +} + +static void +data_item_dump (gconstpointer a, gpointer prefix) +{ + const DataItem *item = a; + + GST_DEBUG ("%s%s %p\n", (gchar *) prefix, item->path, item->factory); +} + +static gint +data_item_compare (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const DataItem *item1 = a, *item2 = b; + gint res; + + res = g_strcmp0 (item1->path, item2->path); + + return res; +} + struct _GstRTSPMountPointsPrivate { GMutex lock; - GHashTable *mounts; /* protected by lock */ + GSequence *mounts; /* protected by lock */ + gboolean dirty; }; G_DEFINE_TYPE (GstRTSPMountPoints, gst_rtsp_mount_points, G_TYPE_OBJECT); @@ -65,8 +117,8 @@ gst_rtsp_mount_points_init (GstRTSPMountPoints * mounts) mounts->priv = priv; g_mutex_init (&priv->lock); - priv->mounts = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_object_unref); + priv->mounts = g_sequence_new (data_item_free); + priv->dirty = FALSE; } static void @@ -77,7 +129,7 @@ gst_rtsp_mount_points_finalize (GObject * obj) GST_DEBUG_OBJECT (mounts, "finalized"); - g_hash_table_unref (priv->mounts); + g_sequence_free (priv->mounts); g_mutex_clear (&priv->lock); G_OBJECT_CLASS (gst_rtsp_mount_points_parent_class)->finalize (obj); @@ -103,21 +155,10 @@ gst_rtsp_mount_points_new (void) static GstRTSPMediaFactory * find_factory (GstRTSPMountPoints * mounts, const GstRTSPUrl * url) { - GstRTSPMountPointsPrivate *priv = mounts->priv; - GstRTSPMediaFactory *result; - - g_mutex_lock (&priv->lock); - /* find the location of the media in the hashtable we only use the absolute - * path of the uri to find a media factory. If the factory depends on other - * properties found in the url, this method should be overridden. */ - result = g_hash_table_lookup (priv->mounts, url->abspath); - if (result) - g_object_ref (result); - g_mutex_unlock (&priv->lock); - - GST_INFO ("found media factory %p for url abspath %s", result, url->abspath); + g_return_val_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts), NULL); + g_return_val_if_fail (url != NULL, NULL); - return result; + return gst_rtsp_mount_points_match (mounts, url->abspath, NULL); } /** @@ -150,6 +191,98 @@ gst_rtsp_mount_points_find_factory (GstRTSPMountPoints * mounts, return result; } +static gboolean +has_prefix (DataItem * str, DataItem * prefix) +{ + /* prefix needs to be smaller than str */ + if (str->len < prefix->len) + return FALSE; + + /* if str is larger, it there should be a / following the prefix */ + if (str->len > prefix->len && str->path[prefix->len] != '/') + return FALSE; + + return strncmp (str->path, prefix->path, prefix->len) == 0; +} + +/** + * gst_rtsp_mount_points_match: + * @mounts: a #GstRTSPMountPoints + * @path: a mount point + * @matched: the amount of @path matched + * + * Find the factory in @mounts that has the longest match with @path. + * + * If @matched is NULL, @path willt match the factory exactly otherwise + * the amount of characters that matched is returned in @matched. + * + * Returns: (transfer full): the #GstRTSPMediaFactory for @path. + * g_object_unref() after usage. + */ +GstRTSPMediaFactory * +gst_rtsp_mount_points_match (GstRTSPMountPoints * mounts, + const gchar * path, gint * matched) +{ + GstRTSPMountPointsPrivate *priv; + GstRTSPMediaFactory *result = NULL; + GSequenceIter *iter, *best; + DataItem item, *ritem; + + g_return_val_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts), NULL); + g_return_val_if_fail (path != NULL, NULL); + + priv = mounts->priv; + + item.path = (gchar *) path; + item.len = strlen (path); + + g_mutex_lock (&priv->lock); + if (priv->dirty) { + g_sequence_sort (priv->mounts, data_item_compare, mounts); + g_sequence_foreach (priv->mounts, (GFunc) data_item_dump, "sort :"); + priv->dirty = FALSE; + } + + /* find the location of the media in the hashtable we only use the absolute + * path of the uri to find a media factory. If the factory depends on other + * properties found in the url, this method should be overridden. */ + iter = g_sequence_get_begin_iter (priv->mounts); + best = NULL; + while (!g_sequence_iter_is_end (iter)) { + ritem = g_sequence_get (iter); + + data_item_dump (ritem, "inspect: "); + + if (best == NULL) { + if (has_prefix (&item, ritem)) { + data_item_dump (ritem, "prefix: "); + best = iter; + } + } else { + if (!has_prefix (&item, ritem)) + break; + + best = iter; + data_item_dump (ritem, "new best: "); + } + iter = g_sequence_iter_next (iter); + } + if (best) { + ritem = g_sequence_get (best); + data_item_dump (ritem, "result: "); + if (matched || ritem->len == item.len) { + result = g_object_ref (ritem->factory); + if (matched) + *matched = ritem->len; + } + } + g_mutex_unlock (&priv->lock); + + GST_INFO ("found media factory %p for path %s", result, path); + + return result; +} + /** * gst_rtsp_mount_points_add_factory: * @mounts: a #GstRTSPMountPoints @@ -168,6 +301,7 @@ gst_rtsp_mount_points_add_factory (GstRTSPMountPoints * mounts, const gchar * path, GstRTSPMediaFactory * factory) { GstRTSPMountPointsPrivate *priv; + DataItem *item; g_return_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts)); g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory)); @@ -175,8 +309,11 @@ gst_rtsp_mount_points_add_factory (GstRTSPMountPoints * mounts, priv = mounts->priv; + item = data_item_new (g_strdup (path), strlen (path), factory); + g_mutex_lock (&priv->lock); - g_hash_table_insert (priv->mounts, g_strdup (path), factory); + g_sequence_append (priv->mounts, item); + priv->dirty = TRUE; g_mutex_unlock (&priv->lock); } @@ -192,13 +329,21 @@ gst_rtsp_mount_points_remove_factory (GstRTSPMountPoints * mounts, const gchar * path) { GstRTSPMountPointsPrivate *priv; + DataItem item; + GSequenceIter *iter; g_return_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts)); g_return_if_fail (path != NULL); priv = mounts->priv; + item.path = (gchar *) path; + g_mutex_lock (&priv->lock); - g_hash_table_remove (priv->mounts, path); + iter = g_sequence_lookup (priv->mounts, &item, data_item_compare, mounts); + if (iter) { + g_sequence_remove (iter); + priv->dirty = TRUE; + } g_mutex_unlock (&priv->lock); } diff --git a/gst/rtsp-server/rtsp-mount-points.h b/gst/rtsp-server/rtsp-mount-points.h index d8dd1b8..2aae301 100644 --- a/gst/rtsp-server/rtsp-mount-points.h +++ b/gst/rtsp-server/rtsp-mount-points.h @@ -66,7 +66,8 @@ struct _GstRTSPMountPoints { struct _GstRTSPMountPointsClass { GObjectClass parent_class; - GstRTSPMediaFactory * (*find_factory) (GstRTSPMountPoints *mounts, const GstRTSPUrl *url); + GstRTSPMediaFactory * (*find_factory) (GstRTSPMountPoints *mounts, + const GstRTSPUrl *url); }; GType gst_rtsp_mount_points_get_type (void); @@ -74,13 +75,19 @@ GType gst_rtsp_mount_points_get_type (void); /* creating a mount points */ GstRTSPMountPoints * gst_rtsp_mount_points_new (void); -/* finding a media factory */ -GstRTSPMediaFactory * gst_rtsp_mount_points_find_factory (GstRTSPMountPoints *mounts, const GstRTSPUrl *url); +GstRTSPMediaFactory * gst_rtsp_mount_points_find_factory (GstRTSPMountPoints *mounts, + const GstRTSPUrl *url); +GstRTSPMediaFactory * gst_rtsp_mount_points_match (GstRTSPMountPoints *mounts, + const gchar *path, + gint * matched); +/* finding a media factory */ /* managing media to a mount point */ -void gst_rtsp_mount_points_add_factory (GstRTSPMountPoints *mounts, const gchar *path, +void gst_rtsp_mount_points_add_factory (GstRTSPMountPoints *mounts, + const gchar *path, GstRTSPMediaFactory *factory); -void gst_rtsp_mount_points_remove_factory (GstRTSPMountPoints *mounts, const gchar *path); +void gst_rtsp_mount_points_remove_factory (GstRTSPMountPoints *mounts, + const gchar *path); G_END_DECLS diff --git a/tests/check/gst/mountpoints.c b/tests/check/gst/mountpoints.c index 209975d..e620f16 100644 --- a/tests/check/gst/mountpoints.c +++ b/tests/check/gst/mountpoints.c @@ -56,6 +56,66 @@ GST_START_TEST (test_create) GST_END_TEST; +static const gchar *paths[] = { + "/test", + "/booz/fooz", + "/booz/foo/zoop", + "/tark/bar", + "/tark/bar/baz", + "/tark/bar/baz/t", + "/boozop", +}; + +GST_START_TEST (test_match) +{ + GstRTSPMountPoints *mounts; + GstRTSPMediaFactory *f[G_N_ELEMENTS (paths)], *tmp; + gint i, matched; + + mounts = gst_rtsp_mount_points_new (); + + for (i = 0; i < G_N_ELEMENTS (paths); i++) { + f[i] = gst_rtsp_media_factory_new (); + gst_rtsp_mount_points_add_factory (mounts, paths[i], f[i]); + } + + tmp = gst_rtsp_mount_points_match (mounts, "/test", &matched); + fail_unless (tmp == f[0]); + fail_unless (matched == 5); + tmp = gst_rtsp_mount_points_match (mounts, "/test/stream=1", &matched); + fail_unless (tmp == f[0]); + fail_unless (matched == 5); + tmp = gst_rtsp_mount_points_match (mounts, "/booz", &matched); + fail_unless (tmp == NULL); + tmp = gst_rtsp_mount_points_match (mounts, "/booz/foo", &matched); + fail_unless (tmp == NULL); + tmp = gst_rtsp_mount_points_match (mounts, "/booz/fooz", &matched); + fail_unless (tmp == f[1]); + fail_unless (matched == 10); + tmp = gst_rtsp_mount_points_match (mounts, "/booz/fooz/zoo", &matched); + fail_unless (tmp == f[1]); + fail_unless (matched == 10); + tmp = gst_rtsp_mount_points_match (mounts, "/booz/foo/zoop", &matched); + fail_unless (tmp == f[2]); + fail_unless (matched == 14); + tmp = gst_rtsp_mount_points_match (mounts, "/tark/bar", &matched); + fail_unless (tmp == f[3]); + fail_unless (matched == 9); + tmp = gst_rtsp_mount_points_match (mounts, "/tark/bar/boo", &matched); + fail_unless (tmp == f[3]); + fail_unless (matched == 9); + tmp = gst_rtsp_mount_points_match (mounts, "/tark/bar/ba", &matched); + fail_unless (tmp == f[3]); + fail_unless (matched == 9); + tmp = gst_rtsp_mount_points_match (mounts, "/tark/bar/baz", &matched); + fail_unless (tmp == f[4]); + fail_unless (matched == 13); + + g_object_unref (mounts); +} + +GST_END_TEST; + static Suite * rtspmountpoints_suite (void) { @@ -65,6 +125,7 @@ rtspmountpoints_suite (void) suite_add_tcase (s, tc); tcase_set_timeout (tc, 20); tcase_add_test (tc, test_create); + tcase_add_test (tc, test_match); return s; } -- 2.7.4