[vlc-devel] [PATCH 5/9] avahi: Implement services advertisement
Hugo Beauzée-Luyssen
hugo at beauzee.fr
Wed Sep 2 15:47:27 CEST 2020
refs #18090
---
modules/services_discovery/avahi.c | 235 ++++++++++++++++++++++++++++-
1 file changed, 234 insertions(+), 1 deletion(-)
diff --git a/modules/services_discovery/avahi.c b/modules/services_discovery/avahi.c
index 77bb9f3711..49dbab9001 100644
--- a/modules/services_discovery/avahi.c
+++ b/modules/services_discovery/avahi.c
@@ -32,12 +32,15 @@
#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
#include <vlc_common.h>
#include <vlc_plugin.h>
-#include <vlc_services_discovery.h>
#include <vlc_renderer_discovery.h>
+#include <vlc_services_discovery.h>
+#include <vlc_services_advertisement.h>
+#include <vlc_list.h>
#include <avahi-client/client.h>
#include <avahi-client/publish.h>
#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
#include <avahi-common/thread-watch.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
@@ -51,6 +54,8 @@ static int OpenSD ( vlc_object_t * );
static void CloseSD( vlc_object_t * );
static int OpenRD ( vlc_object_t * );
static void CloseRD( vlc_object_t * );
+static int OpenSA ( vlc_object_t * );
+static void CloseSA( vlc_object_t * );
VLC_SD_PROBE_HELPER("avahi", N_("Zeroconf network services"), SD_CAT_LAN)
VLC_RD_PROBE_HELPER( "avahi_renderer", "Avahi Zeroconf renderer Discovery" )
@@ -73,6 +78,13 @@ vlc_module_begin ()
set_callbacks( OpenRD, CloseRD )
add_shortcut( "mdns_renderer", "avahi_renderer" )
VLC_RD_PROBE_SUBMODULE
+ add_submodule()
+ set_description( N_( "Avahi DNS Services Advertisement" ) )
+ set_category( CAT_ADVANCED )
+ set_subcategory( SUBCAT_ADVANCED_NETWORK )
+ set_capability( "services_advertisement", 0 )
+ set_callbacks( OpenSA, CloseSA )
+ add_shortcut( "mdns", "avahi_sa" )
vlc_module_end ()
/*****************************************************************************
@@ -88,6 +100,23 @@ typedef struct
bool renderer;
} discovery_sys_t;
+typedef struct sa_sys_t
+{
+ AvahiThreadedPoll *poll;
+ AvahiClient *client;
+ vlc_object_t *parent;
+ AvahiEntryGroup *group;
+ vlc_mutex_t lock;
+ struct vlc_list entries;
+} sa_sys_t;
+
+typedef struct
+{
+ services_advertisement_entry_t* entry;
+ char* alt_name;
+ struct vlc_list node;
+} sa_service_t;
+
static const struct
{
const char *psz_protocol;
@@ -211,6 +240,157 @@ error:
free( model );
}
+static void sa_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata);
+
+static void sa_announce_services( services_advertisement_t* sa, AvahiClient* client )
+{
+ /* This might be invoked from avahi_client_new, therefor before we stored
+ * the created client in sys, so we need to get it as a parameter */
+ sa_sys_t* sys = (sa_sys_t*)sa->p_sys;
+ vlc_mutex_assert( &sys->lock );
+
+ if( !sys->group )
+ {
+ sys->group = avahi_entry_group_new(client, sa_entry_group_callback, sa);
+ if( !sys->group )
+ return;
+ }
+ if( !avahi_entry_group_is_empty( sys->group ) )
+ return;
+ sa_service_t* service;
+ vlc_list_foreach( service, &sys->entries, node )
+ {
+ AvahiStringList* sl = NULL;
+ size_t nb_txts = vlc_sa_entry_Count_TXT( service->entry );
+ if ( nb_txts > 0 )
+ {
+ sl = avahi_string_list_new_from_array(
+ vlc_sa_entry_Get_TXT( service->entry ), nb_txts );
+ }
+ int ret = avahi_entry_group_add_service_strlst( sys->group, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC, 0,
+ service->alt_name ? service->alt_name :
+ vlc_sa_entry_Name( service->entry ),
+ vlc_sa_entry_Type( service->entry ),
+ NULL, NULL,
+ vlc_sa_entry_Port( service->entry ),
+ sl );
+ avahi_string_list_free( sl );
+ if( ret < 0 )
+ {
+ if( ret == AVAHI_ERR_COLLISION )
+ {
+ avahi_free( service->alt_name );
+ service->alt_name = avahi_alternative_service_name(
+ vlc_sa_entry_Name( service->entry ) );
+ sa_announce_services( sa, client );
+ return;
+ }
+ msg_Err( sa, "Failed to register service: %s", avahi_strerror( ret ) );
+ return;
+ }
+ }
+ avahi_entry_group_commit(sys->group);
+}
+
+static int sa_add_service( services_advertisement_t* sa,
+ services_advertisement_entry_t* entry )
+{
+ sa_sys_t* sys = (sa_sys_t*)sa->p_sys;
+ sa_service_t* service = malloc( sizeof( *service ) );
+ if( !service )
+ return VLC_ENOMEM;
+
+ service->alt_name = NULL;
+ service->entry = vlc_sa_entry_Hold( entry );
+ vlc_mutex_lock( &sys->lock );
+ vlc_list_append( &service->node, &sys->entries );
+ if ( sys->group )
+ {
+ avahi_entry_group_reset( sys->group );
+ sa_announce_services( sa, sys->client );
+ }
+ vlc_mutex_unlock( &sys->lock );
+ return VLC_SUCCESS;
+}
+
+static int sa_remove_service( services_advertisement_t* sa,
+ services_advertisement_entry_t* entry )
+{
+ sa_sys_t* sys = sa->p_sys;
+ sa_service_t *s;
+ vlc_mutex_lock( &sys->lock );
+ vlc_list_foreach( s, &sys->entries, node )
+ {
+ if( vlc_sa_entry_Port( s->entry ) != vlc_sa_entry_Port( entry ) ||
+ strcmp( vlc_sa_entry_Type( s->entry ), vlc_sa_entry_Type( entry ) ) )
+ continue;
+
+ vlc_list_remove( &s->node );
+ if ( sys->group )
+ avahi_entry_group_reset( sys->group );
+ sa_announce_services( sa, sys->client );
+ vlc_mutex_unlock( &sys->lock );
+ avahi_free( s->alt_name );
+ vlc_sa_entry_Release( s->entry );
+ free( s );
+ return VLC_SUCCESS;
+ }
+ vlc_mutex_unlock( &sys->lock );
+ return VLC_EGENERIC;
+}
+
+static void sa_entry_group_callback( AvahiEntryGroup *g, AvahiEntryGroupState state,
+ void *userdata )
+{
+ services_advertisement_t *sa = userdata;
+ switch (state)
+ {
+ case AVAHI_ENTRY_GROUP_COLLISION:
+ {
+ sa_sys_t* sys = sa->p_sys;
+ /* Re-announce all services and let the conflicting one get renamed then */
+ avahi_entry_group_reset( sys->group );
+ vlc_mutex_lock( &sys->lock );
+ sa_announce_services( sa, sys->client );
+ vlc_mutex_unlock( &sys->lock );
+ break;
+ }
+ case AVAHI_ENTRY_GROUP_FAILURE:
+ msg_Warn( sa, "Failed to register entry group: %s",
+ avahi_strerror(avahi_client_errno(avahi_entry_group_get_client( g ) ) ) );
+ break;
+ case AVAHI_ENTRY_GROUP_ESTABLISHED:
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ break;
+ }
+}
+
+static void sa_client_callback( AvahiClient *c, AvahiClientState state,
+ void *userdata )
+{
+ services_advertisement_t* sa = userdata;
+ sa_sys_t* sys = sa->p_sys;
+ if( state == AVAHI_CLIENT_FAILURE &&
+ (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) )
+ {
+ msg_Err( sys->parent, "avahi client disconnected" );
+ avahi_threaded_poll_quit( sys->poll );
+ }
+ else if ( state == AVAHI_CLIENT_S_RUNNING )
+ {
+ vlc_mutex_lock( &sys->lock );
+ sa_announce_services( sa, c );
+ vlc_mutex_unlock( &sys->lock );
+ }
+ else if ( state == AVAHI_CLIENT_S_COLLISION ||
+ state == AVAHI_CLIENT_S_REGISTERING )
+ {
+ avahi_entry_group_reset( sys->group );
+ }
+}
+
/*****************************************************************************
* client_callback
*****************************************************************************/
@@ -524,6 +704,41 @@ static int OpenRD( vlc_object_t *p_this )
return ret;
}
+static int OpenSA( vlc_object_t *p_this)
+{
+ int err;
+ services_advertisement_t *p_sa = (services_advertisement_t*)p_this;
+
+ sa_sys_t *p_sys = p_sa->p_sys = calloc( 1, sizeof( *p_sys ) );
+ if( !p_sa->p_sys )
+ return VLC_ENOMEM;
+ p_sys->parent = p_this;
+ vlc_list_init( &p_sys->entries );
+ p_sys->poll = avahi_threaded_poll_new();
+ if( !p_sys->poll )
+ {
+ free( p_sys );
+ return VLC_EGENERIC;
+ }
+
+ p_sys->client = avahi_client_new( avahi_threaded_poll_get( p_sys->poll ),
+ 0, sa_client_callback, p_sa, &err );
+ if( p_sys->client == NULL )
+ {
+ msg_Err( p_sys->parent, "failed to create avahi client: %s",
+ avahi_strerror( err ) );
+ avahi_threaded_poll_free( p_sys->poll );
+ free( p_sys );
+ return VLC_EGENERIC;
+ }
+ avahi_threaded_poll_start( p_sys->poll );
+
+ p_sa->pf_announce = sa_add_service;
+ p_sa->pf_conceal = sa_remove_service;
+
+ return VLC_SUCCESS;
+}
+
/*****************************************************************************
* Close: cleanup
*****************************************************************************/
@@ -555,3 +770,21 @@ static void CloseRD( vlc_object_t *p_this )
clear_renderer_item, NULL );
free( p_sys );
}
+
+
+static void CloseSA( vlc_object_t *p_this )
+{
+ services_advertisement_t *p_sa = (services_advertisement_t*)p_this;
+ sa_sys_t *p_sys = p_sa->p_sys;
+ sa_service_t* s;
+ vlc_list_foreach( s, &p_sys->entries, node )
+ {
+ vlc_sa_entry_Release( s->entry );
+ avahi_free( s->alt_name );
+ free( s );
+ }
+ avahi_threaded_poll_stop( p_sys->poll );
+ avahi_client_free( p_sys->client );
+ avahi_threaded_poll_free( p_sys->poll );
+ free( p_sys );
+}
--
2.20.1
More information about the vlc-devel
mailing list