diff options
-rw-r--r-- | include/media/profile.h | 16 | ||||
-rw-r--r-- | include/plugin.h | 2 | ||||
-rw-r--r-- | include/webserver.h | 2 | ||||
-rw-r--r-- | media/mediaManager.cpp | 44 | ||||
-rw-r--r-- | media/pluginManager.cpp | 6 | ||||
-rw-r--r-- | media/profile.cpp | 51 | ||||
-rw-r--r-- | plugins/profiler/vdrDVBProfiler/dvbProfiler.cpp | 115 | ||||
-rw-r--r-- | plugins/provider/vdrProvider/vdrProvider.cpp | 12 | ||||
-rw-r--r-- | server/webserver.cpp | 13 |
9 files changed, 195 insertions, 66 deletions
diff --git a/include/media/profile.h b/include/media/profile.h index e8b2bda..efac36d 100644 --- a/include/media/profile.h +++ b/include/media/profile.h @@ -58,6 +58,22 @@ struct DLNA4thField { string ToString(); }; +struct ProtocolInfo { + enum StreamType { + DLNA_STREAM_HTTP, + DLNA_STREAM_RTP + }; + + ProtocolInfo(); + ProtocolInfo(string contentType, DLNA4thField fourthField, StreamType type = DLNA_STREAM_HTTP); + + StreamType streamType; + string contentType; + DLNA4thField fourthField; + + string ToString(); +}; + namespace video { } diff --git a/include/plugin.h b/include/plugin.h index 6749336..d267bcd 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -374,7 +374,7 @@ public: * The implementor of a resource provider MUST either implement * this function or the file access methods below. */ - virtual string GetHTTPUri(const string& uri, const string& currentIP); + virtual string GetHTTPUri(const string& uri, const string& currentIP, const string& protocolInfo); virtual bool Seekable() const; diff --git a/include/webserver.h b/include/webserver.h index 39ca8df..02377d7 100644 --- a/include/webserver.h +++ b/include/webserver.h @@ -38,6 +38,8 @@ namespace upnp { const std::string GetStaticContentUrl() const; const std::string GetPresentationUrl() const; + const std::string GetThumbnailDir() const; + std::string GetListenerAddress() const { return mListenerAddress; } uint16_t GetListenerPort() const { return mListenerPort; } diff --git a/media/mediaManager.cpp b/media/mediaManager.cpp index 3df218a..377ed19 100644 --- a/media/mediaManager.cpp +++ b/media/mediaManager.cpp @@ -5,6 +5,7 @@ * Author: savop */ +#include "../include/webserver.h" #include "../include/media/mediaManager.h" #include "../include/pluginManager.h" #include "../include/server.h" @@ -326,27 +327,40 @@ int cMediaManager::CreateResponse(MediaRequest& request, const string& select, c select2.setString("objectID", objectID); + int i=0; + string resourceFile; + string resourceURI; + stringstream tntnet; for(tntdb::Statement::const_iterator it2 = select2.begin(); it2 != select2.end(); ++it2){ row2 = (*it2); - boost::shared_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(row2.getString(property::resource::KEY_RESOURCE))); + string resourceFile = row2.getString(property::resource::KEY_RESOURCE); + boost::shared_ptr<cUPnPResourceProvider> provider(CreateResourceProvider(resourceFile)); + tntnet.str(string()); if(provider.get()){ - string resourceURI = provider->GetHTTPUri(row2.getString(property::resource::KEY_RESOURCE), cMediaServer::GetInstance()->GetServerIPAddress()); - - 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))); + resourceURI = provider->GetHTTPUri(resourceFile, cMediaServer::GetInstance()->GetServerIPAddress(), row2.getString(property::resource::KEY_PROTOCOL_INFO)); + if(resourceURI.empty()){ + tntnet << cMediaServer::GetInstance()->GetWebserver().GetBaseUrl() << "getStream?objectID=" << objectID << "&resourceID=" << i++; + resourceURI = tntnet.str(); } + } else if(resourceFile.find("thumb://",0) == 0 && !resourceFile.substr(8).empty()) { + tntnet << cMediaServer::GetInstance()->GetWebserver().GetBaseUrl() << "thumbs/" << resourceFile.substr(8); + resourceURI = tntnet.str(); + } + + IXML_Element* resource = ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, object, property::resource::KEY_RESOURCE, resourceURI); + + if(resource){ + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_PROTOCOL_INFO, row2.getString(property::resource::KEY_PROTOCOL_INFO)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_BITRATE, row2.getString(property::resource::KEY_BITRATE)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_BITS_PER_SAMPLE, row2.getString(property::resource::KEY_BITS_PER_SAMPLE)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_COLOR_DEPTH, row2.getString(property::resource::KEY_COLOR_DEPTH)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_DURATION, row2.getString(property::resource::KEY_DURATION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_NR_AUDIO_CHANNELS, row2.getString(property::resource::KEY_NR_AUDIO_CHANNELS)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_RESOLUTION, row2.getString(property::resource::KEY_RESOLUTION)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_SAMPLE_FREQUENCY, row2.getString(property::resource::KEY_SAMPLE_FREQUENCY)); + ixml::IxmlAddFilteredProperty(filterList, DIDLDoc, resource, property::resource::KEY_SIZE, tools::ToString(row2.getInt64(property::resource::KEY_SIZE))); } } diff --git a/media/pluginManager.cpp b/media/pluginManager.cpp index ea7f82a..66cf69d 100644 --- a/media/pluginManager.cpp +++ b/media/pluginManager.cpp @@ -278,7 +278,7 @@ bool cUPnPResourceProvider::GetMetadata(const string& uri, cMetadata& metadata){ } -string cUPnPResourceProvider::GetHTTPUri(const string&, const string&){ +string cUPnPResourceProvider::GetHTTPUri(const string&, const string&, const string&){ return string(); } @@ -393,16 +393,12 @@ bool upnp::cPluginManager::DLL::Load(){ if (!(error = dlerror())){ isProvider = true; return true; - } else { - cerr << error << endl; } function = (FuncPtr)dlsym(handle, "UPnPCreateMediaProfiler"); if (!(error = dlerror())){ isProvider = false; return true; - } else { - cerr << error << endl; } } else { cerr << "Error while opening plugin: " << error << endl; diff --git a/media/profile.cpp b/media/profile.cpp index 0cff566..8c9df6f 100644 --- a/media/profile.cpp +++ b/media/profile.cpp @@ -11,6 +11,42 @@ using namespace upnp; +ProtocolInfo::ProtocolInfo() +: streamType(DLNA_STREAM_HTTP) +, contentType(string()) +, fourthField(DLNA4thField()) +{ +} + +ProtocolInfo::ProtocolInfo(string ct, DLNA4thField ffld, StreamType t) +: streamType(t) +, contentType(ct) +, fourthField(ffld) +{ +} + +string ProtocolInfo::ToString(){ + if(contentType.empty()) return string(); + + stringstream ss; + switch(streamType){ + case DLNA_STREAM_HTTP: + ss << "http-get:"; + break; + case DLNA_STREAM_RTP: + ss << "rtsp-rtp-udp:"; + break; + } + + ss << "*:"; + + ss << contentType << ":"; + + ss << fourthField.ToString(); + + return ss.str(); +} + DLNA4thField::DLNA4thField() : profile(string()) , operations(DLNA_OPERATION_NONE) @@ -30,17 +66,20 @@ DLNA4thField::DLNA4thField(string pn, uint8_t op, string ps, bool ci, uint32_t f } string DLNA4thField::ToString(){ - stringstream ss; - if(profile.empty()) return "*"; - ss << "DLNA.ORG_PN=" << profile << ";"; + stringstream ss; + ss << "DLNA.ORG_PN=" << profile; + + if(primaryFlags){ + ss << ";"; - ss << "DLNA.ORG_OP=" << bitset<2>(operations) << ";"; + ss << "DLNA.ORG_OP=" << bitset<2>(operations) << ";"; - ss << "DLNA.ORG_CI=" << bitset<1>(conversionIndicator) << ";"; + ss << "DLNA.ORG_CI=" << bitset<1>(conversionIndicator) << ";"; - ss << "DLNA.ORG_FLAGS=" << hex << primaryFlags << "000000000000000000000000" << ";"; + ss << "DLNA.ORG_FLAGS=" << hex << primaryFlags << "000000000000000000000000"; + } return ss.str(); } diff --git a/plugins/profiler/vdrDVBProfiler/dvbProfiler.cpp b/plugins/profiler/vdrDVBProfiler/dvbProfiler.cpp index 4bb14c6..06948ac 100644 --- a/plugins/profiler/vdrDVBProfiler/dvbProfiler.cpp +++ b/plugins/profiler/vdrDVBProfiler/dvbProfiler.cpp @@ -5,6 +5,8 @@ * Author: savop */ +#include <server.h> +#include <webserver.h> #include <vdr/channels.h> #include <vdr/epg.h> #include <vdr/tools.h> @@ -13,6 +15,8 @@ #include <string> #include <sstream> #include <media/profile.h> +#include <boost/date_time/gregorian/gregorian.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> using namespace std; @@ -65,42 +69,85 @@ private: metadata.SetProperty(cMetadata::Property(property::object::KEY_CHANNEL_NR, (long int)channel->Number())); // Now, we try to get the present event of the schedule - metadata.SetProperty(cMetadata::Property(property::object::KEY_TITLE, string(channel->Name()))); - - cMetadata::Resource resource; - - stringstream protocolInfo; - - protocolInfo << "http-get:*:video/mpeg:"; - - DLNA4thField fourthfield; - - switch (channel->Vtype()) { - case 0x02: - fourthfield = DLNA4thField("MPEG_TS_SD_EU_ISO", DLNA_OPERATION_NONE, - DLNA_PLAYSPEEDS_NONE, DLNA_CONVERSION_NONE, - DLNA_FLAG_STREAMING_TRANSFER | - DLNA_FLAG_SN_INCREASE | - DLNA_FLAG_VERSION_1_5 ); - break; - case 0x1B: - fourthfield = DLNA4thField("AVC_TS_HD_EU_ISO", DLNA_OPERATION_NONE, - DLNA_PLAYSPEEDS_NONE, DLNA_CONVERSION_NONE, - DLNA_FLAG_STREAMING_TRANSFER | - DLNA_FLAG_SN_INCREASE | - DLNA_FLAG_VERSION_1_5 ); - break; - default: - return false; - break; - } - - protocolInfo << fourthfield.ToString(); + { + cSchedulesLock lock; + const cSchedules* schedules = cSchedules::Schedules(lock); + const cSchedule* schedule = (schedules) ? schedules->GetSchedule(channelID) : NULL; + const cEvent* event = (schedule) ? schedule->GetPresentEvent() : NULL; + + if(event){ + stringstream title; + title << channel->Name() << ": " << event->Title(); + metadata.SetProperty(cMetadata::Property(property::object::KEY_TITLE, title.str())); + + boost::posix_time::ptime startTime, endTime; + startTime = boost::posix_time::from_time_t(event->StartTime()); + endTime = boost::posix_time::from_time_t(event->EndTime()); + + metadata.SetProperty(cMetadata::Property(property::object::KEY_DATE, boost::gregorian::to_iso_extended_string(startTime.date()))); + metadata.SetProperty(cMetadata::Property(property::object::KEY_SCHEDULED_START, boost::posix_time::to_iso_extended_string(startTime))); + metadata.SetProperty(cMetadata::Property(property::object::KEY_SCHEDULED_END, boost::posix_time::to_iso_extended_string(endTime))); + metadata.SetProperty(cMetadata::Property(property::object::KEY_DESCRIPTION, string(event->ShortText()))); + metadata.SetProperty(cMetadata::Property(property::object::KEY_LONG_DESCRIPTION, string(event->Description()))); + } else { + metadata.SetProperty(cMetadata::Property(property::object::KEY_TITLE, string(channel->Name()))); + } + + cMetadata::Resource resource; + + DLNA4thField fourthfield; + switch (channel->Vtype()) { + case 0x02: + fourthfield = DLNA4thField("MPEG_TS_SD_EU_ISO", DLNA_OPERATION_NONE, + DLNA_PLAYSPEEDS_NONE, DLNA_CONVERSION_NONE, + DLNA_FLAG_STREAMING_TRANSFER | + DLNA_FLAG_SN_INCREASE | + DLNA_FLAG_VERSION_1_5 ); + break; + case 0x1B: + fourthfield = DLNA4thField("AVC_TS_HD_EU_ISO", DLNA_OPERATION_NONE, + DLNA_PLAYSPEEDS_NONE, DLNA_CONVERSION_NONE, + DLNA_FLAG_STREAMING_TRANSFER | + DLNA_FLAG_SN_INCREASE | + DLNA_FLAG_VERSION_1_5 ); + break; + default: + return false; + break; + } + + resource.SetResourceUri(uri); + resource.SetProtocolInfo(ProtocolInfo("video/mpeg", fourthfield).ToString()); + + if(event){ + boost::posix_time::time_duration duration = boost::posix_time::seconds(event->Duration()); + resource.SetDuration(boost::posix_time::to_simple_string(duration)); + } + + metadata.AddResource(resource); - resource.SetResourceUri(uri); - resource.SetProtocolInfo(protocolInfo.str()); + } - metadata.AddResource(resource); + stringstream ss; + cMetadata::Resource thumbnail; + ss.str(string()); + ss << "channelIcons/" << channel->Name() << ".jpg"; + + stringstream filename, uriStrm; + filename << cMediaServer::GetInstance()->GetWebserver().GetThumbnailDir() << ss.str(); + uriStrm << "thumb://" << ss.str(); + + struct stat fileStat; + + dsyslog("DVBProvider\tTry to get thumbnail for %s in %s", channel->Name(), filename.str().c_str()); + if(stat(filename.str().c_str(), &fileStat) == 0){ + thumbnail.SetResourceUri(uriStrm.str()); + thumbnail.SetProtocolInfo(ProtocolInfo("image/jpeg", DLNA4thField("JPEG_TN")).ToString()); + thumbnail.SetSize(fileStat.st_size); + metadata.AddResource(thumbnail); + } else { + dsyslog("DVBProvider\tFailed to stat %s", filename.str().c_str()); + } return true; } diff --git a/plugins/provider/vdrProvider/vdrProvider.cpp b/plugins/provider/vdrProvider/vdrProvider.cpp index b98568f..4e765bf 100644 --- a/plugins/provider/vdrProvider/vdrProvider.cpp +++ b/plugins/provider/vdrProvider/vdrProvider.cpp @@ -9,12 +9,10 @@ #include <vdr/epg.h> #include <vdr/channels.h> #include <vdr/tools.h> -#include <vdr/config.h> #include <string> #include <sstream> +#include <algorithm> #include <tools.h> -#include <vdr/thread.h> -#include <iostream> #include <pwd.h> #include <unistd.h> @@ -108,18 +106,22 @@ public: return true; } - virtual string GetHTTPUri(const string& uri, const string& currentIP){ + virtual string GetHTTPUri(const string& uri, const string& currentIP, const string& pInfo){ if(!IsRootContainer(uri)) return string(); int port = 3000; stringstream ss; + string protocolInfo = pInfo.substr(pInfo.find_last_of(':')); + + std::replace(protocolInfo.begin(), protocolInfo.end(), ';','+'); + ss << "http://" << currentIP << ":" << port << "/" << "EXT;" << "PROG=cat;" - << "DLNA_contentFeatures=DLNA.ORG_PN=MPEG_TS_SD_EU_ISO+DLNA.ORG_OP=00+DLNA.ORG_CI=0+DLNA.ORG_FLAGS=ED100000000000000000000000000000" + << "DLNA_contentFeatures=" << protocolInfo << "/" << uri.substr(6); diff --git a/server/webserver.cpp b/server/webserver.cpp index f1b589b..2066c7f 100644 --- a/server/webserver.cpp +++ b/server/webserver.cpp @@ -66,6 +66,15 @@ bool cWebserver::Initialise(){ mApplication.mapUrl(ss1.str(), ss2.str(), "static@tntnet"); + // Map static contents + ss1.clear(); ss1.str(string()); + ss1 << "^/thumbs/([^.]+.jpg)$"; + + ss2.clear(); ss2.str(string()); + ss2 << mWebserverRootDir << "/images/thumbs/$1"; + + mApplication.mapUrl(ss1.str(), ss2.str(), "static@tntnet"); + mApplication.mapUrl("^/getStream", "resourceStreamer"); isyslog("UPnP\tUsing %s for static content delivery.", mWebserverRootDir.c_str()); @@ -127,6 +136,10 @@ const std::string cWebserver::GetStaticContentUrl() const { return GetBaseUrl() + mStaticContentUrl; } +const std::string cWebserver::GetThumbnailDir() const { + return mWebserverRootDir + "images/thumbs/"; +} + cWebserver::cWSThread::cWSThread(cWebserver& webServer) : mWebserver(webServer) { |