[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