1 /*****************************************************************************
2 * Upnp.cpp : UPnP discovery module (libupnp)
3 *****************************************************************************
4 * Copyright (C) 2004-2008 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 *****************************************************************************/
29 \TODO: Debug messages: "__FILE__, __LINE__" ok ???, Wrn/Err ???
38 #include <vlc_plugin.h>
39 #include <vlc_services_discovery.h>
44 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
45 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
48 struct services_discovery_sys_t
50 UpnpClient_Handle client_handle;
51 MediaServerList* p_server_list;
52 vlc_mutex_t callback_lock;
55 // VLC callback prototypes
56 static int Open( vlc_object_t* );
57 static void Close( vlc_object_t* );
58 VLC_SD_PROBE_HELPER("upnp", "Universal Plug'n'Play", SD_CAT_LAN)
63 set_shortname( "UPnP" );
64 set_description( N_( "Universal Plug'n'Play" ) );
65 set_category( CAT_PLAYLIST );
66 set_subcategory( SUBCAT_PLAYLIST_SD );
67 set_capability( "services_discovery", 0 );
68 set_callbacks( Open, Close );
70 VLC_SD_PROBE_SUBMODULE
76 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data );
78 const char* xml_getChildElementValue( IXML_Element* p_parent,
79 const char* psz_tag_name );
81 IXML_Document* parseBrowseResult( IXML_Document* p_doc );
86 static int Open( vlc_object_t *p_this )
89 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
90 services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
91 calloc( 1, sizeof( services_discovery_sys_t ) );
93 if(!(p_sd->p_sys = p_sys))
96 i_res = UpnpInit( 0, 0 );
97 if( i_res != UPNP_E_SUCCESS )
99 msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
104 p_sys->p_server_list = new MediaServerList( p_sd );
105 vlc_mutex_init( &p_sys->callback_lock );
107 i_res = UpnpRegisterClient( Callback, p_sd, &p_sys->client_handle );
108 if( i_res != UPNP_E_SUCCESS )
110 msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
111 Close( (vlc_object_t*) p_sd );
115 i_res = UpnpSearchAsync( p_sys->client_handle, 5,
116 MEDIA_SERVER_DEVICE_TYPE, p_sd );
117 if( i_res != UPNP_E_SUCCESS )
119 msg_Err( p_sd, "Search failed: %s", UpnpGetErrorMessage( i_res ) );
120 Close( (vlc_object_t*) p_sd );
124 i_res = UpnpSetMaxContentLength( 262144 );
125 if( i_res != UPNP_E_SUCCESS )
127 msg_Err( p_sd, "Failed to set maximum content length: %s", UpnpGetErrorMessage( i_res ) );
128 Close( (vlc_object_t*) p_sd );
135 static void Close( vlc_object_t *p_this )
137 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
139 UpnpUnRegisterClient( p_sd->p_sys->client_handle );
142 delete p_sd->p_sys->p_server_list;
143 vlc_mutex_destroy( &p_sd->p_sys->callback_lock );
148 // XML utility functions:
150 // Returns the value of a child element, or 0 on error
151 const char* xml_getChildElementValue( IXML_Element* p_parent,
152 const char* psz_tag_name_ )
154 if ( !p_parent ) return 0;
155 if ( !psz_tag_name_ ) return 0;
157 char* psz_tag_name = strdup( psz_tag_name_ );
158 IXML_NodeList* p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
159 free( psz_tag_name );
160 if ( !p_node_list ) return 0;
162 IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
163 ixmlNodeList_free( p_node_list );
164 if ( !p_element ) return 0;
166 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
167 if ( !p_text_node ) return 0;
169 return ixmlNode_getNodeValue( p_text_node );
172 // Extracts the result document from a SOAP response
173 IXML_Document* parseBrowseResult( IXML_Document* p_doc )
177 if ( !p_doc ) return 0;
179 IXML_NodeList* p_result_list = ixmlDocument_getElementsByTagName( p_doc,
182 if ( !p_result_list ) return 0;
184 IXML_Node* p_result_node = ixmlNodeList_item( p_result_list, 0 );
186 ixmlNodeList_free( p_result_list );
188 if ( !p_result_node ) return 0;
190 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_result_node );
191 if ( !p_text_node ) return 0;
193 const char* psz_result_string = ixmlNode_getNodeValue( p_text_node );
194 char* psz_result_xml = strdup( psz_result_string );
196 IXML_Document* p_browse_doc = ixmlParseBuffer( psz_result_xml );
198 free( psz_result_xml );
204 // Handles all UPnP events
205 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data )
207 services_discovery_t* p_sd = ( services_discovery_t* ) p_user_data;
208 services_discovery_sys_t* p_sys = p_sd->p_sys;
209 vlc_mutex_locker locker( &p_sys->callback_lock );
213 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
214 case UPNP_DISCOVERY_SEARCH_RESULT:
216 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
218 IXML_Document *p_description_doc = 0;
221 i_res = UpnpDownloadXmlDoc( p_discovery->Location, &p_description_doc );
222 if ( i_res != UPNP_E_SUCCESS )
225 "%s:%d: Could not download device description!",
226 __FILE__, __LINE__ );
227 msg_Dbg( p_sd, "Fetching data from %s failed: %s",
228 p_discovery->Location, UpnpGetErrorMessage( i_res ) );
232 MediaServer::parseDeviceDescription( p_description_doc,
233 p_discovery->Location, p_sd );
235 ixmlDocument_free( p_description_doc );
239 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
241 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
243 p_sys->p_server_list->removeServer( p_discovery->DeviceId );
247 case UPNP_EVENT_RECEIVED:
249 Upnp_Event* p_e = ( Upnp_Event* )p_event;
251 MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_e->Sid );
252 if ( p_server ) p_server->fetchContents();
256 case UPNP_EVENT_AUTORENEWAL_FAILED:
257 case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
261 Upnp_Event_Subscribe* p_s = ( Upnp_Event_Subscribe* )p_event;
263 MediaServer* p_server = p_sys->p_server_list->getServerBySID( p_s->Sid );
264 if ( p_server ) p_server->subscribeToContentDirectory();
268 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
269 msg_Warn( p_sd, "subscription complete" );
272 case UPNP_DISCOVERY_SEARCH_TIMEOUT:
273 msg_Warn( p_sd, "search timeout" );
278 "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
279 __FILE__, __LINE__, event_type );
283 return UPNP_E_SUCCESS;
287 // Class implementations...
291 void MediaServer::parseDeviceDescription( IXML_Document* p_doc,
292 const char* p_location,
293 services_discovery_t* p_sd )
297 msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
303 msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
307 const char* psz_base_url = p_location;
309 // Try to extract baseURL
310 IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( p_doc, "baseURL" );
314 if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) )
316 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node );
317 if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node );
320 ixmlNodeList_free( p_url_list );
324 IXML_NodeList* p_device_list =
325 ixmlDocument_getElementsByTagName( p_doc, "device" );
329 for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ )
331 IXML_Element* p_device_element =
332 ( IXML_Element* ) ixmlNodeList_item( p_device_list, i );
334 const char* psz_device_type = xml_getChildElementValue( p_device_element,
336 if ( !psz_device_type )
339 "%s:%d: no deviceType!",
340 __FILE__, __LINE__ );
344 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type ) != 0 )
347 const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" );
350 msg_Dbg( p_sd, "%s:%d: no UDN!",
351 __FILE__, __LINE__ );
355 // Check if server is already added
356 if ( p_sd->p_sys->p_server_list->getServer( psz_udn ) != 0 )
358 msg_Dbg( p_sd, "%s:%d: server with uuid '%s' already exists.",
359 __FILE__, __LINE__, psz_udn );
363 const char* psz_friendly_name =
364 xml_getChildElementValue( p_device_element,
367 if ( !psz_friendly_name )
369 msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
373 MediaServer* p_server = new MediaServer( psz_udn, psz_friendly_name, p_sd );
375 if ( !p_sd->p_sys->p_server_list->addServer( p_server ) )
382 // Check for ContentDirectory service...
383 IXML_NodeList* p_service_list =
384 ixmlElement_getElementsByTagName( p_device_element,
386 if ( p_service_list )
388 for ( unsigned int j = 0;
389 j < ixmlNodeList_length( p_service_list ); j++ )
391 IXML_Element* p_service_element =
392 ( IXML_Element* ) ixmlNodeList_item( p_service_list, j );
394 const char* psz_service_type =
395 xml_getChildElementValue( p_service_element,
397 if ( !psz_service_type )
400 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
401 psz_service_type ) != 0 )
404 const char* psz_event_sub_url =
405 xml_getChildElementValue( p_service_element,
407 if ( !psz_event_sub_url )
410 const char* psz_control_url =
411 xml_getChildElementValue( p_service_element,
413 if ( !psz_control_url )
416 // Try to subscribe to ContentDirectory service
418 char* psz_url = ( char* ) malloc( strlen( psz_base_url ) +
419 strlen( psz_event_sub_url ) + 1 );
422 char* psz_s1 = strdup( psz_base_url );
423 char* psz_s2 = strdup( psz_event_sub_url );
425 if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
428 p_server->setContentDirectoryEventURL( psz_url );
429 p_server->subscribeToContentDirectory();
437 // Try to browse content directory...
439 psz_url = ( char* ) malloc( strlen( psz_base_url ) +
440 strlen( psz_control_url ) + 1 );
443 char* psz_s1 = strdup( psz_base_url );
444 char* psz_s2 = strdup( psz_control_url );
446 if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
449 p_server->setContentDirectoryControlURL( psz_url );
450 p_server->fetchContents();
458 ixmlNodeList_free( p_service_list );
461 ixmlNodeList_free( p_device_list );
465 MediaServer::MediaServer( const char* psz_udn,
466 const char* psz_friendly_name,
467 services_discovery_t* p_sd )
472 _friendly_name = psz_friendly_name;
475 _p_input_item = NULL;
478 MediaServer::~MediaServer()
483 const char* MediaServer::getUDN() const
488 const char* MediaServer::getFriendlyName() const
490 return _friendly_name.c_str();
493 void MediaServer::setContentDirectoryEventURL( const char* psz_url )
495 _content_directory_event_url = psz_url;
498 const char* MediaServer::getContentDirectoryEventURL() const
500 return _content_directory_event_url.c_str();
503 void MediaServer::setContentDirectoryControlURL( const char* psz_url )
505 _content_directory_control_url = psz_url;
508 const char* MediaServer::getContentDirectoryControlURL() const
510 return _content_directory_control_url.c_str();
513 void MediaServer::subscribeToContentDirectory()
515 const char* psz_url = getContentDirectoryEventURL();
518 msg_Dbg( _p_sd, "No subscription url set!" );
522 int i_timeout = 1810;
525 int i_res = UpnpSubscribe( _p_sd->p_sys->client_handle, psz_url, &i_timeout, sid );
527 if ( i_res == UPNP_E_SUCCESS )
529 _i_subscription_timeout = i_timeout;
530 memcpy( _subscription_id, sid, sizeof( Upnp_SID ) );
535 "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__,
536 getFriendlyName(), UpnpGetErrorMessage( i_res ) );
540 IXML_Document* MediaServer::_browseAction( const char* psz_object_id_,
541 const char* psz_browser_flag_,
542 const char* psz_filter_,
543 const char* psz_starting_index_,
544 const char* psz_requested_count_,
545 const char* psz_sort_criteria_ )
547 IXML_Document* p_action = 0;
548 IXML_Document* p_response = 0;
549 const char* psz_url = getContentDirectoryControlURL();
553 msg_Dbg( _p_sd, "No subscription url set!" );
557 char* psz_object_id = strdup( psz_object_id_ );
558 char* psz_browse_flag = strdup( psz_browser_flag_ );
559 char* psz_filter = strdup( psz_filter_ );
560 char* psz_starting_index = strdup( psz_starting_index_ );
561 char* psz_requested_count = strdup( psz_requested_count_ );
562 char* psz_sort_criteria = strdup( psz_sort_criteria_ );
563 char* psz_service_type = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
567 i_res = UpnpAddToAction( &p_action, "Browse",
568 psz_service_type, "ObjectID", psz_object_id );
570 if ( i_res != UPNP_E_SUCCESS )
573 "%s:%d: ERROR: %s", __FILE__, __LINE__,
574 UpnpGetErrorMessage( i_res ) );
575 goto browseActionCleanup;
578 i_res = UpnpAddToAction( &p_action, "Browse",
579 psz_service_type, "BrowseFlag", psz_browse_flag );
581 if ( i_res != UPNP_E_SUCCESS )
584 "%s:%d: ERROR: %s", __FILE__, __LINE__,
585 UpnpGetErrorMessage( i_res ) );
586 goto browseActionCleanup;
589 i_res = UpnpAddToAction( &p_action, "Browse",
590 psz_service_type, "Filter", psz_filter );
592 if ( i_res != UPNP_E_SUCCESS )
595 "%s:%d: ERROR: %s", __FILE__, __LINE__,
596 UpnpGetErrorMessage( i_res ) );
597 goto browseActionCleanup;
600 i_res = UpnpAddToAction( &p_action, "Browse",
601 psz_service_type, "StartingIndex", psz_starting_index );
603 if ( i_res != UPNP_E_SUCCESS )
606 "%s:%d: ERROR: %s", __FILE__, __LINE__,
607 UpnpGetErrorMessage( i_res ) );
608 goto browseActionCleanup;
611 i_res = UpnpAddToAction( &p_action, "Browse",
612 psz_service_type, "RequestedCount", psz_requested_count );
614 if ( i_res != UPNP_E_SUCCESS )
617 "%s:%d: ERROR: %s", __FILE__, __LINE__,
618 UpnpGetErrorMessage( i_res ) ); goto browseActionCleanup; }
620 i_res = UpnpAddToAction( &p_action, "Browse",
621 psz_service_type, "SortCriteria", psz_sort_criteria );
623 if ( i_res != UPNP_E_SUCCESS )
626 "%s:%d: ERROR: %s", __FILE__, __LINE__,
627 UpnpGetErrorMessage( i_res ) );
628 goto browseActionCleanup;
631 i_res = UpnpSendAction( _p_sd->p_sys->client_handle,
633 CONTENT_DIRECTORY_SERVICE_TYPE,
638 if ( i_res != UPNP_E_SUCCESS )
641 "%s:%d: ERROR: %s when trying the send() action with URL: %s",
643 UpnpGetErrorMessage( i_res ), psz_url );
645 ixmlDocument_free( p_response );
651 free( psz_object_id );
652 free( psz_browse_flag );
654 free( psz_starting_index );
655 free( psz_requested_count );
656 free( psz_sort_criteria );
658 free( psz_service_type );
660 ixmlDocument_free( p_action );
664 void MediaServer::fetchContents()
666 // Delete previous contents to prevent duplicate entries
670 services_discovery_RemoveItem( _p_sd, _p_input_item );
671 services_discovery_AddItem( _p_sd, _p_input_item, NULL );
674 Container* root = new Container( 0, "0", getFriendlyName() );
676 _fetchContents( root );
679 _p_contents->setInputItem( _p_input_item );
681 _buildPlaylist( _p_contents, NULL );
684 bool MediaServer::_fetchContents( Container* p_parent )
689 "%s:%d: parent==NULL", __FILE__, __LINE__ );
693 IXML_Document* p_response = _browseAction( p_parent->getObjectID(),
694 "BrowseDirectChildren",
699 "%s:%d: ERROR! No response from browse() action",
700 __FILE__, __LINE__ );
704 IXML_Document* p_result = parseBrowseResult( p_response );
705 ixmlDocument_free( p_response );
709 msg_Dbg( _p_sd, "%s:%d: ERROR! browse() response parsing failed",
710 __FILE__, __LINE__ );
714 IXML_NodeList* containerNodeList =
715 ixmlDocument_getElementsByTagName( p_result, "container" );
717 if ( containerNodeList )
719 for ( unsigned int i = 0;
720 i < ixmlNodeList_length( containerNodeList ); i++ )
722 IXML_Element* containerElement =
723 ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
725 const char* objectID = ixmlElement_getAttribute( containerElement,
730 const char* childCountStr =
731 ixmlElement_getAttribute( containerElement, "childCount" );
733 if ( !childCountStr )
736 int childCount = atoi( childCountStr );
737 const char* title = xml_getChildElementValue( containerElement,
743 const char* resource = xml_getChildElementValue( containerElement,
746 if ( resource && childCount < 1 )
748 Item* item = new Item( p_parent, objectID, title, resource );
749 p_parent->addItem( item );
754 Container* container = new Container( p_parent, objectID, title );
755 p_parent->addContainer( container );
757 if ( childCount > 0 )
758 _fetchContents( container );
761 ixmlNodeList_free( containerNodeList );
764 IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( p_result,
768 for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
770 IXML_Element* itemElement =
771 ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
773 const char* objectID =
774 ixmlElement_getAttribute( itemElement, "id" );
780 xml_getChildElementValue( itemElement, "dc:title" );
785 const char* resource =
786 xml_getChildElementValue( itemElement, "res" );
791 Item* item = new Item( p_parent, objectID, title, resource );
792 p_parent->addItem( item );
794 ixmlNodeList_free( itemNodeList );
797 ixmlDocument_free( p_result );
801 void MediaServer::_buildPlaylist( Container* p_parent, input_item_node_t *p_input_node )
803 bool b_send = p_input_node == NULL;
805 p_input_node = input_item_node_Create( p_parent->getInputItem() );
807 for ( unsigned int i = 0; i < p_parent->getNumContainers(); i++ )
809 Container* p_container = p_parent->getContainer( i );
811 input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop",
812 p_container->getTitle() );
813 input_item_node_t *p_new_node =
814 input_item_node_AppendItem( p_input_node, p_input_item );
816 p_container->setInputItem( p_input_item );
817 _buildPlaylist( p_container, p_new_node );
820 for ( unsigned int i = 0; i < p_parent->getNumItems(); i++ )
822 Item* p_item = p_parent->getItem( i );
824 input_item_t* p_input_item = input_item_New( _p_sd,
825 p_item->getResource(),
826 p_item->getTitle() );
827 assert( p_input_item );
828 input_item_node_AppendItem( p_input_node, p_input_item );
829 p_item->setInputItem( p_input_item );
833 input_item_node_PostAndDelete( p_input_node );
836 void MediaServer::setInputItem( input_item_t* p_input_item )
838 if(_p_input_item == p_input_item)
842 vlc_gc_decref( _p_input_item );
844 vlc_gc_incref( p_input_item );
845 _p_input_item = p_input_item;
848 bool MediaServer::compareSID( const char* psz_sid )
850 return ( strncmp( _subscription_id, psz_sid, sizeof( Upnp_SID ) ) == 0 );
854 // MediaServerList...
856 MediaServerList::MediaServerList( services_discovery_t* p_sd )
861 MediaServerList::~MediaServerList()
863 for ( unsigned int i = 0; i < _list.size(); i++ )
869 bool MediaServerList::addServer( MediaServer* p_server )
871 input_item_t* p_input_item = NULL;
872 if ( getServer( p_server->getUDN() ) != 0 ) return false;
874 msg_Dbg( _p_sd, "Adding server '%s' with uuid '%s'", p_server->getFriendlyName(), p_server->getUDN() );
876 p_input_item = input_item_New( _p_sd, "vlc://nop",
877 p_server->getFriendlyName() );
878 p_server->setInputItem( p_input_item );
880 services_discovery_AddItem( _p_sd, p_input_item, NULL );
882 _list.push_back( p_server );
887 MediaServer* MediaServerList::getServer( const char* psz_udn )
889 MediaServer* p_result = 0;
891 for ( unsigned int i = 0; i < _list.size(); i++ )
893 if( strcmp( psz_udn, _list[i]->getUDN() ) == 0 )
903 MediaServer* MediaServerList::getServerBySID( const char* psz_sid )
905 MediaServer* p_server = 0;
907 for ( unsigned int i = 0; i < _list.size(); i++ )
909 if ( _list[i]->compareSID( psz_sid ) )
919 void MediaServerList::removeServer( const char* psz_udn )
921 MediaServer* p_server = getServer( psz_udn );
922 if ( !p_server ) return;
925 "Removing server '%s'", p_server->getFriendlyName() );
927 std::vector<MediaServer*>::iterator it;
928 for ( it = _list.begin(); it != _list.end(); ++it )
930 if ( *it == p_server )
942 Item::Item( Container* p_parent, const char* psz_object_id, const char* psz_title,
943 const char* psz_resource )
947 _objectID = psz_object_id;
949 _resource = psz_resource;
951 _p_input_item = NULL;
957 vlc_gc_decref( _p_input_item );
960 const char* Item::getObjectID() const
962 return _objectID.c_str();
965 const char* Item::getTitle() const
967 return _title.c_str();
970 const char* Item::getResource() const
972 return _resource.c_str();
975 void Item::setInputItem( input_item_t* p_input_item )
977 if(_p_input_item == p_input_item)
981 vlc_gc_decref( _p_input_item );
983 vlc_gc_incref( p_input_item );
984 _p_input_item = p_input_item;
987 input_item_t* Item::getInputItem() const
989 return _p_input_item;
995 Container::Container( Container* p_parent,
996 const char* psz_object_id,
997 const char* psz_title )
1001 _objectID = psz_object_id;
1004 _p_input_item = NULL;
1007 Container::~Container()
1009 for ( unsigned int i = 0; i < _containers.size(); i++ )
1011 delete _containers[i];
1014 for ( unsigned int i = 0; i < _items.size(); i++ )
1020 vlc_gc_decref( _p_input_item );
1023 void Container::addItem( Item* item )
1025 _items.push_back( item );
1028 void Container::addContainer( Container* p_container )
1030 _containers.push_back( p_container );
1033 const char* Container::getObjectID() const
1035 return _objectID.c_str();
1038 const char* Container::getTitle() const
1040 return _title.c_str();
1043 unsigned int Container::getNumItems() const
1045 return _items.size();
1048 unsigned int Container::getNumContainers() const
1050 return _containers.size();
1053 Item* Container::getItem( unsigned int i_index ) const
1055 if ( i_index < _items.size() ) return _items[i_index];
1059 Container* Container::getContainer( unsigned int i_index ) const
1061 if ( i_index < _containers.size() ) return _containers[i_index];
1065 Container* Container::getParent()
1070 void Container::setInputItem( input_item_t* p_input_item )
1072 if(_p_input_item == p_input_item)
1076 vlc_gc_decref( _p_input_item );
1078 vlc_gc_incref( p_input_item );
1079 _p_input_item = p_input_item;
1082 input_item_t* Container::getInputItem() const
1084 return _p_input_item;