Test that removing probes from within the probe functions works.
[platform/upstream/gstreamer.git] / gst / gstregistry.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2005 David A. Schleef <ds@schleef.org>
5  *
6  * gstregistry.c: handle registry
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 /**
24  * SECTION:gstregistry
25  * @short_description: Abstract base class for management of #GstPlugin objects
26  * @see_also: #GstPlugin, #GstPluginFeature
27  *
28  * One registry holds the metadata of a set of plugins.
29  * All registries build the #GstRegistryPool.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 #include "gst_private.h"
36 #include <glib.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #include <errno.h>
43 #include <stdio.h>
44 #include <string.h>
45
46
47 #include "gstinfo.h"
48 #include "gstregistry.h"
49 #include "gstmarshal.h"
50 #include "gstfilter.h"
51
52 #define GST_CAT_DEFAULT GST_CAT_REGISTRY
53
54 /* the one instance of the default registry */
55 static GstRegistry *_gst_registry_default = NULL;
56
57 /*
58  * Design:
59  *
60  * The GstRegistry object is a list of plugins and some functions
61  * for dealing with them.  Plugins are matched 1-1 with a file on
62  * disk, and may or may not be loaded at a given time.  There may
63  * be multiple GstRegistry objects, but the "default registry" is
64  * the only object that has any meaning to the core.
65  *
66  * The registry.xml file in 0.9 is actually a cache of plugin
67  * information.  This is unlike previous versions, where the registry
68  * file was the primary source of plugin information, and was created
69  * by the gst-register command.
70  *
71  * In 0.9, the primary source, at all times, of plugin information
72  * is each plugin file itself.  Thus, if an application wants
73  * information about a particular plugin, or wants to search for
74  * a feature that satisfies given criteria, the primary means of
75  * doing so is to load every plugin and look at the resulting
76  * information that is gathered in the default registry.  Clearly,
77  * this is a time consuming process, so we cache information in
78  * the registry.xml file.
79  *
80  * On startup, plugins are searched for in the plugin search path.
81  * This path can be set directly using the GST_PLUGIN_PATH
82  * environment variable.  The registry file is loaded from
83  * ~/.gstreamer-0.9/registry.xml or the file listed in the
84  * GST_REGISTRY env var.  The only reason to change the registry
85  * location is for testing.
86  *
87  * For each plugin that is found in the plugin search path, there
88  * could be 3 possibilities for cached information:
89  *  - the cache may not contain information about a given file.
90  *  - the cache may have stale information.
91  *  - the cache may have current information.
92  * In the first two cases, the plugin is loaded and the cache
93  * updated.  In addition to these cases, the cache may have entries
94  * for plugins that are not relevant to the current process.  These
95  * are marked as not available to the current process.  If the
96  * cache is updated for whatever reason, it is marked dirty.
97  *
98  * A dirty cache is written out at the end of initialization.  Each
99  * entry is checked to make sure the information is minimally valid.
100  * If not, the entry is simply dropped.
101  *
102  * Implementation notes:
103  *
104  * The "cache" and "default registry" are different concepts and
105  * can represent different sets of plugins.  For various reasons,
106  * at init time, the cache is stored in the default registry, and
107  * plugins not relevant to the current process are marked with the
108  * GST_PLUGIN_FLAG_CACHED bit.  These plugins are removed at the
109  * end of intitialization.
110  *
111  */
112
113 /* Element signals and args */
114 enum
115 {
116   PLUGIN_ADDED,
117   FEATURE_ADDED,
118   LAST_SIGNAL
119 };
120
121 static void gst_registry_class_init (GstRegistryClass * klass);
122 static void gst_registry_init (GstRegistry * registry);
123 static void gst_registry_finalize (GObject * object);
124
125 static guint gst_registry_signals[LAST_SIGNAL] = { 0 };
126
127 static GstPluginFeature *gst_registry_lookup_feature_locked (GstRegistry *
128     registry, const char *name);
129 static GstPlugin *gst_registry_lookup_locked (GstRegistry * registry,
130     const char *filename);
131
132 G_DEFINE_TYPE (GstRegistry, gst_registry, GST_TYPE_OBJECT);
133 static GstObjectClass *parent_class = NULL;
134
135 static void
136 gst_registry_class_init (GstRegistryClass * klass)
137 {
138   GObjectClass *gobject_class;
139
140   gobject_class = (GObjectClass *) klass;
141
142   parent_class = g_type_class_ref (GST_TYPE_OBJECT);
143
144   gst_registry_signals[PLUGIN_ADDED] =
145       g_signal_new ("plugin-added", G_TYPE_FROM_CLASS (klass),
146       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRegistryClass, plugin_added), NULL,
147       NULL, gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
148   gst_registry_signals[FEATURE_ADDED] =
149       g_signal_new ("feature-added", G_TYPE_FROM_CLASS (klass),
150       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRegistryClass, feature_added),
151       NULL, NULL, gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
152
153   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_registry_finalize);
154 }
155
156 static void
157 gst_registry_init (GstRegistry * registry)
158 {
159 }
160
161 static void
162 gst_registry_finalize (GObject * object)
163 {
164   GstRegistry *registry = GST_REGISTRY (object);
165   GList *plugins, *p;
166   GList *features, *f;
167
168   plugins = registry->plugins;
169   registry->plugins = NULL;
170
171   GST_DEBUG_OBJECT (registry, "registry finalize");
172   p = plugins;
173   while (p) {
174     GstPlugin *plugin = p->data;
175
176     if (plugin) {
177       GST_DEBUG_OBJECT (registry, "removing plugin %s",
178           gst_plugin_get_name (plugin));
179       gst_registry_remove_plugin (registry, plugin);
180     }
181     p = g_list_next (p);
182   }
183   g_list_free (plugins);
184
185   features = registry->features;
186   registry->features = NULL;
187
188   f = features;
189   while (f) {
190     GstPluginFeature *feature = f->data;
191
192     if (feature) {
193       gst_registry_remove_feature (registry, feature);
194     }
195     f = g_list_next (f);
196   }
197   g_list_free (features);
198
199
200   G_OBJECT_CLASS (parent_class)->finalize (object);
201 }
202
203 GstRegistry *
204 gst_registry_get_default (void)
205 {
206   if (!_gst_registry_default) {
207     _gst_registry_default = g_object_new (GST_TYPE_REGISTRY, NULL);
208   }
209   return _gst_registry_default;
210 }
211
212 /**
213  * gst_registry_add_path:
214  * @registry: the registry to add the path to
215  * @path: the path to add to the registry
216  *
217  * Add the given path to the registry. The syntax of the
218  * path is specific to the registry. If the path has already been
219  * added, do nothing.
220  */
221 void
222 gst_registry_add_path (GstRegistry * registry, const gchar * path)
223 {
224   g_return_if_fail (GST_IS_REGISTRY (registry));
225   g_return_if_fail (path != NULL);
226
227   if (strlen (path) == 0) {
228     GST_INFO ("Ignoring empty plugin path");
229     return;
230   }
231
232   GST_LOCK (registry);
233   if (g_list_find_custom (registry->paths, path, (GCompareFunc) strcmp)) {
234     g_warning ("path %s already added to registry", path);
235     GST_UNLOCK (registry);
236     return;
237   }
238
239   GST_INFO ("Adding plugin path: \"%s\"", path);
240   registry->paths = g_list_append (registry->paths, g_strdup (path));
241   GST_UNLOCK (registry);
242 }
243
244 /**
245  * gst_registry_get_path_list:
246  * @registry: the registry to get the pathlist of
247  *
248  * Get the list of paths for the given registry.
249  *
250  * Returns: A Glist of paths as strings. g_list_free after use.
251  */
252 GList *
253 gst_registry_get_path_list (GstRegistry * registry)
254 {
255   GList *list;
256
257   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
258
259   GST_LOCK (registry);
260   /* We don't need to copy the strings, because they won't be deleted
261    * as long as the GstRegistry is around */
262   list = g_list_copy (registry->paths);
263   GST_UNLOCK (registry);
264
265   return list;
266 }
267
268
269 /**
270  * gst_registry_add_plugin:
271  * @registry: the registry to add the plugin to
272  * @plugin: the plugin to add
273  *
274  * Add the plugin to the registry. The plugin-added signal will be emitted.
275  *
276  * Returns: TRUE on success.
277  */
278 gboolean
279 gst_registry_add_plugin (GstRegistry * registry, GstPlugin * plugin)
280 {
281   GstPlugin *existing_plugin;
282
283   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
284
285   GST_LOCK (registry);
286   existing_plugin = gst_registry_lookup_locked (registry, plugin->filename);
287   if (existing_plugin) {
288     GST_DEBUG
289         ("Replacing existing plugin %p with new plugin %p for filename \"%s\"",
290         existing_plugin, plugin, plugin->filename);
291     registry->plugins = g_list_remove (registry->plugins, existing_plugin);
292     gst_object_unref (existing_plugin);
293   }
294
295   GST_DEBUG ("adding plugin %p for filename \"%s\"", plugin, plugin->filename);
296
297   registry->plugins = g_list_prepend (registry->plugins, plugin);
298
299   gst_object_ref (plugin);
300   gst_object_sink (plugin);
301   GST_UNLOCK (registry);
302
303   GST_DEBUG ("emitting plugin-added for filename %s", plugin->filename);
304   g_signal_emit (G_OBJECT (registry), gst_registry_signals[PLUGIN_ADDED], 0,
305       plugin);
306
307   return TRUE;
308 }
309
310 /**
311  * gst_registry_remove_plugin:
312  * @registry: the registry to remove the plugin from
313  * @plugin: the plugin to remove
314  *
315  * Remove the plugin from the registry.
316  */
317 void
318 gst_registry_remove_plugin (GstRegistry * registry, GstPlugin * plugin)
319 {
320   g_return_if_fail (GST_IS_REGISTRY (registry));
321
322   GST_LOCK (registry);
323   registry->plugins = g_list_remove (registry->plugins, plugin);
324   GST_UNLOCK (registry);
325   gst_object_unref (plugin);
326 }
327
328 /**
329  * gst_registry_add_feature:
330  * @registry: the registry to add the plugin to
331  * @feature: the feature to add
332  *
333  * Add the feature to the registry. The feature-added signal will be emitted.
334  *
335  * Returns: TRUE on success.
336  */
337 gboolean
338 gst_registry_add_feature (GstRegistry * registry, GstPluginFeature * feature)
339 {
340   GstPluginFeature *existing_feature;
341
342   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
343   g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), FALSE);
344   g_return_val_if_fail (feature->name != NULL, FALSE);
345   g_return_val_if_fail (feature->plugin_name != NULL, FALSE);
346
347   GST_LOCK (registry);
348   existing_feature = gst_registry_lookup_feature_locked (registry,
349       feature->name);
350   if (existing_feature) {
351     GST_DEBUG_OBJECT (registry, "Replacing existing feature %p (%s)",
352         existing_feature, feature->name);
353     registry->features = g_list_remove (registry->features, existing_feature);
354     gst_object_unref (existing_feature);
355   }
356
357   GST_DEBUG_OBJECT (registry, "adding feature %p (%s)", feature, feature->name);
358
359   registry->features = g_list_prepend (registry->features, feature);
360
361   gst_object_ref (feature);
362   gst_object_sink (feature);
363   GST_UNLOCK (registry);
364
365   GST_DEBUG_OBJECT (registry, "emitting feature-added for %s", feature->name);
366   g_signal_emit (G_OBJECT (registry), gst_registry_signals[FEATURE_ADDED], 0,
367       feature);
368
369   return TRUE;
370 }
371
372 /**
373  * gst_registry_remove_feature:
374  * @registry: the registry to remove the feature from
375  * @feature: the feature to remove
376  *
377  * Remove the feature from the registry.
378  */
379 void
380 gst_registry_remove_feature (GstRegistry * registry, GstPluginFeature * feature)
381 {
382   g_return_if_fail (GST_IS_REGISTRY (registry));
383   GST_DEBUG_OBJECT (registry, "removing feature %p (%s)",
384       feature, gst_plugin_feature_get_name (feature));
385
386   GST_LOCK (registry);
387   registry->features = g_list_remove (registry->features, feature);
388   GST_UNLOCK (registry);
389   gst_object_unref (feature);
390 }
391
392 /**
393  * gst_registry_plugin_filter:
394  * @registry: registry to query
395  * @filter: the filter to use
396  * @first: only return first match
397  * @user_data: user data passed to the filter function
398  *
399  * Runs a filter against all plugins in the registry and returns a GList with
400  * the results. If the first flag is set, only the first match is
401  * returned (as a list with a single object).
402  * Every plugin is reffed; use gst_plugin_list_free() after use, which
403  * will unref again.
404  *
405  * Returns: a #GList of #GstPlugin
406  */
407 GList *
408 gst_registry_plugin_filter (GstRegistry * registry,
409     GstPluginFilter filter, gboolean first, gpointer user_data)
410 {
411   GList *list;
412   GList *g;
413
414   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
415
416   GST_LOCK (registry);
417   list = gst_filter_run (registry->plugins, (GstFilterFunc) filter, first,
418       user_data);
419   for (g = list; g; g = g->next) {
420     gst_object_ref (GST_PLUGIN (g->data));
421   }
422   GST_UNLOCK (registry);
423
424   return list;
425 }
426
427 /**
428  * gst_registry_feature_filter:
429  * @registry: registry to query
430  * @filter: the filter to use
431  * @first: only return first match
432  * @user_data: user data passed to the filter function
433  *
434  * Runs a filter against all features of the plugins in the registry
435  * and returns a GList with the results.
436  * If the first flag is set, only the first match is
437  * returned (as a list with a single object).
438  *
439  * Returns: a GList of plugin features, gst_plugin_feature_list_free after use.
440  */
441 GList *
442 gst_registry_feature_filter (GstRegistry * registry,
443     GstPluginFeatureFilter filter, gboolean first, gpointer user_data)
444 {
445   GList *list;
446   GList *g;
447
448   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
449
450   GST_LOCK (registry);
451   list = gst_filter_run (registry->features, (GstFilterFunc) filter, first,
452       user_data);
453   for (g = list; g; g = g->next) {
454     gst_object_ref (GST_PLUGIN_FEATURE (g->data));
455   }
456   GST_UNLOCK (registry);
457
458   return list;
459 }
460
461 /**
462  * gst_registry_find_plugin:
463  * @registry: the registry to search
464  * @name: the plugin name to find
465  *
466  * Find the plugin with the given name in the registry.
467  * The plugin will be reffed; caller is responsible for unreffing.
468  *
469  * Returns: The plugin with the given name or NULL if the plugin was not found.
470  */
471 GstPlugin *
472 gst_registry_find_plugin (GstRegistry * registry, const gchar * name)
473 {
474   GList *walk;
475   GstPlugin *result = NULL;
476
477   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
478   g_return_val_if_fail (name != NULL, NULL);
479
480   walk = gst_registry_plugin_filter (registry,
481       (GstPluginFilter) gst_plugin_name_filter, TRUE, (gpointer) name);
482   if (walk) {
483     result = GST_PLUGIN (walk->data);
484
485     gst_object_ref (result);
486     gst_plugin_list_free (walk);
487   }
488
489   return result;
490 }
491
492 /**
493  * gst_registry_find_feature:
494  * @registry: the registry to search
495  * @name: the pluginfeature name to find
496  * @type: the pluginfeature type to find
497  *
498  * Find the pluginfeature with the given name and type in the registry.
499  *
500  * Returns: The pluginfeature with the given name and type or NULL
501  * if the plugin was not found.
502  */
503 GstPluginFeature *
504 gst_registry_find_feature (GstRegistry * registry, const gchar * name,
505     GType type)
506 {
507   GstPluginFeature *feature = NULL;
508   GList *walk;
509   GstTypeNameData data;
510
511   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
512   g_return_val_if_fail (name != NULL, NULL);
513
514   data.name = name;
515   data.type = type;
516
517   walk = gst_registry_feature_filter (registry,
518       (GstPluginFeatureFilter) gst_plugin_feature_type_name_filter,
519       TRUE, &data);
520
521   if (walk) {
522     feature = GST_PLUGIN_FEATURE (walk->data);
523
524     gst_object_ref (feature);
525     gst_plugin_feature_list_free (walk);
526   }
527
528   return feature;
529 }
530
531 GList *
532 gst_registry_get_feature_list (GstRegistry * registry, GType type)
533 {
534   GstTypeNameData data;
535
536   data.type = type;
537   data.name = NULL;
538
539   return gst_registry_feature_filter (registry,
540       (GstPluginFeatureFilter) gst_plugin_feature_type_name_filter,
541       FALSE, &data);
542 }
543
544 GList *
545 gst_registry_get_plugin_list (GstRegistry * registry)
546 {
547   GList *list;
548   GList *g;
549
550   GST_LOCK (registry);
551   list = g_list_copy (registry->plugins);
552   for (g = list; g; g = g->next) {
553     gst_object_ref (GST_PLUGIN (g->data));
554   }
555   GST_UNLOCK (registry);
556
557   return list;
558 }
559
560 static GstPluginFeature *
561 gst_registry_lookup_feature_locked (GstRegistry * registry, const char *name)
562 {
563   GList *g;
564   GstPluginFeature *feature;
565
566   if (name == NULL)
567     return NULL;
568
569   for (g = registry->features; g; g = g_list_next (g)) {
570     feature = GST_PLUGIN_FEATURE (g->data);
571     if (feature->name && strcmp (name, feature->name) == 0) {
572       return feature;
573     }
574   }
575
576   return NULL;
577 }
578
579 GstPluginFeature *
580 gst_registry_lookup_feature (GstRegistry * registry, const char *name)
581 {
582   GstPluginFeature *feature;
583
584   GST_LOCK (registry);
585   feature = gst_registry_lookup_feature_locked (registry, name);
586   if (feature)
587     gst_object_ref (feature);
588   GST_UNLOCK (registry);
589
590   return feature;
591 }
592
593 static GstPlugin *
594 gst_registry_lookup_locked (GstRegistry * registry, const char *filename)
595 {
596   GList *g;
597   GstPlugin *plugin;
598
599   if (filename == NULL)
600     return NULL;
601
602   for (g = registry->plugins; g; g = g_list_next (g)) {
603     plugin = GST_PLUGIN (g->data);
604     if (plugin->filename && strcmp (filename, plugin->filename) == 0) {
605       return plugin;
606     }
607   }
608
609   return NULL;
610 }
611
612 /**
613  * gst_registry_lookup:
614  * @registry: the registry to look up in
615  * @filename: the name of the file to look up
616  *
617  * Look up a plugin in the given registry with the given filename.
618  * If found, plugin is reffed.  Caller must unref after use.
619  *
620  * Returns: the #GstPlugin if found, or NULL if not.
621  */
622 GstPlugin *
623 gst_registry_lookup (GstRegistry * registry, const char *filename)
624 {
625   GstPlugin *plugin;
626
627   GST_LOCK (registry);
628   plugin = gst_registry_lookup_locked (registry, filename);
629   if (plugin)
630     gst_object_ref (plugin);
631   GST_UNLOCK (registry);
632
633   return plugin;
634 }
635
636 static void
637 gst_registry_scan_path_level (GstRegistry * registry, const gchar * path,
638     int level)
639 {
640   GDir *dir;
641   const gchar *dirent;
642   gchar *filename;
643   GstPlugin *plugin;
644   GstPlugin *newplugin;
645
646   dir = g_dir_open (path, 0, NULL);
647   if (!dir)
648     return;
649
650   while ((dirent = g_dir_read_name (dir))) {
651     filename = g_strjoin ("/", path, dirent, NULL);
652
653     GST_DEBUG ("examining file: %s", filename);
654
655     if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
656       if (level > 0) {
657         GST_DEBUG ("found directory, recursing");
658         gst_registry_scan_path_level (registry, filename, level - 1);
659       } else {
660         GST_DEBUG ("found directory, but recursion level is too deep");
661       }
662       g_free (filename);
663       continue;
664     }
665     if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
666       GST_DEBUG ("not a regular file, ignoring");
667       g_free (filename);
668       continue;
669     }
670     if (!g_str_has_suffix (filename, ".so") &&
671         !g_str_has_suffix (filename, ".dll") &&
672         !g_str_has_suffix (filename, ".dynlib")) {
673       GST_DEBUG ("extension is not recognized as module file, ignoring");
674       g_free (filename);
675       continue;
676     }
677
678     plugin = gst_registry_lookup (registry, filename);
679     if (plugin) {
680       struct stat file_status;
681
682       if (stat (filename, &file_status)) {
683         /* FIXME remove from cache */
684         g_free (filename);
685         continue;
686       }
687
688       if (plugin->file_mtime == file_status.st_mtime &&
689           plugin->file_size == file_status.st_size) {
690         GST_DEBUG ("file %s cached", filename);
691         plugin->flags &= ~GST_PLUGIN_FLAG_CACHED;
692       } else {
693         GST_DEBUG ("cached info for %s is stale", filename);
694         GST_DEBUG ("mtime %ld != %ld or size %" G_GSIZE_FORMAT " != %"
695             G_GSIZE_FORMAT, plugin->file_mtime, file_status.st_mtime,
696             plugin->file_size, file_status.st_size);
697         gst_registry_remove_plugin (gst_registry_get_default (), plugin);
698         newplugin = gst_plugin_load_file (filename, NULL);
699         if (newplugin)
700           gst_object_unref (newplugin);
701       }
702       gst_object_unref (plugin);
703
704     } else {
705       newplugin = gst_plugin_load_file (filename, NULL);
706       if (newplugin)
707         gst_object_unref (newplugin);
708     }
709
710     g_free (filename);
711   }
712
713   g_dir_close (dir);
714 }
715
716 /**
717  * gst_registry_scan_path:
718  * @registry: the registry to add the path to
719  * @path: the path to add to the registry
720  *
721  * Add the given path to the registry. The syntax of the
722  * path is specific to the registry. If the path has already been
723  * added, do nothing.
724  */
725 void
726 gst_registry_scan_path (GstRegistry * registry, const gchar * path)
727 {
728   gst_registry_scan_path_level (registry, path, 10);
729 }
730
731 void
732 _gst_registry_remove_cache_plugins (GstRegistry * registry)
733 {
734   GList *g;
735   GList *g_next;
736   GstPlugin *plugin;
737
738   GST_DEBUG_OBJECT (registry, "removing cached plugins");
739   g = registry->plugins;
740   while (g) {
741     g_next = g->next;
742     plugin = g->data;
743     if (plugin->flags & GST_PLUGIN_FLAG_CACHED) {
744       GST_DEBUG_OBJECT (registry, "removing cached plugin %s",
745           plugin->filename);
746       registry->plugins = g_list_remove (registry->plugins, plugin);
747       gst_object_unref (plugin);
748     }
749     g = g_next;
750   }
751 }
752
753
754 static gboolean
755 _gst_plugin_feature_filter_plugin_name (GstPluginFeature * feature,
756     gpointer user_data)
757 {
758   return (strcmp (feature->plugin_name, (gchar *) user_data) == 0);
759 }
760
761 GList *
762 gst_registry_get_feature_list_by_plugin (GstRegistry * registry,
763     const gchar * name)
764 {
765   return gst_registry_feature_filter (registry,
766       _gst_plugin_feature_filter_plugin_name, FALSE, (gpointer) name);
767 }
768
769 void
770 _gst_registry_cleanup ()
771 {
772   if (!_gst_registry_default)
773     return;
774
775   gst_object_unref (_gst_registry_default);
776   _gst_registry_default = NULL;
777 }