1 /*****************************************************************************
2 * Upnp_intel.cpp : UPnP discovery module (Intel SDK)
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 ???
36 #include "upnp_intel.hpp"
38 #include <vlc_plugin.h>
39 #include <vlc_services_discovery.h>
43 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
44 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
47 struct services_discovery_sys_t
49 UpnpClient_Handle client_handle;
50 MediaServerList* p_server_list;
51 vlc_mutex_t callback_lock;
54 // VLC callback prototypes
55 static int Open( vlc_object_t* );
56 static void Close( vlc_object_t* );
57 VLC_SD_PROBE_HELPER("upnp_intel", "Universal Plug'n'Play", SD_CAT_LAN)
62 set_shortname( "UPnP" );
63 set_description( N_( "Universal Plug'n'Play" ) );
64 set_category( CAT_PLAYLIST );
65 set_subcategory( SUBCAT_PLAYLIST_SD );
66 set_capability( "services_discovery", 0 );
67 set_callbacks( Open, Close );
69 VLC_SD_PROBE_SUBMODULE
75 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data );
77 const char* xml_getChildElementValue( IXML_Element* p_parent,
78 const char* psz_tag_name );
80 IXML_Document* parseBrowseResult( IXML_Document* p_doc );
85 static int Open( vlc_object_t *p_this )
88 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
89 services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
90 calloc( 1, sizeof( services_discovery_sys_t ) );
92 if(!(p_sd->p_sys = p_sys))
95 i_res = UpnpInit( 0, 0 );
96 if( i_res != UPNP_E_SUCCESS )
98 msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
103 p_sys->p_server_list = new MediaServerList( p_sd );
104 vlc_mutex_init( &p_sys->callback_lock );
106 i_res = UpnpRegisterClient( Callback, p_sd, &p_sys->client_handle );
107 if( i_res != UPNP_E_SUCCESS )
109 msg_Err( p_sd, "%s", UpnpGetErrorMessage( i_res ) );
110 Close( (vlc_object_t*) p_sd );
114 i_res = UpnpSearchAsync( p_sys->client_handle, 5,
115 MEDIA_SERVER_DEVICE_TYPE, p_sd );
117 if( i_res != UPNP_E_SUCCESS )
119 msg_Err( p_sd, "%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, "%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;
140 delete p_sd->p_sys->p_server_list;
141 vlc_mutex_destroy( &p_sd->p_sys->callback_lock );
146 // XML utility functions:
148 // Returns the value of a child element, or 0 on error
149 const char* xml_getChildElementValue( IXML_Element* p_parent,
150 const char* psz_tag_name_ )
152 if ( !p_parent ) return 0;
153 if ( !psz_tag_name_ ) return 0;
155 char* psz_tag_name = strdup( psz_tag_name_ );
156 IXML_NodeList* p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
157 free( psz_tag_name );
158 if ( !p_node_list ) return 0;
160 IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
161 ixmlNodeList_free( p_node_list );
162 if ( !p_element ) return 0;
164 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
165 if ( !p_text_node ) return 0;
167 return ixmlNode_getNodeValue( p_text_node );
170 // Extracts the result document from a SOAP response
171 IXML_Document* parseBrowseResult( IXML_Document* p_doc )
175 if ( !p_doc ) return 0;
177 IXML_NodeList* p_result_list = ixmlDocument_getElementsByTagName( p_doc,
180 if ( !p_result_list ) return 0;
182 IXML_Node* p_result_node = ixmlNodeList_item( p_result_list, 0 );
184 ixmlNodeList_free( p_result_list );
186 if ( !p_result_node ) return 0;
188 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_result_node );
189 if ( !p_text_node ) return 0;
191 const char* psz_result_string = ixmlNode_getNodeValue( p_text_node );
192 char* psz_result_xml = strdup( psz_result_string );
194 IXML_Document* p_browse_doc = ixmlParseBuffer( psz_result_xml );
196 free( psz_result_xml );
202 // Handles all UPnP events
203 static int Callback( Upnp_EventType event_type, void* p_event, void* p_user_data )
205 services_discovery_t* p_sd = ( services_discovery_t* ) p_user_data;
206 services_discovery_sys_t* p_sys = p_sd->p_sys;
207 vlc_mutex_locker locker( &p_sys->callback_lock );
211 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
212 case UPNP_DISCOVERY_SEARCH_RESULT:
214 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
216 IXML_Document *p_description_doc = 0;
219 i_res = UpnpDownloadXmlDoc( p_discovery->Location, &p_description_doc );
220 if ( i_res != UPNP_E_SUCCESS )
223 "%s:%d: Could not download device description!",
224 __FILE__, __LINE__ );
228 MediaServer::parseDeviceDescription( p_description_doc,
229 p_discovery->Location, p_sd );
231 ixmlDocument_free( p_description_doc );
235 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
237 struct Upnp_Discovery* p_discovery = ( struct Upnp_Discovery* )p_event;
239 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" );
274 "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
275 __FILE__, __LINE__, event_type );
279 return UPNP_E_SUCCESS;
283 // Class implementations...
287 void MediaServer::parseDeviceDescription( IXML_Document* p_doc,
288 const char* p_location,
289 services_discovery_t* p_sd )
293 msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
299 msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
303 const char* psz_base_url = p_location;
305 // Try to extract baseURL
307 IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( p_doc, "baseURL" );
311 if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) )
313 IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node );
314 if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node );
317 ixmlNodeList_free( p_url_list );
322 IXML_NodeList* p_device_list =
323 ixmlDocument_getElementsByTagName( p_doc, "device" );
327 for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ )
329 IXML_Element* p_device_element =
330 ( IXML_Element* ) ixmlNodeList_item( p_device_list, i );
332 const char* psz_device_type = xml_getChildElementValue( p_device_element,
334 if ( !psz_device_type )
337 "%s:%d: no deviceType!",
338 __FILE__, __LINE__ );
342 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type ) != 0 )
345 const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" );
348 msg_Dbg( p_sd, "%s:%d: no UDN!",
349 __FILE__, __LINE__ );
353 if ( p_sd->p_sys->p_server_list->getServer( psz_udn ) != 0 )
356 const char* psz_friendly_name =
357 xml_getChildElementValue( p_device_element,
360 if ( !psz_friendly_name )
362 msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
366 MediaServer* p_server = new MediaServer( psz_udn, psz_friendly_name, p_sd );
368 if ( !p_sd->p_sys->p_server_list->addServer( p_server ) )
376 // Check for ContentDirectory service...
377 IXML_NodeList* p_service_list =
378 ixmlElement_getElementsByTagName( p_device_element,
380 if ( p_service_list )
382 for ( unsigned int j = 0;
383 j < ixmlNodeList_length( p_service_list ); j++ )
385 IXML_Element* p_service_element =
386 ( IXML_Element* ) ixmlNodeList_item( p_service_list, j );
388 const char* psz_service_type =
389 xml_getChildElementValue( p_service_element,
391 if ( !psz_service_type )
394 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
395 psz_service_type ) != 0 )
398 const char* psz_event_sub_url =
399 xml_getChildElementValue( p_service_element,
401 if ( !psz_event_sub_url )
404 const char* psz_control_url =
405 xml_getChildElementValue( p_service_element,
407 if ( !psz_control_url )
410 // Try to subscribe to ContentDirectory service
412 char* psz_url = ( char* ) malloc( strlen( psz_base_url ) +
413 strlen( psz_event_sub_url ) + 1 );
416 char* psz_s1 = strdup( psz_base_url );
417 char* psz_s2 = strdup( psz_event_sub_url );
419 if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
422 p_server->setContentDirectoryEventURL( psz_url );
423 p_server->subscribeToContentDirectory();
431 // Try to browse content directory...
433 psz_url = ( char* ) malloc( strlen( psz_base_url ) +
434 strlen( psz_control_url ) + 1 );
437 char* psz_s1 = strdup( psz_base_url );
438 char* psz_s2 = strdup( psz_control_url );
440 if ( UpnpResolveURL( psz_s1, psz_s2, psz_url ) ==
443 p_server->setContentDirectoryControlURL( psz_url );
444 p_server->fetchContents();
452 ixmlNodeList_free( p_service_list );
455 ixmlNodeList_free( p_device_list );
459 MediaServer::MediaServer( const char* psz_udn,
460 const char* psz_friendly_name,
461 services_discovery_t* p_sd )
466 _friendlyName = psz_friendly_name;
472 MediaServer::~MediaServer()
477 const char* MediaServer::getUDN() const
479 const char* s = _UDN.c_str();
483 const char* MediaServer::getFriendlyName() const
485 const char* s = _friendlyName.c_str();
489 void MediaServer::setContentDirectoryEventURL( const char* psz_url )
491 _contentDirectoryEventURL = psz_url;
494 const char* MediaServer::getContentDirectoryEventURL() const
496 const char* s = _contentDirectoryEventURL.c_str();
500 void MediaServer::setContentDirectoryControlURL( const char* psz_url )
502 _contentDirectoryControlURL = psz_url;
505 const char* MediaServer::getContentDirectoryControlURL() const
507 return _contentDirectoryControlURL.c_str();
510 void MediaServer::subscribeToContentDirectory()
512 const char* psz_url = getContentDirectoryEventURL();
513 if ( !psz_url || strcmp( psz_url, "" ) == 0 )
515 msg_Dbg( _p_sd, "No subscription url set!" );
519 int i_timeout = 1810;
522 int i_res = UpnpSubscribe( _p_sd->p_sys->client_handle, psz_url, &i_timeout, sid );
524 if ( i_res == UPNP_E_SUCCESS )
526 _subscriptionTimeOut = i_timeout;
527 memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
532 "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__,
533 getFriendlyName(), UpnpGetErrorMessage( i_res ) );
537 IXML_Document* MediaServer::_browseAction( const char* psz_object_id_,
538 const char* psz_browser_flag_,
539 const char* psz_filter_,
540 const char* psz_starting_index_,
541 const char* psz_requested_count_,
542 const char* psz_sort_criteria_ )
544 IXML_Document* p_action = 0;
545 IXML_Document* p_response = 0;
546 const char* psz_url = getContentDirectoryControlURL();
548 if ( !psz_url || strcmp( psz_url, "" ) == 0 )
550 msg_Dbg( _p_sd, "No subscription url set!" );
554 char* psz_object_id = strdup( psz_object_id_ );
555 char* psz_browse_flag = strdup( psz_browser_flag_ );
556 char* psz_filter = strdup( psz_filter_ );
557 char* psz_starting_index = strdup( psz_starting_index_ );
558 char* psz_requested_count = strdup( psz_requested_count_ );
559 char* psz_sort_criteria = strdup( psz_sort_criteria_ );
560 char* psz_service_type = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
564 i_res = UpnpAddToAction( &p_action, "Browse",
565 psz_service_type, "ObjectID", psz_object_id );
567 if ( i_res != UPNP_E_SUCCESS )
570 "%s:%d: ERROR: %s", __FILE__, __LINE__,
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 )
581 "%s:%d: ERROR: %s", __FILE__, __LINE__,
582 UpnpGetErrorMessage( i_res ) );
583 goto browseActionCleanup;
586 i_res = UpnpAddToAction( &p_action, "Browse",
587 psz_service_type, "Filter", psz_filter );
589 if ( i_res != UPNP_E_SUCCESS )
592 "%s:%d: ERROR: %s", __FILE__, __LINE__,
593 UpnpGetErrorMessage( i_res ) );
594 goto browseActionCleanup;
597 i_res = UpnpAddToAction( &p_action, "Browse",
598 psz_service_type, "StartingIndex", psz_starting_index );
600 if ( i_res != UPNP_E_SUCCESS )
603 "%s:%d: ERROR: %s", __FILE__, __LINE__,
604 UpnpGetErrorMessage( i_res ) );
605 goto browseActionCleanup;
608 i_res = UpnpAddToAction( &p_action, "Browse",
609 psz_service_type, "RequestedCount", psz_requested_count );
611 if ( i_res != UPNP_E_SUCCESS )
614 "%s:%d: ERROR: %s", __FILE__, __LINE__,
615 UpnpGetErrorMessage( i_res ) ); goto browseActionCleanup; }
617 i_res = UpnpAddToAction( &p_action, "Browse",
618 psz_service_type, "SortCriteria", psz_sort_criteria );
620 if ( i_res != UPNP_E_SUCCESS )
623 "%s:%d: ERROR: %s", __FILE__, __LINE__,
624 UpnpGetErrorMessage( i_res ) );
625 goto browseActionCleanup;
628 i_res = UpnpSendAction( _p_sd->p_sys->client_handle,
630 CONTENT_DIRECTORY_SERVICE_TYPE,
635 if ( i_res != UPNP_E_SUCCESS )
638 "%s:%d: ERROR: %s when trying the send() action with URL: %s",
640 UpnpGetErrorMessage( i_res ), psz_url );
642 ixmlDocument_free( p_response );
648 free( psz_object_id );
649 free( psz_browse_flag );
651 free( psz_starting_index );
652 free( psz_requested_count );
653 free( psz_sort_criteria );
655 free( psz_service_type );
657 ixmlDocument_free( p_action );
661 void MediaServer::fetchContents()
663 Container* root = new Container( 0, "0", getFriendlyName() );
664 _fetchContents( root );
667 _contents->setInputItem( _inputItem );
669 _buildPlaylist( _contents, NULL );
672 bool MediaServer::_fetchContents( Container* p_parent )
677 "%s:%d: parent==NULL", __FILE__, __LINE__ );
681 IXML_Document* p_response = _browseAction( p_parent->getObjectID(),
682 "BrowseDirectChildren",
687 "%s:%d: ERROR! No response from browse() action",
688 __FILE__, __LINE__ );
692 IXML_Document* result = parseBrowseResult( p_response );
693 ixmlDocument_free( p_response );
698 "%s:%d: ERROR! browse() response parsing failed",
699 __FILE__, __LINE__ );
703 IXML_NodeList* containerNodeList =
704 ixmlDocument_getElementsByTagName( result, "container" );
706 if ( containerNodeList )
708 for ( unsigned int i = 0;
709 i < ixmlNodeList_length( containerNodeList ); i++ )
711 IXML_Element* containerElement =
712 ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
714 const char* objectID = ixmlElement_getAttribute( containerElement,
719 const char* childCountStr =
720 ixmlElement_getAttribute( containerElement, "childCount" );
722 if ( !childCountStr )
725 int childCount = atoi( childCountStr );
726 const char* title = xml_getChildElementValue( containerElement,
732 const char* resource = xml_getChildElementValue( containerElement,
735 if ( resource && childCount < 1 )
737 Item* item = new Item( p_parent, objectID, title, resource );
738 p_parent->addItem( item );
743 Container* container = new Container( p_parent, objectID, title );
744 p_parent->addContainer( container );
746 if ( childCount > 0 )
747 _fetchContents( container );
750 ixmlNodeList_free( containerNodeList );
753 IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result,
757 for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
759 IXML_Element* itemElement =
760 ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
762 const char* objectID =
763 ixmlElement_getAttribute( itemElement, "id" );
769 xml_getChildElementValue( itemElement, "dc:title" );
774 const char* resource =
775 xml_getChildElementValue( itemElement, "res" );
780 Item* item = new Item( p_parent, objectID, title, resource );
781 p_parent->addItem( item );
783 ixmlNodeList_free( itemNodeList );
786 ixmlDocument_free( result );
790 void MediaServer::_buildPlaylist( Container* p_parent, input_item_node_t *p_input_node )
792 bool send = p_input_node == NULL;
794 p_input_node = input_item_node_Create( p_parent->getInputItem() );
796 for ( unsigned int i = 0; i < p_parent->getNumContainers(); i++ )
798 Container* container = p_parent->getContainer( i );
800 input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop", container->getTitle() );
801 input_item_node_t *p_new_node =
802 input_item_node_AppendItem( p_input_node, p_input_item );
804 container->setInputItem( p_input_item );
805 _buildPlaylist( container, p_new_node );
808 for ( unsigned int i = 0; i < p_parent->getNumItems(); i++ )
810 Item* item = p_parent->getItem( i );
812 input_item_t* p_input_item = input_item_New( _p_sd,
815 assert( p_input_item );
816 input_item_node_AppendItem( p_input_node, p_input_item );
817 item->setInputItem( p_input_item );
821 input_item_node_PostAndDelete( p_input_node );
824 void MediaServer::setInputItem( input_item_t* p_input_item )
826 if(_inputItem == p_input_item)
830 vlc_gc_decref( _inputItem );
832 vlc_gc_incref( p_input_item );
833 _inputItem = p_input_item;
836 bool MediaServer::compareSID( const char* sid )
838 return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
842 // MediaServerList...
844 MediaServerList::MediaServerList( services_discovery_t* p_sd )
849 MediaServerList::~MediaServerList()
851 for ( unsigned int i = 0; i < _list.size(); i++ )
857 bool MediaServerList::addServer( MediaServer* s )
859 input_item_t* p_input_item = NULL;
860 if ( getServer( s->getUDN() ) != 0 ) return false;
862 msg_Dbg( _p_sd, "Adding server '%s'",
863 s->getFriendlyName() );
865 services_discovery_t* p_sd = _p_sd;
867 p_input_item = input_item_New( p_sd, "vlc://nop", s->getFriendlyName() );
868 s->setInputItem( p_input_item );
870 services_discovery_AddItem( p_sd, p_input_item, NULL );
872 _list.push_back( s );
877 MediaServer* MediaServerList::getServer( const char* psz_udn )
879 MediaServer* result = 0;
881 for ( unsigned int i = 0; i < _list.size(); i++ )
883 if( strcmp( psz_udn, _list[i]->getUDN() ) == 0 )
893 MediaServer* MediaServerList::getServerBySID( const char* sid )
895 MediaServer* p_server = 0;
897 for ( unsigned int i = 0; i < _list.size(); i++ )
899 if ( _list[i]->compareSID( sid ) )
909 void MediaServerList::removeServer( const char* psz_udn )
911 MediaServer* p_server = getServer( psz_udn );
912 if ( !p_server ) return;
915 "Removing server '%s'", p_server->getFriendlyName() );
917 std::vector<MediaServer*>::iterator it;
918 for ( it = _list.begin(); it != _list.end(); it++ )
920 if ( *it == p_server )
932 Item::Item( Container* p_parent, const char* psz_object_id, const char* psz_title,
933 const char* psz_resource )
937 _objectID = psz_object_id;
939 _resource = psz_resource;
947 vlc_gc_decref( _inputItem );
950 const char* Item::getObjectID() const
952 return _objectID.c_str();
955 const char* Item::getTitle() const
957 return _title.c_str();
960 const char* Item::getResource() const
962 return _resource.c_str();
965 void Item::setInputItem( input_item_t* p_input_item )
967 if(_inputItem == p_input_item)
971 vlc_gc_decref( _inputItem );
973 vlc_gc_incref( p_input_item );
974 _inputItem = p_input_item;
977 input_item_t* Item::getInputItem() const
985 Container::Container( Container* p_parent,
986 const char* psz_object_id,
987 const char* psz_title )
991 _objectID = psz_object_id;
997 Container::~Container()
999 for ( unsigned int i = 0; i < _containers.size(); i++ )
1001 delete _containers[i];
1004 for ( unsigned int i = 0; i < _items.size(); i++ )
1010 vlc_gc_decref( _inputItem );
1013 void Container::addItem( Item* item )
1015 _items.push_back( item );
1018 void Container::addContainer( Container* p_container )
1020 _containers.push_back( p_container );
1023 const char* Container::getObjectID() const
1025 return _objectID.c_str();
1028 const char* Container::getTitle() const
1030 return _title.c_str();
1033 unsigned int Container::getNumItems() const
1035 return _items.size();
1038 unsigned int Container::getNumContainers() const
1040 return _containers.size();
1043 Item* Container::getItem( unsigned int i_index ) const
1045 if ( i_index < _items.size() ) return _items[i_index];
1049 Container* Container::getContainer( unsigned int i_index ) const
1051 if ( i_index < _containers.size() ) return _containers[i_index];
1055 Container* Container::getParent()
1060 void Container::setInputItem( input_item_t* p_input_item )
1062 if(_inputItem == p_input_item)
1066 vlc_gc_decref( _inputItem );
1068 vlc_gc_incref( p_input_item );
1069 _inputItem = p_input_item;
1072 input_item_t* Container::getInputItem() const