1 /*****************************************************************************
2 * Upnp_intel.cpp : UPnP discovery module (Intel SDK)
3 *****************************************************************************
4 * Copyright (C) 2004-2008 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 ???
30 \TODO: Rewrite this using the new service discovery API (see sap.c, shout.c).
37 #include <upnp/upnp.h>
38 #include <upnp/upnptools.h>
45 #include <vlc_common.h>
46 #include <vlc_plugin.h>
47 #include <vlc_playlist.h>
49 /*****************************************************************************
51 *****************************************************************************/
52 static int Open( vlc_object_t* );
53 static void Close( vlc_object_t* );
56 set_shortname( "UPnP" );
57 set_description( N_( "Universal Plug'n'Play discovery ( Intel SDK )" ) );
58 set_category( CAT_PLAYLIST );
59 set_subcategory( SUBCAT_PLAYLIST_SD );
60 set_capability( "services_discovery", 0 );
61 set_callbacks( Open, Close );
65 /*****************************************************************************
67 *****************************************************************************/
71 struct services_discovery_sys_t
73 playlist_t *p_playlist;
74 playlist_item_t *p_node_cat;
75 playlist_item_t *p_node_one;
81 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
82 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
88 class MediaServerList;
92 // Cookie that is passed to the callback
96 services_discovery_t* serviceDiscovery;
97 UpnpClient_Handle clientHandle;
98 MediaServerList* serverList;
103 // Class definitions...
109 Lockable( Cookie* c )
111 vlc_mutex_init( &_mutex );
116 vlc_mutex_destroy( &_mutex );
119 void lock() { vlc_mutex_lock( &_mutex ); }
120 void unlock() { vlc_mutex_unlock( &_mutex ); }
131 Locker( Lockable* l )
151 static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
153 MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
156 const char* getUDN() const;
157 const char* getFriendlyName() const;
159 void setContentDirectoryEventURL( const char* url );
160 const char* getContentDirectoryEventURL() const;
162 void setContentDirectoryControlURL( const char* url );
163 const char* getContentDirectoryControlURL() const;
165 void subscribeToContentDirectory();
166 void fetchContents();
168 void setPlaylistNode( playlist_item_t* node );
170 bool compareSID( const char* sid );
174 bool _fetchContents( Container* parent );
175 void _buildPlaylist( Container* container );
176 IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
180 Container* _contents;
181 playlist_item_t* _playlistNode;
184 std::string _friendlyName;
186 std::string _contentDirectoryEventURL;
187 std::string _contentDirectoryControlURL;
189 int _subscriptionTimeOut;
190 Upnp_SID _subscriptionID;
194 class MediaServerList
198 MediaServerList( Cookie* cookie );
201 bool addServer( MediaServer* s );
202 void removeServer( const char* UDN );
204 MediaServer* getServer( const char* UDN );
205 MediaServer* getServerBySID( const char* );
211 std::vector<MediaServer*> _list;
219 Item( Container* parent, const char* objectID, const char* title, const char* resource );
221 const char* getObjectID() const;
222 const char* getTitle() const;
223 const char* getResource() const;
225 void setPlaylistNode( playlist_item_t* node );
226 playlist_item_t* getPlaylistNode() const ;
230 playlist_item_t* _playlistNode;
233 std::string _objectID;
235 std::string _resource;
243 Container( Container* parent, const char* objectID, const char* title );
246 void addItem( Item* item );
247 void addContainer( Container* container );
249 const char* getObjectID() const;
250 const char* getTitle() const;
252 unsigned int getNumItems() const;
253 unsigned int getNumContainers() const;
255 Item* getItem( unsigned int i ) const;
256 Container* getContainer( unsigned int i ) const;
258 void setPlaylistNode( playlist_item_t* node );
259 playlist_item_t* getPlaylistNode() const;
263 playlist_item_t* _playlistNode;
267 std::string _objectID;
269 std::vector<Item*> _items;
270 std::vector<Container*> _containers;
274 // VLC callback prototypes
276 static void Run( services_discovery_t *p_sd );
277 static playlist_t *pl_Get( services_discovery_t *p_sd )
279 return p_sd->p_sys->p_playlist;
282 // More prototypes...
284 static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
286 const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
287 IXML_Document* parseBrowseResult( IXML_Document* doc );
292 static int Open( vlc_object_t *p_this )
294 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
295 services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
296 malloc( sizeof( services_discovery_sys_t ) );
300 p_sys->p_playlist = pl_Yield( p_sd );
302 /* Create our playlist node */
303 vlc_object_lock( p_sys->p_playlist );
304 playlist_NodesPairCreate( pl_Get( p_sd ), _("Devices"),
305 &p_sys->p_node_cat, &p_sys->p_node_one,
307 vlc_object_unlock( p_sys->p_playlist );
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 vlc_object_lock( p_sys->p_playlist );
318 playlist_NodeDelete( pl_Get( p_sd ), p_sys->p_node_one, true,
320 playlist_NodeDelete( pl_Get( p_sd ), p_sys->p_node_cat, true,
322 vlc_object_unlock( 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 );
341 cookie.lock = new Lockable();
343 res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle );
344 if( res != UPNP_E_SUCCESS )
346 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
350 res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie );
351 if( res != UPNP_E_SUCCESS )
353 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
357 msg_Dbg( p_sd, "UPnP discovery started" );
358 while( vlc_object_alive (p_sd) )
363 msg_Dbg( p_sd, "UPnP discovery stopped" );
367 delete cookie.serverList;
372 // XML utility functions:
374 // Returns the value of a child element, or 0 on error
375 const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
377 if ( !parent ) return 0;
378 if ( !tagName ) return 0;
380 char* s = strdup( tagName );
381 IXML_NodeList* nodeList = ixmlElement_getElementsByTagName( parent, s );
383 if ( !nodeList ) return 0;
385 IXML_Node* element = ixmlNodeList_item( nodeList, 0 );
386 ixmlNodeList_free( nodeList );
387 if ( !element ) return 0;
389 IXML_Node* textNode = ixmlNode_getFirstChild( element );
390 if ( !textNode ) return 0;
392 return ixmlNode_getNodeValue( textNode );
395 // Extracts the result document from a SOAP response
396 IXML_Document* parseBrowseResult( IXML_Document* doc )
398 if ( !doc ) return 0;
400 IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
401 if ( !resultList ) return 0;
403 IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
405 ixmlNodeList_free( resultList );
407 if ( !resultNode ) return 0;
409 IXML_Node* textNode = ixmlNode_getFirstChild( resultNode );
410 if ( !textNode ) return 0;
412 const char* resultString = ixmlNode_getNodeValue( textNode );
413 char* resultXML = strdup( resultString );
415 IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
423 // Handles all UPnP events
424 static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
426 Cookie* cookie = ( Cookie* )pCookie;
428 Locker locker( cookie->lock );
430 switch( eventType ) {
432 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
433 case UPNP_DISCOVERY_SEARCH_RESULT:
435 struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
437 IXML_Document *descriptionDoc = 0;
440 res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
441 if ( res != UPNP_E_SUCCESS )
443 msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
447 MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
449 ixmlDocument_free( descriptionDoc );
453 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
455 struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
457 cookie->serverList->removeServer( discovery->DeviceId );
461 case UPNP_EVENT_RECEIVED:
463 Upnp_Event* e = ( Upnp_Event* )event;
465 MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
466 if ( server ) server->fetchContents();
470 case UPNP_EVENT_AUTORENEWAL_FAILED:
471 case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
475 Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
477 MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
478 if ( server ) server->subscribeToContentDirectory();
482 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
483 msg_Warn( cookie->serviceDiscovery, "subscription complete" );
486 case UPNP_DISCOVERY_SEARCH_TIMEOUT:
487 msg_Warn( cookie->serviceDiscovery, "search timeout" );
491 msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
495 return UPNP_E_SUCCESS;
499 // Class implementations...
503 void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
505 if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
506 if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
508 const char* baseURL = location;
510 // Try to extract baseURL
512 IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
515 if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
517 IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
518 if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
521 ixmlNodeList_free( urlList );
526 IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
530 for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
532 IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
534 const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
535 if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
536 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
538 const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
539 if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
540 if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
542 const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
543 if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
545 MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
546 if ( !cookie->serverList->addServer( server ) ) {
553 // Check for ContentDirectory service...
555 IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
558 for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
560 IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
562 const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
563 if ( !serviceType ) continue;
564 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
566 const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
567 if ( !eventSubURL ) continue;
569 const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
570 if ( !controlURL ) continue;
572 // Try to subscribe to ContentDirectory service
574 char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
577 char* s1 = strdup( baseURL );
578 char* s2 = strdup( eventSubURL );
580 if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
582 // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
584 server->setContentDirectoryEventURL( url );
585 server->subscribeToContentDirectory();
593 // Try to browse content directory...
595 url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
598 char* s1 = strdup( baseURL );
599 char* s2 = strdup( controlURL );
601 if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
603 // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
605 server->setContentDirectoryControlURL( url );
606 server->fetchContents();
615 ixmlNodeList_free( serviceList );
619 ixmlNodeList_free( deviceList );
623 MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
628 _friendlyName = friendlyName;
634 MediaServer::~MediaServer()
638 vlc_object_lock( _cookie->serviceDiscovery->p_sys->p_playlist );
639 playlist_NodeDelete( pl_Get( _cookie->serviceDiscovery ) ,
640 _playlistNode, true, true );
641 vlc_object_unlock( _cookie->serviceDiscovery->p_sys->p_playlist );
647 const char* MediaServer::getUDN() const
649 const char* s = _UDN.c_str();
653 const char* MediaServer::getFriendlyName() const
655 const char* s = _friendlyName.c_str();
659 void MediaServer::setContentDirectoryEventURL( const char* url )
661 _contentDirectoryEventURL = url;
664 const char* MediaServer::getContentDirectoryEventURL() const
666 const char* s = _contentDirectoryEventURL.c_str();
670 void MediaServer::setContentDirectoryControlURL( const char* url )
672 _contentDirectoryControlURL = url;
675 const char* MediaServer::getContentDirectoryControlURL() const
677 return _contentDirectoryControlURL.c_str();
680 void MediaServer::subscribeToContentDirectory()
682 const char* url = getContentDirectoryEventURL();
683 if ( !url || strcmp( url, "" ) == 0 )
685 msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
692 int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
694 if ( res == UPNP_E_SUCCESS )
696 _subscriptionTimeOut = timeOut;
697 memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
701 msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) );
705 IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter,
706 const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria )
708 IXML_Document* action = 0;
709 IXML_Document* response = 0;
711 const char* url = getContentDirectoryControlURL();
712 if ( !url || strcmp( url, "" ) == 0 ) { msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" ); return 0; }
714 char* ObjectID = strdup( pObjectID );
715 char* BrowseFlag = strdup( pBrowseFlag );
716 char* Filter = strdup( pFilter );
717 char* StartingIndex = strdup( pStartingIndex );
718 char* RequestedCount = strdup( pRequestedCount );
719 char* SortCriteria = strdup( pSortCriteria );
721 char* serviceType = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
725 res = UpnpAddToAction( &action, "Browse", serviceType, "ObjectID", ObjectID );
726 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
728 res = UpnpAddToAction( &action, "Browse", serviceType, "BrowseFlag", BrowseFlag );
729 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
731 res = UpnpAddToAction( &action, "Browse", serviceType, "Filter", Filter );
732 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
734 res = UpnpAddToAction( &action, "Browse", serviceType, "StartingIndex", StartingIndex );
735 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
737 res = UpnpAddToAction( &action, "Browse", serviceType, "RequestedCount", RequestedCount );
738 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
740 res = UpnpAddToAction( &action, "Browse", serviceType, "SortCriteria", SortCriteria );
741 if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
743 res = UpnpSendAction( _cookie->clientHandle,
745 CONTENT_DIRECTORY_SERVICE_TYPE,
749 if ( res != UPNP_E_SUCCESS )
751 msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
752 ixmlDocument_free( response );
761 free( StartingIndex );
762 free( RequestedCount );
763 free( SortCriteria );
767 ixmlDocument_free( action );
771 void MediaServer::fetchContents()
773 Container* root = new Container( 0, "0", getFriendlyName() );
774 playlist_t * p_playlist = pl_Get( _cookie->serviceDiscovery );
775 _fetchContents( root );
780 playlist_NodeEmpty( p_playlist, _playlistNode, true );
786 _contents->setPlaylistNode( _playlistNode );
788 _buildPlaylist( _contents );
791 bool MediaServer::_fetchContents( Container* parent )
793 if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; }
795 IXML_Document* response = _browseAction( parent->getObjectID(), "BrowseDirectChildren", "*", "0", "0", "" );
796 if ( !response ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
798 IXML_Document* result = parseBrowseResult( response );
799 ixmlDocument_free( response );
800 if ( !result ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
802 IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" );
803 if ( containerNodeList )
805 for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
807 IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
809 const char* objectID = ixmlElement_getAttribute( containerElement, "id" );
810 if ( !objectID ) continue;
812 const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
813 if ( !childCountStr ) continue;
814 int childCount = atoi( childCountStr );
816 const char* title = xml_getChildElementValue( containerElement, "dc:title" );
817 if ( !title ) continue;
819 const char* resource = xml_getChildElementValue( containerElement, "res" );
821 if ( resource && childCount < 1 )
823 Item* item = new Item( parent, objectID, title, resource );
824 parent->addItem( item );
828 Container* container = new Container( parent, objectID, title );
829 parent->addContainer( container );
831 if ( childCount > 0 ) _fetchContents( container );
835 ixmlNodeList_free( containerNodeList );
838 IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result, "item" );
841 for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
843 IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
845 const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
846 if ( !objectID ) continue;
848 const char* title = xml_getChildElementValue( itemElement, "dc:title" );
849 if ( !title ) continue;
851 const char* resource = xml_getChildElementValue( itemElement, "res" );
852 if ( !resource ) continue;
854 Item* item = new Item( parent, objectID, title, resource );
855 parent->addItem( item );
858 ixmlNodeList_free( itemNodeList );
861 ixmlDocument_free( result );
866 void MediaServer::_buildPlaylist( Container* parent )
868 playlist_t *p_playlist = pl_Get( _cookie->serviceDiscovery );
869 for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
871 Container* container = parent->getContainer( i );
872 playlist_item_t* parentNode = parent->getPlaylistNode();
874 char* title = strdup( container->getTitle() );
875 playlist_item_t* node = playlist_NodeCreate( p_playlist, title, parentNode, 0, NULL );
878 container->setPlaylistNode( node );
879 _buildPlaylist( container );
882 for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
884 Item* item = parent->getItem( i );
885 playlist_item_t* parentNode = parent->getPlaylistNode();
887 input_item_t* p_input = input_item_New( _cookie->serviceDiscovery,
891 /* FIXME: playlist_AddInput() can fail */
892 playlist_BothAddInput( p_playlist, p_input, parentNode,
893 PLAYLIST_APPEND, PLAYLIST_END, &i_cat, NULL,
895 vlc_gc_decref( p_input );
896 /* TODO: do this better by storing ids */
897 playlist_item_t *p_node = playlist_ItemGetById( p_playlist, i_cat, false );
899 item->setPlaylistNode( p_node );
903 void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
905 _playlistNode = playlistNode;
908 bool MediaServer::compareSID( const char* sid )
910 return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
914 // MediaServerList...
916 MediaServerList::MediaServerList( Cookie* cookie )
921 MediaServerList::~MediaServerList()
923 for ( unsigned int i = 0; i < _list.size(); i++ )
929 bool MediaServerList::addServer( MediaServer* s )
931 if ( getServer( s->getUDN() ) != 0 ) return false;
933 msg_Dbg( _cookie->serviceDiscovery, "Adding server '%s'", s->getFriendlyName() );
935 _list.push_back( s );
937 char* name = strdup( s->getFriendlyName() );
938 vlc_object_lock( _cookie->serviceDiscovery->p_sys->p_playlist );
939 playlist_item_t* node = playlist_NodeCreate(
940 pl_Get( _cookie->serviceDiscovery ), name,
941 _cookie->serviceDiscovery->p_sys->p_node_cat,
943 vlc_object_unlock( _cookie->serviceDiscovery->p_sys->p_playlist );
945 s->setPlaylistNode( node );
950 MediaServer* MediaServerList::getServer( const char* UDN )
952 MediaServer* result = 0;
954 for ( unsigned int i = 0; i < _list.size(); i++ )
956 if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
966 MediaServer* MediaServerList::getServerBySID( const char* sid )
968 MediaServer* server = 0;
970 for ( unsigned int i = 0; i < _list.size(); i++ )
972 if ( _list[i]->compareSID( sid ) )
982 void MediaServerList::removeServer( const char* UDN )
984 MediaServer* server = getServer( UDN );
985 if ( !server ) return;
987 msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
989 std::vector<MediaServer*>::iterator it;
990 for ( it = _list.begin(); it != _list.end(); it++ )
1004 Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
1008 _objectID = objectID;
1010 _resource = resource;
1015 const char* Item::getObjectID() const
1017 return _objectID.c_str();
1020 const char* Item::getTitle() const
1022 return _title.c_str();
1025 const char* Item::getResource() const
1027 return _resource.c_str();
1030 void Item::setPlaylistNode( playlist_item_t* node )
1032 _playlistNode = node;
1035 playlist_item_t* Item::getPlaylistNode() const
1037 return _playlistNode;
1043 Container::Container( Container* parent, const char* objectID, const char* title )
1047 _objectID = objectID;
1053 Container::~Container()
1055 for ( unsigned int i = 0; i < _containers.size(); i++ )
1057 delete _containers[i];
1060 for ( unsigned int i = 0; i < _items.size(); i++ )
1066 void Container::addItem( Item* item )
1068 _items.push_back( item );
1071 void Container::addContainer( Container* container )
1073 _containers.push_back( container );
1076 const char* Container::getObjectID() const
1078 return _objectID.c_str();
1081 const char* Container::getTitle() const
1083 return _title.c_str();
1086 unsigned int Container::getNumItems() const
1088 return _items.size();
1091 unsigned int Container::getNumContainers() const
1093 return _containers.size();
1096 Item* Container::getItem( unsigned int i ) const
1098 if ( i < _items.size() ) return _items[i];
1102 Container* Container::getContainer( unsigned int i ) const
1104 if ( i < _containers.size() ) return _containers[i];
1108 void Container::setPlaylistNode( playlist_item_t* node )
1110 _playlistNode = node;
1113 playlist_item_t* Container::getPlaylistNode() const
1115 return _playlistNode;