annotate plugin/FeatureExtractionPluginFactory.cpp @ 251:db0dd744fa8d

* Fix hang and memory leak on startup when encountering a pre-Vamp-1.0 plugin
author Chris Cannam
date Tue, 20 Mar 2007 11:49:11 +0000
parents d3ac9f953ebf
children dc46851837d6
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@52 9 This program is free software; you can redistribute it and/or
Chris@52 10 modify it under the terms of the GNU General Public License as
Chris@52 11 published by the Free Software Foundation; either version 2 of the
Chris@52 12 License, or (at your option) any later version. See the file
Chris@52 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "FeatureExtractionPluginFactory.h"
Chris@0 17 #include "PluginIdentifier.h"
Chris@0 18
Chris@66 19 #include "vamp/vamp.h"
Chris@66 20 #include "vamp-sdk/PluginHostAdapter.h"
Chris@66 21
Chris@150 22 #include "system/System.h"
Chris@66 23
Chris@66 24 #include <QDir>
Chris@66 25 #include <QFile>
Chris@66 26 #include <QFileInfo>
Chris@165 27 #include <QTextStream>
Chris@66 28
Chris@0 29 #include <iostream>
Chris@0 30
Chris@249 31 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1
Chris@249 32
Chris@0 33 static FeatureExtractionPluginFactory *_nativeInstance = 0;
Chris@0 34
Chris@0 35 FeatureExtractionPluginFactory *
Chris@0 36 FeatureExtractionPluginFactory::instance(QString pluginType)
Chris@0 37 {
Chris@71 38 if (pluginType == "vamp") {
Chris@0 39 if (!_nativeInstance) {
Chris@0 40 std::cerr << "FeatureExtractionPluginFactory::instance(" << pluginType.toStdString()
Chris@0 41 << "): creating new FeatureExtractionPluginFactory" << std::endl;
Chris@0 42 _nativeInstance = new FeatureExtractionPluginFactory();
Chris@0 43 }
Chris@0 44 return _nativeInstance;
Chris@0 45 }
Chris@0 46
Chris@0 47 else return 0;
Chris@0 48 }
Chris@0 49
Chris@0 50 FeatureExtractionPluginFactory *
Chris@0 51 FeatureExtractionPluginFactory::instanceFor(QString identifier)
Chris@0 52 {
Chris@0 53 QString type, soName, label;
Chris@0 54 PluginIdentifier::parseIdentifier(identifier, type, soName, label);
Chris@0 55 return instance(type);
Chris@0 56 }
Chris@0 57
Chris@0 58 std::vector<QString>
Chris@66 59 FeatureExtractionPluginFactory::getPluginPath()
Chris@66 60 {
Chris@117 61 if (!m_pluginPath.empty()) return m_pluginPath;
Chris@117 62
Chris@186 63 std::vector<std::string> p = Vamp::PluginHostAdapter::getPluginPath();
Chris@186 64 for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str());
Chris@186 65 return m_pluginPath;
Chris@66 66 }
Chris@66 67
Chris@66 68 std::vector<QString>
Chris@0 69 FeatureExtractionPluginFactory::getAllPluginIdentifiers()
Chris@0 70 {
Chris@0 71 FeatureExtractionPluginFactory *factory;
Chris@0 72 std::vector<QString> rv;
Chris@0 73
Chris@66 74 factory = instance("vamp");
Chris@0 75 if (factory) {
Chris@0 76 std::vector<QString> tmp = factory->getPluginIdentifiers();
Chris@0 77 for (size_t i = 0; i < tmp.size(); ++i) {
Chris@0 78 rv.push_back(tmp[i]);
Chris@0 79 }
Chris@0 80 }
Chris@0 81
Chris@0 82 // Plugins can change the locale, revert it to default.
Chris@0 83 setlocale(LC_ALL, "C");
Chris@0 84 return rv;
Chris@0 85 }
Chris@0 86
Chris@0 87 std::vector<QString>
Chris@0 88 FeatureExtractionPluginFactory::getPluginIdentifiers()
Chris@0 89 {
Chris@0 90 std::vector<QString> rv;
Chris@66 91 std::vector<QString> path = getPluginPath();
Chris@66 92
Chris@66 93 for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) {
Chris@66 94
Chris@249 95 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 96 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i->toStdString() << std::endl;
Chris@249 97 #endif
Chris@66 98
Chris@66 99 QDir pluginDir(*i, PLUGIN_GLOB,
Chris@66 100 QDir::Name | QDir::IgnoreCase,
Chris@66 101 QDir::Files | QDir::Readable);
Chris@66 102
Chris@66 103 for (unsigned int j = 0; j < pluginDir.count(); ++j) {
Chris@66 104
Chris@66 105 QString soname = pluginDir.filePath(pluginDir[j]);
Chris@66 106
Chris@249 107 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 108 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname.toStdString() << std::endl;
Chris@249 109 #endif
Chris@249 110
Chris@66 111 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66 112
Chris@66 113 if (!libraryHandle) {
Chris@71 114 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 115 continue;
Chris@66 116 }
Chris@66 117
Chris@249 118 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 119 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << std::endl;
Chris@249 120 #endif
Chris@249 121
Chris@66 122 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 123 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 124
Chris@66 125 if (!fn) {
Chris@66 126 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 127 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 128 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 129 }
Chris@66 130 continue;
Chris@66 131 }
Chris@66 132
Chris@249 133 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 134 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << std::endl;
Chris@249 135 #endif
Chris@249 136
Chris@66 137 const VampPluginDescriptor *descriptor = 0;
Chris@66 138 int index = 0;
Chris@66 139
Chris@251 140 std::map<std::string, int> known;
Chris@251 141 bool ok = true;
Chris@251 142
Chris@239 143 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@251 144
Chris@251 145 if (known.find(descriptor->identifier) != known.end()) {
Chris@251 146 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library "
Chris@251 147 << soname.toStdString()
Chris@251 148 << " returns the same plugin identifier \""
Chris@251 149 << descriptor->identifier << "\" at indices "
Chris@251 150 << known[descriptor->identifier] << " and "
Chris@251 151 << index << std::endl;
Chris@251 152 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << std::endl;
Chris@251 153 ok = false;
Chris@251 154 break;
Chris@251 155 } else {
Chris@251 156 known[descriptor->identifier] = index;
Chris@251 157 }
Chris@251 158
Chris@251 159 ++index;
Chris@251 160 }
Chris@251 161
Chris@251 162 if (ok) {
Chris@251 163
Chris@251 164 index = 0;
Chris@251 165
Chris@251 166 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@251 167
Chris@251 168 QString id = PluginIdentifier::createIdentifier
Chris@251 169 ("vamp", soname, descriptor->identifier);
Chris@251 170 rv.push_back(id);
Chris@249 171 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@251 172 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id.toStdString() << " at index " << index << std::endl;
Chris@249 173 #endif
Chris@251 174 ++index;
Chris@251 175 }
Chris@66 176 }
Chris@66 177
Chris@66 178 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 179 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 180 }
Chris@66 181 }
Chris@66 182 }
Chris@66 183
Chris@165 184 generateTaxonomy();
Chris@165 185
Chris@0 186 return rv;
Chris@0 187 }
Chris@0 188
Chris@66 189 QString
Chris@66 190 FeatureExtractionPluginFactory::findPluginFile(QString soname, QString inDir)
Chris@66 191 {
Chris@66 192 QString file = "";
Chris@66 193
Chris@249 194 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 195 std::cerr << "FeatureExtractionPluginFactory::findPluginFile(\""
Chris@249 196 << soname.toStdString() << "\", \"" << inDir.toStdString() << "\")"
Chris@249 197 << std::endl;
Chris@249 198 #endif
Chris@249 199
Chris@66 200 if (inDir != "") {
Chris@66 201
Chris@66 202 QDir dir(inDir, PLUGIN_GLOB,
Chris@66 203 QDir::Name | QDir::IgnoreCase,
Chris@66 204 QDir::Files | QDir::Readable);
Chris@66 205 if (!dir.exists()) return "";
Chris@66 206
Chris@66 207 file = dir.filePath(QFileInfo(soname).fileName());
Chris@249 208
Chris@249 209 if (QFileInfo(file).exists() && QFileInfo(file).isFile()) {
Chris@249 210
Chris@249 211 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 212 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 213 << "found trivially at " << file.toStdString() << std::endl;
Chris@249 214 #endif
Chris@249 215
Chris@66 216 return file;
Chris@66 217 }
Chris@66 218
Chris@66 219 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@66 220 file = dir.filePath(dir[j]);
Chris@66 221 if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
Chris@249 222
Chris@249 223 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 224 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 225 << "found at " << file.toStdString() << std::endl;
Chris@249 226 #endif
Chris@249 227
Chris@66 228 return file;
Chris@66 229 }
Chris@66 230 }
Chris@66 231
Chris@249 232 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 233 std::cerr << "FeatureExtractionPluginFactory::findPluginFile (with dir): "
Chris@249 234 << "not found" << std::endl;
Chris@249 235 #endif
Chris@249 236
Chris@66 237 return "";
Chris@66 238
Chris@66 239 } else {
Chris@66 240
Chris@66 241 QFileInfo fi(soname);
Chris@249 242
Chris@249 243 if (fi.isAbsolute() && fi.exists() && fi.isFile()) {
Chris@249 244 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 245 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 246 << "found trivially at " << soname.toStdString() << std::endl;
Chris@249 247 #endif
Chris@249 248 return soname;
Chris@249 249 }
Chris@66 250
Chris@66 251 if (fi.isAbsolute() && fi.absolutePath() != "") {
Chris@66 252 file = findPluginFile(soname, fi.absolutePath());
Chris@66 253 if (file != "") return file;
Chris@66 254 }
Chris@66 255
Chris@66 256 std::vector<QString> path = getPluginPath();
Chris@66 257 for (std::vector<QString>::iterator i = path.begin();
Chris@66 258 i != path.end(); ++i) {
Chris@66 259 if (*i != "") {
Chris@66 260 file = findPluginFile(soname, *i);
Chris@66 261 if (file != "") return file;
Chris@66 262 }
Chris@66 263 }
Chris@66 264
Chris@249 265 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 266 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 267 << "not found" << std::endl;
Chris@249 268 #endif
Chris@249 269
Chris@66 270 return "";
Chris@66 271 }
Chris@66 272 }
Chris@66 273
Chris@66 274 Vamp::Plugin *
Chris@0 275 FeatureExtractionPluginFactory::instantiatePlugin(QString identifier,
Chris@0 276 float inputSampleRate)
Chris@0 277 {
Chris@66 278 Vamp::Plugin *rv = 0;
Chris@66 279
Chris@66 280 const VampPluginDescriptor *descriptor = 0;
Chris@66 281 int index = 0;
Chris@66 282
Chris@66 283 QString type, soname, label;
Chris@66 284 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@71 285 if (type != "vamp") {
Chris@0 286 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::endl;
Chris@0 287 return 0;
Chris@0 288 }
Chris@0 289
Chris@66 290 QString found = findPluginFile(soname);
Chris@66 291
Chris@66 292 if (found == "") {
Chris@66 293 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname.toStdString() << std::endl;
Chris@117 294 return 0;
Chris@66 295 } else if (found != soname) {
Chris@249 296
Chris@249 297 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 298 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname.toStdString() << ", found at " << found.toStdString() << std::endl;
Chris@249 299 std::cerr << soname.toStdString() << " -> " << found.toStdString() << std::endl;
Chris@249 300 #endif
Chris@249 301
Chris@249 302 }
Chris@0 303
Chris@66 304 soname = found;
Chris@66 305
Chris@66 306 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66 307
Chris@66 308 if (!libraryHandle) {
Chris@71 309 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 310 return 0;
Chris@19 311 }
Chris@19 312
Chris@66 313 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 314 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 315
Chris@66 316 if (!fn) {
Chris@66 317 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 318 goto done;
Chris@0 319 }
Chris@0 320
Chris@239 321 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@238 322 if (label == descriptor->identifier) break;
Chris@66 323 ++index;
Chris@47 324 }
Chris@47 325
Chris@66 326 if (!descriptor) {
Chris@66 327 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label.toStdString() << "\" in library " << soname.toStdString() << std::endl;
Chris@66 328 goto done;
Martin@37 329 }
Martin@37 330
Chris@66 331 rv = new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
Chris@66 332
Chris@117 333 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << std::endl;
Chris@79 334
Chris@66 335 //!!! need to dlclose() when plugins from a given library are unloaded
Chris@66 336
Chris@66 337 done:
Chris@66 338 if (!rv) {
Chris@66 339 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 340 std::cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 341 }
Chris@66 342 }
Chris@73 343
Chris@73 344 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label.toStdString() << " from library " << soname.toStdString() << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << std::endl;
Chris@73 345
Chris@66 346 return rv;
Chris@0 347 }
Chris@0 348
Chris@165 349 QString
Chris@165 350 FeatureExtractionPluginFactory::getPluginCategory(QString identifier)
Chris@165 351 {
Chris@165 352 return m_taxonomy[identifier];
Chris@165 353 }
Chris@165 354
Chris@165 355 void
Chris@165 356 FeatureExtractionPluginFactory::generateTaxonomy()
Chris@165 357 {
Chris@165 358 std::vector<QString> pluginPath = getPluginPath();
Chris@165 359 std::vector<QString> path;
Chris@165 360
Chris@165 361 for (size_t i = 0; i < pluginPath.size(); ++i) {
Chris@165 362 if (pluginPath[i].contains("/lib/")) {
Chris@165 363 QString p(pluginPath[i]);
Chris@165 364 path.push_back(p);
Chris@165 365 p.replace("/lib/", "/share/");
Chris@165 366 path.push_back(p);
Chris@165 367 }
Chris@165 368 path.push_back(pluginPath[i]);
Chris@165 369 }
Chris@165 370
Chris@165 371 for (size_t i = 0; i < path.size(); ++i) {
Chris@165 372
Chris@165 373 QDir dir(path[i], "*.cat");
Chris@165 374
Chris@165 375 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i].toStdString() << " has " << dir.count() << " .cat files" << std::endl;
Chris@165 376 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@165 377
Chris@165 378 QFile file(path[i] + "/" + dir[j]);
Chris@165 379
Chris@165 380 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i].toStdString() + "/" + dir[j].toStdString()) << std::endl;
Chris@165 381
Chris@165 382 if (file.open(QIODevice::ReadOnly)) {
Chris@165 383 // std::cerr << "...opened" << std::endl;
Chris@165 384 QTextStream stream(&file);
Chris@165 385 QString line;
Chris@165 386
Chris@165 387 while (!stream.atEnd()) {
Chris@165 388 line = stream.readLine();
Chris@165 389 // std::cerr << "line is: \"" << line.toStdString() << "\"" << std::endl;
Chris@165 390 QString id = PluginIdentifier::canonicalise
Chris@165 391 (line.section("::", 0, 0));
Chris@165 392 QString cat = line.section("::", 1, 1);
Chris@165 393 m_taxonomy[id] = cat;
Chris@165 394 // std::cerr << "FeatureExtractionPluginFactory: set id \"" << id.toStdString() << "\" to cat \"" << cat.toStdString() << "\"" << std::endl;
Chris@165 395 }
Chris@165 396 }
Chris@165 397 }
Chris@165 398 }
Chris@165 399 }