1 /*****************************************************************************
2 * Upnp_intell.cpp : UPnP discovery module (Intel SDK)
3 *****************************************************************************
4 * Copyright (C) 2004-2006 the VideoLAN team
7 * Authors: RĂ©mi Denis-Courmont <rem # videolan.org> (original plugin)
8 * Christian Henz <henz # c-lab.de>
10 * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
28 \TODO: Debug messages: "__FILE__, __LINE__" ok ???, Wrn/Err ???
29 \TODO: Change names to VLC standard ???
36 #include <upnp/upnp.h>
37 #include <upnp/upnptools.h>
41 #include <vlc_playlist.h>
42 #include "vlc_strings.h"
47 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
48 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
54 class MediaServerList;
58 // Cookie that is passed to the callback
62 services_discovery_t* serviceDiscovery;
63 UpnpClient_Handle clientHandle;
64 MediaServerList* serverList;
68 // Class definitions...
76 vlc_mutex_init( c->serviceDiscovery, &_mutex );
81 vlc_mutex_destroy( &_mutex );
84 void lock() { vlc_mutex_lock( &_mutex ); }
85 void unlock() { vlc_mutex_unlock( &_mutex ); }
116 static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
118 MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
121 const char* getUDN() const;
122 const char* getFriendlyName() const;
124 void setContentDirectoryEventURL( const char* url );
125 const char* getContentDirectoryEventURL() const;
127 void setContentDirectoryControlURL( const char* url );
128 const char* getContentDirectoryControlURL() const;
130 void subscribeToContentDirectory();
131 void fetchContents();
133 void setPlaylistNode( playlist_item_t* node );
135 bool compareSID( const char* sid );
139 bool _fetchContents( Container* parent );
140 void _buildPlaylist( Container* container );
141 IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
145 Container* _contents;
146 playlist_item_t* _playlistNode;
149 std::string _friendlyName;
151 std::string _contentDirectoryEventURL;
152 std::string _contentDirectoryControlURL;
154 int _subscriptionTimeOut;
155 Upnp_SID _subscriptionID;
159 class MediaServerList
163 MediaServerList( Cookie* cookie );
166 bool addServer( MediaServer* s );
167 void removeServer( const char* UDN );
169 MediaServer* getServer( const char* UDN );
170 MediaServer* getServerBySID( const char* );
176 std::vector<MediaServer*> _list;
184 Item( Container* parent, const char* objectID, const char* title, const char* resource );
186 const char* getObjectID() const;
187 const char* getTitle() const;
188 const char* getResource() const;
190 void setPlaylistNode( playlist_item_t* node );
191 playlist_item_t* getPlaylistNode() const ;
195 playlist_item_t* _playlistNode;
198 std::string _objectID;
200 std::string _resource;
208 Container( Container* parent, const char* objectID, const char* title );
211 void addItem( Item* item );
212 void addContainer( Container* container );
214 const char* getObjectID() const;
215 const char* getTitle() const;
217 unsigned int getNumItems() const;
218 unsigned int getNumContainers() const;
220 Item* getItem( unsigned int i ) const;
221 Container* getContainer( unsigned int i ) const;
223 void setPlaylistNode( playlist_item_t* node );
224 playlist_item_t* getPlaylistNode() const;
228 playlist_item_t* _playlistNode;
232 std::string _objectID;
234 std::vector<Item*> _items;
235 std::vector<Container*> _containers;
239 // VLC callback prototypes
241 static int Open( vlc_object_t* );
242 static void Close( vlc_object_t* );
243 static void Run( services_discovery_t *p_sd );
248 set_shortname( "UPnP" );
249 set_description( _( "Universal Plug'n'Play discovery ( Intel SDK )" ) );
250 set_category( CAT_PLAYLIST );
251 set_subcategory( SUBCAT_PLAYLIST_SD );
252 set_capability( "services_discovery", 0 );
253 set_callbacks( Open, Close );
257 // More prototypes...
259 static Lockable* CallbackLock;
260 static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
262 const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
263 IXML_Document* parseBrowseResult( IXML_Document* doc );
268 static int Open( vlc_object_t *p_this )
270 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
273 /* Create our playlist node */
274 services_discovery_SetLocalizedName( p_sd, _("Devices") );
279 static void Close( vlc_object_t *p_this )
283 static void Run( services_discovery_t* p_sd )
287 res = UpnpInit( 0, 0 );
288 if( res != UPNP_E_SUCCESS )
290 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
295 cookie.serviceDiscovery = p_sd;
296 cookie.serverList = new MediaServerList( &cookie );
298 CallbackLock = new Lockable( &cookie );
300 res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle );
301 if( res != UPNP_E_SUCCESS )
303 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
307 res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie );
308 if( res != UPNP_E_SUCCESS )
310 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
314 msg_Dbg( p_sd, "UPnP discovery started" );
315 while( !p_sd->b_die )
320 msg_Dbg( p_sd, "UPnP discovery stopped" );
324 delete cookie.serverList;
329 // XML utility functions:
331 // Returns the value of a child element, or 0 on error
332 const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
334 if ( !parent ) return 0;
335 if ( !tagName ) return 0;
337 char* s = strdup( tagName );
338 IXML_NodeList* nodeList = ixmlElement_getElementsByTagName( parent, s );
340 if ( !nodeList ) return 0;
342 IXML_Node* element = ixmlNodeList_item( nodeList, 0 );
343 ixmlNodeList_free( nodeList );
344 if ( !element ) return 0;
346 IXML_Node* textNode = ixmlNode_getFirstChild( element );
347 if ( !textNode ) return 0;
349 return ixmlNode_getNodeValue( textNode );
352 // Extracts the result document from a SOAP response
353 IXML_Document* parseBrowseResult( IXML_Document* doc )
355 if ( !doc ) return 0;
357 IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
358 if ( !resultList ) return 0;
360 IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
362 ixmlNodeList_free( resultList );
364 if ( !resultNode ) return 0;
366 IXML_Node* textNode = ixmlNode_getFirstChild( resultNode );
367 if ( !textNode ) return 0;
369 const char* resultString = ixmlNode_getNodeValue( textNode );
370 char* resultXML = strdup( resultString );
372 resolve_xml_special_chars( resultXML );
374 IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
382 // Handles all UPnP events
383 static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
385 Locker locker( CallbackLock );
387 Cookie* cookie = ( Cookie* )pCookie;
389 switch( eventType ) {
391 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
392 case UPNP_DISCOVERY_SEARCH_RESULT:
394 struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
396 IXML_Document *descriptionDoc = 0;
399 res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
400 if ( res != UPNP_E_SUCCESS )
402 msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
406 MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
408 ixmlDocument_free( descriptionDoc );
412 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
414 struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
416 cookie->serverList->removeServer( discovery->DeviceId );
420 case UPNP_EVENT_RECEIVED:
422 Upnp_Event* e = ( Upnp_Event* )event;
424 MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
425 if ( server ) server->fetchContents();
429 case UPNP_EVENT_AUTORENEWAL_FAILED:
430 case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
434 Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
436 MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
437 if ( server ) server->subscribeToContentDirectory();
441 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
442 msg_Warn( cookie->serviceDiscovery, "subscription complete" );
445 case UPNP_DISCOVERY_SEARCH_TIMEOUT:
446 msg_Warn( cookie->serviceDiscovery, "search timeout" );
450 msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
454 return UPNP_E_SUCCESS;
458 // Class implementations...
462 void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
464 if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
465 if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
467 const char* baseURL = location;
469 // Try to extract baseURL
471 IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
474 if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
476 IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
477 if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
480 ixmlNodeList_free( urlList );
485 IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
489 for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
491 IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
493 const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
494 if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
495 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
497 const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
498 if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
499 if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
501 const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
502 if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
504 MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
505 if ( !cookie->serverList->addServer( server ) ) {
512 // Check for ContentDirectory service...
514 IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
517 for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
519 IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
521 const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
522 if ( !serviceType ) continue;
523 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
525 const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
526 if ( !eventSubURL ) continue;
528 const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
529 if ( !controlURL ) continue;
531 // Try to subscribe to ContentDirectory service
533 char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
536 char* s1 = strdup( baseURL );
537 char* s2 = strdup( eventSubURL );
539 if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
541 // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
543 server->setContentDirectoryEventURL( url );
544 server->subscribeToContentDirectory();
552 // Try to browse content directory...
554 url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
557 char* s1 = strdup( baseURL );
558 char* s2 = strdup( controlURL );
560 if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
562 // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
564 server->setContentDirectoryControlURL( url );
565 server->fetchContents();
574 ixmlNodeList_free( serviceList );
578 ixmlNodeList_free( deviceList );
582 MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
587 _friendlyName = friendlyName;
593 MediaServer::~MediaServer()
597 playlist_NodeDelete( pl_Get( _cookie->serviceDiscovery ) ,
598 _playlistNode, VLC_TRUE, VLC_TRUE );
604 const char* MediaServer::getUDN() const
606 const char* s = _UDN.c_str();
610 const char* MediaServer::getFriendlyName() const
612 const char* s = _friendlyName.c_str();
616 void MediaServer::setContentDirectoryEventURL( const char* url )
618 _contentDirectoryEventURL = url;
621 const char* MediaServer::getContentDirectoryEventURL() const
623 const char* s = _contentDirectoryEventURL.c_str();
627 void MediaServer::setContentDirectoryControlURL( const char* url )
629 _contentDirectoryControlURL = url;
632 const char* MediaServer::getContentDirectoryControlURL() const
634 return _contentDirectoryControlURL.c_str();
637 void MediaServer::subscribeToContentDirectory()
639 const char* url = getContentDirectoryEventURL();
640 if ( !url || strcmp( url, "" ) == 0 )
642 msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
649 int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
651 if ( res == UPNP_E_SUCCESS )
653 _subscriptionTimeOut = timeOut;
654 memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
658 msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) );
662 IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter,
663 const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria )
665 IXML_Document* action = 0;
666 IXML_Document* response = 0;
668 const char* url = getContentDirectoryControlURL();
669 if ( !url || strcmp( url, "" ) == 0 ) { msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" ); return 0; }
671 char* ObjectID = strdup( pObjectID );
672 char* BrowseFlag = strdup( pBrowseFlag );
673 char* Filter = strdup( pFilter );
674 char* StartingIndex = strdup( pStartingIndex );
675 char* RequestedCount = strdup( pRequestedCount );
676 char* SortCriteria = strdup( pSortCriteria );
678 char* serviceType = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
682 res = UpnpAddToAction( &action, "Browse", serviceType, "ObjectID", ObjectID );
683 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
685 res = UpnpAddToAction( &action, "Browse", serviceType, "BrowseFlag", BrowseFlag );
686 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
688 res = UpnpAddToAction( &action, "Browse", serviceType, "Filter", Filter );
689 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
691 res = UpnpAddToAction( &action, "Browse", serviceType, "StartingIndex", StartingIndex );
692 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
694 res = UpnpAddToAction( &action, "Browse", serviceType, "RequestedCount", RequestedCount );
695 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
697 res = UpnpAddToAction( &action, "Browse", serviceType, "SortCriteria", SortCriteria );
698 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
700 res = UpnpSendAction( _cookie->clientHandle,
702 CONTENT_DIRECTORY_SERVICE_TYPE,
706 if ( res != UPNP_E_SUCCESS )
708 msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
709 ixmlDocument_free( response );
718 free( StartingIndex );
719 free( RequestedCount );
720 free( SortCriteria );
724 ixmlDocument_free( action );
728 void MediaServer::fetchContents()
730 Container* root = new Container( 0, "0", getFriendlyName() );
731 playlist_t * p_playlist = pl_Get( _cookie->serviceDiscovery );
732 _fetchContents( root );
737 playlist_NodeEmpty( p_playlist, _playlistNode, VLC_TRUE );
743 _contents->setPlaylistNode( _playlistNode );
745 _buildPlaylist( _contents );
748 bool MediaServer::_fetchContents( Container* parent )
750 if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; }
752 IXML_Document* response = _browseAction( parent->getObjectID(), "BrowseDirectChildren", "*", "0", "0", "" );
753 if ( !response ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
755 IXML_Document* result = parseBrowseResult( response );
756 ixmlDocument_free( response );
757 if ( !result ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
759 IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" );
760 if ( containerNodeList )
762 for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
764 IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
766 const char* objectID = ixmlElement_getAttribute( containerElement, "id" );
767 if ( !objectID ) continue;
769 const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
770 if ( !childCountStr ) continue;
771 int childCount = atoi( childCountStr );
773 const char* title = xml_getChildElementValue( containerElement, "dc:title" );
774 if ( !title ) continue;
776 const char* resource = xml_getChildElementValue( containerElement, "res" );
778 if ( resource && childCount < 1 )
780 Item* item = new Item( parent, objectID, title, resource );
781 parent->addItem( item );
785 Container* container = new Container( parent, objectID, title );
786 parent->addContainer( container );
788 if ( childCount > 0 ) _fetchContents( container );
792 ixmlNodeList_free( containerNodeList );
795 IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result, "item" );
798 for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
800 IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
802 const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
803 if ( !objectID ) continue;
805 const char* title = xml_getChildElementValue( itemElement, "dc:title" );
806 if ( !title ) continue;
808 const char* resource = xml_getChildElementValue( itemElement, "res" );
809 if ( !resource ) continue;
811 Item* item = new Item( parent, objectID, title, resource );
812 parent->addItem( item );
815 ixmlNodeList_free( itemNodeList );
818 ixmlDocument_free( result );
823 void MediaServer::_buildPlaylist( Container* parent )
825 playlist_t *p_playlist = pl_Get( _cookie->serviceDiscovery );
826 for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
828 Container* container = parent->getContainer( i );
829 playlist_item_t* parentNode = parent->getPlaylistNode();
831 char* title = strdup( container->getTitle() );
832 playlist_item_t* node = playlist_NodeCreate( p_playlist, title, parentNode, 0 );
835 container->setPlaylistNode( node );
836 _buildPlaylist( container );
839 for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
841 Item* item = parent->getItem( i );
842 playlist_item_t* parentNode = parent->getPlaylistNode();
844 input_item_t* p_input = input_ItemNew( _cookie->serviceDiscovery,
848 playlist_BothAddInput( p_playlist, p_input, parentNode,
849 PLAYLIST_APPEND, PLAYLIST_END, &i_cat, NULL,
851 /* TODO: do this better by storing ids */
852 playlist_item_t *p_node = playlist_ItemGetById( p_playlist, i_cat, VLC_FALSE );
854 item->setPlaylistNode( p_node );
858 void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
860 _playlistNode = playlistNode;
863 bool MediaServer::compareSID( const char* sid )
865 return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
869 // MediaServerList...
871 MediaServerList::MediaServerList( Cookie* cookie )
876 MediaServerList::~MediaServerList()
878 for ( unsigned int i = 0; i < _list.size(); i++ )
884 bool MediaServerList::addServer( MediaServer* s )
886 if ( getServer( s->getUDN() ) != 0 ) return false;
888 msg_Dbg( _cookie->serviceDiscovery, "Adding server '%s'", s->getFriendlyName() );
890 _list.push_back( s );
892 char* name = strdup( s->getFriendlyName() );
893 playlist_item_t* node = playlist_NodeCreate( pl_Get( _cookie->serviceDiscovery ),
895 _cookie->serviceDiscovery->p_cat, 0 );
897 s->setPlaylistNode( node );
902 MediaServer* MediaServerList::getServer( const char* UDN )
904 MediaServer* result = 0;
906 for ( unsigned int i = 0; i < _list.size(); i++ )
908 if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
918 MediaServer* MediaServerList::getServerBySID( const char* sid )
920 MediaServer* server = 0;
922 for ( unsigned int i = 0; i < _list.size(); i++ )
924 if ( _list[i]->compareSID( sid ) )
934 void MediaServerList::removeServer( const char* UDN )
936 MediaServer* server = getServer( UDN );
937 if ( !server ) return;
939 msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
941 std::vector<MediaServer*>::iterator it;
942 for ( it = _list.begin(); it != _list.end(); it++ )
956 Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
960 _objectID = objectID;
962 _resource = resource;
967 const char* Item::getObjectID() const
969 return _objectID.c_str();
972 const char* Item::getTitle() const
974 return _title.c_str();
977 const char* Item::getResource() const
979 return _resource.c_str();
982 void Item::setPlaylistNode( playlist_item_t* node )
984 _playlistNode = node;
987 playlist_item_t* Item::getPlaylistNode() const
989 return _playlistNode;
995 Container::Container( Container* parent, const char* objectID, const char* title )
999 _objectID = objectID;
1005 Container::~Container()
1007 for ( unsigned int i = 0; i < _containers.size(); i++ )
1009 delete _containers[i];
1012 for ( unsigned int i = 0; i < _items.size(); i++ )
1018 void Container::addItem( Item* item )
1020 _items.push_back( item );
1023 void Container::addContainer( Container* container )
1025 _containers.push_back( container );
1028 const char* Container::getObjectID() const
1030 return _objectID.c_str();
1033 const char* Container::getTitle() const
1035 return _title.c_str();
1038 unsigned int Container::getNumItems() const
1040 return _items.size();
1043 unsigned int Container::getNumContainers() const
1045 return _containers.size();
1048 Item* Container::getItem( unsigned int i ) const
1050 if ( i < _items.size() ) return _items[i];
1054 Container* Container::getContainer( unsigned int i ) const
1056 if ( i < _containers.size() ) return _containers[i];
1060 void Container::setPlaylistNode( playlist_item_t* node )
1062 _playlistNode = node;
1065 playlist_item_t* Container::getPlaylistNode() const
1067 return _playlistNode;