summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormethodus <methodus@web.de>2012-09-30 15:52:54 +0200
committermethodus <methodus@web.de>2012-09-30 15:52:54 +0200
commitc8e697daf68f1c545ee3962d2f2e9b411c56b4f2 (patch)
tree48bc206a65ad956ab4284113c33d4b1d50398495
parentdd3e8652f530c35febcc3f595e38443395f79198 (diff)
downloadvdr-plugin-upnp-c8e697daf68f1c545ee3962d2f2e9b411c56b4f2.tar.gz
vdr-plugin-upnp-c8e697daf68f1c545ee3962d2f2e9b411c56b4f2.tar.bz2
Working on plugin interface. PluginManager almost done.
-rw-r--r--Makefile4
-rw-r--r--httptnt/resourceStreamer.ecpp37
-rw-r--r--include/media/mediaManager.h28
-rw-r--r--include/plugin.h2
-rw-r--r--include/pluginManager.h38
-rw-r--r--media/mediaManager.cpp84
-rw-r--r--media/pluginManager.cpp77
-rw-r--r--server/server.cpp7
8 files changed, 211 insertions, 66 deletions
diff --git a/Makefile b/Makefile
index 2b14a5b..aff0a2f 100644
--- a/Makefile
+++ b/Makefile
@@ -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());