75c872b1ecbc49e26a8121ba29573b9608fc3e2c
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-mount-points.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19 /**
20  * SECTION:rtsp-mount-points
21  * @short_description: Map a path to media
22  * @see_also: #GstRTSPMediaFactory, #GstRTSPClient
23  *
24  * A #GstRTSPMountPoints object maintains a relation between paths
25  * and #GstRTSPMediaFactory objects. This object is usually given to
26  * #GstRTSPClient and used to find the media attached to a path.
27  *
28  * With gst_rtsp_mount_points_add_factory () and
29  * gst_rtsp_mount_points_remove_factory(), factories can be added and
30  * removed.
31  *
32  * With gst_rtsp_mount_points_match() you can find the #GstRTSPMediaFactory
33  * object that completely matches the given path.
34  *
35  * Last reviewed on 2013-07-11 (1.0.0)
36  */
37 #include <string.h>
38
39 #include "rtsp-mount-points.h"
40
41 #define GST_RTSP_MOUNT_POINTS_GET_PRIVATE(obj)  \
42        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_MOUNT_POINTS, GstRTSPMountPointsPrivate))
43
44 typedef struct
45 {
46   gchar *path;
47   gint len;
48   GstRTSPMediaFactory *factory;
49 } DataItem;
50
51 static DataItem *
52 data_item_new (gchar * path, gint len, GstRTSPMediaFactory * factory)
53 {
54   DataItem *item;
55
56   item = g_slice_alloc (sizeof (DataItem));
57   item->path = path;
58   item->len = len;
59   item->factory = factory;
60
61   return item;
62 }
63
64 static void
65 data_item_free (gpointer data)
66 {
67   DataItem *item = data;
68
69   g_free (item->path);
70   g_object_unref (item->factory);
71   g_slice_free1 (sizeof (DataItem), item);
72 }
73
74 static void
75 data_item_dump (gconstpointer a, gconstpointer prefix)
76 {
77   const DataItem *item = a;
78
79   GST_DEBUG ("%s%s %p", (gchar *) prefix, item->path, item->factory);
80 }
81
82 static gint
83 data_item_compare (gconstpointer a, gconstpointer b, gpointer user_data)
84 {
85   const DataItem *item1 = a, *item2 = b;
86   gint res;
87
88   res = g_strcmp0 (item1->path, item2->path);
89
90   return res;
91 }
92
93 struct _GstRTSPMountPointsPrivate
94 {
95   GMutex lock;
96   GSequence *mounts;            /* protected by lock */
97   gboolean dirty;
98 };
99
100 G_DEFINE_TYPE (GstRTSPMountPoints, gst_rtsp_mount_points, G_TYPE_OBJECT);
101
102 GST_DEBUG_CATEGORY_STATIC (rtsp_media_debug);
103 #define GST_CAT_DEFAULT rtsp_media_debug
104
105 static gchar *default_make_path (GstRTSPMountPoints * mounts,
106     const GstRTSPUrl * url);
107 static void gst_rtsp_mount_points_finalize (GObject * obj);
108
109 static void
110 gst_rtsp_mount_points_class_init (GstRTSPMountPointsClass * klass)
111 {
112   GObjectClass *gobject_class;
113
114   g_type_class_add_private (klass, sizeof (GstRTSPMountPointsPrivate));
115
116   gobject_class = G_OBJECT_CLASS (klass);
117
118   gobject_class->finalize = gst_rtsp_mount_points_finalize;
119
120   klass->make_path = default_make_path;
121
122   GST_DEBUG_CATEGORY_INIT (rtsp_media_debug, "rtspmountpoints", 0,
123       "GstRTSPMountPoints");
124 }
125
126 static void
127 gst_rtsp_mount_points_init (GstRTSPMountPoints * mounts)
128 {
129   GstRTSPMountPointsPrivate *priv = GST_RTSP_MOUNT_POINTS_GET_PRIVATE (mounts);
130
131   GST_DEBUG_OBJECT (mounts, "created");
132
133   mounts->priv = priv;
134
135   g_mutex_init (&priv->lock);
136   priv->mounts = g_sequence_new (data_item_free);
137   priv->dirty = FALSE;
138 }
139
140 static void
141 gst_rtsp_mount_points_finalize (GObject * obj)
142 {
143   GstRTSPMountPoints *mounts = GST_RTSP_MOUNT_POINTS (obj);
144   GstRTSPMountPointsPrivate *priv = mounts->priv;
145
146   GST_DEBUG_OBJECT (mounts, "finalized");
147
148   g_sequence_free (priv->mounts);
149   g_mutex_clear (&priv->lock);
150
151   G_OBJECT_CLASS (gst_rtsp_mount_points_parent_class)->finalize (obj);
152 }
153
154 /**
155  * gst_rtsp_mount_points_new:
156  *
157  * Make a new mount points object.
158  *
159  * Returns: (transfer full): a new #GstRTSPMountPoints
160  */
161 GstRTSPMountPoints *
162 gst_rtsp_mount_points_new (void)
163 {
164   GstRTSPMountPoints *result;
165
166   result = g_object_new (GST_TYPE_RTSP_MOUNT_POINTS, NULL);
167
168   return result;
169 }
170
171 static gchar *
172 default_make_path (GstRTSPMountPoints * mounts, const GstRTSPUrl * url)
173 {
174   return g_strdup (url->abspath);
175 }
176
177 /**
178  * gst_rtsp_mount_points_make_path:
179  * @mounts: a #GstRTSPMountPoints
180  * @url: a #GstRTSPUrl
181  *
182  * Make a path string from @url.
183  *
184  * Returns: (transfer full): a path string for @url, g_free() after usage.
185  */
186 gchar *
187 gst_rtsp_mount_points_make_path (GstRTSPMountPoints * mounts,
188     const GstRTSPUrl * url)
189 {
190   GstRTSPMountPointsClass *klass;
191   gchar *result;
192
193   g_return_val_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts), NULL);
194   g_return_val_if_fail (url != NULL, NULL);
195
196   klass = GST_RTSP_MOUNT_POINTS_GET_CLASS (mounts);
197
198   if (klass->make_path)
199     result = klass->make_path (mounts, url);
200   else
201     result = NULL;
202
203   return result;
204 }
205
206 static gboolean
207 has_prefix (DataItem * str, DataItem * prefix)
208 {
209   /* prefix needs to be smaller than str */
210   if (str->len < prefix->len)
211     return FALSE;
212
213   /* if str is larger, it there should be a / following the prefix */
214   if (str->len > prefix->len && str->path[prefix->len] != '/')
215     return FALSE;
216
217   return strncmp (str->path, prefix->path, prefix->len) == 0;
218 }
219
220 /**
221  * gst_rtsp_mount_points_match:
222  * @mounts: a #GstRTSPMountPoints
223  * @path: a mount point
224  * @matched: (out): the amount of @path matched
225  *
226  * Find the factory in @mounts that has the longest match with @path.
227  *
228  * If @matched is %NULL, @path will match the factory exactly otherwise
229  * the amount of characters that matched is returned in @matched.
230  *
231  * Returns: (transfer full): the #GstRTSPMediaFactory for @path.
232  * g_object_unref() after usage.
233  */
234 GstRTSPMediaFactory *
235 gst_rtsp_mount_points_match (GstRTSPMountPoints * mounts,
236     const gchar * path, gint * matched)
237 {
238   GstRTSPMountPointsPrivate *priv;
239   GstRTSPMediaFactory *result = NULL;
240   GSequenceIter *iter, *best;
241   DataItem item, *ritem;
242
243   g_return_val_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts), NULL);
244   g_return_val_if_fail (path != NULL, NULL);
245
246   priv = mounts->priv;
247
248   item.path = (gchar *) path;
249   item.len = strlen (path);
250
251   g_mutex_lock (&priv->lock);
252   if (priv->dirty) {
253     g_sequence_sort (priv->mounts, data_item_compare, mounts);
254     g_sequence_foreach (priv->mounts, (GFunc) data_item_dump,
255         (gpointer) "sort :");
256     priv->dirty = FALSE;
257   }
258
259   /* find the location of the media in the hashtable we only use the absolute
260    * path of the uri to find a media factory. If the factory depends on other
261    * properties found in the url, this method should be overridden. */
262   iter = g_sequence_get_begin_iter (priv->mounts);
263   best = NULL;
264   while (!g_sequence_iter_is_end (iter)) {
265     ritem = g_sequence_get (iter);
266
267     data_item_dump (ritem, "inspect: ");
268
269     if (best == NULL) {
270       if (has_prefix (&item, ritem)) {
271         data_item_dump (ritem, "prefix: ");
272         best = iter;
273       }
274     } else {
275       if (!has_prefix (&item, ritem))
276         break;
277
278       best = iter;
279       data_item_dump (ritem, "new best: ");
280     }
281     iter = g_sequence_iter_next (iter);
282   }
283   if (best) {
284     ritem = g_sequence_get (best);
285     data_item_dump (ritem, "result: ");
286     if (matched || ritem->len == item.len) {
287       result = g_object_ref (ritem->factory);
288       if (matched)
289         *matched = ritem->len;
290     }
291   }
292   g_mutex_unlock (&priv->lock);
293
294   GST_INFO ("found media factory %p for path %s", result, path);
295
296   return result;
297 }
298
299 /**
300  * gst_rtsp_mount_points_add_factory:
301  * @mounts: a #GstRTSPMountPoints
302  * @path: a mount point
303  * @factory: (transfer full): a #GstRTSPMediaFactory
304  *
305  * Attach @factory to the mount point @path in @mounts.
306  *
307  * @path is of the form (/node)+. Any previous mount point will be freed.
308  *
309  * Ownership is taken of the reference on @factory so that @factory should not be
310  * used after calling this function.
311  */
312 void
313 gst_rtsp_mount_points_add_factory (GstRTSPMountPoints * mounts,
314     const gchar * path, GstRTSPMediaFactory * factory)
315 {
316   GstRTSPMountPointsPrivate *priv;
317   DataItem *item;
318
319   g_return_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts));
320   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
321   g_return_if_fail (path != NULL);
322
323   priv = mounts->priv;
324
325   item = data_item_new (g_strdup (path), strlen (path), factory);
326
327   GST_INFO ("adding media factory %p for path %s", factory, path);
328
329   g_mutex_lock (&priv->lock);
330   g_sequence_append (priv->mounts, item);
331   priv->dirty = TRUE;
332   g_mutex_unlock (&priv->lock);
333 }
334
335 /**
336  * gst_rtsp_mount_points_remove_factory:
337  * @mounts: a #GstRTSPMountPoints
338  * @path: a mount point
339  *
340  * Remove the #GstRTSPMediaFactory associated with @path in @mounts.
341  */
342 void
343 gst_rtsp_mount_points_remove_factory (GstRTSPMountPoints * mounts,
344     const gchar * path)
345 {
346   GstRTSPMountPointsPrivate *priv;
347   DataItem item;
348   GSequenceIter *iter;
349
350   g_return_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts));
351   g_return_if_fail (path != NULL);
352
353   priv = mounts->priv;
354
355   item.path = (gchar *) path;
356
357   GST_INFO ("removing media factory for path %s", path);
358
359   g_mutex_lock (&priv->lock);
360   if (priv->dirty) {
361     g_sequence_sort (priv->mounts, data_item_compare, mounts);
362     priv->dirty = FALSE;
363   }
364   iter = g_sequence_lookup (priv->mounts, &item, data_item_compare, mounts);
365   if (iter) {
366     g_sequence_remove (iter);
367     priv->dirty = TRUE;
368   }
369   g_mutex_unlock (&priv->lock);
370 }