From ec171eae6aad8bbf51fdf52c97446db6418c96a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chwia=C5=82a?= Date: Sat, 17 Jan 2015 15:55:52 +0100 Subject: 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 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 = ""; const char* didl_footer = ""; 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::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 += ""; } } + //sec resources related + for (NPT_Cardinal i = 0; i < m_SecResources.GetItemCount(); i++) { + didl += "::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 += " 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 m_CustomData; }; /*---------------------------------------------------------------------- @@ -250,6 +259,9 @@ public: /* resources related */ NPT_Array m_Resources; + /* sec resources related */ + NPT_Array m_SecResources; + /* XBMC specific */ PLT_XbmcInfo m_XbmcInfo; -- cgit v1.2.3 From e1dd7093eb385060a4b44b104083e190c115c97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chwia=C5=82a?= Date: Sat, 17 Jan 2015 16:01:15 +0100 Subject: PATCH for Platinum library needed to external subtitles over UPnP works. --- ...anges-to-external-subtitles-over-UPnP-wor.patch | 122 +++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 lib/libUPnP/patches/0035-platinum-Changes-to-external-subtitles-over-UPnP-wor.patch 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?= +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 = ""; + const char* didl_footer = ""; + 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::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 += ""; + } + } + ++ //sec resources related ++ for (NPT_Cardinal i = 0; i < m_SecResources.GetItemCount(); i++) { ++ didl += "::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 += " 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 m_CustomData; + }; + + /*---------------------------------------------------------------------- +@@ -250,6 +259,9 @@ public: + /* resources related */ + NPT_Array m_Resources; + ++ /* sec resources related */ ++ NPT_Array m_SecResources; ++ + /* XBMC specific */ + PLT_XbmcInfo m_XbmcInfo; + +-- +1.8.3.msysgit.0 + -- cgit v1.2.3 From e46e41974cdf7e9b602bb3788454aff43bc59fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chwia=C5=82a?= Date: Thu, 22 Jan 2015 20:28:46 +0100 Subject: It allows to play video with external subtitle over UPnP. Supported subtitle file extensions: txt, srt, ssa, ass, sub, smi. (most popular ones) Subtitle files are searched like in internal player (in video file folder and in folders specified in options). If more than one subtitle file are found (multiple language versions), Kodi takes the one with language choosed in options (prefered language for subtitles), otherwise first found. Subtitles are added as 2 resources, 2 sec resources and 3 attributes added to video file resource, for greater compatibility with more UPnP devices. Device take the last one it could handle, and skip ones it can't "understand": http://192.168.1.13:1722/video.mp4 http://192.168.1.13:1722/video.srt http://192.168.1.13:1722/video.srt http://192.168.1.13:1722/video.srt http://192.168.1.13:1722/video.srt Samsung devices get subtitles uri from "CaptionInfo.sec" field in http response header for movie file request (it contains "getCaptionInfo.sec" field). This all should be (and probably it would be) changed, when Profile Manager (for DLNA devices) will be available in Kodi. --- xbmc/FileItem.cpp | 5 ++ xbmc/FileItem.h | 1 + xbmc/network/upnp/UPnPInternal.cpp | 97 ++++++++++++++++++++++++++++++++++++++ xbmc/network/upnp/UPnPServer.cpp | 27 +++++++++++ xbmc/network/upnp/UPnPServer.h | 4 ++ 5 files changed, 134 insertions(+) diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp index 3c903a6a2c..77037df6bb 100644 --- a/xbmc/FileItem.cpp +++ b/xbmc/FileItem.cpp @@ -823,6 +823,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 27d8472434..73e69f52da 100644 --- a/xbmc/FileItem.h +++ b/xbmc/FileItem.h @@ -152,6 +152,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 a62d17527d..13e40772be 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 filenames; + std::vector 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 20e3bf5ab2..92d7697961 100644 --- a/xbmc/network/upnp/UPnPServer.cpp +++ b/xbmc/network/upnp/UPnPServer.cpp @@ -1194,6 +1194,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, @@ -1290,5 +1304,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); -- cgit v1.2.3