#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>
"/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://) */
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;
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");
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;
}
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);
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);
}
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);
}
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;
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",
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);
}
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)));
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)));
}
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;
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;
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));
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));
+}