• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
4  *               2003 Andy Wingo <wingo at pobox.com>
5  *               2016 Thibault Saunier <thibault.saunier@collabora.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:element-lv2
25  * @title: lv2
26  * @short_description: bridge for LV2.
27  *
28  * LV2 is a standard for plugins and matching host applications,
29  * mainly targeted at audio processing and generation.  It is intended as
30  * a successor to LADSPA (Linux Audio Developer's Simple Plugin API).
31  *
32  * The LV2 element is a bridge for plugins using the
33  * [LV2](http://www.lv2plug.in/) API.  It scans all installed LV2 plugins and
34  * registers them as gstreamer elements.
35  */
36 
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40 
41 #include <string.h>
42 #include "gstlv2.h"
43 
44 #include <gst/audio/audio-channels.h>
45 #include <lv2/lv2plug.in/ns/ext/port-groups/port-groups.h>
46 #include "lv2/lv2plug.in/ns/ext/event/event.h"
47 #include "lv2/lv2plug.in/ns/ext/presets/presets.h"
48 #include "lv2/lv2plug.in/ns/ext/state/state.h"
49 
50 GST_DEBUG_CATEGORY (lv2_debug);
51 #define GST_CAT_DEFAULT lv2_debug
52 
53 #if defined (G_OS_WIN32)
54 #define GST_LV2_ENVVARS "APPDATA/LV2:COMMONPROGRAMFILES/LV2"
55 #define GST_LV2_DEFAULT_PATH NULL
56 #elif defined (HAVE_OSX)
57 #define GST_LV2_ENVVARS "HOME/Library/Audio/Plug-Ins/LV2:HOME/.lv2"
58 #define GST_LV2_DEFAULT_PATH \
59   "/usr/local/lib/lv2:/usr/lib/lv2:/Library/Audio/Plug-Ins/LV2"
60 #elif defined (G_OS_UNIX)
61 #define GST_LV2_ENVVARS "HOME/.lv2"
62 #define GST_LV2_DEFAULT_PATH \
63   "/usr/lib/lv2:" \
64   "/usr/lib64/lv2:" \
65   "/usr/local/lib/lv2:" \
66   "/usr/local/lib64/lv2:" \
67   LIBDIR "/lv2"
68 #else
69 #error "Unsupported OS"
70 #endif
71 
72 LilvWorld *gst_lv2_world_node = NULL;
73 LilvNode *gst_lv2_audio_node = NULL;
74 LilvNode *gst_lv2_control_node = NULL;
75 LilvNode *gst_lv2_cv_node = NULL;
76 LilvNode *gst_lv2_event_node = NULL;
77 LilvNode *gst_lv2_input_node = NULL;
78 LilvNode *gst_lv2_output_node = NULL;
79 LilvNode *gst_lv2_preset_node = NULL;
80 LilvNode *gst_lv2_state_iface_node = NULL;
81 LilvNode *gst_lv2_state_uri_node = NULL;
82 
83 LilvNode *gst_lv2_integer_prop_node = NULL;
84 LilvNode *gst_lv2_toggled_prop_node = NULL;
85 LilvNode *gst_lv2_designation_pred_node = NULL;
86 LilvNode *gst_lv2_in_place_broken_pred_node = NULL;
87 LilvNode *gst_lv2_optional_pred_node = NULL;
88 LilvNode *gst_lv2_group_pred_node = NULL;
89 LilvNode *gst_lv2_supports_event_pred_node = NULL;
90 LilvNode *gst_lv2_label_pred_node = NULL;
91 
92 LilvNode *gst_lv2_center_role_node = NULL;
93 LilvNode *gst_lv2_left_role_node = NULL;
94 LilvNode *gst_lv2_right_role_node = NULL;
95 LilvNode *gst_lv2_rear_center_role_node = NULL;
96 LilvNode *gst_lv2_rear_left_role_node = NULL;
97 LilvNode *gst_lv2_rear_right_role_node = NULL;
98 LilvNode *gst_lv2_lfe_role_node = NULL;
99 LilvNode *gst_lv2_center_left_role_node = NULL;
100 LilvNode *gst_lv2_center_right_role_node = NULL;
101 LilvNode *gst_lv2_side_left_role_node = NULL;
102 LilvNode *gst_lv2_side_right_role_node = NULL;
103 
104 GstStructure *lv2_meta_all = NULL;
105 
106 static void
lv2_plugin_register_element(GstPlugin * plugin,GstStructure * lv2_meta)107 lv2_plugin_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
108 {
109   guint audio_in, audio_out;
110 
111   gst_structure_get_uint (lv2_meta, "audio-in", &audio_in);
112   gst_structure_get_uint (lv2_meta, "audio-out", &audio_out);
113 
114   if (audio_in == 0) {
115     gst_lv2_source_register_element (plugin, lv2_meta);
116   } else {
117     gst_lv2_filter_register_element (plugin, lv2_meta);
118   }
119 }
120 
121 static void
lv2_count_ports(const LilvPlugin * lv2plugin,guint * audio_in,guint * audio_out,guint * control)122 lv2_count_ports (const LilvPlugin * lv2plugin, guint * audio_in,
123     guint * audio_out, guint * control)
124 {
125   GHashTable *port_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
126       g_free, NULL);
127   guint i;
128 
129   *audio_in = *audio_out = *control = 0;
130   for (i = 0; i < lilv_plugin_get_num_ports (lv2plugin); i++) {
131     const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, i);
132 
133     if (lilv_port_is_a (lv2plugin, port, gst_lv2_audio_node)) {
134       const gboolean is_input =
135           lilv_port_is_a (lv2plugin, port, gst_lv2_input_node);
136       LilvNodes *lv2group =
137           lilv_port_get (lv2plugin, port, gst_lv2_group_pred_node);
138 
139       if (lv2group) {
140         const gchar *uri = lilv_node_as_uri (lv2group);
141 
142         if (g_hash_table_contains (port_groups, uri))
143           continue;
144 
145         g_hash_table_add (port_groups, g_strdup (uri));
146         lilv_node_free (lv2group);
147       }
148 
149       if (is_input)
150         (*audio_in)++;
151       else
152         (*audio_out)++;
153     } else if (lilv_port_is_a (lv2plugin, port, gst_lv2_control_node) ||
154         lilv_port_is_a (lv2plugin, port, gst_lv2_cv_node)) {
155       (*control)++;
156     }
157   }
158   g_hash_table_unref (port_groups);
159 }
160 
161 /* search the plugin path */
162 static gboolean
lv2_plugin_discover(GstPlugin * plugin)163 lv2_plugin_discover (GstPlugin * plugin)
164 {
165   guint audio_in, audio_out, control;
166   LilvIter *i;
167   const LilvPlugins *plugins = lilv_world_get_all_plugins (gst_lv2_world_node);
168 
169   for (i = lilv_plugins_begin (plugins); !lilv_plugins_is_end (plugins, i);
170       i = lilv_plugins_next (plugins, i)) {
171     GstStructure *lv2_meta = NULL;
172     GValue value = { 0, };
173     const LilvPlugin *lv2plugin = lilv_plugins_get (plugins, i);
174     const gchar *plugin_uri, *p;
175     gchar *type_name;
176     gboolean can_do_presets;
177 
178     plugin_uri = lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin));
179 
180     /* check if we support the required host features */
181     if (!gst_lv2_check_required_features (lv2plugin)) {
182       GST_FIXME ("lv2 plugin %s needs host features", plugin_uri);
183       continue;
184     }
185 
186     /* construct the type name from plugin URI */
187     if ((p = strstr (plugin_uri, "://"))) {
188       /* cut off the protocol (e.g. http://) */
189       type_name = g_strdup (&p[3]);
190     } else {
191       type_name = g_strdup (plugin_uri);
192     }
193     g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
194 
195     /* if it's already registered, drop it */
196     if (g_type_from_name (type_name))
197       goto next;
198 
199     /* check if this has any audio ports */
200     lv2_count_ports (lv2plugin, &audio_in, &audio_out, &control);
201 
202     if (audio_in == 0 && audio_out == 0) {
203       GST_FIXME ("plugin %s has no audio pads", type_name);
204       goto next;
205     } else if (audio_in == 0) {
206       if (audio_out != 1) {
207         GST_FIXME ("plugin %s is not a GstBaseSrc (num_src_pads: %d)",
208             type_name, audio_out);
209         goto next;
210       }
211     } else if (audio_out == 0) {
212       GST_FIXME ("plugin %s is a sink element (num_sink_pads: %d"
213           " num_src_pads: %d)", type_name, audio_in, audio_out);
214       goto next;
215     } else {
216       if (audio_in != 1 || audio_out != 1) {
217         GST_FIXME ("plugin %s is not a GstAudioFilter (num_sink_pads: %d"
218             " num_src_pads: %d)", type_name, audio_in, audio_out);
219         goto next;
220       }
221     }
222 
223     /* check supported extensions */
224     can_do_presets =
225         lilv_plugin_has_extension_data (lv2plugin, gst_lv2_state_iface_node)
226         || lilv_plugin_has_feature (lv2plugin, gst_lv2_state_uri_node)
227         || (control > 0);
228     GST_INFO ("plugin %s can%s do presets", type_name,
229         (can_do_presets ? "" : "'t"));
230 
231     lv2_meta = gst_structure_new ("lv2",
232         "element-uri", G_TYPE_STRING, plugin_uri,
233         "element-type-name", G_TYPE_STRING, type_name,
234         "audio-in", G_TYPE_UINT, audio_in,
235         "audio-out", G_TYPE_UINT, audio_out,
236         "can-do-presets", G_TYPE_BOOLEAN, can_do_presets, NULL);
237 
238     g_value_init (&value, GST_TYPE_STRUCTURE);
239     g_value_set_boxed (&value, lv2_meta);
240     gst_structure_set_value (lv2_meta_all, type_name, &value);
241     g_value_unset (&value);
242 
243     // don't free type_name
244     continue;
245 
246   next:
247     g_free (type_name);
248   }
249 
250   return TRUE;
251 }
252 
253 static gboolean
plugin_init(GstPlugin * plugin)254 plugin_init (GstPlugin * plugin)
255 {
256   gboolean res = FALSE;
257   gint n = 0;
258 
259   GST_DEBUG_CATEGORY_INIT (lv2_debug, "lv2",
260       GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD, "LV2");
261 
262   gst_lv2_world_node = lilv_world_new ();
263   lilv_world_load_all (gst_lv2_world_node);
264   gst_lv2_host_init ();
265 
266 /* have been added after lilv-0.22.0, which is the last release */
267 #ifndef LILV_URI_ATOM_PORT
268 #define LILV_URI_ATOM_PORT    "http://lv2plug.in/ns/ext/atom#AtomPort"
269 #endif
270 #ifndef LILV_URI_CV_PORT
271 #define LILV_URI_CV_PORT      "http://lv2plug.in/ns/lv2core#CVPort"
272 #endif
273 
274   gst_lv2_audio_node = lilv_new_uri (gst_lv2_world_node, LILV_URI_AUDIO_PORT);
275   gst_lv2_control_node =
276       lilv_new_uri (gst_lv2_world_node, LILV_URI_CONTROL_PORT);
277   gst_lv2_cv_node = lilv_new_uri (gst_lv2_world_node, LILV_URI_CV_PORT);
278   gst_lv2_event_node = lilv_new_uri (gst_lv2_world_node, LILV_URI_EVENT_PORT);
279   gst_lv2_input_node = lilv_new_uri (gst_lv2_world_node, LILV_URI_INPUT_PORT);
280   gst_lv2_output_node = lilv_new_uri (gst_lv2_world_node, LILV_URI_OUTPUT_PORT);
281   gst_lv2_preset_node = lilv_new_uri (gst_lv2_world_node, LV2_PRESETS__Preset);
282   gst_lv2_state_iface_node =
283       lilv_new_uri (gst_lv2_world_node, LV2_STATE__interface);
284   gst_lv2_state_uri_node = lilv_new_uri (gst_lv2_world_node, LV2_STATE_URI);
285 
286   gst_lv2_integer_prop_node =
287       lilv_new_uri (gst_lv2_world_node, LV2_CORE__integer);
288   gst_lv2_toggled_prop_node =
289       lilv_new_uri (gst_lv2_world_node, LV2_CORE__toggled);
290   gst_lv2_designation_pred_node =
291       lilv_new_uri (gst_lv2_world_node, LV2_CORE__designation);
292   gst_lv2_in_place_broken_pred_node =
293       lilv_new_uri (gst_lv2_world_node, LV2_CORE__inPlaceBroken);
294   gst_lv2_optional_pred_node =
295       lilv_new_uri (gst_lv2_world_node, LV2_CORE__optionalFeature);
296   gst_lv2_group_pred_node =
297       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__group);
298   gst_lv2_supports_event_pred_node =
299       lilv_new_uri (gst_lv2_world_node, LV2_EVENT__supportsEvent);
300   gst_lv2_label_pred_node =
301       lilv_new_uri (gst_lv2_world_node, LILV_NS_RDFS "label");
302 
303   gst_lv2_center_role_node =
304       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__center);
305   gst_lv2_left_role_node =
306       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__left);
307   gst_lv2_right_role_node =
308       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__right);
309   gst_lv2_rear_center_role_node =
310       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__rearCenter);
311   gst_lv2_rear_left_role_node =
312       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__rearLeft);
313   gst_lv2_rear_right_role_node =
314       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__rearRight);
315   gst_lv2_lfe_role_node =
316       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__lowFrequencyEffects);
317   gst_lv2_center_left_role_node =
318       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__centerLeft);
319   gst_lv2_center_right_role_node =
320       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__centerRight);
321   gst_lv2_side_left_role_node =
322       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__sideLeft);
323   gst_lv2_side_right_role_node =
324       lilv_new_uri (gst_lv2_world_node, LV2_PORT_GROUPS__sideRight);
325 
326   gst_plugin_add_dependency_simple (plugin,
327       "LV2_PATH:" GST_LV2_ENVVARS, GST_LV2_DEFAULT_PATH, NULL,
328       GST_PLUGIN_DEPENDENCY_FLAG_RECURSE);
329 
330   /* ensure GstAudioChannelPosition type is registered */
331   if (!gst_audio_channel_position_get_type ())
332     return FALSE;
333 
334   lv2_meta_all = (GstStructure *) gst_plugin_get_cache_data (plugin);
335   if (lv2_meta_all) {
336     n = gst_structure_n_fields (lv2_meta_all);
337   }
338   GST_INFO_OBJECT (plugin, "%d entries in cache", n);
339   if (!n) {
340     lv2_meta_all = gst_structure_new_empty ("lv2");
341     if ((res = lv2_plugin_discover (plugin))) {
342       n = gst_structure_n_fields (lv2_meta_all);
343       GST_INFO_OBJECT (plugin, "%d entries after scanning", n);
344       gst_plugin_set_cache_data (plugin, lv2_meta_all);
345     }
346   } else {
347     res = TRUE;
348   }
349 
350   if (n) {
351     gint i;
352     const gchar *name;
353     const GValue *value;
354 
355     GST_INFO_OBJECT (plugin, "register types");
356 
357     for (i = 0; i < n; i++) {
358       name = gst_structure_nth_field_name (lv2_meta_all, i);
359       value = gst_structure_get_value (lv2_meta_all, name);
360       if (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE) {
361         GstStructure *lv2_meta = g_value_get_boxed (value);
362 
363         lv2_plugin_register_element (plugin, lv2_meta);
364       }
365     }
366   }
367 
368   if (!res) {
369     GST_WARNING_OBJECT (plugin, "no lv2 plugins found, check LV2_PATH");
370   }
371 
372   /* we don't want to fail, even if there are no elements registered */
373   return TRUE;
374 }
375 
376 #ifdef __GNUC__
377 __attribute__ ((destructor))
378 #endif
plugin_cleanup(GstPlugin * plugin)379      static void plugin_cleanup (GstPlugin * plugin)
380 {
381   lilv_node_free (gst_lv2_audio_node);
382   lilv_node_free (gst_lv2_control_node);
383   lilv_node_free (gst_lv2_cv_node);
384   lilv_node_free (gst_lv2_event_node);
385   lilv_node_free (gst_lv2_input_node);
386   lilv_node_free (gst_lv2_output_node);
387   lilv_node_free (gst_lv2_preset_node);
388   lilv_node_free (gst_lv2_state_iface_node);
389   lilv_node_free (gst_lv2_state_uri_node);
390 
391   lilv_node_free (gst_lv2_integer_prop_node);
392   lilv_node_free (gst_lv2_toggled_prop_node);
393   lilv_node_free (gst_lv2_designation_pred_node);
394   lilv_node_free (gst_lv2_in_place_broken_pred_node);
395   lilv_node_free (gst_lv2_optional_pred_node);
396   lilv_node_free (gst_lv2_group_pred_node);
397   lilv_node_free (gst_lv2_supports_event_pred_node);
398   lilv_node_free (gst_lv2_label_pred_node);
399 
400   lilv_node_free (gst_lv2_center_role_node);
401   lilv_node_free (gst_lv2_left_role_node);
402   lilv_node_free (gst_lv2_right_role_node);
403   lilv_node_free (gst_lv2_rear_center_role_node);
404   lilv_node_free (gst_lv2_rear_left_role_node);
405   lilv_node_free (gst_lv2_rear_right_role_node);
406   lilv_node_free (gst_lv2_lfe_role_node);
407   lilv_node_free (gst_lv2_center_left_role_node);
408   lilv_node_free (gst_lv2_center_right_role_node);
409   lilv_node_free (gst_lv2_side_left_role_node);
410   lilv_node_free (gst_lv2_side_right_role_node);
411 
412   lilv_world_free (gst_lv2_world_node);
413 }
414 
415 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
416     GST_VERSION_MINOR,
417     lv2,
418     "All LV2 plugins",
419     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
420