diff --git a/ext/lv2/README b/ext/lv2/README index f3abb3c184..8d0393b18f 100644 --- a/ext/lv2/README +++ b/ext/lv2/README @@ -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 diff --git a/ext/lv2/gstlv2.c b/ext/lv2/gstlv2.c index 7333187625..5210fe0fea 100644 --- a/ext/lv2/gstlv2.c +++ b/ext/lv2/gstlv2.c @@ -39,7 +39,6 @@ #include #include "gstlv2.h" -#include "gstlv2utils.h" #include #include @@ -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); + lv2_count_ports (lv2plugin, &audio_in, &audio_out); - 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) - 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; diff --git a/ext/lv2/gstlv2.h b/ext/lv2/gstlv2.h index d2a9c441c1..5357369790 100644 --- a/ext/lv2/gstlv2.h +++ b/ext/lv2/gstlv2.h @@ -26,6 +26,8 @@ #include #include +#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__ */ diff --git a/ext/lv2/gstlv2filter.c b/ext/lv2/gstlv2filter.c index 4dcc7ad289..fe1f58ed5b 100644 --- a/ext/lv2/gstlv2filter.c +++ b/ext/lv2/gstlv2filter.c @@ -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); } diff --git a/ext/lv2/gstlv2source.c b/ext/lv2/gstlv2source.c index b464901e09..8d3251b70b 100644 --- a/ext/lv2/gstlv2source.c +++ b/ext/lv2/gstlv2source.c @@ -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); } diff --git a/ext/lv2/gstlv2utils.c b/ext/lv2/gstlv2utils.c index 7b841eddf1..407f6e3f3f 100644 --- a/ext/lv2/gstlv2utils.c +++ b/ext/lv2/gstlv2utils.c @@ -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)); +} diff --git a/ext/lv2/gstlv2utils.h b/ext/lv2/gstlv2utils.h index b012ec376b..6ff648271a 100644 --- a/ext/lv2/gstlv2utils.h +++ b/ext/lv2/gstlv2utils.h @@ -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__ */