diff options
author | Sascha Montellese <sascha.montellese@gmail.com> | 2015-01-31 09:31:57 +0100 |
---|---|---|
committer | Sascha Montellese <sascha.montellese@gmail.com> | 2015-01-31 09:31:57 +0100 |
commit | 30350ffdb31e699b3ae01ca8bd9c57bc5fde0576 (patch) | |
tree | 94e4dd182249aaf1ea19433c5307c81e28fdc413 | |
parent | f624010c9b93ff6b8c71a09d94424c706f653bfd (diff) | |
parent | e46e41974cdf7e9b602bb3788454aff43bc59fc4 (diff) |
Merge pull request #6184 from 7pepo7/master-subtitles
upnp: support for external subtitles
-rw-r--r-- | lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp | 1 | ||||
-rw-r--r-- | lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp | 41 | ||||
-rw-r--r-- | lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h | 12 | ||||
-rw-r--r-- | lib/libUPnP/patches/0035-platinum-Changes-to-external-subtitles-over-UPnP-wor.patch | 122 | ||||
-rw-r--r-- | xbmc/FileItem.cpp | 5 | ||||
-rw-r--r-- | xbmc/FileItem.h | 1 | ||||
-rw-r--r-- | xbmc/network/upnp/UPnPInternal.cpp | 97 | ||||
-rw-r--r-- | xbmc/network/upnp/UPnPServer.cpp | 27 | ||||
-rw-r--r-- | xbmc/network/upnp/UPnPServer.h | 4 |
9 files changed, 309 insertions, 1 deletions
diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp index 7b853c8c9a..b58aa50243 100644 --- a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp +++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp @@ -48,6 +48,7 @@ const char* didl_header = "<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metad " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" " xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"" " xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"" + " xmlns:sec=\"http://www.sec.co.kr/\"" " xmlns:xbmc=\"urn:schemas-xbmc-org:metadata-1-0/\">"; const char* didl_footer = "</DIDL-Lite>"; const char* didl_namespace_dc = "http://purl.org/dc/elements/1.1/"; diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp index 4db2d45ebd..f937df27a9 100644 --- a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp +++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp @@ -528,12 +528,51 @@ PLT_MediaObject::ToDidl(NPT_UInt64 mask, NPT_String& didl) didl += " protocolInfo=\""; PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_ProtocolInfo.ToString()); - didl += "\">"; + didl += "\""; + /* adding custom data */ + NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_Resources[i].m_CustomData.GetEntries().GetFirstItem(); + while (entry) + { + didl += " "; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey()); + didl += "=\""; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue()); + didl += "\""; + + entry++; + } + + didl += ">"; PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Uri); didl += "</res>"; } } + //sec resources related + for (NPT_Cardinal i = 0; i < m_SecResources.GetItemCount(); i++) { + didl += "<sec:"; + PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name); + + NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_SecResources[i].attributes.GetEntries().GetFirstItem(); + while (entry) + { + didl += " sec:"; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey()); + didl += "=\""; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue()); + didl += "\""; + + entry++; + } + + didl += ">"; + PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].value); + + didl += "</sec:"; + PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name); + didl += ">"; + } + // xbmc dateadded if ((mask & PLT_FILTER_MASK_XBMC_DATEADDED) && !m_XbmcInfo.date_added.IsEmpty()) { didl += "<xbmc:dateadded>"; diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h index deb4961436..6461b81cf6 100644 --- a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h +++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h @@ -172,6 +172,12 @@ typedef struct { NPT_String unique_identifier; } PLT_XbmcInfo; +typedef struct { + NPT_String name; + NPT_Map<NPT_String, NPT_String> attributes; + NPT_String value; +} PLT_SecResource; + /*---------------------------------------------------------------------- | PLT_MediaItemResource +---------------------------------------------------------------------*/ @@ -192,6 +198,9 @@ public: NPT_UInt32 m_NbAudioChannels; NPT_String m_Resolution; NPT_UInt32 m_ColorDepth; + /* to add custom data to resource, that are not standard one, or are only + proper for some type of devices (UPnP)*/ + NPT_Map<NPT_String, NPT_String> m_CustomData; }; /*---------------------------------------------------------------------- @@ -250,6 +259,9 @@ public: /* resources related */ NPT_Array<PLT_MediaItemResource> m_Resources; + /* sec resources related */ + NPT_Array<PLT_SecResource> m_SecResources; + /* XBMC specific */ PLT_XbmcInfo m_XbmcInfo; diff --git a/lib/libUPnP/patches/0035-platinum-Changes-to-external-subtitles-over-UPnP-wor.patch b/lib/libUPnP/patches/0035-platinum-Changes-to-external-subtitles-over-UPnP-wor.patch new file mode 100644 index 0000000000..880b331e52 --- /dev/null +++ b/lib/libUPnP/patches/0035-platinum-Changes-to-external-subtitles-over-UPnP-wor.patch @@ -0,0 +1,122 @@ +From ec171eae6aad8bbf51fdf52c97446db6418c96a1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Chwia=C5=82a?= <rchwiala@gmail.com> +Date: Sat, 17 Jan 2015 15:55:52 +0100 +Subject: [PATCH] platinum Changes to external subtitles over UPnP works + +Added custom data to resources field - needed to add some attributes to res. +Added new struct PLK_SecResource - needed by Somasung devices. It's not specialized struct (just general one), because I can't find any "sec" specification. +--- + .../Source/Devices/MediaServer/PltDidl.cpp | 1 + + .../Source/Devices/MediaServer/PltMediaItem.cpp | 41 +++++++++++++++++++++- + .../Source/Devices/MediaServer/PltMediaItem.h | 12 +++++++ + 3 files changed, 53 insertions(+), 1 deletion(-) + +diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp +index 7b853c8..b58aa50 100644 +--- a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp ++++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp +@@ -48,6 +48,7 @@ const char* didl_header = "<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metad + " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" + " xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"" + " xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"" ++ " xmlns:sec=\"http://www.sec.co.kr/\"" + " xmlns:xbmc=\"urn:schemas-xbmc-org:metadata-1-0/\">"; + const char* didl_footer = "</DIDL-Lite>"; + const char* didl_namespace_dc = "http://purl.org/dc/elements/1.1/"; +diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp +index 4db2d45..f937df2 100644 +--- a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp ++++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp +@@ -528,12 +528,51 @@ PLT_MediaObject::ToDidl(NPT_UInt64 mask, NPT_String& didl) + + didl += " protocolInfo=\""; + PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_ProtocolInfo.ToString()); +- didl += "\">"; ++ didl += "\""; ++ /* adding custom data */ ++ NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_Resources[i].m_CustomData.GetEntries().GetFirstItem(); ++ while (entry) ++ { ++ didl += " "; ++ PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey()); ++ didl += "=\""; ++ PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue()); ++ didl += "\""; ++ ++ entry++; ++ } ++ ++ didl += ">"; + PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Uri); + didl += "</res>"; + } + } + ++ //sec resources related ++ for (NPT_Cardinal i = 0; i < m_SecResources.GetItemCount(); i++) { ++ didl += "<sec:"; ++ PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name); ++ ++ NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_SecResources[i].attributes.GetEntries().GetFirstItem(); ++ while (entry) ++ { ++ didl += " sec:"; ++ PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey()); ++ didl += "=\""; ++ PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue()); ++ didl += "\""; ++ ++ entry++; ++ } ++ ++ didl += ">"; ++ PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].value); ++ ++ didl += "</sec:"; ++ PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name); ++ didl += ">"; ++ } ++ + // xbmc dateadded + if ((mask & PLT_FILTER_MASK_XBMC_DATEADDED) && !m_XbmcInfo.date_added.IsEmpty()) { + didl += "<xbmc:dateadded>"; +diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h +index deb4961..6461b81 100644 +--- a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h ++++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h +@@ -172,6 +172,12 @@ typedef struct { + NPT_String unique_identifier; + } PLT_XbmcInfo; + ++typedef struct { ++ NPT_String name; ++ NPT_Map<NPT_String, NPT_String> attributes; ++ NPT_String value; ++} PLT_SecResource; ++ + /*---------------------------------------------------------------------- + | PLT_MediaItemResource + +---------------------------------------------------------------------*/ +@@ -192,6 +198,9 @@ public: + NPT_UInt32 m_NbAudioChannels; + NPT_String m_Resolution; + NPT_UInt32 m_ColorDepth; ++ /* to add custom data to resource, that are not standard one, or are only ++ proper for some type of devices (UPnP)*/ ++ NPT_Map<NPT_String, NPT_String> m_CustomData; + }; + + /*---------------------------------------------------------------------- +@@ -250,6 +259,9 @@ public: + /* resources related */ + NPT_Array<PLT_MediaItemResource> m_Resources; + ++ /* sec resources related */ ++ NPT_Array<PLT_SecResource> m_SecResources; ++ + /* XBMC specific */ + PLT_XbmcInfo m_XbmcInfo; + +-- +1.8.3.msysgit.0 + diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp index e9e94b34e8..f8ffd44984 100644 --- a/xbmc/FileItem.cpp +++ b/xbmc/FileItem.cpp @@ -816,6 +816,11 @@ bool CFileItem::IsLyrics() const return URIUtils::HasExtension(m_strPath, ".cdg|.lrc"); } +bool CFileItem::IsSubtitle() const +{ + return URIUtils::HasExtension(m_strPath, g_advancedSettings.m_subtitlesExtensions); +} + bool CFileItem::IsCUESheet() const { return URIUtils::HasExtension(m_strPath, ".cue"); diff --git a/xbmc/FileItem.h b/xbmc/FileItem.h index 45366aad1b..239a8895f3 100644 --- a/xbmc/FileItem.h +++ b/xbmc/FileItem.h @@ -154,6 +154,7 @@ public: */ bool IsPicture() const; bool IsLyrics() const; + bool IsSubtitle() const; /*! \brief Check whether an item is an audio item. Note that this returns true for diff --git a/xbmc/network/upnp/UPnPInternal.cpp b/xbmc/network/upnp/UPnPInternal.cpp index 2b1d6b16d6..95273c2e2d 100644 --- a/xbmc/network/upnp/UPnPInternal.cpp +++ b/xbmc/network/upnp/UPnPInternal.cpp @@ -40,6 +40,8 @@ #include "TextureDatabase.h" #include "ThumbLoader.h" #include "utils/URIUtils.h" +#include "settings/Settings.h" +#include "utils/LangCodeExpander.h" using namespace MUSIC_INFO; using namespace XFILE; @@ -151,6 +153,8 @@ GetMimeType(const CFileItem& item, mime = "audio/" + ext; else if (item.IsPicture() ) mime = "image/" + ext; + else if (item.IsSubtitle()) + mime = "text/" + ext; } /* nothing we can figure out */ @@ -606,6 +610,99 @@ BuildObject(CFileItem& item, } } + //Add external subtitle + if (upnp_server && item.IsVideo()) //only if video file + { + // find any available external subtitles + std::vector<std::string> filenames; + std::vector<std::string> subtitles; + CUtil::ScanForExternalSubtitles(file_path.GetChars(), filenames); + + std::string ext; + for (unsigned int i = 0; i < filenames.size(); i++) + { + ext = URIUtils::GetExtension(filenames[i]).c_str(); + ext = ext.substr(1); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + /* Hardcoded check for extension is not the best way, but it can't be allowed to pass all + subtitle extension (ex. rar or zip). There are the most popular extensions support by UPnP devices.*/ + if (ext == "txt" || ext == "srt" || ext == "ssa" || ext == "ass" || ext == "sub" || ext == "smi") + { + subtitles.push_back(filenames[i]); + } + } + + std::string subtitlePath; + + if (subtitles.size() == 1) + { + subtitlePath = subtitles[0]; + } + else if (!subtitles.empty()) + { + /* trying to find subtitle with prefered language settings */ + std::string preferredLanguage = (CSettings::Get().GetSetting("locale.subtitlelanguage"))->ToString(); + std::string preferredLanguageCode; + CLangCodeExpander::ConvertToThreeCharCode(preferredLanguageCode, preferredLanguage); + + for (unsigned int i = 0; i < subtitles.size(); i++) + { + ExternalStreamInfo info; + CUtil::GetExternalStreamDetailsFromFilename(file_path.GetChars(), subtitles[i], info); + + if (preferredLanguageCode == info.language) + { + subtitlePath = subtitles[i]; + break; + } + } + /* if not found subtitle with prefered language, get the first one */ + if (subtitlePath.empty()) + { + subtitlePath = subtitles[0]; + } + } + + if (!subtitlePath.empty()) + { + /* subtitles are added as 2 resources, 2 sec resources and 1 addon to video resource, to be compatible with + the most of the devices; all UPnP devices take the last one it could handle, + and skip ones it doesn't "understand" */ + // add subtitle resource with standard protocolInfo + NPT_String protocolInfo = GetProtocolInfo(CFileItem(subtitlePath, false), "http", context); + upnp_server->AddSafeResourceUri(object, rooturi, ips, NPT_String(subtitlePath.c_str()), protocolInfo); + // add subtitle resource with smi/caption protocol info (some devices) + PLT_ProtocolInfo protInfo = PLT_ProtocolInfo(protocolInfo); + protocolInfo = protInfo.GetProtocol() + ":" + protInfo.GetMask() + ":smi/caption:" + protInfo.GetExtra(); + upnp_server->AddSafeResourceUri(object, rooturi, ips, NPT_String(subtitlePath.c_str()), protocolInfo); + + ext = URIUtils::GetExtension(subtitlePath).c_str(); + ext = ext.substr(1); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + + NPT_String subtitle_uri = object->m_Resources[object->m_Resources.GetItemCount() - 1].m_Uri; + + // add subtitle to video resource (the first one) (for some devices) + object->m_Resources[0].m_CustomData["xmlns:pv"] = "http://www.pv.com/pvns/"; + object->m_Resources[0].m_CustomData["pv:subtitleFileUri"] = subtitle_uri; + object->m_Resources[0].m_CustomData["pv:subtitleFileType"] = ext.c_str(); + + // for samsung devices + PLT_SecResource sec_res; + sec_res.name = "CaptionInfoEx"; + sec_res.value = subtitle_uri; + sec_res.attributes["type"] = ext.c_str(); + object->m_SecResources.Add(sec_res); + sec_res.name = "CaptionInfo"; + object->m_SecResources.Add(sec_res); + + // adding subtitle uri for movie md5, for later use in http response + NPT_String movie_md5 = object->m_Resources[0].m_Uri; + movie_md5 = movie_md5.Right(movie_md5.GetLength() - movie_md5.Find("/%25/") - 5); + upnp_server->AddSubtitleUriForSecResponse(movie_md5, subtitle_uri); + } + } + return object; failure: diff --git a/xbmc/network/upnp/UPnPServer.cpp b/xbmc/network/upnp/UPnPServer.cpp index df79d3f2bd..e2fe2f9417 100644 --- a/xbmc/network/upnp/UPnPServer.cpp +++ b/xbmc/network/upnp/UPnPServer.cpp @@ -1192,6 +1192,20 @@ CUPnPServer::ServeFile(const NPT_HttpRequest& request, response.GetHeaders().SetHeader("Content-Disposition", disp.c_str()); } + // set getCaptionInfo.sec - sets subtitle uri for Samsung devices + const NPT_String* captionInfoHeader = request.GetHeaders().GetHeaderValue("getCaptionInfo.sec"); + if (captionInfoHeader) + { + NPT_String *sub_uri, movie; + movie = "subtitle://" + md5; + + NPT_AutoLock lock(m_FileMutex); + if (NPT_SUCCEEDED(m_FileMap.Get(movie, sub_uri))) + { + response.GetHeaders().SetHeader("CaptionInfo.sec", sub_uri->GetChars(), false); + } + } + return PLT_HttpServer::ServeFile(request, context, response, @@ -1289,5 +1303,18 @@ CUPnPServer::DefaultSortItems(CFileItemList& items) } } +NPT_Result +CUPnPServer::AddSubtitleUriForSecResponse(NPT_String movie_md5, NPT_String subtitle_uri) +{ + /* using existing m_FileMap to store subtitle uri for movie, + adding subtitle:// prefix, because there is already entry for movie md5 with movie path */ + NPT_String movie = "subtitle://" + movie_md5; + + NPT_AutoLock lock(m_FileMutex); + NPT_CHECK(m_FileMap.Put(movie, subtitle_uri)); + + return NPT_SUCCESS; +} + } /* namespace UPNP */ diff --git a/xbmc/network/upnp/UPnPServer.h b/xbmc/network/upnp/UPnPServer.h index 1518be967d..f8d6f11884 100644 --- a/xbmc/network/upnp/UPnPServer.h +++ b/xbmc/network/upnp/UPnPServer.h @@ -97,6 +97,10 @@ public: } } + /* Samsungs devices get subtitles from header in response (for movie file), not from didl. + It's a way to store subtitle uri generated when building didl, to use later in http response*/ + NPT_Result AddSubtitleUriForSecResponse(NPT_String movie_md5, NPT_String subtitle_uri); + private: void OnScanCompleted(int type); |