Fix make dist
[vlc.git] / modules / services_discovery / upnp_intel.cpp
blob7a721e4c423ecfdeb698e331db59065e9b4c27a3
1 /*****************************************************************************
2 * Upnp_intell.cpp : UPnP discovery module (Intel SDK)
3 *****************************************************************************
4 * Copyright (C) 2004-2006 the VideoLAN team
5 * $Id$
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 ???
33 #include <vector>
34 #include <string>
36 #include <upnp/upnp.h>
37 #include <upnp/upnptools.h>
39 #undef PACKAGE_NAME
40 #include <vlc/vlc.h>
41 #include <vlc_playlist.h>
42 #include "vlc_strings.h"
45 // VLC handle
47 struct services_discovery_sys_t
49 playlist_item_t *p_node_cat;
50 playlist_item_t *p_node_one;
54 // Constants
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";
60 // Classes
62 class MediaServer;
63 class MediaServerList;
64 class Item;
65 class Container;
67 // Cookie that is passed to the callback
69 typedef struct
71 services_discovery_t* serviceDiscovery;
72 UpnpClient_Handle clientHandle;
73 MediaServerList* serverList;
74 } Cookie;
77 // Class definitions...
79 class Lockable
81 public:
83 Lockable( Cookie* c )
85 vlc_mutex_init( c->serviceDiscovery, &_mutex );
88 ~Lockable()
90 vlc_mutex_destroy( &_mutex );
93 void lock() { vlc_mutex_lock( &_mutex ); }
94 void unlock() { vlc_mutex_unlock( &_mutex ); }
96 private:
98 vlc_mutex_t _mutex;
102 class Locker
104 public:
105 Locker( Lockable* l )
107 _lockable = l;
108 _lockable->lock();
111 ~Locker()
113 _lockable->unlock();
116 private:
117 Lockable* _lockable;
121 class MediaServer
123 public:
125 static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
127 MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
128 ~MediaServer();
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 );
146 private:
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* );
152 Cookie* _cookie;
154 Container* _contents;
155 playlist_item_t* _playlistNode;
157 std::string _UDN;
158 std::string _friendlyName;
160 std::string _contentDirectoryEventURL;
161 std::string _contentDirectoryControlURL;
163 int _subscriptionTimeOut;
164 Upnp_SID _subscriptionID;
168 class MediaServerList
170 public:
172 MediaServerList( Cookie* cookie );
173 ~MediaServerList();
175 bool addServer( MediaServer* s );
176 void removeServer( const char* UDN );
178 MediaServer* getServer( const char* UDN );
179 MediaServer* getServerBySID( const char* );
181 private:
183 Cookie* _cookie;
185 std::vector<MediaServer*> _list;
189 class Item
191 public:
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 ;
202 private:
204 playlist_item_t* _playlistNode;
206 Container* _parent;
207 std::string _objectID;
208 std::string _title;
209 std::string _resource;
213 class Container
215 public:
217 Container( Container* parent, const char* objectID, const char* title );
218 ~Container();
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;
235 private:
237 playlist_item_t* _playlistNode;
239 Container* _parent;
241 std::string _objectID;
242 std::string _title;
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 );
254 // Module descriptor
256 vlc_module_begin();
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 );
263 vlc_module_end();
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 );
275 // VLC callbacks...
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 ) );
283 p_sd->pf_run = Run;
284 p_sd->p_sys = p_sys;
286 /* Create our playlist node */
287 playlist_NodesPairCreate( pl_Get( p_sd ), _("Devices"),
288 &p_sys->p_node_cat, &p_sys->p_node_one,
289 VLC_TRUE );
291 return VLC_SUCCESS;
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,
300 VLC_TRUE );
301 playlist_NodeDelete( pl_Get( p_sd ), p_sys->p_node_cat, VLC_TRUE,
302 VLC_TRUE );
304 free( p_sys );
307 static void Run( services_discovery_t* p_sd )
309 int res;
311 res = UpnpInit( 0, 0 );
312 if( res != UPNP_E_SUCCESS )
314 msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
315 return;
318 Cookie cookie;
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 ) );
328 goto shutDown;
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 ) );
335 goto shutDown;
338 msg_Dbg( p_sd, "UPnP discovery started" );
339 while( !p_sd->b_die )
341 msleep( 500 );
344 msg_Dbg( p_sd, "UPnP discovery stopped" );
346 shutDown:
347 UpnpFinish();
348 delete cookie.serverList;
349 delete CallbackLock;
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 );
363 free( 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 );
400 free( resultXML );
402 return browseDoc;
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;
422 int res;
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__ );
427 return res;
430 MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
432 ixmlDocument_free( descriptionDoc );
434 break;
436 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
438 struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
440 cookie->serverList->removeServer( discovery->DeviceId );
442 break;
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();
451 break;
453 case UPNP_EVENT_AUTORENEWAL_FAILED:
454 case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
456 // Re-subscribe...
458 Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
460 MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
461 if ( server ) server->subscribeToContentDirectory();
463 break;
465 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
466 msg_Warn( cookie->serviceDiscovery, "subscription complete" );
467 break;
469 case UPNP_DISCOVERY_SEARCH_TIMEOUT:
470 msg_Warn( cookie->serviceDiscovery, "search timeout" );
471 break;
473 default:
474 msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
475 break;
478 return UPNP_E_SUCCESS;
482 // Class implementations...
484 // MediaServer...
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" );
496 if ( urlList )
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 );
507 // Get devices
509 IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
510 if ( deviceList )
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 ) ) {
531 delete server;
532 server = 0;
533 continue;
536 // Check for ContentDirectory service...
538 IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
539 if ( serviceList )
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 );
558 if ( url )
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();
571 free( s1 );
572 free( s2 );
573 free( url );
576 // Try to browse content directory...
578 url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
579 if ( url )
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();
592 free( s1 );
593 free( s2 );
594 free( url );
598 ixmlNodeList_free( serviceList );
602 ixmlNodeList_free( deviceList );
606 MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
608 _cookie = cookie;
610 _UDN = UDN;
611 _friendlyName = friendlyName;
613 _contents = 0;
614 _playlistNode = 0;
617 MediaServer::~MediaServer()
619 if ( _contents )
621 playlist_NodeDelete( pl_Get( _cookie->serviceDiscovery ) ,
622 _playlistNode, VLC_TRUE, VLC_TRUE );
625 delete _contents;
628 const char* MediaServer::getUDN() const
630 const char* s = _UDN.c_str();
631 return s;
634 const char* MediaServer::getFriendlyName() const
636 const char* s = _friendlyName.c_str();
637 return s;
640 void MediaServer::setContentDirectoryEventURL( const char* url )
642 _contentDirectoryEventURL = url;
645 const char* MediaServer::getContentDirectoryEventURL() const
647 const char* s = _contentDirectoryEventURL.c_str();
648 return s;
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!" );
667 return;
670 int timeOut = 1810;
671 Upnp_SID sid;
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 ) );
680 else
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 );
704 int res;
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,
725 url,
726 CONTENT_DIRECTORY_SERVICE_TYPE,
728 action,
729 &response );
730 if ( res != UPNP_E_SUCCESS )
732 msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
733 ixmlDocument_free( response );
734 response = 0;
737 browseActionCleanup:
739 free( ObjectID );
740 free( BrowseFlag );
741 free( Filter );
742 free( StartingIndex );
743 free( RequestedCount );
744 free( SortCriteria );
746 free( serviceType );
748 ixmlDocument_free( action );
749 return response;
752 void MediaServer::fetchContents()
754 Container* root = new Container( 0, "0", getFriendlyName() );
755 playlist_t * p_playlist = pl_Get( _cookie->serviceDiscovery );
756 _fetchContents( root );
758 if ( _contents )
760 PL_LOCK;
761 playlist_NodeEmpty( p_playlist, _playlistNode, VLC_TRUE );
762 PL_UNLOCK;
763 delete _contents;
766 _contents = root;
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 );
807 else
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" );
820 if ( itemNodeList )
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 );
844 return true;
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 );
857 free( title );
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,
869 item->getResource(),
870 item->getTitle() );
871 int i_cat;
872 playlist_BothAddInput( p_playlist, p_input, parentNode,
873 PLAYLIST_APPEND, PLAYLIST_END, &i_cat, NULL,
874 VLC_FALSE );
875 /* TODO: do this better by storing ids */
876 playlist_item_t *p_node = playlist_ItemGetById( p_playlist, i_cat, VLC_FALSE );
877 assert( p_node );
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 )
897 _cookie = cookie;
900 MediaServerList::~MediaServerList()
902 for ( unsigned int i = 0; i < _list.size(); i++ )
904 delete _list[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 ),
918 name,
919 _cookie->serviceDiscovery->p_sys->p_node_cat, 0 );
920 free( name );
921 s->setPlaylistNode( node );
923 return true;
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 )
934 result = _list[i];
935 break;
939 return result;
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 ) )
950 server = _list[i];
951 break;
955 return server;
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++ )
968 if ( *it == server )
970 _list.erase( it );
971 delete server;
972 break;
978 // Item...
980 Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
982 _parent = parent;
984 _objectID = objectID;
985 _title = title;
986 _resource = resource;
988 _playlistNode = 0;
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;
1017 // Container...
1019 Container::Container( Container* parent, const char* objectID, const char* title )
1021 _parent = parent;
1023 _objectID = objectID;
1024 _title = title;
1026 _playlistNode = 0;
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++ )
1038 delete _items[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];
1075 return 0;
1078 Container* Container::getContainer( unsigned int i ) const
1080 if ( i < _containers.size() ) return _containers[i];
1081 return 0;
1084 void Container::setPlaylistNode( playlist_item_t* node )
1086 _playlistNode = node;
1089 playlist_item_t* Container::getPlaylistNode() const
1091 return _playlistNode;