NativeVampPluginFactory.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006-2016 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
17 #include "PluginIdentifier.h"
18 
19 #include <vamp-hostsdk/PluginHostAdapter.h>
20 #include <vamp-hostsdk/PluginWrapper.h>
21 
22 #include "system/System.h"
23 
24 #include "PluginScan.h"
25 
26 #include <QDir>
27 #include <QFile>
28 #include <QFileInfo>
29 #include <QTextStream>
30 
31 #include <iostream>
32 
33 #include "base/Profiler.h"
34 
35 #include <QMutex>
36 #include <QMutexLocker>
37 
38 using namespace std;
39 
40 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1
41 
42 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper {
43 public:
44  PluginDeletionNotifyAdapter(Vamp::Plugin *plugin,
45  NativeVampPluginFactory *factory) :
46  PluginWrapper(plugin), m_factory(factory) { }
47  ~PluginDeletionNotifyAdapter() override;
48 protected:
50 };
51 
53 {
54  // see notes in vamp-sdk/hostext/PluginLoader.cpp from which this is drawn
55  Vamp::Plugin *p = m_plugin;
56 
57 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
58  SVCERR << "PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter("
59  << this << " for plugin " << p << ")" << endl;
60 #endif
61 
62  delete m_plugin;
63  m_plugin = nullptr;
64  // acceptable use after free here, as pluginDeleted uses p only as
65  // pointer key and does not deref it
66  if (m_factory) m_factory->pluginDeleted(p);
67 }
68 
69 vector<QString>
71 {
72  if (!m_pluginPath.empty()) return m_pluginPath;
73 
74  vector<string> p = Vamp::PluginHostAdapter::getPluginPath();
75  for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str());
76  return m_pluginPath;
77 }
78 
79 static
80 QList<PluginScan::Candidate>
82 {
83 #ifdef HAVE_PLUGIN_CHECKER_HELPER
86 #else
87  auto path = Vamp::PluginHostAdapter::getPluginPath();
88  QList<PluginScan::Candidate> candidates;
89  for (string dirname: path) {
90  SVDEBUG << "NativeVampPluginFactory: scanning directory myself: "
91  << dirname << endl;
92 #if defined(_WIN32)
93 #define PLUGIN_GLOB "*.dll"
94 #elif defined(__APPLE__)
95 #define PLUGIN_GLOB "*.dylib *.so"
96 #else
97 #define PLUGIN_GLOB "*.so"
98 #endif
99  QDir dir(dirname.c_str(), PLUGIN_GLOB,
100  QDir::Name | QDir::IgnoreCase,
101  QDir::Files | QDir::Readable);
102 
103  for (unsigned int i = 0; i < dir.count(); ++i) {
104  QString libpath = dir.filePath(dir[i]);
105  candidates.push_back({ libpath, "" });
106  }
107  }
108 
109  return candidates;
110 #endif
111 }
112 
113 vector<QString>
115 {
116  Profiler profiler("NativeVampPluginFactory::getPluginIdentifiers");
117 
118  QMutexLocker locker(&m_mutex);
119 
120  if (!m_identifiers.empty()) {
121  return m_identifiers;
122  }
123 
124  auto candidates = getCandidateLibraries();
125 
126  SVDEBUG << "INFO: Have " << candidates.size() << " candidate Vamp plugin libraries" << endl;
127 
128  for (auto candidate : candidates) {
129 
130  QString libpath = candidate.libraryPath;
131 
132  SVDEBUG << "INFO: Considering candidate Vamp plugin library " << libpath << endl;
133 
134  void *libraryHandle = DLOPEN(libpath, RTLD_LAZY | RTLD_LOCAL);
135 
136  if (!libraryHandle) {
137  SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to load library " << libpath << ": " << DLERROR() << endl;
138  continue;
139  }
140 
141  VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
142  DLSYM(libraryHandle, "vampGetPluginDescriptor");
143 
144  if (!fn) {
145  SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: No descriptor function in " << libpath << endl;
146  if (DLCLOSE(libraryHandle) != 0) {
147  SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << libpath << endl;
148  }
149  continue;
150  }
151 
152 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
153  SVCERR << "NativeVampPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl;
154 #endif
155 
156  const VampPluginDescriptor *descriptor = nullptr;
157  int index = 0;
158 
159  map<string, int> known;
160  bool ok = true;
161 
162  while ((descriptor = fn(VAMP_API_VERSION, index))) {
163 
164  if (known.find(descriptor->identifier) != known.end()) {
165  SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Plugin library "
166  << libpath
167  << " returns the same plugin identifier \""
168  << descriptor->identifier << "\" at indices "
169  << known[descriptor->identifier] << " and "
170  << index << endl;
171  SVDEBUG << "NativeVampPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl;
172  ok = false;
173  break;
174  } else {
175  known[descriptor->identifier] = index;
176  }
177 
178  ++index;
179  }
180 
181  if (ok) {
182 
183  index = 0;
184 
185  while ((descriptor = fn(VAMP_API_VERSION, index))) {
186 
188  ("vamp", libpath, descriptor->identifier);
189  m_identifiers.push_back(id);
190  m_libraries[id] = libpath;
191 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
192  SVCERR << "NativeVampPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl;
193 #endif
194  ++index;
195  }
196  }
197 
198 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
199  SVCERR << "NativeVampPluginFactory::getPluginIdentifiers: unloading library " << libraryHandle << endl;
200 #endif
201 
202  if (DLCLOSE(libraryHandle) != 0) {
203  SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << libpath << endl;
204  }
205  }
206 
207  generateTaxonomy();
208 
209  // Plugins can change the locale, revert it to default.
211 
212  return m_identifiers;
213 }
214 
215 QString
216 NativeVampPluginFactory::findPluginFile(QString soname, QString inDir)
217 {
218  QString file = "";
219 
220 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
221  SVCERR << "NativeVampPluginFactory::findPluginFile(\""
222  << soname << "\", \"" << inDir << "\")"
223  << endl;
224 #endif
225 
226  if (inDir != "") {
227 
228  QDir dir(inDir, PLUGIN_GLOB,
229  QDir::Name | QDir::IgnoreCase,
230  QDir::Files | QDir::Readable);
231  if (!dir.exists()) return "";
232 
233  file = dir.filePath(QFileInfo(soname).fileName());
234 
235  if (QFileInfo(file).exists() && QFileInfo(file).isFile()) {
236 
237 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
238  SVCERR << "NativeVampPluginFactory::findPluginFile: "
239  << "found trivially at " << file << endl;
240 #endif
241 
242  return file;
243  }
244 
245  for (unsigned int j = 0; j < dir.count(); ++j) {
246  file = dir.filePath(dir[j]);
247  if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
248 
249 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
250  SVCERR << "NativeVampPluginFactory::findPluginFile: "
251  << "found \"" << soname << "\" at " << file << endl;
252 #endif
253 
254  return file;
255  }
256  }
257 
258 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
259  SVCERR << "NativeVampPluginFactory::findPluginFile (with dir): "
260  << "not found" << endl;
261 #endif
262 
263  return "";
264 
265  } else {
266 
267  QFileInfo fi(soname);
268 
269  if (fi.isAbsolute() && fi.exists() && fi.isFile()) {
270 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
271  SVCERR << "NativeVampPluginFactory::findPluginFile: "
272  << "found trivially at " << soname << endl;
273 #endif
274  return soname;
275  }
276 
277  if (fi.isAbsolute() && fi.absolutePath() != "") {
278  file = findPluginFile(soname, fi.absolutePath());
279  if (file != "") return file;
280  }
281 
282  vector<QString> path = getPluginPath();
283  for (vector<QString>::iterator i = path.begin();
284  i != path.end(); ++i) {
285  if (*i != "") {
286  file = findPluginFile(soname, *i);
287  if (file != "") return file;
288  }
289  }
290 
291 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
292  SVCERR << "NativeVampPluginFactory::findPluginFile: "
293  << "not found" << endl;
294 #endif
295 
296  return "";
297  }
298 }
299 
300 std::shared_ptr<Vamp::Plugin>
302  sv_samplerate_t inputSampleRate)
303 {
304  Profiler profiler("NativeVampPluginFactory::instantiatePlugin");
305 
306  std::shared_ptr<Vamp::Plugin> rv = nullptr;
307  Vamp::PluginHostAdapter *plugin = nullptr;
308 
309  const VampPluginDescriptor *descriptor = nullptr;
310  int index = 0;
311 
312  QString type, soname, label;
313  PluginIdentifier::parseIdentifier(identifier, type, soname, label);
314  if (type != "vamp") {
315 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
316  SVCERR << "NativeVampPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl;
317 #endif
318  return nullptr;
319  }
320 
321  QString found = findPluginFile(soname);
322 
323  if (found == "") {
324  SVDEBUG << "NativeVampPluginFactory::instantiatePlugin: Failed to find library file " << soname << endl;
325  return nullptr;
326  } else if (found != soname) {
327 
328 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
329  SVCERR << "NativeVampPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl;
330  SVCERR << soname << " -> " << found << endl;
331 #endif
332 
333  }
334 
335  soname = found;
336 
337  void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
338 
339  if (!libraryHandle) {
340  SVDEBUG << "NativeVampPluginFactory::instantiatePlugin: Failed to load library " << soname << ": " << DLERROR() << endl;
341  return nullptr;
342  }
343 
344  VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
345  DLSYM(libraryHandle, "vampGetPluginDescriptor");
346 
347  if (!fn) {
348  SVDEBUG << "NativeVampPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl;
349  goto done;
350  }
351 
352  while ((descriptor = fn(VAMP_API_VERSION, index))) {
353  if (label == descriptor->identifier) break;
354  ++index;
355  }
356 
357  if (!descriptor) {
358  SVDEBUG << "NativeVampPluginFactory::instantiatePlugin: Failed to find plugin \"" << label << "\" in library " << soname << endl;
359  goto done;
360  }
361 
362  plugin = new Vamp::PluginHostAdapter(descriptor, float(inputSampleRate));
363 
364  if (plugin) {
365  m_handleMap[plugin] = libraryHandle;
366  rv = std::shared_ptr<Vamp::Plugin>
367  (new PluginDeletionNotifyAdapter(plugin, this));
368  }
369 
370 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
371  if (rv) {
372  SVCERR << "NativeVampPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl;
373  }
374 #endif
375 
376 done:
377  if (!rv) {
378  SVCERR << "NativeVampPluginFactory::instantiatePlugin: Failed to construct plugin" << endl;
379  if (DLCLOSE(libraryHandle) != 0) {
380  SVDEBUG << "WARNING: NativeVampPluginFactory::instantiatePlugin: Failed to unload library " << soname << endl;
381  }
382  }
383 
384  return rv;
385 }
386 
387 void
389 {
390  void *handle = m_handleMap[plugin];
391  if (!handle) return;
392 
393  m_handleMap.erase(plugin);
394 
395 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
396  SVCERR << "NativeVampPluginFactory::pluginDeleted: Removed from handle map, which now has " << m_handleMap.size() << " entries" << endl;
397 #endif
398 
399  for (auto h: m_handleMap) {
400  if (h.second == handle) {
401  // still in use
402  SVDEBUG << "NativeVampPluginFactory::pluginDeleted: Not unloading library " << handle << " as other plugins are still loaded from it" << endl;
403  return;
404  }
405  }
406 
407  SVDEBUG << "NativeVampPluginFactory::pluginDeleted: Unloading library " << handle << " after last plugin from this library " << plugin << " was deleted" << endl;
408  DLCLOSE(handle);
409 }
410 
411 QString
413 {
414  return m_taxonomy[identifier];
415 }
416 
417 QString
419 {
420  return m_libraries[identifier];
421 }
422 
423 void
425 {
426  vector<QString> pluginPath = getPluginPath();
427  vector<QString> path;
428 
429  for (size_t i = 0; i < pluginPath.size(); ++i) {
430  if (pluginPath[i].contains("/lib/")) {
431  QString p(pluginPath[i]);
432  path.push_back(p);
433  p.replace("/lib/", "/share/");
434  path.push_back(p);
435  }
436  path.push_back(pluginPath[i]);
437  }
438 
439  for (size_t i = 0; i < path.size(); ++i) {
440 
441  QDir dir(path[i], "*.cat");
442 
443 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl;
444  for (unsigned int j = 0; j < dir.count(); ++j) {
445 
446  QFile file(path[i] + "/" + dir[j]);
447 
448 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl;
449 
450  if (file.open(QIODevice::ReadOnly)) {
451  QTextStream stream(&file);
452  QString line;
453 
454  while (!stream.atEnd()) {
455  line = stream.readLine();
456  QString id = PluginIdentifier::canonicalise
457  (line.section("::", 0, 0));
458  QString cat = line.section("::", 1, 1);
459  m_taxonomy[id] = cat;
460  }
461  }
462  }
463  }
464 }
465 
466 piper_vamp::PluginStaticData
468 {
469  QMutexLocker locker(&m_mutex);
470 
471  if (m_pluginData.find(identifier) != m_pluginData.end()) {
472  return m_pluginData[identifier];
473  }
474 
475  QString type, soname, label;
476  PluginIdentifier::parseIdentifier(identifier, type, soname, label);
477  std::string pluginKey = (soname + ":" + label).toStdString();
478 
479  std::vector<std::string> catlist;
480  for (auto s: getPluginCategory(identifier).split(" > ")) {
481  catlist.push_back(s.toStdString());
482  }
483 
484  std::shared_ptr<Vamp::Plugin> p = instantiatePlugin(identifier, 44100);
485  if (!p) return {};
486 
487  auto psd = piper_vamp::PluginStaticData::fromPlugin(pluginKey,
488  catlist,
489  p.get());
490 
491  m_pluginData[identifier] = psd;
492  return psd;
493 }
494 
PluginDeletionNotifyAdapter(Vamp::Plugin *plugin, NativeVampPluginFactory *factory)
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
#define DLERROR()
Definition: System.h:100
static QList< PluginScan::Candidate > getCandidateLibraries()
#define DLOPEN(a, b)
Definition: System.h:97
static QString canonicalise(QString identifier)
QList< Candidate > getCandidateLibrariesFor(PluginType) const
Return the candidate plugin libraries of the given type that were found by helpers during the startup...
Definition: PluginScan.cpp:132
virtual std::shared_ptr< Vamp::Plugin > instantiatePlugin(QString identifier, sv_samplerate_t inputSampleRate) override
Instantiate (load) and return pointer to the plugin with the given identifier, at the given sample ra...
QString findPluginFile(QString soname, QString inDir="")
static void parseIdentifier(QString identifier, QString &type, QString &soName, QString &label)
FeatureExtractionPluginFactory type for Vamp plugins hosted in-process.
static QString createIdentifier(QString type, QString soName, QString label)
virtual piper_vamp::PluginStaticData getPluginStaticData(QString identifier) override
Return static data for the given plugin.
virtual QString getPluginLibraryPath(QString identifier) override
Get the full file path (including both directory and filename) of the library file that provides a gi...
static PluginScan * getInstance()
Definition: PluginScan.cpp:43
virtual std::vector< QString > getPluginIdentifiers(QString &errorMessage) override
Return all installed plugin identifiers.
std::vector< QString > getPluginPath()
void pluginDeleted(Vamp::Plugin *)
#define DLCLOSE(a)
Definition: System.h:99
void RestoreStartupLocale()
Definition: System.cpp:328
#define SVDEBUG
Definition: Debug.h:106
#define SVCERR
Definition: Debug.h:109
NativeVampPluginFactory * m_factory
virtual QString getPluginCategory(QString identifier) override
Get category metadata about a plugin (without instantiating it).
#define PLUGIN_GLOB
#define DLSYM(a, b)
Definition: System.h:98
Profile point instance class.
Definition: Profiler.h:93