diff options
author | methodus <methodus@web.de> | 2012-09-29 22:48:30 +0200 |
---|---|---|
committer | methodus <methodus@web.de> | 2012-09-29 22:48:30 +0200 |
commit | 97b083ac833ada3c18a5639ee782e575ca583acf (patch) | |
tree | 257848635d775396e86f9aeab682b2a719d2d097 | |
parent | 4cd5debf236f4d4315cce30c4cd11e392f00886a (diff) | |
download | vdr-plugin-upnp-97b083ac833ada3c18a5639ee782e575ca583acf.tar.gz vdr-plugin-upnp-97b083ac833ada3c18a5639ee782e575ca583acf.tar.bz2 |
Streamer almost completed. First tests successful. However, there are no plugins, yet.
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | common/tools.cpp | 4 | ||||
-rw-r--r-- | include/media/mediaManager.h | 11 | ||||
-rw-r--r-- | include/plugin.h | 14 | ||||
-rw-r--r-- | include/pluginManager.h | 11 | ||||
-rw-r--r-- | include/tools.h | 2 | ||||
-rw-r--r-- | media/mediaManager.cpp | 116 | ||||
-rw-r--r-- | media/pluginManager.cpp | 4 | ||||
-rw-r--r-- | server/webserver.cpp | 9 |
9 files changed, 128 insertions, 46 deletions
@@ -54,7 +54,8 @@ DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' TNTOBJ = httptnt/deviceDescription.o \ httptnt/cds_scpd.o \ - httptnt/cms_scpd.o + httptnt/cms_scpd.o \ + httptnt/resourceStreamer.o OBJS = $(PLUGIN).o \ server/server.o \ diff --git a/common/tools.cpp b/common/tools.cpp index d64b86b..61220c6 100644 --- a/common/tools.cpp +++ b/common/tools.cpp @@ -137,8 +137,8 @@ string GenerateUUIDRandomly(){ return uuid.str(); } -void StringExplode(string str, string separator, StringVector results) { - int found; +void StringExplode(string str, string separator, StringVector& results) { + string::size_type found; found = str.find_first_of(separator); while(found != string::npos){ if(found > 0){ diff --git a/include/media/mediaManager.h b/include/media/mediaManager.h index 7e97799..09b3919 100644 --- a/include/media/mediaManager.h +++ b/include/media/mediaManager.h @@ -14,6 +14,7 @@ #include <stdint.h> #include <tntdb/connection.h> #include <tntdb/connect.h> +#include "../../include/plugin.h" #include "../../include/tools.h" namespace upnp { @@ -32,12 +33,12 @@ private: cResourceStreamer(cMediaManager* manager, cUPnPResourceProvider* provider, cMetadata::Resource* resource); public: + virtual ~cResourceStreamer(); std::string GetContentFeatures() const; size_t GetContentLength() const; std::string GetContentType() const; std::string GetTransferMode(const std::string& requestedMode ) const; - std::string GetRange() const; - std::string GetAvailableSeekRange(const std::string& seekRequest) const; + bool Seekable() const; bool Open(string uri); size_t Read(char* buf, size_t bufLen); @@ -92,9 +93,9 @@ public: int Browse(BrowseRequest& request); int Search(SearchRequest& request); - static BrowseFlag ToBrowseFlag(std::string browseFlag); + static BrowseFlag ToBrowseFlag(const std::string& browseFlag); - cResourceStreamer* GetResourceStreamer(std::string objectID); + cResourceStreamer* GetResourceStreamer(const std::string& objectID, int resourceID = 0); private: @@ -105,6 +106,8 @@ private: void OnContainerUpdate(string uri, long updateID); + cUPnPResourceProvider* CreateResourceProvider(const std::string& uri); + uint32_t mSystemUpdateID; IdList mEventedContainerUpdateIDs; StringList mScanDirectories; diff --git a/include/plugin.h b/include/plugin.h index fe2c5f6..da68ba9 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -348,6 +348,8 @@ public: */ virtual string GetHTTPUri(string uri); + virtual bool Seekable() const; + /** * Opens a resource * @@ -480,13 +482,9 @@ public: * object which contains all information about the resource. * * If the plugin is not able to fill the required information about the resource, - * it MUST return NULL. The plugin manager will then rotate through all the + * it MUST return false. The plugin manager will then rotate through all the * registered plugins until it finds a suitable one. * - * The required information MUST be set in the cMediaResource::Create function - * correctly. Otherwise the object creation might fail and a NULL pointer will - * be returned. - * * Additionally, a plugin implementor is advised to add as many as possible * metadata about a resource. Especially, information about the contents might * help the user. @@ -500,9 +498,11 @@ public: * A change of a resource is indicated by the resource provider. * * @param uri the absolute path to the resource. - * @return the media resource information. + * @param metadata the metadate object, where the information shall be saved. + * @return true, if this profiler was able to get the specific metadata, + * false, if not. */ - virtual cMetadata GetMetadata(string uri) = 0; + virtual bool GetMetadata(string uri, cMetadata& metadata) = 0; protected: }; diff --git a/include/pluginManager.h b/include/pluginManager.h index 90ee456..215f224 100644 --- a/include/pluginManager.h +++ b/include/pluginManager.h @@ -8,11 +8,22 @@ #ifndef PLUGINMANAGER_H_ #define PLUGINMANAGER_H_ +#include "../include/plugin.h" + namespace upnp { class cPluginManager { public: + cUPnPResourceProvider* CreateResourceProviderInstance(const std::string& schema); private: + void LoadPlugins(); + void UnloadPlugins(); + + typedef std::list<cUPnPResourceProvider> ProviderList; + typedef std::list<cMediaProfiler> ProfilerList; + + ProviderList providers; + ProfilerList profilers; }; } // namespace upnp diff --git a/include/tools.h b/include/tools.h index 008b74a..cf34b42 100644 --- a/include/tools.h +++ b/include/tools.h @@ -122,7 +122,7 @@ namespace tools { string GenerateUUIDFromURL(string url); string GenerateUUIDRandomly(); - void StringExplode(string str, string separator, StringVector results); + void StringExplode(string str, string separator, StringVector& results); } namespace ixml { diff --git a/media/mediaManager.cpp b/media/mediaManager.cpp index 33fafbd..b6de322 100644 --- a/media/mediaManager.cpp +++ b/media/mediaManager.cpp @@ -15,6 +15,7 @@ #include <tntdb/statement.h> #include <tntdb/result.h> #include <upnp/ixml.h> +#include <memory> namespace upnp { @@ -38,19 +39,24 @@ cResourceStreamer::cResourceStreamer(cMediaManager* manager, cUPnPResourceProvid , manager(manager) { if(resource) - tools::StringExplode(resource->protocolInfo,"*",protocolInfo); + tools::StringExplode(resource->GetProtocolInfo(),":",protocolInfo); +} + +cResourceStreamer::~cResourceStreamer(){ + delete resource; + delete provider; } std::string cResourceStreamer::GetContentFeatures() const { if(resource == NULL) return string(); - return protocolInfo[3]; + return protocolInfo.size() == 4 ? protocolInfo[3] : string(); } std::string cResourceStreamer::GetContentType() const { if(resource == NULL) return string(); - return protocolInfo[2]; + return protocolInfo.size() == 4 ? protocolInfo[2] : string(); } size_t cResourceStreamer::GetContentLength() const { @@ -68,28 +74,28 @@ std::string cResourceStreamer::GetTransferMode(const string&) const { else return "Interactive"; } -std::string cResourceStreamer::GetRange() const { - return string(); -} - -std::string cResourceStreamer::GetAvailableSeekRange(const string& seekRequest) const { - return string(); +bool cResourceStreamer::Seekable() const { + if(!provider) return false; + return provider->Seekable(); } bool cResourceStreamer::Open(string uri){ + if(!provider) return false; return provider->Open(uri); } size_t cResourceStreamer::Read(char* buf, size_t bufLen){ + if(!provider) return 0; return provider->Read(buf, bufLen); } bool cResourceStreamer::Seek(size_t offset, int origin){ + if(!provider) return false; return provider->Seek(offset, origin); } void cResourceStreamer::Close(){ - provider->Close(); + if(provider) provider->Close(); } @@ -216,6 +222,7 @@ int cMediaManager::CreateResponse(MediaRequest& request, const string& select, c bool isContainer; + if(upnpClass.find("object.item",0) == 0){ object = ixmlDocument_createElement(DIDLDoc, "item"); isContainer = false; @@ -257,20 +264,24 @@ int cMediaManager::CreateResponse(MediaRequest& request, const string& select, c for(tntdb::Statement::const_iterator it2 = select2.begin(); it2 != select2.end(); ++it2){ row2 = (*it2); - string resourceURI = row2.getString(property::resource::KEY_RESOURCE); + std::auto_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(row2.getString(property::resource::KEY_RESOURCE))); + + if(provider.get()){ + string resourceURI = provider->GetHTTPUri(row2.getString(property::resource::KEY_RESOURCE)); - IXML_Element* resource = ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOURCE, resourceURI); + IXML_Element* resource = ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOURCE, resourceURI); - if(resource){ - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_PROTOCOL_INFO, row2.getString(property::resource::KEY_PROTOCOL_INFO)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITRATE, row2.getString(property::resource::KEY_BITRATE)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITS_PER_SAMPLE, row2.getString(property::resource::KEY_BITS_PER_SAMPLE)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_COLOR_DEPTH, row2.getString(property::resource::KEY_COLOR_DEPTH)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_DURATION, row2.getString(property::resource::KEY_DURATION)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_NR_AUDIO_CHANNELS, row2.getString(property::resource::KEY_NR_AUDIO_CHANNELS)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOLUTION, row2.getString(property::resource::KEY_RESOLUTION)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SAMPLE_FREQUENCY, row2.getString(property::resource::KEY_SAMPLE_FREQUENCY)); - ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SIZE, tools::ToString(row2.getInt64(property::resource::KEY_SIZE))); + if(resource){ + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_PROTOCOL_INFO, row2.getString(property::resource::KEY_PROTOCOL_INFO)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITRATE, row2.getString(property::resource::KEY_BITRATE)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_BITS_PER_SAMPLE, row2.getString(property::resource::KEY_BITS_PER_SAMPLE)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_COLOR_DEPTH, row2.getString(property::resource::KEY_COLOR_DEPTH)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_DURATION, row2.getString(property::resource::KEY_DURATION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_NR_AUDIO_CHANNELS, row2.getString(property::resource::KEY_NR_AUDIO_CHANNELS)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOLUTION, row2.getString(property::resource::KEY_RESOLUTION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SAMPLE_FREQUENCY, row2.getString(property::resource::KEY_SAMPLE_FREQUENCY)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_SIZE, tools::ToString(row2.getInt64(property::resource::KEY_SIZE))); + } } } @@ -366,7 +377,7 @@ int cMediaManager::Search(SearchRequest& request){ return (request.totalMatches == 0 && request.numberReturned == 0) ? UPNP_CDS_E_CANT_PROCESS_REQUEST : UPNP_E_SUCCESS; } -cMediaManager::BrowseFlag cMediaManager::ToBrowseFlag(std::string browseFlag) { +cMediaManager::BrowseFlag cMediaManager::ToBrowseFlag(const std::string& browseFlag) { if (browseFlag.compare("BrowseMetadata") == 0) return CD_BROWSE_METADATA; else if (browseFlag.compare("BrowseDirectChildren") == 0) @@ -528,6 +539,65 @@ bool cMediaManager::CheckIntegrity(){ return true; } +cResourceStreamer* cMediaManager::GetResourceStreamer(const string& objectID, int resourceID){ + dsyslog("UPnP\tTry to stream resource[%d] of objectID %s", resourceID, objectID.c_str()); + + stringstream resourceSQL; + + resourceSQL << "SELECT * FROM " << db::Resources << " WHERE " + << "`" << property::object::KEY_OBJECTID << "` = " + << ":objectID" + << " ORDER BY resourceID ASC LIMIT " << resourceID << ",1"; + + tntdb::Statement select = mConnection.prepare(resourceSQL.str()); + + tntdb::Result result = select.setString("objectID", objectID) + .select(); + + if(result.size() == 0) return NULL; + + tntdb::Row row = result.getRow(0); + + cMetadata::Resource* resource = new cMetadata::Resource(); + + bool ret = true; + if(!row.isNull(property::resource::KEY_BITRATE)) + ret = resource->SetBitrate(row.getInt32(property::resource::KEY_BITRATE)); + if(!row.isNull(property::resource::KEY_BITS_PER_SAMPLE)) + ret = resource->SetBitsPerSample(row.getInt32(property::resource::KEY_BITS_PER_SAMPLE)); + if(!row.isNull(property::resource::KEY_COLOR_DEPTH)) + ret = resource->SetColorDepth(row.getInt32(property::resource::KEY_COLOR_DEPTH)); + if(!row.isNull(property::resource::KEY_DURATION)) + ret = resource->SetDuration(row.getString(property::resource::KEY_DURATION)); + if(!row.isNull(property::resource::KEY_NR_AUDIO_CHANNELS)) + ret = resource->SetNrAudioChannels(row.getInt32(property::resource::KEY_NR_AUDIO_CHANNELS)); + if(!row.isNull(property::resource::KEY_PROTOCOL_INFO)) + ret = resource->SetProtocolInfo(row.getString(property::resource::KEY_PROTOCOL_INFO)); + if(!row.isNull(property::resource::KEY_RESOLUTION)) + ret = resource->SetResolution(row.getString(property::resource::KEY_RESOLUTION)); + if(!row.isNull(property::resource::KEY_RESOURCE)) + ret = resource->SetResourceUri(row.getString(property::resource::KEY_RESOURCE)); + if(!row.isNull(property::resource::KEY_SAMPLE_FREQUENCY)) + ret = resource->SetSampleFrequency(row.getInt32(property::resource::KEY_SAMPLE_FREQUENCY)); + if(!row.isNull(property::resource::KEY_SIZE)) + ret = resource->SetSize(row.getInt32(property::resource::KEY_SIZE)); + + if(!ret) { + delete resource; + return NULL; + } + + cUPnPResourceProvider* provider = CreateResourceProvider(resource->GetResourceUri()); + + cResourceStreamer* streamer = new cResourceStreamer(this, provider, resource); + + return streamer; +} + +cUPnPResourceProvider* cMediaManager::CreateResourceProvider(const string& uri){ + return NULL; +} + void cMediaManager::SetDatabaseFile(string file){ if(file.empty()) mDatabaseFile = "metadata.db"; else mDatabaseFile = file; diff --git a/media/pluginManager.cpp b/media/pluginManager.cpp index a9bfd73..a3f9c75 100644 --- a/media/pluginManager.cpp +++ b/media/pluginManager.cpp @@ -233,6 +233,10 @@ string cUPnPResourceProvider::GetHTTPUri(string uri){ return string(); } +bool cUPnPResourceProvider::Seekable() const { + return false; +} + bool cUPnPResourceProvider::Open(string uri){ return false; } diff --git a/server/webserver.cpp b/server/webserver.cpp index d2a25f0..f1b589b 100644 --- a/server/webserver.cpp +++ b/server/webserver.cpp @@ -66,14 +66,7 @@ bool cWebserver::Initialise(){ mApplication.mapUrl(ss1.str(), ss2.str(), "static@tntnet"); - // Map static contents - ss1.clear(); ss1.str(string()); - ss1 << "^/" << mServiceUrl << "([^/.]+)"; - - ss2.clear(); ss2.str(string()); - ss2 << mServiceUrl << "$1"; - - mApplication.mapUrl(ss1.str(), ss2.str(), "serviceRedirect"); + mApplication.mapUrl("^/getStream", "resourceStreamer"); isyslog("UPnP\tUsing %s for static content delivery.", mWebserverRootDir.c_str()); |