lv2: add registry caching
authorStefan Sauer <ensonic@users.sf.net>
Sun, 15 May 2016 21:45:15 +0000 (14:45 -0700)
committerStefan Sauer <ensonic@users.sf.net>
Sun, 15 May 2016 21:47:22 +0000 (14:47 -0700)
Use the plugin_cache extra data to avoid introspecting over and over again.

ext/lv2/README
ext/lv2/gstlv2.c
ext/lv2/gstlv2.h
ext/lv2/gstlv2filter.c
ext/lv2/gstlv2source.c
ext/lv2/gstlv2utils.c
ext/lv2/gstlv2utils.h

index f3abb3c..8d0393b 100644 (file)
@@ -33,7 +33,6 @@ gst-launch-1.0 calf-sourceforge-net-plugins-Organ event-in="C-3" name=s ! interl
 
 
 TODO
-* registry cache
 * support http://lv2plug.in/ns/lv2core/#CVPort
   - these ports need a buffer with the property value
   - we should sync, then fill the buffer and connect the port
index 7333187..5210fe0 100644 (file)
@@ -39,7 +39,6 @@
 
 #include <string.h>
 #include "gstlv2.h"
-#include "gstlv2utils.h"
 
 #include <gst/audio/audio-channels.h>
 #include <lv2/lv2plug.in/ns/ext/port-groups/port-groups.h>
@@ -52,24 +51,82 @@ GST_DEBUG_CATEGORY (lv2_debug);
   "/usr/local/lib/lv2" G_SEARCHPATH_SEPARATOR_S \
   LIBDIR "/lv2"
 
-/* search the plugin path
- */
+GstStructure *lv2_meta_all = NULL;
+
+static void
+lv2_plugin_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
+{
+  guint audio_in, audio_out;
+
+  gst_structure_get_uint (lv2_meta, "audio-in", &audio_in);
+  gst_structure_get_uint (lv2_meta, "audio-out", &audio_out);
+
+  if (audio_in == 0) {
+    gst_lv2_source_register_element (plugin, lv2_meta);
+  } else {
+    gst_lv2_filter_register_element (plugin, lv2_meta);
+  }
+}
+
+static void
+lv2_count_ports (const LilvPlugin * lv2plugin, guint * audio_in,
+    guint * audio_out)
+{
+  GHashTable *port_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
+      g_free, NULL);
+  guint i;
+
+  *audio_in = *audio_out = 0;
+  for (i = 0; i < lilv_plugin_get_num_ports (lv2plugin); i++) {
+    const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, i);
+
+    if (lilv_port_is_a (lv2plugin, port, audio_class)) {
+      const gboolean is_input = lilv_port_is_a (lv2plugin, port, input_class);
+      LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
+
+      if (lv2group) {
+        const gchar *uri = lilv_node_as_uri (lv2group);
+
+        if (g_hash_table_contains (port_groups, uri))
+          continue;
+
+        g_hash_table_add (port_groups, g_strdup (uri));
+        lilv_node_free (lv2group);
+      }
+
+      if (is_input)
+        (*audio_in)++;
+      else
+        (*audio_out)++;
+    }
+  }
+  g_hash_table_unref (port_groups);
+}
+
+/* search the plugin path */
 static gboolean
 lv2_plugin_discover (GstPlugin * plugin)
 {
-  guint j, num_sink_pads, num_src_pads;
+  guint audio_in, audio_out;
   LilvIter *i;
   const LilvPlugins *plugins = lilv_world_get_all_plugins (world);
 
   for (i = lilv_plugins_begin (plugins); !lilv_plugins_is_end (plugins, i);
       i = lilv_plugins_next (plugins, i)) {
+    GstStructure *lv2_meta = NULL;
+    GValue value = { 0, };
     const LilvPlugin *lv2plugin = lilv_plugins_get (plugins, i);
     const gchar *plugin_uri, *p;
     gchar *type_name;
-    GHashTable *port_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
-        g_free, NULL);
 
     plugin_uri = lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin));
+
+    /* check if we support the required host features */
+    if (!gst_lv2_check_required_features (lv2plugin)) {
+      GST_FIXME ("lv2 plugin %s needs host features", plugin_uri);
+      continue;
+    }
+
     /* construct the type name from plugin URI */
     if ((p = strstr (plugin_uri, "://"))) {
       /* cut off the protocol (e.g. http://) */
@@ -83,61 +140,47 @@ lv2_plugin_discover (GstPlugin * plugin)
     if (g_type_from_name (type_name))
       goto next;
 
-    /* check if we support the required host features */
-    if (!gst_lv2_check_required_features (lv2plugin)) {
-      GST_FIXME ("lv2 plugin %s needs host features", plugin_uri);
-      goto next;
-    }
-
     /* check if this has any audio ports */
-    num_sink_pads = num_src_pads = 0;
-    for (j = 0; j < lilv_plugin_get_num_ports (lv2plugin); j++) {
-      const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, j);
-
-      if (lilv_port_is_a (lv2plugin, port, audio_class)) {
-        const gboolean is_input = lilv_port_is_a (lv2plugin, port, input_class);
-        LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
-
-        if (lv2group) {
-          const gchar *uri = lilv_node_as_uri (lv2group);
+    lv2_count_ports (lv2plugin, &audio_in, &audio_out);
 
-          if (g_hash_table_contains (port_groups, uri))
-            continue;
-
-          g_hash_table_add (port_groups, g_strdup (uri));
-          lilv_node_free (lv2group);
-        }
-
-        if (is_input)
-          num_sink_pads++;
-        else
-          num_src_pads++;
-      }
-    }
-    if (num_sink_pads == 0 && num_src_pads == 0) {
-      GST_FIXME ("plugin %s has no pads", type_name);
-    } else if (num_sink_pads == 0) {
-      if (num_src_pads != 1) {
+    if (audio_in == 0 && audio_out == 0) {
+      GST_FIXME ("plugin %s has no audio pads", type_name);
+      goto next;
+    } else if (audio_in == 0) {
+      if (audio_out != 1) {
         GST_FIXME ("plugin %s is not a GstBaseSrc (num_src_pads: %d)",
-            type_name, num_src_pads);
+            type_name, audio_out);
         goto next;
       }
-      gst_lv2_source_register_element (plugin, type_name, (gpointer) lv2plugin);
-    } else if (num_src_pads == 0) {
+    } else if (audio_out == 0) {
       GST_FIXME ("plugin %s is a sink element (num_sink_pads: %d"
-          " num_src_pads: %d)", type_name, num_sink_pads, num_src_pads);
+          " num_src_pads: %d)", type_name, audio_in, audio_out);
+      goto next;
     } else {
-      if (num_sink_pads != 1 || num_src_pads != 1) {
+      if (audio_in != 1 || audio_out != 1) {
         GST_FIXME ("plugin %s is not a GstAudioFilter (num_sink_pads: %d"
-            " num_src_pads: %d)", type_name, num_sink_pads, num_src_pads);
+            " num_src_pads: %d)", type_name, audio_in, audio_out);
         goto next;
       }
-      gst_lv2_filter_register_element (plugin, type_name, (gpointer) lv2plugin);
     }
 
+    lv2_meta = gst_structure_new_empty ("lv2");
+    gst_structure_set (lv2_meta,
+        "element-uri", G_TYPE_STRING, plugin_uri,
+        "element-type-name", G_TYPE_STRING, type_name,
+        "audio-in", G_TYPE_UINT, audio_in,
+        "audio-out", G_TYPE_UINT, audio_out, NULL);
+
+    g_value_init (&value, GST_TYPE_STRUCTURE);
+    g_value_set_boxed (&value, lv2_meta);
+    gst_structure_set_value (lv2_meta_all, type_name, &value);
+    g_value_unset (&value);
+
+    // don't free type_name
+    continue;
+
   next:
     g_free (type_name);
-    g_hash_table_unref (port_groups);
   }
 
   return TRUE;
@@ -146,6 +189,9 @@ lv2_plugin_discover (GstPlugin * plugin)
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
+  gboolean res = FALSE;
+  gint n = 0;
+
   GST_DEBUG_CATEGORY_INIT (lv2_debug, "lv2",
       GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD, "LV2");
 
@@ -184,17 +230,47 @@ plugin_init (GstPlugin * plugin)
   gst_plugin_add_dependency_simple (plugin,
       "LV2_PATH", GST_LV2_DEFAULT_PATH, NULL, GST_PLUGIN_DEPENDENCY_FLAG_NONE);
 
-  descriptor_quark = g_quark_from_static_string ("lilv-plugin");
-
   /* ensure GstAudioChannelPosition type is registered */
   if (!gst_audio_channel_position_get_type ())
     return FALSE;
 
-  if (!lv2_plugin_discover (plugin)) {
-    GST_WARNING ("no lv2 plugins found, check LV2_PATH");
+  lv2_meta_all = (GstStructure *) gst_plugin_get_cache_data (plugin);
+  if (lv2_meta_all) {
+    n = gst_structure_n_fields (lv2_meta_all);
   }
+  GST_INFO_OBJECT (plugin, "%d entries in cache", n);
+  if (!n) {
+    lv2_meta_all = gst_structure_new_empty ("lv2");
+    if ((res = lv2_plugin_discover (plugin))) {
+      n = gst_structure_n_fields (lv2_meta_all);
+      GST_INFO_OBJECT (plugin, "%d entries after scanning", n);
+      gst_plugin_set_cache_data (plugin, lv2_meta_all);
+    }
+  } else {
+    res = TRUE;
+  }
+
+  if (n) {
+    gint i;
+    const gchar *name;
+    const GValue *value;
 
+    GST_INFO_OBJECT (plugin, "register types");
 
+    for (i = 0; i < n; i++) {
+      name = gst_structure_nth_field_name (lv2_meta_all, i);
+      value = gst_structure_get_value (lv2_meta_all, name);
+      if (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE) {
+        GstStructure *lv2_meta = g_value_get_boxed (value);
+
+        lv2_plugin_register_element (plugin, lv2_meta);
+      }
+    }
+  }
+
+  if (!res) {
+    GST_WARNING_OBJECT (plugin, "no lv2 plugins found, check LV2_PATH");
+  }
 
   /* we don't want to fail, even if there are no elements registered */
   return TRUE;
index d2a9c44..5357369 100644 (file)
@@ -26,6 +26,8 @@
 #include <lilv/lilv.h>
 #include <gst/gst.h>
 
+#include "gstlv2utils.h"
+
 LilvWorld *world;
 LilvNode *audio_class;
 LilvNode *control_class;
@@ -50,12 +52,10 @@ LilvNode *center_right_role;
 LilvNode *side_left_role;
 LilvNode *side_right_role;
 
-GQuark descriptor_quark;
+GstStructure *lv2_meta_all;
 
-gboolean gst_lv2_filter_register_element (GstPlugin *plugin,
-                                          const gchar *type_name,
-                                          gpointer *lv2plugin);
-gboolean gst_lv2_source_register_element (GstPlugin *plugin,
-                                          const gchar *type_name,
-                                          gpointer *lv2plugin);
+void gst_lv2_filter_register_element (GstPlugin *plugin,
+                                      GstStructure * lv2_meta);
+void gst_lv2_source_register_element (GstPlugin *plugin,
+                                      GstStructure * lv2_meta);
 #endif /* __GST_LV2_H__ */
index 4dcc7ad..fe1f58e 100644 (file)
@@ -410,7 +410,7 @@ gst_lv2_filter_base_finalize (GstLV2FilterClass * lv2_class)
 }
 
 static void
-gst_lv2_filter_class_init (GstLV2FilterClass * klass, LilvPlugin * lv2plugin)
+gst_lv2_filter_class_init (GstLV2FilterClass * klass)
 {
   GObjectClass *gobject_class = (GObjectClass *) klass;
   GstBaseTransformClass *transform_class = GST_BASE_TRANSFORM_CLASS (klass);
@@ -428,8 +428,6 @@ gst_lv2_filter_class_init (GstLV2FilterClass * klass, LilvPlugin * lv2plugin)
   transform_class->transform = gst_lv2_filter_transform;
   transform_class->transform_ip = gst_lv2_filter_transform_ip;
 
-  klass->lv2.plugin = lv2plugin;
-
   gst_lv2_class_install_properties (&klass->lv2, gobject_class, 1);
 }
 
@@ -442,34 +440,24 @@ gst_lv2_filter_init (GstLV2Filter * self, GstLV2FilterClass * klass)
     gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE);
 }
 
-gboolean
-gst_lv2_filter_register_element (GstPlugin * plugin, const gchar * type_name,
-    gpointer * lv2plugin)
+void
+gst_lv2_filter_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
 {
-  GType type;
-  GTypeInfo typeinfo = {
+  GTypeInfo info = {
     sizeof (GstLV2FilterClass),
     (GBaseInitFunc) gst_lv2_filter_base_init,
     (GBaseFinalizeFunc) gst_lv2_filter_base_finalize,
     (GClassInitFunc) gst_lv2_filter_class_init,
     NULL,
-    lv2plugin,
+    NULL,
     sizeof (GstLV2Filter),
     0,
     (GInstanceInitFunc) gst_lv2_filter_init,
   };
 
   /* create the type */
-  type =
-      g_type_register_static (GST_TYPE_AUDIO_FILTER, type_name, &typeinfo, 0);
+  gst_lv2_register_element (plugin, GST_TYPE_AUDIO_FILTER, &info, lv2_meta);
 
   if (!parent_class)
     parent_class = g_type_class_ref (GST_TYPE_AUDIO_FILTER);
-
-
-  /* FIXME: not needed anymore when we can add pad templates, etc in class_init
-   * as class_data contains the Descriptor too */
-  g_type_set_qdata (type, descriptor_quark, lv2plugin);
-
-  return gst_element_register (plugin, type_name, GST_RANK_NONE, type);
 }
index b464901..8d3251b 100644 (file)
@@ -557,9 +557,8 @@ gst_lv2_source_base_finalize (GstLV2SourceClass * lv2_class)
   gst_lv2_class_finalize (&lv2_class->lv2);
 }
 
-
 static void
-gst_lv2_source_class_init (GstLV2SourceClass * klass, LilvPlugin * lv2plugin)
+gst_lv2_source_class_init (GstLV2SourceClass * klass)
 {
   GObjectClass *gobject_class = (GObjectClass *) klass;
   GstBaseSrcClass *src_class = (GstBaseSrcClass *) klass;
@@ -581,8 +580,6 @@ gst_lv2_source_class_init (GstLV2SourceClass * klass, LilvPlugin * lv2plugin)
   src_class->stop = gst_lv2_source_stop;
   src_class->fill = gst_lv2_source_fill;
 
-  klass->lv2.plugin = lv2plugin;
-
   g_object_class_install_property (gobject_class,
       GST_LV2_SOURCE_PROP_SAMPLES_PER_BUFFER,
       g_param_spec_int ("samplesperbuffer", "Samples per buffer",
@@ -629,33 +626,23 @@ gst_lv2_source_init (GstLV2Source * self, GstLV2SourceClass * klass)
   self->generate_samples_per_buffer = self->samples_per_buffer;
 }
 
-gboolean
-gst_lv2_source_register_element (GstPlugin * plugin, const gchar * type_name,
-    gpointer * lv2plugin)
+void
+gst_lv2_source_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
 {
-  GType type;
-  GTypeInfo typeinfo = {
+  GTypeInfo info = {
     sizeof (GstLV2SourceClass),
     (GBaseInitFunc) gst_lv2_source_base_init,
     (GBaseFinalizeFunc) gst_lv2_source_base_finalize,
     (GClassInitFunc) gst_lv2_source_class_init,
     NULL,
-    lv2plugin,
+    NULL,
     sizeof (GstLV2Source),
     0,
     (GInstanceInitFunc) gst_lv2_source_init,
   };
 
-  /* create the type */
-  type = g_type_register_static (GST_TYPE_BASE_SRC, type_name, &typeinfo, 0);
+  gst_lv2_register_element (plugin, GST_TYPE_BASE_SRC, &info, lv2_meta);
 
   if (!parent_class)
     parent_class = g_type_class_ref (GST_TYPE_BASE_SRC);
-
-
-  /* FIXME: not needed anymore when we can add pad templates, etc in class_init
-   * as class_data contains the Descriptor too */
-  g_type_set_qdata (type, descriptor_quark, lv2plugin);
-
-  return gst_element_register (plugin, type_name, GST_RANK_NONE, type);
 }
index 7b841ed..407f6e3 100644 (file)
@@ -279,7 +279,7 @@ static gchar *
 gst_lv2_class_get_param_name (GstLV2Class * klass, GObjectClass * object_class,
     const LilvPort * port)
 {
-  LilvPlugin *lv2plugin = klass->plugin;
+  const LilvPlugin *lv2plugin = klass->plugin;
   gchar *ret;
 
   ret = g_strdup (lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
@@ -316,7 +316,7 @@ gst_lv2_class_get_param_name (GstLV2Class * klass, GObjectClass * object_class,
 static gchar *
 gst_lv2_class_get_param_nick (GstLV2Class * klass, const LilvPort * port)
 {
-  LilvPlugin *lv2plugin = klass->plugin;
+  const LilvPlugin *lv2plugin = klass->plugin;
 
   return g_strdup (lilv_node_as_string (lilv_port_get_name (lv2plugin, port)));
 }
@@ -325,7 +325,7 @@ static GParamSpec *
 gst_lv2_class_get_param_spec (GstLV2Class * klass, GObjectClass * object_class,
     gint portnum)
 {
-  LilvPlugin *lv2plugin = klass->plugin;
+  const LilvPlugin *lv2plugin = klass->plugin;
   const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, portnum);
   LilvNode *lv2def, *lv2min, *lv2max;
   GParamSpec *ret;
@@ -415,7 +415,7 @@ void
 gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
     GstElementClass * elem_class, const gchar * lv2_class_tags)
 {
-  LilvPlugin *lv2plugin = lv2_class->plugin;
+  const LilvPlugin *lv2plugin = lv2_class->plugin;
   LilvNode *val;
   const LilvPluginClass *lv2plugin_class;
   const LilvNode *cval;
@@ -454,16 +454,26 @@ gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
 void
 gst_lv2_class_init (GstLV2Class * lv2_class, GType type)
 {
-  LilvPlugin *lv2plugin;
+  const GValue *value =
+      gst_structure_get_value (lv2_meta_all, g_type_name (type));
+  GstStructure *lv2_meta = g_value_get_boxed (value);
+  const LilvPlugin *lv2plugin;
   /* FIXME Handle channels positionning
    * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */
   guint j, in_pad_index = 0, out_pad_index = 0;
+  const LilvPlugins *plugins = lilv_world_get_all_plugins (world);
+  LilvNode *plugin_uri;
+  const gchar *element_uri;
 
   GST_DEBUG ("LV2 initializing class");
 
-  lv2plugin = (LilvPlugin *) g_type_get_qdata (type, descriptor_quark);
+  element_uri = gst_structure_get_string (lv2_meta, "element-uri");
+  plugin_uri = lilv_new_uri (world, element_uri);
+  g_assert (plugin_uri);
+  lv2plugin = lilv_plugins_get_by_uri (plugins, plugin_uri);
   g_assert (lv2plugin);
   lv2_class->plugin = lv2plugin;
+  lilv_node_free (plugin_uri);
 
   lv2_class->in_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
   lv2_class->out_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
@@ -541,3 +551,14 @@ gst_lv2_class_finalize (GstLV2Class * lv2_class)
   g_array_free (lv2_class->control_out_ports, TRUE);
   lv2_class->control_out_ports = NULL;
 }
+
+void
+gst_lv2_register_element (GstPlugin * plugin, GType parent_type,
+    const GTypeInfo * info, GstStructure * lv2_meta)
+{
+  const gchar *type_name =
+      gst_structure_get_string (lv2_meta, "element-type-name");
+
+  gst_element_register (plugin, type_name, GST_RANK_NONE,
+      g_type_register_static (parent_type, type_name, info, 0));
+}
index b012ec3..6ff6482 100644 (file)
@@ -77,7 +77,7 @@ struct _GstLV2Class
 {
   guint properties;
 
-  LilvPlugin *plugin;
+  const LilvPlugin *plugin;
 
   GstLV2Group in_group; /**< Array of GstLV2Group */
   GstLV2Group out_group; /**< Array of GstLV2Group */
@@ -106,6 +106,8 @@ void gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
 void gst_lv2_class_init (GstLV2Class * lv2_class, GType type);
 void gst_lv2_class_finalize (GstLV2Class * lv2_class);
 
+void gst_lv2_register_element (GstPlugin * plugin, GType parent_type,
+    const GTypeInfo * info, GstStructure * lv2_meta);
 G_END_DECLS
 
 #endif /* __GST_LV2_UTILS_H__ */