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
, "Search failed: %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
)
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
);
242 case UPNP_EVENT_RECEIVED
:
244 Upnp_Event
* p_e
= ( Upnp_Event
* )p_event
;
246 MediaServer
* p_server
= p_sys
->p_server_list
->getServerBySID( p_e
->Sid
);
247 if ( p_server
) p_server
->fetchContents();
251 case UPNP_EVENT_AUTORENEWAL_FAILED
:
252 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
:
256 Upnp_Event_Subscribe
* p_s
= ( Upnp_Event_Subscribe
* )p_event
;
258 MediaServer
* p_server
= p_sys
->p_server_list
->getServerBySID( p_s
->Sid
);
259 if ( p_server
) p_server
->subscribeToContentDirectory();
263 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
264 msg_Warn( p_sd
, "subscription complete" );
267 case UPNP_DISCOVERY_SEARCH_TIMEOUT
:
268 msg_Warn( p_sd
, "search timeout" );
272 msg_Err( p_sd
, "Unhandled event, please report ( type=%d )", event_type
);
276 return UPNP_E_SUCCESS
;
280 // Class implementations...
284 void MediaServer::parseDeviceDescription( IXML_Document
* p_doc
,
285 const char* p_location
,
286 services_discovery_t
* p_sd
)
290 msg_Err( p_sd
, "Null IXML_Document" );
296 msg_Err( p_sd
, "Null location" );
300 const char* psz_base_url
= p_location
;
302 // Try to extract baseURL
303 IXML_NodeList
* p_url_list
= ixmlDocument_getElementsByTagName( p_doc
, "baseURL" );
307 if ( IXML_Node
* p_url_node
= ixmlNodeList_item( p_url_list
, 0 ) )
309 IXML_Node
* p_text_node
= ixmlNode_getFirstChild( p_url_node
);
310 if ( p_text_node
) psz_base_url
= ixmlNode_getNodeValue( p_text_node
);
313 ixmlNodeList_free( p_url_list
);
317 IXML_NodeList
* p_device_list
=
318 ixmlDocument_getElementsByTagName( p_doc
, "device" );
322 for ( unsigned int i
= 0; i
< ixmlNodeList_length( p_device_list
); i
++ )
324 IXML_Element
* p_device_element
=
325 ( IXML_Element
* ) ixmlNodeList_item( p_device_list
, i
);
327 const char* psz_device_type
= xml_getChildElementValue( p_device_element
,
329 if ( !psz_device_type
)
331 msg_Warn( p_sd
, "No deviceType found!" );
335 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE
, psz_device_type
) != 0 )
338 const char* psz_udn
= xml_getChildElementValue( p_device_element
, "UDN" );
341 msg_Warn( p_sd
, "No UDN!" );
345 // Check if server is already added
346 if ( p_sd
->p_sys
->p_server_list
->getServer( psz_udn
) != 0 )
348 msg_Warn( p_sd
, "Server with uuid '%s' already exists.", psz_udn
);
352 const char* psz_friendly_name
=
353 xml_getChildElementValue( p_device_element
,
356 if ( !psz_friendly_name
)
358 msg_Dbg( p_sd
, "No friendlyName!" );
362 MediaServer
* p_server
= new MediaServer( psz_udn
, psz_friendly_name
, p_sd
);
364 if ( !p_sd
->p_sys
->p_server_list
->addServer( p_server
) )
371 // Check for ContentDirectory service...
372 IXML_NodeList
* p_service_list
=
373 ixmlElement_getElementsByTagName( p_device_element
,
375 if ( p_service_list
)
377 for ( unsigned int j
= 0;
378 j
< ixmlNodeList_length( p_service_list
); j
++ )
380 IXML_Element
* p_service_element
=
381 ( IXML_Element
* ) ixmlNodeList_item( p_service_list
, j
);
383 const char* psz_service_type
=
384 xml_getChildElementValue( p_service_element
,
386 if ( !psz_service_type
)
389 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE
,
390 psz_service_type
) != 0 )
393 const char* psz_event_sub_url
=
394 xml_getChildElementValue( p_service_element
,
396 if ( !psz_event_sub_url
)
399 const char* psz_control_url
=
400 xml_getChildElementValue( p_service_element
,
402 if ( !psz_control_url
)
405 // Try to subscribe to ContentDirectory service
407 char* psz_url
= ( char* ) malloc( strlen( psz_base_url
) +
408 strlen( psz_event_sub_url
) + 1 );
411 char* psz_s1
= strdup( psz_base_url
);
412 char* psz_s2
= strdup( psz_event_sub_url
);
414 if ( UpnpResolveURL( psz_s1
, psz_s2
, psz_url
) ==
417 p_server
->setContentDirectoryEventURL( psz_url
);
418 p_server
->subscribeToContentDirectory();
426 // Try to browse content directory...
428 psz_url
= ( char* ) malloc( strlen( psz_base_url
) +
429 strlen( psz_control_url
) + 1 );
432 char* psz_s1
= strdup( psz_base_url
);
433 char* psz_s2
= strdup( psz_control_url
);
435 if ( UpnpResolveURL( psz_s1
, psz_s2
, psz_url
) ==
438 p_server
->setContentDirectoryControlURL( psz_url
);
439 p_server
->fetchContents();
447 ixmlNodeList_free( p_service_list
);
450 ixmlNodeList_free( p_device_list
);
454 MediaServer::MediaServer( const char* psz_udn
,
455 const char* psz_friendly_name
,
456 services_discovery_t
* p_sd
)
461 _friendly_name
= psz_friendly_name
;
464 _p_input_item
= NULL
;
467 MediaServer::~MediaServer()
472 const char* MediaServer::getUDN() const
477 const char* MediaServer::getFriendlyName() const
479 return _friendly_name
.c_str();
482 void MediaServer::setContentDirectoryEventURL( const char* psz_url
)
484 _content_directory_event_url
= psz_url
;
487 const char* MediaServer::getContentDirectoryEventURL() const
489 return _content_directory_event_url
.c_str();
492 void MediaServer::setContentDirectoryControlURL( const char* psz_url
)
494 _content_directory_control_url
= psz_url
;
497 const char* MediaServer::getContentDirectoryControlURL() const
499 return _content_directory_control_url
.c_str();
502 void MediaServer::subscribeToContentDirectory()
504 const char* psz_url
= getContentDirectoryEventURL();
507 msg_Dbg( _p_sd
, "No subscription url set!" );
511 int i_timeout
= 1810;
514 int i_res
= UpnpSubscribe( _p_sd
->p_sys
->client_handle
, psz_url
, &i_timeout
, sid
);
516 if ( i_res
== UPNP_E_SUCCESS
)
518 _i_subscription_timeout
= i_timeout
;
519 memcpy( _subscription_id
, sid
, sizeof( Upnp_SID
) );
523 msg_Dbg( _p_sd
, "Subscribe failed: '%s': %s",
524 getFriendlyName(), UpnpGetErrorMessage( i_res
) );
528 IXML_Document
* MediaServer::_browseAction( const char* psz_object_id_
,
529 const char* psz_browser_flag_
,
530 const char* psz_filter_
,
531 const char* psz_starting_index_
,
532 const char* psz_requested_count_
,
533 const char* psz_sort_criteria_
)
535 IXML_Document
* p_action
= 0;
536 IXML_Document
* p_response
= 0;
537 const char* psz_url
= getContentDirectoryControlURL();
541 msg_Dbg( _p_sd
, "No subscription url set!" );
545 char* psz_object_id
= strdup( psz_object_id_
);
546 char* psz_browse_flag
= strdup( psz_browser_flag_
);
547 char* psz_filter
= strdup( psz_filter_
);
548 char* psz_starting_index
= strdup( psz_starting_index_
);
549 char* psz_requested_count
= strdup( psz_requested_count_
);
550 char* psz_sort_criteria
= strdup( psz_sort_criteria_
);
551 char* psz_service_type
= strdup( CONTENT_DIRECTORY_SERVICE_TYPE
);
555 i_res
= UpnpAddToAction( &p_action
, "Browse",
556 psz_service_type
, "ObjectID", psz_object_id
);
558 if ( i_res
!= UPNP_E_SUCCESS
)
560 msg_Dbg( _p_sd
, "AddToAction 'ObjectID' failed: %s",
561 UpnpGetErrorMessage( i_res
) );
562 goto browseActionCleanup
;
565 i_res
= UpnpAddToAction( &p_action
, "Browse",
566 psz_service_type
, "BrowseFlag", psz_browse_flag
);
568 if ( i_res
!= UPNP_E_SUCCESS
)
570 msg_Dbg( _p_sd
, "AddToAction 'BrowseFlag' failed: %s",
571 UpnpGetErrorMessage( i_res
) );
572 goto browseActionCleanup
;
575 i_res
= UpnpAddToAction( &p_action
, "Browse",
576 psz_service_type
, "Filter", psz_filter
);
578 if ( i_res
!= UPNP_E_SUCCESS
)
580 msg_Dbg( _p_sd
, "AddToAction 'Filter' failed: %s",
581 UpnpGetErrorMessage( i_res
) );
582 goto browseActionCleanup
;
585 i_res
= UpnpAddToAction( &p_action
, "Browse",
586 psz_service_type
, "StartingIndex", psz_starting_index
);
588 if ( i_res
!= UPNP_E_SUCCESS
)
590 msg_Dbg( _p_sd
, "AddToAction 'StartingIndex' failed: %s",
591 UpnpGetErrorMessage( i_res
) );
592 goto browseActionCleanup
;
595 i_res
= UpnpAddToAction( &p_action
, "Browse",
596 psz_service_type
, "RequestedCount", psz_requested_count
);
598 if ( i_res
!= UPNP_E_SUCCESS
)
600 msg_Dbg( _p_sd
, "AddToAction 'RequestedCount' failed: %s",
601 UpnpGetErrorMessage( i_res
) );
602 goto browseActionCleanup
;
605 i_res
= UpnpAddToAction( &p_action
, "Browse",
606 psz_service_type
, "SortCriteria", psz_sort_criteria
);
608 if ( i_res
!= UPNP_E_SUCCESS
)
610 msg_Dbg( _p_sd
, "AddToAction 'SortCriteria' failed: %s",
611 UpnpGetErrorMessage( i_res
) );
612 goto browseActionCleanup
;
615 i_res
= UpnpSendAction( _p_sd
->p_sys
->client_handle
,
617 CONTENT_DIRECTORY_SERVICE_TYPE
,
622 if ( i_res
!= UPNP_E_SUCCESS
)
624 msg_Err( _p_sd
, "%s when trying the send() action with URL: %s",
625 UpnpGetErrorMessage( i_res
), psz_url
);
627 ixmlDocument_free( p_response
);
633 free( psz_object_id
);
634 free( psz_browse_flag
);
636 free( psz_starting_index
);
637 free( psz_requested_count
);
638 free( psz_sort_criteria
);
640 free( psz_service_type
);
642 ixmlDocument_free( p_action
);
646 void MediaServer::fetchContents()
648 // Delete previous contents to prevent duplicate entries
652 services_discovery_RemoveItem( _p_sd
, _p_input_item
);
653 services_discovery_AddItem( _p_sd
, _p_input_item
, NULL
);
656 Container
* root
= new Container( 0, "0", getFriendlyName() );
658 _fetchContents( root
);
661 _p_contents
->setInputItem( _p_input_item
);
663 _buildPlaylist( _p_contents
, NULL
);
666 bool MediaServer::_fetchContents( Container
* p_parent
)
670 msg_Err( _p_sd
, "No parent" );
674 IXML_Document
* p_response
= _browseAction( p_parent
->getObjectID(),
675 "BrowseDirectChildren",
679 msg_Err( _p_sd
, "No response from browse() action" );
683 IXML_Document
* p_result
= parseBrowseResult( p_response
);
684 ixmlDocument_free( p_response
);
688 msg_Err( _p_sd
, "browse() response parsing failed" );
692 IXML_NodeList
* containerNodeList
=
693 ixmlDocument_getElementsByTagName( p_result
, "container" );
695 if ( containerNodeList
)
697 for ( unsigned int i
= 0;
698 i
< ixmlNodeList_length( containerNodeList
); i
++ )
700 IXML_Element
* containerElement
=
701 ( IXML_Element
* )ixmlNodeList_item( containerNodeList
, i
);
703 const char* objectID
= ixmlElement_getAttribute( containerElement
,
708 const char* childCountStr
=
709 ixmlElement_getAttribute( containerElement
, "childCount" );
711 if ( !childCountStr
)
714 int childCount
= atoi( childCountStr
);
715 const char* title
= xml_getChildElementValue( containerElement
,
721 const char* resource
= xml_getChildElementValue( containerElement
,
724 if ( resource
&& childCount
< 1 )
726 Item
* item
= new Item( p_parent
, objectID
, title
, resource
);
727 p_parent
->addItem( item
);
732 Container
* container
= new Container( p_parent
, objectID
, title
);
733 p_parent
->addContainer( container
);
735 if ( childCount
> 0 )
736 _fetchContents( container
);
739 ixmlNodeList_free( containerNodeList
);
742 IXML_NodeList
* itemNodeList
= ixmlDocument_getElementsByTagName( p_result
,
746 for ( unsigned int i
= 0; i
< ixmlNodeList_length( itemNodeList
); i
++ )
748 IXML_Element
* itemElement
=
749 ( IXML_Element
* )ixmlNodeList_item( itemNodeList
, i
);
751 const char* objectID
=
752 ixmlElement_getAttribute( itemElement
, "id" );
758 xml_getChildElementValue( itemElement
, "dc:title" );
763 const char* resource
=
764 xml_getChildElementValue( itemElement
, "res" );
769 Item
* item
= new Item( p_parent
, objectID
, title
, resource
);
770 p_parent
->addItem( item
);
772 ixmlNodeList_free( itemNodeList
);
775 ixmlDocument_free( p_result
);
779 void MediaServer::_buildPlaylist( Container
* p_parent
, input_item_node_t
*p_input_node
)
781 bool b_send
= p_input_node
== NULL
;
783 p_input_node
= input_item_node_Create( p_parent
->getInputItem() );
785 for ( unsigned int i
= 0; i
< p_parent
->getNumContainers(); i
++ )
787 Container
* p_container
= p_parent
->getContainer( i
);
789 input_item_t
* p_input_item
= input_item_New( _p_sd
, "vlc://nop",
790 p_container
->getTitle() );
791 input_item_node_t
*p_new_node
=
792 input_item_node_AppendItem( p_input_node
, p_input_item
);
794 p_container
->setInputItem( p_input_item
);
795 _buildPlaylist( p_container
, p_new_node
);
798 for ( unsigned int i
= 0; i
< p_parent
->getNumItems(); i
++ )
800 Item
* p_item
= p_parent
->getItem( i
);
802 input_item_t
* p_input_item
= input_item_New( _p_sd
,
803 p_item
->getResource(),
804 p_item
->getTitle() );
805 assert( p_input_item
);
806 input_item_node_AppendItem( p_input_node
, p_input_item
);
807 p_item
->setInputItem( p_input_item
);
811 input_item_node_PostAndDelete( p_input_node
);
814 void MediaServer::setInputItem( input_item_t
* p_input_item
)
816 if(_p_input_item
== p_input_item
)
820 vlc_gc_decref( _p_input_item
);
822 vlc_gc_incref( p_input_item
);
823 _p_input_item
= p_input_item
;
826 bool MediaServer::compareSID( const char* psz_sid
)
828 return ( strncmp( _subscription_id
, psz_sid
, sizeof( Upnp_SID
) ) == 0 );
832 // MediaServerList...
834 MediaServerList::MediaServerList( services_discovery_t
* p_sd
)
839 MediaServerList::~MediaServerList()
841 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
847 bool MediaServerList::addServer( MediaServer
* p_server
)
849 input_item_t
* p_input_item
= NULL
;
850 if ( getServer( p_server
->getUDN() ) != 0 ) return false;
852 msg_Dbg( _p_sd
, "Adding server '%s' with uuid '%s'", p_server
->getFriendlyName(), p_server
->getUDN() );
854 p_input_item
= input_item_New( _p_sd
, "vlc://nop",
855 p_server
->getFriendlyName() );
856 p_server
->setInputItem( p_input_item
);
858 services_discovery_AddItem( _p_sd
, p_input_item
, NULL
);
860 _list
.push_back( p_server
);
865 MediaServer
* MediaServerList::getServer( const char* psz_udn
)
867 MediaServer
* p_result
= 0;
869 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
871 if( strcmp( psz_udn
, _list
[i
]->getUDN() ) == 0 )
881 MediaServer
* MediaServerList::getServerBySID( const char* psz_sid
)
883 MediaServer
* p_server
= 0;
885 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
887 if ( _list
[i
]->compareSID( psz_sid
) )
897 void MediaServerList::removeServer( const char* psz_udn
)
899 MediaServer
* p_server
= getServer( psz_udn
);
900 if ( !p_server
) return;
902 msg_Dbg( _p_sd
, "Removing server '%s'", p_server
->getFriendlyName() );
904 std::vector
<MediaServer
*>::iterator it
;
905 for ( it
= _list
.begin(); it
!= _list
.end(); ++it
)
907 if ( *it
== p_server
)
919 Item::Item( Container
* p_parent
, const char* psz_object_id
, const char* psz_title
,
920 const char* psz_resource
)
924 _objectID
= psz_object_id
;
926 _resource
= psz_resource
;
928 _p_input_item
= NULL
;
934 vlc_gc_decref( _p_input_item
);
937 const char* Item::getObjectID() const
939 return _objectID
.c_str();
942 const char* Item::getTitle() const
944 return _title
.c_str();
947 const char* Item::getResource() const
949 return _resource
.c_str();
952 void Item::setInputItem( input_item_t
* p_input_item
)
954 if(_p_input_item
== p_input_item
)
958 vlc_gc_decref( _p_input_item
);
960 vlc_gc_incref( p_input_item
);
961 _p_input_item
= p_input_item
;
964 input_item_t
* Item::getInputItem() const
966 return _p_input_item
;
972 Container::Container( Container
* p_parent
,
973 const char* psz_object_id
,
974 const char* psz_title
)
978 _objectID
= psz_object_id
;
981 _p_input_item
= NULL
;
984 Container::~Container()
986 for ( unsigned int i
= 0; i
< _containers
.size(); i
++ )
988 delete _containers
[i
];
991 for ( unsigned int i
= 0; i
< _items
.size(); i
++ )
997 vlc_gc_decref( _p_input_item
);
1000 void Container::addItem( Item
* item
)
1002 _items
.push_back( item
);
1005 void Container::addContainer( Container
* p_container
)
1007 _containers
.push_back( p_container
);
1010 const char* Container::getObjectID() const
1012 return _objectID
.c_str();
1015 const char* Container::getTitle() const
1017 return _title
.c_str();
1020 unsigned int Container::getNumItems() const
1022 return _items
.size();
1025 unsigned int Container::getNumContainers() const
1027 return _containers
.size();
1030 Item
* Container::getItem( unsigned int i_index
) const
1032 if ( i_index
< _items
.size() ) return _items
[i_index
];
1036 Container
* Container::getContainer( unsigned int i_index
) const
1038 if ( i_index
< _containers
.size() ) return _containers
[i_index
];
1042 Container
* Container::getParent()
1047 void Container::setInputItem( input_item_t
* p_input_item
)
1049 if(_p_input_item
== p_input_item
)
1053 vlc_gc_decref( _p_input_item
);
1055 vlc_gc_incref( p_input_item
);
1056 _p_input_item
= p_input_item
;
1059 input_item_t
* Container::getInputItem() const
1061 return _p_input_item
;