[vlc-devel] [PATCH 1/4] upnp: add and use a callback listener interface

Shaleen Jain shaleen at jain.sh
Wed Jun 27 14:14:29 CEST 2018


Introduce a Listener interface to
UpnpInstanceWrapper that can be used
by modules to get UpnpEvent callbacks
from libupnp decoupling UpnpInstanceWrapper
from any specific module or its members.
---
 modules/services_discovery/upnp.cpp | 133 +++++++++++-----------------
 modules/services_discovery/upnp.hpp |  45 ++++++----
 2 files changed, 84 insertions(+), 94 deletions(-)

diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp
index 7455ceb299..7f8ed230ae 100644
--- a/modules/services_discovery/upnp.cpp
+++ b/modules/services_discovery/upnp.cpp
@@ -1,13 +1,13 @@
 /*****************************************************************************
  * upnp.cpp :  UPnP discovery module (libupnp)
  *****************************************************************************
- * Copyright (C) 2004-2016 VLC authors and VideoLAN
- * $Id$
+ * Copyright (C) 2004-2018 VLC authors and VideoLAN
  *
  * 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
@@ -84,6 +84,7 @@ static const char *const ppsz_readible_satip_channel_lists[] = {
 struct services_discovery_sys_t
 {
     UpnpInstanceWrapper* p_upnp;
+    std::shared_ptr<SD::MediaServerList> p_server_list;
     vlc_thread_t         thread;
 };
 
@@ -93,8 +94,8 @@ struct access_sys_t
 };
 
 UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
+UpnpInstanceWrapper::Listeners UpnpInstanceWrapper::s_listeners;
 vlc_mutex_t UpnpInstanceWrapper::s_lock = VLC_STATIC_MUTEX;
-SD::MediaServerList *UpnpInstanceWrapper::p_server_list = NULL;
 
 /*
  * VLC callback prototypes
@@ -227,7 +228,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 +237,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;
@@ -256,20 +257,34 @@ 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;
     }
 
+    try
+    {
+        p_sys->p_server_list = std::make_shared<SD::MediaServerList>( p_sd );
+    }
+    catch ( const std::bad_alloc& )
+    {
+        msg_Err( p_sd, "Failed to create a MediaServerList");
+        p_sys->p_upnp->release();
+        free(p_sys);
+        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;
     }
@@ -286,7 +301,8 @@ static void Close( vlc_object_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 +718,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 +735,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 +750,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 +774,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;
     }
@@ -1293,7 +1290,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;
@@ -1311,7 +1308,7 @@ static void Close( 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;
 }
 
@@ -1537,26 +1534,14 @@ static char *getIpv4ForMulticast()
 
 #endif /* _WIN32 */
 
-UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj, services_discovery_t *p_sd)
+UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj)
 {
-    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;
         }
 
@@ -1577,7 +1562,6 @@ UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj, services_disc
         {
             msg_Err( p_obj, "Initialization failed: %s", UpnpGetErrorMessage( i_res ) );
             delete instance;
-            delete p_server_list;
             return NULL;
         }
 
@@ -1589,7 +1573,6 @@ UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj, services_disc
         {
             msg_Err( p_obj, "Client registration failed: %s", UpnpGetErrorMessage( i_res ) );
             delete instance;
-            delete p_server_list;
             return NULL;
         }
 
@@ -1601,30 +1584,18 @@ UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj, services_disc
             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)
+void UpnpInstanceWrapper::release()
 {
     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;
@@ -1641,26 +1612,30 @@ UpnpClient_Handle UpnpInstanceWrapper::handle() const
 
 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 )
+    for (Listeners::iterator iter = s_listeners.begin(); iter != s_listeners.end(); ++iter)
     {
-        vlc_mutex_unlock( &s_lock );
-        /* no MediaServerList available (anymore), do nothing */
-        return 0;
+        (*iter)->onEvent(event_type, p_event, p_user_data);
     }
-    vlc_mutex_unlock( &s_lock );
-    SD::MediaServerList::Callback( event_type, p_event );
+
     return 0;
 }
 
-SD::MediaServerList *UpnpInstanceWrapper::lockMediaServerList()
+void UpnpInstanceWrapper::addListener(ListenerPtr listener)
 {
-    vlc_mutex_lock( &s_lock ); /* do not allow deleting the p_server_list while using it */
-    return UpnpInstanceWrapper::p_server_list;
+    vlc_mutex_lock( &s_lock );
+    if ( std::find( s_listeners.begin(), s_listeners.end(), listener) != s_listeners.end() )
+        return;
+    s_listeners.push_back( std::move(listener) );
+    vlc_mutex_unlock( &s_lock );
 }
 
-void UpnpInstanceWrapper::unlockMediaServerList()
+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() )
+        return;
+
+    s_listeners.erase( iter );
     vlc_mutex_unlock( &s_lock );
 }
diff --git a/modules/services_discovery/upnp.hpp b/modules/services_discovery/upnp.hpp
index 14286276db..325e1251c0 100644
--- a/modules/services_discovery/upnp.hpp
+++ b/modules/services_discovery/upnp.hpp
@@ -1,13 +1,13 @@
 /*****************************************************************************
  * upnp.hpp :  UPnP discovery module (libupnp) header
  *****************************************************************************
- * Copyright (C) 2004-2016 VLC authors and VideoLAN
- * $Id$
+ * Copyright (C) 2004-2018 VLC authors and VideoLAN
  *
  * 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
@@ -30,6 +30,7 @@
 
 #include <vector>
 #include <string>
+#include <memory>
 
 #ifdef _WIN32
 #include <windows.h>
@@ -64,26 +65,38 @@ namespace SD
  */
 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, services_discovery_t *p_sd);
-    void release(bool isSd);
+    static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
+    void release();
     UpnpClient_Handle handle() const;
-    static SD::MediaServerList *lockMediaServerList();
-    static void unlockMediaServerList();
+    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();
-
-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 +117,7 @@ struct MediaServerDesc
 };
 
 
-class MediaServerList
+class MediaServerList : public UpnpInstanceWrapper::Listener
 {
 public:
 
@@ -114,7 +127,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.18.0


More information about the vlc-devel mailing list