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 struct services_discovery_sys_t
49 playlist_item_t
*p_node_cat
;
50 playlist_item_t
*p_node_one
;
56 const char* MEDIA_SERVER_DEVICE_TYPE
= "urn:schemas-upnp-org:device:MediaServer:1";
57 const char* CONTENT_DIRECTORY_SERVICE_TYPE
= "urn:schemas-upnp-org:service:ContentDirectory:1";
63 class MediaServerList
;
67 // Cookie that is passed to the callback
71 services_discovery_t
* serviceDiscovery
;
72 UpnpClient_Handle clientHandle
;
73 MediaServerList
* serverList
;
77 // Class definitions...
85 vlc_mutex_init( c
->serviceDiscovery
, &_mutex
);
90 vlc_mutex_destroy( &_mutex
);
93 void lock() { vlc_mutex_lock( &_mutex
); }
94 void unlock() { vlc_mutex_unlock( &_mutex
); }
105 Locker( Lockable
* l
)
125 static void parseDeviceDescription( IXML_Document
* doc
, const char* location
, Cookie
* cookie
);
127 MediaServer( const char* UDN
, const char* friendlyName
, Cookie
* cookie
);
130 const char* getUDN() const;
131 const char* getFriendlyName() const;
133 void setContentDirectoryEventURL( const char* url
);
134 const char* getContentDirectoryEventURL() const;
136 void setContentDirectoryControlURL( const char* url
);
137 const char* getContentDirectoryControlURL() const;
139 void subscribeToContentDirectory();
140 void fetchContents();
142 void setPlaylistNode( playlist_item_t
* node
);
144 bool compareSID( const char* sid
);
148 bool _fetchContents( Container
* parent
);
149 void _buildPlaylist( Container
* container
);
150 IXML_Document
* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
154 Container
* _contents
;
155 playlist_item_t
* _playlistNode
;
158 std::string _friendlyName
;
160 std::string _contentDirectoryEventURL
;
161 std::string _contentDirectoryControlURL
;
163 int _subscriptionTimeOut
;
164 Upnp_SID _subscriptionID
;
168 class MediaServerList
172 MediaServerList( Cookie
* cookie
);
175 bool addServer( MediaServer
* s
);
176 void removeServer( const char* UDN
);
178 MediaServer
* getServer( const char* UDN
);
179 MediaServer
* getServerBySID( const char* );
185 std::vector
<MediaServer
*> _list
;
193 Item( Container
* parent
, const char* objectID
, const char* title
, const char* resource
);
195 const char* getObjectID() const;
196 const char* getTitle() const;
197 const char* getResource() const;
199 void setPlaylistNode( playlist_item_t
* node
);
200 playlist_item_t
* getPlaylistNode() const ;
204 playlist_item_t
* _playlistNode
;
207 std::string _objectID
;
209 std::string _resource
;
217 Container( Container
* parent
, const char* objectID
, const char* title
);
220 void addItem( Item
* item
);
221 void addContainer( Container
* container
);
223 const char* getObjectID() const;
224 const char* getTitle() const;
226 unsigned int getNumItems() const;
227 unsigned int getNumContainers() const;
229 Item
* getItem( unsigned int i
) const;
230 Container
* getContainer( unsigned int i
) const;
232 void setPlaylistNode( playlist_item_t
* node
);
233 playlist_item_t
* getPlaylistNode() const;
237 playlist_item_t
* _playlistNode
;
241 std::string _objectID
;
243 std::vector
<Item
*> _items
;
244 std::vector
<Container
*> _containers
;
248 // VLC callback prototypes
250 static int Open( vlc_object_t
* );
251 static void Close( vlc_object_t
* );
252 static void Run( services_discovery_t
*p_sd
);
257 set_shortname( "UPnP" );
258 set_description( _( "Universal Plug'n'Play discovery ( Intel SDK )" ) );
259 set_category( CAT_PLAYLIST
);
260 set_subcategory( SUBCAT_PLAYLIST_SD
);
261 set_capability( "services_discovery", 0 );
262 set_callbacks( Open
, Close
);
266 // More prototypes...
268 static Lockable
* CallbackLock
;
269 static int Callback( Upnp_EventType eventType
, void* event
, void* pCookie
);
271 const char* xml_getChildElementValue( IXML_Element
* parent
, const char* tagName
);
272 IXML_Document
* parseBrowseResult( IXML_Document
* doc
);
277 static int Open( vlc_object_t
*p_this
)
279 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
280 services_discovery_sys_t
*p_sys
= ( services_discovery_sys_t
* )
281 malloc( sizeof( services_discovery_sys_t
) );
286 /* Create our playlist node */
287 playlist_NodesPairCreate( pl_Get( p_sd
), _("Devices"),
288 &p_sys
->p_node_cat
, &p_sys
->p_node_one
,
294 static void Close( vlc_object_t
*p_this
)
296 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
297 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
299 playlist_NodeDelete( pl_Get( p_sd
), p_sys
->p_node_one
, VLC_TRUE
,
301 playlist_NodeDelete( pl_Get( p_sd
), p_sys
->p_node_cat
, VLC_TRUE
,
307 static void Run( services_discovery_t
* p_sd
)
311 res
= UpnpInit( 0, 0 );
312 if( res
!= UPNP_E_SUCCESS
)
314 msg_Err( p_sd
, "%s", UpnpGetErrorMessage( res
) );
319 cookie
.serviceDiscovery
= p_sd
;
320 cookie
.serverList
= new MediaServerList( &cookie
);
322 CallbackLock
= new Lockable( &cookie
);
324 res
= UpnpRegisterClient( Callback
, &cookie
, &cookie
.clientHandle
);
325 if( res
!= UPNP_E_SUCCESS
)
327 msg_Err( p_sd
, "%s", UpnpGetErrorMessage( res
) );
331 res
= UpnpSearchAsync( cookie
.clientHandle
, 5, MEDIA_SERVER_DEVICE_TYPE
, &cookie
);
332 if( res
!= UPNP_E_SUCCESS
)
334 msg_Err( p_sd
, "%s", UpnpGetErrorMessage( res
) );
338 msg_Dbg( p_sd
, "UPnP discovery started" );
339 while( !p_sd
->b_die
)
344 msg_Dbg( p_sd
, "UPnP discovery stopped" );
348 delete cookie
.serverList
;
353 // XML utility functions:
355 // Returns the value of a child element, or 0 on error
356 const char* xml_getChildElementValue( IXML_Element
* parent
, const char* tagName
)
358 if ( !parent
) return 0;
359 if ( !tagName
) return 0;
361 char* s
= strdup( tagName
);
362 IXML_NodeList
* nodeList
= ixmlElement_getElementsByTagName( parent
, s
);
364 if ( !nodeList
) return 0;
366 IXML_Node
* element
= ixmlNodeList_item( nodeList
, 0 );
367 ixmlNodeList_free( nodeList
);
368 if ( !element
) return 0;
370 IXML_Node
* textNode
= ixmlNode_getFirstChild( element
);
371 if ( !textNode
) return 0;
373 return ixmlNode_getNodeValue( textNode
);
376 // Extracts the result document from a SOAP response
377 IXML_Document
* parseBrowseResult( IXML_Document
* doc
)
379 if ( !doc
) return 0;
381 IXML_NodeList
* resultList
= ixmlDocument_getElementsByTagName( doc
, "Result" );
382 if ( !resultList
) return 0;
384 IXML_Node
* resultNode
= ixmlNodeList_item( resultList
, 0 );
386 ixmlNodeList_free( resultList
);
388 if ( !resultNode
) return 0;
390 IXML_Node
* textNode
= ixmlNode_getFirstChild( resultNode
);
391 if ( !textNode
) return 0;
393 const char* resultString
= ixmlNode_getNodeValue( textNode
);
394 char* resultXML
= strdup( resultString
);
396 resolve_xml_special_chars( resultXML
);
398 IXML_Document
* browseDoc
= ixmlParseBuffer( resultXML
);
406 // Handles all UPnP events
407 static int Callback( Upnp_EventType eventType
, void* event
, void* pCookie
)
409 Locker
locker( CallbackLock
);
411 Cookie
* cookie
= ( Cookie
* )pCookie
;
413 switch( eventType
) {
415 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
416 case UPNP_DISCOVERY_SEARCH_RESULT
:
418 struct Upnp_Discovery
* discovery
= ( struct Upnp_Discovery
* )event
;
420 IXML_Document
*descriptionDoc
= 0;
423 res
= UpnpDownloadXmlDoc( discovery
->Location
, &descriptionDoc
);
424 if ( res
!= UPNP_E_SUCCESS
)
426 msg_Dbg( cookie
->serviceDiscovery
, "%s:%d: Could not download device description!", __FILE__
, __LINE__
);
430 MediaServer::parseDeviceDescription( descriptionDoc
, discovery
->Location
, cookie
);
432 ixmlDocument_free( descriptionDoc
);
436 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
:
438 struct Upnp_Discovery
* discovery
= ( struct Upnp_Discovery
* )event
;
440 cookie
->serverList
->removeServer( discovery
->DeviceId
);
444 case UPNP_EVENT_RECEIVED
:
446 Upnp_Event
* e
= ( Upnp_Event
* )event
;
448 MediaServer
* server
= cookie
->serverList
->getServerBySID( e
->Sid
);
449 if ( server
) server
->fetchContents();
453 case UPNP_EVENT_AUTORENEWAL_FAILED
:
454 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
:
458 Upnp_Event_Subscribe
* s
= ( Upnp_Event_Subscribe
* )event
;
460 MediaServer
* server
= cookie
->serverList
->getServerBySID( s
->Sid
);
461 if ( server
) server
->subscribeToContentDirectory();
465 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
466 msg_Warn( cookie
->serviceDiscovery
, "subscription complete" );
469 case UPNP_DISCOVERY_SEARCH_TIMEOUT
:
470 msg_Warn( cookie
->serviceDiscovery
, "search timeout" );
474 msg_Dbg( cookie
->serviceDiscovery
, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__
, __LINE__
, eventType
);
478 return UPNP_E_SUCCESS
;
482 // Class implementations...
486 void MediaServer::parseDeviceDescription( IXML_Document
* doc
, const char* location
, Cookie
* cookie
)
488 if ( !doc
) { msg_Dbg( cookie
->serviceDiscovery
, "%s:%d: NULL", __FILE__
, __LINE__
); return; }
489 if ( !location
) { msg_Dbg( cookie
->serviceDiscovery
, "%s:%d: NULL", __FILE__
, __LINE__
); return; }
491 const char* baseURL
= location
;
493 // Try to extract baseURL
495 IXML_NodeList
* urlList
= ixmlDocument_getElementsByTagName( doc
, "baseURL" );
498 if ( IXML_Node
* urlNode
= ixmlNodeList_item( urlList
, 0 ) )
500 IXML_Node
* textNode
= ixmlNode_getFirstChild( urlNode
);
501 if ( textNode
) baseURL
= ixmlNode_getNodeValue( textNode
);
504 ixmlNodeList_free( urlList
);
509 IXML_NodeList
* deviceList
= ixmlDocument_getElementsByTagName( doc
, "device" );
513 for ( unsigned int i
= 0; i
< ixmlNodeList_length( deviceList
); i
++ )
515 IXML_Element
* deviceElement
= ( IXML_Element
* )ixmlNodeList_item( deviceList
, i
);
517 const char* deviceType
= xml_getChildElementValue( deviceElement
, "deviceType" );
518 if ( !deviceType
) { msg_Dbg( cookie
->serviceDiscovery
, "%s:%d: no deviceType!", __FILE__
, __LINE__
); continue; }
519 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE
, deviceType
) != 0 ) continue;
521 const char* UDN
= xml_getChildElementValue( deviceElement
, "UDN" );
522 if ( !UDN
) { msg_Dbg( cookie
->serviceDiscovery
, "%s:%d: no UDN!", __FILE__
, __LINE__
); continue; }
523 if ( cookie
->serverList
->getServer( UDN
) != 0 ) continue;
525 const char* friendlyName
= xml_getChildElementValue( deviceElement
, "friendlyName" );
526 if ( !friendlyName
) { msg_Dbg( cookie
->serviceDiscovery
, "%s:%d: no friendlyName!", __FILE__
, __LINE__
); continue; }
528 MediaServer
* server
= new MediaServer( UDN
, friendlyName
, cookie
);
529 if ( !cookie
->serverList
->addServer( server
) ) {
536 // Check for ContentDirectory service...
538 IXML_NodeList
* serviceList
= ixmlElement_getElementsByTagName( deviceElement
, "service" );
541 for ( unsigned int j
= 0; j
< ixmlNodeList_length( serviceList
); j
++ )
543 IXML_Element
* serviceElement
= ( IXML_Element
* )ixmlNodeList_item( serviceList
, j
);
545 const char* serviceType
= xml_getChildElementValue( serviceElement
, "serviceType" );
546 if ( !serviceType
) continue;
547 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE
, serviceType
) != 0 ) continue;
549 const char* eventSubURL
= xml_getChildElementValue( serviceElement
, "eventSubURL" );
550 if ( !eventSubURL
) continue;
552 const char* controlURL
= xml_getChildElementValue( serviceElement
, "controlURL" );
553 if ( !controlURL
) continue;
555 // Try to subscribe to ContentDirectory service
557 char* url
= ( char* )malloc( strlen( baseURL
) + strlen( eventSubURL
) + 1 );
560 char* s1
= strdup( baseURL
);
561 char* s2
= strdup( eventSubURL
);
563 if ( UpnpResolveURL( s1
, s2
, url
) == UPNP_E_SUCCESS
)
565 // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
567 server
->setContentDirectoryEventURL( url
);
568 server
->subscribeToContentDirectory();
576 // Try to browse content directory...
578 url
= ( char* )malloc( strlen( baseURL
) + strlen( controlURL
) + 1 );
581 char* s1
= strdup( baseURL
);
582 char* s2
= strdup( controlURL
);
584 if ( UpnpResolveURL( s1
, s2
, url
) == UPNP_E_SUCCESS
)
586 // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
588 server
->setContentDirectoryControlURL( url
);
589 server
->fetchContents();
598 ixmlNodeList_free( serviceList
);
602 ixmlNodeList_free( deviceList
);
606 MediaServer::MediaServer( const char* UDN
, const char* friendlyName
, Cookie
* cookie
)
611 _friendlyName
= friendlyName
;
617 MediaServer::~MediaServer()
621 playlist_NodeDelete( pl_Get( _cookie
->serviceDiscovery
) ,
622 _playlistNode
, VLC_TRUE
, VLC_TRUE
);
628 const char* MediaServer::getUDN() const
630 const char* s
= _UDN
.c_str();
634 const char* MediaServer::getFriendlyName() const
636 const char* s
= _friendlyName
.c_str();
640 void MediaServer::setContentDirectoryEventURL( const char* url
)
642 _contentDirectoryEventURL
= url
;
645 const char* MediaServer::getContentDirectoryEventURL() const
647 const char* s
= _contentDirectoryEventURL
.c_str();
651 void MediaServer::setContentDirectoryControlURL( const char* url
)
653 _contentDirectoryControlURL
= url
;
656 const char* MediaServer::getContentDirectoryControlURL() const
658 return _contentDirectoryControlURL
.c_str();
661 void MediaServer::subscribeToContentDirectory()
663 const char* url
= getContentDirectoryEventURL();
664 if ( !url
|| strcmp( url
, "" ) == 0 )
666 msg_Dbg( _cookie
->serviceDiscovery
, "No subscription url set!" );
673 int res
= UpnpSubscribe( _cookie
->clientHandle
, url
, &timeOut
, sid
);
675 if ( res
== UPNP_E_SUCCESS
)
677 _subscriptionTimeOut
= timeOut
;
678 memcpy( _subscriptionID
, sid
, sizeof( Upnp_SID
) );
682 msg_Dbg( _cookie
->serviceDiscovery
, "%s:%d: WARNING: '%s': %s", __FILE__
, __LINE__
, getFriendlyName(), UpnpGetErrorMessage( res
) );
686 IXML_Document
* MediaServer::_browseAction( const char* pObjectID
, const char* pBrowseFlag
, const char* pFilter
,
687 const char* pStartingIndex
, const char* pRequestedCount
, const char* pSortCriteria
)
689 IXML_Document
* action
= 0;
690 IXML_Document
* response
= 0;
692 const char* url
= getContentDirectoryControlURL();
693 if ( !url
|| strcmp( url
, "" ) == 0 ) { msg_Dbg( _cookie
->serviceDiscovery
, "No subscription url set!" ); return 0; }
695 char* ObjectID
= strdup( pObjectID
);
696 char* BrowseFlag
= strdup( pBrowseFlag
);
697 char* Filter
= strdup( pFilter
);
698 char* StartingIndex
= strdup( pStartingIndex
);
699 char* RequestedCount
= strdup( pRequestedCount
);
700 char* SortCriteria
= strdup( pSortCriteria
);
702 char* serviceType
= strdup( CONTENT_DIRECTORY_SERVICE_TYPE
);
706 res
= UpnpAddToAction( &action
, "Browse", serviceType
, "ObjectID", ObjectID
);
707 if ( res
!= UPNP_E_SUCCESS
) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup
; }
709 res
= UpnpAddToAction( &action
, "Browse", serviceType
, "BrowseFlag", BrowseFlag
);
710 if ( res
!= UPNP_E_SUCCESS
) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup
; }
712 res
= UpnpAddToAction( &action
, "Browse", serviceType
, "Filter", Filter
);
713 if ( res
!= UPNP_E_SUCCESS
) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup
; }
715 res
= UpnpAddToAction( &action
, "Browse", serviceType
, "StartingIndex", StartingIndex
);
716 if ( res
!= UPNP_E_SUCCESS
) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup
; }
718 res
= UpnpAddToAction( &action
, "Browse", serviceType
, "RequestedCount", RequestedCount
);
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
, "SortCriteria", SortCriteria
);
722 if ( res
!= UPNP_E_SUCCESS
) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup
; }
724 res
= UpnpSendAction( _cookie
->clientHandle
,
726 CONTENT_DIRECTORY_SERVICE_TYPE
,
730 if ( res
!= UPNP_E_SUCCESS
)
732 msg_Dbg( _cookie
->serviceDiscovery
, "%s:%d: ERROR: %s", __FILE__
, __LINE__
, UpnpGetErrorMessage( res
) );
733 ixmlDocument_free( response
);
742 free( StartingIndex
);
743 free( RequestedCount
);
744 free( SortCriteria
);
748 ixmlDocument_free( action
);
752 void MediaServer::fetchContents()
754 Container
* root
= new Container( 0, "0", getFriendlyName() );
755 playlist_t
* p_playlist
= pl_Get( _cookie
->serviceDiscovery
);
756 _fetchContents( root
);
761 playlist_NodeEmpty( p_playlist
, _playlistNode
, VLC_TRUE
);
767 _contents
->setPlaylistNode( _playlistNode
);
769 _buildPlaylist( _contents
);
772 bool MediaServer::_fetchContents( Container
* parent
)
774 if (!parent
) { msg_Dbg( _cookie
->serviceDiscovery
, "%s:%d: parent==NULL", __FILE__
, __LINE__
); return false; }
776 IXML_Document
* response
= _browseAction( parent
->getObjectID(), "BrowseDirectChildren", "*", "0", "0", "" );
777 if ( !response
) { msg_Dbg( _cookie
->serviceDiscovery
, "%s:%d: ERROR!", __FILE__
, __LINE__
); return false; }
779 IXML_Document
* result
= parseBrowseResult( response
);
780 ixmlDocument_free( response
);
781 if ( !result
) { msg_Dbg( _cookie
->serviceDiscovery
, "%s:%d: ERROR!", __FILE__
, __LINE__
); return false; }
783 IXML_NodeList
* containerNodeList
= ixmlDocument_getElementsByTagName( result
, "container" );
784 if ( containerNodeList
)
786 for ( unsigned int i
= 0; i
< ixmlNodeList_length( containerNodeList
); i
++ )
788 IXML_Element
* containerElement
= ( IXML_Element
* )ixmlNodeList_item( containerNodeList
, i
);
790 const char* objectID
= ixmlElement_getAttribute( containerElement
, "id" );
791 if ( !objectID
) continue;
793 const char* childCountStr
= ixmlElement_getAttribute( containerElement
, "childCount" );
794 if ( !childCountStr
) continue;
795 int childCount
= atoi( childCountStr
);
797 const char* title
= xml_getChildElementValue( containerElement
, "dc:title" );
798 if ( !title
) continue;
800 const char* resource
= xml_getChildElementValue( containerElement
, "res" );
802 if ( resource
&& childCount
< 1 )
804 Item
* item
= new Item( parent
, objectID
, title
, resource
);
805 parent
->addItem( item
);
809 Container
* container
= new Container( parent
, objectID
, title
);
810 parent
->addContainer( container
);
812 if ( childCount
> 0 ) _fetchContents( container
);
816 ixmlNodeList_free( containerNodeList
);
819 IXML_NodeList
* itemNodeList
= ixmlDocument_getElementsByTagName( result
, "item" );
822 for ( unsigned int i
= 0; i
< ixmlNodeList_length( itemNodeList
); i
++ )
824 IXML_Element
* itemElement
= ( IXML_Element
* )ixmlNodeList_item( itemNodeList
, i
);
826 const char* objectID
= ixmlElement_getAttribute( itemElement
, "id" );
827 if ( !objectID
) continue;
829 const char* title
= xml_getChildElementValue( itemElement
, "dc:title" );
830 if ( !title
) continue;
832 const char* resource
= xml_getChildElementValue( itemElement
, "res" );
833 if ( !resource
) continue;
835 Item
* item
= new Item( parent
, objectID
, title
, resource
);
836 parent
->addItem( item
);
839 ixmlNodeList_free( itemNodeList
);
842 ixmlDocument_free( result
);
847 void MediaServer::_buildPlaylist( Container
* parent
)
849 playlist_t
*p_playlist
= pl_Get( _cookie
->serviceDiscovery
);
850 for ( unsigned int i
= 0; i
< parent
->getNumContainers(); i
++ )
852 Container
* container
= parent
->getContainer( i
);
853 playlist_item_t
* parentNode
= parent
->getPlaylistNode();
855 char* title
= strdup( container
->getTitle() );
856 playlist_item_t
* node
= playlist_NodeCreate( p_playlist
, title
, parentNode
, 0 );
859 container
->setPlaylistNode( node
);
860 _buildPlaylist( container
);
863 for ( unsigned int i
= 0; i
< parent
->getNumItems(); i
++ )
865 Item
* item
= parent
->getItem( i
);
866 playlist_item_t
* parentNode
= parent
->getPlaylistNode();
868 input_item_t
* p_input
= input_ItemNew( _cookie
->serviceDiscovery
,
872 playlist_BothAddInput( p_playlist
, p_input
, parentNode
,
873 PLAYLIST_APPEND
, PLAYLIST_END
, &i_cat
, NULL
,
875 /* TODO: do this better by storing ids */
876 playlist_item_t
*p_node
= playlist_ItemGetById( p_playlist
, i_cat
, VLC_FALSE
);
878 item
->setPlaylistNode( p_node
);
882 void MediaServer::setPlaylistNode( playlist_item_t
* playlistNode
)
884 _playlistNode
= playlistNode
;
887 bool MediaServer::compareSID( const char* sid
)
889 return ( strncmp( _subscriptionID
, sid
, sizeof( Upnp_SID
) ) == 0 );
893 // MediaServerList...
895 MediaServerList::MediaServerList( Cookie
* cookie
)
900 MediaServerList::~MediaServerList()
902 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
908 bool MediaServerList::addServer( MediaServer
* s
)
910 if ( getServer( s
->getUDN() ) != 0 ) return false;
912 msg_Dbg( _cookie
->serviceDiscovery
, "Adding server '%s'", s
->getFriendlyName() );
914 _list
.push_back( s
);
916 char* name
= strdup( s
->getFriendlyName() );
917 playlist_item_t
* node
= playlist_NodeCreate( pl_Get( _cookie
->serviceDiscovery
),
919 _cookie
->serviceDiscovery
->p_sys
->p_node_cat
, 0 );
921 s
->setPlaylistNode( node
);
926 MediaServer
* MediaServerList::getServer( const char* UDN
)
928 MediaServer
* result
= 0;
930 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
932 if( strcmp( UDN
, _list
[i
]->getUDN() ) == 0 )
942 MediaServer
* MediaServerList::getServerBySID( const char* sid
)
944 MediaServer
* server
= 0;
946 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
948 if ( _list
[i
]->compareSID( sid
) )
958 void MediaServerList::removeServer( const char* UDN
)
960 MediaServer
* server
= getServer( UDN
);
961 if ( !server
) return;
963 msg_Dbg( _cookie
->serviceDiscovery
, "Removing server '%s'", server
->getFriendlyName() );
965 std::vector
<MediaServer
*>::iterator it
;
966 for ( it
= _list
.begin(); it
!= _list
.end(); it
++ )
980 Item::Item( Container
* parent
, const char* objectID
, const char* title
, const char* resource
)
984 _objectID
= objectID
;
986 _resource
= resource
;
991 const char* Item::getObjectID() const
993 return _objectID
.c_str();
996 const char* Item::getTitle() const
998 return _title
.c_str();
1001 const char* Item::getResource() const
1003 return _resource
.c_str();
1006 void Item::setPlaylistNode( playlist_item_t
* node
)
1008 _playlistNode
= node
;
1011 playlist_item_t
* Item::getPlaylistNode() const
1013 return _playlistNode
;
1019 Container::Container( Container
* parent
, const char* objectID
, const char* title
)
1023 _objectID
= objectID
;
1029 Container::~Container()
1031 for ( unsigned int i
= 0; i
< _containers
.size(); i
++ )
1033 delete _containers
[i
];
1036 for ( unsigned int i
= 0; i
< _items
.size(); i
++ )
1042 void Container::addItem( Item
* item
)
1044 _items
.push_back( item
);
1047 void Container::addContainer( Container
* container
)
1049 _containers
.push_back( container
);
1052 const char* Container::getObjectID() const
1054 return _objectID
.c_str();
1057 const char* Container::getTitle() const
1059 return _title
.c_str();
1062 unsigned int Container::getNumItems() const
1064 return _items
.size();
1067 unsigned int Container::getNumContainers() const
1069 return _containers
.size();
1072 Item
* Container::getItem( unsigned int i
) const
1074 if ( i
< _items
.size() ) return _items
[i
];
1078 Container
* Container::getContainer( unsigned int i
) const
1080 if ( i
< _containers
.size() ) return _containers
[i
];
1084 void Container::setPlaylistNode( playlist_item_t
* node
)
1086 _playlistNode
= node
;
1089 playlist_item_t
* Container::getPlaylistNode() const
1091 return _playlistNode
;