aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSascha Montellese <sascha.montellese@gmail.com>2015-01-31 09:31:57 +0100
committerSascha Montellese <sascha.montellese@gmail.com>2015-01-31 09:31:57 +0100
commit30350ffdb31e699b3ae01ca8bd9c57bc5fde0576 (patch)
tree94e4dd182249aaf1ea19433c5307c81e28fdc413
parentf624010c9b93ff6b8c71a09d94424c706f653bfd (diff)
parente46e41974cdf7e9b602bb3788454aff43bc59fc4 (diff)
Merge pull request #6184 from 7pepo7/master-subtitles
upnp: support for external subtitles
-rw-r--r--lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp1
-rw-r--r--lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp41
-rw-r--r--lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.h12
-rw-r--r--lib/libUPnP/patches/0035-platinum-Changes-to-external-subtitles-over-UPnP-wor.patch122
-rw-r--r--xbmc/FileItem.cpp5
-rw-r--r--xbmc/FileItem.h1
-rw-r--r--xbmc/network/upnp/UPnPInternal.cpp97
-rw-r--r--xbmc/network/upnp/UPnPServer.cpp27
-rw-r--r--xbmc/network/upnp/UPnPServer.h4
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);