]> git.sesse.net Git - vlc/blobdiff - modules/services_discovery/upnp_intel.cpp
services_discovery: implement SD categories and use in Qt interface
[vlc] / modules / services_discovery / upnp_intel.cpp
index 297221bc101c482e75443ce9054f17206225b987..a936de2ee93e6a84c29810470d62f53e7597e178 100644 (file)
 /*
   \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
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 
-#include <vlc_common.h>
+#include "upnp_intel.hpp"
+
 #include <vlc_plugin.h>
-#include <vlc_playlist.h>
+#include <vlc_services_discovery.h>
 
-// Constants
 
+// 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;
-
 // VLC handle
-
 struct services_discovery_sys_t
 {
     UpnpClient_Handle clientHandle;
     MediaServerList* serverList;
+    vlc_mutex_t callbackLock;
 };
 
-// Class definitions...
-
-class Lockable
-{
-public:
-
-    Lockable()
-    {
-        vlc_mutex_init( &_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,
-                                        services_discovery_t* p_sd );
-
-    MediaServer( const char* UDN,
-                 const char* friendlyName,
-                 services_discovery_t* p_sd );
-    
-    ~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* );
-
-    services_discovery_t* _p_sd;
-
-    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( services_discovery_t* p_sd );
-    ~MediaServerList();
-
-    bool addServer( MediaServer* s );
-    void removeServer( const char* UDN );
-
-    MediaServer* getServer( const char* UDN );
-    MediaServer* getServerBySID( const char* );
-
-private:
-
-    services_discovery_t* _p_sd;
-
-    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
-{
-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;
-};
-
-
 // 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", N_("Universal Plug'n'Play"), SD_CAT_LAN)
 
 // 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 );
+    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* user_data );
 
 const char* xml_getChildElementValue( IXML_Element* parent,
@@ -287,21 +90,21 @@ static int Open( vlc_object_t *p_this )
     services_discovery_sys_t *p_sys  = ( services_discovery_sys_t * )
             calloc( 1, sizeof( services_discovery_sys_t ) );
 
-    p_sd->p_sys = p_sys;
-
-    services_discovery_SetLocalizedName( p_sd, _("UPnP devices") );
+    if(!(p_sd->p_sys = p_sys))
+        return VLC_ENOMEM;
 
     res = UpnpInit( 0, 0 );
     if( res != UPNP_E_SUCCESS )
     {
         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
+        free( p_sys );
         return VLC_EGENERIC;
     }
 
     p_sys->serverList = new MediaServerList( p_sd );
-    CallbackLock = new Lockable();
+    vlc_mutex_init( &p_sys->callbackLock );
 
-    res = UpnpRegisterClient( Callback, p_sys, &p_sys->clientHandle );
+    res = UpnpRegisterClient( Callback, p_sd, &p_sys->clientHandle );
     if( res != UPNP_E_SUCCESS )
     {
         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
@@ -311,7 +114,7 @@ static int Open( vlc_object_t *p_this )
 
     res = UpnpSearchAsync( p_sys->clientHandle, 5,
             MEDIA_SERVER_DEVICE_TYPE, p_sd );
-    
+
     if( res != UPNP_E_SUCCESS )
     {
         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
@@ -328,25 +131,11 @@ static void Close( vlc_object_t *p_this )
 
     UpnpFinish();
     delete p_sd->p_sys->serverList;
-    delete CallbackLock;
+    vlc_mutex_destroy( &p_sd->p_sys->callbackLock );
 
     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
@@ -406,10 +195,9 @@ IXML_Document* parseBrowseResult( IXML_Document* doc )
 // Handles all UPnP events
 static int Callback( Upnp_EventType eventType, void* event, void* user_data )
 {
-    Locker locker( CallbackLock );
-
     services_discovery_t *p_sd = ( services_discovery_t* ) user_data;
     services_discovery_sys_t* p_sys = p_sd->p_sys;
+    vlc_mutex_locker locker( &p_sys->callbackLock );
 
     switch( eventType ) {
 
@@ -469,11 +257,11 @@ static int Callback( Upnp_EventType eventType, void* event, void* user_data )
     case UPNP_EVENT_SUBSCRIBE_COMPLETE:
         msg_Warn( p_sd, "subscription complete" );
         break;
+
     case UPNP_DISCOVERY_SEARCH_TIMEOUT:
         msg_Warn( p_sd, "search timeout" );
         break;
+
     default:
     msg_Dbg( p_sd,
             "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
@@ -494,14 +282,14 @@ void MediaServer::parseDeviceDescription( IXML_Document* doc,
                                           services_discovery_t* p_sd )
 {
     if ( !doc )
-    { 
-        msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ ); 
+    {
+        msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
         return;
     }
-    
+
     if ( !location )
     {
-        msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ ); 
+        msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
         return;
     }
 
@@ -526,7 +314,7 @@ void MediaServer::parseDeviceDescription( IXML_Document* doc,
 
     IXML_NodeList* deviceList =
                 ixmlDocument_getElementsByTagName( doc, "device" );
-    
+
     if ( deviceList )
     {
         for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
@@ -554,14 +342,14 @@ void MediaServer::parseDeviceDescription( IXML_Document* doc,
                         __FILE__, __LINE__ );
                 continue;
             }
-            
+
             if ( p_sd->p_sys->serverList->getServer( UDN ) != 0 )
                 continue;
 
             const char* friendlyName =
-                       xml_getChildElementValue( deviceElement, 
+                       xml_getChildElementValue( deviceElement,
                                                  "friendlyName" );
-            
+
             if ( !friendlyName )
             {
                 msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
@@ -569,7 +357,7 @@ void MediaServer::parseDeviceDescription( IXML_Document* doc,
             }
 
             MediaServer* server = new MediaServer( UDN, friendlyName, p_sd );
-            
+
             if ( !p_sd->p_sys->serverList->addServer( server ) )
             {
 
@@ -670,8 +458,8 @@ MediaServer::MediaServer( const char* UDN,
     _UDN = UDN;
     _friendlyName = friendlyName;
 
-    _contents = 0;
-    _playlistNode = 0;
+    _contents = NULL;
+    _inputItem = NULL;
 }
 
 MediaServer::~MediaServer()
@@ -749,7 +537,7 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID,
     IXML_Document* action = 0;
     IXML_Document* response = 0;
     const char* url = getContentDirectoryControlURL();
-    
+
     if ( !url || strcmp( url, "" ) == 0 )
     {
         msg_Dbg( _p_sd, "No subscription url set!" );
@@ -768,8 +556,8 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID,
 
     res = UpnpAddToAction( &action, "Browse",
             serviceType, "ObjectID", ObjectID );
-    
-    if ( res != UPNP_E_SUCCESS ) 
+
+    if ( res != UPNP_E_SUCCESS )
     {
         msg_Dbg( _p_sd,
                  "%s:%d: ERROR: %s", __FILE__, __LINE__,
@@ -779,7 +567,7 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID,
 
     res = UpnpAddToAction( &action, "Browse",
             serviceType, "BrowseFlag", BrowseFlag );
-    
+
     if ( res != UPNP_E_SUCCESS )
     {
         msg_Dbg( _p_sd,
@@ -790,7 +578,7 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID,
 
     res = UpnpAddToAction( &action, "Browse",
             serviceType, "Filter", Filter );
-    
+
     if ( res != UPNP_E_SUCCESS )
     {
         msg_Dbg( _p_sd,
@@ -821,7 +609,7 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID,
 
     res = UpnpAddToAction( &action, "Browse",
             serviceType, "SortCriteria", SortCriteria );
-    
+
     if ( res != UPNP_E_SUCCESS )
     {
         msg_Dbg( _p_sd,
@@ -836,7 +624,7 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID,
               0,
               action,
               &response );
-    
+
     if ( res != UPNP_E_SUCCESS )
     {
         msg_Dbg( _p_sd,
@@ -866,22 +654,12 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID,
 void MediaServer::fetchContents()
 {
     Container* root = new Container( 0, "0", getFriendlyName() );
-    playlist_t * p_playlist = pl_Hold( _p_sd );
     _fetchContents( root );
 
-    if ( _contents )
-    {
-        PL_LOCK;
-        playlist_NodeEmpty( p_playlist, _playlistNode, true );
-        PL_UNLOCK;
-        delete _contents;
-    }
-
     _contents = root;
-    _contents->setPlaylistNode( _playlistNode );
+    _contents->setInputItem( _inputItem );
 
-    _buildPlaylist( _contents );
-    pl_Release ( _p_sd );
+    _buildPlaylist( _contents, NULL );
 }
 
 bool MediaServer::_fetchContents( Container* parent )
@@ -906,7 +684,7 @@ bool MediaServer::_fetchContents( Container* parent )
 
     IXML_Document* result = parseBrowseResult( response );
     ixmlDocument_free( response );
-    
+
     if ( !result )
     {
         msg_Dbg( _p_sd,
@@ -917,7 +695,7 @@ bool MediaServer::_fetchContents( Container* parent )
 
     IXML_NodeList* containerNodeList =
                 ixmlDocument_getElementsByTagName( result, "container" );
-    
+
     if ( containerNodeList )
     {
         for ( unsigned int i = 0;
@@ -933,17 +711,17 @@ bool MediaServer::_fetchContents( Container* parent )
 
             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" );
 
@@ -976,19 +754,19 @@ bool MediaServer::_fetchContents( Container* parent )
 
             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;
 
@@ -1002,51 +780,50 @@ bool MediaServer::_fetchContents( Container* parent )
     return true;
 }
 
-void MediaServer::_buildPlaylist( Container* parent )
+void MediaServer::_buildPlaylist( Container* parent, input_item_node_t *p_input_node )
 {
-    playlist_t *p_playlist = pl_Hold( _p_sd );
+    bool send = p_input_node == NULL;
+    if( send )
+        p_input_node = input_item_node_Create( parent->getInputItem() );
+
     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() );
-        PL_LOCK;
-        playlist_item_t* node = playlist_NodeCreate( p_playlist,
-                                                title, parentNode, 0, NULL );
-        PL_UNLOCK;
-        free( title );
+        input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop", parent->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++ )
     {
         Item* item = parent->getItem( i );
-        playlist_item_t* parentNode = parent->getPlaylistNode();
 
-        input_item_t* p_input = input_item_New( _p_sd,
+        input_item_t* p_input_item = input_item_New( _p_sd,
                                                item->getResource(),
                                                item->getTitle() );
-        int i_cat;
-        /* FIXME: playlist_AddInput() can fail */
-        playlist_BothAddInput( p_playlist, p_input, parentNode,
-                               PLAYLIST_APPEND, PLAYLIST_END, &i_cat, NULL,
-                               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 );
-        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 );
     }
-    pl_Release( _p_sd );
+
+    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 )
@@ -1072,41 +849,20 @@ MediaServerList::~MediaServerList()
 
 bool MediaServerList::addServer( MediaServer* s )
 {
+    input_item_t* p_input_item = NULL;
     if ( getServer( s->getUDN() ) != 0 ) return false;
 
     msg_Dbg( _p_sd, "Adding server '%s'",
             s->getFriendlyName() );
 
     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 );
+    p_input_item = input_item_New( p_sd, "vlc://nop", s->getFriendlyName() );
+    s->setInputItem( p_input_item );
 
-    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);
+    services_discovery_AddItem( p_sd, p_input_item, NULL );
 
     _list.push_back( s );
-    
-    char* name = strdup( s->getFriendlyName() );
-    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 );
 
     return true;
 }
@@ -1174,7 +930,13 @@ Item::Item( Container* parent, const char* objectID, const char* title, const ch
     _title = title;
     _resource = resource;
 
-    _playlistNode = 0;
+    _inputItem = NULL;
+}
+
+Item::~Item()
+{
+    if(_inputItem)
+        vlc_gc_decref( _inputItem );
 }
 
 const char* Item::getObjectID() const
@@ -1192,14 +954,21 @@ 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;
 }
 
 
@@ -1214,7 +983,7 @@ Container::Container( Container*  parent,
     _objectID = objectID;
     _title = title;
 
-    _playlistNode = 0;
+    _inputItem = NULL;
 }
 
 Container::~Container()
@@ -1228,6 +997,9 @@ Container::~Container()
     {
         delete _items[i];
     }
+
+    if(_inputItem )
+        vlc_gc_decref( _inputItem );
 }
 
 void Container::addItem( Item* item )
@@ -1272,12 +1044,24 @@ Container* Container::getContainer( unsigned int i ) const
     return 0;
 }
 
-void Container::setPlaylistNode( playlist_item_t* node )
+Container* Container::getParent()
 {
-    _playlistNode = node;
+    return _parent;
+}
+
+void Container::setInputItem( input_item_t* p_input_item )
+{
+    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;
 }