[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