lv2: add registry caching

Use the plugin_cache extra data to avoid introspecting over and over again.
This commit is contained in:
Stefan Sauer 2016-05-15 14:45:15 -07:00
parent da0a3d95e7
commit 1c83ce7a96
7 changed files with 176 additions and 103 deletions

View 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

View 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);
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;

View 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__ */

View 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);
}

View 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);
}

View 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));
}

View 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__ */