Mercurial > hg > vamp-plugin-sdk
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) : |