]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blobdiff - gst/gstregistry.c
Multiarch backward comaptibility.
[glsdk/gstreamer0-10.git] / gst / gstregistry.c
index 23f61ada283e80ddebcadbd79e4b870fc38fc733..79745353b7ec4ae907e6245e6285fb994ff0eec0 100644 (file)
  * <emphasis role="bold">Design:</emphasis>
  *
  * The #GstRegistry object is a list of plugins and some functions for dealing
- * with them. #GstPlugins are matched 1-1 with a file on disk, and may or may
+ * with them. Each #GstPlugin is matched 1-1 with a file on disk, and may or may
  * not be loaded at a given time. There may be multiple #GstRegistry objects,
  * but the "default registry" is the only object that has any meaning to the
  * core.
  *
- * The registry.xml file is actually a cache of plugin information. This is
+ * The registry file is actually a cache of plugin information. This is
  * unlike versions prior to 0.10, where the registry file was the primary source
  * of plugin information, and was created by the gst-register command.
  *
  * or wants to search for a feature that satisfies given criteria, the primary
  * means of doing so is to load every plugin and look at the resulting
  * information that is gathered in the default registry. Clearly, this is a time
- * consuming process, so we cache information in the registry.xml file.
+ * consuming process, so we cache information in the registry file. The format
+ * and location of the cache file is internal to gstreamer.
  *
- * On startup, plugins are searched for in the plugin search path. This path can
- * be set directly using the %GST_PLUGIN_PATH environment variable. The registry
- * file is loaded from ~/.gstreamer-$GST_MAJORMINOR/registry-$ARCH.xml or the
- * file listed in the %GST_REGISTRY env var. The only reason to change the
- * registry location is for testing.
+ * On startup, plugins are searched for in the plugin search path. The following
+ * locations are checked in this order:
+ * <itemizedlist>
+ *   <listitem>
+ *     <para>location from --gst-plugin-path commandline option.</para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>the GST_PLUGIN_PATH environment variable.</para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>the GST_PLUGIN_SYSTEM_PATH environment variable.</para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>default locations (if GST_PLUGIN_SYSTEM_PATH is not set). Those
+ *       default locations are:
+ *       <filename>~/.gstreamer-$GST_MAJORMINOR/plugins/</filename>
+ *       and <filename>$prefix/libs/gstreamer-$GST_MAJORMINOR/</filename>.
+ *     </para>
+ *   </listitem>
+ * </itemizedlist>
+ * The registry cache file is loaded from
+ * <filename>~/.gstreamer-$GST_MAJORMINOR/registry-$ARCH.bin</filename> or the
+ * file listed in the GST_REGISTRY env var. One reason to change the registry
+ * location is for testing.
  *
  * For each plugin that is found in the plugin search path, there could be 3
  * possibilities for cached information:
  * different sets of plugins. For various reasons, at init time, the cache is
  * stored in the default registry, and plugins not relevant to the current
  * process are marked with the %GST_PLUGIN_FLAG_CACHED bit. These plugins are
- * removed at the end of intitialization.
+ * removed at the end of initialization.
  */
 
 #ifdef HAVE_CONFIG_H
 
 #include "gst-i18n-lib.h"
 
+#include "gst.h"
+#include "glib-compat-private.h"
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+extern HMODULE _priv_gst_dll_handle;
+#endif
+
 #define GST_CAT_DEFAULT GST_CAT_REGISTRY
 
+struct _GstRegistryPrivate
+{
+  /* updated whenever the feature list changes */
+  guint32 cookie;
+  /* speedup for searching features */
+  GList *element_factory_list;
+  guint32 efl_cookie;
+  GList *typefind_factory_list;
+  guint32 tfl_cookie;
+};
+
 /* the one instance of the default registry and the mutex protecting the
  * variable. */
 static GStaticMutex _gst_registry_mutex = G_STATIC_MUTEX_INIT;
@@ -136,6 +175,8 @@ extern GList *_priv_gst_plugin_paths;
 
 /* Set to TRUE when the registry cache should be disabled */
 gboolean _gst_disable_registry_cache = FALSE;
+
+static gboolean __registry_reuse_plugin_scanner = TRUE;
 #endif
 
 /* Element signals and args */
@@ -166,6 +207,7 @@ gst_registry_class_init (GstRegistryClass * klass)
   gobject_class = (GObjectClass *) klass;
 
   parent_class = g_type_class_peek_parent (klass);
+  g_type_class_add_private (klass, sizeof (GstRegistryPrivate));
 
   /**
    * GstRegistry::plugin-added:
@@ -193,7 +235,7 @@ gst_registry_class_init (GstRegistryClass * klass)
       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRegistryClass, feature_added),
       NULL, NULL, gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
 
-  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_registry_finalize);
+  gobject_class->finalize = gst_registry_finalize;
 }
 
 static void
@@ -201,6 +243,9 @@ gst_registry_init (GstRegistry * registry)
 {
   registry->feature_hash = g_hash_table_new (g_str_hash, g_str_equal);
   registry->basename_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  registry->priv =
+      G_TYPE_INSTANCE_GET_PRIVATE (registry, GST_TYPE_REGISTRY,
+      GstRegistryPrivate);
 }
 
 static void
@@ -237,7 +282,7 @@ gst_registry_finalize (GObject * object)
     if (feature) {
       GST_LOG_OBJECT (registry, "removing feature %p (%s)",
           feature, gst_plugin_feature_get_name (feature));
-      gst_object_unref (feature);
+      gst_object_unparent (GST_OBJECT_CAST (feature));
     }
     f = g_list_next (f);
   }
@@ -248,6 +293,16 @@ gst_registry_finalize (GObject * object)
   g_hash_table_destroy (registry->basename_hash);
   registry->basename_hash = NULL;
 
+  if (registry->priv->element_factory_list) {
+    GST_DEBUG_OBJECT (registry, "Cleaning up cached element factory list");
+    gst_plugin_feature_list_free (registry->priv->element_factory_list);
+  }
+
+  if (registry->priv->typefind_factory_list) {
+    GST_DEBUG_OBJECT (registry, "Cleaning up cached typefind factory list");
+    gst_plugin_feature_list_free (registry->priv->typefind_factory_list);
+  }
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -257,7 +312,7 @@ gst_registry_finalize (GObject * object)
  * Retrieves the default registry. The caller does not own a reference on the
  * registry, as it is alive as long as GStreamer is initialized.
  *
- * Returns: The default #GstRegistry.
+ * Returns: (transfer none): The default #GstRegistry.
  */
 GstRegistry *
 gst_registry_get_default (void)
@@ -266,7 +321,7 @@ gst_registry_get_default (void)
 
   g_static_mutex_lock (&_gst_registry_mutex);
   if (G_UNLIKELY (!_gst_registry_default)) {
-    _gst_registry_default = g_object_new (GST_TYPE_REGISTRY, NULL);
+    _gst_registry_default = g_object_newv (GST_TYPE_REGISTRY, 0, NULL);
     gst_object_ref_sink (GST_OBJECT_CAST (_gst_registry_default));
   }
   registry = _gst_registry_default;
@@ -322,7 +377,8 @@ was_added:
  *
  * Get the list of paths for the given registry.
  *
- * Returns: A Glist of paths as strings. g_list_free after use.
+ * Returns: (transfer container) (element-type char*): A #GList of paths as
+ *     strings. g_list_free after use.
  *
  * MT safe.
  */
@@ -346,7 +402,7 @@ gst_registry_get_path_list (GstRegistry * registry)
 /**
  * gst_registry_add_plugin:
  * @registry: the registry to add the plugin to
- * @plugin: the plugin to add
+ * @plugin: (transfer full): the plugin to add
  *
  * Add the plugin to the registry. The plugin-added signal will be emitted.
  * This function will sink @plugin.
@@ -370,8 +426,20 @@ gst_registry_add_plugin (GstRegistry * registry, GstPlugin * plugin)
         gst_registry_lookup_bn_locked (registry, plugin->basename);
     if (existing_plugin) {
       GST_DEBUG_OBJECT (registry,
-          "Replacing existing plugin %p with new plugin %p for filename \"%s\"",
-          existing_plugin, plugin, GST_STR_NULL (plugin->filename));
+          "Replacing existing plugin \"%s\" %p with new plugin %p for filename \"%s\"",
+          GST_STR_NULL (existing_plugin->filename), existing_plugin, plugin,
+          GST_STR_NULL (plugin->filename));
+      /* If the new plugin is blacklisted and the existing one isn't cached, do not
+       * accept if it's from a different location than the existing one */
+      if ((plugin->flags & GST_PLUGIN_FLAG_BLACKLISTED) &&
+          strcmp (plugin->filename, existing_plugin->filename)) {
+        GST_WARNING_OBJECT (registry,
+            "Not replacing plugin because new one (%s) is blacklisted but for a different location than existing one (%s)",
+            plugin->filename, existing_plugin->filename);
+        gst_object_unref (plugin);
+        GST_OBJECT_UNLOCK (registry);
+        return FALSE;
+      }
       registry->plugins = g_list_remove (registry->plugins, existing_plugin);
       if (G_LIKELY (existing_plugin->basename))
         g_hash_table_remove (registry->basename_hash,
@@ -402,35 +470,34 @@ gst_registry_remove_features_for_plugin_unlocked (GstRegistry * registry,
     GstPlugin * plugin)
 {
   GList *f;
-  const gchar *name;
 
   g_return_if_fail (GST_IS_REGISTRY (registry));
   g_return_if_fail (GST_IS_PLUGIN (plugin));
 
-  name = gst_plugin_get_name (plugin);
-
   /* Remove all features for this plugin */
   f = registry->features;
   while (f != NULL) {
     GList *next = g_list_next (f);
     GstPluginFeature *feature = f->data;
 
-    if (G_UNLIKELY (feature && !strcmp (feature->plugin_name, name))) {
-      GST_DEBUG_OBJECT (registry, "removing feature %p (%s) for plugin %s",
-          feature, gst_plugin_feature_get_name (feature), name);
+    if (G_UNLIKELY (feature && feature->plugin == plugin)) {
+      GST_DEBUG_OBJECT (registry, "removing feature %p (%s) for plugin %p (%s)",
+          feature, gst_plugin_feature_get_name (feature), plugin,
+          plugin->desc.name);
 
       registry->features = g_list_delete_link (registry->features, f);
       g_hash_table_remove (registry->feature_hash, feature->name);
-      gst_object_unref (feature);
+      gst_object_unparent (GST_OBJECT_CAST (feature));
     }
     f = next;
   }
+  registry->priv->cookie++;
 }
 
 /**
  * gst_registry_remove_plugin:
  * @registry: the registry to remove the plugin from
- * @plugin: the plugin to remove
+ * @plugin: (transfer none): the plugin to remove
  *
  * Remove the plugin from the registry.
  *
@@ -457,7 +524,7 @@ gst_registry_remove_plugin (GstRegistry * registry, GstPlugin * plugin)
 /**
  * gst_registry_add_feature:
  * @registry: the registry to add the plugin to
- * @feature: the feature to add
+ * @feature: (transfer full): the feature to add
  *
  * Add the feature to the registry. The feature-added signal will be emitted.
  * This function sinks @feature.
@@ -496,10 +563,12 @@ gst_registry_add_feature (GstRegistry * registry, GstPluginFeature * feature)
   if (G_UNLIKELY (existing_feature)) {
     /* We unref now. No need to remove the feature name from the hash table, it
      * got replaced by the new feature */
-    gst_object_unref (existing_feature);
+    gst_object_unparent (GST_OBJECT_CAST (existing_feature));
   }
 
-  gst_object_ref_sink (feature);
+  gst_object_set_parent (GST_OBJECT_CAST (feature), GST_OBJECT_CAST (registry));
+
+  registry->priv->cookie++;
   GST_OBJECT_UNLOCK (registry);
 
   GST_LOG_OBJECT (registry, "emitting feature-added for %s", feature->name);
@@ -511,7 +580,7 @@ gst_registry_add_feature (GstRegistry * registry, GstPluginFeature * feature)
 /**
  * gst_registry_remove_feature:
  * @registry: the registry to remove the feature from
- * @feature: the feature to remove
+ * @feature: (transfer none): the feature to remove
  *
  * Remove the feature from the registry.
  *
@@ -529,16 +598,18 @@ gst_registry_remove_feature (GstRegistry * registry, GstPluginFeature * feature)
   GST_OBJECT_LOCK (registry);
   registry->features = g_list_remove (registry->features, feature);
   g_hash_table_remove (registry->feature_hash, feature->name);
+  registry->priv->cookie++;
   GST_OBJECT_UNLOCK (registry);
-  gst_object_unref (feature);
+
+  gst_object_unparent ((GstObject *) feature);
 }
 
 /**
  * gst_registry_plugin_filter:
  * @registry: registry to query
- * @filter: the filter to use
+ * @filter: (scope call): the filter to use
  * @first: only return first match
- * @user_data: user data passed to the filter function
+ * @user_data: (closure): user data passed to the filter function
  *
  * Runs a filter against all plugins in the registry and returns a #GList with
  * the results. If the first flag is set, only the first match is
@@ -546,7 +617,8 @@ gst_registry_remove_feature (GstRegistry * registry, GstPluginFeature * feature)
  * Every plugin is reffed; use gst_plugin_list_free() after use, which
  * will unref again.
  *
- * Returns: a #GList of #GstPlugin. Use gst_plugin_list_free() after usage.
+ * Returns: (transfer full) (element-type Gst.Plugin): a #GList of #GstPlugin.
+ *     Use gst_plugin_list_free() after usage.
  *
  * MT safe.
  */
@@ -554,36 +626,152 @@ GList *
 gst_registry_plugin_filter (GstRegistry * registry,
     GstPluginFilter filter, gboolean first, gpointer user_data)
 {
-  GList *list;
-  GList *g;
+  GList *list = NULL;
 
   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
 
   GST_OBJECT_LOCK (registry);
-  list = gst_filter_run (registry->plugins, (GstFilterFunc) filter, first,
-      user_data);
-  for (g = list; g; g = g->next) {
-    gst_object_ref (GST_PLUGIN_CAST (g->data));
+  {
+    const GList *walk;
+
+    for (walk = registry->plugins; walk != NULL; walk = walk->next) {
+      GstPlugin *plugin = walk->data;
+
+      if (filter == NULL || filter (plugin, user_data)) {
+        list = g_list_prepend (list, gst_object_ref (plugin));
+
+        if (first)
+          break;
+      }
+    }
   }
   GST_OBJECT_UNLOCK (registry);
 
   return list;
 }
 
+#ifdef GST_DISABLE_DEPRECATED
+typedef struct
+{
+  const gchar *name;
+  GType type;
+} GstTypeNameData;
+static gboolean
+gst_plugin_feature_type_name_filter (GstPluginFeature * feature,
+    GstTypeNameData * data)
+{
+  g_assert (GST_IS_PLUGIN_FEATURE (feature));
+
+  return ((data->type == 0 || data->type == G_OBJECT_TYPE (feature)) &&
+      (data->name == NULL
+          || !strcmp (data->name, GST_PLUGIN_FEATURE_NAME (feature))));
+}
+#endif
+
+/* returns TRUE if the list was changed
+ *
+ * Must be called with the object lock taken */
+static gboolean
+gst_registry_get_feature_list_or_create (GstRegistry * registry,
+    GList ** previous, guint32 * cookie, GType type)
+{
+  gboolean res = FALSE;
+  GstRegistryPrivate *priv = registry->priv;
+
+  if (G_UNLIKELY (!*previous || priv->cookie != *cookie)) {
+    GstTypeNameData data;
+    const GList *walk;
+
+    if (*previous) {
+      gst_plugin_feature_list_free (*previous);
+      *previous = NULL;
+    }
+
+    data.type = type;
+    data.name = NULL;
+
+    for (walk = registry->features; walk != NULL; walk = walk->next) {
+      GstPluginFeature *feature = walk->data;
+
+      if (gst_plugin_feature_type_name_filter (feature, &data)) {
+        *previous = g_list_prepend (*previous, gst_object_ref (feature));
+      }
+    }
+
+    *cookie = priv->cookie;
+    res = TRUE;
+  }
+
+  return res;
+}
+
+static gint
+type_find_factory_rank_cmp (const GstPluginFeature * fac1,
+    const GstPluginFeature * fac2)
+{
+  if (G_LIKELY (fac1->rank != fac2->rank))
+    return fac2->rank - fac1->rank;
+
+  /* to make the order in which things happen more deterministic,
+   * sort by name when the ranks are the same. */
+  return strcmp (fac1->name, fac2->name);
+}
+
+static GList *
+gst_registry_get_element_factory_list (GstRegistry * registry)
+{
+  GList *list;
+
+  GST_OBJECT_LOCK (registry);
+
+  gst_registry_get_feature_list_or_create (registry,
+      &registry->priv->element_factory_list, &registry->priv->efl_cookie,
+      GST_TYPE_ELEMENT_FACTORY);
+
+  /* Return reffed copy */
+  list = gst_plugin_feature_list_copy (registry->priv->element_factory_list);
+
+  GST_OBJECT_UNLOCK (registry);
+
+  return list;
+}
+
+static GList *
+gst_registry_get_typefind_factory_list (GstRegistry * registry)
+{
+  GList *list;
+
+  GST_OBJECT_LOCK (registry);
+
+  if (G_UNLIKELY (gst_registry_get_feature_list_or_create (registry,
+              &registry->priv->typefind_factory_list,
+              &registry->priv->tfl_cookie, GST_TYPE_TYPE_FIND_FACTORY)))
+    registry->priv->typefind_factory_list =
+        g_list_sort (registry->priv->typefind_factory_list,
+        (GCompareFunc) type_find_factory_rank_cmp);
+
+  /* Return reffed copy */
+  list = gst_plugin_feature_list_copy (registry->priv->typefind_factory_list);
+
+  GST_OBJECT_UNLOCK (registry);
+
+  return list;
+}
+
 /**
  * gst_registry_feature_filter:
  * @registry: registry to query
- * @filter: the filter to use
+ * @filter: (scope call): the filter to use
  * @first: only return first match
- * @user_data: user data passed to the filter function
+ * @user_data: (closure): user data passed to the filter function
  *
  * Runs a filter against all features of the plugins in the registry
  * and returns a GList with the results.
  * If the first flag is set, only the first match is
  * returned (as a list with a single object).
  *
- * Returns: a #GList of #GstPluginFeature. Use gst_plugin_feature_list_free()
- * after usage.
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a #GList of
+ *     #GstPluginFeature. Use gst_plugin_feature_list_free() after usage.
  *
  * MT safe.
  */
@@ -591,16 +779,24 @@ GList *
 gst_registry_feature_filter (GstRegistry * registry,
     GstPluginFeatureFilter filter, gboolean first, gpointer user_data)
 {
-  GList *list;
-  GList *g;
+  GList *list = NULL;
 
   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
 
   GST_OBJECT_LOCK (registry);
-  list = gst_filter_run (registry->features, (GstFilterFunc) filter, first,
-      user_data);
-  for (g = list; g; g = g->next) {
-    gst_object_ref (GST_PLUGIN_FEATURE_CAST (g->data));
+  {
+    const GList *walk;
+
+    for (walk = registry->features; walk != NULL; walk = walk->next) {
+      GstPluginFeature *feature = walk->data;
+
+      if (filter == NULL || filter (feature, user_data)) {
+        list = g_list_prepend (list, gst_object_ref (feature));
+
+        if (first)
+          break;
+      }
+    }
   }
   GST_OBJECT_UNLOCK (registry);
 
@@ -615,8 +811,8 @@ gst_registry_feature_filter (GstRegistry * registry,
  * Find the plugin with the given name in the registry.
  * The plugin will be reffed; caller is responsible for unreffing.
  *
- * Returns: The plugin with the given name or NULL if the plugin was not found.
- * gst_object_unref() after usage.
+ * Returns: (transfer full): the plugin with the given name or NULL if the
+ *     plugin was not found. gst_object_unref() after usage.
  *
  * MT safe.
  */
@@ -649,8 +845,8 @@ gst_registry_find_plugin (GstRegistry * registry, const gchar * name)
  *
  * Find the pluginfeature with the given name and type in the registry.
  *
- * Returns: The pluginfeature with the given name and type or NULL
- * if the plugin was not found. gst_object_unref() after usage.
+ * Returns: (transfer full): the pluginfeature with the given name and type
+ *     or NULL if the plugin was not found. gst_object_unref() after usage.
  *
  * MT safe.
  */
@@ -680,8 +876,8 @@ gst_registry_find_feature (GstRegistry * registry, const gchar * name,
  *
  * Retrieves a #GList of #GstPluginFeature of @type.
  *
- * Returns: a #GList of #GstPluginFeature of @type. Use
- * gst_plugin_feature_list_free() after usage.
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a #GList of
+ *     #GstPluginFeature of @type. Use gst_plugin_feature_list_free() after use
  *
  * MT safe.
  */
@@ -693,6 +889,12 @@ gst_registry_get_feature_list (GstRegistry * registry, GType type)
   g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
   g_return_val_if_fail (g_type_is_a (type, GST_TYPE_PLUGIN_FEATURE), NULL);
 
+  /* Speed up */
+  if (type == GST_TYPE_ELEMENT_FACTORY)
+    return gst_registry_get_element_factory_list (registry);
+  else if (type == GST_TYPE_TYPE_FIND_FACTORY)
+    return gst_registry_get_typefind_factory_list (registry);
+
   data.type = type;
   data.name = NULL;
 
@@ -708,7 +910,8 @@ gst_registry_get_feature_list (GstRegistry * registry, GType type)
  * Get a copy of all plugins registered in the given registry. The refcount
  * of each element in the list in incremented.
  *
- * Returns: a #GList of #GstPlugin. Use gst_plugin_list_free() after usage.
+ * Returns: (transfer full) (element-type Gst.Plugin): a #GList of #GstPlugin.
+ *     Use gst_plugin_list_free() after usage.
  *
  * MT safe.
  */
@@ -743,8 +946,8 @@ gst_registry_lookup_feature_locked (GstRegistry * registry, const char *name)
  *
  * Find a #GstPluginFeature with @name in @registry.
  *
- * Returns: a #GstPluginFeature with its refcount incremented, use
- * gst_object_unref() after usage.
+ * Returns: (transfer full): a #GstPluginFeature with its refcount incremented,
+ *     use gst_object_unref() after usage.
  *
  * MT safe.
  */
@@ -793,8 +996,8 @@ gst_registry_lookup_bn (GstRegistry * registry, const char *basename)
  * Look up a plugin in the given registry with the given filename.
  * If found, plugin is reffed.
  *
- * Returns: the #GstPlugin if found, or NULL if not. gst_object_unref()
- * after usage.
+ * Returns: (transfer full): the #GstPlugin if found, or NULL if not.
+ *     gst_object_unref() after usage.
  */
 GstPlugin *
 gst_registry_lookup (GstRegistry * registry, const char *filename)
@@ -875,6 +1078,12 @@ gst_registry_scan_plugin_file (GstRegistryScanContext * context,
   gboolean changed = FALSE;
   GstPlugin *newplugin = NULL;
 
+#ifdef G_OS_WIN32
+  /* Disable external plugin loader on Windows until it is ported properly. */
+  context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
+#endif
+
+
   /* Have a plugin to load - see if the scan-helper needs starting */
   if (context->helper_state == REGISTRY_SCAN_HELPER_NOT_STARTED) {
     GST_DEBUG ("Starting plugin scanner for file %s", filename);
@@ -918,10 +1127,35 @@ gst_registry_scan_plugin_file (GstRegistryScanContext * context,
     gst_object_unref (newplugin);
     changed = TRUE;
   }
+#ifndef GST_DISABLE_REGISTRY
+  if (!__registry_reuse_plugin_scanner) {
+    clear_scan_context (context);
+    context->helper_state = REGISTRY_SCAN_HELPER_NOT_STARTED;
+  }
+#endif
 
   return changed;
 }
 
+static gboolean
+is_blacklisted_hidden_directory (const gchar * dirent)
+{
+  if (G_LIKELY (dirent[0] != '.'))
+    return FALSE;
+
+  /* skip the .debug directory, these contain elf files that are not
+   * useful or worse, can crash dlopen () */
+  if (strcmp (dirent, ".debug") == 0)
+    return TRUE;
+
+  /* can also skip .git and .deps dirs, those won't contain useful files.
+   * This speeds up scanning a bit in uninstalled setups. */
+  if (strcmp (dirent, ".git") == 0 || strcmp (dirent, ".deps") == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
 static gboolean
 gst_registry_scan_path_level (GstRegistryScanContext * context,
     const gchar * path, int level)
@@ -937,7 +1171,7 @@ gst_registry_scan_path_level (GstRegistryScanContext * context,
     return FALSE;
 
   while ((dirent = g_dir_read_name (dir))) {
-    struct stat file_status;
+    GStatBuf file_status;
 
     filename = g_build_filename (path, dirent, NULL);
     if (g_stat (filename, &file_status) < 0) {
@@ -948,12 +1182,8 @@ gst_registry_scan_path_level (GstRegistryScanContext * context,
     }
 
     if (file_status.st_mode & S_IFDIR) {
-      /* skip the .debug directory, these contain elf files that are not
-       * useful or worse, can crash dlopen (). do a quick check for the . first
-       * and then call the compare functions. */
-      if (G_UNLIKELY (dirent[0] == '.' && (g_str_equal (dirent, ".debug")
-                  || g_str_equal (dirent, ".git")))) {
-        GST_LOG_OBJECT (context->registry, "ignoring .debug or .git directory");
+      if (G_UNLIKELY (is_blacklisted_hidden_directory (dirent))) {
+        GST_TRACE_OBJECT (context->registry, "ignoring %s directory", dirent);
         g_free (filename);
         continue;
       }
@@ -972,7 +1202,7 @@ gst_registry_scan_path_level (GstRegistryScanContext * context,
       continue;
     }
     if (!(file_status.st_mode & S_IFREG)) {
-      GST_LOG_OBJECT (context->registry, "%s is not a regular file, ignoring",
+      GST_TRACE_OBJECT (context->registry, "%s is not a regular file, ignoring",
           filename);
       g_free (filename);
       continue;
@@ -982,7 +1212,7 @@ gst_registry_scan_path_level (GstRegistryScanContext * context,
         && !g_str_has_suffix (dirent, GST_EXTRA_MODULE_SUFFIX)
 #endif
         ) {
-      GST_LOG_OBJECT (context->registry,
+      GST_TRACE_OBJECT (context->registry,
           "extension is not recognized as module file, ignoring file %s",
           filename);
       g_free (filename);
@@ -992,6 +1222,17 @@ gst_registry_scan_path_level (GstRegistryScanContext * context,
     GST_LOG_OBJECT (context->registry, "file %s looks like a possible module",
         filename);
 
+    /* try to avoid unnecessary plugin-move pain */
+    if (g_str_has_prefix (dirent, "libgstvalve") ||
+        g_str_has_prefix (dirent, "libgstselector")) {
+      GST_WARNING_OBJECT (context->registry, "ignoring old plugin %s which "
+          "has been merged into the corelements plugin", filename);
+      /* Plugin will be removed from cache after the scan completes if it
+       * is still marked 'cached' */
+      g_free (filename);
+      continue;
+    }
+
     /* plug-ins are considered unique by basename; if the given name
      * was already seen by the registry, we ignore it */
     plugin = gst_registry_lookup_bn (context->registry, dirent);
@@ -1025,11 +1266,11 @@ gst_registry_scan_path_level (GstRegistryScanContext * context,
       } else {
         GST_INFO_OBJECT (context->registry, "cached info for %s is stale",
             filename);
-        GST_DEBUG_OBJECT (context->registry, "mtime %ld != %ld or size %"
-            G_GINT64_FORMAT " != %" G_GINT64_FORMAT " or external dependency "
-            "env_vars changed: %d or external dependencies changed: %d"
-            " or old path %s != new path %s",
-            plugin->file_mtime, file_status.st_mtime,
+        GST_DEBUG_OBJECT (context->registry, "mtime %" G_GINT64_FORMAT " != %"
+            G_GINT64_FORMAT " or size %" G_GINT64_FORMAT " != %"
+            G_GINT64_FORMAT " or external dependency env_vars changed: %d or"
+            " external dependencies changed: %d or old path %s != new path %s",
+            (gint64) plugin->file_mtime, (gint64) file_status.st_mtime,
             (gint64) plugin->file_size, (gint64) file_status.st_size,
             env_vars_changed, deps_changed, plugin->filename, filename);
         gst_registry_remove_plugin (context->registry, plugin);
@@ -1110,8 +1351,8 @@ _gst_plugin_feature_filter_plugin_name (GstPluginFeature * feature,
  *
  * Retrieves a #GList of features of the plugin with name @name.
  *
- * Returns: a #GList of #GstPluginFeature. Use gst_plugin_feature_list_free()
- * after usage.
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a #GList of
+ *     #GstPluginFeature. Use gst_plugin_feature_list_free() after usage.
  */
 GList *
 gst_registry_get_feature_list_by_plugin (GstRegistry * registry,
@@ -1126,7 +1367,7 @@ gst_registry_get_feature_list_by_plugin (GstRegistry * registry,
 
 /* Unref and delete the default registry */
 void
-_priv_gst_registry_cleanup ()
+_priv_gst_registry_cleanup (void)
 {
   GstRegistry *registry;
 
@@ -1313,7 +1554,7 @@ scan_and_update_registry (GstRegistry * default_registry,
    * path, and the plugins installed in the user's home directory */
   plugin_path = g_getenv ("GST_PLUGIN_SYSTEM_PATH");
   if (plugin_path == NULL) {
-    char *home_plugins;
+    char *home_plugins, *backwards_compat_plugins;
 
     GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH not set");
 
@@ -1329,6 +1570,13 @@ scan_and_update_registry (GstRegistry * default_registry,
     GST_DEBUG ("scanning main plugins %s", PLUGINDIR);
     changed |= gst_registry_scan_path_internal (&context, PLUGINDIR);
 
+    /* finally, look in the /usr/lib backwards-compatibility path. */
+    backwards_compat_plugins = g_build_filename ("/usr/lib",
+        "gstreamer-" GST_MAJORMINOR, NULL);
+    GST_DEBUG ("scanning plugins %s", backwards_compat_plugins);
+    changed |= gst_registry_scan_path_internal (&context, backwards_compat_plugins);
+    g_free (backwards_compat_plugins);
+
 #ifdef G_OS_WIN32
     {
       char *base_dir;
@@ -1425,6 +1673,12 @@ ensure_current_registry (GError ** error)
   }
 
   if (do_update) {
+    const gchar *reuse_env;
+
+    if ((reuse_env = g_getenv ("GST_REGISTRY_REUSE_PLUGIN_SCANNER"))) {
+      /* do reuse for any value different from "no" */
+      __registry_reuse_plugin_scanner = (strcmp (reuse_env, "no") != 0);
+    }
     /* now check registry */
     GST_DEBUG ("Updating registry cache");
     scan_and_update_registry (default_registry, registry_file, TRUE, error);
@@ -1529,3 +1783,22 @@ gst_update_registry (void)
 
   return res;
 }
+
+/**
+ * gst_registry_get_feature_list_cookie:
+ * @registry: the registry
+ *
+ * Returns the registrys feature list cookie. This changes
+ * every time a feature is added or removed from the registry.
+ *
+ * Returns: the feature list cookie.
+ *
+ * Since: 0.10.26
+ */
+guint32
+gst_registry_get_feature_list_cookie (GstRegistry * registry)
+{
+  g_return_val_if_fail (GST_IS_REGISTRY (registry), 0);
+
+  return registry->priv->cookie;
+}