comparison src/vamp-hostsdk/PluginLoader.cpp @ 390:06988ce35ff0 vh

Initial draft of C API for plugin loading
author Chris Cannam
date Wed, 20 May 2015 16:12:18 +0100
parents 9648ba9812d6
children 8c45dee08a95
comparison
equal deleted inserted replaced
389:93683cf9fce3 390:06988ce35ff0
32 shall not be used in advertising or otherwise to promote the sale, 32 shall not be used in advertising or otherwise to promote the sale,
33 use or other dealings in this Software without prior written 33 use or other dealings in this Software without prior written
34 authorization. 34 authorization.
35 */ 35 */
36 36
37 #include <vamp-hostsdk/PluginHostAdapter.h>
38 #include <vamp-hostsdk/PluginLoader.h> 37 #include <vamp-hostsdk/PluginLoader.h>
39 #include <vamp-hostsdk/PluginInputDomainAdapter.h> 38 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
40 #include <vamp-hostsdk/PluginChannelAdapter.h> 39 #include <vamp-hostsdk/PluginChannelAdapter.h>
41 #include <vamp-hostsdk/PluginBufferingAdapter.h> 40 #include <vamp-hostsdk/PluginBufferingAdapter.h>
41 #include <vamp-hostsdk/PluginHostAdapter.h>
42
43 #include <vamp/vamp.h>
44
45 #include "Files.h"
42 46
43 #include <fstream> 47 #include <fstream>
44 #include <cctype> // tolower
45
46 #include <cstring>
47
48 #ifdef _WIN32
49
50 #include <windows.h>
51 #include <tchar.h>
52 #define PLUGIN_SUFFIX "dll"
53
54 #else /* ! _WIN32 */
55
56 #include <dirent.h>
57 #include <dlfcn.h>
58
59 #ifdef __APPLE__
60 #define PLUGIN_SUFFIX "dylib"
61 #else /* ! __APPLE__ */
62 #define PLUGIN_SUFFIX "so"
63 #endif /* ! __APPLE__ */
64
65 #endif /* ! _WIN32 */
66 48
67 using namespace std; 49 using namespace std;
68 50
69 _VAMP_SDK_HOSTSPACE_BEGIN(PluginLoader.cpp) 51 _VAMP_SDK_HOSTSPACE_BEGIN(PluginLoader.cpp)
70 52
122 map<Plugin *, void *> m_pluginLibraryHandleMap; 104 map<Plugin *, void *> m_pluginLibraryHandleMap;
123 105
124 bool decomposePluginKey(PluginKey key, 106 bool decomposePluginKey(PluginKey key,
125 string &libraryName, string &identifier); 107 string &libraryName, string &identifier);
126 108
127 void *loadLibrary(string path);
128 void unloadLibrary(void *handle);
129 void *lookupInLibrary(void *handle, const char *symbol);
130
131 string splicePath(string a, string b);
132 vector<string> listFiles(string dir, string ext);
133
134 static InstanceCleaner m_cleaner; 109 static InstanceCleaner m_cleaner;
135 }; 110 };
136 111
137 PluginLoader * 112 PluginLoader *
138 PluginLoader::m_instance = 0; 113 PluginLoader::m_instance = 0;
225 } 200 }
226 201
227 void 202 void
228 PluginLoader::Impl::enumeratePlugins(PluginKey forPlugin) 203 PluginLoader::Impl::enumeratePlugins(PluginKey forPlugin)
229 { 204 {
230 vector<string> path = PluginHostAdapter::getPluginPath();
231
232 string libraryName, identifier; 205 string libraryName, identifier;
206 vector<string> fullPaths;
207
233 if (forPlugin != "") { 208 if (forPlugin != "") {
234 if (!decomposePluginKey(forPlugin, libraryName, identifier)) { 209 if (!decomposePluginKey(forPlugin, libraryName, identifier)) {
235 std::cerr << "WARNING: Vamp::HostExt::PluginLoader: Invalid plugin key \"" 210 std::cerr << "WARNING: Vamp::HostExt::PluginLoader: Invalid plugin key \""
236 << forPlugin << "\" in enumerate" << std::endl; 211 << forPlugin << "\" in enumerate" << std::endl;
237 return; 212 return;
238 } 213 }
239 } 214 fullPaths = Files::listLibraryFilesMatching(libraryName);
240 215 } else {
241 for (size_t i = 0; i < path.size(); ++i) { 216 fullPaths = Files::listLibraryFiles();
242 217 }
243 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX); 218
244 219 for (size_t i = 0; i < fullPaths.size(); ++i) {
245 for (vector<string>::iterator fi = files.begin(); 220
246 fi != files.end(); ++fi) { 221 string fullPath = fullPaths[i];
222 void *handle = Files::loadLibrary(fullPath);
223 if (!handle) continue;
247 224
248 if (libraryName != "") { 225 VampGetPluginDescriptorFunction fn =
249 // libraryName is lowercased and lacking an extension, 226 (VampGetPluginDescriptorFunction)Files::lookupInLibrary
250 // as it came from the plugin key 227 (handle, "vampGetPluginDescriptor");
251 string temp = *fi;
252 for (size_t i = 0; i < temp.length(); ++i) {
253 temp[i] = tolower(temp[i]);
254 }
255 string::size_type pi = temp.find('.');
256 if (pi == string::npos) {
257 if (libraryName != temp) continue;
258 } else {
259 if (libraryName != temp.substr(0, pi)) continue;
260 }
261 }
262
263 string fullPath = path[i];
264 fullPath = splicePath(fullPath, *fi);
265 void *handle = loadLibrary(fullPath);
266 if (!handle) continue;
267 228
268 VampGetPluginDescriptorFunction fn = 229 if (!fn) {
269 (VampGetPluginDescriptorFunction)lookupInLibrary 230 if (forPlugin != "") {
270 (handle, "vampGetPluginDescriptor"); 231 cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \""
232 << fullPath << "\"" << endl;
233 }
234 Files::unloadLibrary(handle);
235 continue;
236 }
271 237
272 if (!fn) { 238 int index = 0;
273 if (forPlugin != "") { 239 const VampPluginDescriptor *descriptor = 0;
274 cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \"" 240 bool found = false;
275 << fullPath << "\"" << endl;
276 }
277 unloadLibrary(handle);
278 continue;
279 }
280 241
281 int index = 0; 242 while ((descriptor = fn(VAMP_API_VERSION, index))) {
282 const VampPluginDescriptor *descriptor = 0; 243 ++index;
283 bool found = false; 244 if (identifier != "") {
245 if (descriptor->identifier != identifier) continue;
246 }
247 found = true;
248 PluginKey key = composePluginKey(fullPath, descriptor->identifier);
249 // std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
250 if (m_pluginLibraryNameMap.find(key) ==
251 m_pluginLibraryNameMap.end()) {
252 m_pluginLibraryNameMap[key] = fullPath;
253 }
254 }
255
256 if (!found && forPlugin != "") {
257 cerr << "Vamp::HostExt::PluginLoader: Plugin \""
258 << identifier << "\" not found in library \""
259 << fullPath << "\"" << endl;
260 }
284 261
285 while ((descriptor = fn(VAMP_API_VERSION, index))) { 262 Files::unloadLibrary(handle);
286 ++index;
287 if (identifier != "") {
288 if (descriptor->identifier != identifier) continue;
289 }
290 found = true;
291 PluginKey key = composePluginKey(*fi, descriptor->identifier);
292 // std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
293 if (m_pluginLibraryNameMap.find(key) ==
294 m_pluginLibraryNameMap.end()) {
295 m_pluginLibraryNameMap[key] = fullPath;
296 }
297 }
298
299 if (!found && forPlugin != "") {
300 cerr << "Vamp::HostExt::PluginLoader: Plugin \""
301 << identifier << "\" not found in library \""
302 << fullPath << "\"" << endl;
303 }
304
305 unloadLibrary(handle);
306 }
307 } 263 }
308 264
309 if (forPlugin == "") m_allPluginsEnumerated = true; 265 if (forPlugin == "") m_allPluginsEnumerated = true;
310 } 266 }
311 267
312 PluginLoader::PluginKey 268 PluginLoader::PluginKey
313 PluginLoader::Impl::composePluginKey(string libraryName, string identifier) 269 PluginLoader::Impl::composePluginKey(string libraryName, string identifier)
314 { 270 {
315 string basename = libraryName; 271 string basename = Files::lcBasename(libraryName);
316
317 string::size_type li = basename.rfind('/');
318 if (li != string::npos) basename = basename.substr(li + 1);
319
320 li = basename.find('.');
321 if (li != string::npos) basename = basename.substr(0, li);
322
323 for (size_t i = 0; i < basename.length(); ++i) {
324 basename[i] = tolower(basename[i]);
325 }
326
327 return basename + ":" + identifier; 272 return basename + ":" + identifier;
328 } 273 }
329 274
330 bool 275 bool
331 PluginLoader::Impl::decomposePluginKey(PluginKey key, 276 PluginLoader::Impl::decomposePluginKey(PluginKey key,
380 if (fullPath == "") { 325 if (fullPath == "") {
381 std::cerr << "Vamp::HostExt::PluginLoader: No library found in Vamp path for plugin \"" << key << "\"" << std::endl; 326 std::cerr << "Vamp::HostExt::PluginLoader: No library found in Vamp path for plugin \"" << key << "\"" << std::endl;
382 return 0; 327 return 0;
383 } 328 }
384 329
385 void *handle = loadLibrary(fullPath); 330 void *handle = Files::loadLibrary(fullPath);
386 if (!handle) return 0; 331 if (!handle) return 0;
387 332
388 VampGetPluginDescriptorFunction fn = 333 VampGetPluginDescriptorFunction fn =
389 (VampGetPluginDescriptorFunction)lookupInLibrary 334 (VampGetPluginDescriptorFunction)Files::lookupInLibrary
390 (handle, "vampGetPluginDescriptor"); 335 (handle, "vampGetPluginDescriptor");
391 336
392 if (!fn) { 337 if (!fn) {
393 cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \"" 338 cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \""
394 << fullPath << "\"" << endl; 339 << fullPath << "\"" << endl;
395 unloadLibrary(handle); 340 Files::unloadLibrary(handle);
396 return 0; 341 return 0;
397 } 342 }
398 343
399 int index = 0; 344 int index = 0;
400 const VampPluginDescriptor *descriptor = 0; 345 const VampPluginDescriptor *descriptor = 0;
472 char buffer[1024]; 417 char buffer[1024];
473 418
474 for (vector<string>::iterator i = catpath.begin(); 419 for (vector<string>::iterator i = catpath.begin();
475 i != catpath.end(); ++i) { 420 i != catpath.end(); ++i) {
476 421
477 vector<string> files = listFiles(*i, suffix); 422 vector<string> files = Files::listFiles(*i, suffix);
478 423
479 for (vector<string>::iterator fi = files.begin(); 424 for (vector<string>::iterator fi = files.begin();
480 fi != files.end(); ++fi) { 425 fi != files.end(); ++fi) {
481 426
482 string filepath = splicePath(*i, *fi); 427 string filepath = Files::splicePath(*i, *fi);
483 ifstream is(filepath.c_str(), ifstream::in | ifstream::binary); 428 ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
484 429
485 if (is.fail()) { 430 if (is.fail()) {
486 // cerr << "failed to open: " << filepath << endl; 431 // cerr << "failed to open: " << filepath << endl;
487 continue; 432 continue;
523 } 468 }
524 } 469 }
525 } 470 }
526 } 471 }
527 472
528 void *
529 PluginLoader::Impl::loadLibrary(string path)
530 {
531 void *handle = 0;
532 #ifdef _WIN32
533 #ifdef UNICODE
534 int len = path.length() + 1; // cannot be more wchars than length in bytes of utf8 string
535 wchar_t *buffer = new wchar_t[len];
536 int rv = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), len, buffer, len);
537 if (rv <= 0) {
538 cerr << "Vamp::HostExt::PluginLoader: Unable to convert library path \""
539 << path << "\" to wide characters " << endl;
540 delete[] buffer;
541 return handle;
542 }
543 handle = LoadLibrary(buffer);
544 delete[] buffer;
545 #else
546 handle = LoadLibrary(path.c_str());
547 #endif
548 if (!handle) {
549 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
550 << path << "\"" << endl;
551 }
552 #else
553 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
554 if (!handle) {
555 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
556 << path << "\": " << dlerror() << endl;
557 }
558 #endif
559 return handle;
560 }
561
562 void
563 PluginLoader::Impl::unloadLibrary(void *handle)
564 {
565 #ifdef _WIN32
566 FreeLibrary((HINSTANCE)handle);
567 #else
568 dlclose(handle);
569 #endif
570 }
571
572 void *
573 PluginLoader::Impl::lookupInLibrary(void *handle, const char *symbol)
574 {
575 #ifdef _WIN32
576 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
577 #else
578 return (void *)dlsym(handle, symbol);
579 #endif
580 }
581
582 string
583 PluginLoader::Impl::splicePath(string a, string b)
584 {
585 #ifdef _WIN32
586 return a + "\\" + b;
587 #else
588 return a + "/" + b;
589 #endif
590 }
591
592 vector<string>
593 PluginLoader::Impl::listFiles(string dir, string extension)
594 {
595 vector<string> files;
596
597 #ifdef _WIN32
598 string expression = dir + "\\*." + extension;
599 #ifdef UNICODE
600 int len = expression.length() + 1; // cannot be more wchars than length in bytes of utf8 string
601 wchar_t *buffer = new wchar_t[len];
602 int rv = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), len, buffer, len);
603 if (rv <= 0) {
604 cerr << "Vamp::HostExt::PluginLoader: Unable to convert wildcard path \""
605 << expression << "\" to wide characters" << endl;
606 delete[] buffer;
607 return files;
608 }
609 WIN32_FIND_DATA data;
610 HANDLE fh = FindFirstFile(buffer, &data);
611 if (fh == INVALID_HANDLE_VALUE) {
612 delete[] buffer;
613 return files;
614 }
615
616 bool ok = true;
617 while (ok) {
618 wchar_t *fn = data.cFileName;
619 int wlen = wcslen(fn) + 1;
620 int maxlen = wlen * 6;
621 char *conv = new char[maxlen];
622 int rv = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, maxlen, 0, 0);
623 if (rv > 0) {
624 files.push_back(conv);
625 }
626 delete[] conv;
627 ok = FindNextFile(fh, &data);
628 }
629
630 FindClose(fh);
631 delete[] buffer;
632 #else
633 WIN32_FIND_DATA data;
634 HANDLE fh = FindFirstFile(expression.c_str(), &data);
635 if (fh == INVALID_HANDLE_VALUE) return files;
636
637 bool ok = true;
638 while (ok) {
639 files.push_back(data.cFileName);
640 ok = FindNextFile(fh, &data);
641 }
642
643 FindClose(fh);
644 #endif
645 #else
646
647 size_t extlen = extension.length();
648 DIR *d = opendir(dir.c_str());
649 if (!d) return files;
650
651 struct dirent *e = 0;
652 while ((e = readdir(d))) {
653
654 if (!e->d_name) continue;
655
656 size_t len = strlen(e->d_name);
657 if (len < extlen + 2 ||
658 e->d_name + len - extlen - 1 != "." + extension) {
659 continue;
660 }
661
662 files.push_back(e->d_name);
663 }
664
665 closedir(d);
666 #endif
667
668 return files;
669 }
670
671 void 473 void
672 PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter) 474 PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
673 { 475 {
674 void *handle = m_pluginLibraryHandleMap[adapter]; 476 void *handle = m_pluginLibraryHandleMap[adapter];
675 if (handle) unloadLibrary(handle); 477 if (handle) Files::unloadLibrary(handle);
676 m_pluginLibraryHandleMap.erase(adapter); 478 m_pluginLibraryHandleMap.erase(adapter);
677 } 479 }
678 480
679 PluginLoader::Impl::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin, 481 PluginLoader::Impl::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
680 Impl *loader) : 482 Impl *loader) :