]> git.sesse.net Git - vlc/commitdiff
UPnP services discovery: Merge the SoC 2008 fixes, remove the Cookie class and use...
authorMirsal Ennaime <mirsal@mirsal-laptop.(none)>
Sun, 21 Dec 2008 16:20:52 +0000 (17:20 +0100)
committerMirsal Ennaime <mirsal@mirsal-laptop.(none)>
Sun, 4 Jan 2009 10:31:52 +0000 (11:31 +0100)
modules/services_discovery/upnp_intel.cpp

index b3389ad146164dd3cac9c2872adb5a0c51170d85..297221bc101c482e75443ce9054f17206225b987 100644 (file)
@@ -6,6 +6,7 @@
  *
  * 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
  *
 #include <vlc_plugin.h>
 #include <vlc_playlist.h>
 
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-static int Open( vlc_object_t* );
-static void Close( vlc_object_t* );
-
-vlc_module_begin ()
-    set_shortname( "UPnP" )
-    set_description( N_( "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 )
-vlc_module_end ()
-
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-
 // Constants
 
 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
@@ -78,24 +59,21 @@ class MediaServer;
 class MediaServerList;
 class Item;
 class Container;
-class Lockable;
 
-// Cookie that is passed to the callback
+// VLC handle
 
-typedef struct
+struct services_discovery_sys_t
 {
-    services_discovery_t* serviceDiscovery;
     UpnpClient_Handle clientHandle;
     MediaServerList* serverList;
-    Lockable* lock;
-} Cookie;
-
+};
 
 // Class definitions...
 
 class Lockable
 {
 public:
+
     Lockable()
     {
         vlc_mutex_init( &_mutex );
@@ -110,6 +88,7 @@ public:
     void unlock() { vlc_mutex_unlock( &_mutex ); }
 
 private:
+
     vlc_mutex_t _mutex;
 };
 
@@ -119,13 +98,13 @@ class Locker
 public:
     Locker( Lockable* l )
     {
-    _lockable = l;
-    _lockable->lock();
+        _lockable = l;
+        _lockable->lock();
     }
 
     ~Locker()
     {
-    _lockable->unlock();
+        _lockable->unlock();
     }
 
 private:
@@ -137,9 +116,14 @@ class MediaServer
 {
 public:
 
-    static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
+    static void parseDeviceDescription( IXML_Document* doc,
+                                        const char*    location,
+                                        services_discovery_t* p_sd );
 
-    MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
+    MediaServer( const char* UDN,
+                 const char* friendlyName,
+                 services_discovery_t* p_sd );
+    
     ~MediaServer();
 
     const char* getUDN() const;
@@ -162,9 +146,11 @@ private:
 
     bool _fetchContents( Container* parent );
     void _buildPlaylist( Container* container );
-    IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
+    
+    IXML_Document* _browseAction( const char*, const char*,
+            const char*, const char*, const char*, const char* );
 
-    Cookie* _cookie;
+    services_discovery_t* _p_sd;
 
     Container* _contents;
     playlist_item_t* _playlistNode;
@@ -184,7 +170,7 @@ class MediaServerList
 {
 public:
 
-    MediaServerList( Cookie* cookie );
+    MediaServerList( services_discovery_t* p_sd );
     ~MediaServerList();
 
     bool addServer( MediaServer* s );
@@ -195,7 +181,7 @@ public:
 
 private:
 
-    Cookie* _cookie;
+    services_discovery_t* _p_sd;
 
     std::vector<MediaServer*> _list;
 };
@@ -205,7 +191,10 @@ class Item
 {
 public:
 
-    Item( Container* parent, const char* objectID, const char* title, const char* resource );
+    Item( Container*  parent,
+          const char* objectID,
+          const char* title,
+          const char* resource );
 
     const char* getObjectID() const;
     const char* getTitle() const;
@@ -260,112 +249,109 @@ private:
 };
 
 
-
-// VLC handle
-
-struct services_discovery_sys_t
-{
-    playlist_t *p_playlist;
-    playlist_item_t *p_node_cat;
-    playlist_item_t *p_node_one;
-    Cookie cookie;
-};
-
-
-
 // VLC callback prototypes
 
-static playlist_t *pl_Get( services_discovery_t *p_sd )
-{
-    return p_sd->p_sys->p_playlist;
-}
+static int Open( vlc_object_t* );
+static void Close( vlc_object_t* );
+static void Run( services_discovery_t *p_sd );
+
+// Module descriptor
 
+vlc_module_begin();
+set_shortname( "UPnP" );
+set_description( N_( "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 );
+vlc_module_end();
 
 
 // More prototypes...
 
-static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
+static Lockable* CallbackLock;
+static int Callback( Upnp_EventType eventType, void* event, void* user_data );
 
-const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
-IXML_Document* parseBrowseResult( IXML_Document* doc );
+const char* xml_getChildElementValue( IXML_Element* parent,
+                                      const char*   tagName );
 
+IXML_Document* parseBrowseResult( IXML_Document* doc );
 
 
 // VLC callbacks...
 
 static int Open( vlc_object_t *p_this )
 {
+    int 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 ) );
-    playlist_t *p_playlist = pl_Hold( p_sd );
+            calloc( 1, sizeof( services_discovery_sys_t ) );
 
     p_sd->p_sys = p_sys;
-    p_sys->p_playlist = p_playlist;
-    Cookie *cookie = &p_sys->cookie;
 
-    /* Create our playlist node */
-    PL_LOCK;
-    playlist_NodesPairCreate( p_playlist, _("Devices"),
-                              &p_sys->p_node_cat, &p_sys->p_node_one,
-                              true );
-    PL_UNLOCK;
-
-    cookie->serviceDiscovery = p_sd;
-    cookie->lock = new Lockable();
-    cookie->serverList = new MediaServerList( cookie );
+    services_discovery_SetLocalizedName( p_sd, _("UPnP devices") );
 
-    int res = UpnpInit( 0, 0 );
+    res = UpnpInit( 0, 0 );
     if( res != UPNP_E_SUCCESS )
     {
         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
-        goto shutDown;
+        return VLC_EGENERIC;
     }
 
-    res = UpnpRegisterClient( Callback, cookie, &cookie->clientHandle );
+    p_sys->serverList = new MediaServerList( p_sd );
+    CallbackLock = new Lockable();
+
+    res = UpnpRegisterClient( Callback, p_sys, &p_sys->clientHandle );
     if( res != UPNP_E_SUCCESS )
     {
         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
-        goto shutDown;
+        Close( (vlc_object_t*) p_sd );
+        return VLC_EGENERIC;
     }
 
-    res = UpnpSearchAsync( cookie->clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE,
-                           cookie );
+    res = UpnpSearchAsync( p_sys->clientHandle, 5,
+            MEDIA_SERVER_DEVICE_TYPE, p_sd );
+    
     if( res != UPNP_E_SUCCESS )
     {
         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
-        goto shutDown;
+        Close( (vlc_object_t*) p_sd );
+        return VLC_EGENERIC;
     }
 
     return VLC_SUCCESS;
-
- shutDown:
-    Close( p_this );
-    return VLC_EGENERIC;
 }
 
 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_t *p_playlist = p_sys->p_playlist;
 
     UpnpFinish();
-    delete p_sys->cookie.serverList;
-    delete p_sys->cookie.lock;
+    delete p_sd->p_sys->serverList;
+    delete CallbackLock;
 
-    PL_LOCK;
-    playlist_NodeDelete( p_playlist, p_sys->p_node_one, true, true );
-    playlist_NodeDelete( p_playlist, p_sys->p_node_cat, true, true );
-    PL_UNLOCK;
-    pl_Release( p_sd );
-    free( p_sys );
+    free( p_sd->p_sys );
 }
 
+static void Run( services_discovery_t* p_sd )
+{
+
+    msg_Dbg( p_sd, "UPnP discovery started" );
+    while( vlc_object_alive (p_sd) )
+    {
+        msleep( 500 );
+    }
+
+    msg_Dbg( p_sd, "UPnP discovery stopped" );
+
+}
+
+
 // 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* parent,
+                                      const char*   tagName )
 {
     if ( !parent ) return 0;
     if ( !tagName ) return 0;
@@ -388,9 +374,13 @@ const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName
 // Extracts the result document from a SOAP response
 IXML_Document* parseBrowseResult( IXML_Document* doc )
 {
+    ixmlRelaxParser(1);
+
     if ( !doc ) return 0;
 
-    IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
+    IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc,
+                                                                   "Result" );
+
     if ( !resultList ) return 0;
 
     IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
@@ -414,11 +404,12 @@ IXML_Document* parseBrowseResult( IXML_Document* doc )
 
 
 // Handles all UPnP events
-static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
+static int Callback( Upnp_EventType eventType, void* event, void* user_data )
 {
-    Cookie* cookie = ( Cookie* )pCookie;
+    Locker locker( CallbackLock );
 
-    Locker locker( cookie->lock );
+    services_discovery_t *p_sd = ( services_discovery_t* ) user_data;
+    services_discovery_sys_t* p_sys = p_sd->p_sys;
 
     switch( eventType ) {
 
@@ -433,11 +424,14 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
         res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
         if ( 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 res;
         }
 
-        MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
+        MediaServer::parseDeviceDescription( descriptionDoc,
+                discovery->Location, p_sd );
 
         ixmlDocument_free( descriptionDoc );
     }
@@ -447,7 +441,7 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
     {
         struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
 
-        cookie->serverList->removeServer( discovery->DeviceId );
+        p_sys->serverList->removeServer( discovery->DeviceId );
     }
     break;
 
@@ -455,7 +449,7 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
     {
         Upnp_Event* e = ( Upnp_Event* )event;
 
-        MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
+        MediaServer* server = p_sys->serverList->getServerBySID( e->Sid );
         if ( server ) server->fetchContents();
     }
     break;
@@ -467,21 +461,23 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
 
         Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
 
-        MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
+        MediaServer* server = p_sys->serverList->getServerBySID( s->Sid );
         if ( server ) 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__, eventType );
     break;
     }
 
@@ -493,129 +489,183 @@ 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* doc,
+                                          const char*    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; }
+    if ( !doc )
+    { 
+        msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ ); 
+        return;
+    }
+    
+    if ( !location )
+    {
+        msg_Dbg( p_sd, "%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 ( !urlList )
     {
-        IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
-        if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
-    }
 
-    ixmlNodeList_free( urlList );
+        if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
+        {
+            IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
+            if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
+        }
+
+        ixmlNodeList_free( urlList );
     }
 
     // Get devices
 
-    IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
+    IXML_NodeList* deviceList =
+                ixmlDocument_getElementsByTagName( doc, "device" );
+    
     if ( deviceList )
     {
-
-    for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
-    {
-        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;
-        }
-
-        // Check for ContentDirectory service...
-
-        IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
-        if ( serviceList )
-        {
-            for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
+        for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
         {
-            IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
-
-            const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
-            if ( !serviceType ) continue;
-            if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
-
-            const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
-            if ( !eventSubURL ) continue;
-
-            const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
-            if ( !controlURL ) continue;
+            IXML_Element* deviceElement =
+                   ( IXML_Element* ) ixmlNodeList_item( deviceList, i );
 
-            // Try to subscribe to ContentDirectory service
-
-            char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
-            if ( url )
+            const char* deviceType = xml_getChildElementValue( deviceElement,
+                                                               "deviceType" );
+            if ( !deviceType )
             {
-                char* s1 = strdup( baseURL );
-                char* s2 = strdup( eventSubURL );
-
-                if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
-                {
-                // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
-
-                server->setContentDirectoryEventURL( url );
-                server->subscribeToContentDirectory();
-                }
-
-                free( s1 );
-                free( s2 );
-                free( url );
+                msg_Dbg( p_sd,
+                        "%s:%d: no deviceType!",
+                        __FILE__, __LINE__ );
+                continue;
             }
 
-            // Try to browse content directory...
+            if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 )
+                continue;
 
-            url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
-            if ( url )
+            const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
+            if ( !UDN )
             {
-            char* s1 = strdup( baseURL );
-            char* s2 = strdup( controlURL );
-
-            if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
+                msg_Dbg( p_sd, "%s:%d: no UDN!",
+                        __FILE__, __LINE__ );
+                continue;
+            }
+            
+            if ( p_sd->p_sys->serverList->getServer( UDN ) != 0 )
+                continue;
+
+            const char* friendlyName =
+                       xml_getChildElementValue( deviceElement, 
+                                                 "friendlyName" );
+            
+            if ( !friendlyName )
             {
-                // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
-
-                server->setContentDirectoryControlURL( url );
-                server->fetchContents();
+                msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
+                continue;
             }
 
-            free( s1 );
-            free( s2 );
-            free( url );
-            }
-        }
+            MediaServer* server = new MediaServer( UDN, friendlyName, p_sd );
+            
+            if ( !p_sd->p_sys->serverList->addServer( server ) )
+            {
 
-        ixmlNodeList_free( serviceList );
-        }
-    }
+                delete server;
+                server = 0;
+                continue;
+            }
 
-    ixmlNodeList_free( deviceList );
+            // Check for ContentDirectory service...
+            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 );
+
+                    const char* serviceType =
+                        xml_getChildElementValue( serviceElement,
+                                                  "serviceType" );
+                    if ( !serviceType )
+                        continue;
+
+                    if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
+                                serviceType ) != 0 )
+                        continue;
+
+                    const char* eventSubURL =
+                        xml_getChildElementValue( serviceElement,
+                                                  "eventSubURL" );
+                    if ( !eventSubURL )
+                        continue;
+
+                    const char* controlURL =
+                        xml_getChildElementValue( serviceElement,
+                                                  "controlURL" );
+                    if ( !controlURL )
+                        continue;
+
+                    // Try to subscribe to ContentDirectory service
+
+                    char* url = ( char* ) malloc( strlen( baseURL ) +
+                            strlen( eventSubURL ) + 1 );
+                    if ( url )
+                    {
+                        char* s1 = strdup( baseURL );
+                        char* s2 = strdup( eventSubURL );
+
+                        if ( UpnpResolveURL( s1, s2, url ) ==
+                                UPNP_E_SUCCESS )
+                        {
+                            server->setContentDirectoryEventURL( url );
+                            server->subscribeToContentDirectory();
+                        }
+
+                        free( s1 );
+                        free( s2 );
+                        free( url );
+                    }
+
+                    // Try to browse content directory...
+
+                    url = ( char* ) malloc( strlen( baseURL ) +
+                            strlen( controlURL ) + 1 );
+                    if ( url )
+                    {
+                        char* s1 = strdup( baseURL );
+                        char* s2 = strdup( controlURL );
+
+                        if ( UpnpResolveURL( s1, s2, url ) ==
+                                UPNP_E_SUCCESS )
+                        {
+                            server->setContentDirectoryControlURL( url );
+                            server->fetchContents();
+                        }
+
+                        free( s1 );
+                        free( s2 );
+                        free( url );
+                    }
+               }
+               ixmlNodeList_free( serviceList );
+           }
+       }
+       ixmlNodeList_free( deviceList );
     }
 }
 
-MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
+MediaServer::MediaServer( const char* UDN,
+                          const char* friendlyName,
+                          services_discovery_t* p_sd )
 {
-    _cookie = cookie;
+    _p_sd = p_sd;
 
     _UDN = UDN;
     _friendlyName = friendlyName;
@@ -626,14 +676,6 @@ MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* coo
 
 MediaServer::~MediaServer()
 {
-    if ( _contents )
-    {
-        vlc_object_lock( _cookie->serviceDiscovery->p_sys->p_playlist );
-        playlist_NodeDelete( pl_Get( _cookie->serviceDiscovery ) ,
-                             _playlistNode, true, true );
-        vlc_object_unlock( _cookie->serviceDiscovery->p_sys->p_playlist );
-    }
-
     delete _contents;
 }
 
@@ -675,34 +717,44 @@ void MediaServer::subscribeToContentDirectory()
     const char* url = getContentDirectoryEventURL();
     if ( !url || strcmp( url, "" ) == 0 )
     {
-    msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
-    return;
+        msg_Dbg( _p_sd, "No subscription url set!" );
+        return;
     }
 
     int timeOut = 1810;
     Upnp_SID sid;
 
-    int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
+    int res = UpnpSubscribe( _p_sd->p_sys->clientHandle, url, &timeOut, sid );
 
     if ( res == UPNP_E_SUCCESS )
     {
-    _subscriptionTimeOut = timeOut;
-    memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
+        _subscriptionTimeOut = 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( 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* pObjectID,
+                                           const char* pBrowseFlag,
+                                           const char* pFilter,
+                                           const char* pStartingIndex,
+                                           const char* pRequestedCount,
+                                           const char* pSortCriteria )
 {
     IXML_Document* action = 0;
     IXML_Document* response = 0;
-
     const char* url = getContentDirectoryControlURL();
-    if ( !url || strcmp( url, "" ) == 0 ) { msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" ); return 0; }
+    
+    if ( !url || strcmp( url, "" ) == 0 )
+    {
+        msg_Dbg( _p_sd, "No subscription url set!" );
+        return 0;
+    }
 
     char* ObjectID = strdup( pObjectID );
     char* BrowseFlag = strdup( pBrowseFlag );
@@ -710,40 +762,90 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pB
     char* StartingIndex = strdup( pStartingIndex );
     char* RequestedCount = strdup( pRequestedCount );
     char* SortCriteria = strdup( pSortCriteria );
-
     char* serviceType = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
 
     int res;
 
-    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; }
+    res = UpnpAddToAction( &action, "Browse",
+            serviceType, "ObjectID", ObjectID );
+    
+    if ( res != UPNP_E_SUCCESS ) 
+    {
+        msg_Dbg( _p_sd,
+                 "%s:%d: ERROR: %s", __FILE__, __LINE__,
+                 UpnpGetErrorMessage( res ) );
+        goto browseActionCleanup;
+    }
 
-    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; }
+    res = UpnpAddToAction( &action, "Browse",
+            serviceType, "BrowseFlag", BrowseFlag );
+    
+    if ( res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( 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; }
+    res = UpnpAddToAction( &action, "Browse",
+            serviceType, "Filter", Filter );
+    
+    if ( res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( res ) );
+        goto browseActionCleanup;
+    }
 
-    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; }
+    res = UpnpAddToAction( &action, "Browse",
+            serviceType, "StartingIndex", StartingIndex );
 
-    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; }
+    if ( res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( res ) );
+        goto browseActionCleanup;
+    }
+
+    res = UpnpAddToAction( &action, "Browse",
+            serviceType, "RequestedCount", RequestedCount );
 
-    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 ( res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+                "%s:%d: ERROR: %s", __FILE__, __LINE__,
+                UpnpGetErrorMessage( res ) ); goto browseActionCleanup; }
 
-    res = UpnpSendAction( _cookie->clientHandle,
+    res = UpnpAddToAction( &action, "Browse",
+            serviceType, "SortCriteria", SortCriteria );
+    
+    if ( res != UPNP_E_SUCCESS )
+    {
+        msg_Dbg( _p_sd,
+             "%s:%d: ERROR: %s", __FILE__, __LINE__,
+             UpnpGetErrorMessage( res ) );
+        goto browseActionCleanup;
+    }
+
+    res = UpnpSendAction( _p_sd->p_sys->clientHandle,
               url,
               CONTENT_DIRECTORY_SERVICE_TYPE,
               0,
               action,
               &response );
+    
     if ( 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( res ), url );
+
+        ixmlDocument_free( response );
+        response = 0;
     }
 
  browseActionCleanup:
@@ -764,7 +866,7 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pB
 void MediaServer::fetchContents()
 {
     Container* root = new Container( 0, "0", getFriendlyName() );
-    playlist_t * p_playlist = pl_Get( _cookie->serviceDiscovery );
+    playlist_t * p_playlist = pl_Hold( _p_sd );
     _fetchContents( root );
 
     if ( _contents )
@@ -779,93 +881,140 @@ void MediaServer::fetchContents()
     _contents->setPlaylistNode( _playlistNode );
 
     _buildPlaylist( _contents );
+    pl_Release ( _p_sd );
 }
 
 bool MediaServer::_fetchContents( Container* parent )
 {
-    if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; }
+    if (!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* response = _browseAction( parent->getObjectID(),
+                                      "BrowseDirectChildren",
+                                      "*", "0", "0", "" );
+    if ( !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; }
+    
+    if ( !result )
+    {
+        msg_Dbg( _p_sd,
+                "%s:%d: ERROR! browse() response parsing failed",
+                __FILE__, __LINE__ );
+        return false;
+    }
 
-    IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" );
+    IXML_NodeList* containerNodeList =
+                ixmlDocument_getElementsByTagName( result, "container" );
+    
     if ( containerNodeList )
     {
-    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* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
-        if ( !childCountStr ) continue;
-        int childCount = atoi( childCountStr );
-
-        const char* title = xml_getChildElementValue( containerElement, "dc:title" );
-        if ( !title ) continue;
-
-        const char* resource = xml_getChildElementValue( containerElement, "res" );
-
-        if ( resource && childCount < 1 )
-        {
-        Item* item = new Item( parent, objectID, title, resource );
-        parent->addItem( item );
-        }
-        else
+        for ( unsigned int i = 0;
+                i < ixmlNodeList_length( containerNodeList ); i++ )
         {
-        Container* container = new Container( parent, objectID, title );
-        parent->addContainer( container );
+            IXML_Element* containerElement =
+                  ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
+
+            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* title = xml_getChildElementValue( containerElement,
+                                                          "dc:title" );
+            
+            if ( !title )
+                continue;
+            
+            const char* resource = xml_getChildElementValue( containerElement,
+                                                             "res" );
+
+            if ( resource && childCount < 1 )
+            {
+                Item* item = new Item( parent, objectID, title, resource );
+                parent->addItem( item );
+            }
 
-        if ( childCount > 0 ) _fetchContents( container );
-        }
-    }
+            else
+            {
+                Container* container = new Container( parent, objectID, title );
+                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 );
-
-        const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
-        if ( !objectID ) continue;
-
-        const char* title = xml_getChildElementValue( itemElement, "dc:title" );
-        if ( !title ) continue;
-
-        const char* resource = xml_getChildElementValue( itemElement, "res" );
-        if ( !resource ) continue;
-
-        Item* item = new Item( parent, objectID, title, resource );
-        parent->addItem( item );
-    }
-
-    ixmlNodeList_free( itemNodeList );
+        {
+            IXML_Element* itemElement =
+                        ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
+
+            const char* objectID =
+                        ixmlElement_getAttribute( itemElement, "id" );
+            
+            if ( !objectID )
+                continue;
+
+            const char* title =
+                        xml_getChildElementValue( itemElement, "dc:title" );
+            
+            if ( !title )
+                continue;
+
+            const char* resource =
+                        xml_getChildElementValue( itemElement, "res" );
+            
+            if ( !resource )
+                continue;
+
+            Item* item = new Item( parent, objectID, title, resource );
+            parent->addItem( item );
+        }
+        ixmlNodeList_free( itemNodeList );
     }
 
     ixmlDocument_free( result );
-
     return true;
 }
 
 void MediaServer::_buildPlaylist( Container* parent )
 {
-    playlist_t *p_playlist = pl_Get( _cookie->serviceDiscovery );
+    playlist_t *p_playlist = pl_Hold( _p_sd );
     for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
     {
         Container* container = parent->getContainer( i );
         playlist_item_t* parentNode = parent->getPlaylistNode();
 
         char* title = strdup( container->getTitle() );
-        playlist_item_t* node = playlist_NodeCreate( p_playlist, title, parentNode, 0, NULL );
+        PL_LOCK;
+        playlist_item_t* node = playlist_NodeCreate( p_playlist,
+                                                title, parentNode, 0, NULL );
+        PL_UNLOCK;
         free( title );
 
         container->setPlaylistNode( node );
@@ -877,7 +1026,7 @@ void MediaServer::_buildPlaylist( Container* parent )
         Item* item = parent->getItem( i );
         playlist_item_t* parentNode = parent->getPlaylistNode();
 
-        input_item_t* p_input = input_item_New( _cookie->serviceDiscovery,
+        input_item_t* p_input = input_item_New( _p_sd,
                                                item->getResource(),
                                                item->getTitle() );
         int i_cat;
@@ -887,10 +1036,12 @@ void MediaServer::_buildPlaylist( Container* parent )
                                pl_Unlocked );
         vlc_gc_decref( p_input );
         /* TODO: do this better by storing ids */
-        playlist_item_t *p_node = playlist_ItemGetById( p_playlist, i_cat, false );
+        playlist_item_t *p_node =
+                playlist_ItemGetById( p_playlist, i_cat, false );
         assert( p_node );
         item->setPlaylistNode( p_node );
     }
+    pl_Release( _p_sd );
 }
 
 void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
@@ -906,16 +1057,16 @@ 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];
     }
 }
 
@@ -923,17 +1074,37 @@ bool MediaServerList::addServer( MediaServer* s )
 {
     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;
+    services_discovery_sys_t* p_sys = p_sd->p_sys;
 
+    playlist_item_t *p_node_cat;
+    playlist_item_t *p_node_one;
+    playlist_t* p_playlist = pl_Hold( _p_sd );
+
+    for(int i = 0; i < p_playlist->i_sds; i++ )
+    {
+        if(p_playlist->pp_sds[i]->p_sd == p_sd )
+        {
+            p_node_cat = p_playlist->pp_sds[i]->p_cat;
+            p_node_one = p_playlist->pp_sds[i]->p_one;
+            break;
+        }
+    }
+
+    assert (p_node_cat);
+    assert (p_node_one);
+
+    _list.push_back( s );
+    
     char* name = strdup( s->getFriendlyName() );
-    vlc_object_lock( _cookie->serviceDiscovery->p_sys->p_playlist );
-    playlist_item_t* node = playlist_NodeCreate(
-                                pl_Get( _cookie->serviceDiscovery ), name,
-                                _cookie->serviceDiscovery->p_sys->p_node_cat,
-                                0, NULL );
-    vlc_object_unlock( _cookie->serviceDiscovery->p_sys->p_playlist );
+    PL_LOCK;
+    playlist_item_t* node =
+            playlist_NodeCreate( p_playlist, name, p_node_cat, 0, NULL );
+    PL_UNLOCK;
+    pl_Release( _p_sd );
     free( name );
     s->setPlaylistNode( node );
 
@@ -947,10 +1118,10 @@ MediaServer* MediaServerList::getServer( const char* UDN )
     for ( unsigned int i = 0; i < _list.size(); i++ )
     {
         if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
-    {
-        result = _list[i];
-        break;
-    }
+        {
+            result = _list[i];
+            break;
+        }
     }
 
     return result;
@@ -962,11 +1133,11 @@ MediaServer* MediaServerList::getServerBySID( const char* sid )
 
     for ( unsigned int i = 0; i < _list.size(); i++ )
     {
-    if ( _list[i]->compareSID( sid ) )
-    {
-        server = _list[i];
-        break;
-    }
+        if ( _list[i]->compareSID( sid ) )
+        {
+            server = _list[i];
+            break;
+        }
     }
 
     return server;
@@ -977,17 +1148,18 @@ void MediaServerList::removeServer( const char* UDN )
     MediaServer* server = getServer( UDN );
     if ( !server ) return;
 
-    msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
+    msg_Dbg( _p_sd,
+            "Removing server '%s'", server->getFriendlyName() );
 
     std::vector<MediaServer*>::iterator it;
     for ( it = _list.begin(); it != _list.end(); it++ )
     {
         if ( *it == server )
-    {
-              _list.erase( it );
-        delete server;
-        break;
-    }
+        {
+            _list.erase( it );
+            delete server;
+            break;
+        }
     }
 }
 
@@ -1033,7 +1205,9 @@ playlist_item_t* Item::getPlaylistNode() const
 
 // Container...
 
-Container::Container( Container* parent, const char* objectID, const char* title )
+Container::Container( Container*  parent,
+                      const char* objectID,
+                      const char* title )
 {
     _parent = parent;
 
@@ -1047,12 +1221,12 @@ 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];
     }
 }