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 *****************************************************************************/
28 #define __STDC_CONSTANT_MACROS 1
37 #include <vlc_plugin.h>
38 #include <vlc_services_discovery.h>
45 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
46 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
51 struct services_discovery_sys_t
53 UpnpClient_Handle client_handle;
54 MediaServerList* p_server_list;
55 vlc_mutex_t callback_lock;
59 * VLC callback prototypes
61 static int Open( vlc_object_t* );
62 static void Close( vlc_object_t* );
63 VLC_SD_PROBE_HELPER( "upnp", "Universal Plug'n'Play", SD_CAT_LAN )
69 set_shortname( "UPnP" );
70 set_description( N_( "Universal Plug'n'Play" ) );
71 set_category( CAT_PLAYLIST );
72 set_subcategory( SUBCAT_PLAYLIST_SD );
73 set_capability( "services_discovery", 0 );
74 set_callbacks( Open, Close );
76 VLC_SD_PROBE_SUBMODULE
82 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data );
84 const char* xml_getChildElementValue( IXML_Element* p_parent,
85 const char* psz_tag_name );
87 const char* xml_getChildElementAttributeValue( IXML_Element* p_parent,
88 const char* psz_tag_name_,
89 const char* psz_attribute_ );
91 IXML_Document* parseBrowseResult( IXML_Document* p_doc );
94 * Initializes UPNP instance.
96 static int Open( vlc_object_t *p_this )
99 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
100 services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
101 calloc( 1, sizeof( services_discovery_sys_t ) );
103 if( !( p_sd->p_sys = p_sys ) )
106 /* Initialize on first IPv4-capable adapter and first open port
107 * TODO: use UpnpInit2() to utilize IPv6.
109 i_res = UpnpInit( 0, 0 );
110 if( i_res != UPNP_E_SUCCESS )
112 msg_Err( p_sd, "Initialization failed: %s", UpnpGetErrorMessage( i_res ) );
117 p_sys->p_server_list = new MediaServerList( p_sd );
118 vlc_mutex_init( &p_sys->callback_lock );
120 /* Register a control point */
121 i_res = UpnpRegisterClient( Callback, p_sd, &p_sys->client_handle );
122 if( i_res != UPNP_E_SUCCESS )
124 msg_Err( p_sd, "Client registration failed: %s", UpnpGetErrorMessage( i_res ) );
125 Close( (vlc_object_t*) p_sd );
129 /* Search for media servers */
130 i_res = UpnpSearchAsync( p_sys->client_handle, 5,
131 MEDIA_SERVER_DEVICE_TYPE, p_sd );
132 if( i_res != UPNP_E_SUCCESS )
134 msg_Err( p_sd, "Error sending search request: %s", UpnpGetErrorMessage( i_res ) );
135 Close( (vlc_object_t*) p_sd );
139 i_res = UpnpSetMaxContentLength( 0 );
140 if( i_res != UPNP_E_SUCCESS )
142 msg_Err( p_sd, "Failed to set maximum content length: %s", UpnpGetErrorMessage( i_res ) );
143 Close( (vlc_object_t*) p_sd );
151 * Releases resources.
153 static void Close( vlc_object_t *p_this )
155 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
157 UpnpUnRegisterClient( p_sd->p_sys->client_handle );
160 delete p_sd->p_sys->p_server_list;
161 vlc_mutex_destroy( &p_sd->p_sys->callback_lock );
166 /* XML utility functions */
169 * Returns the value of a child element, or NULL on error
171 const char* xml_getChildElementValue( IXML_Element* p_parent,
172 const char* psz_tag_name_ )
174 if ( !p_parent ) return NULL;
175 if ( !psz_tag_name_ ) return NULL;
177 IXML_NodeList* p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name_ );
178 if ( !p_node_list ) return NULL;
180 IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
181 ixmlNodeList_free( p_node_list );
182 if ( !p_element ) return NULL;
184 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
185 if ( !p_text_node ) return NULL;
187 return ixmlNode_getNodeValue( p_text_node );
191 * Returns the value of a child element's attribute, or NULL on error
193 const char* xml_getChildElementAttributeValue( IXML_Element* p_parent,
194 const char* psz_tag_name_,
195 const char* psz_attribute_ )
197 if ( !p_parent ) return NULL;
198 if ( !psz_tag_name_ ) return NULL;
199 if ( !psz_attribute_ ) return NULL;
201 IXML_NodeList* p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name_ );
202 if ( !p_node_list ) return NULL;
204 IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
205 ixmlNodeList_free( p_node_list );
206 if ( !p_element ) return NULL;
208 return ixmlElement_getAttribute( (IXML_Element*) p_element, psz_attribute_ );
212 * Extracts the result document from a SOAP response
214 IXML_Document* parseBrowseResult( IXML_Document* p_doc )
216 ixmlRelaxParser( 1 );
218 if ( !p_doc ) return 0;
220 IXML_NodeList* p_result_list = ixmlDocument_getElementsByTagName( p_doc,
223 if ( !p_result_list ) return 0;
225 IXML_Node* p_result_node = ixmlNodeList_item( p_result_list, 0 );
227 ixmlNodeList_free( p_result_list );
229 if ( !p_result_node ) return 0;
231 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_result_node );
232 if ( !p_text_node ) return 0;
234 const char* psz_result_string = ixmlNode_getNodeValue( p_text_node );
236 IXML_Document* p_browse_doc = ixmlParseBuffer( psz_result_string );
243 * Handles all UPnP events
245 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data )
247 services_discovery_t* p_sd = ( services_discovery_t* ) p_user_data;
248 services_discovery_sys_t* p_sys = p_sd->p_sys;
249 vlc_mutex_locker locker( &p_sys->callback_lock );
253 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
254 case UPNP_DISCOVERY_SEARCH_RESULT:
256 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
258 IXML_Document *p_description_doc = 0;
261 i_res = UpnpDownloadXmlDoc( p_discovery->Location, &p_description_doc );
262 if ( i_res != UPNP_E_SUCCESS )
264 msg_Warn( p_sd, "Could not download device description! "
265 "Fetching data from %s failed: %s",
266 p_discovery->Location, UpnpGetErrorMessage( i_res ) );
270 MediaServer::parseDeviceDescription( p_description_doc,
271 p_discovery->Location, p_sd );
273 ixmlDocument_free( p_description_doc );
277 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
279 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
281 p_sys->p_server_list->removeServer( p_discovery->DeviceId );
286 case UPNP_EVENT_RECEIVED:
288 Upnp_Event* p_e = ( Upnp_Event* )p_event;
290 MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_e->Sid );
291 if ( p_server ) p_server->fetchContents();
295 case UPNP_EVENT_AUTORENEWAL_FAILED:
296 case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
300 Upnp_Event_Subscribe* p_s = ( Upnp_Event_Subscribe* )p_event;
302 MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_s->Sid );
303 if ( p_server ) p_server->subscribeToContentDirectory();
307 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
308 msg_Warn( p_sd, "subscription complete" );
311 case UPNP_DISCOVERY_SEARCH_TIMEOUT:
312 msg_Warn( p_sd, "search timeout" );
316 msg_Err( p_sd, "Unhandled event, please report ( type=%d )", event_type );
320 return UPNP_E_SUCCESS;
325 * Local class implementations.
332 void MediaServer::parseDeviceDescription( IXML_Document* p_doc,
333 const char* p_location,
334 services_discovery_t* p_sd )
338 msg_Err( p_sd, "Null IXML_Document" );
344 msg_Err( p_sd, "Null location" );
348 const char* psz_base_url = p_location;
350 /* Try to extract baseURL */
351 IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( p_doc, "URLBase" );
355 if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) )
357 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node );
358 if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node );
361 ixmlNodeList_free( p_url_list );
365 IXML_NodeList* p_device_list =
366 ixmlDocument_getElementsByTagName( p_doc, "device" );
370 for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ )
372 IXML_Element* p_device_element =
373 ( IXML_Element* ) ixmlNodeList_item( p_device_list, i );
375 const char* psz_device_type = xml_getChildElementValue( p_device_element,
377 if ( !psz_device_type )
379 msg_Warn( p_sd, "No deviceType found!" );
383 if ( strncmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type,
384 strlen( MEDIA_SERVER_DEVICE_TYPE ) - 1 ) != 0 )
387 const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" );
390 msg_Warn( p_sd, "No UDN!" );
394 /* Check if server is already added */
395 if ( p_sd->p_sys->p_server_list->getServer( psz_udn ) != 0 )
397 msg_Warn( p_sd, "Server with uuid '%s' already exists.", psz_udn );
401 const char* psz_friendly_name =
402 xml_getChildElementValue( p_device_element,
405 if ( !psz_friendly_name )
407 msg_Dbg( p_sd, "No friendlyName!" );
411 MediaServer* p_server = new MediaServer( psz_udn, psz_friendly_name, p_sd );
413 if ( !p_sd->p_sys->p_server_list->addServer( p_server ) )
420 /* Check for ContentDirectory service. */
421 IXML_NodeList* p_service_list =
422 ixmlElement_getElementsByTagName( p_device_element,
424 if ( p_service_list )
426 for ( unsigned int j = 0;
427 j < ixmlNodeList_length( p_service_list ); j++ )
429 IXML_Element* p_service_element =
430 ( IXML_Element* ) ixmlNodeList_item( p_service_list, j );
432 const char* psz_service_type =
433 xml_getChildElementValue( p_service_element,
435 if ( !psz_service_type )
437 msg_Warn( p_sd, "No service type found." );
441 int k = strlen( CONTENT_DIRECTORY_SERVICE_TYPE ) - 1;
442 if ( strncmp( CONTENT_DIRECTORY_SERVICE_TYPE,
443 psz_service_type, k ) != 0 )
446 p_server->_i_content_directory_service_version =
449 const char* psz_event_sub_url =
450 xml_getChildElementValue( p_service_element,
452 if ( !psz_event_sub_url )
454 msg_Warn( p_sd, "No event subscription url found." );
458 const char* psz_control_url =
459 xml_getChildElementValue( p_service_element,
461 if ( !psz_control_url )
463 msg_Warn( p_sd, "No control url found." );
467 /* Try to subscribe to ContentDirectory service */
469 char* psz_url = ( char* ) malloc( strlen( psz_base_url ) +
470 strlen( psz_event_sub_url ) + 1 );
473 if ( UpnpResolveURL( psz_base_url, psz_event_sub_url, psz_url ) ==
476 p_server->setContentDirectoryEventURL( psz_url );
477 p_server->subscribeToContentDirectory();
483 /* Try to browse content directory. */
485 psz_url = ( char* ) malloc( strlen( psz_base_url ) +
486 strlen( psz_control_url ) + 1 );
489 if ( UpnpResolveURL( psz_base_url, psz_control_url, psz_url ) ==
492 p_server->setContentDirectoryControlURL( psz_url );
493 p_server->fetchContents();
499 ixmlNodeList_free( p_service_list );
502 ixmlNodeList_free( p_device_list );
506 MediaServer::MediaServer( const char* psz_udn,
507 const char* psz_friendly_name,
508 services_discovery_t* p_sd )
513 _friendly_name = psz_friendly_name;
516 _p_input_item = NULL;
517 _i_content_directory_service_version = 1;
520 MediaServer::~MediaServer()
525 const char* MediaServer::getUDN() const
530 const char* MediaServer::getFriendlyName() const
532 return _friendly_name.c_str();
535 void MediaServer::setContentDirectoryEventURL( const char* psz_url )
537 _content_directory_event_url = psz_url;
540 const char* MediaServer::getContentDirectoryEventURL() const
542 return _content_directory_event_url.c_str();
545 void MediaServer::setContentDirectoryControlURL( const char* psz_url )
547 _content_directory_control_url = psz_url;
550 const char* MediaServer::getContentDirectoryControlURL() const
552 return _content_directory_control_url.c_str();
556 * Subscribes current client handle to Content Directory Service.
557 * CDS exports the server shares to clients.
559 void MediaServer::subscribeToContentDirectory()
561 const char* psz_url = getContentDirectoryEventURL();
564 msg_Dbg( _p_sd, "No subscription url set!" );
568 int i_timeout = 1810;
571 int i_res = UpnpSubscribe( _p_sd->p_sys->client_handle, psz_url, &i_timeout, sid );
573 if ( i_res == UPNP_E_SUCCESS )
575 _i_subscription_timeout = i_timeout;
576 memcpy( _subscription_id, sid, sizeof( Upnp_SID ) );
580 msg_Dbg( _p_sd, "Subscribe failed: '%s': %s",
581 getFriendlyName(), UpnpGetErrorMessage( i_res ) );
585 * Constructs UpnpAction to browse available content.
587 IXML_Document* MediaServer::_browseAction( const char* psz_object_id_,
588 const char* psz_browser_flag_,
589 const char* psz_filter_,
590 const char* psz_starting_index_,
591 const char* psz_requested_count_,
592 const char* psz_sort_criteria_ )
594 IXML_Document* p_action = 0;
595 IXML_Document* p_response = 0;
596 const char* psz_url = getContentDirectoryControlURL();
600 msg_Dbg( _p_sd, "No subscription url set!" );
604 char* psz_service_type = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
606 psz_service_type[strlen( psz_service_type ) - 1] =
607 _i_content_directory_service_version;
611 i_res = UpnpAddToAction( &p_action, "Browse",
612 psz_service_type, "ObjectID", psz_object_id_ );
614 if ( i_res != UPNP_E_SUCCESS )
616 msg_Dbg( _p_sd, "AddToAction 'ObjectID' failed: %s",
617 UpnpGetErrorMessage( i_res ) );
618 goto browseActionCleanup;
621 i_res = UpnpAddToAction( &p_action, "Browse",
622 psz_service_type, "BrowseFlag", psz_browser_flag_ );
624 if ( i_res != UPNP_E_SUCCESS )
626 msg_Dbg( _p_sd, "AddToAction 'BrowseFlag' failed: %s",
627 UpnpGetErrorMessage( i_res ) );
628 goto browseActionCleanup;
631 i_res = UpnpAddToAction( &p_action, "Browse",
632 psz_service_type, "Filter", psz_filter_ );
634 if ( i_res != UPNP_E_SUCCESS )
636 msg_Dbg( _p_sd, "AddToAction 'Filter' failed: %s",
637 UpnpGetErrorMessage( i_res ) );
638 goto browseActionCleanup;
641 i_res = UpnpAddToAction( &p_action, "Browse",
642 psz_service_type, "StartingIndex", psz_starting_index_ );
644 if ( i_res != UPNP_E_SUCCESS )
646 msg_Dbg( _p_sd, "AddToAction 'StartingIndex' failed: %s",
647 UpnpGetErrorMessage( i_res ) );
648 goto browseActionCleanup;
651 i_res = UpnpAddToAction( &p_action, "Browse",
652 psz_service_type, "RequestedCount", psz_requested_count_ );
654 if ( i_res != UPNP_E_SUCCESS )
656 msg_Dbg( _p_sd, "AddToAction 'RequestedCount' failed: %s",
657 UpnpGetErrorMessage( i_res ) );
658 goto browseActionCleanup;
661 i_res = UpnpAddToAction( &p_action, "Browse",
662 psz_service_type, "SortCriteria", psz_sort_criteria_ );
664 if ( i_res != UPNP_E_SUCCESS )
666 msg_Dbg( _p_sd, "AddToAction 'SortCriteria' failed: %s",
667 UpnpGetErrorMessage( i_res ) );
668 goto browseActionCleanup;
671 i_res = UpnpSendAction( _p_sd->p_sys->client_handle,
674 0, /* ignored in SDK, must be NULL */
678 if ( i_res != UPNP_E_SUCCESS )
680 msg_Err( _p_sd, "%s when trying the send() action with URL: %s",
681 UpnpGetErrorMessage( i_res ), psz_url );
683 ixmlDocument_free( p_response );
689 free( psz_service_type );
691 ixmlDocument_free( p_action );
695 void MediaServer::fetchContents()
697 /* Delete previous contents to prevent duplicate entries */
701 services_discovery_RemoveItem( _p_sd, _p_input_item );
702 services_discovery_AddItem( _p_sd, _p_input_item, NULL );
705 Container* root = new Container( 0, "0", getFriendlyName() );
707 _fetchContents( root );
710 _p_contents->setInputItem( _p_input_item );
712 _buildPlaylist( _p_contents, NULL );
716 * Fetches and parses the UPNP response
718 bool MediaServer::_fetchContents( Container* p_parent )
722 msg_Err( _p_sd, "No parent" );
726 IXML_Document* p_response = _browseAction( p_parent->getObjectID(),
727 "BrowseDirectChildren",
731 msg_Err( _p_sd, "No response from browse() action" );
735 IXML_Document* p_result = parseBrowseResult( p_response );
736 ixmlDocument_free( p_response );
740 msg_Err( _p_sd, "browse() response parsing failed" );
746 msg_Dbg( _p_sd, "Got DIDL document: %s",
747 ixmlPrintDocument( p_result ) );
751 IXML_NodeList* containerNodeList =
752 ixmlDocument_getElementsByTagName( p_result, "container" );
754 if ( containerNodeList )
756 for ( unsigned int i = 0;
757 i < ixmlNodeList_length( containerNodeList ); i++ )
759 IXML_Element* containerElement =
760 ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
762 const char* objectID = ixmlElement_getAttribute( containerElement,
767 const char* title = xml_getChildElementValue( containerElement,
773 Container* container = new Container( p_parent, objectID, title );
774 p_parent->addContainer( container );
775 _fetchContents( container );
777 ixmlNodeList_free( containerNodeList );
780 IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( p_result,
784 for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
786 IXML_Element* itemElement =
787 ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
789 const char* objectID =
790 ixmlElement_getAttribute( itemElement, "id" );
796 xml_getChildElementValue( itemElement, "dc:title" );
801 const char* resource =
802 xml_getChildElementValue( itemElement, "res" );
807 const char* psz_duration = xml_getChildElementAttributeValue( itemElement,
811 mtime_t i_duration = -1;
812 int i_hours, i_minutes, i_seconds, i_decis;
816 if( sscanf( psz_duration, "%02d:%02d:%02d.%d",
817 &i_hours, &i_minutes, &i_seconds, &i_decis ))
818 i_duration = INT64_C(1000000) * ( i_hours*3600 +
821 INT64_C(100000) * i_decis;
824 Item* item = new Item( p_parent, objectID, title, resource, i_duration );
825 p_parent->addItem( item );
827 ixmlNodeList_free( itemNodeList );
830 ixmlDocument_free( p_result );
834 // TODO: Create a permanent fix for the item duplication bug. The current fix
835 // is essentially only a small hack. Although it fixes the problem, it introduces
836 // annoying cosmetic issues with the playlist. For example, when the UPnP Server
837 // rebroadcasts it's directory structure, the VLC Client deletes the old directory
838 // structure, causing the user to go back to the root node of the directory. The
839 // directory is then rebuilt, and the user is forced to traverse through the directory
840 // to find the item they were looking for. Some servers may not push the directory
841 // structure too often, but we cannot rely on this fix.
843 // I have thought up another fix, but this would require certain features to
844 // be present within the VLC services discovery. Currently, services_discovery_AddItem
845 // does not allow the programmer to nest items. It only allows a "2 deep" scope.
846 // An example of the limitation is below:
852 // services_discovery_AddItem will not let the programmer specify a child-node to
853 // insert items into, so we would not be able to do the following:
859 // + Sub Item 1 of Item 2
860 // + Sub-Sub Item 1 of Sub Item 1
862 // This creates a HUGE limitation on what we are able to do. If we were able to do
863 // the above, we could simply preserve the old directory listing, and compare what items
864 // do not exist in the new directory listing, then remove them from the shown listing using
865 // services_discovery_RemoveItem. If new files were introduced within an already existing
866 // container, we could simply do so with services_discovery_AddItem.
869 * Builds playlist based on available input items.
871 void MediaServer::_buildPlaylist( Container* p_parent, input_item_node_t *p_input_node )
873 bool b_send = p_input_node == NULL;
875 p_input_node = input_item_node_Create( p_parent->getInputItem() );
877 for ( unsigned int i = 0; i < p_parent->getNumContainers(); i++ )
879 Container* p_container = p_parent->getContainer( i );
881 input_item_t* p_input_item = input_item_New( "vlc://nop",
882 p_container->getTitle() );
883 input_item_node_t *p_new_node =
884 input_item_node_AppendItem( p_input_node, p_input_item );
886 p_container->setInputItem( p_input_item );
887 _buildPlaylist( p_container, p_new_node );
890 for ( unsigned int i = 0; i < p_parent->getNumItems(); i++ )
892 Item* p_item = p_parent->getItem( i );
894 input_item_t* p_input_item = input_item_NewExt( p_item->getResource(),
899 p_item->getDuration() );
901 assert( p_input_item );
902 input_item_node_AppendItem( p_input_node, p_input_item );
903 p_item->setInputItem( p_input_item );
907 input_item_node_PostAndDelete( p_input_node );
910 void MediaServer::setInputItem( input_item_t* p_input_item )
912 if( _p_input_item == p_input_item )
916 vlc_gc_decref( _p_input_item );
918 vlc_gc_incref( p_input_item );
919 _p_input_item = p_input_item;
922 input_item_t* MediaServer::getInputItem() const
924 return _p_input_item;
927 bool MediaServer::compareSID( const char* psz_sid )
929 return ( strncmp( _subscription_id, psz_sid, sizeof( Upnp_SID ) ) == 0 );
934 * MediaServerList class
936 MediaServerList::MediaServerList( services_discovery_t* p_sd )
941 MediaServerList::~MediaServerList()
943 for ( unsigned int i = 0; i < _list.size(); i++ )
949 bool MediaServerList::addServer( MediaServer* p_server )
951 input_item_t* p_input_item = NULL;
952 if ( getServer( p_server->getUDN() ) != 0 ) return false;
954 msg_Dbg( _p_sd, "Adding server '%s' with uuid '%s'", p_server->getFriendlyName(), p_server->getUDN() );
956 p_input_item = input_item_New( "vlc://nop", p_server->getFriendlyName() );
958 input_item_SetDescription( p_input_item, p_server->getUDN() );
960 p_server->setInputItem( p_input_item );
962 services_discovery_AddItem( _p_sd, p_input_item, NULL );
964 _list.push_back( p_server );
969 MediaServer* MediaServerList::getServer( const char* psz_udn )
971 MediaServer* p_result = 0;
973 for ( unsigned int i = 0; i < _list.size(); i++ )
975 if( strcmp( psz_udn, _list[i]->getUDN() ) == 0 )
985 MediaServer* MediaServerList::getServerBySID( const char* psz_sid )
987 MediaServer* p_server = 0;
989 for ( unsigned int i = 0; i < _list.size(); i++ )
991 if ( _list[i]->compareSID( psz_sid ) )
1001 void MediaServerList::removeServer( const char* psz_udn )
1003 MediaServer* p_server = getServer( psz_udn );
1004 if ( !p_server ) return;
1006 msg_Dbg( _p_sd, "Removing server '%s'", p_server->getFriendlyName() );
1008 services_discovery_RemoveItem( _p_sd, p_server->getInputItem() );
1010 std::vector<MediaServer*>::iterator it;
1011 for ( it = _list.begin(); it != _list.end(); ++it )
1013 if ( *it == p_server )
1026 Item::Item( Container* p_parent, const char* psz_object_id, const char* psz_title,
1027 const char* psz_resource, mtime_t i_duration )
1031 _objectID = psz_object_id;
1033 _resource = psz_resource;
1034 _duration = i_duration;
1036 _p_input_item = NULL;
1042 vlc_gc_decref( _p_input_item );
1045 const char* Item::getObjectID() const
1047 return _objectID.c_str();
1050 const char* Item::getTitle() const
1052 return _title.c_str();
1055 const char* Item::getResource() const
1057 return _resource.c_str();
1060 mtime_t Item::getDuration() const
1065 void Item::setInputItem( input_item_t* p_input_item )
1067 if( _p_input_item == p_input_item )
1071 vlc_gc_decref( _p_input_item );
1073 vlc_gc_incref( p_input_item );
1074 _p_input_item = p_input_item;
1080 Container::Container( Container* p_parent,
1081 const char* psz_object_id,
1082 const char* psz_title )
1086 _objectID = psz_object_id;
1089 _p_input_item = NULL;
1092 Container::~Container()
1094 for ( unsigned int i = 0; i < _containers.size(); i++ )
1096 delete _containers[i];
1099 for ( unsigned int i = 0; i < _items.size(); i++ )
1105 vlc_gc_decref( _p_input_item );
1108 void Container::addItem( Item* item )
1110 _items.push_back( item );
1113 void Container::addContainer( Container* p_container )
1115 _containers.push_back( p_container );
1118 const char* Container::getObjectID() const
1120 return _objectID.c_str();
1123 const char* Container::getTitle() const
1125 return _title.c_str();
1128 unsigned int Container::getNumItems() const
1130 return _items.size();
1133 unsigned int Container::getNumContainers() const
1135 return _containers.size();
1138 Item* Container::getItem( unsigned int i_index ) const
1140 if ( i_index < _items.size() ) return _items[i_index];
1144 Container* Container::getContainer( unsigned int i_index ) const
1146 if ( i_index < _containers.size() ) return _containers[i_index];
1150 Container* Container::getParent()
1155 void Container::setInputItem( input_item_t* p_input_item )
1157 if( _p_input_item == p_input_item )
1161 vlc_gc_decref( _p_input_item );
1163 vlc_gc_incref( p_input_item );
1164 _p_input_item = p_input_item;
1167 input_item_t* Container::getInputItem() const
1169 return _p_input_item;