diff options
author | methodus <methodus@web.de> | 2012-09-30 15:52:54 +0200 |
---|---|---|
committer | methodus <methodus@web.de> | 2012-09-30 15:52:54 +0200 |
commit | c8e697daf68f1c545ee3962d2f2e9b411c56b4f2 (patch) | |
tree | 48bc206a65ad956ab4284113c33d4b1d50398495 | |
parent | dd3e8652f530c35febcc3f595e38443395f79198 (diff) | |
download | vdr-plugin-upnp-c8e697daf68f1c545ee3962d2f2e9b411c56b4f2.tar.gz vdr-plugin-upnp-c8e697daf68f1c545ee3962d2f2e9b411c56b4f2.tar.bz2 |
Working on plugin interface. PluginManager almost done.
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | httptnt/resourceStreamer.ecpp | 37 | ||||
-rw-r--r-- | include/media/mediaManager.h | 28 | ||||
-rw-r--r-- | include/plugin.h | 2 | ||||
-rw-r--r-- | include/pluginManager.h | 38 | ||||
-rw-r--r-- | media/mediaManager.cpp | 84 | ||||
-rw-r--r-- | media/pluginManager.cpp | 77 | ||||
-rw-r--r-- | server/server.cpp | 7 |
8 files changed, 211 insertions, 66 deletions
@@ -27,6 +27,9 @@ VDRDIR ?= ../../.. LIBDIR ?= ../../lib TMPDIR ?= /tmp +PLUGINDIR= ./PLUGINS +PLUGINLIBDIR= $(PLUGINDIR)/lib/upnp + ### Make sure that necessary options are included: include $(VDRDIR)/Make.global @@ -49,6 +52,7 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR)/include DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -DPLUGINDIR=\"$(PLUGINLIBDIR)\" ### The object files (add further files here): diff --git a/httptnt/resourceStreamer.ecpp b/httptnt/resourceStreamer.ecpp index 2c64fed..96c4ea4 100644 --- a/httptnt/resourceStreamer.ecpp +++ b/httptnt/resourceStreamer.ecpp @@ -1,8 +1,8 @@ -<%args> -objectID; -int resourceID = 0; -</%args> -<%pre> +//<%args> +//objectID; +//int resourceID = 0; +//</%args> +//<%pre> #include <string> #include <stdint.h> #include <limits> @@ -16,11 +16,11 @@ int resourceID = 0; #include <tnt/http.h> using namespace upnp; -</%pre> -<# +//</%pre> +//<# int doRequest(tnt::HttpReply reply, tnt::HttpRequest request, std::string objectID, int resourceID) { -#> -<%cpp> +//#> +//<%cpp> if(objectID.empty()){ reply.out() << "Object ID missing"; return HTTP_BAD_REQUEST; @@ -28,7 +28,7 @@ int doRequest(tnt::HttpReply reply, tnt::HttpRequest request, std::string object cMediaServer* server = cMediaServer::GetInstance(); cMediaManager& manager = server->GetManager(); - std::auto_ptr<cResourceStreamer> streamer(manager.GetResourceStreamer(objectID, resourceID)); + boost::shared_ptr<cResourceStreamer> streamer(manager.GetResourceStreamer(objectID, resourceID)); if(!streamer.get()){ reply.out() << "Object ID not found or invalid"; @@ -49,6 +49,11 @@ int doRequest(tnt::HttpReply reply, tnt::HttpRequest request, std::string object if(!request.isMethodHEAD()){ + if(!streamer->Open()){ + code = HTTP_INTERNAL_SERVER_ERROR; + goto ret; + } + int offset = 0, length = std::numeric_limits<long>::max(); bool hasRange = false; @@ -66,7 +71,10 @@ int doRequest(tnt::HttpReply reply, tnt::HttpRequest request, std::string object } } - streamer->Seek(offset, SEEK_SET); + if(!streamer->Seek(offset, SEEK_SET)){ + code = HTTP_INTERNAL_SERVER_ERROR; + goto ret; + } } char buffer[KB(16)]; @@ -97,9 +105,10 @@ int doRequest(tnt::HttpReply reply, tnt::HttpRequest request, std::string object } ret: + streamer->Close(); //reply.out() << std::flush; return code; -</%cpp> -<# +//</%cpp> +//<# } -#> +//#> diff --git a/include/media/mediaManager.h b/include/media/mediaManager.h index 09b3919..18cf458 100644 --- a/include/media/mediaManager.h +++ b/include/media/mediaManager.h @@ -40,13 +40,14 @@ public: std::string GetTransferMode(const std::string& requestedMode ) const; bool Seekable() const; - bool Open(string uri); + bool Open(); size_t Read(char* buf, size_t bufLen); bool Seek(size_t offset, int origin); void Close(); }; class cMediaManager : public cThread { + friend class upnp::cPluginManager; private: struct MediaRequest { @@ -80,7 +81,8 @@ public: cMediaManager(); virtual ~cMediaManager(); - void SetDatabaseFile(string file); + void SetPluginDirectory(const string& directory); + void SetDatabaseFile(const string& file); bool Initialise(); @@ -108,11 +110,23 @@ private: cUPnPResourceProvider* CreateResourceProvider(const std::string& uri); - uint32_t mSystemUpdateID; - IdList mEventedContainerUpdateIDs; - StringList mScanDirectories; - string mDatabaseFile; - tntdb::Connection mConnection; + void AddProviderFunctor(upnp::cPluginManager::FunctionPtr providerFunctor); + void AddProfiler(cMediaProfiler* profiler); + + uint32_t systemUpdateID; + IdList eventedContainerUpdateIDs; + StringList scanDirectories; + string databaseFile; + string pluginDirectory; + tntdb::Connection connection; + + upnp::cPluginManager* pluginManager; + + typedef std::map<std::string, upnp::cPluginManager::FunctionPtr> ProviderMap; + typedef std::list<boost::shared_ptr<cMediaProfiler>> ProfilerList; + + ProviderMap providers; + ProfilerList profilers; }; diff --git a/include/plugin.h b/include/plugin.h index da68ba9..6b4a94d 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -15,6 +15,8 @@ using namespace std; +#define UPNPPLUGIN_VERSION "1.0.0" + namespace upnp { namespace property { diff --git a/include/pluginManager.h b/include/pluginManager.h index 215f224..5c38e34 100644 --- a/include/pluginManager.h +++ b/include/pluginManager.h @@ -9,21 +9,43 @@ #define PLUGINMANAGER_H_ #include "../include/plugin.h" +#include <string> +#include <map> +#include <list> namespace upnp { +class cMediaManager; + class cPluginManager { public: - cUPnPResourceProvider* CreateResourceProviderInstance(const std::string& schema); -private: - void LoadPlugins(); - void UnloadPlugins(); + typedef void*(*FunctionPtr)(void); - typedef std::list<cUPnPResourceProvider> ProviderList; - typedef std::list<cMediaProfiler> ProfilerList; + cPluginManager(cMediaManager* manager); + virtual ~cPluginManager(); + + bool LoadPlugins(const std::string& directory); +private: + class DLL { + public: + DLL(const std::string& file); + virtual ~DLL(); + + bool Load(); + FunctionPtr GetProvider() const { return provider; }; + FunctionPtr GetProfiler() const { return profiler; }; + private: + std::string file; + void* handle; + FunctionPtr provider; + FunctionPtr profiler; + }; + + typedef std::list<boost::shared_ptr<DLL>> DLLList; + + DLLList dlls; + cMediaManager* manager; - ProviderList providers; - ProfilerList profilers; }; } // namespace upnp diff --git a/media/mediaManager.cpp b/media/mediaManager.cpp index b6de322..8b9cce7 100644 --- a/media/mediaManager.cpp +++ b/media/mediaManager.cpp @@ -19,6 +19,8 @@ namespace upnp { +#define DEFAULTPLUGINDIR PLUGINDIR + static const char* DIDLFragment = "<DIDL-Lite " "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " @@ -79,13 +81,13 @@ bool cResourceStreamer::Seekable() const { return provider->Seekable(); } -bool cResourceStreamer::Open(string uri){ - if(!provider) return false; - return provider->Open(uri); +bool cResourceStreamer::Open(){ + if(!provider || !resource) return false; + return provider->Open(resource->GetResourceUri()); } size_t cResourceStreamer::Read(char* buf, size_t bufLen){ - if(!provider) return 0; + if(!provider) return -1; return provider->Read(buf, bufLen); } @@ -100,33 +102,36 @@ void cResourceStreamer::Close(){ cMediaManager::cMediaManager() -: mSystemUpdateID(0) -, mDatabaseFile("metadata.db") +: systemUpdateID(0) +, databaseFile("metadata.db") +, pluginDirectory(DEFAULTPLUGINDIR) +, pluginManager(NULL) { } cMediaManager::~cMediaManager(){ + delete pluginManager; } uint32_t cMediaManager::GetSystemUpdateID() const { - return mSystemUpdateID; + return systemUpdateID; } IdList cMediaManager::GetContainerUpdateIDs(bool unevented){ - IdList list = mEventedContainerUpdateIDs; + IdList list = eventedContainerUpdateIDs; if(!unevented) - mEventedContainerUpdateIDs.clear(); + eventedContainerUpdateIDs.clear(); return list; } void cMediaManager::OnContainerUpdate(string uri, long updateID){ - ++mSystemUpdateID; + ++systemUpdateID; - mEventedContainerUpdateIDs[tools::GenerateUUIDFromURL(uri)] = updateID; + eventedContainerUpdateIDs[tools::GenerateUUIDFromURL(uri)] = updateID; - mScanDirectories.push_back(uri); + scanDirectories.push_back(uri); // Start scanning for changed files. Start(); @@ -163,7 +168,7 @@ StringList cMediaManager::GetSortCapabilities() const { } StringList cMediaManager::GetSupportedProtocolInfos() const { - tntdb::Connection conn = mConnection; + tntdb::Connection conn = connection; stringstream ss; @@ -195,10 +200,10 @@ int cMediaManager::CreateResponse(MediaRequest& request, const string& select, c << "`" << property::object::KEY_OBJECTID << "` = " << ":objectID"; - tntdb::Statement select1 = mConnection.prepare(select); - tntdb::Result result = mConnection.select(count); - tntdb::Statement select2 = mConnection.prepare(resources.str()); - tntdb::Statement select3 = mConnection.prepare(details.str()); + tntdb::Statement select1 = connection.prepare(select); + tntdb::Result result = connection.select(count); + tntdb::Statement select2 = connection.prepare(resources.str()); + tntdb::Statement select3 = connection.prepare(details.str()); StringList filterList = cFilterCriteria::parse(request.filter); @@ -264,7 +269,7 @@ int cMediaManager::CreateResponse(MediaRequest& request, const string& select, c for(tntdb::Statement::const_iterator it2 = select2.begin(); it2 != select2.end(); ++it2){ row2 = (*it2); - std::auto_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(row2.getString(property::resource::KEY_RESOURCE))); + boost::shared_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(row2.getString(property::resource::KEY_RESOURCE))); if(provider.get()){ string resourceURI = provider->GetHTTPUri(row2.getString(property::resource::KEY_RESOURCE)); @@ -387,17 +392,25 @@ cMediaManager::BrowseFlag cMediaManager::ToBrowseFlag(const std::string& browseF } bool cMediaManager::Initialise(){ + + pluginManager = new upnp::cPluginManager(this); + + if(!pluginManager->LoadPlugins(pluginDirectory)){ + esyslog("UPnP\tError while loading upnp plugin directory '%s'", pluginDirectory); + return false; + } + try { stringstream ss; - ss << "sqlite:" << mDatabaseFile; + ss << "sqlite:" << databaseFile; - mConnection = tntdb::connect(ss.str()); + connection = tntdb::connect(ss.str()); dsyslog("UPNP\tPreparing database structure..."); if(CheckIntegrity()) return true; - mConnection.beginTransaction(); + connection.beginTransaction(); ss.str(string()); @@ -420,7 +433,7 @@ bool cMediaManager::Initialise(){ << "`" << property::object::KEY_OBJECT_UPDATE_ID << "` INTEGER" << ")"; - tntdb::Statement objectTable = mConnection.prepare(ss.str()); + tntdb::Statement objectTable = connection.prepare(ss.str()); objectTable.execute(); @@ -435,7 +448,7 @@ bool cMediaManager::Initialise(){ << " `value` TEXT" << ")"; - tntdb::Statement detailsTable = mConnection.prepare(ss.str()); + tntdb::Statement detailsTable = connection.prepare(ss.str()); detailsTable.execute(); @@ -458,7 +471,7 @@ bool cMediaManager::Initialise(){ << "`" << property::resource::KEY_COLOR_DEPTH << "` INTEGER" << ")"; - tntdb::Statement resourcesTable = mConnection.prepare(ss.str()); + tntdb::Statement resourcesTable = connection.prepare(ss.str()); resourcesTable.execute(); @@ -475,7 +488,7 @@ bool cMediaManager::Initialise(){ << "`" << property::object::KEY_LONG_DESCRIPTION << "`) " << " VALUES (:objectID, :parentID, :title, :class, :restricted, :creator, :description, :longDescription)"; - tntdb::Statement rootContainer = mConnection.prepare(ss.str()); + tntdb::Statement rootContainer = connection.prepare(ss.str()); const cMediaServer::Description desc = cMediaServer::GetInstance()->GetServerDescription(); @@ -489,14 +502,14 @@ bool cMediaManager::Initialise(){ .setString("longDescription", desc.modelDescription) .execute(); - mConnection.commitTransaction(); + connection.commitTransaction(); return true; } catch (const std::exception& e) { esyslog("UPnP\tException occurred while initializing database: %s", e.what()); - mConnection.rollbackTransaction(); + connection.rollbackTransaction(); return false; } @@ -506,7 +519,7 @@ bool cMediaManager::Initialise(){ bool cMediaManager::CheckIntegrity(){ - tntdb::Statement checkTable = mConnection.prepare( + tntdb::Statement checkTable = connection.prepare( "SELECT name FROM sqlite_master WHERE type='table' AND name=:table;" ); @@ -529,7 +542,7 @@ bool cMediaManager::CheckIntegrity(){ << property::object::KEY_OBJECTID << "` = '0' AND `" << property::object::KEY_PARENTID << "` = '-1';"; - tntdb::Statement checkObject = mConnection.prepare(ss.str()); + tntdb::Statement checkObject = connection.prepare(ss.str()); if( checkObject.select().size() != 1 ){ isyslog("UPnP\tRoot item does not exist or more than one root item exist."); @@ -549,7 +562,7 @@ cResourceStreamer* cMediaManager::GetResourceStreamer(const string& objectID, in << ":objectID" << " ORDER BY resourceID ASC LIMIT " << resourceID << ",1"; - tntdb::Statement select = mConnection.prepare(resourceSQL.str()); + tntdb::Statement select = connection.prepare(resourceSQL.str()); tntdb::Result result = select.setString("objectID", objectID) .select(); @@ -598,9 +611,14 @@ cUPnPResourceProvider* cMediaManager::CreateResourceProvider(const string& uri){ return NULL; } -void cMediaManager::SetDatabaseFile(string file){ - if(file.empty()) mDatabaseFile = "metadata.db"; - else mDatabaseFile = file; +void cMediaManager::SetDatabaseFile(const string& file){ + if(file.empty()) databaseFile = "metadata.db"; + else databaseFile = file; +} + +void cMediaManager::SetPluginDirectory(const string& directory){ + if(directory.empty()) pluginDirectory = DEFAULTPLUGINDIR; + else pluginDirectory = directory; } void cMediaManager::Action(){ diff --git a/media/pluginManager.cpp b/media/pluginManager.cpp index a3f9c75..662ae4b 100644 --- a/media/pluginManager.cpp +++ b/media/pluginManager.cpp @@ -8,6 +8,8 @@ #include "../include/plugin.h" #include "../include/tools.h" #include <string> +#include <dlfcn.h> +#include <dirent.h> using namespace std; @@ -252,6 +254,79 @@ bool cUPnPResourceProvider::Seek(size_t offset, int origin){ void cUPnPResourceProvider::Close(){ } -} // namespace uünü +upnp::cPluginManager::cPluginManager(cMediaManager* manager) +: manager(manager) +{} + +upnp::cPluginManager::~cPluginManager(){} + +#define UPNPPLUGIN_PREFIX "libupnp-" +#define SO_INDICATOR ".so." + +bool upnp::cPluginManager::LoadPlugins(const string& directory){ + + DIR* dirHandle; + struct dirent* dirEntry; + + if((dirHandle = opendir(directory.c_str())) == NULL){ + esyslog("UPnP\tLoading directory '%s' failed. Errno: %d", directory.c_str(), errno); + return false; + } + + string filename; + while ((dirEntry = readdir(dirHandle)) != NULL) { + filename = dirEntry->d_name; + if(filename.compare(".") || filename.compare("..")){ + if(filename.find(UPNPPLUGIN_PREFIX,0) == 0 && + filename.find(UPNPPLUGIN_VERSION) != string::npos && + filename.find(SO_INDICATOR) != string::npos) + { + boost::shared_ptr<DLL> dll(new DLL(filename)); + if(dll->Load()) + dlls.push_back(dll); + } + } + } + closedir(dp); + + return true; +} + +upnp::cPluginManager::DLL::DLL(const string& f) +: file(f) +, handle(NULL) +, provider(NULL) +, profiler(NULL) +{ +} + +bool upnp::cPluginManager::DLL::Load(){ + if(handle) + return true; + + handle = dlopen(file.c_str(), RTLD_NOW); + + const char* error = dlerror(); + if(!error){ + provider = (FunctionPtr)dlsym(handle, "UPnPCreateResourceProvider"); + if (!(error = dlerror())){ + isyslog("UPnP\tFound provider in %s", file.c_str()); + } + profiler = (FunctionPtr)dlsym(handle, "UPnPCreateMediaProfiler"); + if (!(error = dlerror())){ + isyslog("UPnP\tFound profiler in %s", file.c_str()); + } + } + + return false; +} + +upnp::cPluginManager::DLL::~DLL() +{ + if(handle) + dlclose(handle); +} + +} // namespace upnp diff --git a/server/server.cpp b/server/server.cpp index acfe7a1..95e7f16 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -179,6 +179,7 @@ bool cMediaServer::Initialize(){ } mWebserver = new cWebserver(GetServerIPAddress()); + mMediaManager = new cMediaManager(); if(mCurrentConfiguration.expertSettings){ @@ -204,12 +205,12 @@ bool cMediaServer::Initialize(){ mWebserver->SetPresentationUrl(ss.str()); } - if(mCurrentConfiguration.webServerPort) mWebserver->SetListenerPort(mCurrentConfiguration.webServerPort); - } - mMediaManager = new cMediaManager(); + if(!mCurrentConfiguration.databaseFile.empty()) + mMediaManager->SetDatabaseFile(mCurrentConfiguration.databaseFile); + } ret = UpnpSetMaxContentLength(GetMaxContentLength()); |