]> git.sesse.net Git - vlc/blobdiff - modules/services_discovery/upnp_intel.cpp
Use var_InheritString for --decklink-video-connection.
[vlc] / modules / services_discovery / upnp_intel.cpp
index 8d43bcf4b00e37393c323651e5da7390b2caad82..dec8f8db7157b5ea8322ae407498d3ec9a16b9a5 100644 (file)
@@ -1,11 +1,12 @@
 /*****************************************************************************
- * Upnp_intell.cpp :  UPnP discovery module (Intel SDK)
+ * Upnp_intel.cpp :  UPnP discovery module (Intel SDK)
  *****************************************************************************
- * Copyright (C) 2004-2006 the VideoLAN team
+ * Copyright (C) 2004-2008 the VideoLAN team
  * $Id$
  *
  * Authors: RĂ©mi Denis-Courmont <rem # videolan.org> (original plugin)
  *          Christian Henz <henz # c-lab.de>
+ *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
  *
  * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
  *
 
 /*
   \TODO: Debug messages: "__FILE__, __LINE__" ok ???, Wrn/Err ???
-  \TODO: Change names to VLC standard ???
-  \TODO: Rewrite this using the new service discovery API (see sap.c, shout.c).
 */
-
-
-#include <vector>
-#include <string>
-
-#include <upnp/upnp.h>
-#include <upnp/upnptools.h>
-
 #undef PACKAGE_NAME
-#include <vlc/vlc.h>
-#include <vlc_playlist.h>
-#include "vlc_strings.h"
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
+#include "upnp_intel.hpp"
 
-// VLC handle
-
-struct services_discovery_sys_t
-{
-    playlist_item_t *p_node_cat;
-    playlist_item_t *p_node_one;
-};
+#include <vlc_plugin.h>
+#include <vlc_services_discovery.h>
 
 
 // Constants
-
 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
 
-
-// Classes
-
-class MediaServer;
-class MediaServerList;
-class Item;
-class Container;
-
-// Cookie that is passed to the callback
-
-typedef struct
-{
-    services_discovery_t* serviceDiscovery;
-    UpnpClient_Handle clientHandle;
-    MediaServerList* serverList;
-} Cookie;
-
-
-// Class definitions...
-
-class Lockable
-{
-public:
-
-    Lockable( Cookie* c )
-    {
-    vlc_mutex_init( c->serviceDiscovery, &_mutex );
-    }
-
-    ~Lockable()
-    {
-    vlc_mutex_destroy( &_mutex );
-    }
-
-    void lock() { vlc_mutex_lock( &_mutex ); }
-    void unlock() { vlc_mutex_unlock( &_mutex ); }
-
-private:
-
-    vlc_mutex_t _mutex;
-};
-
-
-class Locker
-{
-public:
-    Locker( Lockable* l )
-    {
-    _lockable = l;
-    _lockable->lock();
-    }
-
-    ~Locker()
-    {
-    _lockable->unlock();
-    }
-
-private:
-    Lockable* _lockable;
-};
-
-
-class MediaServer
-{
-public:
-
-    static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
-
-    MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
-    ~MediaServer();
-
-    const char* getUDN() const;
-    const char* getFriendlyName() const;
-
-    void setContentDirectoryEventURL( const char* url );
-    const char* getContentDirectoryEventURL() const;
-
-    void setContentDirectoryControlURL( const char* url );
-    const char* getContentDirectoryControlURL() const;
-
-    void subscribeToContentDirectory();
-    void fetchContents();
-
-    void setPlaylistNode( playlist_item_t* node );
-
-    bool compareSID( const char* sid );
-
-private:
-
-    bool _fetchContents( Container* parent );
-    void _buildPlaylist( Container* container );
-    IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
-
-    Cookie* _cookie;
-
-    Container* _contents;
-    playlist_item_t* _playlistNode;
-
-    std::string _UDN;
-    std::string _friendlyName;
-
-    std::string _contentDirectoryEventURL;
-    std::string _contentDirectoryControlURL;
-
-    int _subscriptionTimeOut;
-    Upnp_SID _subscriptionID;
-};
-
-
-class MediaServerList
-{
-public:
-
-    MediaServerList( Cookie* cookie );
-    ~MediaServerList();
-
-    bool addServer( MediaServer* s );
-    void removeServer( const char* UDN );
-
-    MediaServer* getServer( const char* UDN );
-    MediaServer* getServerBySID( const char* );
-
-private:
-
-    Cookie* _cookie;
-
-    std::vector<MediaServer*> _list;
-};
-
-
-class Item
-{
-public:
-
-    Item( Container* parent, const char* objectID, const char* title, const char* resource );
-
-    const char* getObjectID() const;
-    const char* getTitle() const;
-    const char* getResource() const;
-
-    void setPlaylistNode( playlist_item_t* node );
-    playlist_item_t* getPlaylistNode() const ;
-
-private:
-
-    playlist_item_t* _playlistNode;
-
-    Container* _parent;
-    std::string _objectID;
-    std::string _title;
-    std::string _resource;
-};
-
-
-class Container
+// VLC handle
+struct services_discovery_sys_t
 {
-public:
-
-    Container( Container* parent, const char* objectID, const char* title );
-    ~Container();
-
-    void addItem( Item* item );
-    void addContainer( Container* container );
-
-    const char* getObjectID() const;
-    const char* getTitle() const;
-
-    unsigned int getNumItems() const;
-    unsigned int getNumContainers() const;
-
-    Item* getItem( unsigned int i ) const;
-    Container* getContainer( unsigned int i ) const;
-
-    void setPlaylistNode( playlist_item_t* node );
-    playlist_item_t* getPlaylistNode() const;
-
-private:
-
-    playlist_item_t* _playlistNode;
-
-    Container* _parent;
-
-    std::string _objectID;
-    std::string _title;
-    std::vector<Item*> _items;
-    std::vector<Container*> _containers;
+    UpnpClient_Handle client_handle;
+    MediaServerList* p_server_list;
+    vlc_mutex_t callback_lock;
 };
 
-
 // VLC callback prototypes
-
 static int Open( vlc_object_t* );
 static void Close( vlc_object_t* );
-static void Run( services_discovery_t *p_sd );
+VLC_SD_PROBE_HELPER("upnp_intel", "Universal Plug'n'Play", SD_CAT_LAN)
 
 // Module descriptor
 
 vlc_module_begin();
-set_shortname( "UPnP" );
-set_description( _( "Universal Plug'n'Play discovery ( Intel SDK )" ) );
-set_category( CAT_PLAYLIST );
-set_subcategory( SUBCAT_PLAYLIST_SD );
-set_capability( "services_discovery", 0 );
-set_callbacks( Open, Close );
+    set_shortname( "UPnP" );
+    set_description( N_( "Universal Plug'n'Play" ) );
+    set_category( CAT_PLAYLIST );
+    set_subcategory( SUBCAT_PLAYLIST_SD );
+    set_capability( "services_discovery", 0 );
+    set_callbacks( Open, Close );
+
+    VLC_SD_PROBE_SUBMODULE
 vlc_module_end();
 
 
 // More prototypes...
 
-static Lockable* CallbackLock;
-static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
+static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data );
 
-const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
-IXML_Document* parseBrowseResult( IXML_Document* doc );
+const char* xml_getChildElementValue( IXML_Element* p_parent,
+                                      const char*   psz_tag_name );
+
+IXML_Document* parseBrowseResult( IXML_Document* p_doc );
 
 
 // VLC callbacks...
 
 static int Open( vlc_object_t *p_this )
 {
+    int i_res;
     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
     services_discovery_sys_t *p_sys  = ( services_discovery_sys_t * )
-    malloc( sizeof( services_discovery_sys_t ) );
-
-    p_sd->pf_run = Run;
-    p_sd->p_sys = p_sys;
+            calloc( 1, sizeof( services_discovery_sys_t ) );
 
-    /* Create our playlist node */
-    playlist_NodesPairCreate( pl_Get( p_sd ), _("Devices"),
-                              &p_sys->p_node_cat, &p_sys->p_node_one,
-                              VLC_TRUE );
+    if(!(p_sd->p_sys = p_sys))
+        return VLC_ENOMEM;
 
-    return VLC_SUCCESS;
-}
-
-static void Close( vlc_object_t *p_this )
-{
-    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
-
-    playlist_NodeDelete( pl_Get( p_sd ), p_sys->p_node_one, VLC_TRUE,
-                         VLC_TRUE );
-    playlist_NodeDelete( pl_Get( p_sd ), p_sys->p_node_cat, VLC_TRUE,
-                         VLC_TRUE );
-
-    free( p_sys );
-}
-
-static void Run( services_discovery_t* p_sd )
-{
-    int res;
-
-    res = UpnpInit( 0, 0 );
-    if( res != UPNP_E_SUCCESS )
+    i_res = UpnpInit( 0, 0 );
+    if( i_res != UPNP_E_SUCCESS )
     {
-        msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
-        return;
+        msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
+        free( p_sys );
+        return VLC_EGENERIC;
     }
 
-    Cookie cookie;
-    cookie.serviceDiscovery = p_sd;
-    cookie.serverList = new MediaServerList( &cookie );
-
-    CallbackLock = new Lockable( &cookie );
+    p_sys->p_server_list = new MediaServerList( p_sd );
+    vlc_mutex_init( &p_sys->callback_lock );
 
-    res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle );
-    if( res != UPNP_E_SUCCESS )
+    i_res = UpnpRegisterClient( Callback, p_sd, &p_sys->client_handle );
+    if( i_res != UPNP_E_SUCCESS )
     {
-        msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
-        goto shutDown;
+        msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
+        Close( (vlc_object_t*) p_sd );
+        return VLC_EGENERIC;
     }
 
-    res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie );
-    if( res != UPNP_E_SUCCESS )
+    i_res = UpnpSearchAsync( p_sys->client_handle, 5,
+            MEDIA_SERVER_DEVICE_TYPE, p_sd );
+
+    if( i_res != UPNP_E_SUCCESS )
     {
-        msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
-        goto shutDown;
+        msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
+        Close( (vlc_object_t*) p_sd );
+        return VLC_EGENERIC;
     }
 
-    msg_Dbg( p_sd, "UPnP discovery started" );
-    while( !p_sd->b_die )
+    i_res = UpnpSetMaxContentLength( 262144 );
+    if( i_res != UPNP_E_SUCCESS )
     {
-        msleep( 500 );
+        msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
+        Close( (vlc_object_t*) p_sd );
+        return VLC_EGENERIC;
     }
 
-    msg_Dbg( p_sd, "UPnP discovery stopped" );
+    return VLC_SUCCESS;
+}
+
+static void Close( vlc_object_t *p_this )
+{
+    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
 
- shutDown:
     UpnpFinish();
-    delete cookie.serverList;
-    delete CallbackLock;
-}
+    delete p_sd->p_sys->p_server_list;
+    vlc_mutex_destroy( &p_sd->p_sys->callback_lock );
 
+    free( p_sd->p_sys );
+}
 
 // XML utility functions:
 
 // Returns the value of a child element, or 0 on error
-const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
+const char* xml_getChildElementValue( IXML_Element* p_parent,
+                                      const char*   psz_tag_name_ )
 {
-    if ( !parent ) return 0;
-    if ( !tagName ) return 0;
+    if ( !p_parent ) return 0;
+    if ( !psz_tag_name_ ) return 0;
 
-    char* s = strdup( tagName );
-    IXML_NodeList* nodeList = ixmlElement_getElementsByTagName( parent, s );
-    free( s );
-    if ( !nodeList ) return 0;
+    char* psz_tag_name = strdup( psz_tag_name_ );
+    IXML_NodeList* p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
+    free( psz_tag_name );
+    if ( !p_node_list ) return 0;
 
-    IXML_Node* element = ixmlNodeList_item( nodeList, 0 );
-    ixmlNodeList_free( nodeList );
-    if ( !element ) return 0;
+    IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
+    ixmlNodeList_free( p_node_list );
+    if ( !p_element ) return 0;
 
-    IXML_Node* textNode = ixmlNode_getFirstChild( element );
-    if ( !textNode ) return 0;
+    IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
+    if ( !p_text_node ) return 0;
 
-    return ixmlNode_getNodeValue( textNode );
+    return ixmlNode_getNodeValue( p_text_node );
 }
 
 // Extracts the result document from a SOAP response
-IXML_Document* parseBrowseResult( IXML_Document* doc )
+IXML_Document* parseBrowseResult( IXML_Document* p_doc )
 {
-    if ( !doc ) return 0;
+    ixmlRelaxParser(1);
+
+    if ( !p_doc ) return 0;
 
-    IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
-    if ( !resultList ) return 0;
+    IXML_NodeList* p_result_list = ixmlDocument_getElementsByTagName( p_doc,
+                                                                   "Result" );
 
-    IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
+    if ( !p_result_list ) return 0;
 
-    ixmlNodeList_free( resultList );
+    IXML_Node* p_result_node = ixmlNodeList_item( p_result_list, 0 );
 
-    if ( !resultNode ) return 0;
+    ixmlNodeList_free( p_result_list );
 
-    IXML_Node* textNode = ixmlNode_getFirstChild( resultNode );
-    if ( !textNode ) return 0;
+    if ( !p_result_node ) return 0;
 
-    const char* resultString = ixmlNode_getNodeValue( textNode );
-    char* resultXML = strdup( resultString );
+    IXML_Node* p_text_node = ixmlNode_getFirstChild( p_result_node );
+    if ( !p_text_node ) return 0;
 
-    resolve_xml_special_chars( resultXML );
+    const char* psz_result_string = ixmlNode_getNodeValue( p_text_node );
+    char* psz_result_xml = strdup( psz_result_string );
 
-    IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
+    IXML_Document* p_browse_doc = ixmlParseBuffer( psz_result_xml );
 
-    free( resultXML );
+    free( psz_result_xml );
 
-    return browseDoc;
+    return p_browse_doc;
 }
 
 
 // Handles all UPnP events
-static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
+static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data )
 {
-    Locker locker( CallbackLock );
-
-    Cookie* cookie = ( Cookie* )pCookie;
-
-    switch( eventType ) {
+    services_discovery_t* p_sd = ( services_discovery_t* ) p_user_data;
+    services_discovery_sys_t* p_sys = p_sd->p_sys;
+    vlc_mutex_locker locker( &p_sys->callback_lock );
 
+    switch( event_type )
+    {
     case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
     case UPNP_DISCOVERY_SEARCH_RESULT:
     {
-        struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
+        struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
 
-        IXML_Document *descriptionDoc = 0;
+        IXML_Document *p_description_doc = 0;
 
-        int res;
-        res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
-        if ( res != UPNP_E_SUCCESS )
+        int i_res;
+        i_res = UpnpDownloadXmlDoc( p_discovery->Location, &p_description_doc );
+        if ( i_res != UPNP_E_SUCCESS )
         {
-          msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
-          return res;
+            msg_Dbg( p_sd,
+                    "%s:%d: Could not download device description!",
+                    __FILE__, __LINE__ );
+            return i_res;
         }
 
-        MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
+        MediaServer::parseDeviceDescription( p_description_doc,
+                p_discovery->Location, p_sd );
 
-        ixmlDocument_free( descriptionDoc );
+        ixmlDocument_free( p_description_doc );
     }
     break;
 
     case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
     {
-        struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
+        struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
 
-        cookie->serverList->removeServer( discovery->DeviceId );
+        p_sys->p_server_list->removeServer( p_discovery->DeviceId );
     }
     break;
 
     case UPNP_EVENT_RECEIVED:
     {
-        Upnp_Event* e = ( Upnp_Event* )event;
+        Upnp_Event* p_e = ( Upnp_Event* )p_event;
 
-        MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
-        if ( server ) server->fetchContents();
+        MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_e->Sid );
+        if ( p_server ) p_server->fetchContents();
     }
     break;
 
@@ -456,23 +254,25 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
     {
         // Re-subscribe...
 
-        Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
+        Upnp_Event_Subscribe* p_s = ( Upnp_Event_Subscribe* )p_event;
 
-        MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
-        if ( server ) server->subscribeToContentDirectory();
+        MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_s->Sid );
+        if ( p_server ) p_server->subscribeToContentDirectory();
     }
     break;
 
     case UPNP_EVENT_SUBSCRIBE_COMPLETE:
-        msg_Warn( cookie->serviceDiscovery, "subscription complete" );
+        msg_Warn( p_sd, "subscription complete" );
         break;
+
     case UPNP_DISCOVERY_SEARCH_TIMEOUT:
-        msg_Warn( cookie->serviceDiscovery, "search timeout" );
+        msg_Warn( p_sd, "search timeout" );
         break;
+
     default:
-    msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
+    msg_Dbg( p_sd,
+            "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
+            __FILE__, __LINE__, event_type );
     break;
     }
 
@@ -484,145 +284,193 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
 
 // MediaServer...
 
-void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
+void MediaServer::parseDeviceDescription( IXML_Document* p_doc,
+                                          const char*    p_location,
+                                          services_discovery_t* p_sd )
 {
-    if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
-    if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
-
-    const char* baseURL = location;
-
-    // Try to extract baseURL
-
-    IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
-    if ( urlList )
-    {
-    if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
+    if ( !p_doc )
     {
-        IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
-        if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
+        msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
+        return;
     }
 
-    ixmlNodeList_free( urlList );
+    if ( !p_location )
+    {
+        msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
+        return;
     }
 
-    // Get devices
+    const char* psz_base_url = p_location;
 
-    IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
-    if ( deviceList )
-    {
+    // Try to extract baseURL
 
-    for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
+    IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( p_doc, "baseURL" );
+    if ( !p_url_list )
     {
-        IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
 
-        const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
-        if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
-        if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
-
-        const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
-        if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
-        if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
-
-        const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
-        if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
-
-        MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
-        if ( !cookie->serverList->addServer( server ) ) {
-
-        delete server;
-        server = 0;
-        continue;
+        if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) )
+        {
+            IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node );
+            if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node );
         }
 
-        // Check for ContentDirectory service...
+        ixmlNodeList_free( p_url_list );
+    }
 
-        IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
-        if ( serviceList )
-        {
-            for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
-        {
-            IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
+    // Get devices
 
-            const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
-            if ( !serviceType ) continue;
-            if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
+    IXML_NodeList* p_device_list =
+                ixmlDocument_getElementsByTagName( p_doc, "device" );
 
-            const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
-            if ( !eventSubURL ) continue;
+    if ( p_device_list )
+    {
+        for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ )
+        {
+            IXML_Element* p_device_element =
+                   ( IXML_Element* ) ixmlNodeList_item( p_device_list, i );
 
-            const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
-            if ( !controlURL ) continue;
+            const char* psz_device_type = xml_getChildElementValue( p_device_element,
+                                                               "deviceType" );
+            if ( !psz_device_type )
+            {
+                msg_Dbg( p_sd,
+                        "%s:%d: no deviceType!",
+                        __FILE__, __LINE__ );
+                continue;
+            }
 
-            // Try to subscribe to ContentDirectory service
+            if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type ) != 0 )
+                continue;
 
-            char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
-            if ( url )
+            const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" );
+            if ( !psz_udn )
             {
-                char* s1 = strdup( baseURL );
-                char* s2 = strdup( eventSubURL );
+                msg_Dbg( p_sd, "%s:%d: no UDN!",
+                        __FILE__, __LINE__ );
+                continue;
+            }
 
-                if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
-                {
-                // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
+            if ( p_sd->p_sys->p_server_list->getServer( psz_udn ) != 0 )
+                continue;
 
-                server->setContentDirectoryEventURL( url );
-                server->subscribeToContentDirectory();
-                }
+            const char* psz_friendly_name =
+                       xml_getChildElementValue( p_device_element,
+                                                 "friendlyName" );
 
-                free( s1 );
-                free( s2 );
-                free( url );
+            if ( !psz_friendly_name )
+            {
+                msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
+                continue;
             }
 
-            // Try to browse content directory...
+            MediaServer* p_server = new MediaServer( psz_udn, psz_friendly_name, p_sd );
 
-            url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
-            if ( url )
+            if ( !p_sd->p_sys->p_server_list->addServer( p_server ) )
             {
-            char* s1 = strdup( baseURL );
-            char* s2 = strdup( controlURL );
 
-            if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
-            {
-                // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
-
-                server->setContentDirectoryControlURL( url );
-                server->fetchContents();
-            }
-
-            free( s1 );
-            free( s2 );
-            free( url );
+                delete p_server;
+                p_server = 0;
+                continue;
             }
-        }
-
-        ixmlNodeList_free( serviceList );
-        }
-    }
 
-    ixmlNodeList_free( deviceList );
+            // Check for ContentDirectory service...
+            IXML_NodeList* p_service_list =
+                       ixmlElement_getElementsByTagName( p_device_element,
+                                                         "service" );
+            if ( p_service_list )
+            {
+                for ( unsigned int j = 0;
+                      j < ixmlNodeList_length( p_service_list ); j++ )
+                {
+                    IXML_Element* p_service_element =
+                        ( IXML_Element* ) ixmlNodeList_item( p_service_list, j );
+
+                    const char* psz_service_type =
+                        xml_getChildElementValue( p_service_element,
+                                                  "serviceType" );
+                    if ( !psz_service_type )
+                        continue;
+
+                    if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
+                                psz_service_type ) != 0 )
+                        continue;
+
+                    const char* psz_event_sub_url =
+                        xml_getChildElementValue( p_service_element,
+                                                  "eventSubURL" );
+                    if ( !psz_event_sub_url )
+                        continue;
+
+                    const char* psz_control_url =
+                        xml_getChildElementValue( p_service_element,
+                                                  "controlURL" );
+                    if ( !psz_control_url )
+                        continue;
+
+                    // Try to subscribe to ContentDirectory service
+
+                    char* psz_url = ( char* ) malloc( strlen( psz_base_url ) +
+                            strlen( psz_event_sub_url ) + 1 );
+                    if ( psz_url )
+                    {
+                        char* psz_s1 = strdup( psz_base_url );
+                        char* psz_s2 = strdup( psz_event_sub_url );
+
+                        if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
+                                UPNP_E_SUCCESS )
+                        {
+                            p_server->setContentDirectoryEventURL( psz_url );
+                            p_server->subscribeToContentDirectory();
+                        }
+
+                        free( psz_s1 );
+                        free( psz_s2 );
+                        free( psz_url );
+                    }
+
+                    // Try to browse content directory...
+
+                    psz_url = ( char* ) malloc( strlen( psz_base_url ) +
+                            strlen( psz_control_url ) + 1 );
+                    if ( psz_url )
+                    {
+                        char* psz_s1 = strdup( psz_base_url );
+                        char* psz_s2 = strdup( psz_control_url );
+
+                        if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
+                                UPNP_E_SUCCESS )
+                        {
+                            p_server->setContentDirectoryControlURL( psz_url );
+                            p_server->fetchContents();
+                        }
+
+                        free( psz_s1 );
+                        free( psz_s2 );
+                        free( psz_url );
+                    }
+               }
+               ixmlNodeList_free( p_service_list );
+           }
+       }
+       ixmlNodeList_free( p_device_list );
     }
 }
 
-MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
+MediaServer::MediaServer( const char* psz_udn,
+                          const char* psz_friendly_name,
+                          services_discovery_t* p_sd )
 {
-    _cookie = cookie;
+    _p_sd = p_sd;
 
-    _UDN = UDN;
-    _friendlyName = friendlyName;
+    _UDN = psz_udn;
+    _friendlyName = psz_friendly_name;
 
-    _contents = 0;
-    _playlistNode = 0;
+    _contents = NULL;
+    _inputItem = NULL;
 }
 
 MediaServer::~MediaServer()
 {
-    if ( _contents )
-    {
-        playlist_NodeDelete( pl_Get( _cookie->serviceDiscovery ) ,
-                             _playlistNode, VLC_TRUE, VLC_TRUE );
-    }
-
     delete _contents;
 }
 
@@ -638,9 +486,9 @@ const char* MediaServer::getFriendlyName() const
     return s;
 }
 
-void MediaServer::setContentDirectoryEventURL( const char* url )
+void MediaServer::setContentDirectoryEventURL( const char* psz_url )
 {
-    _contentDirectoryEventURL = url;
+    _contentDirectoryEventURL = psz_url;
 }
 
 const char* MediaServer::getContentDirectoryEventURL() const
@@ -649,9 +497,9 @@ const char* MediaServer::getContentDirectoryEventURL() const
     return s;
 }
 
-void MediaServer::setContentDirectoryControlURL( const char* url )
+void MediaServer::setContentDirectoryControlURL( const char* psz_url )
 {
-    _contentDirectoryControlURL = url;
+    _contentDirectoryControlURL = psz_url;
 }
 
 const char* MediaServer::getContentDirectoryControlURL() const
@@ -661,228 +509,328 @@ const char* MediaServer::getContentDirectoryControlURL() const
 
 void MediaServer::subscribeToContentDirectory()
 {
-    const char* url = getContentDirectoryEventURL();
-    if ( !url || strcmp( url, "" ) == 0 )
+    const char* psz_url = getContentDirectoryEventURL();
+    if ( !psz_url || strcmp( psz_url, "" ) == 0 )
     {
-    msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
-    return;
+        msg_Dbg( _p_sd, "No subscription url set!" );
+        return;
     }
 
-    int timeOut = 1810;
+    int i_timeout = 1810;
     Upnp_SID sid;
 
-    int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
+    int i_res = UpnpSubscribe( _p_sd->p_sys->client_handle, psz_url, &i_timeout, sid );
 
-    if ( res == UPNP_E_SUCCESS )
+    if ( i_res == UPNP_E_SUCCESS )
     {
-    _subscriptionTimeOut = timeOut;
-    memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
+        _subscriptionTimeOut = i_timeout;
+        memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
     }
     else
     {
-    msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) );
+        msg_Dbg( _p_sd,
+                "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__,
+                getFriendlyName(), UpnpGetErrorMessage( i_res ) );
     }
 }
 
-IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter,
-                       const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria )
+IXML_Document* MediaServer::_browseAction( const char* psz_object_id_,
+                                           const char* psz_browser_flag_,
+                                           const char* psz_filter_,
+                                           const char* psz_starting_index_,
+                                           const char* psz_requested_count_,
+                                           const char* psz_sort_criteria_ )
 {
-    IXML_Document* action = 0;
-    IXML_Document* response = 0;
+    IXML_Document* p_action = 0;
+    IXML_Document* p_response = 0;
+    const char* psz_url = getContentDirectoryControlURL();
+
+    if ( !psz_url || strcmp( psz_url, "" ) == 0 )
+    {
+        msg_Dbg( _p_sd, "No subscription url set!" );
+        return 0;
+    }
+
+    char* psz_object_id = strdup( psz_object_id_ );
+    char* psz_browse_flag = strdup( psz_browser_flag_ );
+    char* psz_filter = strdup( psz_filter_ );
+    char* psz_starting_index = strdup( psz_starting_index_ );
+    char* psz_requested_count = strdup( psz_requested_count_ );
+    char* psz_sort_criteria = strdup( psz_sort_criteria_ );
+    char* psz_service_type = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
+
+    int i_res;
+
+    i_res = UpnpAddToAction( &p_action, "Browse",
+            psz_service_type, "ObjectID", psz_object_id );
 
-    const char* url = getContentDirectoryControlURL();
-    if ( !url || strcmp( url, "" ) == 0 ) { msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" ); return 0; }
+    if ( i_res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+                 "%s:%d: ERROR: %s", __FILE__, __LINE__,
+                 UpnpGetErrorMessage( i_res ) );
+        goto browseActionCleanup;
+    }
 
-    char* ObjectID = strdup( pObjectID );
-    char* BrowseFlag = strdup( pBrowseFlag );
-    char* Filter = strdup( pFilter );
-    char* StartingIndex = strdup( pStartingIndex );
-    char* RequestedCount = strdup( pRequestedCount );
-    char* SortCriteria = strdup( pSortCriteria );
+    i_res = UpnpAddToAction( &p_action, "Browse",
+            psz_service_type, "BrowseFlag", psz_browse_flag );
+
+    if ( i_res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( i_res ) );
+        goto browseActionCleanup;
+    }
 
-    char* serviceType = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
+    i_res = UpnpAddToAction( &p_action, "Browse",
+            psz_service_type, "Filter", psz_filter );
 
-    int res;
+    if ( i_res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( i_res ) );
+        goto browseActionCleanup;
+    }
 
-    res = UpnpAddToAction( &action, "Browse", serviceType, "ObjectID", ObjectID );
-    if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
+    i_res = UpnpAddToAction( &p_action, "Browse",
+            psz_service_type, "StartingIndex", psz_starting_index );
 
-    res = UpnpAddToAction( &action, "Browse", serviceType, "BrowseFlag", BrowseFlag );
-    if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
+    if ( i_res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( i_res ) );
+        goto browseActionCleanup;
+    }
 
-    res = UpnpAddToAction( &action, "Browse", serviceType, "Filter", Filter );
-    if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
+    i_res = UpnpAddToAction( &p_action, "Browse",
+            psz_service_type, "RequestedCount", psz_requested_count );
 
-    res = UpnpAddToAction( &action, "Browse", serviceType, "StartingIndex", StartingIndex );
-    if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
+    if ( i_res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+                "%s:%d: ERROR: %s", __FILE__, __LINE__,
+                UpnpGetErrorMessage( i_res ) ); goto browseActionCleanup; }
 
-    res = UpnpAddToAction( &action, "Browse", serviceType, "RequestedCount", RequestedCount );
-    if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
+    i_res = UpnpAddToAction( &p_action, "Browse",
+            psz_service_type, "SortCriteria", psz_sort_criteria );
 
-    res = UpnpAddToAction( &action, "Browse", serviceType, "SortCriteria", SortCriteria );
-    if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
+    if ( i_res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( i_res ) );
+        goto browseActionCleanup;
+    }
 
-    res = UpnpSendAction( _cookie->clientHandle,
-              url,
+    i_res = UpnpSendAction( _p_sd->p_sys->client_handle,
+              psz_url,
               CONTENT_DIRECTORY_SERVICE_TYPE,
               0,
-              action,
-              &response );
-    if ( res != UPNP_E_SUCCESS )
+              p_action,
+              &p_response );
+
+    if ( i_res != UPNP_E_SUCCESS )
     {
-    msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
-    ixmlDocument_free( response );
-    response = 0;
+        msg_Dbg( _p_sd,
+                "%s:%d: ERROR: %s when trying the send() action with URL: %s",
+                __FILE__, __LINE__,
+                UpnpGetErrorMessage( i_res ), psz_url );
+
+        ixmlDocument_free( p_response );
+        p_response = 0;
     }
 
  browseActionCleanup:
 
-    free( ObjectID );
-    free( BrowseFlag );
-    free( Filter );
-    free( StartingIndex );
-    free( RequestedCount );
-    free( SortCriteria );
+    free( psz_object_id );
+    free( psz_browse_flag );
+    free( psz_filter );
+    free( psz_starting_index );
+    free( psz_requested_count );
+    free( psz_sort_criteria );
 
-    free( serviceType );
+    free( psz_service_type );
 
-    ixmlDocument_free( action );
-    return response;
+    ixmlDocument_free( p_action );
+    return p_response;
 }
 
 void MediaServer::fetchContents()
 {
     Container* root = new Container( 0, "0", getFriendlyName() );
-    playlist_t * p_playlist = pl_Get( _cookie->serviceDiscovery );
     _fetchContents( root );
 
-    if ( _contents )
-    {
-        PL_LOCK;
-        playlist_NodeEmpty( p_playlist, _playlistNode, VLC_TRUE );
-        PL_UNLOCK;
-        delete _contents;
-    }
-
     _contents = root;
-    _contents->setPlaylistNode( _playlistNode );
+    _contents->setInputItem( _inputItem );
 
-    _buildPlaylist( _contents );
+    _buildPlaylist( _contents, NULL );
 }
 
-bool MediaServer::_fetchContents( Container* parent )
+bool MediaServer::_fetchContents( Container* p_parent )
 {
-    if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; }
+    if (!p_parent)
+    {
+        msg_Dbg( _p_sd,
+                "%s:%d: parent==NULL", __FILE__, __LINE__ );
+        return false;
+    }
 
-    IXML_Document* response = _browseAction( parent->getObjectID(), "BrowseDirectChildren", "*", "0", "0", "" );
-    if ( !response ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
+    IXML_Document* p_response = _browseAction( p_parent->getObjectID(),
+                                      "BrowseDirectChildren",
+                                      "*", "0", "0", "" );
+    if ( !p_response )
+    {
+        msg_Dbg( _p_sd,
+                "%s:%d: ERROR! No response from browse() action",
+                __FILE__, __LINE__ );
+        return false;
+    }
 
-    IXML_Document* result = parseBrowseResult( response );
-    ixmlDocument_free( response );
-    if ( !result ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
+    IXML_Document* result = parseBrowseResult( p_response );
+    ixmlDocument_free( p_response );
 
-    IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" );
-    if ( containerNodeList )
+    if ( !result )
     {
-    for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
+        msg_Dbg( _p_sd,
+                "%s:%d: ERROR! browse() response parsing failed",
+                __FILE__, __LINE__ );
+        return false;
+    }
+
+    IXML_NodeList* containerNodeList =
+                ixmlDocument_getElementsByTagName( result, "container" );
+
+    if ( containerNodeList )
     {
-              IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
+        for ( unsigned int i = 0;
+                i < ixmlNodeList_length( containerNodeList ); i++ )
+        {
+            IXML_Element* containerElement =
+                  ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
 
-        const char* objectID = ixmlElement_getAttribute( containerElement, "id" );
-        if ( !objectID ) continue;
+            const char* objectID = ixmlElement_getAttribute( containerElement,
+                                                             "id" );
+            if ( !objectID )
+                continue;
 
-        const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
-        if ( !childCountStr ) continue;
-        int childCount = atoi( childCountStr );
+            const char* childCountStr =
+                    ixmlElement_getAttribute( containerElement, "childCount" );
 
-        const char* title = xml_getChildElementValue( containerElement, "dc:title" );
-        if ( !title ) continue;
+            if ( !childCountStr )
+                continue;
 
-        const char* resource = xml_getChildElementValue( containerElement, "res" );
+            int childCount = atoi( childCountStr );
+            const char* title = xml_getChildElementValue( containerElement,
+                                                          "dc:title" );
 
-        if ( resource && childCount < 1 )
-        {
-        Item* item = new Item( parent, objectID, title, resource );
-        parent->addItem( item );
-        }
-        else
-        {
-        Container* container = new Container( parent, objectID, title );
-        parent->addContainer( container );
+            if ( !title )
+                continue;
 
-        if ( childCount > 0 ) _fetchContents( container );
-        }
-    }
+            const char* resource = xml_getChildElementValue( containerElement,
+                                                             "res" );
+
+            if ( resource && childCount < 1 )
+            {
+                Item* item = new Item( p_parent, objectID, title, resource );
+                p_parent->addItem( item );
+            }
+
+            else
+            {
+                Container* container = new Container( p_parent, objectID, title );
+                p_parent->addContainer( container );
 
-    ixmlNodeList_free( containerNodeList );
+                if ( childCount > 0 )
+                    _fetchContents( container );
+            }
+        }
+        ixmlNodeList_free( containerNodeList );
     }
 
-    IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result, "item" );
+    IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result,
+                                                                     "item" );
     if ( itemNodeList )
     {
         for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
-    {
-        IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
+        {
+            IXML_Element* itemElement =
+                        ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
 
-        const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
-        if ( !objectID ) continue;
+            const char* objectID =
+                        ixmlElement_getAttribute( itemElement, "id" );
 
-        const char* title = xml_getChildElementValue( itemElement, "dc:title" );
-        if ( !title ) continue;
+            if ( !objectID )
+                continue;
 
-        const char* resource = xml_getChildElementValue( itemElement, "res" );
-        if ( !resource ) continue;
+            const char* title =
+                        xml_getChildElementValue( itemElement, "dc:title" );
 
-        Item* item = new Item( parent, objectID, title, resource );
-        parent->addItem( item );
-    }
+            if ( !title )
+                continue;
+
+            const char* resource =
+                        xml_getChildElementValue( itemElement, "res" );
+
+            if ( !resource )
+                continue;
 
-    ixmlNodeList_free( itemNodeList );
+            Item* item = new Item( p_parent, objectID, title, resource );
+            p_parent->addItem( item );
+        }
+        ixmlNodeList_free( itemNodeList );
     }
 
     ixmlDocument_free( result );
-
     return true;
 }
 
-void MediaServer::_buildPlaylist( Container* parent )
+void MediaServer::_buildPlaylist( Container* p_parent, input_item_node_t *p_input_node )
 {
-    playlist_t *p_playlist = pl_Get( _cookie->serviceDiscovery );
-    for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
+    bool send = p_input_node == NULL;
+    if( send )
+        p_input_node = input_item_node_Create( p_parent->getInputItem() );
+
+    for ( unsigned int i = 0; i < p_parent->getNumContainers(); i++ )
     {
-        Container* container = parent->getContainer( i );
-        playlist_item_t* parentNode = parent->getPlaylistNode();
+        Container* container = p_parent->getContainer( i );
 
-        char* title = strdup( container->getTitle() );
-        playlist_item_t* node = playlist_NodeCreate( p_playlist, title, parentNode, 0, NULL );
-        free( title );
+        input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop", container->getTitle() );
+        input_item_node_t *p_new_node =
+            input_item_node_AppendItem( p_input_node, p_input_item );
 
-        container->setPlaylistNode( node );
-        _buildPlaylist( container );
+        container->setInputItem( p_input_item );
+        _buildPlaylist( container, p_new_node );
     }
 
-    for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
+    for ( unsigned int i = 0; i < p_parent->getNumItems(); i++ )
     {
-        Item* item = parent->getItem( i );
-        playlist_item_t* parentNode = parent->getPlaylistNode();
+        Item* item = p_parent->getItem( i );
 
-        input_item_t* p_input = input_ItemNew( _cookie->serviceDiscovery,
+        input_item_t* p_input_item = input_item_New( _p_sd,
                                                item->getResource(),
                                                item->getTitle() );
-        int i_cat;
-        playlist_BothAddInput( p_playlist, p_input, parentNode,
-                               PLAYLIST_APPEND, PLAYLIST_END, &i_cat, NULL,
-                               VLC_FALSE );
-        /* TODO: do this better by storing ids */
-        playlist_item_t *p_node = playlist_ItemGetById( p_playlist, i_cat, VLC_FALSE );
-        assert( p_node );
-        item->setPlaylistNode( p_node );
+        assert( p_input_item );
+        input_item_node_AppendItem( p_input_node, p_input_item );
+        item->setInputItem( p_input_item );
     }
+
+    if( send )
+        input_item_node_PostAndDelete( p_input_node );
 }
 
-void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
+void MediaServer::setInputItem( input_item_t* p_input_item )
 {
-    _playlistNode = playlistNode;
+    if(_inputItem == p_input_item)
+        return;
+
+    if(_inputItem)
+        vlc_gc_decref( _inputItem );
+
+    vlc_gc_incref( p_input_item );
+    _inputItem = p_input_item;
 }
 
 bool MediaServer::compareSID( const char* sid )
@@ -893,48 +841,50 @@ bool MediaServer::compareSID( const char* sid )
 
 // MediaServerList...
 
-MediaServerList::MediaServerList( Cookie* cookie )
+MediaServerList::MediaServerList( services_discovery_t* p_sd )
 {
-    _cookie = cookie;
+    _p_sd = p_sd;
 }
 
 MediaServerList::~MediaServerList()
 {
     for ( unsigned int i = 0; i < _list.size(); i++ )
     {
-    delete _list[i];
+        delete _list[i];
     }
 }
 
 bool MediaServerList::addServer( MediaServer* s )
 {
+    input_item_t* p_input_item = NULL;
     if ( getServer( s->getUDN() ) != 0 ) return false;
 
-    msg_Dbg( _cookie->serviceDiscovery, "Adding server '%s'", s->getFriendlyName() );
+    msg_Dbg( _p_sd, "Adding server '%s'",
+            s->getFriendlyName() );
 
-    _list.push_back( s );
+    services_discovery_t* p_sd = _p_sd;
 
-    char* name = strdup( s->getFriendlyName() );
-    playlist_item_t* node = playlist_NodeCreate( pl_Get( _cookie->serviceDiscovery ),
-                                                 name,
-                                          _cookie->serviceDiscovery->p_sys->p_node_cat, 0, NULL );
-    free( name );
-    s->setPlaylistNode( node );
+    p_input_item = input_item_New( p_sd, "vlc://nop", s->getFriendlyName() );
+    s->setInputItem( p_input_item );
+
+    services_discovery_AddItem( p_sd, p_input_item, NULL );
+
+    _list.push_back( s );
 
     return true;
 }
 
-MediaServer* MediaServerList::getServer( const char* UDN )
+MediaServer* MediaServerList::getServer( const char* psz_udn )
 {
     MediaServer* result = 0;
 
     for ( unsigned int i = 0; i < _list.size(); i++ )
     {
-        if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
-    {
-        result = _list[i];
-        break;
-    }
+        if( strcmp( psz_udn, _list[i]->getUDN() ) == 0 )
+        {
+            result = _list[i];
+            break;
+        }
     }
 
     return result;
@@ -942,51 +892,59 @@ MediaServer* MediaServerList::getServer( const char* UDN )
 
 MediaServer* MediaServerList::getServerBySID( const char* sid )
 {
-    MediaServer* server = 0;
+    MediaServer* p_server = 0;
 
     for ( unsigned int i = 0; i < _list.size(); i++ )
     {
-    if ( _list[i]->compareSID( sid ) )
-    {
-        server = _list[i];
-        break;
-    }
+        if ( _list[i]->compareSID( sid ) )
+        {
+            p_server = _list[i];
+            break;
+        }
     }
 
-    return server;
+    return p_server;
 }
 
-void MediaServerList::removeServer( const char* UDN )
+void MediaServerList::removeServer( const char* psz_udn )
 {
-    MediaServer* server = getServer( UDN );
-    if ( !server ) return;
+    MediaServer* p_server = getServer( psz_udn );
+    if ( !p_server ) return;
 
-    msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
+    msg_Dbg( _p_sd,
+            "Removing server '%s'", p_server->getFriendlyName() );
 
     std::vector<MediaServer*>::iterator it;
     for ( it = _list.begin(); it != _list.end(); it++ )
     {
-        if ( *it == server )
-    {
-              _list.erase( it );
-        delete server;
-        break;
-    }
+        if ( *it == p_server )
+        {
+            _list.erase( it );
+            delete p_server;
+            break;
+        }
     }
 }
 
 
 // Item...
 
-Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
+Item::Item( Container* p_parent, const char* psz_object_id, const char* psz_title,
+          const char* psz_resource )
 {
-    _parent = parent;
+    _parent = p_parent;
 
-    _objectID = objectID;
-    _title = title;
-    _resource = resource;
+    _objectID = psz_object_id;
+    _title = psz_title;
+    _resource = psz_resource;
+
+    _inputItem = NULL;
+}
 
-    _playlistNode = 0;
+Item::~Item()
+{
+    if(_inputItem)
+        vlc_gc_decref( _inputItem );
 }
 
 const char* Item::getObjectID() const
@@ -1004,40 +962,52 @@ const char* Item::getResource() const
     return _resource.c_str();
 }
 
-void Item::setPlaylistNode( playlist_item_t* node )
+void Item::setInputItem( input_item_t* p_input_item )
 {
-    _playlistNode = node;
+    if(_inputItem == p_input_item)
+        return;
+
+    if(_inputItem)
+        vlc_gc_decref( _inputItem );
+
+    vlc_gc_incref( p_input_item );
+    _inputItem = p_input_item;
 }
 
-playlist_item_t* Item::getPlaylistNode() const
+input_item_t* Item::getInputItem() const
 {
-    return _playlistNode;
+    return _inputItem;
 }
 
 
 // Container...
 
-Container::Container( Container* parent, const char* objectID, const char* title )
+Container::Container( Container*  p_parent,
+                      const char* psz_object_id,
+                      const char* psz_title )
 {
-    _parent = parent;
+    _parent = p_parent;
 
-    _objectID = objectID;
-    _title = title;
+    _objectID = psz_object_id;
+    _title = psz_title;
 
-    _playlistNode = 0;
+    _inputItem = NULL;
 }
 
 Container::~Container()
 {
     for ( unsigned int i = 0; i < _containers.size(); i++ )
     {
-    delete _containers[i];
+        delete _containers[i];
     }
 
     for ( unsigned int i = 0; i < _items.size(); i++ )
     {
-    delete _items[i];
+        delete _items[i];
     }
+
+    if(_inputItem )
+        vlc_gc_decref( _inputItem );
 }
 
 void Container::addItem( Item* item )
@@ -1045,9 +1015,9 @@ void Container::addItem( Item* item )
     _items.push_back( item );
 }
 
-void Container::addContainer( Container* container )
+void Container::addContainer( Container* p_container )
 {
-    _containers.push_back( container );
+    _containers.push_back( p_container );
 }
 
 const char* Container::getObjectID() const
@@ -1070,24 +1040,36 @@ unsigned int Container::getNumContainers() const
     return _containers.size();
 }
 
-Item* Container::getItem( unsigned int i ) const
+Item* Container::getItem( unsigned int i_index ) const
 {
-    if ( i < _items.size() ) return _items[i];
+    if ( i_index < _items.size() ) return _items[i_index];
     return 0;
 }
 
-Container* Container::getContainer( unsigned int i ) const
+Container* Container::getContainer( unsigned int i_index ) const
 {
-    if ( i < _containers.size() ) return _containers[i];
+    if ( i_index < _containers.size() ) return _containers[i_index];
     return 0;
 }
 
-void Container::setPlaylistNode( playlist_item_t* node )
+Container* Container::getParent()
+{
+    return _parent;
+}
+
+void Container::setInputItem( input_item_t* p_input_item )
 {
-    _playlistNode = node;
+    if(_inputItem == p_input_item)
+        return;
+
+    if(_inputItem)
+        vlc_gc_decref( _inputItem );
+
+    vlc_gc_incref( p_input_item );
+    _inputItem = p_input_item;
 }
 
-playlist_item_t* Container::getPlaylistNode() const
+input_item_t* Container::getInputItem() const
 {
-    return _playlistNode;
+    return _inputItem;
 }