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 ???
37 #include <upnp/upnp.h>
38 #include <upnp/upnptools.h>
44 #include "vlc_strings.h"
49 struct services_discovery_sys_t
51 playlist_item_t *p_node;
52 playlist_t *p_playlist;
58 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
59 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
65 class MediaServerList;
69 // Cookie that is passed to the callback
73 services_discovery_t* serviceDiscovery;
74 UpnpClient_Handle clientHandle;
75 MediaServerList* serverList;
79 // Class definitions...
87 vlc_mutex_init( c->serviceDiscovery, &_mutex );
92 vlc_mutex_destroy( &_mutex );
95 void lock() { vlc_mutex_lock( &_mutex ); }
96 void unlock() { vlc_mutex_unlock( &_mutex ); }
107 Locker( Lockable* l )
127 static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
129 MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
132 const char* getUDN() const;
133 const char* getFriendlyName() const;
135 void setContentDirectoryEventURL( const char* url );
136 const char* getContentDirectoryEventURL() const;
138 void setContentDirectoryControlURL( const char* url );
139 const char* getContentDirectoryControlURL() const;
141 void subscribeToContentDirectory();
142 void fetchContents();
144 void setPlaylistNode( playlist_item_t* node );
146 bool compareSID( const char* sid );
150 bool _fetchContents( Container* parent );
151 void _buildPlaylist( Container* container );
152 IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
156 Container* _contents;
157 playlist_item_t* _playlistNode;
160 std::string _friendlyName;
162 std::string _contentDirectoryEventURL;
163 std::string _contentDirectoryControlURL;
165 int _subscriptionTimeOut;
166 Upnp_SID _subscriptionID;
170 class MediaServerList
174 MediaServerList( Cookie* cookie );
177 bool addServer( MediaServer* s );
178 void removeServer( const char* UDN );
180 MediaServer* getServer( const char* UDN );
181 MediaServer* getServerBySID( const char* );
187 std::vector<MediaServer*> _list;
195 Item( Container* parent, const char* objectID, const char* title, const char* resource );
197 const char* getObjectID() const;
198 const char* getTitle() const;
199 const char* getResource() const;
201 void setPlaylistNode( playlist_item_t* node );
202 playlist_item_t* getPlaylistNode() const ;
206 playlist_item_t* _playlistNode;
209 std::string _objectID;
211 std::string _resource;
219 Container( Container* parent, const char* objectID, const char* title );
222 void addItem( Item* item );
223 void addContainer( Container* container );
225 const char* getObjectID() const;
226 const char* getTitle() const;
228 unsigned int getNumItems() const;
229 unsigned int getNumContainers() const;
231 Item* getItem( unsigned int i ) const;
232 Container* getContainer( unsigned int i ) const;
234 void setPlaylistNode( playlist_item_t* node );
235 playlist_item_t* getPlaylistNode() const;
239 playlist_item_t* _playlistNode;
243 std::string _objectID;
245 std::vector<Item*> _items;
246 std::vector<Container*> _containers;
250 // VLC callback prototypes
252 static int Open( vlc_object_t* );
253 static void Close( vlc_object_t* );
254 static void Run( services_discovery_t *p_sd );
259 set_shortname( "UPnP" );
260 set_description( _( "Universal Plug'n'Play discovery ( Intel SDK )" ) );
261 set_category( CAT_PLAYLIST );
262 set_subcategory( SUBCAT_PLAYLIST_SD );
263 set_capability( "services_discovery", 0 );
264 set_callbacks( Open, Close );
268 // More prototypes...
270 static Lockable* CallbackLock;
271 static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
273 const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
274 IXML_Document* parseBrowseResult( IXML_Document* doc );
279 static int Open( vlc_object_t *p_this )
281 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
282 services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
283 malloc( sizeof( services_discovery_sys_t ) );
285 playlist_view_t *p_view;
291 /* Create our playlist node */
292 p_sys->p_playlist = ( playlist_t * )vlc_object_find( p_sd,
295 if( !p_sys->p_playlist )
297 msg_Warn( p_sd, "unable to find playlist, cancelling UPnP listening" );
301 p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
302 p_sys->p_node = playlist_NodeCreate( p_sys->p_playlist, VIEW_CATEGORY,
303 "UPnP", p_view->p_root );
304 p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
305 p_sys->p_node->i_flags &= ~PLAYLIST_SKIP_FLAG;
306 val.b_bool = VLC_TRUE;
307 var_Set( p_sys->p_playlist, "intf-change", val );
312 static void Close( vlc_object_t *p_this )
314 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
315 services_discovery_sys_t *p_sys = p_sd->p_sys;
317 if( p_sys->p_playlist )
319 playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
321 vlc_object_release( p_sys->p_playlist );
327 static void Run( services_discovery_t* p_sd )
331 res = UpnpInit( 0, 0 );
332 if( res != UPNP_E_SUCCESS )
334 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
339 cookie.serviceDiscovery = p_sd;
340 cookie.serverList = new MediaServerList( &cookie );
342 CallbackLock = new Lockable( &cookie );
344 res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle );
345 if( res != UPNP_E_SUCCESS )
347 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
351 res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie );
352 if( res != UPNP_E_SUCCESS )
354 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
358 msg_Dbg( p_sd, "UPnP discovery started" );
359 while( !p_sd->b_die )
364 msg_Dbg( p_sd, "UPnP discovery stopped" );
368 delete cookie.serverList;
373 // XML utility functions:
375 // Returns the value of a child element, or 0 on error
376 const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
378 if ( !parent ) return 0;
379 if ( !tagName ) return 0;
381 char* s = strdup( tagName );
382 IXML_NodeList* nodeList = ixmlElement_getElementsByTagName( parent, s );
384 if ( !nodeList ) return 0;
386 IXML_Node* element = ixmlNodeList_item( nodeList, 0 );
387 ixmlNodeList_free( nodeList );
388 if ( !element ) return 0;
390 IXML_Node* textNode = ixmlNode_getFirstChild( element );
391 if ( !textNode ) return 0;
393 return ixmlNode_getNodeValue( textNode );
396 // Extracts the result document from a SOAP response
397 IXML_Document* parseBrowseResult( IXML_Document* doc )
399 if ( !doc ) return 0;
401 IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
402 if ( !resultList ) return 0;
404 IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
406 ixmlNodeList_free( resultList );
408 if ( !resultNode ) return 0;
410 IXML_Node* textNode = ixmlNode_getFirstChild( resultNode );
411 if ( !textNode ) return 0;
413 const char* resultString = ixmlNode_getNodeValue( textNode );
414 char* resultXML = convert_xml_special_chars( resultString );
416 IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
424 // Handles all UPnP events
425 static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
427 Locker locker( CallbackLock );
429 Cookie* cookie = ( Cookie* )pCookie;
431 switch( eventType ) {
433 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
434 case UPNP_DISCOVERY_SEARCH_RESULT:
436 struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
438 IXML_Document *descriptionDoc = 0;
441 res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
442 if ( res != UPNP_E_SUCCESS )
444 msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
448 MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
450 ixmlDocument_free( descriptionDoc );
454 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
456 struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
458 cookie->serverList->removeServer( discovery->DeviceId );
462 case UPNP_EVENT_RECEIVED:
464 Upnp_Event* e = ( Upnp_Event* )event;
466 MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
467 if ( server ) server->fetchContents();
471 case UPNP_EVENT_AUTORENEWAL_FAILED:
472 case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
476 Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
478 MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
479 if ( server ) server->subscribeToContentDirectory();
484 msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
488 return UPNP_E_SUCCESS;
492 // Class implementations...
496 void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
498 if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
499 if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
501 const char* baseURL = location;
503 // Try to extract baseURL
505 IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
508 if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
510 IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
511 if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
514 ixmlNodeList_free( urlList );
519 IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
523 for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
525 IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
527 const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
528 if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
529 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
531 const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
532 if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
533 if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
535 const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
536 if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
538 MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
539 if ( !cookie->serverList->addServer( server ) ) {
546 // Check for ContentDirectory service...
548 IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
551 for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
553 IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
555 const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
556 if ( !serviceType ) continue;
557 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
559 const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
560 if ( !eventSubURL ) continue;
562 const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
563 if ( !controlURL ) continue;
565 // Try to subscribe to ContentDirectory service
567 char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
570 char* s1 = strdup( baseURL );
571 char* s2 = strdup( eventSubURL );
573 if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
575 // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
577 server->setContentDirectoryEventURL( url );
578 server->subscribeToContentDirectory();
586 // Try to browse content directory...
588 url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
591 char* s1 = strdup( baseURL );
592 char* s2 = strdup( controlURL );
594 if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
596 // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
598 server->setContentDirectoryControlURL( url );
599 server->fetchContents();
608 ixmlNodeList_free( serviceList );
612 ixmlNodeList_free( deviceList );
616 MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
621 _friendlyName = friendlyName;
627 MediaServer::~MediaServer()
631 playlist_NodeDelete( _cookie->serviceDiscovery->p_sys->p_playlist,
640 const char* MediaServer::getUDN() const
642 const char* s = _UDN.c_str();
646 const char* MediaServer::getFriendlyName() const
648 const char* s = _friendlyName.c_str();
652 void MediaServer::setContentDirectoryEventURL( const char* url )
654 _contentDirectoryEventURL = url;
657 const char* MediaServer::getContentDirectoryEventURL() const
659 const char* s = _contentDirectoryEventURL.c_str();
663 void MediaServer::setContentDirectoryControlURL( const char* url )
665 _contentDirectoryControlURL = url;
668 const char* MediaServer::getContentDirectoryControlURL() const
670 return _contentDirectoryControlURL.c_str();
673 void MediaServer::subscribeToContentDirectory()
675 const char* url = getContentDirectoryEventURL();
676 if ( !url || strcmp( url, "" ) == 0 )
678 msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
685 int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
687 if ( res == UPNP_E_SUCCESS )
689 _subscriptionTimeOut = timeOut;
690 memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
694 msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) );
698 IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter,
699 const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria )
701 IXML_Document* action = 0;
702 IXML_Document* response = 0;
704 const char* url = getContentDirectoryControlURL();
705 if ( !url || strcmp( url, "" ) == 0 ) { msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" ); return 0; }
707 char* ObjectID = strdup( pObjectID );
708 char* BrowseFlag = strdup( pBrowseFlag );
709 char* Filter = strdup( pFilter );
710 char* StartingIndex = strdup( pStartingIndex );
711 char* RequestedCount = strdup( pRequestedCount );
712 char* SortCriteria = strdup( pSortCriteria );
714 char* serviceType = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
718 res = UpnpAddToAction( &action, "Browse", serviceType, "ObjectID", ObjectID );
719 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
721 res = UpnpAddToAction( &action, "Browse", serviceType, "BrowseFlag", BrowseFlag );
722 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
724 res = UpnpAddToAction( &action, "Browse", serviceType, "Filter", Filter );
725 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
727 res = UpnpAddToAction( &action, "Browse", serviceType, "StartingIndex", StartingIndex );
728 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
730 res = UpnpAddToAction( &action, "Browse", serviceType, "RequestedCount", RequestedCount );
731 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
733 res = UpnpAddToAction( &action, "Browse", serviceType, "SortCriteria", SortCriteria );
734 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
736 res = UpnpSendAction( _cookie->clientHandle,
738 CONTENT_DIRECTORY_SERVICE_TYPE,
742 if ( res != UPNP_E_SUCCESS )
744 msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
745 ixmlDocument_free( response );
754 free( StartingIndex );
755 free( RequestedCount );
756 free( SortCriteria );
760 ixmlDocument_free( action );
764 void MediaServer::fetchContents()
766 Container* root = new Container( 0, "0", getFriendlyName() );
767 _fetchContents( root );
771 vlc_mutex_lock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
773 playlist_NodeEmpty( _cookie->serviceDiscovery->p_sys->p_playlist,
777 vlc_mutex_unlock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
783 _contents->setPlaylistNode( _playlistNode );
785 _buildPlaylist( _contents );
788 bool MediaServer::_fetchContents( Container* parent )
790 if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; }
792 IXML_Document* response = _browseAction( parent->getObjectID(), "BrowseDirectChildren", "*", "0", "0", "" );
793 if ( !response ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
795 IXML_Document* result = parseBrowseResult( response );
796 ixmlDocument_free( response );
797 if ( !result ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
799 IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" );
800 if ( containerNodeList )
802 for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
804 IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
806 const char* objectID = ixmlElement_getAttribute( containerElement, "id" );
807 if ( !objectID ) continue;
809 const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
810 if ( !childCountStr ) continue;
811 int childCount = atoi( childCountStr );
813 const char* title = xml_getChildElementValue( containerElement, "dc:title" );
814 if ( !title ) continue;
816 const char* resource = xml_getChildElementValue( containerElement, "res" );
818 if ( resource && childCount < 1 )
820 Item* item = new Item( parent, objectID, title, resource );
821 parent->addItem( item );
825 Container* container = new Container( parent, objectID, title );
826 parent->addContainer( container );
828 if ( childCount > 0 ) _fetchContents( container );
832 ixmlNodeList_free( containerNodeList );
835 IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result, "item" );
838 for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
840 IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
842 const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
843 if ( !objectID ) continue;
845 const char* title = xml_getChildElementValue( itemElement, "dc:title" );
846 if ( !title ) continue;
848 const char* resource = xml_getChildElementValue( itemElement, "res" );
849 if ( !resource ) continue;
851 Item* item = new Item( parent, objectID, title, resource );
852 parent->addItem( item );
855 ixmlNodeList_free( itemNodeList );
858 ixmlDocument_free( result );
863 void MediaServer::_buildPlaylist( Container* parent )
865 for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
867 Container* container = parent->getContainer( i );
868 playlist_item_t* parentNode = parent->getPlaylistNode();
870 char* title = strdup( container->getTitle() );
871 playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist,
877 container->setPlaylistNode( node );
878 _buildPlaylist( container );
881 for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
883 Item* item = parent->getItem( i );
884 playlist_item_t* parentNode = parent->getPlaylistNode();
886 playlist_item_t* node = playlist_ItemNew( _cookie->serviceDiscovery,
890 playlist_NodeAddItem( _cookie->serviceDiscovery->p_sys->p_playlist,
893 parentNode, PLAYLIST_APPEND, PLAYLIST_END );
895 item->setPlaylistNode( node );
899 void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
901 _playlistNode = playlistNode;
904 bool MediaServer::compareSID( const char* sid )
906 return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
910 // MediaServerList...
912 MediaServerList::MediaServerList( Cookie* cookie )
917 MediaServerList::~MediaServerList()
919 for ( unsigned int i = 0; i < _list.size(); i++ )
925 bool MediaServerList::addServer( MediaServer* s )
927 if ( getServer( s->getUDN() ) != 0 ) return false;
929 msg_Dbg( _cookie->serviceDiscovery, "Adding server '%s'", s->getFriendlyName() );
931 _list.push_back( s );
933 char* name = strdup( s->getFriendlyName() );
934 playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist,
937 _cookie->serviceDiscovery->p_sys->p_node );
939 s->setPlaylistNode( node );
944 MediaServer* MediaServerList::getServer( const char* UDN )
946 MediaServer* result = 0;
948 for ( unsigned int i = 0; i < _list.size(); i++ )
950 if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
960 MediaServer* MediaServerList::getServerBySID( const char* sid )
962 MediaServer* server = 0;
964 for ( unsigned int i = 0; i < _list.size(); i++ )
966 if ( _list[i]->compareSID( sid ) )
976 void MediaServerList::removeServer( const char* UDN )
978 MediaServer* server = getServer( UDN );
979 if ( !server ) return;
981 msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
983 std::vector<MediaServer*>::iterator it;
984 for ( it = _list.begin(); it != _list.end(); it++ )
998 Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
1002 _objectID = objectID;
1004 _resource = resource;
1009 const char* Item::getObjectID() const
1011 return _objectID.c_str();
1014 const char* Item::getTitle() const
1016 return _title.c_str();
1019 const char* Item::getResource() const
1021 return _resource.c_str();
1024 void Item::setPlaylistNode( playlist_item_t* node )
1026 _playlistNode = node;
1029 playlist_item_t* Item::getPlaylistNode() const
1031 return _playlistNode;
1037 Container::Container( Container* parent, const char* objectID, const char* title )
1041 _objectID = objectID;
1047 Container::~Container()
1049 for ( unsigned int i = 0; i < _containers.size(); i++ )
1051 delete _containers[i];
1054 for ( unsigned int i = 0; i < _items.size(); i++ )
1060 void Container::addItem( Item* item )
1062 _items.push_back( item );
1065 void Container::addContainer( Container* container )
1067 _containers.push_back( container );
1070 const char* Container::getObjectID() const
1072 return _objectID.c_str();
1075 const char* Container::getTitle() const
1077 return _title.c_str();
1080 unsigned int Container::getNumItems() const
1082 return _items.size();
1085 unsigned int Container::getNumContainers() const
1087 return _containers.size();
1090 Item* Container::getItem( unsigned int i ) const
1092 if ( i < _items.size() ) return _items[i];
1096 Container* Container::getContainer( unsigned int i ) const
1098 if ( i < _containers.size() ) return _containers[i];
1102 void Container::setPlaylistNode( playlist_item_t* node )
1104 _playlistNode = node;
1107 playlist_item_t* Container::getPlaylistNode() const
1109 return _playlistNode;