upnp_intel.cpp
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:28k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * Upnp_intel.cpp :  UPnP discovery module (Intel SDK)
  3.  *****************************************************************************
  4.  * Copyright (C) 2004-2008 the VideoLAN team
  5.  * $Id: f64a74bb056cddb75698c35b0a8ffe68bad39725 $
  6.  *
  7.  * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
  8.  *          Christian Henz <henz # c-lab.de>
  9.  *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
  10.  *
  11.  * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
  12.  *
  13.  * This program is free software; you can redistribute it and/or modify
  14.  * it under the terms of the GNU General Public License as published by
  15.  * the Free Software Foundation; either version 2 of the License, or
  16.  * (at your option) any later version.
  17.  *
  18.  * This program is distributed in the hope that it will be useful,
  19.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.  * GNU General Public License for more details.
  22.  *
  23.  * You should have received a copy of the GNU General Public License
  24.  * along with this program; if not, write to the Free Software
  25.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  26.  *****************************************************************************/
  27. /*
  28.   TODO: Debug messages: "__FILE__, __LINE__" ok ???, Wrn/Err ???
  29.   TODO: Change names to VLC standard ???
  30. */
  31. #undef PACKAGE_NAME
  32. #ifdef HAVE_CONFIG_H
  33. # include "config.h"
  34. #endif
  35. #include "upnp_intel.hpp"
  36. #include <vlc_plugin.h>
  37. #include <vlc_services_discovery.h>
  38. // Constants
  39. const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
  40. const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
  41. // VLC handle
  42. struct services_discovery_sys_t
  43. {
  44.     UpnpClient_Handle clientHandle;
  45.     MediaServerList* serverList;
  46.     Lockable* callbackLock;
  47. };
  48. // VLC callback prototypes
  49. static int Open( vlc_object_t* );
  50. static void Close( vlc_object_t* );
  51. // Module descriptor
  52. vlc_module_begin();
  53.     set_shortname( "UPnP" );
  54.     set_description( N_( "Universal Plug'n'Play discovery" ) );
  55.     set_category( CAT_PLAYLIST );
  56.     set_subcategory( SUBCAT_PLAYLIST_SD );
  57.     set_capability( "services_discovery", 0 );
  58.     set_callbacks( Open, Close );
  59. vlc_module_end();
  60. // More prototypes...
  61. static int Callback( Upnp_EventType eventType, void* event, void* user_data );
  62. const char* xml_getChildElementValue( IXML_Element* parent,
  63.                                       const char*   tagName );
  64. IXML_Document* parseBrowseResult( IXML_Document* doc );
  65. // VLC callbacks...
  66. static int Open( vlc_object_t *p_this )
  67. {
  68.     int res;
  69.     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
  70.     services_discovery_sys_t *p_sys  = ( services_discovery_sys_t * )
  71.             calloc( 1, sizeof( services_discovery_sys_t ) );
  72.     if(!(p_sd->p_sys = p_sys))
  73.         return VLC_ENOMEM;
  74.     res = UpnpInit( 0, 0 );
  75.     if( res != UPNP_E_SUCCESS )
  76.     {
  77.         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
  78.         free( p_sys );
  79.         return VLC_EGENERIC;
  80.     }
  81.     p_sys->serverList = new MediaServerList( p_sd );
  82.     p_sys->callbackLock = new Lockable();
  83.     res = UpnpRegisterClient( Callback, p_sd, &p_sys->clientHandle );
  84.     if( res != UPNP_E_SUCCESS )
  85.     {
  86.         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
  87.         Close( (vlc_object_t*) p_sd );
  88.         return VLC_EGENERIC;
  89.     }
  90.     res = UpnpSearchAsync( p_sys->clientHandle, 5,
  91.             MEDIA_SERVER_DEVICE_TYPE, p_sd );
  92.     
  93.     if( res != UPNP_E_SUCCESS )
  94.     {
  95.         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
  96.         Close( (vlc_object_t*) p_sd );
  97.         return VLC_EGENERIC;
  98.     }
  99.     return VLC_SUCCESS;
  100. }
  101. static void Close( vlc_object_t *p_this )
  102. {
  103.     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
  104.     UpnpFinish();
  105.     delete p_sd->p_sys->serverList;
  106.     delete p_sd->p_sys->callbackLock;
  107.     free( p_sd->p_sys );
  108. }
  109. // XML utility functions:
  110. // Returns the value of a child element, or 0 on error
  111. const char* xml_getChildElementValue( IXML_Element* parent,
  112.                                       const char*   tagName )
  113. {
  114.     if ( !parent ) return 0;
  115.     if ( !tagName ) return 0;
  116.     char* s = strdup( tagName );
  117.     IXML_NodeList* nodeList = ixmlElement_getElementsByTagName( parent, s );
  118.     free( s );
  119.     if ( !nodeList ) return 0;
  120.     IXML_Node* element = ixmlNodeList_item( nodeList, 0 );
  121.     ixmlNodeList_free( nodeList );
  122.     if ( !element ) return 0;
  123.     IXML_Node* textNode = ixmlNode_getFirstChild( element );
  124.     if ( !textNode ) return 0;
  125.     return ixmlNode_getNodeValue( textNode );
  126. }
  127. // Extracts the result document from a SOAP response
  128. IXML_Document* parseBrowseResult( IXML_Document* doc )
  129. {
  130.     ixmlRelaxParser(1);
  131.     if ( !doc ) return 0;
  132.     IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc,
  133.                                                                    "Result" );
  134.     if ( !resultList ) return 0;
  135.     IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
  136.     ixmlNodeList_free( resultList );
  137.     if ( !resultNode ) return 0;
  138.     IXML_Node* textNode = ixmlNode_getFirstChild( resultNode );
  139.     if ( !textNode ) return 0;
  140.     const char* resultString = ixmlNode_getNodeValue( textNode );
  141.     char* resultXML = strdup( resultString );
  142.     IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
  143.     free( resultXML );
  144.     return browseDoc;
  145. }
  146. // Handles all UPnP events
  147. static int Callback( Upnp_EventType eventType, void* event, void* user_data )
  148. {
  149.     services_discovery_t *p_sd = ( services_discovery_t* ) user_data;
  150.     services_discovery_sys_t* p_sys = p_sd->p_sys;
  151.     Locker locker( p_sys->callbackLock );
  152.     switch( eventType ) {
  153.     case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
  154.     case UPNP_DISCOVERY_SEARCH_RESULT:
  155.     {
  156.         struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
  157.         IXML_Document *descriptionDoc = 0;
  158.         int res;
  159.         res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
  160.         if ( res != UPNP_E_SUCCESS )
  161.         {
  162.             msg_Dbg( p_sd,
  163.                     "%s:%d: Could not download device description!",
  164.                     __FILE__, __LINE__ );
  165.             return res;
  166.         }
  167.         MediaServer::parseDeviceDescription( descriptionDoc,
  168.                 discovery->Location, p_sd );
  169.         ixmlDocument_free( descriptionDoc );
  170.     }
  171.     break;
  172.     case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
  173.     {
  174.         struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
  175.         p_sys->serverList->removeServer( discovery->DeviceId );
  176.     }
  177.     break;
  178.     case UPNP_EVENT_RECEIVED:
  179.     {
  180.         Upnp_Event* e = ( Upnp_Event* )event;
  181.         MediaServer* server = p_sys->serverList->getServerBySID( e->Sid );
  182.         if ( server ) server->fetchContents();
  183.     }
  184.     break;
  185.     case UPNP_EVENT_AUTORENEWAL_FAILED:
  186.     case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
  187.     {
  188.         // Re-subscribe...
  189.         Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
  190.         MediaServer* server = p_sys->serverList->getServerBySID( s->Sid );
  191.         if ( server ) server->subscribeToContentDirectory();
  192.     }
  193.     break;
  194.     case UPNP_EVENT_SUBSCRIBE_COMPLETE:
  195.         msg_Warn( p_sd, "subscription complete" );
  196.         break;
  197.  
  198.     case UPNP_DISCOVERY_SEARCH_TIMEOUT:
  199.         msg_Warn( p_sd, "search timeout" );
  200.         break;
  201.  
  202.     default:
  203.     msg_Dbg( p_sd,
  204.             "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
  205.             __FILE__, __LINE__, eventType );
  206.     break;
  207.     }
  208.     return UPNP_E_SUCCESS;
  209. }
  210. // Class implementations...
  211. // MediaServer...
  212. void MediaServer::parseDeviceDescription( IXML_Document* doc,
  213.                                           const char*    location,
  214.                                           services_discovery_t* p_sd )
  215. {
  216.     if ( !doc )
  217.     { 
  218.         msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ ); 
  219.         return;
  220.     }
  221.     
  222.     if ( !location )
  223.     {
  224.         msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ ); 
  225.         return;
  226.     }
  227.     const char* baseURL = location;
  228.     // Try to extract baseURL
  229.     IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
  230.     if ( !urlList )
  231.     {
  232.         if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
  233.         {
  234.             IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
  235.             if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
  236.         }
  237.         ixmlNodeList_free( urlList );
  238.     }
  239.     // Get devices
  240.     IXML_NodeList* deviceList =
  241.                 ixmlDocument_getElementsByTagName( doc, "device" );
  242.     
  243.     if ( deviceList )
  244.     {
  245.         for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
  246.         {
  247.             IXML_Element* deviceElement =
  248.                    ( IXML_Element* ) ixmlNodeList_item( deviceList, i );
  249.             const char* deviceType = xml_getChildElementValue( deviceElement,
  250.                                                                "deviceType" );
  251.             if ( !deviceType )
  252.             {
  253.                 msg_Dbg( p_sd,
  254.                         "%s:%d: no deviceType!",
  255.                         __FILE__, __LINE__ );
  256.                 continue;
  257.             }
  258.             if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 )
  259.                 continue;
  260.             const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
  261.             if ( !UDN )
  262.             {
  263.                 msg_Dbg( p_sd, "%s:%d: no UDN!",
  264.                         __FILE__, __LINE__ );
  265.                 continue;
  266.             }
  267.             
  268.             if ( p_sd->p_sys->serverList->getServer( UDN ) != 0 )
  269.                 continue;
  270.             const char* friendlyName =
  271.                        xml_getChildElementValue( deviceElement, 
  272.                                                  "friendlyName" );
  273.             
  274.             if ( !friendlyName )
  275.             {
  276.                 msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
  277.                 continue;
  278.             }
  279.             MediaServer* server = new MediaServer( UDN, friendlyName, p_sd );
  280.             
  281.             if ( !p_sd->p_sys->serverList->addServer( server ) )
  282.             {
  283.                 delete server;
  284.                 server = 0;
  285.                 continue;
  286.             }
  287.             // Check for ContentDirectory service...
  288.             IXML_NodeList* serviceList =
  289.                        ixmlElement_getElementsByTagName( deviceElement,
  290.                                                          "service" );
  291.             if ( serviceList )
  292.             {
  293.                 for ( unsigned int j = 0;
  294.                       j < ixmlNodeList_length( serviceList ); j++ )
  295.                 {
  296.                     IXML_Element* serviceElement =
  297.                         ( IXML_Element* ) ixmlNodeList_item( serviceList, j );
  298.                     const char* serviceType =
  299.                         xml_getChildElementValue( serviceElement,
  300.                                                   "serviceType" );
  301.                     if ( !serviceType )
  302.                         continue;
  303.                     if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
  304.                                 serviceType ) != 0 )
  305.                         continue;
  306.                     const char* eventSubURL =
  307.                         xml_getChildElementValue( serviceElement,
  308.                                                   "eventSubURL" );
  309.                     if ( !eventSubURL )
  310.                         continue;
  311.                     const char* controlURL =
  312.                         xml_getChildElementValue( serviceElement,
  313.                                                   "controlURL" );
  314.                     if ( !controlURL )
  315.                         continue;
  316.                     // Try to subscribe to ContentDirectory service
  317.                     char* url = ( char* ) malloc( strlen( baseURL ) +
  318.                             strlen( eventSubURL ) + 1 );
  319.                     if ( url )
  320.                     {
  321.                         char* s1 = strdup( baseURL );
  322.                         char* s2 = strdup( eventSubURL );
  323.                         if ( UpnpResolveURL( s1, s2, url ) ==
  324.                                 UPNP_E_SUCCESS )
  325.                         {
  326.                             server->setContentDirectoryEventURL( url );
  327.                             server->subscribeToContentDirectory();
  328.                         }
  329.                         free( s1 );
  330.                         free( s2 );
  331.                         free( url );
  332.                     }
  333.                     // Try to browse content directory...
  334.                     url = ( char* ) malloc( strlen( baseURL ) +
  335.                             strlen( controlURL ) + 1 );
  336.                     if ( url )
  337.                     {
  338.                         char* s1 = strdup( baseURL );
  339.                         char* s2 = strdup( controlURL );
  340.                         if ( UpnpResolveURL( s1, s2, url ) ==
  341.                                 UPNP_E_SUCCESS )
  342.                         {
  343.                             server->setContentDirectoryControlURL( url );
  344.                             server->fetchContents();
  345.                         }
  346.                         free( s1 );
  347.                         free( s2 );
  348.                         free( url );
  349.                     }
  350.                }
  351.                ixmlNodeList_free( serviceList );
  352.            }
  353.        }
  354.        ixmlNodeList_free( deviceList );
  355.     }
  356. }
  357. MediaServer::MediaServer( const char* UDN,
  358.                           const char* friendlyName,
  359.                           services_discovery_t* p_sd )
  360. {
  361.     _p_sd = p_sd;
  362.     _UDN = UDN;
  363.     _friendlyName = friendlyName;
  364.     _contents = NULL;
  365.     _inputItem = NULL;
  366. }
  367. MediaServer::~MediaServer()
  368. {
  369.     delete _contents;
  370. }
  371. const char* MediaServer::getUDN() const
  372. {
  373.   const char* s = _UDN.c_str();
  374.   return s;
  375. }
  376. const char* MediaServer::getFriendlyName() const
  377. {
  378.     const char* s = _friendlyName.c_str();
  379.     return s;
  380. }
  381. void MediaServer::setContentDirectoryEventURL( const char* url )
  382. {
  383.     _contentDirectoryEventURL = url;
  384. }
  385. const char* MediaServer::getContentDirectoryEventURL() const
  386. {
  387.     const char* s =  _contentDirectoryEventURL.c_str();
  388.     return s;
  389. }
  390. void MediaServer::setContentDirectoryControlURL( const char* url )
  391. {
  392.     _contentDirectoryControlURL = url;
  393. }
  394. const char* MediaServer::getContentDirectoryControlURL() const
  395. {
  396.     return _contentDirectoryControlURL.c_str();
  397. }
  398. void MediaServer::subscribeToContentDirectory()
  399. {
  400.     const char* url = getContentDirectoryEventURL();
  401.     if ( !url || strcmp( url, "" ) == 0 )
  402.     {
  403.         msg_Dbg( _p_sd, "No subscription url set!" );
  404.         return;
  405.     }
  406.     int timeOut = 1810;
  407.     Upnp_SID sid;
  408.     int res = UpnpSubscribe( _p_sd->p_sys->clientHandle, url, &timeOut, sid );
  409.     if ( res == UPNP_E_SUCCESS )
  410.     {
  411.         _subscriptionTimeOut = timeOut;
  412.         memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
  413.     }
  414.     else
  415.     {
  416.         msg_Dbg( _p_sd,
  417.                 "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__,
  418.                 getFriendlyName(), UpnpGetErrorMessage( res ) );
  419.     }
  420. }
  421. IXML_Document* MediaServer::_browseAction( const char* pObjectID,
  422.                                            const char* pBrowseFlag,
  423.                                            const char* pFilter,
  424.                                            const char* pStartingIndex,
  425.                                            const char* pRequestedCount,
  426.                                            const char* pSortCriteria )
  427. {
  428.     IXML_Document* action = 0;
  429.     IXML_Document* response = 0;
  430.     const char* url = getContentDirectoryControlURL();
  431.     
  432.     if ( !url || strcmp( url, "" ) == 0 )
  433.     {
  434.         msg_Dbg( _p_sd, "No subscription url set!" );
  435.         return 0;
  436.     }
  437.     char* ObjectID = strdup( pObjectID );
  438.     char* BrowseFlag = strdup( pBrowseFlag );
  439.     char* Filter = strdup( pFilter );
  440.     char* StartingIndex = strdup( pStartingIndex );
  441.     char* RequestedCount = strdup( pRequestedCount );
  442.     char* SortCriteria = strdup( pSortCriteria );
  443.     char* serviceType = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
  444.     int res;
  445.     res = UpnpAddToAction( &action, "Browse",
  446.             serviceType, "ObjectID", ObjectID );
  447.     
  448.     if ( res != UPNP_E_SUCCESS ) 
  449.     {
  450.         msg_Dbg( _p_sd,
  451.                  "%s:%d: ERROR: %s", __FILE__, __LINE__,
  452.                  UpnpGetErrorMessage( res ) );
  453.         goto browseActionCleanup;
  454.     }
  455.     res = UpnpAddToAction( &action, "Browse",
  456.             serviceType, "BrowseFlag", BrowseFlag );
  457.     
  458.     if ( res != UPNP_E_SUCCESS )
  459.     {
  460.         msg_Dbg( _p_sd,
  461.              "%s:%d: ERROR: %s", __FILE__, __LINE__,
  462.              UpnpGetErrorMessage( res ) );
  463.         goto browseActionCleanup;
  464.     }
  465.     res = UpnpAddToAction( &action, "Browse",
  466.             serviceType, "Filter", Filter );
  467.     
  468.     if ( res != UPNP_E_SUCCESS )
  469.     {
  470.         msg_Dbg( _p_sd,
  471.              "%s:%d: ERROR: %s", __FILE__, __LINE__,
  472.              UpnpGetErrorMessage( res ) );
  473.         goto browseActionCleanup;
  474.     }
  475.     res = UpnpAddToAction( &action, "Browse",
  476.             serviceType, "StartingIndex", StartingIndex );
  477.     if ( res != UPNP_E_SUCCESS )
  478.     {
  479.         msg_Dbg( _p_sd,
  480.              "%s:%d: ERROR: %s", __FILE__, __LINE__,
  481.              UpnpGetErrorMessage( res ) );
  482.         goto browseActionCleanup;
  483.     }
  484.     res = UpnpAddToAction( &action, "Browse",
  485.             serviceType, "RequestedCount", RequestedCount );
  486.     if ( res != UPNP_E_SUCCESS )
  487.     {
  488.         msg_Dbg( _p_sd,
  489.                 "%s:%d: ERROR: %s", __FILE__, __LINE__,
  490.                 UpnpGetErrorMessage( res ) ); goto browseActionCleanup; }
  491.     res = UpnpAddToAction( &action, "Browse",
  492.             serviceType, "SortCriteria", SortCriteria );
  493.     
  494.     if ( res != UPNP_E_SUCCESS )
  495.     {
  496.         msg_Dbg( _p_sd,
  497.              "%s:%d: ERROR: %s", __FILE__, __LINE__,
  498.              UpnpGetErrorMessage( res ) );
  499.         goto browseActionCleanup;
  500.     }
  501.     res = UpnpSendAction( _p_sd->p_sys->clientHandle,
  502.               url,
  503.               CONTENT_DIRECTORY_SERVICE_TYPE,
  504.               0,
  505.               action,
  506.               &response );
  507.     
  508.     if ( res != UPNP_E_SUCCESS )
  509.     {
  510.         msg_Dbg( _p_sd,
  511.                 "%s:%d: ERROR: %s when trying the send() action with URL: %s",
  512.                 __FILE__, __LINE__,
  513.                 UpnpGetErrorMessage( res ), url );
  514.         ixmlDocument_free( response );
  515.         response = 0;
  516.     }
  517.  browseActionCleanup:
  518.     free( ObjectID );
  519.     free( BrowseFlag );
  520.     free( Filter );
  521.     free( StartingIndex );
  522.     free( RequestedCount );
  523.     free( SortCriteria );
  524.     free( serviceType );
  525.     ixmlDocument_free( action );
  526.     return response;
  527. }
  528. void MediaServer::fetchContents()
  529. {
  530.     Container* root = new Container( 0, "0", getFriendlyName() );
  531.     _fetchContents( root );
  532.     _contents = root;
  533.     _contents->setInputItem( _inputItem );
  534.     _buildPlaylist( _contents );
  535. }
  536. bool MediaServer::_fetchContents( Container* parent )
  537. {
  538.     if (!parent)
  539.     {
  540.         msg_Dbg( _p_sd,
  541.                 "%s:%d: parent==NULL", __FILE__, __LINE__ );
  542.         return false;
  543.     }
  544.     IXML_Document* response = _browseAction( parent->getObjectID(),
  545.                                       "BrowseDirectChildren",
  546.                                       "*", "0", "0", "" );
  547.     if ( !response )
  548.     {
  549.         msg_Dbg( _p_sd,
  550.                 "%s:%d: ERROR! No response from browse() action",
  551.                 __FILE__, __LINE__ );
  552.         return false;
  553.     }
  554.     IXML_Document* result = parseBrowseResult( response );
  555.     ixmlDocument_free( response );
  556.     
  557.     if ( !result )
  558.     {
  559.         msg_Dbg( _p_sd,
  560.                 "%s:%d: ERROR! browse() response parsing failed",
  561.                 __FILE__, __LINE__ );
  562.         return false;
  563.     }
  564.     IXML_NodeList* containerNodeList =
  565.                 ixmlDocument_getElementsByTagName( result, "container" );
  566.     
  567.     if ( containerNodeList )
  568.     {
  569.         for ( unsigned int i = 0;
  570.                 i < ixmlNodeList_length( containerNodeList ); i++ )
  571.         {
  572.             IXML_Element* containerElement =
  573.                   ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
  574.             const char* objectID = ixmlElement_getAttribute( containerElement,
  575.                                                              "id" );
  576.             if ( !objectID )
  577.                 continue;
  578.             const char* childCountStr =
  579.                     ixmlElement_getAttribute( containerElement, "childCount" );
  580.             
  581.             if ( !childCountStr )
  582.                 continue;
  583.             
  584.             int childCount = atoi( childCountStr );
  585.             const char* title = xml_getChildElementValue( containerElement,
  586.                                                           "dc:title" );
  587.             
  588.             if ( !title )
  589.                 continue;
  590.             
  591.             const char* resource = xml_getChildElementValue( containerElement,
  592.                                                              "res" );
  593.             if ( resource && childCount < 1 )
  594.             {
  595.                 Item* item = new Item( parent, objectID, title, resource );
  596.                 parent->addItem( item );
  597.             }
  598.             else
  599.             {
  600.                 Container* container = new Container( parent, objectID, title );
  601.                 parent->addContainer( container );
  602.                 if ( childCount > 0 )
  603.                     _fetchContents( container );
  604.             }
  605.         }
  606.         ixmlNodeList_free( containerNodeList );
  607.     }
  608.     IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result,
  609.                                                                      "item" );
  610.     if ( itemNodeList )
  611.     {
  612.         for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
  613.         {
  614.             IXML_Element* itemElement =
  615.                         ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
  616.             const char* objectID =
  617.                         ixmlElement_getAttribute( itemElement, "id" );
  618.             
  619.             if ( !objectID )
  620.                 continue;
  621.             const char* title =
  622.                         xml_getChildElementValue( itemElement, "dc:title" );
  623.             
  624.             if ( !title )
  625.                 continue;
  626.             const char* resource =
  627.                         xml_getChildElementValue( itemElement, "res" );
  628.             
  629.             if ( !resource )
  630.                 continue;
  631.             Item* item = new Item( parent, objectID, title, resource );
  632.             parent->addItem( item );
  633.         }
  634.         ixmlNodeList_free( itemNodeList );
  635.     }
  636.     ixmlDocument_free( result );
  637.     return true;
  638. }
  639. void MediaServer::_buildPlaylist( Container* parent )
  640. {
  641.     for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
  642.     {
  643.         Container* container = parent->getContainer( i );
  644.         input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop", parent->getTitle() ); 
  645.         input_item_AddSubItem( parent->getInputItem(), p_input_item );
  646.         container->setInputItem( p_input_item );
  647.         _buildPlaylist( container );
  648.     }
  649.     for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
  650.     {
  651.         Item* item = parent->getItem( i );
  652.         input_item_t* p_input_item = input_item_New( _p_sd,
  653.                                                item->getResource(),
  654.                                                item->getTitle() );
  655.         assert( p_input_item );
  656.         input_item_AddSubItem( parent->getInputItem(), p_input_item );
  657.         item->setInputItem( p_input_item );
  658.     }
  659. }
  660. void MediaServer::setInputItem( input_item_t* p_input_item )
  661. {
  662.     if(_inputItem == p_input_item)
  663.         return;
  664.     if(_inputItem)
  665.         vlc_gc_decref( _inputItem );
  666.     vlc_gc_incref( p_input_item );
  667.     _inputItem = p_input_item;
  668. }
  669. bool MediaServer::compareSID( const char* sid )
  670. {
  671.     return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
  672. }
  673. // MediaServerList...
  674. MediaServerList::MediaServerList( services_discovery_t* p_sd )
  675. {
  676.     _p_sd = p_sd;
  677. }
  678. MediaServerList::~MediaServerList()
  679. {
  680.     for ( unsigned int i = 0; i < _list.size(); i++ )
  681.     {
  682.         delete _list[i];
  683.     }
  684. }
  685. bool MediaServerList::addServer( MediaServer* s )
  686. {
  687.     input_item_t* p_input_item = NULL;
  688.     if ( getServer( s->getUDN() ) != 0 ) return false;
  689.     msg_Dbg( _p_sd, "Adding server '%s'",
  690.             s->getFriendlyName() );
  691.     services_discovery_t* p_sd = _p_sd;
  692.     p_input_item = input_item_New( p_sd, "vlc://nop", s->getFriendlyName() ); 
  693.     s->setInputItem( p_input_item );
  694.     services_discovery_AddItem( p_sd, p_input_item, NULL );
  695.     _list.push_back( s );
  696.     return true;
  697. }
  698. MediaServer* MediaServerList::getServer( const char* UDN )
  699. {
  700.     MediaServer* result = 0;
  701.     for ( unsigned int i = 0; i < _list.size(); i++ )
  702.     {
  703.         if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
  704.         {
  705.             result = _list[i];
  706.             break;
  707.         }
  708.     }
  709.     return result;
  710. }
  711. MediaServer* MediaServerList::getServerBySID( const char* sid )
  712. {
  713.     MediaServer* server = 0;
  714.     for ( unsigned int i = 0; i < _list.size(); i++ )
  715.     {
  716.         if ( _list[i]->compareSID( sid ) )
  717.         {
  718.             server = _list[i];
  719.             break;
  720.         }
  721.     }
  722.     return server;
  723. }
  724. void MediaServerList::removeServer( const char* UDN )
  725. {
  726.     MediaServer* server = getServer( UDN );
  727.     if ( !server ) return;
  728.     msg_Dbg( _p_sd,
  729.             "Removing server '%s'", server->getFriendlyName() );
  730.     std::vector<MediaServer*>::iterator it;
  731.     for ( it = _list.begin(); it != _list.end(); it++ )
  732.     {
  733.         if ( *it == server )
  734.         {
  735.             _list.erase( it );
  736.             delete server;
  737.             break;
  738.         }
  739.     }
  740. }
  741. // Item...
  742. Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
  743. {
  744.     _parent = parent;
  745.     _objectID = objectID;
  746.     _title = title;
  747.     _resource = resource;
  748.     _inputItem = NULL;
  749. }
  750. Item::~Item()
  751. {
  752.     if(_inputItem)
  753.         vlc_gc_decref( _inputItem );
  754. }
  755. const char* Item::getObjectID() const
  756. {
  757.     return _objectID.c_str();
  758. }
  759. const char* Item::getTitle() const
  760. {
  761.     return _title.c_str();
  762. }
  763. const char* Item::getResource() const
  764. {
  765.     return _resource.c_str();
  766. }
  767. void Item::setInputItem( input_item_t* p_input_item )
  768. {
  769.     if(_inputItem == p_input_item)
  770.         return;
  771.     if(_inputItem)
  772.         vlc_gc_decref( _inputItem );
  773.     vlc_gc_incref( p_input_item );
  774.     _inputItem = p_input_item;
  775. }
  776. input_item_t* Item::getInputItem() const
  777. {
  778.     return _inputItem;
  779. }
  780. // Container...
  781. Container::Container( Container*  parent,
  782.                       const char* objectID,
  783.                       const char* title )
  784. {
  785.     _parent = parent;
  786.     _objectID = objectID;
  787.     _title = title;
  788.     _inputItem = NULL;
  789. }
  790. Container::~Container()
  791. {
  792.     for ( unsigned int i = 0; i < _containers.size(); i++ )
  793.     {
  794.         delete _containers[i];
  795.     }
  796.     for ( unsigned int i = 0; i < _items.size(); i++ )
  797.     {
  798.         delete _items[i];
  799.     }
  800.     if(_inputItem )
  801.         vlc_gc_decref( _inputItem );
  802. }
  803. void Container::addItem( Item* item )
  804. {
  805.     _items.push_back( item );
  806. }
  807. void Container::addContainer( Container* container )
  808. {
  809.     _containers.push_back( container );
  810. }
  811. const char* Container::getObjectID() const
  812. {
  813.     return _objectID.c_str();
  814. }
  815. const char* Container::getTitle() const
  816. {
  817.     return _title.c_str();
  818. }
  819. unsigned int Container::getNumItems() const
  820. {
  821.     return _items.size();
  822. }
  823. unsigned int Container::getNumContainers() const
  824. {
  825.     return _containers.size();
  826. }
  827. Item* Container::getItem( unsigned int i ) const
  828. {
  829.     if ( i < _items.size() ) return _items[i];
  830.     return 0;
  831. }
  832. Container* Container::getContainer( unsigned int i ) const
  833. {
  834.     if ( i < _containers.size() ) return _containers[i];
  835.     return 0;
  836. }
  837. Container* Container::getParent()
  838. {
  839.     return _parent;
  840. }
  841. void Container::setInputItem( input_item_t* p_input_item )
  842. {
  843.     if(_inputItem == p_input_item)
  844.         return;
  845.     if(_inputItem)
  846.         vlc_gc_decref( _inputItem );
  847.     vlc_gc_incref( p_input_item );
  848.     _inputItem = p_input_item;
  849. }
  850. input_item_t* Container::getInputItem() const
  851. {
  852.     return _inputItem;
  853. }