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>
10 * Hugo Beauzée-Luyssen <hugo@beauzee.fr>
12 * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
31 #include <vlc_access.h>
32 #include <vlc_plugin.h>
33 #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";
49 struct services_discovery_sys_t
51 UpnpInstanceWrapper
* p_upnp
;
52 SD::MediaServerList
* p_server_list
;
57 UpnpInstanceWrapper
* p_upnp
;
58 Access::MediaServer
* p_server
;
61 UpnpInstanceWrapper
* UpnpInstanceWrapper::s_instance
;
62 vlc_mutex_t
UpnpInstanceWrapper::s_lock
= VLC_STATIC_MUTEX
;
65 * VLC callback prototypes
69 static int Open( vlc_object_t
* );
70 static void Close( vlc_object_t
* );
75 static int Open( vlc_object_t
* );
76 static void Close( vlc_object_t
* );
79 VLC_SD_PROBE_HELPER( "upnp", "Universal Plug'n'Play", SD_CAT_LAN
)
85 set_shortname( "UPnP" );
86 set_description( N_( "Universal Plug'n'Play" ) );
87 set_category( CAT_PLAYLIST
);
88 set_subcategory( SUBCAT_PLAYLIST_SD
);
89 set_capability( "services_discovery", 0 );
90 set_callbacks( SD::Open
, SD::Close
);
93 set_category( CAT_INPUT
)
94 set_subcategory( SUBCAT_INPUT_ACCESS
)
95 set_callbacks( Access::Open
, Access::Close
)
96 set_capability( "access", 0 )
98 VLC_SD_PROBE_SUBMODULE
103 * Returns the value of a child element, or NULL on error
105 const char* xml_getChildElementValue( IXML_Element
* p_parent
,
106 const char* psz_tag_name
)
109 assert( psz_tag_name
);
111 IXML_NodeList
* p_node_list
;
112 p_node_list
= ixmlElement_getElementsByTagName( p_parent
, psz_tag_name
);
113 if ( !p_node_list
) return NULL
;
115 IXML_Node
* p_element
= ixmlNodeList_item( p_node_list
, 0 );
116 ixmlNodeList_free( p_node_list
);
117 if ( !p_element
) return NULL
;
119 IXML_Node
* p_text_node
= ixmlNode_getFirstChild( p_element
);
120 if ( !p_text_node
) return NULL
;
122 return ixmlNode_getNodeValue( p_text_node
);
126 * Extracts the result document from a SOAP response
128 IXML_Document
* parseBrowseResult( IXML_Document
* p_doc
)
132 // ixml*_getElementsByTagName will ultimately only case the pointer to a Node
133 // pointer, and pass it to a private function. Don't bother have a IXML_Document
134 // version of getChildElementValue
135 const char* psz_raw_didl
= xml_getChildElementValue( (IXML_Element
*)p_doc
, "Result" );
140 /* First, try parsing the buffer as is */
141 IXML_Document
* p_result_doc
= ixmlParseBuffer( psz_raw_didl
);
142 if( !p_result_doc
) {
143 /* Missing namespaces confuse the ixml parser. This is a very ugly
144 * hack but it is needeed until devices start sending valid XML.
148 * The DIDL document is extracted from the Result tag, then wrapped into
149 * a valid XML header and a new root tag which contains missing namespace
150 * definitions so the ixml parser understands it.
152 * If you know of a better workaround, please oh please fix it */
153 const char* psz_xml_result_fmt
= "<?xml version=\"1.0\" ?>"
154 "<Result xmlns:sec=\"urn:samsung:metadata:2009\">%s</Result>";
156 char* psz_xml_result_string
= NULL
;
157 if( -1 == asprintf( &psz_xml_result_string
,
162 p_result_doc
= ixmlParseBuffer( psz_xml_result_string
);
163 free( psz_xml_result_string
);
169 IXML_NodeList
*p_elems
= ixmlDocument_getElementsByTagName( p_result_doc
,
172 IXML_Node
*p_node
= ixmlNodeList_item( p_elems
, 0 );
173 ixmlNodeList_free( p_elems
);
175 return (IXML_Document
*)p_node
;
182 * Initializes UPNP instance.
184 static int Open( vlc_object_t
*p_this
)
186 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
187 services_discovery_sys_t
*p_sys
= ( services_discovery_sys_t
* )
188 calloc( 1, sizeof( services_discovery_sys_t
) );
190 if( !( p_sd
->p_sys
= p_sys
) )
193 p_sys
->p_server_list
= new(std::nothrow
) SD::MediaServerList( p_sd
);
194 if ( unlikely( p_sys
->p_server_list
== NULL
) )
199 p_sys
->p_upnp
= UpnpInstanceWrapper::get( p_this
, SD::MediaServerList::Callback
, p_sys
->p_server_list
);
200 if ( !p_sys
->p_upnp
)
206 /* Search for media servers */
207 int i_res
= UpnpSearchAsync( p_sys
->p_upnp
->handle(), 5,
208 MEDIA_SERVER_DEVICE_TYPE
, p_sys
->p_upnp
);
209 if( i_res
!= UPNP_E_SUCCESS
)
211 msg_Err( p_sd
, "Error sending search request: %s", UpnpGetErrorMessage( i_res
) );
220 * Releases resources.
222 static void Close( vlc_object_t
*p_this
)
224 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
225 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
228 p_sys
->p_upnp
->release( true );
229 delete p_sys
->p_server_list
;
233 MediaServerDesc::MediaServerDesc(const std::string
& udn
, const std::string
& fName
, const std::string
& loc
)
235 , friendlyName( fName
)
241 MediaServerDesc::~MediaServerDesc()
244 vlc_gc_decref( inputItem
);
248 * MediaServerList class
250 MediaServerList::MediaServerList( services_discovery_t
* p_sd
)
253 vlc_mutex_init( &lock_
);
256 MediaServerList::~MediaServerList()
258 vlc_delete_all(list_
);
259 vlc_mutex_destroy( &lock_
);
262 bool MediaServerList::addServer( MediaServerDesc
* desc
)
264 vlc_mutex_locker
lock( &lock_
);
265 input_item_t
* p_input_item
= NULL
;
266 if ( getServer( desc
->UDN
) )
269 msg_Dbg( p_sd_
, "Adding server '%s' with uuid '%s'", desc
->friendlyName
.c_str(), desc
->UDN
.c_str() );
272 if( asprintf(&psz_mrl
, "upnp://%s?ObjectID=0", desc
->location
.c_str() ) < 0 )
275 p_input_item
= input_item_NewWithTypeExt( psz_mrl
, desc
->friendlyName
.c_str(), 0,
276 NULL
, 0, -1, ITEM_TYPE_NODE
, 1);
280 desc
->inputItem
= p_input_item
;
281 input_item_SetDescription( p_input_item
, desc
->UDN
.c_str() );
282 services_discovery_AddItem( p_sd_
, p_input_item
, NULL
);
283 list_
.push_back( desc
);
287 MediaServerDesc
* MediaServerList::getServer( const std::string
& udn
)
289 std::vector
<MediaServerDesc
*>::const_iterator it
= list_
.begin();
290 std::vector
<MediaServerDesc
*>::const_iterator ite
= list_
.end();
292 for ( ; it
!= ite
; ++it
)
294 if( udn
== (*it
)->UDN
)
302 void MediaServerList::parseNewServer( IXML_Document
*doc
, const std::string
&location
)
306 msg_Err( p_sd_
, "Null IXML_Document" );
310 if ( location
.empty() )
312 msg_Err( p_sd_
, "Empty location" );
316 const char* psz_base_url
= location
.c_str();
318 /* Try to extract baseURL */
319 IXML_NodeList
* p_url_list
= ixmlDocument_getElementsByTagName( doc
, "URLBase" );
322 if ( IXML_Node
* p_url_node
= ixmlNodeList_item( p_url_list
, 0 ) )
324 IXML_Node
* p_text_node
= ixmlNode_getFirstChild( p_url_node
);
326 psz_base_url
= ixmlNode_getNodeValue( p_text_node
);
328 ixmlNodeList_free( p_url_list
);
332 IXML_NodeList
* p_device_list
= ixmlDocument_getElementsByTagName( doc
, "device" );
334 if ( !p_device_list
)
336 for ( unsigned int i
= 0; i
< ixmlNodeList_length( p_device_list
); i
++ )
338 IXML_Element
* p_device_element
= ( IXML_Element
* ) ixmlNodeList_item( p_device_list
, i
);
340 if( !p_device_element
)
343 const char* psz_device_type
= xml_getChildElementValue( p_device_element
, "deviceType" );
345 if ( !psz_device_type
)
347 msg_Warn( p_sd_
, "No deviceType found!" );
351 if ( strncmp( MEDIA_SERVER_DEVICE_TYPE
, psz_device_type
,
352 strlen( MEDIA_SERVER_DEVICE_TYPE
) - 1 ) )
355 const char* psz_udn
= xml_getChildElementValue( p_device_element
,
359 msg_Warn( p_sd_
, "No UDN!" );
363 /* Check if server is already added */
364 if ( p_sd_
->p_sys
->p_server_list
->getServer( psz_udn
) )
366 msg_Warn( p_sd_
, "Server with uuid '%s' already exists.", psz_udn
);
370 const char* psz_friendly_name
=
371 xml_getChildElementValue( p_device_element
,
374 if ( !psz_friendly_name
)
376 msg_Dbg( p_sd_
, "No friendlyName!" );
380 // We now have basic info, we need to get the content browsing url
381 // so the access module can browse without fetching the manifest again
383 /* Check for ContentDirectory service. */
384 IXML_NodeList
* p_service_list
= ixmlElement_getElementsByTagName( p_device_element
, "service" );
385 if ( !p_service_list
)
387 for ( unsigned int j
= 0; j
< ixmlNodeList_length( p_service_list
); j
++ )
389 IXML_Element
* p_service_element
= (IXML_Element
*)ixmlNodeList_item( p_service_list
, j
);
391 const char* psz_service_type
= xml_getChildElementValue( p_service_element
, "serviceType" );
392 if ( !psz_service_type
)
394 msg_Warn( p_sd_
, "No service type found." );
398 int k
= strlen( CONTENT_DIRECTORY_SERVICE_TYPE
) - 1;
399 if ( strncmp( CONTENT_DIRECTORY_SERVICE_TYPE
,
400 psz_service_type
, k
) )
403 const char* psz_control_url
= xml_getChildElementValue( p_service_element
,
405 if ( !psz_control_url
)
407 msg_Warn( p_sd_
, "No control url found." );
411 /* Try to browse content directory. */
412 char* psz_url
= ( char* ) malloc( strlen( psz_base_url
) + strlen( psz_control_url
) + 1 );
415 if ( UpnpResolveURL( psz_base_url
, psz_control_url
, psz_url
) == UPNP_E_SUCCESS
)
417 SD::MediaServerDesc
* p_server
= new(std::nothrow
) SD::MediaServerDesc( psz_udn
,
418 psz_friendly_name
, psz_url
);
420 if ( unlikely( !p_server
) )
423 if ( !addServer( p_server
) )
433 ixmlNodeList_free( p_service_list
);
435 ixmlNodeList_free( p_device_list
);
438 void MediaServerList::removeServer( const std::string
& udn
)
440 vlc_mutex_locker
lock( &lock_
);
442 MediaServerDesc
* p_server
= getServer( udn
);
446 msg_Dbg( p_sd_
, "Removing server '%s'", p_server
->friendlyName
.c_str() );
448 assert(p_server
->inputItem
);
449 services_discovery_RemoveItem( p_sd_
, p_server
->inputItem
);
451 std::vector
<MediaServerDesc
*>::iterator it
= std::find(list_
.begin(), list_
.end(), p_server
);
452 if (it
!= list_
.end())
460 * Handles servers listing UPnP events
462 int MediaServerList::Callback( Upnp_EventType event_type
, void* p_event
, void* p_user_data
)
464 MediaServerList
* self
= static_cast<MediaServerList
*>( p_user_data
);
465 services_discovery_t
* p_sd
= self
->p_sd_
;
469 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
470 case UPNP_DISCOVERY_SEARCH_RESULT
:
472 struct Upnp_Discovery
* p_discovery
= ( struct Upnp_Discovery
* )p_event
;
474 IXML_Document
*p_description_doc
= NULL
;
477 i_res
= UpnpDownloadXmlDoc( p_discovery
->Location
, &p_description_doc
);
478 if ( i_res
!= UPNP_E_SUCCESS
)
480 msg_Warn( p_sd
, "Could not download device description! "
481 "Fetching data from %s failed: %s",
482 p_discovery
->Location
, UpnpGetErrorMessage( i_res
) );
485 self
->parseNewServer( p_description_doc
, p_discovery
->Location
);
486 ixmlDocument_free( p_description_doc
);
490 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
:
492 struct Upnp_Discovery
* p_discovery
= ( struct Upnp_Discovery
* )p_event
;
494 self
->removeServer( p_discovery
->DeviceId
);
498 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
499 msg_Warn( p_sd
, "subscription complete" );
502 case UPNP_DISCOVERY_SEARCH_TIMEOUT
:
503 msg_Warn( p_sd
, "search timeout" );
506 case UPNP_EVENT_RECEIVED
:
507 case UPNP_EVENT_AUTORENEWAL_FAILED
:
508 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
:
509 // Those are for the access part
513 msg_Err( p_sd
, "Unhandled event, please report ( type=%d )", event_type
);
517 return UPNP_E_SUCCESS
;
525 MediaServer::MediaServer(const char *psz_url
, access_t
*p_access
)
527 , access_( p_access
)
528 , xmlDocument_( NULL
)
529 , containerNodeList_( NULL
)
530 , containerNodeIndex_( 0 )
531 , itemNodeList_( NULL
)
532 , itemNodeIndex_( 0 )
536 MediaServer::~MediaServer()
538 ixmlNodeList_free( containerNodeList_
);
539 ixmlNodeList_free( itemNodeList_
);
540 ixmlDocument_free( xmlDocument_
);
543 input_item_t
* MediaServer::newItem(const char *objectID
, const char *title
)
546 vlc_UrlParse( &url
, url_
.c_str() );
549 if (asprintf( &psz_url
, "upnp://%s://%s:%u%s?ObjectID=%s", url
.psz_protocol
,
550 url
.psz_host
, url
.i_port
? url
.i_port
: 80, url
.psz_path
, objectID
) < 0 )
552 vlc_UrlClean( &url
);
555 vlc_UrlClean( &url
);
557 input_item_t
* p_item
= input_item_NewWithTypeExt( psz_url
, title
, 0, NULL
,
558 0, -1, ITEM_TYPE_DIRECTORY
, 1 );
563 input_item_t
* MediaServer::newItem(const char* title
, const char*, const char*,
564 mtime_t duration
, const char* psz_url
)
566 return input_item_NewWithTypeExt( psz_url
, title
, 0, NULL
, 0,
567 duration
, ITEM_TYPE_FILE
, 1 );
571 IXML_Document
* MediaServer::_browseAction( const char* psz_object_id_
,
572 const char* psz_browser_flag_
,
573 const char* psz_filter_
,
574 const char* psz_requested_count_
,
575 const char* psz_sort_criteria_
)
577 IXML_Document
* p_action
= NULL
;
578 IXML_Document
* p_response
= NULL
;
579 const char* psz_url
= url_
.c_str();
583 msg_Dbg( access_
, "No subscription url set!" );
589 i_res
= UpnpAddToAction( &p_action
, "Browse",
590 CONTENT_DIRECTORY_SERVICE_TYPE
, "ObjectID", psz_object_id_
);
592 if ( i_res
!= UPNP_E_SUCCESS
)
594 msg_Dbg( access_
, "AddToAction 'ObjectID' failed: %s",
595 UpnpGetErrorMessage( i_res
) );
596 goto browseActionCleanup
;
599 i_res
= UpnpAddToAction( &p_action
, "Browse",
600 CONTENT_DIRECTORY_SERVICE_TYPE
, "StartingIndex", "0" );
601 if ( i_res
!= UPNP_E_SUCCESS
)
603 msg_Dbg( access_
, "AddToAction 'StartingIndex' failed: %s",
604 UpnpGetErrorMessage( i_res
) );
605 goto browseActionCleanup
;
608 i_res
= UpnpAddToAction( &p_action
, "Browse",
609 CONTENT_DIRECTORY_SERVICE_TYPE
, "BrowseFlag", psz_browser_flag_
);
611 if ( i_res
!= UPNP_E_SUCCESS
)
613 msg_Dbg( access_
, "AddToAction 'BrowseFlag' failed: %s",
614 UpnpGetErrorMessage( i_res
) );
615 goto browseActionCleanup
;
618 i_res
= UpnpAddToAction( &p_action
, "Browse",
619 CONTENT_DIRECTORY_SERVICE_TYPE
, "Filter", psz_filter_
);
621 if ( i_res
!= UPNP_E_SUCCESS
)
623 msg_Dbg( access_
, "AddToAction 'Filter' failed: %s",
624 UpnpGetErrorMessage( i_res
) );
625 goto browseActionCleanup
;
628 i_res
= UpnpAddToAction( &p_action
, "Browse",
629 CONTENT_DIRECTORY_SERVICE_TYPE
, "RequestedCount", psz_requested_count_
);
631 if ( i_res
!= UPNP_E_SUCCESS
)
633 msg_Dbg( access_
, "AddToAction 'RequestedCount' failed: %s",
634 UpnpGetErrorMessage( i_res
) );
635 goto browseActionCleanup
;
638 i_res
= UpnpAddToAction( &p_action
, "Browse",
639 CONTENT_DIRECTORY_SERVICE_TYPE
, "SortCriteria", psz_sort_criteria_
);
641 if ( i_res
!= UPNP_E_SUCCESS
)
643 msg_Dbg( access_
, "AddToAction 'SortCriteria' failed: %s",
644 UpnpGetErrorMessage( i_res
) );
645 goto browseActionCleanup
;
648 i_res
= UpnpSendAction( access_
->p_sys
->p_upnp
->handle(),
650 CONTENT_DIRECTORY_SERVICE_TYPE
,
651 NULL
, /* ignored in SDK, must be NULL */
655 if ( i_res
!= UPNP_E_SUCCESS
)
657 msg_Err( access_
, "%s when trying the send() action with URL: %s",
658 UpnpGetErrorMessage( i_res
), psz_url
);
660 ixmlDocument_free( p_response
);
665 ixmlDocument_free( p_action
);
670 * Fetches and parses the UPNP response
672 void MediaServer::fetchContents()
674 const char* objectID
= "";
676 vlc_UrlParse( &url
, access_
->psz_location
);
678 if ( url
.psz_option
&& !strncmp( url
.psz_option
, "ObjectID=", strlen( "ObjectID=" ) ) )
680 objectID
= &url
.psz_option
[strlen( "ObjectID=" )];
683 IXML_Document
* p_response
= _browseAction( objectID
,
684 "BrowseDirectChildren",
685 "id,dc:title,res," /* Filter */
686 "sec:CaptionInfo,sec:CaptionInfoEx,"
688 "0", /* RequestedCount */
689 "" /* SortCriteria */
691 vlc_UrlClean( &url
);
694 msg_Err( access_
, "No response from browse() action" );
698 xmlDocument_
= parseBrowseResult( p_response
);
700 ixmlDocument_free( p_response
);
704 msg_Err( access_
, "browse() response parsing failed" );
709 msg_Dbg( access_
, "Got DIDL document: %s", ixmlPrintDocument( xmlDocument_
) );
712 containerNodeList_
= ixmlDocument_getElementsByTagName( xmlDocument_
, "container" );
713 itemNodeList_
= ixmlDocument_getElementsByTagName( xmlDocument_
, "item" );
716 input_item_t
* MediaServer::getNextItem()
718 input_item_t
*p_item
= NULL
;
727 if ( containerNodeList_
)
729 for ( ; !p_item
&& containerNodeIndex_
< ixmlNodeList_length( containerNodeList_
)
730 ; containerNodeIndex_
++ )
732 IXML_Element
* containerElement
= (IXML_Element
*)ixmlNodeList_item( containerNodeList_
,
733 containerNodeIndex_
);
735 const char* objectID
= ixmlElement_getAttribute( containerElement
,
740 const char* title
= xml_getChildElementValue( containerElement
,
744 p_item
= newItem(objectID
, title
);
750 for ( ; !p_item
&& itemNodeIndex_
< ixmlNodeList_length( itemNodeList_
)
753 IXML_Element
* itemElement
=
754 ( IXML_Element
* )ixmlNodeList_item( itemNodeList_
,
757 const char* objectID
=
758 ixmlElement_getAttribute( itemElement
, "id" );
764 xml_getChildElementValue( itemElement
, "dc:title" );
769 const char* psz_subtitles
= xml_getChildElementValue( itemElement
,
772 if ( !psz_subtitles
)
773 psz_subtitles
= xml_getChildElementValue( itemElement
,
774 "sec:CaptionInfoEx" );
776 if ( !psz_subtitles
)
777 psz_subtitles
= xml_getChildElementValue( itemElement
,
780 /* Try to extract all resources in DIDL */
781 IXML_NodeList
* p_resource_list
= ixmlDocument_getElementsByTagName( (IXML_Document
*) itemElement
, "res" );
782 if ( p_resource_list
&& ixmlNodeList_length( p_resource_list
) > 0 )
784 mtime_t i_duration
= -1;
785 int i_hours
, i_minutes
, i_seconds
;
786 IXML_Element
* p_resource
= ( IXML_Element
* ) ixmlNodeList_item( p_resource_list
, 0 );
787 const char* psz_resource_url
= xml_getChildElementValue( p_resource
, "res" );
788 if( !psz_resource_url
)
790 const char* psz_duration
= ixmlElement_getAttribute( p_resource
, "duration" );
794 if( sscanf( psz_duration
, "%d:%02d:%02d",
795 &i_hours
, &i_minutes
, &i_seconds
) )
796 i_duration
= INT64_C(1000000) * ( i_hours
*3600 +
801 p_item
= newItem( title
, objectID
, psz_subtitles
, i_duration
,
804 ixmlNodeList_free( p_resource_list
);
811 static input_item_t
* ReadDirectory( access_t
*p_access
)
813 return p_access
->p_sys
->p_server
->getNextItem();
816 static int Open( vlc_object_t
*p_this
)
818 access_t
* p_access
= (access_t
*)p_this
;
819 access_sys_t
* p_sys
= new(std::nothrow
) access_sys_t
;
820 if ( unlikely( !p_sys
) )
823 p_access
->p_sys
= p_sys
;
824 p_sys
->p_server
= new(std::nothrow
) MediaServer( p_access
->psz_location
,
826 if ( !p_sys
->p_server
)
831 p_sys
->p_upnp
= UpnpInstanceWrapper::get( p_this
, NULL
, NULL
);
832 if ( !p_sys
->p_upnp
)
834 delete p_sys
->p_server
;
839 p_access
->pf_readdir
= ReadDirectory
;
840 p_access
->pf_control
= access_vaDirectoryControlHelper
;
841 p_access
->info
.b_dir_sorted
= true;
842 p_access
->info
.b_dir_can_loop
= true;
847 static void Close( vlc_object_t
* p_this
)
849 access_t
* p_access
= (access_t
*)p_this
;
850 p_access
->p_sys
->p_upnp
->release( false );
851 delete p_access
->p_sys
->p_server
;
852 delete p_access
->p_sys
;
857 UpnpInstanceWrapper::UpnpInstanceWrapper()
865 UpnpInstanceWrapper::~UpnpInstanceWrapper()
867 UpnpUnRegisterClient( handle_
);
871 UpnpInstanceWrapper
*UpnpInstanceWrapper::get(vlc_object_t
*p_obj
, Upnp_FunPtr callback
, SD::MediaServerList
*opaque
)
873 vlc_mutex_locker
lock( &s_lock
);
874 if ( s_instance
== NULL
)
876 UpnpInstanceWrapper
* instance
= new(std::nothrow
) UpnpInstanceWrapper
;
877 if ( unlikely( !instance
) )
880 #ifdef UPNP_ENABLE_IPV6
881 char* psz_miface
= var_InheritString( p_obj
, "miface" );
882 msg_Info( p_obj
, "Initializing libupnp on '%s' interface", psz_miface
);
883 int i_res
= UpnpInit2( psz_miface
, 0 );
886 /* If UpnpInit2 isnt available, initialize on first IPv4-capable interface */
887 int i_res
= UpnpInit( 0, 0 );
889 if( i_res
!= UPNP_E_SUCCESS
)
891 msg_Err( p_obj
, "Initialization failed: %s", UpnpGetErrorMessage( i_res
) );
896 ixmlRelaxParser( 1 );
898 /* Register a control point */
899 i_res
= UpnpRegisterClient( Callback
, instance
, &instance
->handle_
);
900 if( i_res
!= UPNP_E_SUCCESS
)
902 msg_Err( p_obj
, "Client registration failed: %s", UpnpGetErrorMessage( i_res
) );
907 /* libupnp does not treat a maximum content length of 0 as unlimited
908 * until 64dedf (~ pupnp v1.6.7) and provides no sane way to discriminate
909 * between versions */
910 if( (i_res
= UpnpSetMaxContentLength( INT_MAX
)) != UPNP_E_SUCCESS
)
912 msg_Err( p_obj
, "Failed to set maximum content length: %s",
913 UpnpGetErrorMessage( i_res
));
917 s_instance
= instance
;
919 s_instance
->refcount_
++;
920 // This assumes a single UPNP SD instance
921 if (callback
&& opaque
)
923 assert(!s_instance
->callback_
&& !s_instance
->opaque_
);
924 s_instance
->opaque_
= opaque
;
925 s_instance
->callback_
= callback
;
930 void UpnpInstanceWrapper::release(bool isSd
)
932 vlc_mutex_locker
lock( &s_lock
);
938 if (--s_instance
->refcount_
== 0)
945 UpnpClient_Handle
UpnpInstanceWrapper::handle() const
950 int UpnpInstanceWrapper::Callback(Upnp_EventType event_type
, void *p_event
, void *p_user_data
)
952 UpnpInstanceWrapper
* self
= static_cast<UpnpInstanceWrapper
*>( p_user_data
);
953 vlc_mutex_locker
lock( &self
->s_lock
);
954 if ( !self
->callback_
)
956 self
->callback_( event_type
, p_event
, self
->opaque_
);