OrangePi3588Media/src/plugin_loader.cpp
2026-01-13 08:28:55 +08:00

178 lines
4.7 KiB
C++

#include "plugin_loader.h"
#include <sstream>
#include "utils/logger.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
namespace rk3588 {
namespace {
void* LoadLibraryHandle(const std::string& path, std::string& err) {
#ifdef _WIN32
HMODULE handle = LoadLibraryA(path.c_str());
if (!handle) {
err = "LoadLibrary failed for " + path;
}
return handle;
#else
void* handle = dlopen(path.c_str(), RTLD_LAZY);
if (!handle) {
const char* dl_err = dlerror();
err = dl_err ? dl_err : "dlopen failed";
}
return handle;
#endif
}
void CloseLibraryHandle(void* handle) {
if (!handle) return;
#ifdef _WIN32
FreeLibrary(static_cast<HMODULE>(handle));
#else
dlclose(handle);
#endif
}
void* LoadSymbol(void* handle, const char* name) {
#ifdef _WIN32
return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name));
#else
return dlsym(handle, name);
#endif
}
std::string SharedLibExtension() {
#ifdef _WIN32
return ".dll";
#elif __APPLE__
return ".dylib";
#else
return ".so";
#endif
}
} // namespace
PluginLoader::PluginLoader(std::string plugin_dir)
: plugin_dir_(std::move(plugin_dir)) {}
PluginLoader::PluginLoader(PluginLoader&& other) noexcept
: plugin_dir_(std::move(other.plugin_dir_)), cache_(std::move(other.cache_)) {
other.cache_.clear();
}
PluginLoader& PluginLoader::operator=(PluginLoader&& other) noexcept {
if (this == &other) return *this;
for (auto& kv : cache_) {
CloseLibraryHandle(kv.second.handle);
}
cache_.clear();
plugin_dir_ = std::move(other.plugin_dir_);
cache_ = std::move(other.cache_);
other.cache_.clear();
return *this;
}
PluginLoader::~PluginLoader() {
for (auto& kv : cache_) {
CloseLibraryHandle(kv.second.handle);
}
}
void PluginLoader::SetPluginDir(std::string plugin_dir) {
if (plugin_dir == plugin_dir_) return;
for (auto& kv : cache_) {
CloseLibraryHandle(kv.second.handle);
}
cache_.clear();
plugin_dir_ = std::move(plugin_dir);
}
std::string PluginLoader::BuildLibraryPath(const std::string& type) const {
#ifdef _WIN32
return plugin_dir_ + "/" + type + SharedLibExtension();
#else
return plugin_dir_ + "/lib" + type + SharedLibExtension();
#endif
}
bool PluginLoader::LoadPlugin(const std::string& type, PluginHandle& out, std::string& err) {
std::string path = BuildLibraryPath(type);
void* handle = LoadLibraryHandle(path, err);
if (!handle) return false;
auto create = reinterpret_cast<CreateNodeFn>(LoadSymbol(handle, "CreateNode"));
auto destroy = reinterpret_cast<DestroyNodeFn>(LoadSymbol(handle, "DestroyNode"));
auto get_type = reinterpret_cast<GetTypeFn>(LoadSymbol(handle, "GetNodeType"));
auto get_abi = reinterpret_cast<GetAbiFn>(LoadSymbol(handle, "GetAbiVersion"));
if (!create || !destroy || !get_type || !get_abi) {
err = "Missing required symbols in plugin: " + path;
CloseLibraryHandle(handle);
return false;
}
if (get_abi() != kNodeAbiVersion) {
std::ostringstream oss;
oss << "ABI mismatch for plugin " << type << ": expected " << kNodeAbiVersion
<< " got " << get_abi();
err = oss.str();
CloseLibraryHandle(handle);
return false;
}
if (const char* declared = get_type()) {
if (type != declared) {
err = "Plugin type mismatch: requested '" + type + "' but plugin reports '" +
std::string(declared) + "'";
CloseLibraryHandle(handle);
return false;
}
}
out.handle = handle;
out.create = create;
out.destroy = destroy;
out.get_type = get_type;
out.get_abi = get_abi;
return true;
}
Result<NodePtr> PluginLoader::CreateNode(const std::string& type) {
auto it = cache_.find(type);
if (it == cache_.end()) {
PluginHandle handle;
std::string err;
if (!LoadPlugin(type, handle, err)) {
LogError("[PluginLoader] Failed to load plugin '" + type + "': " + err);
return Error("Failed to load plugin '" + type + "': " + err);
}
it = cache_.emplace(type, std::move(handle)).first;
}
PluginHandle& handle = it->second;
NodePtr node(handle.create(), NodeDeleter{handle.destroy});
if (!node) {
return Error("CreateNode returned null for type " + type);
}
return node;
}
NodePtr PluginLoader::Create(const std::string& type, std::string& err) {
auto result = CreateNode(type);
if (result.Failed()) {
err = result.ErrMessage();
return NodePtr(nullptr, NodeDeleter{});
}
return std::move(result).Take();
}
} // namespace rk3588