1 /*****************************************************************************
2 * Upnp.cpp : UPnP discovery module (libupnp)
3 *****************************************************************************
4 * Copyright (C) 2004-2011 the VideoLAN team
7 * Authors: RĂ©mi Denis-Courmont <rem # videolan.org> (original plugin)
8 * Christian Henz <henz # c-lab.de>
9 * Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
11 * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
35 #include <vlc_plugin.h>
36 #include <vlc_services_discovery.h>
41 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
42 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
45 struct services_discovery_sys_t
47 UpnpClient_Handle client_handle;
48 MediaServerList* p_server_list;
49 vlc_mutex_t callback_lock;
52 // VLC callback prototypes
53 static int Open( vlc_object_t* );
54 static void Close( vlc_object_t* );
55 VLC_SD_PROBE_HELPER( "upnp", "Universal Plug'n'Play", SD_CAT_LAN )
60 set_shortname( "UPnP" );
61 set_description( N_( "Universal Plug'n'Play" ) );
62 set_category( CAT_PLAYLIST );
63 set_subcategory( SUBCAT_PLAYLIST_SD );
64 set_capability( "services_discovery", 0 );
65 set_callbacks( Open, Close );
67 VLC_SD_PROBE_SUBMODULE
73 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data );
75 const char* xml_getChildElementValue( IXML_Element* p_parent,
76 const char* psz_tag_name );
78 IXML_Document* parseBrowseResult( IXML_Document* p_doc );
83 static int Open( vlc_object_t *p_this )
86 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
87 services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
88 calloc( 1, sizeof( services_discovery_sys_t ) );
90 if( !( p_sd->p_sys = p_sys ) )
93 i_res = UpnpInit( 0, 0 );
94 if( i_res != UPNP_E_SUCCESS )
96 msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
101 p_sys->p_server_list = new MediaServerList( p_sd );
102 vlc_mutex_init( &p_sys->callback_lock );
104 i_res = UpnpRegisterClient( Callback, p_sd, &p_sys->client_handle );
105 if( i_res != UPNP_E_SUCCESS )
107 msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
108 Close( (vlc_object_t*) p_sd );
112 i_res = UpnpSearchAsync( p_sys->client_handle, 5,
113 MEDIA_SERVER_DEVICE_TYPE, p_sd );
114 if( i_res != UPNP_E_SUCCESS )
116 msg_Err( p_sd, "Error sending search request: %s", UpnpGetErrorMessage( i_res ) );
117 Close( (vlc_object_t*) p_sd );
121 i_res = UpnpSetMaxContentLength( 262144 );
122 if( i_res != UPNP_E_SUCCESS )
124 msg_Err( p_sd, "Failed to set maximum content length: %s", UpnpGetErrorMessage( i_res ) );
125 Close( (vlc_object_t*) p_sd );
132 static void Close( vlc_object_t *p_this )
134 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
136 UpnpUnRegisterClient( p_sd->p_sys->client_handle );
139 delete p_sd->p_sys->p_server_list;
140 vlc_mutex_destroy( &p_sd->p_sys->callback_lock );
145 // XML utility functions:
147 // Returns the value of a child element, or 0 on error
148 const char* xml_getChildElementValue( IXML_Element* p_parent,
149 const char* psz_tag_name_ )
151 if ( !p_parent ) return 0;
152 if ( !psz_tag_name_ ) return 0;
154 char* psz_tag_name = strdup( psz_tag_name_ );
155 IXML_NodeList* p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
156 free( psz_tag_name );
157 if ( !p_node_list ) return 0;
159 IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
160 ixmlNodeList_free( p_node_list );
161 if ( !p_element ) return 0;
163 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
164 if ( !p_text_node ) return 0;
166 return ixmlNode_getNodeValue( p_text_node );
169 // Extracts the result document from a SOAP response
170 IXML_Document* parseBrowseResult( IXML_Document* p_doc )
172 ixmlRelaxParser( 1 );
174 if ( !p_doc ) return 0;
176 IXML_NodeList* p_result_list = ixmlDocument_getElementsByTagName( p_doc,
179 if ( !p_result_list ) return 0;
181 IXML_Node* p_result_node = ixmlNodeList_item( p_result_list, 0 );
183 ixmlNodeList_free( p_result_list );
185 if ( !p_result_node ) return 0;
187 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_result_node );
188 if ( !p_text_node ) return 0;
190 const char* psz_result_string = ixmlNode_getNodeValue( p_text_node );
191 char* psz_result_xml = strdup( psz_result_string );
193 IXML_Document* p_browse_doc = ixmlParseBuffer( psz_result_xml );
195 free( psz_result_xml );
201 // Handles all UPnP events
202 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data )
204 services_discovery_t* p_sd = ( services_discovery_t* ) p_user_data;
205 services_discovery_sys_t* p_sys = p_sd->p_sys;
206 vlc_mutex_locker locker( &p_sys->callback_lock );
210 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
211 case UPNP_DISCOVERY_SEARCH_RESULT:
213 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
215 IXML_Document *p_description_doc = 0;
218 i_res = UpnpDownloadXmlDoc( p_discovery->Location, &p_description_doc );
219 if ( i_res != UPNP_E_SUCCESS )
221 msg_Warn( p_sd, "Could not download device description! "
222 "Fetching data from %s failed: %s",
223 p_discovery->Location, UpnpGetErrorMessage( i_res ) );
227 MediaServer::parseDeviceDescription( p_description_doc,
228 p_discovery->Location, p_sd );
230 ixmlDocument_free( p_description_doc );
234 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
236 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
238 p_sys->p_server_list->removeServer( p_discovery->DeviceId );
243 case UPNP_EVENT_RECEIVED:
245 Upnp_Event* p_e = ( Upnp_Event* )p_event;
247 MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_e->Sid );
248 if ( p_server ) p_server->fetchContents();
252 case UPNP_EVENT_AUTORENEWAL_FAILED:
253 case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
257 Upnp_Event_Subscribe* p_s = ( Upnp_Event_Subscribe* )p_event;
259 MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_s->Sid );
260 if ( p_server ) p_server->subscribeToContentDirectory();
264 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
265 msg_Warn( p_sd, "subscription complete" );
268 case UPNP_DISCOVERY_SEARCH_TIMEOUT:
269 msg_Warn( p_sd, "search timeout" );
273 msg_Err( p_sd, "Unhandled event, please report ( type=%d )", event_type );
277 return UPNP_E_SUCCESS;
281 // Class implementations...
285 void MediaServer::parseDeviceDescription( IXML_Document* p_doc,
286 const char* p_location,
287 services_discovery_t* p_sd )
291 msg_Err( p_sd, "Null IXML_Document" );
297 msg_Err( p_sd, "Null location" );
301 const char* psz_base_url = p_location;
303 // Try to extract baseURL
304 IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( p_doc, "baseURL" );
308 if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) )
310 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node );
311 if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node );
314 ixmlNodeList_free( p_url_list );
318 IXML_NodeList* p_device_list =
319 ixmlDocument_getElementsByTagName( p_doc, "device" );
323 for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ )
325 IXML_Element* p_device_element =
326 ( IXML_Element* ) ixmlNodeList_item( p_device_list, i );
328 const char* psz_device_type = xml_getChildElementValue( p_device_element,
330 if ( !psz_device_type )
332 msg_Warn( p_sd, "No deviceType found!" );
336 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type ) != 0 )
339 const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" );
342 msg_Warn( p_sd, "No UDN!" );
346 // Check if server is already added
347 if ( p_sd->p_sys->p_server_list->getServer( psz_udn ) != 0 )
349 msg_Warn( p_sd, "Server with uuid '%s' already exists.", psz_udn );
353 const char* psz_friendly_name =
354 xml_getChildElementValue( p_device_element,
357 if ( !psz_friendly_name )
359 msg_Dbg( p_sd, "No friendlyName!" );
363 MediaServer* p_server = new MediaServer( psz_udn, psz_friendly_name, p_sd );
365 if ( !p_sd->p_sys->p_server_list->addServer( p_server ) )
372 // Check for ContentDirectory service...
373 IXML_NodeList* p_service_list =
374 ixmlElement_getElementsByTagName( p_device_element,
376 if ( p_service_list )
378 for ( unsigned int j = 0;
379 j < ixmlNodeList_length( p_service_list ); j++ )
381 IXML_Element* p_service_element =
382 ( IXML_Element* ) ixmlNodeList_item( p_service_list, j );
384 const char* psz_service_type =
385 xml_getChildElementValue( p_service_element,
387 if ( !psz_service_type )
389 msg_Warn( p_sd, "No service type found." );
393 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
394 psz_service_type ) != 0 )
397 const char* psz_event_sub_url =
398 xml_getChildElementValue( p_service_element,
400 if ( !psz_event_sub_url )
402 msg_Warn( p_sd, "No event subscription url found." );
406 const char* psz_control_url =
407 xml_getChildElementValue( p_service_element,
409 if ( !psz_control_url )
411 msg_Warn( p_sd, "No control url found." );
415 // Try to subscribe to ContentDirectory service
417 char* psz_url = ( char* ) malloc( strlen( psz_base_url ) +
418 strlen( psz_event_sub_url ) + 1 );
421 char* psz_s1 = strdup( psz_base_url );
422 char* psz_s2 = strdup( psz_event_sub_url );
424 if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
427 p_server->setContentDirectoryEventURL( psz_url );
428 p_server->subscribeToContentDirectory();
436 // Try to browse content directory...
438 psz_url = ( char* ) malloc( strlen( psz_base_url ) +
439 strlen( psz_control_url ) + 1 );
442 char* psz_s1 = strdup( psz_base_url );
443 char* psz_s2 = strdup( psz_control_url );
445 if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
448 p_server->setContentDirectoryControlURL( psz_url );
449 p_server->fetchContents();
457 ixmlNodeList_free( p_service_list );
460 ixmlNodeList_free( p_device_list );
464 MediaServer::MediaServer( const char* psz_udn,
465 const char* psz_friendly_name,
466 services_discovery_t* p_sd )
471 _friendly_name = psz_friendly_name;
474 _p_input_item = NULL;
477 MediaServer::~MediaServer()
482 const char* MediaServer::getUDN() const
487 const char* MediaServer::getFriendlyName() const
489 return _friendly_name.c_str();
492 void MediaServer::setContentDirectoryEventURL( const char* psz_url )
494 _content_directory_event_url = psz_url;
497 const char* MediaServer::getContentDirectoryEventURL() const
499 return _content_directory_event_url.c_str();
502 void MediaServer::setContentDirectoryControlURL( const char* psz_url )
504 _content_directory_control_url = psz_url;
507 const char* MediaServer::getContentDirectoryControlURL() const
509 return _content_directory_control_url.c_str();
512 void MediaServer::subscribeToContentDirectory()
514 const char* psz_url = getContentDirectoryEventURL();
517 msg_Dbg( _p_sd, "No subscription url set!" );
521 int i_timeout = 1810;
524 int i_res = UpnpSubscribe( _p_sd->p_sys->client_handle, psz_url, &i_timeout, sid );
526 if ( i_res == UPNP_E_SUCCESS )
528 _i_subscription_timeout = i_timeout;
529 memcpy( _subscription_id, sid, sizeof( Upnp_SID ) );
533 msg_Dbg( _p_sd, "Subscribe failed: '%s': %s",
534 getFriendlyName(), UpnpGetErrorMessage( i_res ) );
538 IXML_Document* MediaServer::_browseAction( const char* psz_object_id_,
539 const char* psz_browser_flag_,
540 const char* psz_filter_,
541 const char* psz_starting_index_,
542 const char* psz_requested_count_,
543 const char* psz_sort_criteria_ )
545 IXML_Document* p_action = 0;
546 IXML_Document* p_response = 0;
547 const char* psz_url = getContentDirectoryControlURL();
551 msg_Dbg( _p_sd, "No subscription url set!" );
555 char* psz_object_id = strdup( psz_object_id_ );
556 char* psz_browse_flag = strdup( psz_browser_flag_ );
557 char* psz_filter = strdup( psz_filter_ );
558 char* psz_starting_index = strdup( psz_starting_index_ );
559 char* psz_requested_count = strdup( psz_requested_count_ );
560 char* psz_sort_criteria = strdup( psz_sort_criteria_ );
561 char* psz_service_type = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
565 i_res = UpnpAddToAction( &p_action, "Browse",
566 psz_service_type, "ObjectID", psz_object_id );
568 if ( i_res != UPNP_E_SUCCESS )
570 msg_Dbg( _p_sd, "AddToAction 'ObjectID' failed: %s",
571 UpnpGetErrorMessage( i_res ) );
572 goto browseActionCleanup;
575 i_res = UpnpAddToAction( &p_action, "Browse",
576 psz_service_type, "BrowseFlag", psz_browse_flag );
578 if ( i_res != UPNP_E_SUCCESS )
580 msg_Dbg( _p_sd, "AddToAction 'BrowseFlag' failed: %s",
581 UpnpGetErrorMessage( i_res ) );
582 goto browseActionCleanup;
585 i_res = UpnpAddToAction( &p_action, "Browse",
586 psz_service_type, "Filter", psz_filter );
588 if ( i_res != UPNP_E_SUCCESS )
590 msg_Dbg( _p_sd, "AddToAction 'Filter' failed: %s",
591 UpnpGetErrorMessage( i_res ) );
592 goto browseActionCleanup;
595 i_res = UpnpAddToAction( &p_action, "Browse",
596 psz_service_type, "StartingIndex", psz_starting_index );
598 if ( i_res != UPNP_E_SUCCESS )
600 msg_Dbg( _p_sd, "AddToAction 'StartingIndex' failed: %s",
601 UpnpGetErrorMessage( i_res ) );
602 goto browseActionCleanup;
605 i_res = UpnpAddToAction( &p_action, "Browse",
606 psz_service_type, "RequestedCount", psz_requested_count );
608 if ( i_res != UPNP_E_SUCCESS )
610 msg_Dbg( _p_sd, "AddToAction 'RequestedCount' failed: %s",
611 UpnpGetErrorMessage( i_res ) );
612 goto browseActionCleanup;
615 i_res = UpnpAddToAction( &p_action, "Browse",
616 psz_service_type, "SortCriteria", psz_sort_criteria );
618 if ( i_res != UPNP_E_SUCCESS )
620 msg_Dbg( _p_sd, "AddToAction 'SortCriteria' failed: %s",
621 UpnpGetErrorMessage( i_res ) );
622 goto browseActionCleanup;
625 i_res = UpnpSendAction( _p_sd->p_sys->client_handle,
628 0, // ignored in SDK, must be NULL
632 if ( i_res != UPNP_E_SUCCESS )
634 msg_Err( _p_sd, "%s when trying the send() action with URL: %s",
635 UpnpGetErrorMessage( i_res ), psz_url );
637 ixmlDocument_free( p_response );
643 free( psz_object_id );
644 free( psz_browse_flag );
646 free( psz_starting_index );
647 free( psz_requested_count );
648 free( psz_sort_criteria );
650 free( psz_service_type );
652 ixmlDocument_free( p_action );
656 void MediaServer::fetchContents()
658 // Delete previous contents to prevent duplicate entries
662 services_discovery_RemoveItem( _p_sd, _p_input_item );
663 services_discovery_AddItem( _p_sd, _p_input_item, NULL );
666 Container* root = new Container( 0, "0", getFriendlyName() );
668 _fetchContents( root );
671 _p_contents->setInputItem( _p_input_item );
673 _buildPlaylist( _p_contents, NULL );
676 bool MediaServer::_fetchContents( Container* p_parent )
680 msg_Err( _p_sd, "No parent" );
684 IXML_Document* p_response = _browseAction( p_parent->getObjectID(),
685 "BrowseDirectChildren",
689 msg_Err( _p_sd, "No response from browse() action" );
693 IXML_Document* p_result = parseBrowseResult( p_response );
694 ixmlDocument_free( p_response );
698 msg_Err( _p_sd, "browse() response parsing failed" );
704 msg_Dbg( _p_sd, "Got DIDL document: %s",
705 ixmlPrintDocument( p_result ) );
709 IXML_NodeList* containerNodeList =
710 ixmlDocument_getElementsByTagName( p_result, "container" );
712 if ( containerNodeList )
714 for ( unsigned int i = 0;
715 i < ixmlNodeList_length( containerNodeList ); i++ )
717 IXML_Element* containerElement =
718 ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
720 const char* objectID = ixmlElement_getAttribute( containerElement,
725 const char* childCountStr =
726 ixmlElement_getAttribute( containerElement, "childCount" );
728 if ( !childCountStr )
731 int childCount = atoi( childCountStr );
732 const char* title = xml_getChildElementValue( containerElement,
738 const char* resource = xml_getChildElementValue( containerElement,
741 if ( resource && childCount < 1 )
743 Item* item = new Item( p_parent, objectID, title, resource );
744 p_parent->addItem( item );
749 Container* container = new Container( p_parent, objectID, title );
750 p_parent->addContainer( container );
752 if ( childCount > 0 )
753 _fetchContents( container );
756 ixmlNodeList_free( containerNodeList );
759 IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( p_result,
763 for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
765 IXML_Element* itemElement =
766 ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
768 const char* objectID =
769 ixmlElement_getAttribute( itemElement, "id" );
775 xml_getChildElementValue( itemElement, "dc:title" );
780 const char* resource =
781 xml_getChildElementValue( itemElement, "res" );
786 Item* item = new Item( p_parent, objectID, title, resource );
787 p_parent->addItem( item );
789 ixmlNodeList_free( itemNodeList );
792 ixmlDocument_free( p_result );
796 // TODO: Create a permanent fix for the item duplication bug. The current fix
797 // is essentially only a small hack. Although it fixes the problem, it introduces
798 // annoying cosmetic issues with the playlist. For example, when the UPnP Server
799 // rebroadcasts it's directory structure, the VLC Client deletes the old directory
800 // structure, causing the user to go back to the root node of the directory. The
801 // directory is then rebuilt, and the user is forced to traverse through the directory
802 // to find the item they were looking for. Some servers may not push the directory
803 // structure too often, but we cannot rely on this fix.
805 // I have thought up another fix, but this would require certain features to
806 // be present within the VLC services discovery. Currently, services_discovery_AddItem
807 // does not allow the programmer to nest items. It only allows a "2 deep" scope.
808 // An example of the limitation is below:
814 // services_discovery_AddItem will not let the programmer specify a child-node to
815 // insert items into, so we would not be able to do the following:
821 // + Sub Item 1 of Item 2
822 // + Sub-Sub Item 1 of Sub Item 1
824 // This creates a HUGE limitation on what we are able to do. If we were able to do
825 // the above, we could simply preserve the old directory listing, and compare what items
826 // do not exist in the new directory listing, then remove them from the shown listing using
827 // services_discovery_RemoveItem. If new files were introduced within an already existing
828 // container, we could simply do so with services_discovery_AddItem.
829 void MediaServer::_buildPlaylist( Container* p_parent, input_item_node_t *p_input_node )
831 bool b_send = p_input_node == NULL;
833 p_input_node = input_item_node_Create( p_parent->getInputItem() );
835 for ( unsigned int i = 0; i < p_parent->getNumContainers(); i++ )
837 Container* p_container = p_parent->getContainer( i );
839 input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop",
840 p_container->getTitle() );
841 input_item_node_t *p_new_node =
842 input_item_node_AppendItem( p_input_node, p_input_item );
844 p_container->setInputItem( p_input_item );
845 _buildPlaylist( p_container, p_new_node );
848 for ( unsigned int i = 0; i < p_parent->getNumItems(); i++ )
850 Item* p_item = p_parent->getItem( i );
852 input_item_t* p_input_item = input_item_New( _p_sd,
853 p_item->getResource(),
854 p_item->getTitle() );
855 assert( p_input_item );
856 input_item_node_AppendItem( p_input_node, p_input_item );
857 p_item->setInputItem( p_input_item );
861 input_item_node_PostAndDelete( p_input_node );
864 void MediaServer::setInputItem( input_item_t* p_input_item )
866 if( _p_input_item == p_input_item )
870 vlc_gc_decref( _p_input_item );
872 vlc_gc_incref( p_input_item );
873 _p_input_item = p_input_item;
876 input_item_t* MediaServer::getInputItem() const
878 return _p_input_item;
881 bool MediaServer::compareSID( const char* psz_sid )
883 return ( strncmp( _subscription_id, psz_sid, sizeof( Upnp_SID ) ) == 0 );
887 // MediaServerList...
889 MediaServerList::MediaServerList( services_discovery_t* p_sd )
894 MediaServerList::~MediaServerList()
896 for ( unsigned int i = 0; i < _list.size(); i++ )
902 bool MediaServerList::addServer( MediaServer* p_server )
904 input_item_t* p_input_item = NULL;
905 if ( getServer( p_server->getUDN() ) != 0 ) return false;
907 msg_Dbg( _p_sd, "Adding server '%s' with uuid '%s'", p_server->getFriendlyName(), p_server->getUDN() );
909 p_input_item = input_item_New( _p_sd, "vlc://nop",
910 p_server->getFriendlyName() );
911 p_server->setInputItem( p_input_item );
913 services_discovery_AddItem( _p_sd, p_input_item, NULL );
915 _list.push_back( p_server );
920 MediaServer* MediaServerList::getServer( const char* psz_udn )
922 MediaServer* p_result = 0;
924 for ( unsigned int i = 0; i < _list.size(); i++ )
926 if( strcmp( psz_udn, _list[i]->getUDN() ) == 0 )
936 MediaServer* MediaServerList::getServerBySID( const char* psz_sid )
938 MediaServer* p_server = 0;
940 for ( unsigned int i = 0; i < _list.size(); i++ )
942 if ( _list[i]->compareSID( psz_sid ) )
952 void MediaServerList::removeServer( const char* psz_udn )
954 MediaServer* p_server = getServer( psz_udn );
955 if ( !p_server ) return;
957 msg_Dbg( _p_sd, "Removing server '%s'", p_server->getFriendlyName() );
959 services_discovery_RemoveItem( _p_sd, p_server->getInputItem() );
961 std::vector<MediaServer*>::iterator it;
962 for ( it = _list.begin(); it != _list.end(); ++it )
964 if ( *it == p_server )
976 Item::Item( Container* p_parent, const char* psz_object_id, const char* psz_title,
977 const char* psz_resource )
981 _objectID = psz_object_id;
983 _resource = psz_resource;
985 _p_input_item = NULL;
991 vlc_gc_decref( _p_input_item );
994 const char* Item::getObjectID() const
996 return _objectID.c_str();
999 const char* Item::getTitle() const
1001 return _title.c_str();
1004 const char* Item::getResource() const
1006 return _resource.c_str();
1009 void Item::setInputItem( input_item_t* p_input_item )
1011 if( _p_input_item == p_input_item )
1015 vlc_gc_decref( _p_input_item );
1017 vlc_gc_incref( p_input_item );
1018 _p_input_item = p_input_item;
1023 Container::Container( Container* p_parent,
1024 const char* psz_object_id,
1025 const char* psz_title )
1029 _objectID = psz_object_id;
1032 _p_input_item = NULL;
1035 Container::~Container()
1037 for ( unsigned int i = 0; i < _containers.size(); i++ )
1039 delete _containers[i];
1042 for ( unsigned int i = 0; i < _items.size(); i++ )
1048 vlc_gc_decref( _p_input_item );
1051 void Container::addItem( Item* item )
1053 _items.push_back( item );
1056 void Container::addContainer( Container* p_container )
1058 _containers.push_back( p_container );
1061 const char* Container::getObjectID() const
1063 return _objectID.c_str();
1066 const char* Container::getTitle() const
1068 return _title.c_str();
1071 unsigned int Container::getNumItems() const
1073 return _items.size();
1076 unsigned int Container::getNumContainers() const
1078 return _containers.size();
1081 Item* Container::getItem( unsigned int i_index ) const
1083 if ( i_index < _items.size() ) return _items[i_index];
1087 Container* Container::getContainer( unsigned int i_index ) const
1089 if ( i_index < _containers.size() ) return _containers[i_index];
1093 Container* Container::getParent()
1098 void Container::setInputItem( input_item_t* p_input_item )
1100 if( _p_input_item == p_input_item )
1104 vlc_gc_decref( _p_input_item );
1106 vlc_gc_incref( p_input_item );
1107 _p_input_item = p_input_item;
1110 input_item_t* Container::getInputItem() const
1112 return _p_input_item;