[vlc-devel] [PATCH] upnp: refactor out UpnpInstanceWrapper

Shaleen Jain shaleen at jain.sh
Fri Jun 22 16:03:15 CEST 2018


Refactor out the UpnpInstanceWrapper singleton
class so that it is decoupled from any specific module
and can be used by other modules without them all
having to be in a single file, by introducing a
Listener interface that can be used by
modules to get callbacks from libupnp.
---
 v2: add missing copyright headers
---
 modules/services_discovery/Makefile.am      |   4 +-
 modules/services_discovery/upnp-wrapper.cpp | 158 +++++++
 modules/services_discovery/upnp-wrapper.hpp | 349 ++++++++++++++
 modules/services_discovery/upnp.cpp         | 496 ++------------------
 modules/services_discovery/upnp.hpp         |  62 +--
 5 files changed, 562 insertions(+), 507 deletions(-)
 create mode 100644 modules/services_discovery/upnp-wrapper.cpp
 create mode 100644 modules/services_discovery/upnp-wrapper.hpp

diff --git a/modules/services_discovery/Makefile.am b/modules/services_discovery/Makefile.am
index 41c7871a29..f63df23b32 100644
--- a/modules/services_discovery/Makefile.am
+++ b/modules/services_discovery/Makefile.am
@@ -26,7 +26,9 @@ libmtp_plugin_la_LIBADD = $(MTP_LIBS)
 EXTRA_LTLIBRARIES += libmtp_plugin.la
 sd_LTLIBRARIES += $(LTLIBmtp)
 
-libupnp_plugin_la_SOURCES = services_discovery/upnp.cpp services_discovery/upnp.hpp
+libupnp_plugin_la_SOURCES = services_discovery/upnp.cpp services_discovery/upnp.hpp \
+			    services_discovery/upnp-wrapper.hpp \
+			    services_discovery/upnp-wrapper.cpp
 libupnp_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(UPNP_CFLAGS)
 libupnp_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(sddir)'
 libupnp_plugin_la_LIBADD = $(UPNP_LIBS)
diff --git a/modules/services_discovery/upnp-wrapper.cpp b/modules/services_discovery/upnp-wrapper.cpp
new file mode 100644
index 0000000000..e542c22837
--- /dev/null
+++ b/modules/services_discovery/upnp-wrapper.cpp
@@ -0,0 +1,158 @@
+/*****************************************************************************
+ * upnp-wrapper.cpp :  UPnP Instance Wrapper class
+ *****************************************************************************
+ * Copyright © 2004-2018 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
+ *          Christian Henz <henz # c-lab.de>
+ *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
+ *          Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *          Shaleen Jain <shaleen at jain.sh>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "upnp-wrapper.hpp"
+
+UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
+UpnpInstanceWrapper::Listeners UpnpInstanceWrapper::s_listeners;
+vlc_mutex_t UpnpInstanceWrapper::s_lock = VLC_STATIC_MUTEX;
+
+
+UpnpInstanceWrapper::UpnpInstanceWrapper()
+    : m_handle( -1 )
+    , m_refcount( 0 )
+{
+}
+
+UpnpInstanceWrapper::~UpnpInstanceWrapper()
+{
+    UpnpUnRegisterClient( m_handle );
+    UpnpFinish();
+}
+
+
+UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj)
+{
+    vlc_mutex_locker lock( &s_lock );
+    if ( s_instance == NULL )
+    {
+        UpnpInstanceWrapper* instance = new(std::nothrow) UpnpInstanceWrapper;
+        if ( unlikely( !instance ) )
+        {
+            return NULL;
+        }
+
+    #ifdef UPNP_ENABLE_IPV6
+        char* psz_miface = var_InheritString( p_obj, "miface" );
+        if (psz_miface == NULL)
+            psz_miface = getPreferedAdapter();
+        msg_Info( p_obj, "Initializing libupnp on '%s' interface", psz_miface ? psz_miface : "default" );
+        int i_res = UpnpInit2( psz_miface, 0 );
+        free( psz_miface );
+    #else
+        /* If UpnpInit2 isnt available, initialize on first IPv4-capable interface */
+        char *psz_hostip = getIpv4ForMulticast();
+        int i_res = UpnpInit( psz_hostip, 0 );
+        free(psz_hostip);
+    #endif /* UPNP_ENABLE_IPV6 */
+        if( i_res != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Initialization failed: %s", UpnpGetErrorMessage( i_res ) );
+            delete instance;
+            return NULL;
+        }
+
+        ixmlRelaxParser( 1 );
+
+        /* Register a control point */
+        i_res = UpnpRegisterClient( Callback, instance, &instance->m_handle );
+        if( i_res != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Client registration failed: %s", UpnpGetErrorMessage( i_res ) );
+            delete instance;
+            return NULL;
+        }
+
+        /* libupnp does not treat a maximum content length of 0 as unlimited
+         * until 64dedf (~ pupnp v1.6.7) and provides no sane way to discriminate
+         * between versions */
+        if( (i_res = UpnpSetMaxContentLength( INT_MAX )) != UPNP_E_SUCCESS )
+        {
+            msg_Err( p_obj, "Failed to set maximum content length: %s",
+                    UpnpGetErrorMessage( i_res ));
+            delete instance;
+            return NULL;
+        }
+        s_instance = instance;
+    }
+    s_instance->m_refcount++;
+    return s_instance;
+}
+
+void UpnpInstanceWrapper::release()
+{
+    UpnpInstanceWrapper *p_delete = NULL;
+    vlc_mutex_lock( &s_lock );
+    if (--s_instance->m_refcount == 0)
+    {
+        p_delete = s_instance;
+        s_instance = NULL;
+    }
+    vlc_mutex_unlock( &s_lock );
+    delete p_delete;
+}
+
+UpnpClient_Handle UpnpInstanceWrapper::handle() const
+{
+    return m_handle;
+}
+
+int UpnpInstanceWrapper::Callback(Upnp_EventType event_type, UpnpEventPtr p_event, void *p_user_data)
+{
+    for (Listeners::iterator iter = s_listeners.begin(); iter != s_listeners.end(); ++iter)
+    {
+        (*iter)->onEvent(event_type, p_event, p_user_data);
+    }
+
+    return 0;
+}
+
+void UpnpInstanceWrapper::addListener(ListenerPtr listener)
+{
+    vlc_mutex_lock( &s_lock );
+    if ( std::find( s_listeners.begin(), s_listeners.end(), listener) != s_listeners.end() )
+    {
+        vlc_mutex_unlock( &s_lock );
+        return;
+    }
+    s_listeners.push_back( listener );
+    vlc_mutex_unlock( &s_lock );
+}
+
+void UpnpInstanceWrapper::removeListener(ListenerPtr listener)
+{
+    vlc_mutex_lock( &s_lock );
+    Listeners::iterator iter = std::find( s_listeners.begin(), s_listeners.end(), listener );
+    if ( iter == s_listeners.end() )
+    {
+        vlc_mutex_unlock( &s_lock );
+        return;
+    }
+
+    s_listeners.erase( iter );
+    vlc_mutex_unlock( &s_lock );
+}
diff --git a/modules/services_discovery/upnp-wrapper.hpp b/modules/services_discovery/upnp-wrapper.hpp
new file mode 100644
index 0000000000..af3e208dd3
--- /dev/null
+++ b/modules/services_discovery/upnp-wrapper.hpp
@@ -0,0 +1,349 @@
+/*****************************************************************************
+ * upnp-wrapper.cpp :  UPnP Instance Wrapper class header
+ *****************************************************************************
+ * Copyright © 2004-2018 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
+ *          Christian Henz <henz # c-lab.de>
+ *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
+ *          Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *          Shaleen Jain <shaleen at jain.sh>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <assert.h>
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include <vlc_common.h>
+
+#if UPNP_VERSION < 10800
+typedef void* UpnpEventPtr;
+#else
+typedef const void* UpnpEventPtr;
+#endif
+
+/**
+ * libUpnp allows only one instance per process, so we create a wrapper
+ * class around it that acts and behaves as a singleton. Letting us get
+ * multiple references to it but only ever having a single instance in memory.
+ * At the same time we let any module wishing to get a callback from the library
+ * to register a UpnpInstanceWrapper::Listener to get the Listener#onEvent()
+ * callback without having any hard dependencies.
+ */
+class UpnpInstanceWrapper
+{
+public:
+    class Listener
+    {
+	public:
+	    virtual ~Listener() {}
+	    virtual int onEvent( Upnp_EventType event_type,
+				 UpnpEventPtr p_event,
+                                 void* p_user_data ) = 0;
+    };
+
+private:
+    static UpnpInstanceWrapper* s_instance;
+    static vlc_mutex_t s_lock;
+    UpnpClient_Handle m_handle;
+    int m_refcount;
+    typedef std::shared_ptr<Listener> ListenerPtr;
+    typedef std::vector<ListenerPtr> Listeners;
+    static Listeners s_listeners;
+
+public:
+    // This increases the refcount before returning the instance
+    static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
+    void release();
+    UpnpClient_Handle handle() const;
+    void addListener(ListenerPtr listener);
+    void removeListener(ListenerPtr listener);
+
+private:
+    static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
+
+    UpnpInstanceWrapper();
+    ~UpnpInstanceWrapper();
+};
+
+// **************************
+// Helper functions
+// **************************
+
+#if UPNP_VERSION < 10623
+/*
+ * Compat functions and typedefs for libupnp prior to 1.8
+ */
+
+typedef Upnp_Discovery UpnpDiscovery;
+typedef Upnp_Action_Complete UpnpActionComplete;
+
+inline const char* UpnpDiscovery_get_Location_cstr( const UpnpDiscovery* p_discovery )
+{
+  return p_discovery->Location;
+}
+
+inline const char* UpnpDiscovery_get_DeviceID_cstr( const UpnpDiscovery* p_discovery )
+{
+  return p_discovery->DeviceId;
+}
+
+inline static IXML_Document* UpnpActionComplete_get_ActionResult( const UpnpActionComplete* p_result )
+{
+  return p_result->ActionResult;
+}
+#endif
+
+/*
+ * Returns the value of a child element, or NULL on error
+ */
+inline const char* xml_getChildElementValue( IXML_Element* p_parent,
+                                      const char*   psz_tag_name )
+{
+    assert( p_parent );
+    assert( psz_tag_name );
+
+    IXML_NodeList* p_node_list;
+    p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
+    if ( !p_node_list ) return NULL;
+
+    IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
+    ixmlNodeList_free( p_node_list );
+    if ( !p_element )   return NULL;
+
+    IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
+    if ( !p_text_node ) return NULL;
+
+    return ixmlNode_getNodeValue( p_text_node );
+}
+
+#ifdef _WIN32
+
+inline IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
+{
+    const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
+
+    IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
+    while (p_multicast != NULL)
+    {
+        if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
+            return p_multicast;
+        p_multicast = p_multicast->Next;
+    }
+    return NULL;
+}
+
+inline bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
+{
+    if ( p_adapter->OperStatus != IfOperStatusUp )
+        return false;
+    if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
+    {
+        IP_ADAPTER_ADDRESSES_XP* p_adapter_xp = reinterpret_cast<IP_ADAPTER_ADDRESSES_XP*>( p_adapter );
+        // On Windows Server 2003 and Windows XP, this member is zero if IPv4 is not available on the interface.
+        if (ipv6)
+            return p_adapter_xp->Ipv6IfIndex != 0;
+        return p_adapter_xp->IfIndex != 0;
+    }
+    IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
+    if (p_adapter_lh->FirstGatewayAddress == NULL)
+        return false;
+    if (ipv6)
+        return p_adapter_lh->Ipv6Enabled;
+    return p_adapter_lh->Ipv4Enabled;
+}
+
+inline IP_ADAPTER_ADDRESSES* ListAdapters()
+{
+    ULONG addrSize;
+    const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
+    IP_ADAPTER_ADDRESSES* addresses = NULL;
+    HRESULT hr;
+
+    /**
+     * https://msdn.microsoft.com/en-us/library/aa365915.aspx
+     *
+     * The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a
+     * 15KB working buffer pointed to by the AdapterAddresses parameter. On typical computers,
+     * this dramatically reduces the chances that the GetAdaptersAddresses function returns
+     * ERROR_BUFFER_OVERFLOW, which would require calling GetAdaptersAddresses function multiple
+     * times. The example code illustrates this method of use.
+     */
+    addrSize = 15 * 1024;
+    do
+    {
+        free(addresses);
+        addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
+        if (addresses == NULL)
+            return NULL;
+        hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
+    } while (hr == ERROR_BUFFER_OVERFLOW);
+    if (hr != NO_ERROR) {
+        free(addresses);
+        return NULL;
+    }
+    return addresses;
+}
+
+#ifdef UPNP_ENABLE_IPV6
+
+inline char* getPreferedAdapter()
+{
+    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
+
+    addresses = ListAdapters();
+    if (addresses == NULL)
+        return NULL;
+
+    /* find one with multicast capabilities */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable( p_adapter, true ))
+        {
+            /* make sure it supports 239.255.255.250 */
+            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
+            if (p_multicast != NULL)
+            {
+                char* res = FromWide( p_adapter->FriendlyName );
+                free( addresses );
+                return res;
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+    free(addresses);
+    return NULL;
+}
+
+#else
+
+inline char *getIpv4ForMulticast()
+{
+    IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
+    wchar_t psz_uri[32];
+    DWORD strSize;
+    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
+
+    addresses = ListAdapters();
+    if (addresses == NULL)
+        return NULL;
+
+    /* find one with multicast capabilities */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable( p_adapter, false ))
+        {
+            /* make sure it supports 239.255.255.250 */
+            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
+            if (p_multicast != NULL)
+            {
+                /* get an IPv4 address */
+                IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
+                while (p_unicast != NULL)
+                {
+                    strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+                    if( WSAAddressToString( p_unicast->Address.lpSockaddr,
+                                            p_unicast->Address.iSockaddrLength,
+                                            NULL, psz_uri, &strSize ) == 0 )
+                    {
+                        if ( p_best_ip == NULL ||
+                             p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
+                        {
+                            p_best_ip = p_unicast;
+                        }
+                    }
+                    p_unicast = p_unicast->Next;
+                }
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+
+    if ( p_best_ip != NULL )
+        goto done;
+
+    /* find any with IPv4 */
+    p_adapter = addresses;
+    while (p_adapter != NULL)
+    {
+        if (isAdapterSuitable(p_adapter, false))
+        {
+            /* get an IPv4 address */
+            IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
+            while (p_unicast != NULL)
+            {
+                strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+                if( WSAAddressToString( p_unicast->Address.lpSockaddr,
+                                        p_unicast->Address.iSockaddrLength,
+                                        NULL, psz_uri, &strSize ) == 0 )
+                {
+                    if ( p_best_ip == NULL ||
+                         p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
+                    {
+                        p_best_ip = p_unicast;
+                    }
+                }
+                p_unicast = p_unicast->Next;
+            }
+        }
+        p_adapter = p_adapter->Next;
+    }
+
+done:
+    if (p_best_ip != NULL)
+    {
+        strSize = sizeof( psz_uri ) / sizeof( wchar_t );
+        WSAAddressToString( p_best_ip->Address.lpSockaddr,
+                            p_best_ip->Address.iSockaddrLength,
+                            NULL, psz_uri, &strSize );
+        free(addresses);
+        return FromWide( psz_uri );
+    }
+    free(addresses);
+    return NULL;
+}
+#endif /* UPNP_ENABLE_IPV6 */
+#else /* _WIN32 */
+
+#ifdef UPNP_ENABLE_IPV6
+
+inline char *getPreferedAdapter()
+{
+    return NULL;
+}
+
+#else
+
+inline char *getIpv4ForMulticast()
+{
+    return NULL;
+}
+
+#endif
+
+#endif /* _WIN32 */
diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp
index 7455ceb299..f4085293ee 100644
--- a/modules/services_discovery/upnp.cpp
+++ b/modules/services_discovery/upnp.cpp
@@ -1,13 +1,14 @@
 /*****************************************************************************
  * upnp.cpp :  UPnP discovery module (libupnp)
  *****************************************************************************
- * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ * Copyright (C) 2004-2018 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
  *          Christian Henz <henz # c-lab.de>
  *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
  *          Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *          Shaleen Jain <shaleen at jain.sh>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
@@ -38,30 +39,6 @@
 #include <set>
 #include <string>
 
-#if UPNP_VERSION < 10623
-/*
- * Compat functions and typedefs for libupnp prior to 1.8
- */
-
-typedef Upnp_Discovery UpnpDiscovery;
-typedef Upnp_Action_Complete UpnpActionComplete;
-
-static const char* UpnpDiscovery_get_Location_cstr( const UpnpDiscovery* p_discovery )
-{
-  return p_discovery->Location;
-}
-
-static const char* UpnpDiscovery_get_DeviceID_cstr( const UpnpDiscovery* p_discovery )
-{
-  return p_discovery->DeviceId;
-}
-
-static IXML_Document* UpnpActionComplete_get_ActionResult( const UpnpActionComplete* p_result )
-{
-  return p_result->ActionResult;
-}
-#endif
-
 /*
  * Constants
 */
@@ -81,34 +58,31 @@ static const char *const ppsz_readible_satip_channel_lists[] = {
 /*
  * VLC handle
  */
-struct services_discovery_sys_t
+struct access_sys_t
 {
     UpnpInstanceWrapper* p_upnp;
-    vlc_thread_t         thread;
 };
 
-struct access_sys_t
+struct services_discovery_sys_t
 {
-    UpnpInstanceWrapper* p_upnp;
+    UpnpInstanceWrapper*  p_upnp;
+    vlc_thread_t          thread;
+    std::shared_ptr<UpnpInstanceWrapper::Listener> p_server_list;
 };
 
-UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
-vlc_mutex_t UpnpInstanceWrapper::s_lock = VLC_STATIC_MUTEX;
-SD::MediaServerList *UpnpInstanceWrapper::p_server_list = NULL;
-
 /*
  * VLC callback prototypes
  */
 namespace SD
 {
-    static int Open( vlc_object_t* );
-    static void Close( vlc_object_t* );
+    static int OpenSD( vlc_object_t* );
+    static void CloseSD( vlc_object_t* );
 }
 
 namespace Access
 {
-    static int Open( vlc_object_t* );
-    static void Close( vlc_object_t* );
+    static int OpenAccess( vlc_object_t* );
+    static void CloseAccess( vlc_object_t* );
 }
 
 VLC_SD_PROBE_HELPER( "upnp", N_("Universal Plug'n'Play"), SD_CAT_LAN )
@@ -122,7 +96,7 @@ vlc_module_begin()
     set_category( CAT_PLAYLIST );
     set_subcategory( SUBCAT_PLAYLIST_SD );
     set_capability( "services_discovery", 0 );
-    set_callbacks( SD::Open, SD::Close );
+    set_callbacks( SD::OpenSD, SD::CloseSD );
 
     add_string( "satip-channelist", "ASTRA_19_2E", SATIP_CHANNEL_LIST,
                 SATIP_CHANNEL_LIST, false )
@@ -133,36 +107,12 @@ vlc_module_begin()
     add_submodule()
         set_category( CAT_INPUT )
         set_subcategory( SUBCAT_INPUT_ACCESS )
-        set_callbacks( Access::Open, Access::Close )
+        set_callbacks( Access::OpenAccess, Access::CloseAccess )
         set_capability( "access", 0 )
 
     VLC_SD_PROBE_SUBMODULE
 vlc_module_end()
 
-
-/*
- * Returns the value of a child element, or NULL on error
- */
-const char* xml_getChildElementValue( IXML_Element* p_parent,
-                                      const char*   psz_tag_name )
-{
-    assert( p_parent );
-    assert( psz_tag_name );
-
-    IXML_NodeList* p_node_list;
-    p_node_list = ixmlElement_getElementsByTagName( p_parent, psz_tag_name );
-    if ( !p_node_list ) return NULL;
-
-    IXML_Node* p_element = ixmlNodeList_item( p_node_list, 0 );
-    ixmlNodeList_free( p_node_list );
-    if ( !p_element )   return NULL;
-
-    IXML_Node* p_text_node = ixmlNode_getFirstChild( p_element );
-    if ( !p_text_node ) return NULL;
-
-    return ixmlNode_getNodeValue( p_text_node );
-}
-
 /*
  * Extracts the result document from a SOAP response
  */
@@ -227,7 +177,7 @@ SearchThread( void *p_data )
 
     /* Search for media servers */
     int i_res = UpnpSearchAsync( p_sys->p_upnp->handle(), 5,
-            MEDIA_SERVER_DEVICE_TYPE, p_sys->p_upnp );
+            MEDIA_SERVER_DEVICE_TYPE, MEDIA_SERVER_DEVICE_TYPE );
     if( i_res != UPNP_E_SUCCESS )
     {
         msg_Err( p_sd, "Error sending search request: %s", UpnpGetErrorMessage( i_res ) );
@@ -236,7 +186,7 @@ SearchThread( void *p_data )
 
     /* Search for Sat Ip servers*/
     i_res = UpnpSearchAsync( p_sys->p_upnp->handle(), 5,
-            SATIP_SERVER_DEVICE_TYPE, p_sys->p_upnp );
+            SATIP_SERVER_DEVICE_TYPE, MEDIA_SERVER_DEVICE_TYPE );
     if( i_res != UPNP_E_SUCCESS )
         msg_Err( p_sd, "Error sending search request: %s", UpnpGetErrorMessage( i_res ) );
     return NULL;
@@ -245,7 +195,7 @@ SearchThread( void *p_data )
 /*
  * Initializes UPNP instance.
  */
-static int Open( vlc_object_t *p_this )
+static int OpenSD( vlc_object_t *p_this )
 {
     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
     services_discovery_sys_t *p_sys  = ( services_discovery_sys_t * )
@@ -256,20 +206,30 @@ static int Open( vlc_object_t *p_this )
 
     p_sd->description = _("Universal Plug'n'Play");
 
-    p_sys->p_upnp = UpnpInstanceWrapper::get( p_this, p_sd );
+    p_sys->p_upnp = UpnpInstanceWrapper::get( p_this );
     if ( !p_sys->p_upnp )
     {
         free(p_sys);
         return VLC_EGENERIC;
     }
 
+    p_sys->p_server_list = std::shared_ptr<UpnpInstanceWrapper::Listener>
+                            ( new(std::nothrow) SD::MediaServerList( p_sd ));
+    if ( unlikely( p_sys->p_server_list == NULL ) )
+    {
+        msg_Err( p_sd, "Failed to create a MediaServerList");
+        return VLC_EGENERIC;
+    }
+    p_sys->p_upnp->addListener( p_sys->p_server_list );
+
     /* XXX: Contrary to what the libupnp doc states, UpnpSearchAsync is
      * blocking (select() and send() are called). Therefore, Call
      * UpnpSearchAsync from an other thread. */
     if ( vlc_clone( &p_sys->thread, SearchThread, p_this,
                     VLC_THREAD_PRIORITY_LOW ) )
     {
-        p_sys->p_upnp->release( true );
+        p_sys->p_upnp->removeListener( p_sys->p_server_list );
+        p_sys->p_upnp->release();
         free(p_sys);
         return VLC_EGENERIC;
     }
@@ -280,13 +240,14 @@ static int Open( vlc_object_t *p_this )
 /*
  * Releases resources.
  */
-static void Close( vlc_object_t *p_this )
+static void CloseSD( vlc_object_t *p_this )
 {
     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
     services_discovery_sys_t *p_sys = reinterpret_cast<services_discovery_sys_t *>( p_sd->p_sys );
 
     vlc_join( p_sys->thread, NULL );
-    p_sys->p_upnp->release( true );
+    p_sys->p_upnp->removeListener( p_sys->p_server_list );
+    p_sys->p_upnp->release();
     free( p_sys );
 }
 
@@ -702,8 +663,11 @@ void MediaServerList::removeServer( const std::string& udn )
 /*
  * Handles servers listing UPnP events
  */
-int MediaServerList::Callback( Upnp_EventType event_type, UpnpEventPtr p_event )
+int MediaServerList::onEvent( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data )
 {
+    if (p_user_data != MEDIA_SERVER_DEVICE_TYPE)
+        return 0;
+
     switch( event_type )
     {
     case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
@@ -716,23 +680,14 @@ int MediaServerList::Callback( Upnp_EventType event_type, UpnpEventPtr p_event )
         int i_res;
         i_res = UpnpDownloadXmlDoc( UpnpDiscovery_get_Location_cstr( p_discovery ), &p_description_doc );
 
-        MediaServerList *self = UpnpInstanceWrapper::lockMediaServerList();
-        if ( !self )
-        {
-            UpnpInstanceWrapper::unlockMediaServerList();
-            return UPNP_E_CANCELED;
-        }
-
         if ( i_res != UPNP_E_SUCCESS )
         {
-            msg_Warn( self->m_sd, "Could not download device description! "
+            msg_Warn( m_sd, "Could not download device description! "
                             "Fetching data from %s failed: %s",
                             UpnpDiscovery_get_Location_cstr( p_discovery ), UpnpGetErrorMessage( i_res ) );
-            UpnpInstanceWrapper::unlockMediaServerList();
             return i_res;
         }
-        self->parseNewServer( p_description_doc, UpnpDiscovery_get_Location_cstr( p_discovery ) );
-        UpnpInstanceWrapper::unlockMediaServerList();
+        parseNewServer( p_description_doc, UpnpDiscovery_get_Location_cstr( p_discovery ) );
         ixmlDocument_free( p_description_doc );
     }
     break;
@@ -740,29 +695,19 @@ int MediaServerList::Callback( Upnp_EventType event_type, UpnpEventPtr p_event )
     case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
     {
         const UpnpDiscovery* p_discovery = ( const UpnpDiscovery* )p_event;
-
-        MediaServerList *self = UpnpInstanceWrapper::lockMediaServerList();
-        if ( self )
-            self->removeServer( UpnpDiscovery_get_DeviceID_cstr( p_discovery ) );
-        UpnpInstanceWrapper::unlockMediaServerList();
+        removeServer( UpnpDiscovery_get_DeviceID_cstr( p_discovery ) );
     }
     break;
 
     case UPNP_EVENT_SUBSCRIBE_COMPLETE:
     {
-        MediaServerList *self = UpnpInstanceWrapper::lockMediaServerList();
-        if ( self )
-            msg_Warn( self->m_sd, "subscription complete" );
-        UpnpInstanceWrapper::unlockMediaServerList();
+        msg_Warn( m_sd, "subscription complete" );
     }
         break;
 
     case UPNP_DISCOVERY_SEARCH_TIMEOUT:
     {
-        MediaServerList *self = UpnpInstanceWrapper::lockMediaServerList();
-        if ( self )
-            msg_Warn( self->m_sd, "search timeout" );
-        UpnpInstanceWrapper::unlockMediaServerList();
+        msg_Warn( m_sd, "search timeout" );
     }
         break;
 
@@ -774,10 +719,7 @@ int MediaServerList::Callback( Upnp_EventType event_type, UpnpEventPtr p_event )
 
     default:
     {
-        MediaServerList *self = UpnpInstanceWrapper::lockMediaServerList();
-        if ( self )
-            msg_Err( self->m_sd, "Unhandled event, please report ( type=%d )", event_type );
-        UpnpInstanceWrapper::unlockMediaServerList();
+        msg_Err( m_sd, "Unhandled event, please report ( type=%d )", event_type );
     }
         break;
     }
@@ -1285,7 +1227,7 @@ static int ReadDirectory( stream_t *p_access, input_item_node_t* p_node )
     return VLC_SUCCESS;
 }
 
-static int Open( vlc_object_t *p_this )
+static int OpenAccess( vlc_object_t *p_this )
 {
     stream_t* p_access = (stream_t*)p_this;
     access_sys_t* p_sys = new(std::nothrow) access_sys_t;
@@ -1293,7 +1235,7 @@ static int Open( vlc_object_t *p_this )
         return VLC_ENOMEM;
 
     p_access->p_sys = p_sys;
-    p_sys->p_upnp = UpnpInstanceWrapper::get( p_this, NULL );
+    p_sys->p_upnp = UpnpInstanceWrapper::get( p_this);
     if ( !p_sys->p_upnp )
     {
         delete p_sys;
@@ -1306,361 +1248,13 @@ static int Open( vlc_object_t *p_this )
     return VLC_SUCCESS;
 }
 
-static void Close( vlc_object_t* p_this )
+static void CloseAccess( vlc_object_t* p_this )
 {
     stream_t* p_access = (stream_t*)p_this;
     access_sys_t *sys = (access_sys_t *)p_access->p_sys;
 
-    sys->p_upnp->release( false );
+    sys->p_upnp->release();
     delete sys;
 }
 
 }
-
-UpnpInstanceWrapper::UpnpInstanceWrapper()
-    : m_handle( -1 )
-    , m_refcount( 0 )
-{
-}
-
-UpnpInstanceWrapper::~UpnpInstanceWrapper()
-{
-    UpnpUnRegisterClient( m_handle );
-    UpnpFinish();
-}
-
-#ifdef _WIN32
-
-static IP_ADAPTER_MULTICAST_ADDRESS* getMulticastAddress(IP_ADAPTER_ADDRESSES* p_adapter)
-{
-    const unsigned long i_broadcast_ip = inet_addr("239.255.255.250");
-
-    IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = p_adapter->FirstMulticastAddress;
-    while (p_multicast != NULL)
-    {
-        if (((struct sockaddr_in *)p_multicast->Address.lpSockaddr)->sin_addr.S_un.S_addr == i_broadcast_ip)
-            return p_multicast;
-        p_multicast = p_multicast->Next;
-    }
-    return NULL;
-}
-
-static bool isAdapterSuitable(IP_ADAPTER_ADDRESSES* p_adapter, bool ipv6)
-{
-    if ( p_adapter->OperStatus != IfOperStatusUp )
-        return false;
-    if (p_adapter->Length == sizeof(IP_ADAPTER_ADDRESSES_XP))
-    {
-        IP_ADAPTER_ADDRESSES_XP* p_adapter_xp = reinterpret_cast<IP_ADAPTER_ADDRESSES_XP*>( p_adapter );
-        // On Windows Server 2003 and Windows XP, this member is zero if IPv4 is not available on the interface.
-        if (ipv6)
-            return p_adapter_xp->Ipv6IfIndex != 0;
-        return p_adapter_xp->IfIndex != 0;
-    }
-    IP_ADAPTER_ADDRESSES_LH* p_adapter_lh = reinterpret_cast<IP_ADAPTER_ADDRESSES_LH*>( p_adapter );
-    if (p_adapter_lh->FirstGatewayAddress == NULL)
-        return false;
-    if (ipv6)
-        return p_adapter_lh->Ipv6Enabled;
-    return p_adapter_lh->Ipv4Enabled;
-}
-
-static IP_ADAPTER_ADDRESSES* ListAdapters()
-{
-    ULONG addrSize;
-    const ULONG queryFlags = GAA_FLAG_INCLUDE_GATEWAYS|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_DNS_SERVER;
-    IP_ADAPTER_ADDRESSES* addresses = NULL;
-    HRESULT hr;
-
-    /**
-     * https://msdn.microsoft.com/en-us/library/aa365915.aspx
-     *
-     * The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a
-     * 15KB working buffer pointed to by the AdapterAddresses parameter. On typical computers,
-     * this dramatically reduces the chances that the GetAdaptersAddresses function returns
-     * ERROR_BUFFER_OVERFLOW, which would require calling GetAdaptersAddresses function multiple
-     * times. The example code illustrates this method of use.
-     */
-    addrSize = 15 * 1024;
-    do
-    {
-        free(addresses);
-        addresses = (IP_ADAPTER_ADDRESSES*)malloc( addrSize );
-        if (addresses == NULL)
-            return NULL;
-        hr = GetAdaptersAddresses(AF_UNSPEC, queryFlags, NULL, addresses, &addrSize);
-    } while (hr == ERROR_BUFFER_OVERFLOW);
-    if (hr != NO_ERROR) {
-        free(addresses);
-        return NULL;
-    }
-    return addresses;
-}
-
-#ifdef UPNP_ENABLE_IPV6
-
-static char* getPreferedAdapter()
-{
-    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
-
-    addresses = ListAdapters();
-    if (addresses == NULL)
-        return NULL;
-
-    /* find one with multicast capabilities */
-    p_adapter = addresses;
-    while (p_adapter != NULL)
-    {
-        if (isAdapterSuitable( p_adapter, true ))
-        {
-            /* make sure it supports 239.255.255.250 */
-            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
-            if (p_multicast != NULL)
-            {
-                char* res = FromWide( p_adapter->FriendlyName );
-                free( addresses );
-                return res;
-            }
-        }
-        p_adapter = p_adapter->Next;
-    }
-    free(addresses);
-    return NULL;
-}
-
-#else
-
-static char *getIpv4ForMulticast()
-{
-    IP_ADAPTER_UNICAST_ADDRESS *p_best_ip = NULL;
-    wchar_t psz_uri[32];
-    DWORD strSize;
-    IP_ADAPTER_ADDRESSES *p_adapter, *addresses;
-
-    addresses = ListAdapters();
-    if (addresses == NULL)
-        return NULL;
-
-    /* find one with multicast capabilities */
-    p_adapter = addresses;
-    while (p_adapter != NULL)
-    {
-        if (isAdapterSuitable( p_adapter, false ))
-        {
-            /* make sure it supports 239.255.255.250 */
-            IP_ADAPTER_MULTICAST_ADDRESS *p_multicast = getMulticastAddress( p_adapter );
-            if (p_multicast != NULL)
-            {
-                /* get an IPv4 address */
-                IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
-                while (p_unicast != NULL)
-                {
-                    strSize = sizeof( psz_uri ) / sizeof( wchar_t );
-                    if( WSAAddressToString( p_unicast->Address.lpSockaddr,
-                                            p_unicast->Address.iSockaddrLength,
-                                            NULL, psz_uri, &strSize ) == 0 )
-                    {
-                        if ( p_best_ip == NULL ||
-                             p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
-                        {
-                            p_best_ip = p_unicast;
-                        }
-                    }
-                    p_unicast = p_unicast->Next;
-                }
-            }
-        }
-        p_adapter = p_adapter->Next;
-    }
-
-    if ( p_best_ip != NULL )
-        goto done;
-
-    /* find any with IPv4 */
-    p_adapter = addresses;
-    while (p_adapter != NULL)
-    {
-        if (isAdapterSuitable(p_adapter, false))
-        {
-            /* get an IPv4 address */
-            IP_ADAPTER_UNICAST_ADDRESS *p_unicast = p_adapter->FirstUnicastAddress;
-            while (p_unicast != NULL)
-            {
-                strSize = sizeof( psz_uri ) / sizeof( wchar_t );
-                if( WSAAddressToString( p_unicast->Address.lpSockaddr,
-                                        p_unicast->Address.iSockaddrLength,
-                                        NULL, psz_uri, &strSize ) == 0 )
-                {
-                    if ( p_best_ip == NULL ||
-                         p_best_ip->ValidLifetime > p_unicast->ValidLifetime )
-                    {
-                        p_best_ip = p_unicast;
-                    }
-                }
-                p_unicast = p_unicast->Next;
-            }
-        }
-        p_adapter = p_adapter->Next;
-    }
-
-done:
-    if (p_best_ip != NULL)
-    {
-        strSize = sizeof( psz_uri ) / sizeof( wchar_t );
-        WSAAddressToString( p_best_ip->Address.lpSockaddr,
-                            p_best_ip->Address.iSockaddrLength,
-                            NULL, psz_uri, &strSize );
-        free(addresses);
-        return FromWide( psz_uri );
-    }
-    free(addresses);
-    return NULL;
-}
-#endif /* UPNP_ENABLE_IPV6 */
-#else /* _WIN32 */
-
-#ifdef UPNP_ENABLE_IPV6
-
-static char *getPreferedAdapter()
-{
-    return NULL;
-}
-
-#else
-
-static char *getIpv4ForMulticast()
-{
-    return NULL;
-}
-
-#endif
-
-#endif /* _WIN32 */
-
-UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj, services_discovery_t *p_sd)
-{
-    SD::MediaServerList *p_server_list = NULL;
-    if (p_sd)
-    {
-        p_server_list = new(std::nothrow) SD::MediaServerList( p_sd );
-        if ( unlikely( p_server_list == NULL ) )
-        {
-            msg_Err( p_sd, "Failed to create a MediaServerList");
-            return NULL;
-        }
-    }
-
-    vlc_mutex_locker lock( &s_lock );
-    if ( s_instance == NULL )
-    {
-        UpnpInstanceWrapper* instance = new(std::nothrow) UpnpInstanceWrapper;
-        if ( unlikely( !instance ) )
-        {
-            delete p_server_list;
-            return NULL;
-        }
-
-    #ifdef UPNP_ENABLE_IPV6
-        char* psz_miface = var_InheritString( p_obj, "miface" );
-        if (psz_miface == NULL)
-            psz_miface = getPreferedAdapter();
-        msg_Info( p_obj, "Initializing libupnp on '%s' interface", psz_miface ? psz_miface : "default" );
-        int i_res = UpnpInit2( psz_miface, 0 );
-        free( psz_miface );
-    #else
-        /* If UpnpInit2 isnt available, initialize on first IPv4-capable interface */
-        char *psz_hostip = getIpv4ForMulticast();
-        int i_res = UpnpInit( psz_hostip, 0 );
-        free(psz_hostip);
-    #endif /* UPNP_ENABLE_IPV6 */
-        if( i_res != UPNP_E_SUCCESS )
-        {
-            msg_Err( p_obj, "Initialization failed: %s", UpnpGetErrorMessage( i_res ) );
-            delete instance;
-            delete p_server_list;
-            return NULL;
-        }
-
-        ixmlRelaxParser( 1 );
-
-        /* Register a control point */
-        i_res = UpnpRegisterClient( Callback, instance, &instance->m_handle );
-        if( i_res != UPNP_E_SUCCESS )
-        {
-            msg_Err( p_obj, "Client registration failed: %s", UpnpGetErrorMessage( i_res ) );
-            delete instance;
-            delete p_server_list;
-            return NULL;
-        }
-
-        /* libupnp does not treat a maximum content length of 0 as unlimited
-         * until 64dedf (~ pupnp v1.6.7) and provides no sane way to discriminate
-         * between versions */
-        if( (i_res = UpnpSetMaxContentLength( INT_MAX )) != UPNP_E_SUCCESS )
-        {
-            msg_Err( p_obj, "Failed to set maximum content length: %s",
-                    UpnpGetErrorMessage( i_res ));
-            delete instance;
-            delete p_server_list;
-            return NULL;
-        }
-        s_instance = instance;
-    }
-    s_instance->m_refcount++;
-    // This assumes a single UPNP SD instance
-    if (p_server_list != NULL)
-    {
-        assert(!UpnpInstanceWrapper::p_server_list);
-        UpnpInstanceWrapper::p_server_list = p_server_list;
-    }
-    return s_instance;
-}
-
-void UpnpInstanceWrapper::release(bool isSd)
-{
-    UpnpInstanceWrapper *p_delete = NULL;
-    vlc_mutex_lock( &s_lock );
-    if ( isSd )
-    {
-        delete UpnpInstanceWrapper::p_server_list;
-        UpnpInstanceWrapper::p_server_list = NULL;
-    }
-    if (--s_instance->m_refcount == 0)
-    {
-        p_delete = s_instance;
-        s_instance = NULL;
-    }
-    vlc_mutex_unlock( &s_lock );
-    delete p_delete;
-}
-
-UpnpClient_Handle UpnpInstanceWrapper::handle() const
-{
-    return m_handle;
-}
-
-int UpnpInstanceWrapper::Callback(Upnp_EventType event_type, UpnpEventPtr p_event, void *p_user_data)
-{
-    VLC_UNUSED(p_user_data);
-    vlc_mutex_lock( &s_lock );
-    if ( !UpnpInstanceWrapper::p_server_list )
-    {
-        vlc_mutex_unlock( &s_lock );
-        /* no MediaServerList available (anymore), do nothing */
-        return 0;
-    }
-    vlc_mutex_unlock( &s_lock );
-    SD::MediaServerList::Callback( event_type, p_event );
-    return 0;
-}
-
-SD::MediaServerList *UpnpInstanceWrapper::lockMediaServerList()
-{
-    vlc_mutex_lock( &s_lock ); /* do not allow deleting the p_server_list while using it */
-    return UpnpInstanceWrapper::p_server_list;
-}
-
-void UpnpInstanceWrapper::unlockMediaServerList()
-{
-    vlc_mutex_unlock( &s_lock );
-}
diff --git a/modules/services_discovery/upnp.hpp b/modules/services_discovery/upnp.hpp
index 14286276db..a75c754879 100644
--- a/modules/services_discovery/upnp.hpp
+++ b/modules/services_discovery/upnp.hpp
@@ -1,13 +1,14 @@
 /*****************************************************************************
  * upnp.hpp :  UPnP discovery module (libupnp) header
  *****************************************************************************
- * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ * Copyright (C) 2004-2018 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
  *          Christian Henz <henz # c-lab.de>
  *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
  *          Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *          Shaleen Jain <shaleen at jain.sh>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
@@ -23,10 +24,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "upnp-wrapper.hpp"
 
 #include <vector>
 #include <string>
@@ -35,57 +33,9 @@
 #include <windows.h>
 #include <wincrypt.h>
 #endif
-#include <upnp/upnp.h>
-#include <upnp/upnptools.h>
 
-#include <vlc_common.h>
 #include <vlc_url.h>
 
-#if UPNP_VERSION < 10800
-typedef void* UpnpEventPtr;
-#else
-typedef const void* UpnpEventPtr;
-#endif
-
-namespace SD
-{
-    class MediaServerList;
-}
-
-/*
- * libUpnp allows only one instance per process, so we have to share one for
- * both SD & Access module
- * Since the callback is bound to the UpnpClient_Handle, we have to register
- * a wrapper callback, in order for the access module to be able to initialize
- * libUpnp first.
- * When a SD wishes to use libUpnp, it will provide its own callback, that the
- * wrapper will forward.
- * This way, we always have a register callback & a client handle.
- */
-class UpnpInstanceWrapper
-{
-public:
-    // This increases the refcount before returning the instance
-    static UpnpInstanceWrapper* get(vlc_object_t* p_obj, services_discovery_t *p_sd);
-    void release(bool isSd);
-    UpnpClient_Handle handle() const;
-    static SD::MediaServerList *lockMediaServerList();
-    static void unlockMediaServerList();
-
-private:
-    static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
-
-    UpnpInstanceWrapper();
-    ~UpnpInstanceWrapper();
-
-private:
-    static UpnpInstanceWrapper* s_instance;
-    static vlc_mutex_t s_lock;
-    UpnpClient_Handle m_handle;
-    static SD::MediaServerList* p_server_list;
-    int m_refcount;
-};
-
 namespace SD
 {
 
@@ -104,7 +54,7 @@ struct MediaServerDesc
 };
 
 
-class MediaServerList
+class MediaServerList : public UpnpInstanceWrapper::Listener
 {
 public:
 
@@ -114,7 +64,9 @@ public:
     bool addServer(MediaServerDesc *desc );
     void removeServer(const std::string &udn );
     MediaServerDesc* getServer( const std::string& udn );
-    static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event );
+    int onEvent(Upnp_EventType event_type,
+                UpnpEventPtr p_event,
+                void* p_user_data) override;
 
 private:
     void parseNewServer( IXML_Document* doc, const std::string& location );
-- 
2.17.1



More information about the vlc-devel mailing list