comparison vamp-sdk/hostext/PluginLoader.cpp @ 59:fa79c4ec847d host-factory-stuff

* Put hostext stuff in the HostExt sub-namespace * Tidy up system-specific stuff in PluginLoader * Make PluginLoader return a deletion-notifying wrapper which permits the library to be unloaded when no longer in use * Add PluginChannelAdapter * Make vamp-simple-host use PluginChannelAdapter, and use the PluginLoader for plugin-running task. Also some other enhancements to host
author cannam
date Thu, 24 May 2007 15:17:07 +0000
parents 0284955e31e5
children 97c5ac99d725
comparison
equal deleted inserted replaced
58:0284955e31e5 59:fa79c4ec847d
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-sdk/PluginHostAdapter.h"
37 #include "PluginLoader.h" 38 #include "PluginLoader.h"
38 #include "PluginHostAdapter.h"
39
40 #include "system.h"
41 39
42 #include <fstream> 40 #include <fstream>
43 41
44 #include <dirent.h> // POSIX directory open and read 42 #ifdef _WIN32
43
44 #include <windows.h>
45 #include <tchar.h>
46 #define PLUGIN_SUFFIX "dll"
47
48 #else /* ! _WIN32 */
49
50 #include <dirent.h>
51 #include <dlfcn.h>
52
53 #ifdef __APPLE__
54 #define PLUGIN_SUFFIX "dylib"
55 #else /* ! __APPLE__ */
56 #define PLUGIN_SUFFIX "so"
57 #endif /* ! __APPLE__ */
58
59 #endif /* ! _WIN32 */
45 60
46 using namespace std; 61 using namespace std;
47 62
48 namespace Vamp { 63 namespace Vamp {
49 64
65 namespace HostExt {
66
67 PluginLoader *
68 PluginLoader::m_instance = 0;
69
50 PluginLoader::PluginLoader() 70 PluginLoader::PluginLoader()
51 { 71 {
52 } 72 }
53 73
54 PluginLoader::~PluginLoader() 74 PluginLoader::~PluginLoader()
55 { 75 {
76 }
77
78 PluginLoader *
79 PluginLoader::getInstance()
80 {
81 if (!m_instance) m_instance = new PluginLoader();
82 return m_instance;
56 } 83 }
57 84
58 vector<PluginLoader::PluginKey> 85 vector<PluginLoader::PluginKey>
59 PluginLoader::listPlugins() 86 PluginLoader::listPlugins()
60 { 87 {
61 if (m_pluginLibraryMap.empty()) { 88 if (m_pluginLibraryNameMap.empty()) generateLibraryMap();
62
63 vector<string> path = PluginHostAdapter::getPluginPath();
64
65 size_t suffixLen = strlen(PLUGIN_SUFFIX);
66
67 for (size_t i = 0; i < path.size(); ++i) {
68
69 vector<string> files = getFilesInDir(path[i], PLUGIN_SUFFIX);
70
71
72 for (vector<string>::iterator fi = files.begin();
73 fi != files.end(); ++fi) {
74
75 string basename = *fi;
76 basename = basename.substr(0, basename.length() - suffixLen - 1);
77
78 string fullPath = path[i];
79 fullPath = fullPath + "/" + *fi; //!!! systemize
80 void *handle = DLOPEN(fullPath, RTLD_LAZY);
81
82 if (!handle) {
83 cerr << "Vamp::PluginLoader: " << *fi
84 << ": unable to load library (" << DLERROR()
85 << ")" << endl;
86 continue;
87 }
88
89 VampGetPluginDescriptorFunction fn =
90 (VampGetPluginDescriptorFunction)DLSYM
91 (handle, "vampGetPluginDescriptor");
92
93 if (!fn) {
94 DLCLOSE(handle);
95 continue;
96 }
97
98 int index = 0;
99 const VampPluginDescriptor *descriptor = 0;
100
101 while ((descriptor = fn(VAMP_API_VERSION, index))) {
102 PluginKey key = basename + ":" + descriptor->identifier;
103 if (m_pluginLibraryMap.find(key) ==
104 m_pluginLibraryMap.end()) {
105 m_pluginLibraryMap[key] = fullPath;
106 }
107 ++index;
108 }
109
110 DLCLOSE(handle);
111 }
112 }
113 }
114 89
115 vector<PluginKey> plugins; 90 vector<PluginKey> plugins;
116 for (map<PluginKey, string>::iterator mi = 91 for (map<PluginKey, string>::iterator mi =
117 m_pluginLibraryMap.begin(); 92 m_pluginLibraryNameMap.begin();
118 mi != m_pluginLibraryMap.end(); ++mi) { 93 mi != m_pluginLibraryNameMap.end(); ++mi) {
119 plugins.push_back(mi->first); 94 plugins.push_back(mi->first);
120 } 95 }
121 96
122 return plugins; 97 return plugins;
98 }
99
100 void
101 PluginLoader::generateLibraryMap()
102 {
103 vector<string> path = PluginHostAdapter::getPluginPath();
104
105 for (size_t i = 0; i < path.size(); ++i) {
106
107 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
108
109 for (vector<string>::iterator fi = files.begin();
110 fi != files.end(); ++fi) {
111
112 string fullPath = path[i];
113 fullPath = splicePath(fullPath, *fi);
114 void *handle = loadLibrary(fullPath);
115 if (!handle) continue;
116
117 VampGetPluginDescriptorFunction fn =
118 (VampGetPluginDescriptorFunction)lookupInLibrary
119 (handle, "vampGetPluginDescriptor");
120
121 if (!fn) {
122 unloadLibrary(handle);
123 continue;
124 }
125
126 int index = 0;
127 const VampPluginDescriptor *descriptor = 0;
128
129 while ((descriptor = fn(VAMP_API_VERSION, index))) {
130 PluginKey key = composePluginKey(*fi, descriptor->identifier);
131 if (m_pluginLibraryNameMap.find(key) ==
132 m_pluginLibraryNameMap.end()) {
133 m_pluginLibraryNameMap[key] = fullPath;
134 }
135 ++index;
136 }
137
138 unloadLibrary(handle);
139 }
140 }
141 }
142
143 PluginLoader::PluginKey
144 PluginLoader::composePluginKey(string libraryName, string identifier)
145 {
146 string basename = libraryName;
147
148 string::size_type li = basename.rfind('/');
149 if (li != string::npos) basename = basename.substr(li + 1);
150
151 li = basename.find('.');
152 if (li != string::npos) basename = basename.substr(0, li);
153
154 return basename + ":" + identifier;
123 } 155 }
124 156
125 PluginLoader::PluginCategoryHierarchy 157 PluginLoader::PluginCategoryHierarchy
126 PluginLoader::getPluginCategory(PluginKey plugin) 158 PluginLoader::getPluginCategory(PluginKey plugin)
127 { 159 {
131 } 163 }
132 164
133 string 165 string
134 PluginLoader::getLibraryPathForPlugin(PluginKey plugin) 166 PluginLoader::getLibraryPathForPlugin(PluginKey plugin)
135 { 167 {
136 if (m_pluginLibraryMap.empty()) (void)listPlugins(); 168 if (m_pluginLibraryNameMap.empty()) generateLibraryMap();
137 if (m_pluginLibraryMap.find(plugin) == m_pluginLibraryMap.end()) return ""; 169 if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) return "";
138 return m_pluginLibraryMap[plugin]; 170 return m_pluginLibraryNameMap[plugin];
139 } 171 }
140 172
141 Plugin * 173 Plugin *
142 PluginLoader::load(PluginKey key, float inputSampleRate) 174 PluginLoader::loadPlugin(PluginKey key, float inputSampleRate)
143 { 175 {
144 string fullPath = getLibraryPathForPlugin(key); 176 string fullPath = getLibraryPathForPlugin(key);
145 if (fullPath == "") return 0; 177 if (fullPath == "") return 0;
146 178
147 string::size_type ki = key.find(':'); 179 string::size_type ki = key.find(':');
150 return 0; 182 return 0;
151 } 183 }
152 184
153 string identifier = key.substr(ki + 1); 185 string identifier = key.substr(ki + 1);
154 186
155 void *handle = DLOPEN(fullPath, RTLD_LAZY); 187 void *handle = loadLibrary(fullPath);
156 188 if (!handle) return 0;
157 if (!handle) {
158 cerr << "Vamp::PluginLoader: " << fullPath
159 << ": unable to load library (" << DLERROR()
160 << ")" << endl;
161 return 0;
162 }
163 189
164 VampGetPluginDescriptorFunction fn = 190 VampGetPluginDescriptorFunction fn =
165 (VampGetPluginDescriptorFunction)DLSYM 191 (VampGetPluginDescriptorFunction)lookupInLibrary
166 (handle, "vampGetPluginDescriptor"); 192 (handle, "vampGetPluginDescriptor");
167 193
168 if (!fn) { 194 if (!fn) {
169 //!!! refcount this! --!!! no, POSIX says dlopen/dlclose will 195 unloadLibrary(handle);
170 // reference count. check on win32
171 DLCLOSE(handle);
172 return 0; 196 return 0;
173 } 197 }
174 198
175 int index = 0; 199 int index = 0;
176 const VampPluginDescriptor *descriptor = 0; 200 const VampPluginDescriptor *descriptor = 0;
177 201
178 while ((descriptor = fn(VAMP_API_VERSION, index))) { 202 while ((descriptor = fn(VAMP_API_VERSION, index))) {
203
179 if (string(descriptor->identifier) == identifier) { 204 if (string(descriptor->identifier) == identifier) {
180 return new Vamp::PluginHostAdapter(descriptor, inputSampleRate); 205
206 Vamp::PluginHostAdapter *plugin =
207 new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
208
209 PluginDeletionNotifyAdapter *adapter =
210 new PluginDeletionNotifyAdapter(plugin, this);
211
212 m_pluginLibraryHandleMap[adapter] = handle;
213 return adapter;
181 } 214 }
215
182 ++index; 216 ++index;
183 } 217 }
184 218
185 //!!! flag error 219 cerr << "Vamp::HostExt::PluginLoader: Plugin \""
220 << identifier << "\" not found in library \""
221 << fullPath << "\"" << endl;
222
186 return 0; 223 return 0;
187 }
188
189 vector<string>
190 PluginLoader::getFilesInDir(string dir, string extension)
191 {
192 vector<string> files;
193
194 DIR *d = opendir(dir.c_str());
195 if (!d) return files;
196
197 struct dirent *e = 0;
198 while ((e = readdir(d))) {
199
200 if (!(e->d_type & DT_REG) || !e->d_name) {
201 continue;
202 }
203
204 int len = strlen(e->d_name);
205 if (len < int(extension.length() + 2) ||
206 e->d_name[len - extension.length() - 1] != '.' ||
207 strcmp(e->d_name + len - extension.length(), extension.c_str())) {
208 continue;
209 }
210
211 files.push_back(e->d_name);
212 }
213
214 closedir(d);
215
216 return files;
217 } 224 }
218 225
219 void 226 void
220 PluginLoader::generateTaxonomy() 227 PluginLoader::generateTaxonomy()
221 { 228 {
227 234
228 string suffix = "cat"; 235 string suffix = "cat";
229 236
230 for (vector<string>::iterator i = path.begin(); 237 for (vector<string>::iterator i = path.begin();
231 i != path.end(); ++i) { 238 i != path.end(); ++i) {
239
240 // It doesn't matter that we're using literal forward-slash in
241 // this bit, as it's only relevant if the path contains
242 // "/lib/", which is only meaningful and only plausible on
243 // systems with forward-slash delimiters
232 244
233 string dir = *i; 245 string dir = *i;
234 string::size_type li = dir.find(libfragment); 246 string::size_type li = dir.find(libfragment);
235 247
236 if (li != string::npos) { 248 if (li != string::npos) {
246 char buffer[1024]; 258 char buffer[1024];
247 259
248 for (vector<string>::iterator i = catpath.begin(); 260 for (vector<string>::iterator i = catpath.begin();
249 i != catpath.end(); ++i) { 261 i != catpath.end(); ++i) {
250 262
251 vector<string> files = getFilesInDir(*i, suffix); 263 vector<string> files = listFiles(*i, suffix);
252 264
253 for (vector<string>::iterator fi = files.begin(); 265 for (vector<string>::iterator fi = files.begin();
254 fi != files.end(); ++fi) { 266 fi != files.end(); ++fi) {
255 267
256 string filepath = *i + "/" + *fi; //!!! systemize 268 string filepath = splicePath(*i, *fi);
257 ifstream is(filepath.c_str(), ifstream::in | ifstream::binary); 269 ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
258 270
259 if (is.fail()) { 271 if (is.fail()) {
260 // cerr << "failed to open: " << filepath << endl; 272 // cerr << "failed to open: " << filepath << endl;
261 continue; 273 continue;
297 } 309 }
298 } 310 }
299 } 311 }
300 } 312 }
301 313
302 314 void *
303 } 315 PluginLoader::loadLibrary(string path)
316 {
317 void *handle = 0;
318 #ifdef _WIN32
319 handle = LoadLibrary(path.c_str());
320 if (!handle) {
321 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
322 << path << "\"" << endl;
323 }
324 #else
325 handle = dlopen(path.c_str(), RTLD_LAZY);
326 if (!handle) {
327 cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
328 << path << "\": " << dlerror() << endl;
329 }
330 #endif
331 return handle;
332 }
333
334 void
335 PluginLoader::unloadLibrary(void *handle)
336 {
337 #ifdef _WIN32
338 FreeLibrary((HINSTANCE)handle);
339 #else
340 dlclose(handle);
341 #endif
342 }
343
344 void *
345 PluginLoader::lookupInLibrary(void *handle, const char *symbol)
346 {
347 #ifdef _WIN32
348 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
349 #else
350 return (void *)dlsym(handle, symbol);
351 #endif
352 }
353
354 string
355 PluginLoader::splicePath(string a, string b)
356 {
357 #ifdef _WIN32
358 return a + "\\" + b;
359 #else
360 return a + "/" + b;
361 #endif
362 }
363
364 vector<string>
365 PluginLoader::listFiles(string dir, string extension)
366 {
367 vector<string> files;
368 size_t extlen = extension.length();
369
370 #ifdef _WIN32
371
372 string expression = dir + "\\*." + extension;
373 WIN32_FIND_DATA data;
374 HANDLE fh = FindFirstFile(expression.c_str(), &data);
375 if (fh == INVALID_HANDLE_VALUE) return files;
376
377 bool ok = true;
378 while (ok) {
379 files.push_back(data.cFileName);
380 ok = FindNextFile(fh, &data);
381 }
382
383 FindClose(fh);
384
385 #else
386 DIR *d = opendir(dir.c_str());
387 if (!d) return files;
388
389 struct dirent *e = 0;
390 while ((e = readdir(d))) {
391
392 if (!(e->d_type & DT_REG) || !e->d_name) continue;
393
394 size_t len = strlen(e->d_name);
395 if (len < extlen + 2 ||
396 e->d_name + len - extlen - 1 != "." + extension) {
397 continue;
398 }
399
400 files.push_back(e->d_name);
401 }
402
403 closedir(d);
404 #endif
405
406 return files;
407 }
408
409 void
410 PluginLoader::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
411 {
412 void *handle = m_pluginLibraryHandleMap[adapter];
413 if (handle) unloadLibrary(handle);
414 m_pluginLibraryHandleMap.erase(adapter);
415 }
416
417 PluginLoader::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
418 PluginLoader *loader) :
419 PluginWrapper(plugin),
420 m_loader(loader)
421 {
422 }
423
424 PluginLoader::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
425 {
426 // We need to delete the plugin before calling pluginDeleted, as
427 // the delete call may require calling through to the descriptor
428 // (for e.g. cleanup) but pluginDeleted may unload the required
429 // library for the call. To prevent a double deletion when our
430 // parent's destructor runs (after this one), be sure to set
431 // m_plugin to 0 after deletion.
432 delete m_plugin;
433 m_plugin = 0;
434
435 if (m_loader) m_loader->pluginDeleted(this);
436 }
437
438 }
439
440 }