[vlc-devel] [PATCH 1/3] upnp: add and use a callback listener interface
Hugo Beauzée-Luyssen
hugo at beauzee.fr
Wed Jun 27 12:47:57 CEST 2018
Hi,
On Mon, Jun 25, 2018, at 6:49 AM, Shaleen Jain wrote:
> 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 their members.
> ---
> modules/services_discovery/upnp.cpp | 127 +++++++++++-----------------
> modules/services_discovery/upnp.hpp | 44 +++++++---
> 2 files changed, 79 insertions(+), 92 deletions(-)
>
> diff --git a/modules/services_discovery/upnp.cpp b/modules/
> services_discovery/upnp.cpp
> index 7455ceb299..fc81a847a2 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
> @@ -84,6 +85,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 +95,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 +229,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 +238,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 +258,29 @@ 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::make_shared<SD::MediaServerList>( p_sd );
> + if ( unlikely( p_sys->p_server_list == NULL ) )
make_shared will throw a std::bad_alloc in case of allocation failure, but it won't return NULL/nullptr
> + {
> + 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;
> }
> @@ -286,7 +297,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 +714,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 +731,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 +746,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 +770,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 +1286,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 +1304,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 +1530,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 +1558,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 +1569,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 +1580,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 +1608,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..8c13265fc8 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
> @@ -30,6 +31,7 @@
>
> #include <vector>
> #include <string>
> +#include <memory>
>
> #ifdef _WIN32
> #include <windows.h>
> @@ -64,26 +66,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 +118,7 @@ struct MediaServerDesc
> };
>
>
> -class MediaServerList
> +class MediaServerList : public UpnpInstanceWrapper::Listener
> {
> public:
>
> @@ -114,7 +128,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
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
Aside from that, LGTM
--
Hugo Beauzée-Luyssen
hugo at beauzee.fr
More information about the vlc-devel
mailing list