[UPnP] changed: make UPnP optional (enabled by default), fix some missing #ifdef...
authorAlasdair Campbell <alcoheca@gmail.com>
Fri, 3 Aug 2012 10:49:52 +0000 (11:49 +0100)
committerAlasdair Campbell <alcoheca@gmail.com>
Fri, 7 Sep 2012 18:20:21 +0000 (19:20 +0100)
15 files changed:
.gitignore
Makefile.in
configure.in
lib/libUPnP/Makefile.in
xbmc/Application.cpp
xbmc/filesystem/FileFactory.cpp
xbmc/filesystem/Makefile.in
xbmc/filesystem/UPnPDirectory.cpp
xbmc/network/Makefile.in
xbmc/network/UPnP.cpp [deleted file]
xbmc/network/UPnP.h [deleted file]
xbmc/network/upnp/Makefile.in [new file with mode: 0644]
xbmc/network/upnp/UPnP.cpp [new file with mode: 0644]
xbmc/network/upnp/UPnP.h [new file with mode: 0644]
xbmc/system.h

index 0e86dfe..e65c83c 100644 (file)
@@ -1156,6 +1156,7 @@ lib/cmyth/Makefile
 
 # /xbmc/network/
 /xbmc/network/Makefile
+/xbmc/network/upnp/Makefile
 
 /lib/python/Makefile
 /lib/python/Include/pyconfig.h
index 352266e..fb80df6 100644 (file)
@@ -22,7 +22,6 @@ DVDPLAYER_ARCHIVES=xbmc/cores/dvdplayer/DVDPlayer.a \
 DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \
                    lib/SlingboxLib/SlingboxLib.a \
                    lib/libRTV/librtv.a \
-                   lib/libUPnP/libupnp.a \
                    lib/libXDAAP/libxdaap.a \
                    lib/libhts/libhts.a \
                    lib/libsquish/libsquish.a \
@@ -108,6 +107,11 @@ DIRECTORY_ARCHIVES += xbmc/windowing/egl/windowing_egl.a
 DIRECTORY_ARCHIVES += xbmc/visualizations/EGLHelpers/eglhelpers.a
 endif
 
+ifeq (@USE_UPNP@,1)
+DIRECTORY_ARCHIVES += lib/libUPnP/libupnp.a \
+                      xbmc/network/upnp/upnp.a
+endif
+
 ifeq ($(findstring osx,@ARCH@),osx)
 DIRECTORY_ARCHIVES += xbmc/osx/osx.a
 DIRECTORY_ARCHIVES += xbmc/network/osx/network.a
index fdcef0e..e8cc72b 100644 (file)
@@ -367,6 +367,12 @@ AC_ARG_ENABLE([airtunes],
   [use_airtunes=$enableval],
   [use_airtunes=auto])
 
+AC_ARG_ENABLE([upnp],
+  [AS_HELP_STRING([--disable-upnp],
+  [disable UPnP support (default is enabled)])],
+  [use_upnp=$enableval],
+  [use_upnp=yes])
+
 AC_ARG_ENABLE([ffmpeg_libvorbis],
   [AS_HELP_STRING([--enable-ffmpeg-libvorbis],
   [enable FFmpeg vorbis encoding (default is no)])],
@@ -2134,6 +2140,15 @@ else
   final_message="$final_message\n  AirTunes support:\tNo"
 fi
 
+if test "x$use_upnp" != "xno"; then
+  final_message="$final_message\n  UPnP support:\t\tYes"
+  USE_UPNP=1
+  AC_DEFINE([USE_UPNP], [1], [Define to 1 to enable UPnP support.])
+else
+  USE_UPNP=0
+  final_message="$final_message\n  UPnP support:\t\tNo"
+fi
+
 if test "$use_optical_drive" = "yes"; then
   final_message="$final_message\n  Optical drive:\tYes"
 else
@@ -2244,6 +2259,7 @@ OUTPUT_FILES="Makefile \
     xbmc/guilib/Makefile \
     xbmc/interfaces/Makefile \
     xbmc/network/Makefile \
+    xbmc/network/upnp/Makefile \
     lib/libRTV/Makefile \
     lib/libexif/Makefile \
     lib/libXDAAP/Makefile \
@@ -2342,6 +2358,7 @@ AC_SUBST(USE_LIBCEC)
 AC_SUBST(USE_CEC_RPI_API)
 AC_SUBST(USE_MYSQL)
 AC_SUBST(USE_WEB_SERVER)
+AC_SUBST(USE_UPNP)
 AC_SUBST(USE_ANDROID)
 AC_SUBST(GTEST_CONFIGURED)
 
index 2fcd3d4..e1f9462 100644 (file)
@@ -1,5 +1,6 @@
 ARCH=@ARCH@
 
+ifeq (@USE_UPNP@, 1)
 SRCS= Platinum/Source/Core/PltAction.cpp \
       Platinum/Source/Core/PltArgument.cpp \
       Platinum/Source/Core/PltConstants.cpp \
@@ -93,3 +94,4 @@ LIB=libupnp.a
 include ../../Makefile.include
 -include $(filter %.P, $(OBJS:.o=.P))
 
+endif
index f0411b4..1359514 100644 (file)
 #include "filesystem/FileDAAP.h"
 #endif
 #ifdef HAS_UPNP
-#include "network/UPnP.h"
+#include "network/upnp/UPnP.h"
 #include "filesystem/UPnPDirectory.h"
 #endif
 #if defined(_LINUX) && defined(HAS_FILESYSTEM_SMB)
@@ -4032,6 +4032,7 @@ bool CApplication::PlayFile(const CFileItem& item, bool bRestart)
     return false;
   }
 
+#ifdef HAS_UPNP
   if (URIUtils::IsUPnP(item.GetPath()))
   {
     CFileItem item_new(item);
@@ -4039,6 +4040,7 @@ bool CApplication::PlayFile(const CFileItem& item, bool bRestart)
       return PlayFile(item_new, false);
     return false;
   }
+#endif
 
   // if we have a stacked set of files, we need to setup our stack routines for
   // "seamless" seeking and total time of the movie etc.
@@ -5352,8 +5354,10 @@ void CApplication::ProcessSlow()
 #endif
 
   // update upnp server/renderer states
+#ifdef HAS_UPNP
   if(UPNP::CUPnP::IsInstantiated())
     UPNP::CUPnP::GetInstance()->UpdateState();
+#endif
 
   //Check to see if current playing Title has changed and whether we should broadcast the fact
   CheckForTitleChange();
index 6ee61eb..c87deb5 100644 (file)
@@ -77,7 +77,9 @@
 #if defined(TARGET_ANDROID)
 #include "AndroidAppFile.h"
 #endif
+#ifdef HAS_UPNP
 #include "UPnPFile.h"
+#endif
 #include "PipesManager.h"
 #include "PipeFile.h"
 #include "MusicDatabaseFile.h"
@@ -191,7 +193,9 @@ IFile* CFileFactory::CreateLoader(const CURL& url)
     else if (strProtocol == "afp") return new CAFPFile();
 #endif
     else if (strProtocol == "pipe") return new CPipeFile();    
+#ifdef HAS_UPNP
     else if (strProtocol == "upnp") return new CUPnPFile();
+#endif
 #if defined(TARGET_ANDROID)
     else if (strProtocol == "androidapp") return new CFileAndroidApp();
 #endif
index 3d7c361..060f61f 100644 (file)
@@ -52,7 +52,6 @@ SRCS=AddonsDirectory.cpp \
      MythDirectory.cpp \
      MythFile.cpp \
      MythSession.cpp \
-     NptXbmcFile.cpp \
      NSFFileDirectory.cpp \
      OGGFileDirectory.cpp \
      PlaylistDirectory.cpp \
@@ -84,8 +83,6 @@ SRCS=AddonsDirectory.cpp \
      udf25.cpp \
      UDFDirectory.cpp \
      UDFFile.cpp \
-     UPnPDirectory.cpp \
-     UPnPFile.cpp \
      VideoDatabaseDirectory.cpp \
      VirtualDirectory.cpp \
      VTPDirectory.cpp \
@@ -126,6 +123,10 @@ ifeq (@HAVE_LIBBLURAY@,1)
 SRCS+=BlurayDirectory.cpp
 endif
 
+ifeq (@USE_UPNP@,1)
+SRCS+=NptXbmcFile.cpp \
+      UPnPDirectory.cpp \
+      UPnPFile.cpp
 INCLUDES+=-I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Core \
           -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Platinum \
           -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Devices/MediaConnect \
@@ -134,6 +135,7 @@ INCLUDES+=-I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Core \
           -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Extras \
           -I@abs_top_srcdir@/lib/libUPnP/Neptune/Source/System/Posix \
           -I@abs_top_srcdir@/lib/libUPnP/Neptune/Source/Core
+endif
 
 LIB=filesystem.a
 
index 293ac60..798504b 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "UPnPDirectory.h"
 #include "URL.h"
-#include "network/UPnP.h"
+#include "network/upnp/UPnP.h"
 #include "Platinum.h"
 #include "PltSyncMediaBrowser.h"
 #include "video/VideoInfoTag.h"
index faaa445..a045614 100644 (file)
@@ -1,12 +1,3 @@
-INCLUDES+=-I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Core \
-          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Platinum \
-          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Devices/MediaConnect \
-          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Devices/MediaRenderer \
-          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Devices/MediaServer \
-          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Extras \
-          -I@abs_top_srcdir@/lib/libUPnP/Neptune/Source/System/Posix \
-          -I@abs_top_srcdir@/lib/libUPnP/Neptune/Source/Core
-
 SRCS=cddb.cpp \
      DNSNameCache.cpp \
      EventClient.cpp \
@@ -18,7 +9,6 @@ SRCS=cddb.cpp \
      Socket.cpp \
      TCPServer.cpp \
      UdpClient.cpp \
-     UPnP.cpp \
      WebServer.cpp \
      ZeroconfBrowser.cpp \
      Zeroconf.cpp \
diff --git a/xbmc/network/UPnP.cpp b/xbmc/network/UPnP.cpp
deleted file mode 100644 (file)
index d5ee930..0000000
+++ /dev/null
@@ -1,2423 +0,0 @@
-/*
-* UPnP Support for XBMC
-* Copyright (c) 2006 c0diq (Sylvain Rebaud)
-* Portions Copyright (c) by the authors of libPlatinum
-*
-* http://www.plutinosoft.com/blog/category/platinum/
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "threads/SystemClock.h"
-#include "UPnP.h"
-#include "utils/URIUtils.h"
-#include "Application.h"
-#include "ApplicationMessenger.h"
-#include "Network.h"
-#include "utils/log.h"
-#include "filesystem/MusicDatabaseDirectory.h"
-#include "filesystem/VideoDatabaseDirectory.h"
-#include "music/MusicDatabase.h"
-#include "video/VideoDatabase.h"
-#include "filesystem/VideoDatabaseDirectory/DirectoryNode.h"
-#include "filesystem/VideoDatabaseDirectory/QueryParams.h"
-#include "filesystem/File.h"
-#include "NptStrings.h"
-#include "Platinum.h"
-#include "PltMediaConnect.h"
-#include "PltMediaRenderer.h"
-#include "PltSyncMediaBrowser.h"
-#include "PltDidl.h"
-#include "NptNetwork.h"
-#include "NptConsole.h"
-#include "music/tags/MusicInfoTag.h"
-#include "pictures/PictureInfoTag.h"
-#include "pictures/GUIWindowSlideShow.h"
-#include "filesystem/Directory.h"
-#include "URL.h"
-#include "settings/GUISettings.h"
-#include "GUIUserMessages.h"
-#include "settings/Settings.h"
-#include "settings/AdvancedSettings.h"
-#include "utils/StringUtils.h"
-#include "FileItem.h"
-#include "guilib/GUIWindowManager.h"
-#include "GUIInfoManager.h"
-#include "utils/TimeUtils.h"
-#include "utils/md5.h"
-#include "guilib/Key.h"
-#include "ThumbLoader.h"
-#include "Util.h"
-
-using namespace std;
-using namespace MUSIC_INFO;
-using namespace XFILE;
-
-NPT_SET_LOCAL_LOGGER("xbmc.upnp")
-
-#define UPNP_DEFAULT_MAX_RETURNED_ITEMS 200
-#define UPNP_DEFAULT_MIN_RETURNED_ITEMS 30
-
-/*
-# Play speed
-#    1 normal
-#    0 invalid
-DLNA_ORG_PS = 'DLNA.ORG_PS'
-DLNA_ORG_PS_VAL = '1'
-
-# Convertion Indicator
-#    1 transcoded
-#    0 not transcoded
-DLNA_ORG_CI = 'DLNA.ORG_CI'
-DLNA_ORG_CI_VAL = '0'
-
-# Operations
-#    00 not time seek range, not range
-#    01 range supported
-#    10 time seek range supported
-#    11 both supported
-DLNA_ORG_OP = 'DLNA.ORG_OP'
-DLNA_ORG_OP_VAL = '01'
-
-# Flags
-#    senderPaced                      80000000  31
-#    lsopTimeBasedSeekSupported       40000000  30
-#    lsopByteBasedSeekSupported       20000000  29
-#    playcontainerSupported           10000000  28
-#    s0IncreasingSupported            08000000  27
-#    sNIncreasingSupported            04000000  26
-#    rtspPauseSupported               02000000  25
-#    streamingTransferModeSupported   01000000  24
-#    interactiveTransferModeSupported 00800000  23
-#    backgroundTransferModeSupported  00400000  22
-#    connectionStallingSupported      00200000  21
-#    dlnaVersion15Supported           00100000  20
-DLNA_ORG_FLAGS = 'DLNA.ORG_FLAGS'
-DLNA_ORG_FLAGS_VAL = '01500000000000000000000000000000'
-*/
-
-/*----------------------------------------------------------------------
-|   NPT_Console::Output
-+---------------------------------------------------------------------*/
-void
-NPT_Console::Output(const char* message)
-{
-    CLog::Log(LOGDEBUG, "%s", message);
-}
-
-namespace UPNP
-{
-
-/*----------------------------------------------------------------------
-|   static
-+---------------------------------------------------------------------*/
-CUPnP* CUPnP::upnp = NULL;
-
-namespace
-{
-
-  enum EClientQuirks
-  {
-    ECLIENTQUIRKS_NONE = 0x0
-
-    /* Client requires folder's to be marked as storageFolers as verndor type (360)*/
-  , ECLIENTQUIRKS_ONLYSTORAGEFOLDER = 0x01
-
-    /* Client can't handle subtypes for videoItems (360) */
-  , ECLIENTQUIRKS_BASICVIDEOCLASS = 0x02
-
-    /* Client requires album to be set to [Unknown Series] to show title (WMP) */
-  , ECLIENTQUIRKS_UNKNOWNSERIES = 0x04
-  };
-
-  static EClientQuirks GetClientQuirks(const PLT_HttpRequestContext* context)
-  {
-    if(context == NULL)
-        return ECLIENTQUIRKS_NONE;
-
-    unsigned int quirks = 0;
-    const NPT_String* user_agent = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_USER_AGENT); 
-    const NPT_String* server     = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_SERVER);
-
-    if (user_agent) {
-        if (user_agent->Find("XBox", 0, true) >= 0 || 
-            user_agent->Find("Xenon", 0, true) >= 0)
-            quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
-
-        if (user_agent->Find("Windows-Media-Player", 0, true) >= 0)
-            quirks |= ECLIENTQUIRKS_UNKNOWNSERIES;
-
-    }
-    if (server) {
-        if (server->Find("Xbox", 0, true) >= 0)
-            quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
-    }
-
-    return (EClientQuirks)quirks;
-  }
-}
-
-
-/*----------------------------------------------------------------------
-|   CDeviceHostReferenceHolder class
-+---------------------------------------------------------------------*/
-class CDeviceHostReferenceHolder
-{
-public:
-    PLT_DeviceHostReference m_Device;
-};
-
-/*----------------------------------------------------------------------
-|   CCtrlPointReferenceHolder class
-+---------------------------------------------------------------------*/
-class CCtrlPointReferenceHolder
-{
-public:
-    PLT_CtrlPointReference m_CtrlPoint;
-};
-
-/*----------------------------------------------------------------------
-|   CUPnPCleaner class
-+---------------------------------------------------------------------*/
-class CUPnPCleaner : public NPT_Thread
-{
-public:
-    CUPnPCleaner(CUPnP* upnp) : NPT_Thread(true), m_UPnP(upnp) {}
-    void Run() {
-        delete m_UPnP;
-    }
-
-    CUPnP* m_UPnP;
-};
-
-/*----------------------------------------------------------------------
-|   CUPnP::CUPnP
-+---------------------------------------------------------------------*/
-class CUPnPServer : public PLT_MediaConnect,
-                    public PLT_FileMediaConnectDelegate
-{
-public:
-    CUPnPServer(const char* friendly_name, const char* uuid = NULL, int port = 0) :
-        PLT_MediaConnect(friendly_name, false, uuid, port),
-        PLT_FileMediaConnectDelegate("/", "/") {
-    }
-
-    // PLT_MediaServer methods
-    virtual NPT_Result OnBrowseMetadata(PLT_ActionReference&          action,
-                                        const char*                   object_id,
-                                        const char*                   filter,
-                                        NPT_UInt32                    starting_index,
-                                        NPT_UInt32                    requested_count,
-                                        const char*                   sort_criteria,
-                                        const PLT_HttpRequestContext& context);
-    virtual NPT_Result OnBrowseDirectChildren(PLT_ActionReference&          action,
-                                              const char*                   object_id,
-                                              const char*                   filter,
-                                              NPT_UInt32                    starting_index,
-                                              NPT_UInt32                    requested_count,
-                                              const char*                   sort_criteria,
-                                              const PLT_HttpRequestContext& context);
-    virtual NPT_Result OnSearchContainer(PLT_ActionReference&          action,
-                                         const char*                   container_id,
-                                         const char*                   search_criteria,
-                                         const char*                   filter,
-                                         NPT_UInt32                    starting_index,
-                                         NPT_UInt32                    requested_count,
-                                         const char*                   sort_criteria,
-                                         const PLT_HttpRequestContext& context);
-
-    // PLT_FileMediaServer methods
-    virtual NPT_Result ServeFile(const NPT_HttpRequest&              request,
-                                 const NPT_HttpRequestContext& context,
-                                 NPT_HttpResponse&             response,
-                                 const NPT_String&             file_path);
-
-    // class methods
-    static NPT_Result PopulateObjectFromTag(CMusicInfoTag&         tag,
-                                            PLT_MediaObject&       object,
-                                            NPT_String*            file_path,
-                                            PLT_MediaItemResource* resource,
-                                            EClientQuirks          quirks);
-    static NPT_Result PopulateObjectFromTag(CVideoInfoTag&         tag,
-                                            PLT_MediaObject&       object,
-                                            NPT_String*            file_path,
-                                            PLT_MediaItemResource* resource,
-                                            EClientQuirks          quirks);
-    static PLT_MediaObject* BuildObject(const CFileItem&              item,
-                                        NPT_String&                   file_path,
-                                        bool                          with_count,
-                                        const PLT_HttpRequestContext* context = NULL,
-                                        CUPnPServer*                  upnp_server = NULL);
-    NPT_String BuildSafeResourceUri(const NPT_HttpUrl &rooturi,
-                                    const char*        host,
-                                    const char*        file_path);
-
-    void AddSafeResourceUri(PLT_MediaObject* object, const NPT_HttpUrl& rooturi, NPT_List<NPT_IpAddress> ips, const char* file_path, const NPT_String& info)
-    {
-        PLT_MediaItemResource res;
-        for(NPT_List<NPT_IpAddress>::Iterator ip = ips.GetFirstItem(); ip; ++ip) {
-            res.m_ProtocolInfo = PLT_ProtocolInfo(info);
-            res.m_Uri          = BuildSafeResourceUri(rooturi, (*ip).ToString(), file_path);
-            object->m_Resources.Add(res);
-        }
-    }
-
-
-    static const char* GetMimeTypeFromExtension(const char* extension, const PLT_HttpRequestContext* context = NULL);
-    static NPT_String  GetMimeType(const CFileItem& item, const PLT_HttpRequestContext* context = NULL);
-    static NPT_String  GetMimeType(const char* filename, const PLT_HttpRequestContext* context = NULL);
-    static const CStdString& CorrectAllItemsSortHack(const CStdString &item);
-
-private:
-    PLT_MediaObject* Build(CFileItemPtr                  item,
-                           bool                          with_count,
-                           const PLT_HttpRequestContext& context,
-                           const char*                   parent_id = NULL);
-    NPT_Result       BuildResponse(PLT_ActionReference&          action,
-                                   CFileItemList&                items,
-                                   const char*                   filter,
-                                   NPT_UInt32                    starting_index,
-                                   NPT_UInt32                    requested_count,
-                                   const char*                   sort_criteria,
-                                   const PLT_HttpRequestContext& context,
-                                   const char*                   parent_id /* = NULL */);
-
-    // class methods
-    static NPT_String GetParentFolder(NPT_String file_path) {
-        int index = file_path.ReverseFind("\\");
-        if (index == -1) return "";
-
-        return file_path.Left(index);
-    }
-    static const NPT_String GetProtocolInfo(const CFileItem& item,
-                                            const char* protocol,
-                                            const PLT_HttpRequestContext* context = NULL);
-
-    NPT_Mutex                       m_FileMutex;
-    NPT_Map<NPT_String, NPT_String> m_FileMap;
-
-public:
-    // class members
-    static NPT_UInt32 m_MaxReturnedItems;
-};
-
-NPT_UInt32 CUPnPServer::m_MaxReturnedItems = 0;
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::BuildSafeResourceUri
-+---------------------------------------------------------------------*/
-NPT_String CUPnPServer::BuildSafeResourceUri(const NPT_HttpUrl &rooturi,
-                                             const char* host,
-                                             const char* file_path)
-{
-    CStdString md5;
-    XBMC::XBMC_MD5 md5state;
-    md5state.append(file_path);
-    md5state.getDigest(md5);
-    md5 += "/" + URIUtils::GetFileName(file_path);
-    { NPT_AutoLock lock(m_FileMutex);
-      NPT_CHECK(m_FileMap.Put(md5.c_str(), file_path));
-    }
-    return PLT_FileMediaServer::BuildSafeResourceUri(rooturi, host, md5.c_str());
-}
-
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::GetMimeType
-+---------------------------------------------------------------------*/
-NPT_String
-CUPnPServer::GetMimeType(const char* filename,
-                            const PLT_HttpRequestContext* context /* = NULL */)
-{
-    NPT_String ext = URIUtils::GetExtension(filename).c_str();
-    ext.TrimLeft('.');
-    ext = ext.ToLowercase();
-
-    return PLT_MimeType::GetMimeTypeFromExtension(ext, context);
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::GetMimeType
-+---------------------------------------------------------------------*/
-NPT_String
-CUPnPServer::GetMimeType(const CFileItem& item,
-                            const PLT_HttpRequestContext* context /* = NULL */)
-{
-    CStdString path = item.GetPath();
-    if (item.HasVideoInfoTag() && !item.GetVideoInfoTag()->GetPath().IsEmpty()) {
-        path = item.GetVideoInfoTag()->GetPath();
-    } else if (item.HasMusicInfoTag() && !item.GetMusicInfoTag()->GetURL().IsEmpty()) {
-        path = item.GetMusicInfoTag()->GetURL();
-    }
-
-    if(path.Left(8).Equals("stack://"))
-      return "audio/x-mpegurl";
-
-    NPT_String ext = URIUtils::GetExtension(path).c_str();
-    ext.TrimLeft('.');
-    ext = ext.ToLowercase();
-
-    NPT_String mime;
-
-    /* We always use Platinum mime type first
-       as it is defined to map extension to DLNA compliant mime type
-       or custom according to context (who asked for it) */
-    if (!ext.IsEmpty()) {
-        mime = PLT_MimeType::GetMimeTypeFromExtension(ext, context);
-        if (mime == "application/octet-stream") mime = "";
-    }
-
-    /* if Platinum couldn't map it, default to XBMC mapping */
-    if (mime.IsEmpty()) {
-        NPT_String mime = item.GetMimeType().c_str();
-        if (mime == "application/octet-stream") mime = "";
-    }
-
-    /* fallback to generic mime type if not found */
-    if (mime.IsEmpty()) {
-        if (item.IsVideo() || item.IsVideoDb() )
-            mime = "video/" + ext;
-        else if (item.IsAudio() || item.IsMusicDb() )
-            mime = "audio/" + ext;
-        else if (item.IsPicture() )
-            mime = "image/" + ext;
-    }
-
-    /* nothing we can figure out */
-    if (mime.IsEmpty()) {
-        mime = "application/octet-stream";
-    }
-
-    return mime;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::GetProtocolInfo
-+---------------------------------------------------------------------*/
-const NPT_String
-CUPnPServer::GetProtocolInfo(const CFileItem&              item,
-                             const char*                   protocol,
-                             const PLT_HttpRequestContext* context /* = NULL */)
-{
-    NPT_String proto = protocol;
-
-    /* fixup the protocol just in case nothing was passed */
-    if (proto.IsEmpty()) {
-        proto = item.GetAsUrl().GetProtocol();
-    }
-
-    /*
-       map protocol to right prefix and use xbmc-get for
-       unsupported UPnP protocols for other xbmc clients
-       TODO: add rtsp ?
-    */
-    if (proto == "http") {
-        proto = "http-get";
-    } else {
-        proto = "xbmc-get";
-    }
-
-    /* we need a valid extension to retrieve the mimetype for the protocol info */
-    NPT_String mime = GetMimeType(item, context);
-    proto += ":*:" + mime + ":" + PLT_ProtocolInfo::GetDlnaExtension(mime, context);
-    return proto;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::PopulateObjectFromTag
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::PopulateObjectFromTag(CMusicInfoTag&         tag,
-                                   PLT_MediaObject&       object,
-                                   NPT_String*            file_path, /* = NULL */
-                                   PLT_MediaItemResource* resource,  /* = NULL */
-                                   EClientQuirks          quirks)
-{
-    if (!tag.GetURL().IsEmpty() && file_path)
-      *file_path = tag.GetURL();
-
-    std::vector<std::string> genres = tag.GetGenre();
-    for (unsigned int index = 0; index < genres.size(); index++)
-      object.m_Affiliation.genres.Add(genres.at(index).c_str());
-    object.m_Title = tag.GetTitle();
-    object.m_Affiliation.album = tag.GetAlbum();
-    for (unsigned int index = 0; index < tag.GetArtist().size(); index++)
-    {
-      object.m_People.artists.Add(tag.GetArtist().at(index).c_str());
-      object.m_People.artists.Add(tag.GetArtist().at(index).c_str(), "Performer");
-    }
-    object.m_People.artists.Add(StringUtils::Join(!tag.GetAlbumArtist().empty() ? tag.GetAlbumArtist() : tag.GetArtist(), g_advancedSettings.m_musicItemSeparator).c_str(), "AlbumArtist");
-    if(tag.GetAlbumArtist().empty())
-        object.m_Creator = StringUtils::Join(tag.GetArtist(), g_advancedSettings.m_musicItemSeparator);
-    else
-        object.m_Creator = StringUtils::Join(tag.GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator);
-    object.m_MiscInfo.original_track_number = tag.GetTrackNumber();
-    if(tag.GetDatabaseId() >= 0) {
-      object.m_ReferenceID = NPT_String::Format("musicdb://4/%i%s", tag.GetDatabaseId(), URIUtils::GetExtension(tag.GetURL()).c_str());
-    }
-    if (object.m_ReferenceID == object.m_ObjectID)
-        object.m_ReferenceID = "";
-
-    if (resource) resource->m_Duration = tag.GetDuration();
-
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::PopulateObjectFromTag
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::PopulateObjectFromTag(CVideoInfoTag&         tag,
-                                   PLT_MediaObject&       object,
-                                   NPT_String*            file_path, /* = NULL */
-                                   PLT_MediaItemResource* resource,  /* = NULL */
-                                   EClientQuirks          quirks)
-{
-    // some usefull buffers
-    CStdStringArray strings;
-
-    if (!tag.m_strFileNameAndPath.IsEmpty() && file_path)
-      *file_path = tag.m_strFileNameAndPath;
-
-    if (tag.m_iDbId != -1 ) {
-        if (!tag.m_artist.empty()) {
-          object.m_ObjectClass.type = "object.item.videoItem.musicVideoClip";
-          object.m_Creator = StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator);
-          object.m_Title = tag.m_strTitle;
-          object.m_ReferenceID = NPT_String::Format("videodb://3/2/%i", tag.m_iDbId);
-        } else if (!tag.m_strShowTitle.IsEmpty()) {
-          object.m_ObjectClass.type = "object.item.videoItem.videoBroadcast";
-          object.m_Recorded.program_title  = "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
-          object.m_Recorded.program_title += "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
-          object.m_Recorded.program_title += " : " + tag.m_strTitle;
-          object.m_Recorded.series_title = tag.m_strShowTitle;
-          object.m_Recorded.episode_number = tag.m_iSeason * 100 + tag.m_iEpisode;
-          object.m_Title = object.m_Recorded.series_title + " - " + object.m_Recorded.program_title;
-          object.m_Date = tag.m_firstAired.GetAsLocalizedDate();
-          if(tag.m_iSeason != -1)
-              object.m_ReferenceID = NPT_String::Format("videodb://2/0/%i", tag.m_iDbId);
-        } else {
-          object.m_ObjectClass.type = "object.item.videoItem.movie";
-          object.m_Title = tag.m_strTitle;
-          object.m_Date = NPT_String::FromInteger(tag.m_iYear) + "-01-01";
-          object.m_ReferenceID = NPT_String::Format("videodb://1/2/%i", tag.m_iDbId);
-        }
-    }
-
-    if(quirks & ECLIENTQUIRKS_BASICVIDEOCLASS)
-        object.m_ObjectClass.type = "object.item.videoItem";
-
-    if(object.m_ReferenceID == object.m_ObjectID)
-        object.m_ReferenceID = "";
-
-    for (unsigned int index = 0; index < tag.m_genre.size(); index++)
-      object.m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
-
-    for(CVideoInfoTag::iCast it = tag.m_cast.begin();it != tag.m_cast.end();it++) {
-        object.m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
-    }
-
-    object.m_People.director = StringUtils::Join(tag.m_director, g_advancedSettings.m_videoItemSeparator);
-    for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
-      object.m_People.authors.Add(tag.m_writingCredits[index].c_str());
-
-    object.m_Description.description = tag.m_strTagLine;
-    object.m_Description.long_description = tag.m_strPlot;
-    if (resource) resource->m_Duration = tag.m_streamDetails.GetVideoDuration();
-    if (resource) resource->m_Resolution = NPT_String::FromInteger(tag.m_streamDetails.GetVideoWidth()) + "x" + NPT_String::FromInteger(tag.m_streamDetails.GetVideoHeight());
-
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::CorrectAllItemsSortHack
-+---------------------------------------------------------------------*/
-const CStdString&
-CUPnPServer::CorrectAllItemsSortHack(const CStdString &item)
-{
-    // This is required as in order for the "* All Albums" etc. items to sort
-    // correctly, they must have fake artist/album etc. information generated.
-    // This looks nasty if we attempt to render it to the GUI, thus this (further)
-    // workaround
-    if ((item.size() == 1 && item[0] == 0x01) || (item.size() > 1 && ((unsigned char) item[1]) == 0xff))
-        return StringUtils::EmptyString;
-
-    return item;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::BuildObject
-+---------------------------------------------------------------------*/
-PLT_MediaObject*
-CUPnPServer::BuildObject(const CFileItem&              item,
-                         NPT_String&                   file_path,
-                         bool                          with_count,
-                         const PLT_HttpRequestContext* context /* = NULL */,
-                         CUPnPServer*                  upnp_server /* = NULL */)
-{
-    PLT_MediaItemResource resource;
-    PLT_MediaObject*      object = NULL;
-
-    CLog::Log(LOGDEBUG, "Building didl for object '%s'", (const char*)item.GetPath());
-
-    EClientQuirks quirks = GetClientQuirks(context);
-
-    // get list of ip addresses
-    NPT_List<NPT_IpAddress> ips;
-    NPT_HttpUrl rooturi;
-    NPT_CHECK_LABEL(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure);
-
-    // if we're passed an interface where we received the request from
-    // move the ip to the top
-    if (context && context->GetLocalAddress().GetIpAddress().ToString() != "0.0.0.0") {
-        rooturi = NPT_HttpUrl(context->GetLocalAddress().GetIpAddress().ToString(), context->GetLocalAddress().GetPort(), "/");
-        ips.Remove(context->GetLocalAddress().GetIpAddress());
-        ips.Insert(ips.GetFirstItem(), context->GetLocalAddress().GetIpAddress());
-    }
-
-    if (!item.m_bIsFolder) {
-        object = new PLT_MediaItem();
-        object->m_ObjectID = item.GetPath();
-
-        /* Setup object type */
-        if (item.IsMusicDb() || item.IsAudio()) {
-            object->m_ObjectClass.type = "object.item.audioItem.musicTrack";
-
-            if (item.HasMusicInfoTag()) {
-                CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
-                PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks);
-            }
-        } else if (item.IsVideoDb() || item.IsVideo()) {
-            object->m_ObjectClass.type = "object.item.videoItem";
-
-            if(quirks & ECLIENTQUIRKS_UNKNOWNSERIES)
-                object->m_Affiliation.album = "[Unknown Series]";
-
-            if (item.HasVideoInfoTag()) {
-                CVideoInfoTag *tag = (CVideoInfoTag*)item.GetVideoInfoTag();
-                PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks);
-            }
-        } else if (item.IsPicture()) {
-            object->m_ObjectClass.type = "object.item.imageItem.photo";
-        } else {
-            object->m_ObjectClass.type = "object.item";
-        }
-
-        // duration of zero is invalid
-        if (resource.m_Duration == 0) resource.m_Duration = -1;
-
-        // Set the resource file size
-        resource.m_Size = item.m_dwSize;
-        if (resource.m_Size == 0) {
-            struct __stat64 info;
-            if(CFile::Stat((const char*)file_path, &info) == 0 && info.st_size >= 0)
-              resource.m_Size = info.st_size;
-        }
-        if(resource.m_Size == 0)
-          resource.m_Size = (NPT_LargeSize)-1;
-
-        // set date
-        if (object->m_Date.IsEmpty() && item.m_dateTime.IsValid()) {
-            object->m_Date = item.m_dateTime.GetAsLocalizedDate();
-        }
-
-        if (upnp_server) {
-            upnp_server->AddSafeResourceUri(object, rooturi, ips, file_path, GetProtocolInfo(item, "http", context));
-        }
-
-        // if the item is remote, add a direct link to the item
-        if (URIUtils::IsRemote((const char*)file_path)) {
-            resource.m_ProtocolInfo = PLT_ProtocolInfo(CUPnPServer::GetProtocolInfo(item, item.GetAsUrl().GetProtocol(), context));
-            resource.m_Uri = file_path;
-
-            // if the direct link can be served directly using http, then push it in front
-            // otherwise keep the xbmc-get resource last and let a compatible client look for it
-            if (resource.m_ProtocolInfo.ToString().StartsWith("xbmc", true)) {
-                object->m_Resources.Add(resource);
-            } else {
-                object->m_Resources.Insert(object->m_Resources.GetFirstItem(), resource);
-            }
-        }
-
-        // Some upnp clients expect all audio items to have parent root id 4
-#ifdef WMP_ID_MAPPING
-        object->m_ParentID = "4";
-#endif
-    } else {
-        PLT_MediaContainer* container = new PLT_MediaContainer;
-        object = container;
-
-        /* Assign a title and id for this container */
-        container->m_ObjectID = item.GetPath();
-        container->m_ObjectClass.type = "object.container";
-        container->m_ChildrenCount = -1;
-
-        CStdStringArray strings;
-
-        /* this might be overkill, but hey */
-        if (item.IsMusicDb()) {
-            MUSICDATABASEDIRECTORY::NODE_TYPE node = CMusicDatabaseDirectory::GetDirectoryType(item.GetPath());
-            switch(node) {
-                case MUSICDATABASEDIRECTORY::NODE_TYPE_ARTIST: {
-                      container->m_ObjectClass.type += ".person.musicArtist";
-                      CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
-                      if (tag) {
-                          container->m_People.artists.Add(
-                              CorrectAllItemsSortHack(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "Performer");
-                          container->m_People.artists.Add(
-                              CorrectAllItemsSortHack(StringUtils::Join(!tag->GetAlbumArtist().empty() ? tag->GetAlbumArtist() : tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "AlbumArtist");
-                      }
-#ifdef WMP_ID_MAPPING
-                      // Some upnp clients expect all artists to have parent root id 107
-                      container->m_ParentID = "107";
-#endif
-                  }
-                  break;
-                case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM:
-                case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_COMPILATIONS:
-                case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_RECENTLY_ADDED:
-                case MUSICDATABASEDIRECTORY::NODE_TYPE_YEAR_ALBUM: {
-                      container->m_ObjectClass.type += ".album.musicAlbum";
-                      // for Sonos to be happy
-                      CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
-                      if (tag) {
-                          container->m_People.artists.Add(
-                              CorrectAllItemsSortHack(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "Performer");
-                          container->m_People.artists.Add(
-                              CorrectAllItemsSortHack(StringUtils::Join(!tag->GetAlbumArtist().empty() ? tag->GetAlbumArtist() : tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "AlbumArtist");
-                          container->m_Affiliation.album = CorrectAllItemsSortHack(tag->GetAlbum()).c_str();
-                      }
-#ifdef WMP_ID_MAPPING
-                      // Some upnp clients expect all albums to have parent root id 7
-                      container->m_ParentID = "7";
-#endif
-                  }
-                  break;
-                case MUSICDATABASEDIRECTORY::NODE_TYPE_GENRE:
-                  container->m_ObjectClass.type += ".genre.musicGenre";
-                  break;
-                default:
-                  break;
-            }
-        } else if (item.IsVideoDb()) {
-            VIDEODATABASEDIRECTORY::NODE_TYPE node = CVideoDatabaseDirectory::GetDirectoryType(item.GetPath());
-            CVideoInfoTag &tag = *(CVideoInfoTag*)item.GetVideoInfoTag();
-            switch(node) {
-                case VIDEODATABASEDIRECTORY::NODE_TYPE_GENRE:
-                  container->m_ObjectClass.type += ".genre.movieGenre";
-                  break;
-                case VIDEODATABASEDIRECTORY::NODE_TYPE_ACTOR:
-                  container->m_ObjectClass.type += ".person.videoArtist";
-                  container->m_Creator = StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator);
-                  container->m_Title   = tag.m_strTitle;
-                  break;
-                case VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_TVSHOWS:
-                  container->m_ObjectClass.type += ".album.videoAlbum";
-                  container->m_Recorded.program_title  = "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
-                  container->m_Recorded.program_title += "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
-                  container->m_Recorded.program_title += " : " + tag.m_strTitle;
-                  container->m_Recorded.series_title = tag.m_strShowTitle;
-                  container->m_Recorded.episode_number = tag.m_iSeason * 100 + tag.m_iEpisode;
-                  container->m_Title = container->m_Recorded.series_title + " - " + container->m_Recorded.program_title;
-                  container->m_Title = tag.m_strTitle;
-                  if(!tag.m_firstAired.IsValid() && tag.m_iYear)
-                    container->m_Date = NPT_String::FromInteger(tag.m_iYear) + "-01-01";
-                  else
-                    container->m_Date = tag.m_firstAired.GetAsLocalizedDate();
-
-                  for (unsigned int index = 0; index < tag.m_genre.size(); index++)
-                    container->m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
-
-                  for(CVideoInfoTag::iCast it = tag.m_cast.begin();it != tag.m_cast.end();it++) {
-                      container->m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
-                  }
-
-                  container->m_People.director = StringUtils::Join(tag.m_director, g_advancedSettings.m_videoItemSeparator);;
-                  for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
-                    container->m_People.authors.Add(tag.m_writingCredits[index].c_str());
-
-                  container->m_Description.description = tag.m_strTagLine;
-                  container->m_Description.long_description = tag.m_strPlot;
-
-                  break;
-                default:
-                  container->m_ObjectClass.type += ".storageFolder";
-                  break;
-            }
-        } else if (item.IsPlayList()) {
-            container->m_ObjectClass.type += ".playlistContainer";
-        }
-
-        if(quirks & ECLIENTQUIRKS_ONLYSTORAGEFOLDER) {
-          container->m_ObjectClass.type = "object.container.storageFolder";
-        }
-
-        /* Get the number of children for this container */
-        if (with_count && upnp_server) {
-            if (object->m_ObjectID.StartsWith("virtualpath://")) {
-                NPT_LargeSize count = 0;
-                NPT_CHECK_LABEL(NPT_File::GetSize(file_path, count), failure);
-                container->m_ChildrenCount = count;
-            } else {
-                /* this should be a standard path */
-                // TODO - get file count of this directory
-            }
-        }
-    }
-
-    // set a title for the object
-    if (object->m_Title.IsEmpty()) {
-        if (!item.GetLabel().IsEmpty()) {
-            CStdString title = item.GetLabel();
-            if (item.IsPlayList() || !item.m_bIsFolder) URIUtils::RemoveExtension(title);
-            object->m_Title = title;
-        } else {
-            CStdString title, volumeNumber;
-            CUtil::GetVolumeFromFileName(item.GetPath(), title, volumeNumber);
-            if (!item.m_bIsFolder) URIUtils::RemoveExtension(title);
-            object->m_Title = title;
-        }
-    }
-    // set a thumbnail if we have one
-    if (item.HasThumbnail() && upnp_server) {
-        PLT_AlbumArtInfo art;
-        art.uri = upnp_server->BuildSafeResourceUri(
-            rooturi,
-            (*ips.GetFirstItem()).ToString(),
-            item.GetThumbnailImage());
-        // Set DLNA profileID by extension, defaulting to JPEG.
-        NPT_String ext = URIUtils::GetExtension(item.GetThumbnailImage()).c_str();
-        if (strcmp(ext, ".png") == 0) {
-            art.dlna_profile = "PNG_TN";
-        } else {
-            art.dlna_profile = "JPEG_TN";
-        }
-        object->m_ExtraInfo.album_arts.Add(art);
-    }
-
-    if (upnp_server) {
-        if (item.HasProperty("fanart_image")) {
-            upnp_server->AddSafeResourceUri(object, rooturi, ips, item.GetProperty("fanart_image").asString().c_str(), "xbmc.org:*:fanart:*" );
-        }
-    }
-
-    return object;
-
-failure:
-    delete object;
-    return NULL;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::Build
-+---------------------------------------------------------------------*/
-PLT_MediaObject*
-CUPnPServer::Build(CFileItemPtr                  item,
-                   bool                          with_count,
-                   const PLT_HttpRequestContext& context,
-                   const char*                   parent_id /* = NULL */)
-{
-    PLT_MediaObject* object = NULL;
-    NPT_String       path = item->GetPath().c_str();
-
-    //HACK: temporary disabling count as it thrashes HDD
-    with_count = false;
-
-    CLog::Log(LOGDEBUG, "Preparing upnp object for item '%s'", (const char*)path);
-
-    if (path == "virtualpath://upnproot") {
-        path.TrimRight("/");
-        if (path.StartsWith("virtualpath://")) {
-            object = new PLT_MediaContainer;
-            object->m_Title = item->GetLabel();
-            object->m_ObjectClass.type = "object.container";
-            object->m_ObjectID = path;
-
-            // root
-            object->m_ObjectID = "0";
-            object->m_ParentID = "-1";
-            // root has 5 children
-            if (with_count) {
-                ((PLT_MediaContainer*)object)->m_ChildrenCount = 5;
-            }
-        } else {
-            goto failure;
-        }
-
-    } else {
-        // db path handling
-        NPT_String file_path, share_name;
-        file_path = item->GetPath();
-        share_name = "";
-
-        if (path.StartsWith("musicdb://")) {
-            if (path == "musicdb://" ) {
-                item->SetLabel("Music Library");
-                item->SetLabelPreformated(true);
-            } else {
-                if (!item->HasMusicInfoTag() || !item->GetMusicInfoTag()->Loaded() )
-                    item->LoadMusicTag();
-
-                if (!item->HasThumbnail() )
-                    item->SetThumbnailImage(CThumbLoader::GetCachedImage(*item, "thumb"));
-
-                if (item->GetLabel().IsEmpty()) {
-                    /* if no label try to grab it from node type */
-                    CStdString label;
-                    if (CMusicDatabaseDirectory::GetLabel((const char*)path, label)) {
-                        item->SetLabel(label);
-                        item->SetLabelPreformated(true);
-                    }
-                }
-            }
-        } else if (file_path.StartsWith("videodb://")) {
-            if (path == "videodb://" ) {
-                item->SetLabel("Video Library");
-                item->SetLabelPreformated(true);
-            } else {
-                if (!item->HasVideoInfoTag()) {
-                    XFILE::VIDEODATABASEDIRECTORY::CQueryParams params;
-                    XFILE::VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo((const char*)path, params);
-
-                    CVideoDatabase db;
-                    if (!db.Open() ) return NULL;
-
-                    if (params.GetMovieId() >= 0 )
-                        db.GetMovieInfo((const char*)path, *item->GetVideoInfoTag(), params.GetMovieId());
-                    else if (params.GetEpisodeId() >= 0 )
-                        db.GetEpisodeInfo((const char*)path, *item->GetVideoInfoTag(), params.GetEpisodeId());
-                    else if (params.GetTvShowId() >= 0 )
-                        db.GetTvShowInfo((const char*)path, *item->GetVideoInfoTag(), params.GetTvShowId());
-                }
-
-                // try to grab title from tag
-                if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strTitle.IsEmpty()) {
-                    item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
-                    item->SetLabelPreformated(true);
-                }
-
-                // try to grab it from the folder
-                if (item->GetLabel().IsEmpty()) {
-                    CStdString label;
-                    if (CVideoDatabaseDirectory::GetLabel((const char*)path, label)) {
-                        item->SetLabel(label);
-                        item->SetLabelPreformated(true);
-                    }
-                }
-
-                if (!item->HasThumbnail() )
-                    item->SetThumbnailImage(CThumbLoader::GetCachedImage(*item, "thumb"));
-            }
-        }
-
-        // not a virtual path directory, new system
-        object = BuildObject(*item.get(), file_path, with_count, &context, this);
-
-        // set parent id if passed, otherwise it should have been determined
-        if (object && parent_id) {
-            object->m_ParentID = parent_id;
-        }
-    }
-
-    if (object) {
-        // remap Root virtualpath://upnproot/ to id "0"
-        if (object->m_ObjectID == "virtualpath://upnproot/")
-            object->m_ObjectID = "0";
-
-        // remap Parent Root virtualpath://upnproot/ to id "0"
-        if (object->m_ParentID == "virtualpath://upnproot/")
-            object->m_ParentID = "0";
-    }
-    return object;
-
-failure:
-    delete object;
-    return NULL;
-}
-
-/*----------------------------------------------------------------------
-|   TranslateWMPObjectId
-+---------------------------------------------------------------------*/
-static NPT_String TranslateWMPObjectId(NPT_String id)
-{
-    if (id == "0") {
-        id = "virtualpath://upnproot/";
-    } else if (id == "15") {
-        // Xbox 360 asking for videos
-        id = "videodb://";
-    } else if (id == "16") {
-        // Xbox 360 asking for photos
-    } else if (id == "107") {
-        // Sonos uses 107 for artists root container id
-        id = "musicdb://2/";
-    } else if (id == "7") {
-        // Sonos uses 7 for albums root container id
-        id = "musicdb://3/";
-    } else if (id == "4") {
-        // Sonos uses 4 for tracks root container id
-        id = "musicdb://4/";
-    }
-
-    CLog::Log(LOGDEBUG, "UPnP Translated id to '%s'", (const char*)id);
-    return id;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::OnBrowseMetadata
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::OnBrowseMetadata(PLT_ActionReference&          action,
-                              const char*                   object_id,
-                              const char*                   filter,
-                              NPT_UInt32                    starting_index,
-                              NPT_UInt32                    requested_count,
-                              const char*                   sort_criteria,
-                              const PLT_HttpRequestContext& context)
-{
-    NPT_COMPILER_UNUSED(sort_criteria);
-    NPT_COMPILER_UNUSED(requested_count);
-    NPT_COMPILER_UNUSED(starting_index);
-
-    NPT_String                     didl;
-    NPT_Reference<PLT_MediaObject> object;
-    NPT_String                     id = TranslateWMPObjectId(object_id);
-    vector<CStdString>             paths;
-    CFileItemPtr                   item;
-
-    CLog::Log(LOGINFO, "Received UPnP Browse Metadata request for object '%s'", (const char*)object_id);
-
-    if (id.StartsWith("virtualpath://")) {
-        id.TrimRight("/");
-        if (id == "virtualpath://upnproot") {
-            id += "/";
-            item.reset(new CFileItem((const char*)id, true));
-            item->SetLabel("Root");
-            item->SetLabelPreformated(true);
-            object = Build(item, true, context);
-        } else {
-            return NPT_FAILURE;
-        }
-    } else {
-        // determine if it's a container by calling CDirectory::Exists
-        item.reset(new CFileItem((const char*)id, CDirectory::Exists((const char*)id)));
-
-        // determine parent id for shared paths only
-        // otherwise let db find out
-        CStdString parent;
-        if (!URIUtils::GetParentPath((const char*)id, parent)) parent = "0";
-
-//#ifdef WMP_ID_MAPPING
-//        if (!id.StartsWith("musicdb://") && !id.StartsWith("videodb://")) {
-//            parent = "";
-//        }
-//#endif
-
-        object = Build(item, true, context, parent.empty()?NULL:parent.c_str());
-    }
-
-    if (object.IsNull()) {
-        /* error */
-        NPT_LOG_WARNING_1("CUPnPServer::OnBrowseMetadata - Object null (%s)", object_id);
-        action->SetError(701, "No Such Object.");
-        return NPT_FAILURE;
-    }
-
-    NPT_String tmp;
-    NPT_CHECK(PLT_Didl::ToDidl(*object.AsPointer(), filter, tmp));
-
-    /* add didl header and footer */
-    didl = didl_header + tmp + didl_footer;
-
-    NPT_CHECK(action->SetArgumentValue("Result", didl));
-    NPT_CHECK(action->SetArgumentValue("NumberReturned", "1"));
-    NPT_CHECK(action->SetArgumentValue("TotalMatches", "1"));
-
-    // update ID may be wrong here, it should be the one of the container?
-    NPT_CHECK(action->SetArgumentValue("UpdateId", "0"));
-
-    // TODO: We need to keep track of the overall SystemUpdateID of the CDS
-
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::OnBrowseDirectChildren
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::OnBrowseDirectChildren(PLT_ActionReference&          action,
-                                    const char*                   object_id,
-                                    const char*                   filter,
-                                    NPT_UInt32                    starting_index,
-                                    NPT_UInt32                    requested_count,
-                                    const char*                   sort_criteria,
-                                    const PLT_HttpRequestContext& context)
-{
-    CFileItemList items;
-    NPT_String    parent_id = TranslateWMPObjectId(object_id);
-
-    CLog::Log(LOGINFO, "UPnP: Received Browse DirectChildren request for object '%s', with sort criteria %s", object_id, sort_criteria);
-
-    items.SetPath(CStdString(parent_id));
-    if (!items.Load()) {
-        // cache anything that takes more than a second to retrieve
-      unsigned int time = XbmcThreads::SystemClockMillis();
-
-        if (parent_id.StartsWith("virtualpath://upnproot")) {
-            CFileItemPtr item;
-
-            // music library
-            item.reset(new CFileItem("musicdb://", true));
-            item->SetLabel("Music Library");
-            item->SetLabelPreformated(true);
-            items.Add(item);
-
-            // video library
-            item.reset(new CFileItem("videodb://", true));
-            item->SetLabel("Video Library");
-            item->SetLabelPreformated(true);
-            items.Add(item);
-
-        } else {
-            CDirectory::GetDirectory((const char*)parent_id, items);
-        }
-
-        if (items.CacheToDiscAlways() || (items.CacheToDiscIfSlow() && (XbmcThreads::SystemClockMillis() - time) > 1000 )) {
-            items.Save();
-        }
-    }
-
-    // Always sort by label
-    items.Sort(SORT_METHOD_LABEL, SortOrderAscending);
-
-    // Don't pass parent_id if action is Search not BrowseDirectChildren, as
-    // we want the engine to determine the best parent id, not necessarily the one
-    // passed
-    NPT_String action_name = action->GetActionDesc().GetName();
-    return BuildResponse(
-        action,
-        items,
-        filter,
-        starting_index,
-        requested_count,
-        sort_criteria,
-        context,
-        (action_name.Compare("Search", true)==0)?NULL:parent_id.GetChars());
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::BuildResponse
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::BuildResponse(PLT_ActionReference&          action,
-                           CFileItemList&                items,
-                           const char*                   filter,
-                           NPT_UInt32                    starting_index,
-                           NPT_UInt32                    requested_count,
-                           const char*                   sort_criteria,
-                           const PLT_HttpRequestContext& context,
-                           const char*                   parent_id /* = NULL */)
-{
-    NPT_COMPILER_UNUSED(sort_criteria);
-
-    CLog::Log(LOGDEBUG, "Building UPnP response with filter '%s', starting @ %d with %d requested",
-        (const char*)filter,
-        starting_index,
-        requested_count);
-
-    // won't return more than UPNP_MAX_RETURNED_ITEMS items at a time to keep things smooth
-    // 0 requested means as many as possible
-    NPT_UInt32 max_count  = (requested_count == 0)?m_MaxReturnedItems:min((unsigned long)requested_count, (unsigned long)m_MaxReturnedItems);
-    NPT_UInt32 stop_index = min((unsigned long)(starting_index + max_count), (unsigned long)items.Size()); // don't return more than we can
-
-    NPT_Cardinal count = 0;
-    NPT_String didl = didl_header;
-    PLT_MediaObjectReference object;
-    for (unsigned long i=starting_index; i<stop_index; ++i) {
-        object = Build(items[i], true, context, parent_id);
-        if (object.IsNull()) {
-            continue;
-        }
-
-        NPT_String tmp;
-        NPT_CHECK(PLT_Didl::ToDidl(*object.AsPointer(), filter, tmp));
-
-        // Neptunes string growing is dead slow for small additions
-        if (didl.GetCapacity() < tmp.GetLength() + didl.GetLength()) {
-            didl.Reserve((tmp.GetLength() + didl.GetLength())*2);
-        }
-        didl += tmp;
-        ++count;
-    }
-
-    didl += didl_footer;
-
-    CLog::Log(LOGDEBUG, "Returning UPnP response with %d items out of %d total matches",
-        count,
-        items.Size());
-
-    NPT_CHECK(action->SetArgumentValue("Result", didl));
-    NPT_CHECK(action->SetArgumentValue("NumberReturned", NPT_String::FromInteger(count)));
-    NPT_CHECK(action->SetArgumentValue("TotalMatches", NPT_String::FromInteger(items.Size())));
-    NPT_CHECK(action->SetArgumentValue("UpdateId", "0"));
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   FindSubCriteria
-+---------------------------------------------------------------------*/
-static
-NPT_String
-FindSubCriteria(NPT_String criteria, const char* name)
-{
-    NPT_String result;
-    int search = criteria.Find(name);
-    if (search >= 0) {
-        criteria = criteria.Right(criteria.GetLength() - search - NPT_StringLength(name));
-        criteria.TrimLeft(" ");
-        if (criteria.GetLength()>0 && criteria[0] == '=') {
-            criteria.TrimLeft("= ");
-            if (criteria.GetLength()>0 && criteria[0] == '\"') {
-                search = criteria.Find("\"", 1);
-                if (search > 0) result = criteria.SubString(1, search-1);
-            }
-        }
-    }
-    return result;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::OnSearchContainer
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::OnSearchContainer(PLT_ActionReference&          action,
-                               const char*                   object_id,
-                               const char*                   search_criteria,
-                               const char*                   filter,
-                               NPT_UInt32                    starting_index,
-                               NPT_UInt32                    requested_count,
-                               const char*                   sort_criteria,
-                               const PLT_HttpRequestContext& context)
-{
-    CLog::Log(LOGDEBUG, "Received Search request for object '%s' with search '%s'",
-        (const char*)object_id,
-        (const char*)search_criteria);
-
-    NPT_String id = object_id;
-    if (id.StartsWith("musicdb://")) {
-        // we browse for all tracks given a genre, artist or album
-        if (NPT_String(search_criteria).Find("object.item.audioItem") >= 0) {
-            if (!id.EndsWith("/")) id += "/";
-            NPT_Cardinal count = id.SubString(10).Split("/").GetItemCount();
-            // remove extra empty node count
-            count = count?count-1:0;
-
-            // genre
-            if (id.StartsWith("musicdb://1/")) {
-                // all tracks of all genres
-                if (count == 1)
-                    id += "-1/-1/-1/";
-                // all tracks of a specific genre
-                else if (count == 2)
-                    id += "-1/-1/";
-                // all tracks of a specific genre of a specfic artist
-                else if (count == 3)
-                    id += "-1/";
-            } else if (id.StartsWith("musicdb://2/")) {
-                // all tracks by all artists
-                if (count == 1)
-                    id += "-1/-1/";
-                // all tracks of a specific artist
-                else if (count == 2)
-                    id += "-1/";
-            } else if (id.StartsWith("musicdb://3/")) {
-                // all albums ?
-                if (count == 1) id += "-1/";
-            }
-        }
-        return OnBrowseDirectChildren(action, id, filter, starting_index, requested_count, sort_criteria, context);
-    } else if (NPT_String(search_criteria).Find("object.item.audioItem") >= 0) {
-        // look for artist, album & genre filters
-        NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
-        NPT_String album = FindSubCriteria(search_criteria, "upnp:album");
-        NPT_String artist = FindSubCriteria(search_criteria, "upnp:artist");
-        // sonos looks for microsoft specific stuff
-        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistPerformer");
-        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistAlbumArtist");
-        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:authorComposer");
-
-        CMusicDatabase database;
-        database.Open();
-
-        if (genre.GetLength() > 0) {
-            // all tracks by genre filtered by artist and/or album
-            CStdString strPath;
-            strPath.Format("musicdb://1/%ld/%ld/%ld/",
-                database.GetGenreByName((const char*)genre),
-                database.GetArtistByName((const char*)artist), // will return -1 if no artist
-                database.GetAlbumByName((const char*)album));  // will return -1 if no album
-
-            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
-        } else if (artist.GetLength() > 0) {
-            // all tracks by artist name filtered by album if passed
-            CStdString strPath;
-            strPath.Format("musicdb://2/%ld/%ld/",
-                database.GetArtistByName((const char*)artist),
-                database.GetAlbumByName((const char*)album)); // will return -1 if no album
-
-            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
-        } else if (album.GetLength() > 0) {
-            // all tracks by album name
-            CStdString strPath;
-            strPath.Format("musicdb://3/%ld/",
-                database.GetAlbumByName((const char*)album));
-
-            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
-        }
-
-        // browse all songs
-        return OnBrowseDirectChildren(action, "musicdb://4/", filter, starting_index, requested_count, sort_criteria, context);
-    } else if (NPT_String(search_criteria).Find("object.container.album.musicAlbum") >= 0) {
-        // sonos filters by genre
-        NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
-
-        // 360 hack: artist/albums using search
-        NPT_String artist = FindSubCriteria(search_criteria, "upnp:artist");
-        // sonos looks for microsoft specific stuff
-        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistPerformer");
-        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistAlbumArtist");
-        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:authorComposer");
-
-        CMusicDatabase database;
-        database.Open();
-
-        if (genre.GetLength() > 0) {
-            CStdString strPath;
-            strPath.Format("musicdb://1/%ld/%ld/",
-                database.GetGenreByName((const char*)genre),
-                database.GetArtistByName((const char*)artist)); // no artist should return -1
-            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
-        } else if (artist.GetLength() > 0) {
-            CStdString strPath;
-            strPath.Format("musicdb://2/%ld/",
-                database.GetArtistByName((const char*)artist));
-            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
-        }
-
-        // all albums
-        return OnBrowseDirectChildren(action, "musicdb://3/", filter, starting_index, requested_count, sort_criteria, context);
-    } else if (NPT_String(search_criteria).Find("object.container.person.musicArtist") >= 0) {
-        // Sonos filters by genre
-        NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
-        if (genre.GetLength() > 0) {
-            CMusicDatabase database;
-            database.Open();
-            CStdString strPath;
-            strPath.Format("musicdb://1/%ld/", database.GetGenreByName((const char*)genre));
-            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
-        }
-        return OnBrowseDirectChildren(action, "musicdb://2/", filter, starting_index, requested_count, sort_criteria, context);
-    }  else if (NPT_String(search_criteria).Find("object.container.genre.musicGenre") >= 0) {
-        return OnBrowseDirectChildren(action, "musicdb://1/", filter, starting_index, requested_count, sort_criteria, context);
-    } else if (NPT_String(search_criteria).Find("object.container.playlistContainer") >= 0) {
-        return OnBrowseDirectChildren(action, "special://musicplaylists/", filter, starting_index, requested_count, sort_criteria, context);
-    } else if (NPT_String(search_criteria).Find("object.item.videoItem") >= 0) {
-      CFileItemList items, itemsall;
-
-      CVideoDatabase database;
-      if (!database.Open()) {
-        action->SetError(800, "Internal Error");
-        return NPT_SUCCESS;
-      }
-
-      if (!database.GetMoviesNav("videodb://1/2/", items)) {
-        action->SetError(800, "Internal Error");
-        return NPT_SUCCESS;
-      }
-      itemsall.Append(items);
-      items.Clear();
-
-      // TODO - set proper base url for this
-      if (!database.GetEpisodesByWhere("videodb://2/0/", "", items, false)) {
-        action->SetError(800, "Internal Error");
-        return NPT_SUCCESS;
-      }
-      itemsall.Append(items);
-      items.Clear();
-
-      return BuildResponse(action, itemsall, filter, starting_index, requested_count, sort_criteria, context, NULL);
-  } else if (NPT_String(search_criteria).Find("object.item.imageItem") >= 0) {
-      CFileItemList items;
-      return BuildResponse(action, items, filter, starting_index, requested_count, sort_criteria, context, NULL);;
-  }
-
-  return NPT_FAILURE;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPServer::ServeFile
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::ServeFile(const NPT_HttpRequest&              request,
-                       const NPT_HttpRequestContext& context,
-                       NPT_HttpResponse&             response,
-                       const NPT_String&             md5)
-{
-    // Translate hash to filename
-    NPT_String file_path(md5), *file_path2;
-    { NPT_AutoLock lock(m_FileMutex);
-      if(NPT_SUCCEEDED(m_FileMap.Get(md5, file_path2))) {
-        file_path = *file_path2;
-        CLog::Log(LOGDEBUG, "Received request to serve '%s' = '%s'", (const char*)md5, (const char*)file_path);
-      } else {
-        CLog::Log(LOGDEBUG, "Received request to serve unknown md5 '%s'", (const char*)md5);
-        response.SetStatus(404, "File Not Found");
-        return NPT_SUCCESS;
-      }
-    }
-
-    // File requested
-    NPT_HttpUrl rooturi(context.GetLocalAddress().GetIpAddress().ToString(), context.GetLocalAddress().GetPort(), "/");
-
-    if (file_path.Left(8).Compare("stack://", true) == 0) {
-
-        NPT_List<NPT_String> files = file_path.SubString(8).Split(" , ");
-        if (files.GetItemCount() == 0) {
-            response.SetStatus(404, "File Not Found");
-            return NPT_SUCCESS;
-        }
-
-        NPT_String output;
-        output.Reserve(file_path.GetLength()*2);
-        output += "#EXTM3U\r\n";
-
-        NPT_List<NPT_String>::Iterator url = files.GetFirstItem();
-        for (;url;url++) {
-            output += "#EXTINF:-1," + URIUtils::GetFileName((const char*)*url);
-            output += "\r\n";
-            output += BuildSafeResourceUri(
-                          rooturi,
-                          context.GetLocalAddress().GetIpAddress().ToString(),
-                          *url);
-            output += "\r\n";
-        }
-
-        PLT_HttpHelper::SetBody(response, (const char*)output, output.GetLength());
-        response.GetHeaders().SetHeader("Content-Disposition", "inline; filename=\"stack.m3u\"");
-        return NPT_SUCCESS;
-    }
-
-    if(URIUtils::IsURL((const char*)file_path))
-    {
-      CStdString disp = "inline; filename=\"" + URIUtils::GetFileName((const char*)file_path) + "\"";
-      response.GetHeaders().SetHeader("Content-Disposition", disp.c_str());
-    }
-
-    return PLT_HttpServer::ServeFile(request,
-                                       context,
-                                       response,
-                                       file_path);
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer
-+---------------------------------------------------------------------*/
-class CUPnPRenderer : public PLT_MediaRenderer
-{
-public:
-    CUPnPRenderer(const char*  friendly_name,
-                  bool         show_ip = false,
-                  const char*  uuid = NULL,
-                  unsigned int port = 0);
-
-    void UpdateState();
-
-    // Http server handler
-    virtual NPT_Result ProcessHttpRequest(NPT_HttpRequest&              request,
-                                          const NPT_HttpRequestContext& context,
-                                          NPT_HttpResponse&             response);
-
-    // AVTransport methods
-    virtual NPT_Result OnNext(PLT_ActionReference& action);
-    virtual NPT_Result OnPause(PLT_ActionReference& action);
-    virtual NPT_Result OnPlay(PLT_ActionReference& action);
-    virtual NPT_Result OnPrevious(PLT_ActionReference& action);
-    virtual NPT_Result OnStop(PLT_ActionReference& action);
-    virtual NPT_Result OnSeek(PLT_ActionReference& action);
-    virtual NPT_Result OnSetAVTransportURI(PLT_ActionReference& action);
-
-    // RenderingControl methods
-    virtual NPT_Result OnSetVolume(PLT_ActionReference& action);
-    virtual NPT_Result OnSetMute(PLT_ActionReference& action);
-
-private:
-    NPT_Result SetupServices();
-    NPT_Result GetMetadata(NPT_String& meta);
-    NPT_Result PlayMedia(const char* uri,
-                         const char* metadata = NULL,
-                         PLT_Action* action = NULL);
-    NPT_Mutex m_state;
-};
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::CUPnPRenderer
-+---------------------------------------------------------------------*/
-CUPnPRenderer::CUPnPRenderer(const char*  friendly_name,
-                             bool         show_ip /* = false */,
-                             const char*  uuid /* = NULL */,
-                             unsigned int port /* = 0 */) :
-    PLT_MediaRenderer(friendly_name,
-                      show_ip,
-                      uuid,
-                      port)
-{
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::SetupServices
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::SetupServices()
-{
-    NPT_CHECK(PLT_MediaRenderer::SetupServices());
-
-    // update what we can play
-    PLT_Service* service = NULL;
-    NPT_CHECK_FATAL(FindServiceByType("urn:schemas-upnp-org:service:ConnectionManager:1", service));
-    service->SetStateVariable("SinkProtocolInfo"
-        ,"http-get:*:*:*"
-        ",xbmc-get:*:*:*"
-        ",http-get:*:audio/mpegurl:*"
-        ",http-get:*:audio/mpeg:*"
-        ",http-get:*:audio/mpeg3:*"
-        ",http-get:*:audio/mp3:*"
-        ",http-get:*:audio/mp4:*"
-        ",http-get:*:audio/basic:*"
-        ",http-get:*:audio/midi:*"
-        ",http-get:*:audio/ulaw:*"
-        ",http-get:*:audio/ogg:*"
-        ",http-get:*:audio/DVI4:*"
-        ",http-get:*:audio/G722:*"
-        ",http-get:*:audio/G723:*"
-        ",http-get:*:audio/G726-16:*"
-        ",http-get:*:audio/G726-24:*"
-        ",http-get:*:audio/G726-32:*"
-        ",http-get:*:audio/G726-40:*"
-        ",http-get:*:audio/G728:*"
-        ",http-get:*:audio/G729:*"
-        ",http-get:*:audio/G729D:*"
-        ",http-get:*:audio/G729E:*"
-        ",http-get:*:audio/GSM:*"
-        ",http-get:*:audio/GSM-EFR:*"
-        ",http-get:*:audio/L8:*"
-        ",http-get:*:audio/L16:*"
-        ",http-get:*:audio/LPC:*"
-        ",http-get:*:audio/MPA:*"
-        ",http-get:*:audio/PCMA:*"
-        ",http-get:*:audio/PCMU:*"
-        ",http-get:*:audio/QCELP:*"
-        ",http-get:*:audio/RED:*"
-        ",http-get:*:audio/VDVI:*"
-        ",http-get:*:audio/ac3:*"
-        ",http-get:*:audio/vorbis:*"
-        ",http-get:*:audio/speex:*"
-        ",http-get:*:audio/x-aiff:*"
-        ",http-get:*:audio/x-pn-realaudio:*"
-        ",http-get:*:audio/x-realaudio:*"
-        ",http-get:*:audio/x-wav:*"
-        ",http-get:*:audio/x-ms-wma:*"
-        ",http-get:*:audio/x-mpegurl:*"
-        ",http-get:*:application/x-shockwave-flash:*"
-        ",http-get:*:application/ogg:*"
-        ",http-get:*:application/sdp:*"
-        ",http-get:*:image/gif:*"
-        ",http-get:*:image/jpeg:*"
-        ",http-get:*:image/ief:*"
-        ",http-get:*:image/png:*"
-        ",http-get:*:image/tiff:*"
-        ",http-get:*:video/avi:*"
-        ",http-get:*:video/mpeg:*"
-        ",http-get:*:video/fli:*"
-        ",http-get:*:video/flv:*"
-        ",http-get:*:video/quicktime:*"
-        ",http-get:*:video/vnd.vivo:*"
-        ",http-get:*:video/vc1:*"
-        ",http-get:*:video/ogg:*"
-        ",http-get:*:video/mp4:*"
-        ",http-get:*:video/BT656:*"
-        ",http-get:*:video/CelB:*"
-        ",http-get:*:video/JPEG:*"
-        ",http-get:*:video/H261:*"
-        ",http-get:*:video/H263:*"
-        ",http-get:*:video/H263-1998:*"
-        ",http-get:*:video/H263-2000:*"
-        ",http-get:*:video/MPV:*"
-        ",http-get:*:video/MP2T:*"
-        ",http-get:*:video/MP1S:*"
-        ",http-get:*:video/MP2P:*"
-        ",http-get:*:video/BMPEG:*"
-        ",http-get:*:video/x-ms-wmv:*"
-        ",http-get:*:video/x-ms-avi:*"
-        ",http-get:*:video/x-flv:*"
-        ",http-get:*:video/x-fli:*"
-        ",http-get:*:video/x-ms-asf:*"
-        ",http-get:*:video/x-ms-asx:*"
-        ",http-get:*:video/x-ms-wmx:*"
-        ",http-get:*:video/x-ms-wvx:*"
-        ",http-get:*:video/x-msvideo:*"
-        );
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::ProcessHttpRequest
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::ProcessHttpRequest(NPT_HttpRequest&              request,
-                                  const NPT_HttpRequestContext& context,
-                                  NPT_HttpResponse&             response)
-{
-    // get the address of who sent us some data back
-    NPT_String  ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
-    NPT_String  method     = request.GetMethod();
-    NPT_String  protocol   = request.GetProtocol();
-    NPT_HttpUrl url        = request.GetUrl();
-
-    if (url.GetPath() == "/thumb.jpg") {
-        NPT_HttpUrlQuery query(url.GetQuery());
-        NPT_String filepath = query.GetField("path");
-        if (!filepath.IsEmpty()) {
-            NPT_HttpEntity* entity = response.GetEntity();
-            if (entity == NULL) return NPT_ERROR_INVALID_STATE;
-
-            // check the method
-            if (request.GetMethod() != NPT_HTTP_METHOD_GET &&
-                request.GetMethod() != NPT_HTTP_METHOD_HEAD) {
-                response.SetStatus(405, "Method Not Allowed");
-                return NPT_SUCCESS;
-            }
-
-            // ensure that the request's path is a valid thumb path
-            if (URIUtils::IsRemote(filepath.GetChars()) ||
-                !filepath.StartsWith(g_settings.GetUserDataFolder())) {
-                response.SetStatus(404, "Not Found");
-                return NPT_SUCCESS;
-            }
-
-            // prevent hackers from accessing files outside of our root
-            if ((filepath.Find("/..") >= 0) || (filepath.Find("\\..") >=0)) {
-                return NPT_FAILURE;
-            }
-
-            // open the file
-            NPT_File file(filepath);
-            NPT_Result result = file.Open(NPT_FILE_OPEN_MODE_READ);
-            if (NPT_FAILED(result)) {
-                response.SetStatus(404, "Not Found");
-                return NPT_SUCCESS;
-            }
-            NPT_InputStreamReference stream;
-            file.GetInputStream(stream);
-            entity->SetContentType(CUPnPServer::GetMimeType(filepath));
-            entity->SetInputStream(stream, true);
-
-            return NPT_SUCCESS;
-        }
-    }
-
-    return PLT_MediaRenderer::ProcessHttpGetRequest(request, context, response);
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::UpdateState
-+---------------------------------------------------------------------*/
-void
-CUPnPRenderer::UpdateState()
-{
-    NPT_AutoLock lock(m_state);
-
-    PLT_Service *avt, *rct;
-    if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))
-        return;
-    if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:RenderingControl:1", rct)))
-        return;
-
-    /* don't update state while transitioning */
-    NPT_String state;
-    avt->GetStateVariableValue("TransportState", state);
-    if(state == "TRANSITIONING")
-        return;
-
-    CStdString buffer;
-    int volume;
-    if (g_settings.m_bMute) {
-        rct->SetStateVariable("Mute", "1");
-    } else {
-        rct->SetStateVariable("Mute", "0");
-    }
-    volume = g_application.GetVolume();
-
-    buffer.Format("%d", volume);
-    rct->SetStateVariable("Volume", buffer.c_str());
-
-    buffer.Format("%d", 256 * (volume * 60 - 60) / 100);
-    rct->SetStateVariable("VolumeDb", buffer.c_str());
-
-    if (g_application.IsPlaying() || g_application.IsPaused()) {
-        if (g_application.IsPaused()) {
-            avt->SetStateVariable("TransportState", "PAUSED_PLAYBACK");
-        } else {
-            avt->SetStateVariable("TransportState", "PLAYING");
-        }
-
-        avt->SetStateVariable("TransportStatus", "OK");
-        avt->SetStateVariable("TransportPlaySpeed", (const char*)NPT_String::FromInteger(g_application.GetPlaySpeed()));
-        avt->SetStateVariable("NumberOfTracks", "1");
-        avt->SetStateVariable("CurrentTrack", "1");
-
-        buffer = g_infoManager.GetCurrentPlayTime(TIME_FORMAT_HH_MM_SS);
-        avt->SetStateVariable("RelativeTimePosition", buffer.c_str());
-        avt->SetStateVariable("AbsoluteTimePosition", buffer.c_str());
-
-        buffer = g_infoManager.GetDuration(TIME_FORMAT_HH_MM_SS);
-        if (buffer.length() > 0) {
-          avt->SetStateVariable("CurrentTrackDuration", buffer.c_str());
-          avt->SetStateVariable("CurrentMediaDuration", buffer.c_str());
-        } else {
-          avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
-          avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
-        }
-
-        avt->SetStateVariable("AVTransportURI", g_application.CurrentFile().c_str());
-        avt->SetStateVariable("CurrentTrackURI", g_application.CurrentFile().c_str());
-
-        NPT_String metadata;
-        avt->GetStateVariableValue("AVTransportURIMetaData", metadata);
-        // try to recreate the didl dynamically if not set
-        if (metadata.IsEmpty()) {
-            GetMetadata(metadata);
-        }
-        avt->SetStateVariable("CurrentTrackMetadata", metadata);
-        avt->SetStateVariable("AVTransportURIMetaData", metadata);
-    } else if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
-        avt->SetStateVariable("TransportState", "PLAYING");
-
-        avt->SetStateVariable("AVTransportURI" , g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
-        avt->SetStateVariable("CurrentTrackURI", g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
-        avt->SetStateVariable("TransportPlaySpeed", "1");
-
-        CGUIWindowSlideShow *slideshow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
-        if (slideshow)
-        {
-          CStdString index;
-          index.Format("%d", slideshow->NumSlides());
-          avt->SetStateVariable("NumberOfTracks", index.c_str());
-          index.Format("%d", slideshow->CurrentSlide());
-          avt->SetStateVariable("CurrentTrack", index.c_str());
-
-        }
-
-        avt->SetStateVariable("CurrentTrackMetadata", "");
-        avt->SetStateVariable("AVTransportURIMetaData", "");
-
-    } else {
-        avt->SetStateVariable("TransportState", "STOPPED");
-        avt->SetStateVariable("TransportPlaySpeed", "1");
-        avt->SetStateVariable("NumberOfTracks", "0");
-        avt->SetStateVariable("CurrentTrack", "0");
-        avt->SetStateVariable("RelativeTimePosition", "00:00:00");
-        avt->SetStateVariable("AbsoluteTimePosition", "00:00:00");
-        avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
-        avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
-    }
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::GetMetadata
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::GetMetadata(NPT_String& meta)
-{
-    NPT_Result res = NPT_FAILURE;
-    const CFileItem &item = g_application.CurrentFileItem();
-    NPT_String file_path;
-    PLT_MediaObject* object = CUPnPServer::BuildObject(item, file_path, false);
-    if (object) {
-        // fetch the path to the thumbnail
-        CStdString thumb = g_infoManager.GetImage(MUSICPLAYER_COVER, -1); //TODO: Only audio for now
-
-        NPT_String ip;
-        if (g_application.getNetwork().GetFirstConnectedInterface()) {
-            ip = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
-        }
-        // build url, use the internal device http server to serv the image
-        NPT_HttpUrlQuery query;
-        query.AddField("path", thumb.c_str());
-        PLT_AlbumArtInfo art;
-        art.uri = NPT_HttpUrl(
-            ip,
-            m_URLDescription.GetPort(),
-            "/thumb.jpg",
-            query.ToString()).ToString();
-        art.dlna_profile = "JPEG_TN";
-        object->m_ExtraInfo.album_arts.Add(art);
-
-        res = PLT_Didl::ToDidl(*object, "*", meta);
-        delete object;
-    }
-    return res;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnNext
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnNext(PLT_ActionReference& action)
-{
-    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
-        CAction action(ACTION_NEXT_PICTURE);
-        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
-    } else {
-        CApplicationMessenger::Get().PlayListPlayerNext();
-    }
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnPause
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnPause(PLT_ActionReference& action)
-{
-    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
-        CAction action(ACTION_PAUSE);
-        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
-    } else if (!g_application.IsPaused())
-      CApplicationMessenger::Get().MediaPause();
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnPlay
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnPlay(PLT_ActionReference& action)
-{
-    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
-        return NPT_SUCCESS;
-    } else if (g_application.IsPaused()) {
-      CApplicationMessenger::Get().MediaPause();
-    } else if (!g_application.IsPlaying()) {
-        NPT_String uri, meta;
-        PLT_Service* service;
-        // look for value set previously by SetAVTransportURI
-        NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
-        NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURI", uri));
-        NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURIMetaData", meta));
-
-        // if not set, use the current file being played
-        PlayMedia(uri, meta);
-    }
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnPrevious
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnPrevious(PLT_ActionReference& action)
-{
-    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
-        CAction action(ACTION_PREV_PICTURE);
-        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
-    } else {
-        CApplicationMessenger::Get().PlayListPlayerPrevious();
-    }
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnStop
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnStop(PLT_ActionReference& action)
-{
-    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
-        CAction action(ACTION_STOP);
-        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
-    } else {
-        CApplicationMessenger::Get().MediaStop();
-    }
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnSetAVTransportURI
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSetAVTransportURI(PLT_ActionReference& action)
-{
-    NPT_String uri, meta;
-    PLT_Service* service;
-    NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
-
-    NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURI", uri));
-    NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURIMetaData", meta));
-
-    // if not playing already, just keep around uri & metadata
-    // and wait for play command
-    if (!g_application.IsPlaying() && g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW) {
-        service->SetStateVariable("TransportState", "STOPPED");
-        service->SetStateVariable("TransportStatus", "OK");
-        service->SetStateVariable("TransportPlaySpeed", "1");
-        service->SetStateVariable("AVTransportURI", uri);
-        service->SetStateVariable("AVTransportURIMetaData", meta);
-
-        NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
-        return NPT_SUCCESS;
-    }
-
-    return PlayMedia(uri, meta, action.AsPointer());
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::PlayMedia
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::PlayMedia(const char* uri, const char* meta, PLT_Action* action)
-{
-    bool bImageFile = false;
-    PLT_Service* service;
-    NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
-
-    { NPT_AutoLock lock(m_state);
-      service->SetStateVariable("TransportState", "TRANSITIONING");
-      service->SetStateVariable("TransportStatus", "OK");
-    }
-
-    PLT_MediaObjectListReference list;
-    PLT_MediaObject*             object = NULL;
-
-    if (meta && NPT_SUCCEEDED(PLT_Didl::FromDidl(meta, list))) {
-        list->Get(0, object);
-    }
-
-    if (object) {
-        CFileItem item(uri, false);
-
-        PLT_MediaItemResource* res = object->m_Resources.GetFirstItem();
-        for(NPT_Cardinal i = 0; i < object->m_Resources.GetItemCount(); i++) {
-            if(object->m_Resources[i].m_Uri == uri) {
-                res = &object->m_Resources[i];
-                break;
-            }
-        }
-        for(NPT_Cardinal i = 0; i < object->m_Resources.GetItemCount(); i++) {
-            if(object->m_Resources[i].m_ProtocolInfo.ToString().StartsWith("xbmc-get:")) {
-                res = &object->m_Resources[i];
-                item.SetPath(CStdString(res->m_Uri));
-                break;
-            }
-        }
-
-        if (res && res->m_ProtocolInfo.IsValid()) {
-            item.SetMimeType((const char*)res->m_ProtocolInfo.GetContentType());
-        }
-
-        item.m_dateTime.SetFromDateString((const char*)object->m_Date);
-        item.m_strTitle = (const char*)object->m_Title;
-        item.SetLabel((const char*)object->m_Title);
-        item.SetLabelPreformated(true);
-        if (object->m_ExtraInfo.album_arts.GetItem(0)) {
-            // only considers first album art
-            item.SetThumbnailImage((const char*)object->m_ExtraInfo.album_arts.GetItem(0)->uri);
-        }
-        if (object->m_ObjectClass.type.StartsWith("object.item.audioItem")) {
-            if(NPT_SUCCEEDED(CUPnP::PopulateTagFromObject(*item.GetMusicInfoTag(), *object, res)))
-                item.SetLabelPreformated(false);
-        } else if (object->m_ObjectClass.type.StartsWith("object.item.videoItem")) {
-            if(NPT_SUCCEEDED(CUPnP::PopulateTagFromObject(*item.GetVideoInfoTag(), *object, res)))
-                item.SetLabelPreformated(false);
-        } else if (object->m_ObjectClass.type.StartsWith("object.item.imageItem")) {
-            bImageFile = true;
-        }
-        bImageFile?CApplicationMessenger::Get().PictureShow(item.GetPath())
-                  :CApplicationMessenger::Get().MediaPlay(item);
-    } else {
-        bImageFile = NPT_String(PLT_MediaObject::GetUPnPClass(uri)).StartsWith("object.item.imageItem", true);
-
-        bImageFile?CApplicationMessenger::Get().PictureShow((const char*)uri)
-                  :CApplicationMessenger::Get().MediaPlay((const char*)uri);
-    }
-
-    if (g_application.IsPlaying() || g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
-        NPT_AutoLock lock(m_state);
-        service->SetStateVariable("TransportState", "PLAYING");
-        service->SetStateVariable("TransportStatus", "OK");
-        service->SetStateVariable("AVTransportURI", uri);
-        service->SetStateVariable("AVTransportURIMetaData", meta);
-    } else {
-        NPT_AutoLock lock(m_state);
-        service->SetStateVariable("TransportState", "STOPPED");
-        service->SetStateVariable("TransportStatus", "ERROR_OCCURRED");
-    }
-
-    if (action) {
-        NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
-    }
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnSetVolume
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSetVolume(PLT_ActionReference& action)
-{
-    NPT_String volume;
-    NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredVolume", volume));
-    g_application.SetVolume((float)strtod((const char*)volume, NULL));
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnSetMute
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSetMute(PLT_ActionReference& action)
-{
-    NPT_String mute;
-    NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredMute",mute));
-    if((mute == "1") ^ g_settings.m_bMute)
-        g_application.ToggleMute();
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnPRenderer::OnSeek
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSeek(PLT_ActionReference& action)
-{
-    if (!g_application.IsPlaying()) return NPT_ERROR_INVALID_STATE;
-
-    NPT_String unit, target;
-    NPT_CHECK_SEVERE(action->GetArgumentValue("Unit", unit));
-    NPT_CHECK_SEVERE(action->GetArgumentValue("Target", target));
-
-    if (!unit.Compare("REL_TIME")) {
-        // converts target to seconds
-        NPT_UInt32 seconds;
-        NPT_CHECK_SEVERE(PLT_Didl::ParseTimeStamp(target, seconds));
-        g_application.SeekTime(seconds);
-    }
-
-    return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-|   CRendererReferenceHolder class
-+---------------------------------------------------------------------*/
-class CRendererReferenceHolder
-{
-public:
-    PLT_DeviceHostReference m_Device;
-};
-
-/*----------------------------------------------------------------------
-|   CMediaBrowser class
-+---------------------------------------------------------------------*/
-class CMediaBrowser : public PLT_SyncMediaBrowser,
-                      public PLT_MediaContainerChangesListener
-{
-public:
-    CMediaBrowser(PLT_CtrlPointReference& ctrlPoint)
-        : PLT_SyncMediaBrowser(ctrlPoint, true)
-    {
-        SetContainerListener(this);
-    }
-
-    // PLT_MediaBrowser methods
-    virtual bool OnMSAdded(PLT_DeviceDataReference& device)
-    {
-        CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
-        message.SetStringParam("upnp://");
-        g_windowManager.SendThreadMessage(message);
-
-        return PLT_SyncMediaBrowser::OnMSAdded(device);
-    }
-    virtual void OnMSRemoved(PLT_DeviceDataReference& device)
-    {
-        PLT_SyncMediaBrowser::OnMSRemoved(device);
-
-        CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
-        message.SetStringParam("upnp://");
-        g_windowManager.SendThreadMessage(message);
-
-        PLT_SyncMediaBrowser::OnMSRemoved(device);
-    }
-
-    // PLT_MediaContainerChangesListener methods
-    virtual void OnContainerChanged(PLT_DeviceDataReference& device,
-                                    const char*              item_id,
-                                    const char*              update_id)
-    {
-        NPT_String path = "upnp://"+device->GetUUID()+"/";
-        if (!NPT_StringsEqual(item_id, "0")) {
-            CStdString id = item_id;
-            CURL::Encode(id);
-            path += id.c_str();
-            path += "/";
-        }
-
-        CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
-        message.SetStringParam(path.GetChars());
-        g_windowManager.SendThreadMessage(message);
-    }
-};
-
-
-/*----------------------------------------------------------------------
-|   CUPnP::CUPnP
-+---------------------------------------------------------------------*/
-CUPnP::CUPnP() :
-    m_MediaBrowser(NULL),
-    m_ServerHolder(new CDeviceHostReferenceHolder()),
-    m_RendererHolder(new CRendererReferenceHolder()),
-    m_CtrlPointHolder(new CCtrlPointReferenceHolder())
-{
-    // initialize upnp context
-    m_UPnP = new PLT_UPnP();
-
-    // keep main IP around
-    if (g_application.getNetwork().GetFirstConnectedInterface()) {
-        m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
-    }
-    NPT_List<NPT_IpAddress> list;
-    if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list))) {
-        m_IP = (*(list.GetFirstItem())).ToString();
-    }
-
-    // start upnp monitoring
-    m_UPnP->Start();
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::~CUPnP
-+---------------------------------------------------------------------*/
-CUPnP::~CUPnP()
-{
-    m_UPnP->Stop();
-    StopClient();
-    StopServer();
-
-    delete m_UPnP;
-    delete m_ServerHolder;
-    delete m_RendererHolder;
-    delete m_CtrlPointHolder;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::GetInstance
-+---------------------------------------------------------------------*/
-CUPnP*
-CUPnP::GetInstance()
-{
-    if (!upnp) {
-        upnp = new CUPnP();
-    }
-
-    return upnp;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::ReleaseInstance
-+---------------------------------------------------------------------*/
-void
-CUPnP::ReleaseInstance(bool bWait)
-{
-    if (upnp) {
-        CUPnP* _upnp = upnp;
-        upnp = NULL;
-
-        if (bWait) {
-            delete _upnp;
-        } else {
-            // since it takes a while to clean up
-            // starts a detached thread to do this
-            CUPnPCleaner* cleaner = new CUPnPCleaner(_upnp);
-            cleaner->Start();
-        }
-    }
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::StartClient
-+---------------------------------------------------------------------*/
-void
-CUPnP::StartClient()
-{
-    if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
-
-    // create controlpoint
-    m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint();
-
-    // start it
-    m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
-
-    // start browser
-    m_MediaBrowser = new CMediaBrowser(m_CtrlPointHolder->m_CtrlPoint);
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::StopClient
-+---------------------------------------------------------------------*/
-void
-CUPnP::StopClient()
-{
-    if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
-
-    m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
-    m_CtrlPointHolder->m_CtrlPoint = NULL;
-
-    delete m_MediaBrowser;
-    m_MediaBrowser = NULL;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::CreateServer
-+---------------------------------------------------------------------*/
-CUPnPServer*
-CUPnP::CreateServer(int port /* = 0 */)
-{
-    CUPnPServer* device =
-        new CUPnPServer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
-                        g_settings.m_UPnPUUIDServer.length()?g_settings.m_UPnPUUIDServer.c_str():NULL,
-                        port);
-
-    // trying to set optional upnp values for XP UPnP UI Icons to detect us
-    // but it doesn't work anyways as it requires multicast for XP to detect us
-    device->m_PresentationURL =
-        NPT_HttpUrl(m_IP,
-                    atoi(g_guiSettings.GetString("services.webserverport")),
-                    "/").ToString();
-
-    device->m_ModelName        = "XBMC Media Center";
-    device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
-    device->m_ModelDescription = "XBMC Media Center - Media Server";
-    device->m_ModelURL         = "http://www.xbmc.org/";
-    device->m_Manufacturer     = "Team XBMC";
-    device->m_ManufacturerURL  = "http://www.xbmc.org/";
-
-    device->SetDelegate(device);
-    return device;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::StartServer
-+---------------------------------------------------------------------*/
-void
-CUPnP::StartServer()
-{
-    if (!m_ServerHolder->m_Device.IsNull()) return;
-
-    // load upnpserver.xml so that g_settings.m_vecUPnPMusiCMediaSources, etc.. are loaded
-    CStdString filename;
-    URIUtils::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);
-    g_settings.LoadUPnPXml(filename);
-
-    // create the server with a XBox compatible friendlyname and UUID from upnpserver.xml if found
-    m_ServerHolder->m_Device = CreateServer(g_settings.m_UPnPPortServer);
-
-    // start server
-    NPT_Result res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
-    if (NPT_FAILED(res)) {
-        // if the upnp device port was not 0, it could have failed because
-        // of port being in used, so restart with a random port
-        if (g_settings.m_UPnPPortServer > 0) m_ServerHolder->m_Device = CreateServer(0);
-
-        res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
-    }
-
-    // save port but don't overwrite saved settings if port was random
-    if (NPT_SUCCEEDED(res)) {
-        if (g_settings.m_UPnPPortServer == 0) {
-            g_settings.m_UPnPPortServer = m_ServerHolder->m_Device->GetPort();
-        }
-        CUPnPServer::m_MaxReturnedItems = UPNP_DEFAULT_MAX_RETURNED_ITEMS;
-        if (g_settings.m_UPnPMaxReturnedItems > 0) {
-            // must be > UPNP_DEFAULT_MIN_RETURNED_ITEMS
-            CUPnPServer::m_MaxReturnedItems = max(UPNP_DEFAULT_MIN_RETURNED_ITEMS, g_settings.m_UPnPMaxReturnedItems);
-        }
-        g_settings.m_UPnPMaxReturnedItems = CUPnPServer::m_MaxReturnedItems;
-    }
-
-    // save UUID
-    g_settings.m_UPnPUUIDServer = m_ServerHolder->m_Device->GetUUID();
-    g_settings.SaveUPnPXml(filename);
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::StopServer
-+---------------------------------------------------------------------*/
-void
-CUPnP::StopServer()
-{
-    if (m_ServerHolder->m_Device.IsNull()) return;
-
-    m_UPnP->RemoveDevice(m_ServerHolder->m_Device);
-    m_ServerHolder->m_Device = NULL;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::CreateRenderer
-+---------------------------------------------------------------------*/
-CUPnPRenderer*
-CUPnP::CreateRenderer(int port /* = 0 */)
-{
-    CUPnPRenderer* device =
-        new CUPnPRenderer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
-                          false,
-                          (g_settings.m_UPnPUUIDRenderer.length() ? g_settings.m_UPnPUUIDRenderer.c_str() : NULL),
-                          port);
-
-    device->m_PresentationURL =
-        NPT_HttpUrl(m_IP,
-                    atoi(g_guiSettings.GetString("services.webserverport")),
-                    "/").ToString();
-    device->m_ModelName        = "XBMC Media Center";
-    device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
-    device->m_ModelDescription = "XBMC Media Center - Media Renderer";
-    device->m_ModelURL         = "http://www.xbmc.org/";
-    device->m_Manufacturer     = "Team XBMC";
-    device->m_ManufacturerURL  = "http://www.xbmc.org/";
-
-    return device;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::StartRenderer
-+---------------------------------------------------------------------*/
-void CUPnP::StartRenderer()
-{
-    if (!m_RendererHolder->m_Device.IsNull()) return;
-
-    CStdString filename;
-    URIUtils::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);
-    g_settings.LoadUPnPXml(filename);
-
-    m_RendererHolder->m_Device = CreateRenderer(g_settings.m_UPnPPortRenderer);
-
-    NPT_Result res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
-
-    // failed most likely because port is in use, try again with random port now
-    if (NPT_FAILED(res) && g_settings.m_UPnPPortRenderer != 0) {
-        m_RendererHolder->m_Device = CreateRenderer(0);
-
-        res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
-    }
-
-    // save port but don't overwrite saved settings if random
-    if (NPT_SUCCEEDED(res) && g_settings.m_UPnPPortRenderer == 0) {
-        g_settings.m_UPnPPortRenderer = m_RendererHolder->m_Device->GetPort();
-    }
-
-    // save UUID
-    g_settings.m_UPnPUUIDRenderer = m_RendererHolder->m_Device->GetUUID();
-    g_settings.SaveUPnPXml(filename);
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::StopRenderer
-+---------------------------------------------------------------------*/
-void CUPnP::StopRenderer()
-{
-    if (m_RendererHolder->m_Device.IsNull()) return;
-
-    m_UPnP->RemoveDevice(m_RendererHolder->m_Device);
-    m_RendererHolder->m_Device = NULL;
-}
-
-/*----------------------------------------------------------------------
-|   CUPnP::UpdateState
-+---------------------------------------------------------------------*/
-void CUPnP::UpdateState()
-{
-  if (!m_RendererHolder->m_Device.IsNull())
-      ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();
-}
-
-int CUPnP::PopulateTagFromObject(CMusicInfoTag&          tag,
-                                 PLT_MediaObject&       object,
-                                 PLT_MediaItemResource* resource /* = NULL */)
-{
-    tag.SetTitle((const char*)object.m_Title);
-    tag.SetArtist((const char*)object.m_Creator);
-    for(PLT_PersonRoles::Iterator it = object.m_People.artists.GetFirstItem(); it; it++) {
-        if     (it->role == "")            tag.SetArtist((const char*)it->name);
-        else if(it->role == "Performer")   tag.SetArtist((const char*)it->name);
-        else if(it->role == "AlbumArtist") tag.SetAlbumArtist((const char*)it->name);
-    }
-    tag.SetTrackNumber(object.m_MiscInfo.original_track_number);
-    for (NPT_List<NPT_String>::Iterator it = object.m_Affiliation.genres.GetFirstItem(); it; it++)
-        tag.SetGenre((const char*) *it);
-    tag.SetAlbum((const char*)object.m_Affiliation.album);
-    if(resource)
-        tag.SetDuration(resource->m_Duration);
-    tag.SetLoaded();
-    return NPT_SUCCESS;
-}
-
-int CUPnP::PopulateTagFromObject(CVideoInfoTag&         tag,
-                                 PLT_MediaObject&       object,
-                                 PLT_MediaItemResource* resource /* = NULL */)
-{
-    CDateTime date;
-    date.SetFromDateString((const char*)object.m_Date);
-
-    if(!object.m_Recorded.program_title.IsEmpty())
-    {
-        int episode;
-        int season;
-        int title = object.m_Recorded.program_title.Find(" : ");
-        if(sscanf(object.m_Recorded.program_title, "S%2dE%2d", &season, &episode) == 2 && title >= 0) {
-            tag.m_strTitle = object.m_Recorded.program_title.SubString(title + 3);
-            tag.m_iEpisode = episode;
-            tag.m_iSeason  = season;
-        } else {
-            tag.m_strTitle = object.m_Recorded.program_title;
-            tag.m_iSeason  = object.m_Recorded.episode_number / 100;
-            tag.m_iEpisode = object.m_Recorded.episode_number % 100;
-        }
-        tag.m_firstAired = date;
-    }
-    else
-    {
-        tag.m_strTitle     = object.m_Title;
-        tag.m_premiered    = date;
-    }
-    tag.m_iYear       = date.GetYear();
-    for (unsigned int index = 0; index < object.m_Affiliation.genres.GetItemCount(); index++)
-      tag.m_genre.push_back(object.m_Affiliation.genres.GetItem(index)->GetChars());
-    tag.m_director = StringUtils::Split((CStdString)object.m_People.director, g_advancedSettings.m_videoItemSeparator);
-    tag.m_strTagLine  = object.m_Description.description;
-    tag.m_strPlot     = object.m_Description.long_description;
-    tag.m_strShowTitle = object.m_Recorded.series_title;
-
-    if(resource)
-      tag.m_strRuntime.Format("%d",resource->m_Duration);
-    return NPT_SUCCESS;
-}
-
-} /* namespace UPNP */
diff --git a/xbmc/network/UPnP.h b/xbmc/network/UPnP.h
deleted file mode 100644 (file)
index aaa7197..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-* UPnP Support for XBMC
-* Copyright (c) 2006 c0diq (Sylvain Rebaud)
-* Portions Copyright (c) by the authors of libPlatinum
-*
-* http://www.plutinosoft.com/blog/category/platinum/
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-
-#pragma once
-
-#include "utils/StdString.h"
-
-namespace MUSIC_INFO {
-class CMusicInfoTag;
-}
-
-class CVideoInfoTag;
-
-
-class PLT_UPnP;
-class PLT_SyncMediaBrowser;
-class PLT_MediaObject;
-class PLT_MediaItemResource;
-
-namespace UPNP
-{
-
-class CDeviceHostReferenceHolder;
-class CCtrlPointReferenceHolder;
-class CRendererReferenceHolder;
-class CUPnPRenderer;
-class CUPnPServer;
-
-class CUPnP
-{
-public:
-    CUPnP();
-    ~CUPnP();
-
-    // server
-    void StartServer();
-    void StopServer();
-
-    // client
-    void StartClient();
-    void StopClient();
-    bool IsClientStarted() { return (m_MediaBrowser != NULL); }
-
-    // renderer
-    void StartRenderer();
-    void StopRenderer();
-    void UpdateState();
-
-    // methods
-    static int PopulateTagFromObject(MUSIC_INFO::CMusicInfoTag& tag,
-                                     PLT_MediaObject&           object,
-                                     PLT_MediaItemResource*     resource = NULL);
-    static int PopulateTagFromObject(CVideoInfoTag&             tag,
-                                     PLT_MediaObject&           object,
-                                     PLT_MediaItemResource*     resource = NULL);
-
-    // class methods
-    static CUPnP* GetInstance();
-    static void   ReleaseInstance(bool bWait);
-    static bool   IsInstantiated() { return upnp != NULL; }
-
-private:
-    // methods
-    CUPnPRenderer* CreateRenderer(int port = 0);
-    CUPnPServer*   CreateServer(int port = 0);
-
-public:
-    PLT_SyncMediaBrowser*       m_MediaBrowser;
-
-private:
-    CStdString                  m_IP;
-    PLT_UPnP*                   m_UPnP;
-    CDeviceHostReferenceHolder* m_ServerHolder;
-    CRendererReferenceHolder*   m_RendererHolder;
-    CCtrlPointReferenceHolder*  m_CtrlPointHolder;
-
-
-    static CUPnP* upnp;
-};
-
-} /* namespace UPNP */
diff --git a/xbmc/network/upnp/Makefile.in b/xbmc/network/upnp/Makefile.in
new file mode 100644 (file)
index 0000000..909e633
--- /dev/null
@@ -0,0 +1,19 @@
+ifeq (@USE_UPNP@, 1)
+INCLUDES+=-I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Core \
+          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Platinum \
+          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Devices/MediaConnect \
+          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Devices/MediaRenderer \
+          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Devices/MediaServer \
+          -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Extras \
+          -I@abs_top_srcdir@/lib/libUPnP/Neptune/Source/System/Posix \
+          -I@abs_top_srcdir@/lib/libUPnP/Neptune/Source/Core
+
+SRCS= UPnP.cpp
+
+LIB=upnp.a
+
+include @abs_top_srcdir@/Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+
+endif
+
diff --git a/xbmc/network/upnp/UPnP.cpp b/xbmc/network/upnp/UPnP.cpp
new file mode 100644 (file)
index 0000000..14ca923
--- /dev/null
@@ -0,0 +1,2423 @@
+/*
+* UPnP Support for XBMC
+* Copyright (c) 2006 c0diq (Sylvain Rebaud)
+* Portions Copyright (c) by the authors of libPlatinum
+*
+* http://www.plutinosoft.com/blog/category/platinum/
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "threads/SystemClock.h"
+#include "UPnP.h"
+#include "utils/URIUtils.h"
+#include "Application.h"
+#include "ApplicationMessenger.h"
+#include "network/Network.h"
+#include "utils/log.h"
+#include "filesystem/MusicDatabaseDirectory.h"
+#include "filesystem/VideoDatabaseDirectory.h"
+#include "music/MusicDatabase.h"
+#include "video/VideoDatabase.h"
+#include "filesystem/VideoDatabaseDirectory/DirectoryNode.h"
+#include "filesystem/VideoDatabaseDirectory/QueryParams.h"
+#include "filesystem/File.h"
+#include "NptStrings.h"
+#include "Platinum.h"
+#include "PltMediaConnect.h"
+#include "PltMediaRenderer.h"
+#include "PltSyncMediaBrowser.h"
+#include "PltDidl.h"
+#include "NptNetwork.h"
+#include "NptConsole.h"
+#include "music/tags/MusicInfoTag.h"
+#include "pictures/PictureInfoTag.h"
+#include "pictures/GUIWindowSlideShow.h"
+#include "filesystem/Directory.h"
+#include "URL.h"
+#include "settings/GUISettings.h"
+#include "GUIUserMessages.h"
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/StringUtils.h"
+#include "FileItem.h"
+#include "guilib/GUIWindowManager.h"
+#include "GUIInfoManager.h"
+#include "utils/TimeUtils.h"
+#include "utils/md5.h"
+#include "guilib/Key.h"
+#include "ThumbLoader.h"
+#include "Util.h"
+
+using namespace std;
+using namespace MUSIC_INFO;
+using namespace XFILE;
+
+NPT_SET_LOCAL_LOGGER("xbmc.upnp")
+
+#define UPNP_DEFAULT_MAX_RETURNED_ITEMS 200
+#define UPNP_DEFAULT_MIN_RETURNED_ITEMS 30
+
+/*
+# Play speed
+#    1 normal
+#    0 invalid
+DLNA_ORG_PS = 'DLNA.ORG_PS'
+DLNA_ORG_PS_VAL = '1'
+
+# Convertion Indicator
+#    1 transcoded
+#    0 not transcoded
+DLNA_ORG_CI = 'DLNA.ORG_CI'
+DLNA_ORG_CI_VAL = '0'
+
+# Operations
+#    00 not time seek range, not range
+#    01 range supported
+#    10 time seek range supported
+#    11 both supported
+DLNA_ORG_OP = 'DLNA.ORG_OP'
+DLNA_ORG_OP_VAL = '01'
+
+# Flags
+#    senderPaced                      80000000  31
+#    lsopTimeBasedSeekSupported       40000000  30
+#    lsopByteBasedSeekSupported       20000000  29
+#    playcontainerSupported           10000000  28
+#    s0IncreasingSupported            08000000  27
+#    sNIncreasingSupported            04000000  26
+#    rtspPauseSupported               02000000  25
+#    streamingTransferModeSupported   01000000  24
+#    interactiveTransferModeSupported 00800000  23
+#    backgroundTransferModeSupported  00400000  22
+#    connectionStallingSupported      00200000  21
+#    dlnaVersion15Supported           00100000  20
+DLNA_ORG_FLAGS = 'DLNA.ORG_FLAGS'
+DLNA_ORG_FLAGS_VAL = '01500000000000000000000000000000'
+*/
+
+/*----------------------------------------------------------------------
+|   NPT_Console::Output
++---------------------------------------------------------------------*/
+void
+NPT_Console::Output(const char* message)
+{
+    CLog::Log(LOGDEBUG, "%s", message);
+}
+
+namespace UPNP
+{
+
+/*----------------------------------------------------------------------
+|   static
++---------------------------------------------------------------------*/
+CUPnP* CUPnP::upnp = NULL;
+
+namespace
+{
+
+  enum EClientQuirks
+  {
+    ECLIENTQUIRKS_NONE = 0x0
+
+    /* Client requires folder's to be marked as storageFolers as verndor type (360)*/
+  , ECLIENTQUIRKS_ONLYSTORAGEFOLDER = 0x01
+
+    /* Client can't handle subtypes for videoItems (360) */
+  , ECLIENTQUIRKS_BASICVIDEOCLASS = 0x02
+
+    /* Client requires album to be set to [Unknown Series] to show title (WMP) */
+  , ECLIENTQUIRKS_UNKNOWNSERIES = 0x04
+  };
+
+  static EClientQuirks GetClientQuirks(const PLT_HttpRequestContext* context)
+  {
+    if(context == NULL)
+        return ECLIENTQUIRKS_NONE;
+
+    unsigned int quirks = 0;
+    const NPT_String* user_agent = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_USER_AGENT); 
+    const NPT_String* server     = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_SERVER);
+
+    if (user_agent) {
+        if (user_agent->Find("XBox", 0, true) >= 0 || 
+            user_agent->Find("Xenon", 0, true) >= 0)
+            quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
+
+        if (user_agent->Find("Windows-Media-Player", 0, true) >= 0)
+            quirks |= ECLIENTQUIRKS_UNKNOWNSERIES;
+
+    }
+    if (server) {
+        if (server->Find("Xbox", 0, true) >= 0)
+            quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
+    }
+
+    return (EClientQuirks)quirks;
+  }
+}
+
+
+/*----------------------------------------------------------------------
+|   CDeviceHostReferenceHolder class
++---------------------------------------------------------------------*/
+class CDeviceHostReferenceHolder
+{
+public:
+    PLT_DeviceHostReference m_Device;
+};
+
+/*----------------------------------------------------------------------
+|   CCtrlPointReferenceHolder class
++---------------------------------------------------------------------*/
+class CCtrlPointReferenceHolder
+{
+public:
+    PLT_CtrlPointReference m_CtrlPoint;
+};
+
+/*----------------------------------------------------------------------
+|   CUPnPCleaner class
++---------------------------------------------------------------------*/
+class CUPnPCleaner : public NPT_Thread
+{
+public:
+    CUPnPCleaner(CUPnP* upnp) : NPT_Thread(true), m_UPnP(upnp) {}
+    void Run() {
+        delete m_UPnP;
+    }
+
+    CUPnP* m_UPnP;
+};
+
+/*----------------------------------------------------------------------
+|   CUPnP::CUPnP
++---------------------------------------------------------------------*/
+class CUPnPServer : public PLT_MediaConnect,
+                    public PLT_FileMediaConnectDelegate
+{
+public:
+    CUPnPServer(const char* friendly_name, const char* uuid = NULL, int port = 0) :
+        PLT_MediaConnect(friendly_name, false, uuid, port),
+        PLT_FileMediaConnectDelegate("/", "/") {
+    }
+
+    // PLT_MediaServer methods
+    virtual NPT_Result OnBrowseMetadata(PLT_ActionReference&          action,
+                                        const char*                   object_id,
+                                        const char*                   filter,
+                                        NPT_UInt32                    starting_index,
+                                        NPT_UInt32                    requested_count,
+                                        const char*                   sort_criteria,
+                                        const PLT_HttpRequestContext& context);
+    virtual NPT_Result OnBrowseDirectChildren(PLT_ActionReference&          action,
+                                              const char*                   object_id,
+                                              const char*                   filter,
+                                              NPT_UInt32                    starting_index,
+                                              NPT_UInt32                    requested_count,
+                                              const char*                   sort_criteria,
+                                              const PLT_HttpRequestContext& context);
+    virtual NPT_Result OnSearchContainer(PLT_ActionReference&          action,
+                                         const char*                   container_id,
+                                         const char*                   search_criteria,
+                                         const char*                   filter,
+                                         NPT_UInt32                    starting_index,
+                                         NPT_UInt32                    requested_count,
+                                         const char*                   sort_criteria,
+                                         const PLT_HttpRequestContext& context);
+
+    // PLT_FileMediaServer methods
+    virtual NPT_Result ServeFile(const NPT_HttpRequest&              request,
+                                 const NPT_HttpRequestContext& context,
+                                 NPT_HttpResponse&             response,
+                                 const NPT_String&             file_path);
+
+    // class methods
+    static NPT_Result PopulateObjectFromTag(CMusicInfoTag&         tag,
+                                            PLT_MediaObject&       object,
+                                            NPT_String*            file_path,
+                                            PLT_MediaItemResource* resource,
+                                            EClientQuirks          quirks);
+    static NPT_Result PopulateObjectFromTag(CVideoInfoTag&         tag,
+                                            PLT_MediaObject&       object,
+                                            NPT_String*            file_path,
+                                            PLT_MediaItemResource* resource,
+                                            EClientQuirks          quirks);
+    static PLT_MediaObject* BuildObject(const CFileItem&              item,
+                                        NPT_String&                   file_path,
+                                        bool                          with_count,
+                                        const PLT_HttpRequestContext* context = NULL,
+                                        CUPnPServer*                  upnp_server = NULL);
+    NPT_String BuildSafeResourceUri(const NPT_HttpUrl &rooturi,
+                                    const char*        host,
+                                    const char*        file_path);
+
+    void AddSafeResourceUri(PLT_MediaObject* object, const NPT_HttpUrl& rooturi, NPT_List<NPT_IpAddress> ips, const char* file_path, const NPT_String& info)
+    {
+        PLT_MediaItemResource res;
+        for(NPT_List<NPT_IpAddress>::Iterator ip = ips.GetFirstItem(); ip; ++ip) {
+            res.m_ProtocolInfo = PLT_ProtocolInfo(info);
+            res.m_Uri          = BuildSafeResourceUri(rooturi, (*ip).ToString(), file_path);
+            object->m_Resources.Add(res);
+        }
+    }
+
+
+    static const char* GetMimeTypeFromExtension(const char* extension, const PLT_HttpRequestContext* context = NULL);
+    static NPT_String  GetMimeType(const CFileItem& item, const PLT_HttpRequestContext* context = NULL);
+    static NPT_String  GetMimeType(const char* filename, const PLT_HttpRequestContext* context = NULL);
+    static const CStdString& CorrectAllItemsSortHack(const CStdString &item);
+
+private:
+    PLT_MediaObject* Build(CFileItemPtr                  item,
+                           bool                          with_count,
+                           const PLT_HttpRequestContext& context,
+                           const char*                   parent_id = NULL);
+    NPT_Result       BuildResponse(PLT_ActionReference&          action,
+                                   CFileItemList&                items,
+                                   const char*                   filter,
+                                   NPT_UInt32                    starting_index,
+                                   NPT_UInt32                    requested_count,
+                                   const char*                   sort_criteria,
+                                   const PLT_HttpRequestContext& context,
+                                   const char*                   parent_id /* = NULL */);
+
+    // class methods
+    static NPT_String GetParentFolder(NPT_String file_path) {
+        int index = file_path.ReverseFind("\\");
+        if (index == -1) return "";
+
+        return file_path.Left(index);
+    }
+    static const NPT_String GetProtocolInfo(const CFileItem& item,
+                                            const char* protocol,
+                                            const PLT_HttpRequestContext* context = NULL);
+
+    NPT_Mutex                       m_FileMutex;
+    NPT_Map<NPT_String, NPT_String> m_FileMap;
+
+public:
+    // class members
+    static NPT_UInt32 m_MaxReturnedItems;
+};
+
+NPT_UInt32 CUPnPServer::m_MaxReturnedItems = 0;
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::BuildSafeResourceUri
++---------------------------------------------------------------------*/
+NPT_String CUPnPServer::BuildSafeResourceUri(const NPT_HttpUrl &rooturi,
+                                             const char* host,
+                                             const char* file_path)
+{
+    CStdString md5;
+    XBMC::XBMC_MD5 md5state;
+    md5state.append(file_path);
+    md5state.getDigest(md5);
+    md5 += "/" + URIUtils::GetFileName(file_path);
+    { NPT_AutoLock lock(m_FileMutex);
+      NPT_CHECK(m_FileMap.Put(md5.c_str(), file_path));
+    }
+    return PLT_FileMediaServer::BuildSafeResourceUri(rooturi, host, md5.c_str());
+}
+
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::GetMimeType
++---------------------------------------------------------------------*/
+NPT_String
+CUPnPServer::GetMimeType(const char* filename,
+                            const PLT_HttpRequestContext* context /* = NULL */)
+{
+    NPT_String ext = URIUtils::GetExtension(filename).c_str();
+    ext.TrimLeft('.');
+    ext = ext.ToLowercase();
+
+    return PLT_MimeType::GetMimeTypeFromExtension(ext, context);
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::GetMimeType
++---------------------------------------------------------------------*/
+NPT_String
+CUPnPServer::GetMimeType(const CFileItem& item,
+                            const PLT_HttpRequestContext* context /* = NULL */)
+{
+    CStdString path = item.GetPath();
+    if (item.HasVideoInfoTag() && !item.GetVideoInfoTag()->GetPath().IsEmpty()) {
+        path = item.GetVideoInfoTag()->GetPath();
+    } else if (item.HasMusicInfoTag() && !item.GetMusicInfoTag()->GetURL().IsEmpty()) {
+        path = item.GetMusicInfoTag()->GetURL();
+    }
+
+    if(path.Left(8).Equals("stack://"))
+      return "audio/x-mpegurl";
+
+    NPT_String ext = URIUtils::GetExtension(path).c_str();
+    ext.TrimLeft('.');
+    ext = ext.ToLowercase();
+
+    NPT_String mime;
+
+    /* We always use Platinum mime type first
+       as it is defined to map extension to DLNA compliant mime type
+       or custom according to context (who asked for it) */
+    if (!ext.IsEmpty()) {
+        mime = PLT_MimeType::GetMimeTypeFromExtension(ext, context);
+        if (mime == "application/octet-stream") mime = "";
+    }
+
+    /* if Platinum couldn't map it, default to XBMC mapping */
+    if (mime.IsEmpty()) {
+        NPT_String mime = item.GetMimeType().c_str();
+        if (mime == "application/octet-stream") mime = "";
+    }
+
+    /* fallback to generic mime type if not found */
+    if (mime.IsEmpty()) {
+        if (item.IsVideo() || item.IsVideoDb() )
+            mime = "video/" + ext;
+        else if (item.IsAudio() || item.IsMusicDb() )
+            mime = "audio/" + ext;
+        else if (item.IsPicture() )
+            mime = "image/" + ext;
+    }
+
+    /* nothing we can figure out */
+    if (mime.IsEmpty()) {
+        mime = "application/octet-stream";
+    }
+
+    return mime;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::GetProtocolInfo
++---------------------------------------------------------------------*/
+const NPT_String
+CUPnPServer::GetProtocolInfo(const CFileItem&              item,
+                             const char*                   protocol,
+                             const PLT_HttpRequestContext* context /* = NULL */)
+{
+    NPT_String proto = protocol;
+
+    /* fixup the protocol just in case nothing was passed */
+    if (proto.IsEmpty()) {
+        proto = item.GetAsUrl().GetProtocol();
+    }
+
+    /*
+       map protocol to right prefix and use xbmc-get for
+       unsupported UPnP protocols for other xbmc clients
+       TODO: add rtsp ?
+    */
+    if (proto == "http") {
+        proto = "http-get";
+    } else {
+        proto = "xbmc-get";
+    }
+
+    /* we need a valid extension to retrieve the mimetype for the protocol info */
+    NPT_String mime = GetMimeType(item, context);
+    proto += ":*:" + mime + ":" + PLT_ProtocolInfo::GetDlnaExtension(mime, context);
+    return proto;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::PopulateObjectFromTag
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPServer::PopulateObjectFromTag(CMusicInfoTag&         tag,
+                                   PLT_MediaObject&       object,
+                                   NPT_String*            file_path, /* = NULL */
+                                   PLT_MediaItemResource* resource,  /* = NULL */
+                                   EClientQuirks          quirks)
+{
+    if (!tag.GetURL().IsEmpty() && file_path)
+      *file_path = tag.GetURL();
+
+    std::vector<std::string> genres = tag.GetGenre();
+    for (unsigned int index = 0; index < genres.size(); index++)
+      object.m_Affiliation.genres.Add(genres.at(index).c_str());
+    object.m_Title = tag.GetTitle();
+    object.m_Affiliation.album = tag.GetAlbum();
+    for (unsigned int index = 0; index < tag.GetArtist().size(); index++)
+    {
+      object.m_People.artists.Add(tag.GetArtist().at(index).c_str());
+      object.m_People.artists.Add(tag.GetArtist().at(index).c_str(), "Performer");
+    }
+    object.m_People.artists.Add(StringUtils::Join(!tag.GetAlbumArtist().empty() ? tag.GetAlbumArtist() : tag.GetArtist(), g_advancedSettings.m_musicItemSeparator).c_str(), "AlbumArtist");
+    if(tag.GetAlbumArtist().empty())
+        object.m_Creator = StringUtils::Join(tag.GetArtist(), g_advancedSettings.m_musicItemSeparator);
+    else
+        object.m_Creator = StringUtils::Join(tag.GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator);
+    object.m_MiscInfo.original_track_number = tag.GetTrackNumber();
+    if(tag.GetDatabaseId() >= 0) {
+      object.m_ReferenceID = NPT_String::Format("musicdb://4/%i%s", tag.GetDatabaseId(), URIUtils::GetExtension(tag.GetURL()).c_str());
+    }
+    if (object.m_ReferenceID == object.m_ObjectID)
+        object.m_ReferenceID = "";
+
+    if (resource) resource->m_Duration = tag.GetDuration();
+
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::PopulateObjectFromTag
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPServer::PopulateObjectFromTag(CVideoInfoTag&         tag,
+                                   PLT_MediaObject&       object,
+                                   NPT_String*            file_path, /* = NULL */
+                                   PLT_MediaItemResource* resource,  /* = NULL */
+                                   EClientQuirks          quirks)
+{
+    // some usefull buffers
+    CStdStringArray strings;
+
+    if (!tag.m_strFileNameAndPath.IsEmpty() && file_path)
+      *file_path = tag.m_strFileNameAndPath;
+
+    if (tag.m_iDbId != -1 ) {
+        if (!tag.m_artist.empty()) {
+          object.m_ObjectClass.type = "object.item.videoItem.musicVideoClip";
+          object.m_Creator = StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator);
+          object.m_Title = tag.m_strTitle;
+          object.m_ReferenceID = NPT_String::Format("videodb://3/2/%i", tag.m_iDbId);
+        } else if (!tag.m_strShowTitle.IsEmpty()) {
+          object.m_ObjectClass.type = "object.item.videoItem.videoBroadcast";
+          object.m_Recorded.program_title  = "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
+          object.m_Recorded.program_title += "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
+          object.m_Recorded.program_title += " : " + tag.m_strTitle;
+          object.m_Recorded.series_title = tag.m_strShowTitle;
+          object.m_Recorded.episode_number = tag.m_iSeason * 100 + tag.m_iEpisode;
+          object.m_Title = object.m_Recorded.series_title + " - " + object.m_Recorded.program_title;
+          object.m_Date = tag.m_firstAired.GetAsLocalizedDate();
+          if(tag.m_iSeason != -1)
+              object.m_ReferenceID = NPT_String::Format("videodb://2/0/%i", tag.m_iDbId);
+        } else {
+          object.m_ObjectClass.type = "object.item.videoItem.movie";
+          object.m_Title = tag.m_strTitle;
+          object.m_Date = NPT_String::FromInteger(tag.m_iYear) + "-01-01";
+          object.m_ReferenceID = NPT_String::Format("videodb://1/2/%i", tag.m_iDbId);
+        }
+    }
+
+    if(quirks & ECLIENTQUIRKS_BASICVIDEOCLASS)
+        object.m_ObjectClass.type = "object.item.videoItem";
+
+    if(object.m_ReferenceID == object.m_ObjectID)
+        object.m_ReferenceID = "";
+
+    for (unsigned int index = 0; index < tag.m_genre.size(); index++)
+      object.m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
+
+    for(CVideoInfoTag::iCast it = tag.m_cast.begin();it != tag.m_cast.end();it++) {
+        object.m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
+    }
+
+    object.m_People.director = StringUtils::Join(tag.m_director, g_advancedSettings.m_videoItemSeparator);
+    for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
+      object.m_People.authors.Add(tag.m_writingCredits[index].c_str());
+
+    object.m_Description.description = tag.m_strTagLine;
+    object.m_Description.long_description = tag.m_strPlot;
+    if (resource) resource->m_Duration = tag.m_streamDetails.GetVideoDuration();
+    if (resource) resource->m_Resolution = NPT_String::FromInteger(tag.m_streamDetails.GetVideoWidth()) + "x" + NPT_String::FromInteger(tag.m_streamDetails.GetVideoHeight());
+
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::CorrectAllItemsSortHack
++---------------------------------------------------------------------*/
+const CStdString&
+CUPnPServer::CorrectAllItemsSortHack(const CStdString &item)
+{
+    // This is required as in order for the "* All Albums" etc. items to sort
+    // correctly, they must have fake artist/album etc. information generated.
+    // This looks nasty if we attempt to render it to the GUI, thus this (further)
+    // workaround
+    if ((item.size() == 1 && item[0] == 0x01) || (item.size() > 1 && ((unsigned char) item[1]) == 0xff))
+        return StringUtils::EmptyString;
+
+    return item;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::BuildObject
++---------------------------------------------------------------------*/
+PLT_MediaObject*
+CUPnPServer::BuildObject(const CFileItem&              item,
+                         NPT_String&                   file_path,
+                         bool                          with_count,
+                         const PLT_HttpRequestContext* context /* = NULL */,
+                         CUPnPServer*                  upnp_server /* = NULL */)
+{
+    PLT_MediaItemResource resource;
+    PLT_MediaObject*      object = NULL;
+
+    CLog::Log(LOGDEBUG, "Building didl for object '%s'", (const char*)item.GetPath());
+
+    EClientQuirks quirks = GetClientQuirks(context);
+
+    // get list of ip addresses
+    NPT_List<NPT_IpAddress> ips;
+    NPT_HttpUrl rooturi;
+    NPT_CHECK_LABEL(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure);
+
+    // if we're passed an interface where we received the request from
+    // move the ip to the top
+    if (context && context->GetLocalAddress().GetIpAddress().ToString() != "0.0.0.0") {
+        rooturi = NPT_HttpUrl(context->GetLocalAddress().GetIpAddress().ToString(), context->GetLocalAddress().GetPort(), "/");
+        ips.Remove(context->GetLocalAddress().GetIpAddress());
+        ips.Insert(ips.GetFirstItem(), context->GetLocalAddress().GetIpAddress());
+    }
+
+    if (!item.m_bIsFolder) {
+        object = new PLT_MediaItem();
+        object->m_ObjectID = item.GetPath();
+
+        /* Setup object type */
+        if (item.IsMusicDb() || item.IsAudio()) {
+            object->m_ObjectClass.type = "object.item.audioItem.musicTrack";
+
+            if (item.HasMusicInfoTag()) {
+                CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
+                PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks);
+            }
+        } else if (item.IsVideoDb() || item.IsVideo()) {
+            object->m_ObjectClass.type = "object.item.videoItem";
+
+            if(quirks & ECLIENTQUIRKS_UNKNOWNSERIES)
+                object->m_Affiliation.album = "[Unknown Series]";
+
+            if (item.HasVideoInfoTag()) {
+                CVideoInfoTag *tag = (CVideoInfoTag*)item.GetVideoInfoTag();
+                PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks);
+            }
+        } else if (item.IsPicture()) {
+            object->m_ObjectClass.type = "object.item.imageItem.photo";
+        } else {
+            object->m_ObjectClass.type = "object.item";
+        }
+
+        // duration of zero is invalid
+        if (resource.m_Duration == 0) resource.m_Duration = -1;
+
+        // Set the resource file size
+        resource.m_Size = item.m_dwSize;
+        if (resource.m_Size == 0) {
+            struct __stat64 info;
+            if(CFile::Stat((const char*)file_path, &info) == 0 && info.st_size >= 0)
+              resource.m_Size = info.st_size;
+        }
+        if(resource.m_Size == 0)
+          resource.m_Size = (NPT_LargeSize)-1;
+
+        // set date
+        if (object->m_Date.IsEmpty() && item.m_dateTime.IsValid()) {
+            object->m_Date = item.m_dateTime.GetAsLocalizedDate();
+        }
+
+        if (upnp_server) {
+            upnp_server->AddSafeResourceUri(object, rooturi, ips, file_path, GetProtocolInfo(item, "http", context));
+        }
+
+        // if the item is remote, add a direct link to the item
+        if (URIUtils::IsRemote((const char*)file_path)) {
+            resource.m_ProtocolInfo = PLT_ProtocolInfo(CUPnPServer::GetProtocolInfo(item, item.GetAsUrl().GetProtocol(), context));
+            resource.m_Uri = file_path;
+
+            // if the direct link can be served directly using http, then push it in front
+            // otherwise keep the xbmc-get resource last and let a compatible client look for it
+            if (resource.m_ProtocolInfo.ToString().StartsWith("xbmc", true)) {
+                object->m_Resources.Add(resource);
+            } else {
+                object->m_Resources.Insert(object->m_Resources.GetFirstItem(), resource);
+            }
+        }
+
+        // Some upnp clients expect all audio items to have parent root id 4
+#ifdef WMP_ID_MAPPING
+        object->m_ParentID = "4";
+#endif
+    } else {
+        PLT_MediaContainer* container = new PLT_MediaContainer;
+        object = container;
+
+        /* Assign a title and id for this container */
+        container->m_ObjectID = item.GetPath();
+        container->m_ObjectClass.type = "object.container";
+        container->m_ChildrenCount = -1;
+
+        CStdStringArray strings;
+
+        /* this might be overkill, but hey */
+        if (item.IsMusicDb()) {
+            MUSICDATABASEDIRECTORY::NODE_TYPE node = CMusicDatabaseDirectory::GetDirectoryType(item.GetPath());
+            switch(node) {
+                case MUSICDATABASEDIRECTORY::NODE_TYPE_ARTIST: {
+                      container->m_ObjectClass.type += ".person.musicArtist";
+                      CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
+                      if (tag) {
+                          container->m_People.artists.Add(
+                              CorrectAllItemsSortHack(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "Performer");
+                          container->m_People.artists.Add(
+                              CorrectAllItemsSortHack(StringUtils::Join(!tag->GetAlbumArtist().empty() ? tag->GetAlbumArtist() : tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "AlbumArtist");
+                      }
+#ifdef WMP_ID_MAPPING
+                      // Some upnp clients expect all artists to have parent root id 107
+                      container->m_ParentID = "107";
+#endif
+                  }
+                  break;
+                case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM:
+                case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_COMPILATIONS:
+                case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_RECENTLY_ADDED:
+                case MUSICDATABASEDIRECTORY::NODE_TYPE_YEAR_ALBUM: {
+                      container->m_ObjectClass.type += ".album.musicAlbum";
+                      // for Sonos to be happy
+                      CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
+                      if (tag) {
+                          container->m_People.artists.Add(
+                              CorrectAllItemsSortHack(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "Performer");
+                          container->m_People.artists.Add(
+                              CorrectAllItemsSortHack(StringUtils::Join(!tag->GetAlbumArtist().empty() ? tag->GetAlbumArtist() : tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "AlbumArtist");
+                          container->m_Affiliation.album = CorrectAllItemsSortHack(tag->GetAlbum()).c_str();
+                      }
+#ifdef WMP_ID_MAPPING
+                      // Some upnp clients expect all albums to have parent root id 7
+                      container->m_ParentID = "7";
+#endif
+                  }
+                  break;
+                case MUSICDATABASEDIRECTORY::NODE_TYPE_GENRE:
+                  container->m_ObjectClass.type += ".genre.musicGenre";
+                  break;
+                default:
+                  break;
+            }
+        } else if (item.IsVideoDb()) {
+            VIDEODATABASEDIRECTORY::NODE_TYPE node = CVideoDatabaseDirectory::GetDirectoryType(item.GetPath());
+            CVideoInfoTag &tag = *(CVideoInfoTag*)item.GetVideoInfoTag();
+            switch(node) {
+                case VIDEODATABASEDIRECTORY::NODE_TYPE_GENRE:
+                  container->m_ObjectClass.type += ".genre.movieGenre";
+                  break;
+                case VIDEODATABASEDIRECTORY::NODE_TYPE_ACTOR:
+                  container->m_ObjectClass.type += ".person.videoArtist";
+                  container->m_Creator = StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator);
+                  container->m_Title   = tag.m_strTitle;
+                  break;
+                case VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_TVSHOWS:
+                  container->m_ObjectClass.type += ".album.videoAlbum";
+                  container->m_Recorded.program_title  = "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
+                  container->m_Recorded.program_title += "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
+                  container->m_Recorded.program_title += " : " + tag.m_strTitle;
+                  container->m_Recorded.series_title = tag.m_strShowTitle;
+                  container->m_Recorded.episode_number = tag.m_iSeason * 100 + tag.m_iEpisode;
+                  container->m_Title = container->m_Recorded.series_title + " - " + container->m_Recorded.program_title;
+                  container->m_Title = tag.m_strTitle;
+                  if(!tag.m_firstAired.IsValid() && tag.m_iYear)
+                    container->m_Date = NPT_String::FromInteger(tag.m_iYear) + "-01-01";
+                  else
+                    container->m_Date = tag.m_firstAired.GetAsLocalizedDate();
+
+                  for (unsigned int index = 0; index < tag.m_genre.size(); index++)
+                    container->m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
+
+                  for(CVideoInfoTag::iCast it = tag.m_cast.begin();it != tag.m_cast.end();it++) {
+                      container->m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
+                  }
+
+                  container->m_People.director = StringUtils::Join(tag.m_director, g_advancedSettings.m_videoItemSeparator);;
+                  for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
+                    container->m_People.authors.Add(tag.m_writingCredits[index].c_str());
+
+                  container->m_Description.description = tag.m_strTagLine;
+                  container->m_Description.long_description = tag.m_strPlot;
+
+                  break;
+                default:
+                  container->m_ObjectClass.type += ".storageFolder";
+                  break;
+            }
+        } else if (item.IsPlayList()) {
+            container->m_ObjectClass.type += ".playlistContainer";
+        }
+
+        if(quirks & ECLIENTQUIRKS_ONLYSTORAGEFOLDER) {
+          container->m_ObjectClass.type = "object.container.storageFolder";
+        }
+
+        /* Get the number of children for this container */
+        if (with_count && upnp_server) {
+            if (object->m_ObjectID.StartsWith("virtualpath://")) {
+                NPT_LargeSize count = 0;
+                NPT_CHECK_LABEL(NPT_File::GetSize(file_path, count), failure);
+                container->m_ChildrenCount = count;
+            } else {
+                /* this should be a standard path */
+                // TODO - get file count of this directory
+            }
+        }
+    }
+
+    // set a title for the object
+    if (object->m_Title.IsEmpty()) {
+        if (!item.GetLabel().IsEmpty()) {
+            CStdString title = item.GetLabel();
+            if (item.IsPlayList() || !item.m_bIsFolder) URIUtils::RemoveExtension(title);
+            object->m_Title = title;
+        } else {
+            CStdString title, volumeNumber;
+            CUtil::GetVolumeFromFileName(item.GetPath(), title, volumeNumber);
+            if (!item.m_bIsFolder) URIUtils::RemoveExtension(title);
+            object->m_Title = title;
+        }
+    }
+    // set a thumbnail if we have one
+    if (item.HasThumbnail() && upnp_server) {
+        PLT_AlbumArtInfo art;
+        art.uri = upnp_server->BuildSafeResourceUri(
+            rooturi,
+            (*ips.GetFirstItem()).ToString(),
+            item.GetThumbnailImage());
+        // Set DLNA profileID by extension, defaulting to JPEG.
+        NPT_String ext = URIUtils::GetExtension(item.GetThumbnailImage()).c_str();
+        if (strcmp(ext, ".png") == 0) {
+            art.dlna_profile = "PNG_TN";
+        } else {
+            art.dlna_profile = "JPEG_TN";
+        }
+        object->m_ExtraInfo.album_arts.Add(art);
+    }
+
+    if (upnp_server) {
+        if (item.HasProperty("fanart_image")) {
+            upnp_server->AddSafeResourceUri(object, rooturi, ips, item.GetProperty("fanart_image").asString().c_str(), "xbmc.org:*:fanart:*" );
+        }
+    }
+
+    return object;
+
+failure:
+    delete object;
+    return NULL;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::Build
++---------------------------------------------------------------------*/
+PLT_MediaObject*
+CUPnPServer::Build(CFileItemPtr                  item,
+                   bool                          with_count,
+                   const PLT_HttpRequestContext& context,
+                   const char*                   parent_id /* = NULL */)
+{
+    PLT_MediaObject* object = NULL;
+    NPT_String       path = item->GetPath().c_str();
+
+    //HACK: temporary disabling count as it thrashes HDD
+    with_count = false;
+
+    CLog::Log(LOGDEBUG, "Preparing upnp object for item '%s'", (const char*)path);
+
+    if (path == "virtualpath://upnproot") {
+        path.TrimRight("/");
+        if (path.StartsWith("virtualpath://")) {
+            object = new PLT_MediaContainer;
+            object->m_Title = item->GetLabel();
+            object->m_ObjectClass.type = "object.container";
+            object->m_ObjectID = path;
+
+            // root
+            object->m_ObjectID = "0";
+            object->m_ParentID = "-1";
+            // root has 5 children
+            if (with_count) {
+                ((PLT_MediaContainer*)object)->m_ChildrenCount = 5;
+            }
+        } else {
+            goto failure;
+        }
+
+    } else {
+        // db path handling
+        NPT_String file_path, share_name;
+        file_path = item->GetPath();
+        share_name = "";
+
+        if (path.StartsWith("musicdb://")) {
+            if (path == "musicdb://" ) {
+                item->SetLabel("Music Library");
+                item->SetLabelPreformated(true);
+            } else {
+                if (!item->HasMusicInfoTag() || !item->GetMusicInfoTag()->Loaded() )
+                    item->LoadMusicTag();
+
+                if (!item->HasThumbnail() )
+                    item->SetThumbnailImage(CThumbLoader::GetCachedImage(*item, "thumb"));
+
+                if (item->GetLabel().IsEmpty()) {
+                    /* if no label try to grab it from node type */
+                    CStdString label;
+                    if (CMusicDatabaseDirectory::GetLabel((const char*)path, label)) {
+                        item->SetLabel(label);
+                        item->SetLabelPreformated(true);
+                    }
+                }
+            }
+        } else if (file_path.StartsWith("videodb://")) {
+            if (path == "videodb://" ) {
+                item->SetLabel("Video Library");
+                item->SetLabelPreformated(true);
+            } else {
+                if (!item->HasVideoInfoTag()) {
+                    XFILE::VIDEODATABASEDIRECTORY::CQueryParams params;
+                    XFILE::VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo((const char*)path, params);
+
+                    CVideoDatabase db;
+                    if (!db.Open() ) return NULL;
+
+                    if (params.GetMovieId() >= 0 )
+                        db.GetMovieInfo((const char*)path, *item->GetVideoInfoTag(), params.GetMovieId());
+                    else if (params.GetEpisodeId() >= 0 )
+                        db.GetEpisodeInfo((const char*)path, *item->GetVideoInfoTag(), params.GetEpisodeId());
+                    else if (params.GetTvShowId() >= 0 )
+                        db.GetTvShowInfo((const char*)path, *item->GetVideoInfoTag(), params.GetTvShowId());
+                }
+
+                // try to grab title from tag
+                if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strTitle.IsEmpty()) {
+                    item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
+                    item->SetLabelPreformated(true);
+                }
+
+                // try to grab it from the folder
+                if (item->GetLabel().IsEmpty()) {
+                    CStdString label;
+                    if (CVideoDatabaseDirectory::GetLabel((const char*)path, label)) {
+                        item->SetLabel(label);
+                        item->SetLabelPreformated(true);
+                    }
+                }
+
+                if (!item->HasThumbnail() )
+                    item->SetThumbnailImage(CThumbLoader::GetCachedImage(*item, "thumb"));
+            }
+        }
+
+        // not a virtual path directory, new system
+        object = BuildObject(*item.get(), file_path, with_count, &context, this);
+
+        // set parent id if passed, otherwise it should have been determined
+        if (object && parent_id) {
+            object->m_ParentID = parent_id;
+        }
+    }
+
+    if (object) {
+        // remap Root virtualpath://upnproot/ to id "0"
+        if (object->m_ObjectID == "virtualpath://upnproot/")
+            object->m_ObjectID = "0";
+
+        // remap Parent Root virtualpath://upnproot/ to id "0"
+        if (object->m_ParentID == "virtualpath://upnproot/")
+            object->m_ParentID = "0";
+    }
+    return object;
+
+failure:
+    delete object;
+    return NULL;
+}
+
+/*----------------------------------------------------------------------
+|   TranslateWMPObjectId
++---------------------------------------------------------------------*/
+static NPT_String TranslateWMPObjectId(NPT_String id)
+{
+    if (id == "0") {
+        id = "virtualpath://upnproot/";
+    } else if (id == "15") {
+        // Xbox 360 asking for videos
+        id = "videodb://";
+    } else if (id == "16") {
+        // Xbox 360 asking for photos
+    } else if (id == "107") {
+        // Sonos uses 107 for artists root container id
+        id = "musicdb://2/";
+    } else if (id == "7") {
+        // Sonos uses 7 for albums root container id
+        id = "musicdb://3/";
+    } else if (id == "4") {
+        // Sonos uses 4 for tracks root container id
+        id = "musicdb://4/";
+    }
+
+    CLog::Log(LOGDEBUG, "UPnP Translated id to '%s'", (const char*)id);
+    return id;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::OnBrowseMetadata
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPServer::OnBrowseMetadata(PLT_ActionReference&          action,
+                              const char*                   object_id,
+                              const char*                   filter,
+                              NPT_UInt32                    starting_index,
+                              NPT_UInt32                    requested_count,
+                              const char*                   sort_criteria,
+                              const PLT_HttpRequestContext& context)
+{
+    NPT_COMPILER_UNUSED(sort_criteria);
+    NPT_COMPILER_UNUSED(requested_count);
+    NPT_COMPILER_UNUSED(starting_index);
+
+    NPT_String                     didl;
+    NPT_Reference<PLT_MediaObject> object;
+    NPT_String                     id = TranslateWMPObjectId(object_id);
+    vector<CStdString>             paths;
+    CFileItemPtr                   item;
+
+    CLog::Log(LOGINFO, "Received UPnP Browse Metadata request for object '%s'", (const char*)object_id);
+
+    if (id.StartsWith("virtualpath://")) {
+        id.TrimRight("/");
+        if (id == "virtualpath://upnproot") {
+            id += "/";
+            item.reset(new CFileItem((const char*)id, true));
+            item->SetLabel("Root");
+            item->SetLabelPreformated(true);
+            object = Build(item, true, context);
+        } else {
+            return NPT_FAILURE;
+        }
+    } else {
+        // determine if it's a container by calling CDirectory::Exists
+        item.reset(new CFileItem((const char*)id, CDirectory::Exists((const char*)id)));
+
+        // determine parent id for shared paths only
+        // otherwise let db find out
+        CStdString parent;
+        if (!URIUtils::GetParentPath((const char*)id, parent)) parent = "0";
+
+//#ifdef WMP_ID_MAPPING
+//        if (!id.StartsWith("musicdb://") && !id.StartsWith("videodb://")) {
+//            parent = "";
+//        }
+//#endif
+
+        object = Build(item, true, context, parent.empty()?NULL:parent.c_str());
+    }
+
+    if (object.IsNull()) {
+        /* error */
+        NPT_LOG_WARNING_1("CUPnPServer::OnBrowseMetadata - Object null (%s)", object_id);
+        action->SetError(701, "No Such Object.");
+        return NPT_FAILURE;
+    }
+
+    NPT_String tmp;
+    NPT_CHECK(PLT_Didl::ToDidl(*object.AsPointer(), filter, tmp));
+
+    /* add didl header and footer */
+    didl = didl_header + tmp + didl_footer;
+
+    NPT_CHECK(action->SetArgumentValue("Result", didl));
+    NPT_CHECK(action->SetArgumentValue("NumberReturned", "1"));
+    NPT_CHECK(action->SetArgumentValue("TotalMatches", "1"));
+
+    // update ID may be wrong here, it should be the one of the container?
+    NPT_CHECK(action->SetArgumentValue("UpdateId", "0"));
+
+    // TODO: We need to keep track of the overall SystemUpdateID of the CDS
+
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::OnBrowseDirectChildren
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPServer::OnBrowseDirectChildren(PLT_ActionReference&          action,
+                                    const char*                   object_id,
+                                    const char*                   filter,
+                                    NPT_UInt32                    starting_index,
+                                    NPT_UInt32                    requested_count,
+                                    const char*                   sort_criteria,
+                                    const PLT_HttpRequestContext& context)
+{
+    CFileItemList items;
+    NPT_String    parent_id = TranslateWMPObjectId(object_id);
+
+    CLog::Log(LOGINFO, "UPnP: Received Browse DirectChildren request for object '%s', with sort criteria %s", object_id, sort_criteria);
+
+    items.SetPath(CStdString(parent_id));
+    if (!items.Load()) {
+        // cache anything that takes more than a second to retrieve
+      unsigned int time = XbmcThreads::SystemClockMillis();
+
+        if (parent_id.StartsWith("virtualpath://upnproot")) {
+            CFileItemPtr item;
+
+            // music library
+            item.reset(new CFileItem("musicdb://", true));
+            item->SetLabel("Music Library");
+            item->SetLabelPreformated(true);
+            items.Add(item);
+
+            // video library
+            item.reset(new CFileItem("videodb://", true));
+            item->SetLabel("Video Library");
+            item->SetLabelPreformated(true);
+            items.Add(item);
+
+        } else {
+            CDirectory::GetDirectory((const char*)parent_id, items);
+        }
+
+        if (items.CacheToDiscAlways() || (items.CacheToDiscIfSlow() && (XbmcThreads::SystemClockMillis() - time) > 1000 )) {
+            items.Save();
+        }
+    }
+
+    // Always sort by label
+    items.Sort(SORT_METHOD_LABEL, SortOrderAscending);
+
+    // Don't pass parent_id if action is Search not BrowseDirectChildren, as
+    // we want the engine to determine the best parent id, not necessarily the one
+    // passed
+    NPT_String action_name = action->GetActionDesc().GetName();
+    return BuildResponse(
+        action,
+        items,
+        filter,
+        starting_index,
+        requested_count,
+        sort_criteria,
+        context,
+        (action_name.Compare("Search", true)==0)?NULL:parent_id.GetChars());
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::BuildResponse
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPServer::BuildResponse(PLT_ActionReference&          action,
+                           CFileItemList&                items,
+                           const char*                   filter,
+                           NPT_UInt32                    starting_index,
+                           NPT_UInt32                    requested_count,
+                           const char*                   sort_criteria,
+                           const PLT_HttpRequestContext& context,
+                           const char*                   parent_id /* = NULL */)
+{
+    NPT_COMPILER_UNUSED(sort_criteria);
+
+    CLog::Log(LOGDEBUG, "Building UPnP response with filter '%s', starting @ %d with %d requested",
+        (const char*)filter,
+        starting_index,
+        requested_count);
+
+    // won't return more than UPNP_MAX_RETURNED_ITEMS items at a time to keep things smooth
+    // 0 requested means as many as possible
+    NPT_UInt32 max_count  = (requested_count == 0)?m_MaxReturnedItems:min((unsigned long)requested_count, (unsigned long)m_MaxReturnedItems);
+    NPT_UInt32 stop_index = min((unsigned long)(starting_index + max_count), (unsigned long)items.Size()); // don't return more than we can
+
+    NPT_Cardinal count = 0;
+    NPT_String didl = didl_header;
+    PLT_MediaObjectReference object;
+    for (unsigned long i=starting_index; i<stop_index; ++i) {
+        object = Build(items[i], true, context, parent_id);
+        if (object.IsNull()) {
+            continue;
+        }
+
+        NPT_String tmp;
+        NPT_CHECK(PLT_Didl::ToDidl(*object.AsPointer(), filter, tmp));
+
+        // Neptunes string growing is dead slow for small additions
+        if (didl.GetCapacity() < tmp.GetLength() + didl.GetLength()) {
+            didl.Reserve((tmp.GetLength() + didl.GetLength())*2);
+        }
+        didl += tmp;
+        ++count;
+    }
+
+    didl += didl_footer;
+
+    CLog::Log(LOGDEBUG, "Returning UPnP response with %d items out of %d total matches",
+        count,
+        items.Size());
+
+    NPT_CHECK(action->SetArgumentValue("Result", didl));
+    NPT_CHECK(action->SetArgumentValue("NumberReturned", NPT_String::FromInteger(count)));
+    NPT_CHECK(action->SetArgumentValue("TotalMatches", NPT_String::FromInteger(items.Size())));
+    NPT_CHECK(action->SetArgumentValue("UpdateId", "0"));
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   FindSubCriteria
++---------------------------------------------------------------------*/
+static
+NPT_String
+FindSubCriteria(NPT_String criteria, const char* name)
+{
+    NPT_String result;
+    int search = criteria.Find(name);
+    if (search >= 0) {
+        criteria = criteria.Right(criteria.GetLength() - search - NPT_StringLength(name));
+        criteria.TrimLeft(" ");
+        if (criteria.GetLength()>0 && criteria[0] == '=') {
+            criteria.TrimLeft("= ");
+            if (criteria.GetLength()>0 && criteria[0] == '\"') {
+                search = criteria.Find("\"", 1);
+                if (search > 0) result = criteria.SubString(1, search-1);
+            }
+        }
+    }
+    return result;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::OnSearchContainer
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPServer::OnSearchContainer(PLT_ActionReference&          action,
+                               const char*                   object_id,
+                               const char*                   search_criteria,
+                               const char*                   filter,
+                               NPT_UInt32                    starting_index,
+                               NPT_UInt32                    requested_count,
+                               const char*                   sort_criteria,
+                               const PLT_HttpRequestContext& context)
+{
+    CLog::Log(LOGDEBUG, "Received Search request for object '%s' with search '%s'",
+        (const char*)object_id,
+        (const char*)search_criteria);
+
+    NPT_String id = object_id;
+    if (id.StartsWith("musicdb://")) {
+        // we browse for all tracks given a genre, artist or album
+        if (NPT_String(search_criteria).Find("object.item.audioItem") >= 0) {
+            if (!id.EndsWith("/")) id += "/";
+            NPT_Cardinal count = id.SubString(10).Split("/").GetItemCount();
+            // remove extra empty node count
+            count = count?count-1:0;
+
+            // genre
+            if (id.StartsWith("musicdb://1/")) {
+                // all tracks of all genres
+                if (count == 1)
+                    id += "-1/-1/-1/";
+                // all tracks of a specific genre
+                else if (count == 2)
+                    id += "-1/-1/";
+                // all tracks of a specific genre of a specfic artist
+                else if (count == 3)
+                    id += "-1/";
+            } else if (id.StartsWith("musicdb://2/")) {
+                // all tracks by all artists
+                if (count == 1)
+                    id += "-1/-1/";
+                // all tracks of a specific artist
+                else if (count == 2)
+                    id += "-1/";
+            } else if (id.StartsWith("musicdb://3/")) {
+                // all albums ?
+                if (count == 1) id += "-1/";
+            }
+        }
+        return OnBrowseDirectChildren(action, id, filter, starting_index, requested_count, sort_criteria, context);
+    } else if (NPT_String(search_criteria).Find("object.item.audioItem") >= 0) {
+        // look for artist, album & genre filters
+        NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
+        NPT_String album = FindSubCriteria(search_criteria, "upnp:album");
+        NPT_String artist = FindSubCriteria(search_criteria, "upnp:artist");
+        // sonos looks for microsoft specific stuff
+        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistPerformer");
+        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistAlbumArtist");
+        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:authorComposer");
+
+        CMusicDatabase database;
+        database.Open();
+
+        if (genre.GetLength() > 0) {
+            // all tracks by genre filtered by artist and/or album
+            CStdString strPath;
+            strPath.Format("musicdb://1/%ld/%ld/%ld/",
+                database.GetGenreByName((const char*)genre),
+                database.GetArtistByName((const char*)artist), // will return -1 if no artist
+                database.GetAlbumByName((const char*)album));  // will return -1 if no album
+
+            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
+        } else if (artist.GetLength() > 0) {
+            // all tracks by artist name filtered by album if passed
+            CStdString strPath;
+            strPath.Format("musicdb://2/%ld/%ld/",
+                database.GetArtistByName((const char*)artist),
+                database.GetAlbumByName((const char*)album)); // will return -1 if no album
+
+            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
+        } else if (album.GetLength() > 0) {
+            // all tracks by album name
+            CStdString strPath;
+            strPath.Format("musicdb://3/%ld/",
+                database.GetAlbumByName((const char*)album));
+
+            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
+        }
+
+        // browse all songs
+        return OnBrowseDirectChildren(action, "musicdb://4/", filter, starting_index, requested_count, sort_criteria, context);
+    } else if (NPT_String(search_criteria).Find("object.container.album.musicAlbum") >= 0) {
+        // sonos filters by genre
+        NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
+
+        // 360 hack: artist/albums using search
+        NPT_String artist = FindSubCriteria(search_criteria, "upnp:artist");
+        // sonos looks for microsoft specific stuff
+        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistPerformer");
+        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistAlbumArtist");
+        artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:authorComposer");
+
+        CMusicDatabase database;
+        database.Open();
+
+        if (genre.GetLength() > 0) {
+            CStdString strPath;
+            strPath.Format("musicdb://1/%ld/%ld/",
+                database.GetGenreByName((const char*)genre),
+                database.GetArtistByName((const char*)artist)); // no artist should return -1
+            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
+        } else if (artist.GetLength() > 0) {
+            CStdString strPath;
+            strPath.Format("musicdb://2/%ld/",
+                database.GetArtistByName((const char*)artist));
+            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
+        }
+
+        // all albums
+        return OnBrowseDirectChildren(action, "musicdb://3/", filter, starting_index, requested_count, sort_criteria, context);
+    } else if (NPT_String(search_criteria).Find("object.container.person.musicArtist") >= 0) {
+        // Sonos filters by genre
+        NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
+        if (genre.GetLength() > 0) {
+            CMusicDatabase database;
+            database.Open();
+            CStdString strPath;
+            strPath.Format("musicdb://1/%ld/", database.GetGenreByName((const char*)genre));
+            return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
+        }
+        return OnBrowseDirectChildren(action, "musicdb://2/", filter, starting_index, requested_count, sort_criteria, context);
+    }  else if (NPT_String(search_criteria).Find("object.container.genre.musicGenre") >= 0) {
+        return OnBrowseDirectChildren(action, "musicdb://1/", filter, starting_index, requested_count, sort_criteria, context);
+    } else if (NPT_String(search_criteria).Find("object.container.playlistContainer") >= 0) {
+        return OnBrowseDirectChildren(action, "special://musicplaylists/", filter, starting_index, requested_count, sort_criteria, context);
+    } else if (NPT_String(search_criteria).Find("object.item.videoItem") >= 0) {
+      CFileItemList items, itemsall;
+
+      CVideoDatabase database;
+      if (!database.Open()) {
+        action->SetError(800, "Internal Error");
+        return NPT_SUCCESS;
+      }
+
+      if (!database.GetMoviesNav("videodb://1/2/", items)) {
+        action->SetError(800, "Internal Error");
+        return NPT_SUCCESS;
+      }
+      itemsall.Append(items);
+      items.Clear();
+
+      // TODO - set proper base url for this
+      if (!database.GetEpisodesByWhere("videodb://2/0/", "", items, false)) {
+        action->SetError(800, "Internal Error");
+        return NPT_SUCCESS;
+      }
+      itemsall.Append(items);
+      items.Clear();
+
+      return BuildResponse(action, itemsall, filter, starting_index, requested_count, sort_criteria, context, NULL);
+  } else if (NPT_String(search_criteria).Find("object.item.imageItem") >= 0) {
+      CFileItemList items;
+      return BuildResponse(action, items, filter, starting_index, requested_count, sort_criteria, context, NULL);;
+  }
+
+  return NPT_FAILURE;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPServer::ServeFile
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPServer::ServeFile(const NPT_HttpRequest&              request,
+                       const NPT_HttpRequestContext& context,
+                       NPT_HttpResponse&             response,
+                       const NPT_String&             md5)
+{
+    // Translate hash to filename
+    NPT_String file_path(md5), *file_path2;
+    { NPT_AutoLock lock(m_FileMutex);
+      if(NPT_SUCCEEDED(m_FileMap.Get(md5, file_path2))) {
+        file_path = *file_path2;
+        CLog::Log(LOGDEBUG, "Received request to serve '%s' = '%s'", (const char*)md5, (const char*)file_path);
+      } else {
+        CLog::Log(LOGDEBUG, "Received request to serve unknown md5 '%s'", (const char*)md5);
+        response.SetStatus(404, "File Not Found");
+        return NPT_SUCCESS;
+      }
+    }
+
+    // File requested
+    NPT_HttpUrl rooturi(context.GetLocalAddress().GetIpAddress().ToString(), context.GetLocalAddress().GetPort(), "/");
+
+    if (file_path.Left(8).Compare("stack://", true) == 0) {
+
+        NPT_List<NPT_String> files = file_path.SubString(8).Split(" , ");
+        if (files.GetItemCount() == 0) {
+            response.SetStatus(404, "File Not Found");
+            return NPT_SUCCESS;
+        }
+
+        NPT_String output;
+        output.Reserve(file_path.GetLength()*2);
+        output += "#EXTM3U\r\n";
+
+        NPT_List<NPT_String>::Iterator url = files.GetFirstItem();
+        for (;url;url++) {
+            output += "#EXTINF:-1," + URIUtils::GetFileName((const char*)*url);
+            output += "\r\n";
+            output += BuildSafeResourceUri(
+                          rooturi,
+                          context.GetLocalAddress().GetIpAddress().ToString(),
+                          *url);
+            output += "\r\n";
+        }
+
+        PLT_HttpHelper::SetBody(response, (const char*)output, output.GetLength());
+        response.GetHeaders().SetHeader("Content-Disposition", "inline; filename=\"stack.m3u\"");
+        return NPT_SUCCESS;
+    }
+
+    if(URIUtils::IsURL((const char*)file_path))
+    {
+      CStdString disp = "inline; filename=\"" + URIUtils::GetFileName((const char*)file_path) + "\"";
+      response.GetHeaders().SetHeader("Content-Disposition", disp.c_str());
+    }
+
+    return PLT_HttpServer::ServeFile(request,
+                                       context,
+                                       response,
+                                       file_path);
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer
++---------------------------------------------------------------------*/
+class CUPnPRenderer : public PLT_MediaRenderer
+{
+public:
+    CUPnPRenderer(const char*  friendly_name,
+                  bool         show_ip = false,
+                  const char*  uuid = NULL,
+                  unsigned int port = 0);
+
+    void UpdateState();
+
+    // Http server handler
+    virtual NPT_Result ProcessHttpRequest(NPT_HttpRequest&              request,
+                                          const NPT_HttpRequestContext& context,
+                                          NPT_HttpResponse&             response);
+
+    // AVTransport methods
+    virtual NPT_Result OnNext(PLT_ActionReference& action);
+    virtual NPT_Result OnPause(PLT_ActionReference& action);
+    virtual NPT_Result OnPlay(PLT_ActionReference& action);
+    virtual NPT_Result OnPrevious(PLT_ActionReference& action);
+    virtual NPT_Result OnStop(PLT_ActionReference& action);
+    virtual NPT_Result OnSeek(PLT_ActionReference& action);
+    virtual NPT_Result OnSetAVTransportURI(PLT_ActionReference& action);
+
+    // RenderingControl methods
+    virtual NPT_Result OnSetVolume(PLT_ActionReference& action);
+    virtual NPT_Result OnSetMute(PLT_ActionReference& action);
+
+private:
+    NPT_Result SetupServices();
+    NPT_Result GetMetadata(NPT_String& meta);
+    NPT_Result PlayMedia(const char* uri,
+                         const char* metadata = NULL,
+                         PLT_Action* action = NULL);
+    NPT_Mutex m_state;
+};
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::CUPnPRenderer
++---------------------------------------------------------------------*/
+CUPnPRenderer::CUPnPRenderer(const char*  friendly_name,
+                             bool         show_ip /* = false */,
+                             const char*  uuid /* = NULL */,
+                             unsigned int port /* = 0 */) :
+    PLT_MediaRenderer(friendly_name,
+                      show_ip,
+                      uuid,
+                      port)
+{
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::SetupServices
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::SetupServices()
+{
+    NPT_CHECK(PLT_MediaRenderer::SetupServices());
+
+    // update what we can play
+    PLT_Service* service = NULL;
+    NPT_CHECK_FATAL(FindServiceByType("urn:schemas-upnp-org:service:ConnectionManager:1", service));
+    service->SetStateVariable("SinkProtocolInfo"
+        ,"http-get:*:*:*"
+        ",xbmc-get:*:*:*"
+        ",http-get:*:audio/mpegurl:*"
+        ",http-get:*:audio/mpeg:*"
+        ",http-get:*:audio/mpeg3:*"
+        ",http-get:*:audio/mp3:*"
+        ",http-get:*:audio/mp4:*"
+        ",http-get:*:audio/basic:*"
+        ",http-get:*:audio/midi:*"
+        ",http-get:*:audio/ulaw:*"
+        ",http-get:*:audio/ogg:*"
+        ",http-get:*:audio/DVI4:*"
+        ",http-get:*:audio/G722:*"
+        ",http-get:*:audio/G723:*"
+        ",http-get:*:audio/G726-16:*"
+        ",http-get:*:audio/G726-24:*"
+        ",http-get:*:audio/G726-32:*"
+        ",http-get:*:audio/G726-40:*"
+        ",http-get:*:audio/G728:*"
+        ",http-get:*:audio/G729:*"
+        ",http-get:*:audio/G729D:*"
+        ",http-get:*:audio/G729E:*"
+        ",http-get:*:audio/GSM:*"
+        ",http-get:*:audio/GSM-EFR:*"
+        ",http-get:*:audio/L8:*"
+        ",http-get:*:audio/L16:*"
+        ",http-get:*:audio/LPC:*"
+        ",http-get:*:audio/MPA:*"
+        ",http-get:*:audio/PCMA:*"
+        ",http-get:*:audio/PCMU:*"
+        ",http-get:*:audio/QCELP:*"
+        ",http-get:*:audio/RED:*"
+        ",http-get:*:audio/VDVI:*"
+        ",http-get:*:audio/ac3:*"
+        ",http-get:*:audio/vorbis:*"
+        ",http-get:*:audio/speex:*"
+        ",http-get:*:audio/x-aiff:*"
+        ",http-get:*:audio/x-pn-realaudio:*"
+        ",http-get:*:audio/x-realaudio:*"
+        ",http-get:*:audio/x-wav:*"
+        ",http-get:*:audio/x-ms-wma:*"
+        ",http-get:*:audio/x-mpegurl:*"
+        ",http-get:*:application/x-shockwave-flash:*"
+        ",http-get:*:application/ogg:*"
+        ",http-get:*:application/sdp:*"
+        ",http-get:*:image/gif:*"
+        ",http-get:*:image/jpeg:*"
+        ",http-get:*:image/ief:*"
+        ",http-get:*:image/png:*"
+        ",http-get:*:image/tiff:*"
+        ",http-get:*:video/avi:*"
+        ",http-get:*:video/mpeg:*"
+        ",http-get:*:video/fli:*"
+        ",http-get:*:video/flv:*"
+        ",http-get:*:video/quicktime:*"
+        ",http-get:*:video/vnd.vivo:*"
+        ",http-get:*:video/vc1:*"
+        ",http-get:*:video/ogg:*"
+        ",http-get:*:video/mp4:*"
+        ",http-get:*:video/BT656:*"
+        ",http-get:*:video/CelB:*"
+        ",http-get:*:video/JPEG:*"
+        ",http-get:*:video/H261:*"
+        ",http-get:*:video/H263:*"
+        ",http-get:*:video/H263-1998:*"
+        ",http-get:*:video/H263-2000:*"
+        ",http-get:*:video/MPV:*"
+        ",http-get:*:video/MP2T:*"
+        ",http-get:*:video/MP1S:*"
+        ",http-get:*:video/MP2P:*"
+        ",http-get:*:video/BMPEG:*"
+        ",http-get:*:video/x-ms-wmv:*"
+        ",http-get:*:video/x-ms-avi:*"
+        ",http-get:*:video/x-flv:*"
+        ",http-get:*:video/x-fli:*"
+        ",http-get:*:video/x-ms-asf:*"
+        ",http-get:*:video/x-ms-asx:*"
+        ",http-get:*:video/x-ms-wmx:*"
+        ",http-get:*:video/x-ms-wvx:*"
+        ",http-get:*:video/x-msvideo:*"
+        );
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::ProcessHttpRequest
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::ProcessHttpRequest(NPT_HttpRequest&              request,
+                                  const NPT_HttpRequestContext& context,
+                                  NPT_HttpResponse&             response)
+{
+    // get the address of who sent us some data back
+    NPT_String  ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
+    NPT_String  method     = request.GetMethod();
+    NPT_String  protocol   = request.GetProtocol();
+    NPT_HttpUrl url        = request.GetUrl();
+
+    if (url.GetPath() == "/thumb.jpg") {
+        NPT_HttpUrlQuery query(url.GetQuery());
+        NPT_String filepath = query.GetField("path");
+        if (!filepath.IsEmpty()) {
+            NPT_HttpEntity* entity = response.GetEntity();
+            if (entity == NULL) return NPT_ERROR_INVALID_STATE;
+
+            // check the method
+            if (request.GetMethod() != NPT_HTTP_METHOD_GET &&
+                request.GetMethod() != NPT_HTTP_METHOD_HEAD) {
+                response.SetStatus(405, "Method Not Allowed");
+                return NPT_SUCCESS;
+            }
+
+            // ensure that the request's path is a valid thumb path
+            if (URIUtils::IsRemote(filepath.GetChars()) ||
+                !filepath.StartsWith(g_settings.GetUserDataFolder())) {
+                response.SetStatus(404, "Not Found");
+                return NPT_SUCCESS;
+            }
+
+            // prevent hackers from accessing files outside of our root
+            if ((filepath.Find("/..") >= 0) || (filepath.Find("\\..") >=0)) {
+                return NPT_FAILURE;
+            }
+
+            // open the file
+            NPT_File file(filepath);
+            NPT_Result result = file.Open(NPT_FILE_OPEN_MODE_READ);
+            if (NPT_FAILED(result)) {
+                response.SetStatus(404, "Not Found");
+                return NPT_SUCCESS;
+            }
+            NPT_InputStreamReference stream;
+            file.GetInputStream(stream);
+            entity->SetContentType(CUPnPServer::GetMimeType(filepath));
+            entity->SetInputStream(stream, true);
+
+            return NPT_SUCCESS;
+        }
+    }
+
+    return PLT_MediaRenderer::ProcessHttpGetRequest(request, context, response);
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::UpdateState
++---------------------------------------------------------------------*/
+void
+CUPnPRenderer::UpdateState()
+{
+    NPT_AutoLock lock(m_state);
+
+    PLT_Service *avt, *rct;
+    if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))
+        return;
+    if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:RenderingControl:1", rct)))
+        return;
+
+    /* don't update state while transitioning */
+    NPT_String state;
+    avt->GetStateVariableValue("TransportState", state);
+    if(state == "TRANSITIONING")
+        return;
+
+    CStdString buffer;
+    int volume;
+    if (g_settings.m_bMute) {
+        rct->SetStateVariable("Mute", "1");
+    } else {
+        rct->SetStateVariable("Mute", "0");
+    }
+    volume = g_application.GetVolume();
+
+    buffer.Format("%d", volume);
+    rct->SetStateVariable("Volume", buffer.c_str());
+
+    buffer.Format("%d", 256 * (volume * 60 - 60) / 100);
+    rct->SetStateVariable("VolumeDb", buffer.c_str());
+
+    if (g_application.IsPlaying() || g_application.IsPaused()) {
+        if (g_application.IsPaused()) {
+            avt->SetStateVariable("TransportState", "PAUSED_PLAYBACK");
+        } else {
+            avt->SetStateVariable("TransportState", "PLAYING");
+        }
+
+        avt->SetStateVariable("TransportStatus", "OK");
+        avt->SetStateVariable("TransportPlaySpeed", (const char*)NPT_String::FromInteger(g_application.GetPlaySpeed()));
+        avt->SetStateVariable("NumberOfTracks", "1");
+        avt->SetStateVariable("CurrentTrack", "1");
+
+        buffer = g_infoManager.GetCurrentPlayTime(TIME_FORMAT_HH_MM_SS);
+        avt->SetStateVariable("RelativeTimePosition", buffer.c_str());
+        avt->SetStateVariable("AbsoluteTimePosition", buffer.c_str());
+
+        buffer = g_infoManager.GetDuration(TIME_FORMAT_HH_MM_SS);
+        if (buffer.length() > 0) {
+          avt->SetStateVariable("CurrentTrackDuration", buffer.c_str());
+          avt->SetStateVariable("CurrentMediaDuration", buffer.c_str());
+        } else {
+          avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
+          avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
+        }
+
+        avt->SetStateVariable("AVTransportURI", g_application.CurrentFile().c_str());
+        avt->SetStateVariable("CurrentTrackURI", g_application.CurrentFile().c_str());
+
+        NPT_String metadata;
+        avt->GetStateVariableValue("AVTransportURIMetaData", metadata);
+        // try to recreate the didl dynamically if not set
+        if (metadata.IsEmpty()) {
+            GetMetadata(metadata);
+        }
+        avt->SetStateVariable("CurrentTrackMetadata", metadata);
+        avt->SetStateVariable("AVTransportURIMetaData", metadata);
+    } else if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
+        avt->SetStateVariable("TransportState", "PLAYING");
+
+        avt->SetStateVariable("AVTransportURI" , g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
+        avt->SetStateVariable("CurrentTrackURI", g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
+        avt->SetStateVariable("TransportPlaySpeed", "1");
+
+        CGUIWindowSlideShow *slideshow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
+        if (slideshow)
+        {
+          CStdString index;
+          index.Format("%d", slideshow->NumSlides());
+          avt->SetStateVariable("NumberOfTracks", index.c_str());
+          index.Format("%d", slideshow->CurrentSlide());
+          avt->SetStateVariable("CurrentTrack", index.c_str());
+
+        }
+
+        avt->SetStateVariable("CurrentTrackMetadata", "");
+        avt->SetStateVariable("AVTransportURIMetaData", "");
+
+    } else {
+        avt->SetStateVariable("TransportState", "STOPPED");
+        avt->SetStateVariable("TransportPlaySpeed", "1");
+        avt->SetStateVariable("NumberOfTracks", "0");
+        avt->SetStateVariable("CurrentTrack", "0");
+        avt->SetStateVariable("RelativeTimePosition", "00:00:00");
+        avt->SetStateVariable("AbsoluteTimePosition", "00:00:00");
+        avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
+        avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
+    }
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::GetMetadata
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::GetMetadata(NPT_String& meta)
+{
+    NPT_Result res = NPT_FAILURE;
+    const CFileItem &item = g_application.CurrentFileItem();
+    NPT_String file_path;
+    PLT_MediaObject* object = CUPnPServer::BuildObject(item, file_path, false);
+    if (object) {
+        // fetch the path to the thumbnail
+        CStdString thumb = g_infoManager.GetImage(MUSICPLAYER_COVER, -1); //TODO: Only audio for now
+
+        NPT_String ip;
+        if (g_application.getNetwork().GetFirstConnectedInterface()) {
+            ip = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
+        }
+        // build url, use the internal device http server to serv the image
+        NPT_HttpUrlQuery query;
+        query.AddField("path", thumb.c_str());
+        PLT_AlbumArtInfo art;
+        art.uri = NPT_HttpUrl(
+            ip,
+            m_URLDescription.GetPort(),
+            "/thumb.jpg",
+            query.ToString()).ToString();
+        art.dlna_profile = "JPEG_TN";
+        object->m_ExtraInfo.album_arts.Add(art);
+
+        res = PLT_Didl::ToDidl(*object, "*", meta);
+        delete object;
+    }
+    return res;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnNext
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnNext(PLT_ActionReference& action)
+{
+    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
+        CAction action(ACTION_NEXT_PICTURE);
+        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
+    } else {
+        CApplicationMessenger::Get().PlayListPlayerNext();
+    }
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnPause
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnPause(PLT_ActionReference& action)
+{
+    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
+        CAction action(ACTION_PAUSE);
+        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
+    } else if (!g_application.IsPaused())
+      CApplicationMessenger::Get().MediaPause();
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnPlay
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnPlay(PLT_ActionReference& action)
+{
+    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
+        return NPT_SUCCESS;
+    } else if (g_application.IsPaused()) {
+      CApplicationMessenger::Get().MediaPause();
+    } else if (!g_application.IsPlaying()) {
+        NPT_String uri, meta;
+        PLT_Service* service;
+        // look for value set previously by SetAVTransportURI
+        NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+        NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURI", uri));
+        NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURIMetaData", meta));
+
+        // if not set, use the current file being played
+        PlayMedia(uri, meta);
+    }
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnPrevious
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnPrevious(PLT_ActionReference& action)
+{
+    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
+        CAction action(ACTION_PREV_PICTURE);
+        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
+    } else {
+        CApplicationMessenger::Get().PlayListPlayerPrevious();
+    }
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnStop
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnStop(PLT_ActionReference& action)
+{
+    if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
+        CAction action(ACTION_STOP);
+        CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
+    } else {
+        CApplicationMessenger::Get().MediaStop();
+    }
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnSetAVTransportURI
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSetAVTransportURI(PLT_ActionReference& action)
+{
+    NPT_String uri, meta;
+    PLT_Service* service;
+    NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+
+    NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURI", uri));
+    NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURIMetaData", meta));
+
+    // if not playing already, just keep around uri & metadata
+    // and wait for play command
+    if (!g_application.IsPlaying() && g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW) {
+        service->SetStateVariable("TransportState", "STOPPED");
+        service->SetStateVariable("TransportStatus", "OK");
+        service->SetStateVariable("TransportPlaySpeed", "1");
+        service->SetStateVariable("AVTransportURI", uri);
+        service->SetStateVariable("AVTransportURIMetaData", meta);
+
+        NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
+        return NPT_SUCCESS;
+    }
+
+    return PlayMedia(uri, meta, action.AsPointer());
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::PlayMedia
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::PlayMedia(const char* uri, const char* meta, PLT_Action* action)
+{
+    bool bImageFile = false;
+    PLT_Service* service;
+    NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
+
+    { NPT_AutoLock lock(m_state);
+      service->SetStateVariable("TransportState", "TRANSITIONING");
+      service->SetStateVariable("TransportStatus", "OK");
+    }
+
+    PLT_MediaObjectListReference list;
+    PLT_MediaObject*             object = NULL;
+
+    if (meta && NPT_SUCCEEDED(PLT_Didl::FromDidl(meta, list))) {
+        list->Get(0, object);
+    }
+
+    if (object) {
+        CFileItem item(uri, false);
+
+        PLT_MediaItemResource* res = object->m_Resources.GetFirstItem();
+        for(NPT_Cardinal i = 0; i < object->m_Resources.GetItemCount(); i++) {
+            if(object->m_Resources[i].m_Uri == uri) {
+                res = &object->m_Resources[i];
+                break;
+            }
+        }
+        for(NPT_Cardinal i = 0; i < object->m_Resources.GetItemCount(); i++) {
+            if(object->m_Resources[i].m_ProtocolInfo.ToString().StartsWith("xbmc-get:")) {
+                res = &object->m_Resources[i];
+                item.SetPath(CStdString(res->m_Uri));
+                break;
+            }
+        }
+
+        if (res && res->m_ProtocolInfo.IsValid()) {
+            item.SetMimeType((const char*)res->m_ProtocolInfo.GetContentType());
+        }
+
+        item.m_dateTime.SetFromDateString((const char*)object->m_Date);
+        item.m_strTitle = (const char*)object->m_Title;
+        item.SetLabel((const char*)object->m_Title);
+        item.SetLabelPreformated(true);
+        if (object->m_ExtraInfo.album_arts.GetItem(0)) {
+            // only considers first album art
+            item.SetThumbnailImage((const char*)object->m_ExtraInfo.album_arts.GetItem(0)->uri);
+        }
+        if (object->m_ObjectClass.type.StartsWith("object.item.audioItem")) {
+            if(NPT_SUCCEEDED(CUPnP::PopulateTagFromObject(*item.GetMusicInfoTag(), *object, res)))
+                item.SetLabelPreformated(false);
+        } else if (object->m_ObjectClass.type.StartsWith("object.item.videoItem")) {
+            if(NPT_SUCCEEDED(CUPnP::PopulateTagFromObject(*item.GetVideoInfoTag(), *object, res)))
+                item.SetLabelPreformated(false);
+        } else if (object->m_ObjectClass.type.StartsWith("object.item.imageItem")) {
+            bImageFile = true;
+        }
+        bImageFile?CApplicationMessenger::Get().PictureShow(item.GetPath())
+                  :CApplicationMessenger::Get().MediaPlay(item);
+    } else {
+        bImageFile = NPT_String(PLT_MediaObject::GetUPnPClass(uri)).StartsWith("object.item.imageItem", true);
+
+        bImageFile?CApplicationMessenger::Get().PictureShow((const char*)uri)
+                  :CApplicationMessenger::Get().MediaPlay((const char*)uri);
+    }
+
+    if (g_application.IsPlaying() || g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
+        NPT_AutoLock lock(m_state);
+        service->SetStateVariable("TransportState", "PLAYING");
+        service->SetStateVariable("TransportStatus", "OK");
+        service->SetStateVariable("AVTransportURI", uri);
+        service->SetStateVariable("AVTransportURIMetaData", meta);
+    } else {
+        NPT_AutoLock lock(m_state);
+        service->SetStateVariable("TransportState", "STOPPED");
+        service->SetStateVariable("TransportStatus", "ERROR_OCCURRED");
+    }
+
+    if (action) {
+        NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
+    }
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnSetVolume
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSetVolume(PLT_ActionReference& action)
+{
+    NPT_String volume;
+    NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredVolume", volume));
+    g_application.SetVolume((float)strtod((const char*)volume, NULL));
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnSetMute
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSetMute(PLT_ActionReference& action)
+{
+    NPT_String mute;
+    NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredMute",mute));
+    if((mute == "1") ^ g_settings.m_bMute)
+        g_application.ToggleMute();
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnPRenderer::OnSeek
++---------------------------------------------------------------------*/
+NPT_Result
+CUPnPRenderer::OnSeek(PLT_ActionReference& action)
+{
+    if (!g_application.IsPlaying()) return NPT_ERROR_INVALID_STATE;
+
+    NPT_String unit, target;
+    NPT_CHECK_SEVERE(action->GetArgumentValue("Unit", unit));
+    NPT_CHECK_SEVERE(action->GetArgumentValue("Target", target));
+
+    if (!unit.Compare("REL_TIME")) {
+        // converts target to seconds
+        NPT_UInt32 seconds;
+        NPT_CHECK_SEVERE(PLT_Didl::ParseTimeStamp(target, seconds));
+        g_application.SeekTime(seconds);
+    }
+
+    return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+|   CRendererReferenceHolder class
++---------------------------------------------------------------------*/
+class CRendererReferenceHolder
+{
+public:
+    PLT_DeviceHostReference m_Device;
+};
+
+/*----------------------------------------------------------------------
+|   CMediaBrowser class
++---------------------------------------------------------------------*/
+class CMediaBrowser : public PLT_SyncMediaBrowser,
+                      public PLT_MediaContainerChangesListener
+{
+public:
+    CMediaBrowser(PLT_CtrlPointReference& ctrlPoint)
+        : PLT_SyncMediaBrowser(ctrlPoint, true)
+    {
+        SetContainerListener(this);
+    }
+
+    // PLT_MediaBrowser methods
+    virtual bool OnMSAdded(PLT_DeviceDataReference& device)
+    {
+        CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
+        message.SetStringParam("upnp://");
+        g_windowManager.SendThreadMessage(message);
+
+        return PLT_SyncMediaBrowser::OnMSAdded(device);
+    }
+    virtual void OnMSRemoved(PLT_DeviceDataReference& device)
+    {
+        PLT_SyncMediaBrowser::OnMSRemoved(device);
+
+        CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
+        message.SetStringParam("upnp://");
+        g_windowManager.SendThreadMessage(message);
+
+        PLT_SyncMediaBrowser::OnMSRemoved(device);
+    }
+
+    // PLT_MediaContainerChangesListener methods
+    virtual void OnContainerChanged(PLT_DeviceDataReference& device,
+                                    const char*              item_id,
+                                    const char*              update_id)
+    {
+        NPT_String path = "upnp://"+device->GetUUID()+"/";
+        if (!NPT_StringsEqual(item_id, "0")) {
+            CStdString id = item_id;
+            CURL::Encode(id);
+            path += id.c_str();
+            path += "/";
+        }
+
+        CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
+        message.SetStringParam(path.GetChars());
+        g_windowManager.SendThreadMessage(message);
+    }
+};
+
+
+/*----------------------------------------------------------------------
+|   CUPnP::CUPnP
++---------------------------------------------------------------------*/
+CUPnP::CUPnP() :
+    m_MediaBrowser(NULL),
+    m_ServerHolder(new CDeviceHostReferenceHolder()),
+    m_RendererHolder(new CRendererReferenceHolder()),
+    m_CtrlPointHolder(new CCtrlPointReferenceHolder())
+{
+    // initialize upnp context
+    m_UPnP = new PLT_UPnP();
+
+    // keep main IP around
+    if (g_application.getNetwork().GetFirstConnectedInterface()) {
+        m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
+    }
+    NPT_List<NPT_IpAddress> list;
+    if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list))) {
+        m_IP = (*(list.GetFirstItem())).ToString();
+    }
+
+    // start upnp monitoring
+    m_UPnP->Start();
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::~CUPnP
++---------------------------------------------------------------------*/
+CUPnP::~CUPnP()
+{
+    m_UPnP->Stop();
+    StopClient();
+    StopServer();
+
+    delete m_UPnP;
+    delete m_ServerHolder;
+    delete m_RendererHolder;
+    delete m_CtrlPointHolder;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::GetInstance
++---------------------------------------------------------------------*/
+CUPnP*
+CUPnP::GetInstance()
+{
+    if (!upnp) {
+        upnp = new CUPnP();
+    }
+
+    return upnp;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::ReleaseInstance
++---------------------------------------------------------------------*/
+void
+CUPnP::ReleaseInstance(bool bWait)
+{
+    if (upnp) {
+        CUPnP* _upnp = upnp;
+        upnp = NULL;
+
+        if (bWait) {
+            delete _upnp;
+        } else {
+            // since it takes a while to clean up
+            // starts a detached thread to do this
+            CUPnPCleaner* cleaner = new CUPnPCleaner(_upnp);
+            cleaner->Start();
+        }
+    }
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::StartClient
++---------------------------------------------------------------------*/
+void
+CUPnP::StartClient()
+{
+    if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
+
+    // create controlpoint
+    m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint();
+
+    // start it
+    m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
+
+    // start browser
+    m_MediaBrowser = new CMediaBrowser(m_CtrlPointHolder->m_CtrlPoint);
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::StopClient
++---------------------------------------------------------------------*/
+void
+CUPnP::StopClient()
+{
+    if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
+
+    m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
+    m_CtrlPointHolder->m_CtrlPoint = NULL;
+
+    delete m_MediaBrowser;
+    m_MediaBrowser = NULL;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::CreateServer
++---------------------------------------------------------------------*/
+CUPnPServer*
+CUPnP::CreateServer(int port /* = 0 */)
+{
+    CUPnPServer* device =
+        new CUPnPServer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
+                        g_settings.m_UPnPUUIDServer.length()?g_settings.m_UPnPUUIDServer.c_str():NULL,
+                        port);
+
+    // trying to set optional upnp values for XP UPnP UI Icons to detect us
+    // but it doesn't work anyways as it requires multicast for XP to detect us
+    device->m_PresentationURL =
+        NPT_HttpUrl(m_IP,
+                    atoi(g_guiSettings.GetString("services.webserverport")),
+                    "/").ToString();
+
+    device->m_ModelName        = "XBMC Media Center";
+    device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
+    device->m_ModelDescription = "XBMC Media Center - Media Server";
+    device->m_ModelURL         = "http://www.xbmc.org/";
+    device->m_Manufacturer     = "Team XBMC";
+    device->m_ManufacturerURL  = "http://www.xbmc.org/";
+
+    device->SetDelegate(device);
+    return device;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::StartServer
++---------------------------------------------------------------------*/
+void
+CUPnP::StartServer()
+{
+    if (!m_ServerHolder->m_Device.IsNull()) return;
+
+    // load upnpserver.xml so that g_settings.m_vecUPnPMusiCMediaSources, etc.. are loaded
+    CStdString filename;
+    URIUtils::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);
+    g_settings.LoadUPnPXml(filename);
+
+    // create the server with a XBox compatible friendlyname and UUID from upnpserver.xml if found
+    m_ServerHolder->m_Device = CreateServer(g_settings.m_UPnPPortServer);
+
+    // start server
+    NPT_Result res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
+    if (NPT_FAILED(res)) {
+        // if the upnp device port was not 0, it could have failed because
+        // of port being in used, so restart with a random port
+        if (g_settings.m_UPnPPortServer > 0) m_ServerHolder->m_Device = CreateServer(0);
+
+        res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
+    }
+
+    // save port but don't overwrite saved settings if port was random
+    if (NPT_SUCCEEDED(res)) {
+        if (g_settings.m_UPnPPortServer == 0) {
+            g_settings.m_UPnPPortServer = m_ServerHolder->m_Device->GetPort();
+        }
+        CUPnPServer::m_MaxReturnedItems = UPNP_DEFAULT_MAX_RETURNED_ITEMS;
+        if (g_settings.m_UPnPMaxReturnedItems > 0) {
+            // must be > UPNP_DEFAULT_MIN_RETURNED_ITEMS
+            CUPnPServer::m_MaxReturnedItems = max(UPNP_DEFAULT_MIN_RETURNED_ITEMS, g_settings.m_UPnPMaxReturnedItems);
+        }
+        g_settings.m_UPnPMaxReturnedItems = CUPnPServer::m_MaxReturnedItems;
+    }
+
+    // save UUID
+    g_settings.m_UPnPUUIDServer = m_ServerHolder->m_Device->GetUUID();
+    g_settings.SaveUPnPXml(filename);
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::StopServer
++---------------------------------------------------------------------*/
+void
+CUPnP::StopServer()
+{
+    if (m_ServerHolder->m_Device.IsNull()) return;
+
+    m_UPnP->RemoveDevice(m_ServerHolder->m_Device);
+    m_ServerHolder->m_Device = NULL;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::CreateRenderer
++---------------------------------------------------------------------*/
+CUPnPRenderer*
+CUPnP::CreateRenderer(int port /* = 0 */)
+{
+    CUPnPRenderer* device =
+        new CUPnPRenderer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
+                          false,
+                          (g_settings.m_UPnPUUIDRenderer.length() ? g_settings.m_UPnPUUIDRenderer.c_str() : NULL),
+                          port);
+
+    device->m_PresentationURL =
+        NPT_HttpUrl(m_IP,
+                    atoi(g_guiSettings.GetString("services.webserverport")),
+                    "/").ToString();
+    device->m_ModelName        = "XBMC Media Center";
+    device->m_ModelNumber      = g_infoManager.GetVersion().c_str();
+    device->m_ModelDescription = "XBMC Media Center - Media Renderer";
+    device->m_ModelURL         = "http://www.xbmc.org/";
+    device->m_Manufacturer     = "Team XBMC";
+    device->m_ManufacturerURL  = "http://www.xbmc.org/";
+
+    return device;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::StartRenderer
++---------------------------------------------------------------------*/
+void CUPnP::StartRenderer()
+{
+    if (!m_RendererHolder->m_Device.IsNull()) return;
+
+    CStdString filename;
+    URIUtils::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);
+    g_settings.LoadUPnPXml(filename);
+
+    m_RendererHolder->m_Device = CreateRenderer(g_settings.m_UPnPPortRenderer);
+
+    NPT_Result res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
+
+    // failed most likely because port is in use, try again with random port now
+    if (NPT_FAILED(res) && g_settings.m_UPnPPortRenderer != 0) {
+        m_RendererHolder->m_Device = CreateRenderer(0);
+
+        res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
+    }
+
+    // save port but don't overwrite saved settings if random
+    if (NPT_SUCCEEDED(res) && g_settings.m_UPnPPortRenderer == 0) {
+        g_settings.m_UPnPPortRenderer = m_RendererHolder->m_Device->GetPort();
+    }
+
+    // save UUID
+    g_settings.m_UPnPUUIDRenderer = m_RendererHolder->m_Device->GetUUID();
+    g_settings.SaveUPnPXml(filename);
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::StopRenderer
++---------------------------------------------------------------------*/
+void CUPnP::StopRenderer()
+{
+    if (m_RendererHolder->m_Device.IsNull()) return;
+
+    m_UPnP->RemoveDevice(m_RendererHolder->m_Device);
+    m_RendererHolder->m_Device = NULL;
+}
+
+/*----------------------------------------------------------------------
+|   CUPnP::UpdateState
++---------------------------------------------------------------------*/
+void CUPnP::UpdateState()
+{
+  if (!m_RendererHolder->m_Device.IsNull())
+      ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();
+}
+
+int CUPnP::PopulateTagFromObject(CMusicInfoTag&          tag,
+                                 PLT_MediaObject&       object,
+                                 PLT_MediaItemResource* resource /* = NULL */)
+{
+    tag.SetTitle((const char*)object.m_Title);
+    tag.SetArtist((const char*)object.m_Creator);
+    for(PLT_PersonRoles::Iterator it = object.m_People.artists.GetFirstItem(); it; it++) {
+        if     (it->role == "")            tag.SetArtist((const char*)it->name);
+        else if(it->role == "Performer")   tag.SetArtist((const char*)it->name);
+        else if(it->role == "AlbumArtist") tag.SetAlbumArtist((const char*)it->name);
+    }
+    tag.SetTrackNumber(object.m_MiscInfo.original_track_number);
+    for (NPT_List<NPT_String>::Iterator it = object.m_Affiliation.genres.GetFirstItem(); it; it++)
+        tag.SetGenre((const char*) *it);
+    tag.SetAlbum((const char*)object.m_Affiliation.album);
+    if(resource)
+        tag.SetDuration(resource->m_Duration);
+    tag.SetLoaded();
+    return NPT_SUCCESS;
+}
+
+int CUPnP::PopulateTagFromObject(CVideoInfoTag&         tag,
+                                 PLT_MediaObject&       object,
+                                 PLT_MediaItemResource* resource /* = NULL */)
+{
+    CDateTime date;
+    date.SetFromDateString((const char*)object.m_Date);
+
+    if(!object.m_Recorded.program_title.IsEmpty())
+    {
+        int episode;
+        int season;
+        int title = object.m_Recorded.program_title.Find(" : ");
+        if(sscanf(object.m_Recorded.program_title, "S%2dE%2d", &season, &episode) == 2 && title >= 0) {
+            tag.m_strTitle = object.m_Recorded.program_title.SubString(title + 3);
+            tag.m_iEpisode = episode;
+            tag.m_iSeason  = season;
+        } else {
+            tag.m_strTitle = object.m_Recorded.program_title;
+            tag.m_iSeason  = object.m_Recorded.episode_number / 100;
+            tag.m_iEpisode = object.m_Recorded.episode_number % 100;
+        }
+        tag.m_firstAired = date;
+    }
+    else
+    {
+        tag.m_strTitle     = object.m_Title;
+        tag.m_premiered    = date;
+    }
+    tag.m_iYear       = date.GetYear();
+    for (unsigned int index = 0; index < object.m_Affiliation.genres.GetItemCount(); index++)
+      tag.m_genre.push_back(object.m_Affiliation.genres.GetItem(index)->GetChars());
+    tag.m_director = StringUtils::Split((CStdString)object.m_People.director, g_advancedSettings.m_videoItemSeparator);
+    tag.m_strTagLine  = object.m_Description.description;
+    tag.m_strPlot     = object.m_Description.long_description;
+    tag.m_strShowTitle = object.m_Recorded.series_title;
+
+    if(resource)
+      tag.m_strRuntime.Format("%d",resource->m_Duration);
+    return NPT_SUCCESS;
+}
+
+} /* namespace UPNP */
diff --git a/xbmc/network/upnp/UPnP.h b/xbmc/network/upnp/UPnP.h
new file mode 100644 (file)
index 0000000..aaa7197
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+* UPnP Support for XBMC
+* Copyright (c) 2006 c0diq (Sylvain Rebaud)
+* Portions Copyright (c) by the authors of libPlatinum
+*
+* http://www.plutinosoft.com/blog/category/platinum/
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#pragma once
+
+#include "utils/StdString.h"
+
+namespace MUSIC_INFO {
+class CMusicInfoTag;
+}
+
+class CVideoInfoTag;
+
+
+class PLT_UPnP;
+class PLT_SyncMediaBrowser;
+class PLT_MediaObject;
+class PLT_MediaItemResource;
+
+namespace UPNP
+{
+
+class CDeviceHostReferenceHolder;
+class CCtrlPointReferenceHolder;
+class CRendererReferenceHolder;
+class CUPnPRenderer;
+class CUPnPServer;
+
+class CUPnP
+{
+public:
+    CUPnP();
+    ~CUPnP();
+
+    // server
+    void StartServer();
+    void StopServer();
+
+    // client
+    void StartClient();
+    void StopClient();
+    bool IsClientStarted() { return (m_MediaBrowser != NULL); }
+
+    // renderer
+    void StartRenderer();
+    void StopRenderer();
+    void UpdateState();
+
+    // methods
+    static int PopulateTagFromObject(MUSIC_INFO::CMusicInfoTag& tag,
+                                     PLT_MediaObject&           object,
+                                     PLT_MediaItemResource*     resource = NULL);
+    static int PopulateTagFromObject(CVideoInfoTag&             tag,
+                                     PLT_MediaObject&           object,
+                                     PLT_MediaItemResource*     resource = NULL);
+
+    // class methods
+    static CUPnP* GetInstance();
+    static void   ReleaseInstance(bool bWait);
+    static bool   IsInstantiated() { return upnp != NULL; }
+
+private:
+    // methods
+    CUPnPRenderer* CreateRenderer(int port = 0);
+    CUPnPServer*   CreateServer(int port = 0);
+
+public:
+    PLT_SyncMediaBrowser*       m_MediaBrowser;
+
+private:
+    CStdString                  m_IP;
+    PLT_UPnP*                   m_UPnP;
+    CDeviceHostReferenceHolder* m_ServerHolder;
+    CRendererReferenceHolder*   m_RendererHolder;
+    CCtrlPointReferenceHolder*  m_CtrlPointHolder;
+
+
+    static CUPnP* upnp;
+};
+
+} /* namespace UPNP */
index b05b545..0f55a43 100644 (file)
@@ -36,7 +36,6 @@
 #define HAS_SCREENSAVER
 #define HAS_PYTHON
 #define HAS_SYSINFO
-#define HAS_UPNP
 #define HAS_VIDEO_PLAYBACK
 #define HAS_VISUALISATION
 #define HAS_PVRCLIENTS
   #define HAS_MYSQL
 #endif
 
+#if defined(USE_UPNP)
+  #define HAS_UPNP
+#endif
+
 /**********************
  * Non-free Components
  **********************/