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 ???
30 \TODO: Change names to VLC standard ???
37 #include "upnp_intel.hpp"
39 #include <vlc_plugin.h>
40 #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 clientHandle
;
51 MediaServerList
* serverList
;
52 vlc_mutex_t callbackLock
;
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 eventType
, void* event
, void* user_data
);
78 const char* xml_getChildElementValue( IXML_Element
* parent
,
79 const char* tagName
);
81 IXML_Document
* parseBrowseResult( IXML_Document
* 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 res
= UpnpInit( 0, 0 );
97 if( res
!= UPNP_E_SUCCESS
)
99 msg_Err( p_sd
, "%s", UpnpGetErrorMessage( res
) );
104 p_sys
->serverList
= new MediaServerList( p_sd
);
105 vlc_mutex_init( &p_sys
->callbackLock
);
107 res
= UpnpRegisterClient( Callback
, p_sd
, &p_sys
->clientHandle
);
108 if( res
!= UPNP_E_SUCCESS
)
110 msg_Err( p_sd
, "%s", UpnpGetErrorMessage( res
) );
111 Close( (vlc_object_t
*) p_sd
);
115 res
= UpnpSearchAsync( p_sys
->clientHandle
, 5,
116 MEDIA_SERVER_DEVICE_TYPE
, p_sd
);
118 if( res
!= UPNP_E_SUCCESS
)
120 msg_Err( p_sd
, "%s", UpnpGetErrorMessage( res
) );
121 Close( (vlc_object_t
*) p_sd
);
128 static void Close( vlc_object_t
*p_this
)
130 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
133 delete p_sd
->p_sys
->serverList
;
134 vlc_mutex_destroy( &p_sd
->p_sys
->callbackLock
);
139 // XML utility functions:
141 // Returns the value of a child element, or 0 on error
142 const char* xml_getChildElementValue( IXML_Element
* parent
,
143 const char* tagName
)
145 if ( !parent
) return 0;
146 if ( !tagName
) return 0;
148 char* s
= strdup( tagName
);
149 IXML_NodeList
* nodeList
= ixmlElement_getElementsByTagName( parent
, s
);
151 if ( !nodeList
) return 0;
153 IXML_Node
* element
= ixmlNodeList_item( nodeList
, 0 );
154 ixmlNodeList_free( nodeList
);
155 if ( !element
) return 0;
157 IXML_Node
* textNode
= ixmlNode_getFirstChild( element
);
158 if ( !textNode
) return 0;
160 return ixmlNode_getNodeValue( textNode
);
163 // Extracts the result document from a SOAP response
164 IXML_Document
* parseBrowseResult( IXML_Document
* doc
)
168 if ( !doc
) return 0;
170 IXML_NodeList
* resultList
= ixmlDocument_getElementsByTagName( doc
,
173 if ( !resultList
) return 0;
175 IXML_Node
* resultNode
= ixmlNodeList_item( resultList
, 0 );
177 ixmlNodeList_free( resultList
);
179 if ( !resultNode
) return 0;
181 IXML_Node
* textNode
= ixmlNode_getFirstChild( resultNode
);
182 if ( !textNode
) return 0;
184 const char* resultString
= ixmlNode_getNodeValue( textNode
);
185 char* resultXML
= strdup( resultString
);
187 IXML_Document
* browseDoc
= ixmlParseBuffer( resultXML
);
195 // Handles all UPnP events
196 static int Callback( Upnp_EventType eventType
, void* event
, void* user_data
)
198 services_discovery_t
*p_sd
= ( services_discovery_t
* ) user_data
;
199 services_discovery_sys_t
* p_sys
= p_sd
->p_sys
;
200 vlc_mutex_locker
locker( &p_sys
->callbackLock
);
202 switch( eventType
) {
204 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
205 case UPNP_DISCOVERY_SEARCH_RESULT
:
207 struct Upnp_Discovery
* discovery
= ( struct Upnp_Discovery
* )event
;
209 IXML_Document
*descriptionDoc
= 0;
212 res
= UpnpDownloadXmlDoc( discovery
->Location
, &descriptionDoc
);
213 if ( res
!= UPNP_E_SUCCESS
)
216 "%s:%d: Could not download device description!",
217 __FILE__
, __LINE__
);
221 MediaServer::parseDeviceDescription( descriptionDoc
,
222 discovery
->Location
, p_sd
);
224 ixmlDocument_free( descriptionDoc
);
228 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
:
230 struct Upnp_Discovery
* discovery
= ( struct Upnp_Discovery
* )event
;
232 p_sys
->serverList
->removeServer( discovery
->DeviceId
);
236 case UPNP_EVENT_RECEIVED
:
238 Upnp_Event
* e
= ( Upnp_Event
* )event
;
240 MediaServer
* server
= p_sys
->serverList
->getServerBySID( e
->Sid
);
241 if ( server
) server
->fetchContents();
245 case UPNP_EVENT_AUTORENEWAL_FAILED
:
246 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
:
250 Upnp_Event_Subscribe
* s
= ( Upnp_Event_Subscribe
* )event
;
252 MediaServer
* server
= p_sys
->serverList
->getServerBySID( s
->Sid
);
253 if ( server
) server
->subscribeToContentDirectory();
257 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
258 msg_Warn( p_sd
, "subscription complete" );
261 case UPNP_DISCOVERY_SEARCH_TIMEOUT
:
262 msg_Warn( p_sd
, "search timeout" );
267 "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
268 __FILE__
, __LINE__
, eventType
);
272 return UPNP_E_SUCCESS
;
276 // Class implementations...
280 void MediaServer::parseDeviceDescription( IXML_Document
* doc
,
281 const char* location
,
282 services_discovery_t
* p_sd
)
286 msg_Dbg( p_sd
, "%s:%d: NULL", __FILE__
, __LINE__
);
292 msg_Dbg( p_sd
, "%s:%d: NULL", __FILE__
, __LINE__
);
296 const char* baseURL
= location
;
298 // Try to extract baseURL
300 IXML_NodeList
* urlList
= ixmlDocument_getElementsByTagName( doc
, "baseURL" );
304 if ( IXML_Node
* urlNode
= ixmlNodeList_item( urlList
, 0 ) )
306 IXML_Node
* textNode
= ixmlNode_getFirstChild( urlNode
);
307 if ( textNode
) baseURL
= ixmlNode_getNodeValue( textNode
);
310 ixmlNodeList_free( urlList
);
315 IXML_NodeList
* deviceList
=
316 ixmlDocument_getElementsByTagName( doc
, "device" );
320 for ( unsigned int i
= 0; i
< ixmlNodeList_length( deviceList
); i
++ )
322 IXML_Element
* deviceElement
=
323 ( IXML_Element
* ) ixmlNodeList_item( deviceList
, i
);
325 const char* deviceType
= xml_getChildElementValue( deviceElement
,
330 "%s:%d: no deviceType!",
331 __FILE__
, __LINE__
);
335 if ( strcmp( MEDIA_SERVER_DEVICE_TYPE
, deviceType
) != 0 )
338 const char* UDN
= xml_getChildElementValue( deviceElement
, "UDN" );
341 msg_Dbg( p_sd
, "%s:%d: no UDN!",
342 __FILE__
, __LINE__
);
346 if ( p_sd
->p_sys
->serverList
->getServer( UDN
) != 0 )
349 const char* friendlyName
=
350 xml_getChildElementValue( deviceElement
,
355 msg_Dbg( p_sd
, "%s:%d: no friendlyName!", __FILE__
, __LINE__
);
359 MediaServer
* server
= new MediaServer( UDN
, friendlyName
, p_sd
);
361 if ( !p_sd
->p_sys
->serverList
->addServer( server
) )
369 // Check for ContentDirectory service...
370 IXML_NodeList
* serviceList
=
371 ixmlElement_getElementsByTagName( deviceElement
,
375 for ( unsigned int j
= 0;
376 j
< ixmlNodeList_length( serviceList
); j
++ )
378 IXML_Element
* serviceElement
=
379 ( IXML_Element
* ) ixmlNodeList_item( serviceList
, j
);
381 const char* serviceType
=
382 xml_getChildElementValue( serviceElement
,
387 if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE
,
391 const char* eventSubURL
=
392 xml_getChildElementValue( serviceElement
,
397 const char* controlURL
=
398 xml_getChildElementValue( serviceElement
,
403 // Try to subscribe to ContentDirectory service
405 char* url
= ( char* ) malloc( strlen( baseURL
) +
406 strlen( eventSubURL
) + 1 );
409 char* s1
= strdup( baseURL
);
410 char* s2
= strdup( eventSubURL
);
412 if ( UpnpResolveURL( s1
, s2
, url
) ==
415 server
->setContentDirectoryEventURL( url
);
416 server
->subscribeToContentDirectory();
424 // Try to browse content directory...
426 url
= ( char* ) malloc( strlen( baseURL
) +
427 strlen( controlURL
) + 1 );
430 char* s1
= strdup( baseURL
);
431 char* s2
= strdup( controlURL
);
433 if ( UpnpResolveURL( s1
, s2
, url
) ==
436 server
->setContentDirectoryControlURL( url
);
437 server
->fetchContents();
445 ixmlNodeList_free( serviceList
);
448 ixmlNodeList_free( deviceList
);
452 MediaServer::MediaServer( const char* UDN
,
453 const char* friendlyName
,
454 services_discovery_t
* p_sd
)
459 _friendlyName
= friendlyName
;
465 MediaServer::~MediaServer()
470 const char* MediaServer::getUDN() const
472 const char* s
= _UDN
.c_str();
476 const char* MediaServer::getFriendlyName() const
478 const char* s
= _friendlyName
.c_str();
482 void MediaServer::setContentDirectoryEventURL( const char* url
)
484 _contentDirectoryEventURL
= url
;
487 const char* MediaServer::getContentDirectoryEventURL() const
489 const char* s
= _contentDirectoryEventURL
.c_str();
493 void MediaServer::setContentDirectoryControlURL( const char* url
)
495 _contentDirectoryControlURL
= url
;
498 const char* MediaServer::getContentDirectoryControlURL() const
500 return _contentDirectoryControlURL
.c_str();
503 void MediaServer::subscribeToContentDirectory()
505 const char* url
= getContentDirectoryEventURL();
506 if ( !url
|| strcmp( url
, "" ) == 0 )
508 msg_Dbg( _p_sd
, "No subscription url set!" );
515 int res
= UpnpSubscribe( _p_sd
->p_sys
->clientHandle
, url
, &timeOut
, sid
);
517 if ( res
== UPNP_E_SUCCESS
)
519 _subscriptionTimeOut
= timeOut
;
520 memcpy( _subscriptionID
, sid
, sizeof( Upnp_SID
) );
525 "%s:%d: WARNING: '%s': %s", __FILE__
, __LINE__
,
526 getFriendlyName(), UpnpGetErrorMessage( res
) );
530 IXML_Document
* MediaServer::_browseAction( const char* pObjectID
,
531 const char* pBrowseFlag
,
533 const char* pStartingIndex
,
534 const char* pRequestedCount
,
535 const char* pSortCriteria
)
537 IXML_Document
* action
= 0;
538 IXML_Document
* response
= 0;
539 const char* url
= getContentDirectoryControlURL();
541 if ( !url
|| strcmp( url
, "" ) == 0 )
543 msg_Dbg( _p_sd
, "No subscription url set!" );
547 char* ObjectID
= strdup( pObjectID
);
548 char* BrowseFlag
= strdup( pBrowseFlag
);
549 char* Filter
= strdup( pFilter
);
550 char* StartingIndex
= strdup( pStartingIndex
);
551 char* RequestedCount
= strdup( pRequestedCount
);
552 char* SortCriteria
= strdup( pSortCriteria
);
553 char* serviceType
= strdup( CONTENT_DIRECTORY_SERVICE_TYPE
);
557 res
= UpnpAddToAction( &action
, "Browse",
558 serviceType
, "ObjectID", ObjectID
);
560 if ( res
!= UPNP_E_SUCCESS
)
563 "%s:%d: ERROR: %s", __FILE__
, __LINE__
,
564 UpnpGetErrorMessage( res
) );
565 goto browseActionCleanup
;
568 res
= UpnpAddToAction( &action
, "Browse",
569 serviceType
, "BrowseFlag", BrowseFlag
);
571 if ( res
!= UPNP_E_SUCCESS
)
574 "%s:%d: ERROR: %s", __FILE__
, __LINE__
,
575 UpnpGetErrorMessage( res
) );
576 goto browseActionCleanup
;
579 res
= UpnpAddToAction( &action
, "Browse",
580 serviceType
, "Filter", Filter
);
582 if ( res
!= UPNP_E_SUCCESS
)
585 "%s:%d: ERROR: %s", __FILE__
, __LINE__
,
586 UpnpGetErrorMessage( res
) );
587 goto browseActionCleanup
;
590 res
= UpnpAddToAction( &action
, "Browse",
591 serviceType
, "StartingIndex", StartingIndex
);
593 if ( res
!= UPNP_E_SUCCESS
)
596 "%s:%d: ERROR: %s", __FILE__
, __LINE__
,
597 UpnpGetErrorMessage( res
) );
598 goto browseActionCleanup
;
601 res
= UpnpAddToAction( &action
, "Browse",
602 serviceType
, "RequestedCount", RequestedCount
);
604 if ( res
!= UPNP_E_SUCCESS
)
607 "%s:%d: ERROR: %s", __FILE__
, __LINE__
,
608 UpnpGetErrorMessage( res
) ); goto browseActionCleanup
; }
610 res
= UpnpAddToAction( &action
, "Browse",
611 serviceType
, "SortCriteria", SortCriteria
);
613 if ( res
!= UPNP_E_SUCCESS
)
616 "%s:%d: ERROR: %s", __FILE__
, __LINE__
,
617 UpnpGetErrorMessage( res
) );
618 goto browseActionCleanup
;
621 res
= UpnpSendAction( _p_sd
->p_sys
->clientHandle
,
623 CONTENT_DIRECTORY_SERVICE_TYPE
,
628 if ( res
!= UPNP_E_SUCCESS
)
631 "%s:%d: ERROR: %s when trying the send() action with URL: %s",
633 UpnpGetErrorMessage( res
), url
);
635 ixmlDocument_free( response
);
644 free( StartingIndex
);
645 free( RequestedCount
);
646 free( SortCriteria
);
650 ixmlDocument_free( action
);
654 void MediaServer::fetchContents()
656 Container
* root
= new Container( 0, "0", getFriendlyName() );
657 _fetchContents( root
);
660 _contents
->setInputItem( _inputItem
);
662 _buildPlaylist( _contents
, NULL
);
665 bool MediaServer::_fetchContents( Container
* parent
)
670 "%s:%d: parent==NULL", __FILE__
, __LINE__
);
674 IXML_Document
* response
= _browseAction( parent
->getObjectID(),
675 "BrowseDirectChildren",
680 "%s:%d: ERROR! No response from browse() action",
681 __FILE__
, __LINE__
);
685 IXML_Document
* result
= parseBrowseResult( response
);
686 ixmlDocument_free( response
);
691 "%s:%d: ERROR! browse() response parsing failed",
692 __FILE__
, __LINE__
);
696 IXML_NodeList
* containerNodeList
=
697 ixmlDocument_getElementsByTagName( result
, "container" );
699 if ( containerNodeList
)
701 for ( unsigned int i
= 0;
702 i
< ixmlNodeList_length( containerNodeList
); i
++ )
704 IXML_Element
* containerElement
=
705 ( IXML_Element
* )ixmlNodeList_item( containerNodeList
, i
);
707 const char* objectID
= ixmlElement_getAttribute( containerElement
,
712 const char* childCountStr
=
713 ixmlElement_getAttribute( containerElement
, "childCount" );
715 if ( !childCountStr
)
718 int childCount
= atoi( childCountStr
);
719 const char* title
= xml_getChildElementValue( containerElement
,
725 const char* resource
= xml_getChildElementValue( containerElement
,
728 if ( resource
&& childCount
< 1 )
730 Item
* item
= new Item( parent
, objectID
, title
, resource
);
731 parent
->addItem( item
);
736 Container
* container
= new Container( parent
, objectID
, title
);
737 parent
->addContainer( container
);
739 if ( childCount
> 0 )
740 _fetchContents( container
);
743 ixmlNodeList_free( containerNodeList
);
746 IXML_NodeList
* itemNodeList
= ixmlDocument_getElementsByTagName( result
,
750 for ( unsigned int i
= 0; i
< ixmlNodeList_length( itemNodeList
); i
++ )
752 IXML_Element
* itemElement
=
753 ( IXML_Element
* )ixmlNodeList_item( itemNodeList
, i
);
755 const char* objectID
=
756 ixmlElement_getAttribute( itemElement
, "id" );
762 xml_getChildElementValue( itemElement
, "dc:title" );
767 const char* resource
=
768 xml_getChildElementValue( itemElement
, "res" );
773 Item
* item
= new Item( parent
, objectID
, title
, resource
);
774 parent
->addItem( item
);
776 ixmlNodeList_free( itemNodeList
);
779 ixmlDocument_free( result
);
783 void MediaServer::_buildPlaylist( Container
* parent
, input_item_node_t
*p_input_node
)
785 bool send
= p_input_node
== NULL
;
787 p_input_node
= input_item_node_Create( parent
->getInputItem() );
789 for ( unsigned int i
= 0; i
< parent
->getNumContainers(); i
++ )
791 Container
* container
= parent
->getContainer( i
);
793 input_item_t
* p_input_item
= input_item_New( _p_sd
, "vlc://nop", parent
->getTitle() );
794 input_item_node_t
*p_new_node
=
795 input_item_node_AppendItem( p_input_node
, p_input_item
);
797 container
->setInputItem( p_input_item
);
798 _buildPlaylist( container
, p_new_node
);
801 for ( unsigned int i
= 0; i
< parent
->getNumItems(); i
++ )
803 Item
* item
= parent
->getItem( i
);
805 input_item_t
* p_input_item
= input_item_New( _p_sd
,
808 assert( p_input_item
);
809 input_item_node_AppendItem( p_input_node
, p_input_item
);
810 item
->setInputItem( p_input_item
);
814 input_item_node_PostAndDelete( p_input_node
);
817 void MediaServer::setInputItem( input_item_t
* p_input_item
)
819 if(_inputItem
== p_input_item
)
823 vlc_gc_decref( _inputItem
);
825 vlc_gc_incref( p_input_item
);
826 _inputItem
= p_input_item
;
829 bool MediaServer::compareSID( const char* sid
)
831 return ( strncmp( _subscriptionID
, sid
, sizeof( Upnp_SID
) ) == 0 );
835 // MediaServerList...
837 MediaServerList::MediaServerList( services_discovery_t
* p_sd
)
842 MediaServerList::~MediaServerList()
844 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
850 bool MediaServerList::addServer( MediaServer
* s
)
852 input_item_t
* p_input_item
= NULL
;
853 if ( getServer( s
->getUDN() ) != 0 ) return false;
855 msg_Dbg( _p_sd
, "Adding server '%s'",
856 s
->getFriendlyName() );
858 services_discovery_t
* p_sd
= _p_sd
;
860 p_input_item
= input_item_New( p_sd
, "vlc://nop", s
->getFriendlyName() );
861 s
->setInputItem( p_input_item
);
863 services_discovery_AddItem( p_sd
, p_input_item
, NULL
);
865 _list
.push_back( s
);
870 MediaServer
* MediaServerList::getServer( const char* UDN
)
872 MediaServer
* result
= 0;
874 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
876 if( strcmp( UDN
, _list
[i
]->getUDN() ) == 0 )
886 MediaServer
* MediaServerList::getServerBySID( const char* sid
)
888 MediaServer
* server
= 0;
890 for ( unsigned int i
= 0; i
< _list
.size(); i
++ )
892 if ( _list
[i
]->compareSID( sid
) )
902 void MediaServerList::removeServer( const char* UDN
)
904 MediaServer
* server
= getServer( UDN
);
905 if ( !server
) return;
908 "Removing server '%s'", server
->getFriendlyName() );
910 std::vector
<MediaServer
*>::iterator it
;
911 for ( it
= _list
.begin(); it
!= _list
.end(); it
++ )
925 Item::Item( Container
* parent
, const char* objectID
, const char* title
, const char* resource
)
929 _objectID
= objectID
;
931 _resource
= resource
;
939 vlc_gc_decref( _inputItem
);
942 const char* Item::getObjectID() const
944 return _objectID
.c_str();
947 const char* Item::getTitle() const
949 return _title
.c_str();
952 const char* Item::getResource() const
954 return _resource
.c_str();
957 void Item::setInputItem( input_item_t
* p_input_item
)
959 if(_inputItem
== p_input_item
)
963 vlc_gc_decref( _inputItem
);
965 vlc_gc_incref( p_input_item
);
966 _inputItem
= p_input_item
;
969 input_item_t
* Item::getInputItem() const
977 Container::Container( Container
* parent
,
978 const char* objectID
,
983 _objectID
= objectID
;
989 Container::~Container()
991 for ( unsigned int i
= 0; i
< _containers
.size(); i
++ )
993 delete _containers
[i
];
996 for ( unsigned int i
= 0; i
< _items
.size(); i
++ )
1002 vlc_gc_decref( _inputItem
);
1005 void Container::addItem( Item
* item
)
1007 _items
.push_back( item
);
1010 void Container::addContainer( Container
* container
)
1012 _containers
.push_back( container
);
1015 const char* Container::getObjectID() const
1017 return _objectID
.c_str();
1020 const char* Container::getTitle() const
1022 return _title
.c_str();
1025 unsigned int Container::getNumItems() const
1027 return _items
.size();
1030 unsigned int Container::getNumContainers() const
1032 return _containers
.size();
1035 Item
* Container::getItem( unsigned int i
) const
1037 if ( i
< _items
.size() ) return _items
[i
];
1041 Container
* Container::getContainer( unsigned int i
) const
1043 if ( i
< _containers
.size() ) return _containers
[i
];
1047 Container
* Container::getParent()
1052 void Container::setInputItem( input_item_t
* p_input_item
)
1054 if(_inputItem
== p_input_item
)
1058 vlc_gc_decref( _inputItem
);
1060 vlc_gc_incref( p_input_item
);
1061 _inputItem
= p_input_item
;
1064 input_item_t
* Container::getInputItem() const