File 0002-Add-Avahi-implementation-for-chromecast-renderer-dis.patch of Package vlc

From c3e38bce3b30c6e0974ec5da47e22260ed5b7ea8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Br=C3=BCns?= <stefan.bruens@rwth-aachen.de>
Date: Tue, 25 Dec 2018 03:48:15 +0100
Subject: [PATCH 2/2] Add Avahi implementation for chromecast renderer
 discovery

---
 modules/services_discovery/avahi.c | 246 ++++++++++++++++++++++++-----
 1 file changed, 209 insertions(+), 37 deletions(-)

diff --git a/modules/services_discovery/avahi.c b/modules/services_discovery/avahi.c
index aa58c7f673..527e08a2e9 100644
--- a/modules/services_discovery/avahi.c
+++ b/modules/services_discovery/avahi.c
@@ -33,6 +33,7 @@
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_services_discovery.h>
+#include <vlc_renderer_discovery.h>
 
 #include <avahi-client/client.h>
 #include <avahi-client/publish.h>
@@ -48,8 +49,11 @@
 /* Callbacks */
 static int  Open ( vlc_object_t * );
 static void Close( vlc_object_t * );
+static int  OpenRD ( vlc_object_t * );
+static void CloseRD( 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" )
 
 vlc_module_begin ()
     set_shortname( "Avahi" )
@@ -61,45 +65,153 @@ vlc_module_begin ()
     add_shortcut( "mdns", "avahi" )
 
     VLC_SD_PROBE_SUBMODULE
+    add_submodule() \
+        set_description( N_( "Avahi Renderer Discovery" ) )
+        set_category( CAT_SOUT )
+        set_subcategory( SUBCAT_SOUT_RENDERER )
+        set_capability( "renderer_discovery", 0 )
+        set_callbacks( OpenRD, CloseRD )
+        add_shortcut( "mdns_renderer", "avahi_renderer" )
+        VLC_RD_PROBE_SUBMODULE
 vlc_module_end ()
 
 /*****************************************************************************
  * Local structures
  *****************************************************************************/
 
-struct services_discovery_sys_t
+typedef
+struct
 {
     AvahiThreadedPoll   *poll;
     AvahiClient         *client;
     vlc_dictionary_t    services_name_to_input_item;
-};
+    vlc_object_t        *parent;
+    bool                renderer;
+} discovery_sys_t;
+
+struct vlc_renderer_discovery_sys
+{
+    discovery_sys_t s;
+};
+
+struct services_discovery_sys_t
+{
+    discovery_sys_t s;
+};
 
 static const struct
 {
     const char *psz_protocol;
     const char *psz_service_name;
+    bool        b_renderer;
 } protocols[] = {
-    { "ftp", "_ftp._tcp" },
-    { "smb", "_smb._tcp" },
-    { "nfs", "_nfs._tcp" },
-    { "sftp", "_sftp-ssh._tcp" },
-    { "rtsp", "_rtsp._tcp" },
+    { "ftp", "_ftp._tcp", false },
+    { "smb", "_smb._tcp", false },
+    { "nfs", "_nfs._tcp", false },
+    { "sftp", "_sftp-ssh._tcp", false },
+    { "rtsp", "_rtsp._tcp", false },
+    { "chromecast", "_googlecast._tcp", true },
 };
 #define NB_PROTOCOLS (sizeof(protocols) / sizeof(*protocols))
 
+/*****************************************************************************
+ * helpers
+ *****************************************************************************/
+static void add_renderer( const char *psz_protocol, const char *psz_name,
+                          const char *psz_addr, uint16_t i_port,
+                          AvahiStringList *txt, discovery_sys_t *p_sys )
+{
+    vlc_renderer_discovery_t *p_rd = ( vlc_renderer_discovery_t* )(p_sys->parent);
+    AvahiStringList *asl = NULL;
+    if ( !strcmp( "chromecast", psz_protocol ) ) {
+        int renderer_flags = 0;
+
+        /* Friendly name */
+        asl = avahi_string_list_find( txt, "fn" );
+        if( asl != NULL )
+        {
+            char *key = NULL;
+            char *value = NULL;
+            if( avahi_string_list_get_pair( asl, &key, &value, NULL ) == 0 &&
+                value != NULL )
+            {
+
+            }
+            msg_Info( p_rd, "key: '%s' value: '%s'", key, value );
+
+            if( key != NULL )
+                avahi_free( (void *)key );
+            if( value != NULL )
+                avahi_free( (void *)value );
+        }
+
+        /* capabilities */
+        asl = avahi_string_list_find( txt, "ca" );
+        if( asl != NULL ) {
+            char *key = NULL;
+            char *value = NULL;
+            if( avahi_string_list_get_pair( asl, &key, &value, NULL ) == 0 &&
+                value != NULL )
+            {
+                int ca = atoi( value );
+
+                if ( ( ca & 0x01 ) != 0 )
+                    renderer_flags |= VLC_RENDERER_CAN_VIDEO;
+                if ( ( ca & 0x04 ) != 0 )
+                    renderer_flags |= VLC_RENDERER_CAN_AUDIO;
+            }
+            msg_Info( p_rd, "key: '%s' value: '%s'", key, value );
+
+            if( key != NULL )
+                avahi_free( (void *)key );
+            if( value != NULL )
+                avahi_free( (void *)value );
+        }
+        /*
+         * "md": model
+         * "id": uuid
+         * "ic": icon URL, relative to http://<ip>:8008/
+         */
+
+        const char *extra_uri = renderer_flags & VLC_RENDERER_CAN_VIDEO ? NULL : "no-video";
+        char *uri = NULL;
+        char *icon_uri = NULL;
+        if( asprintf( &uri, "%s://%s:%u", psz_protocol, psz_addr, i_port ) < 0 )
+            return;
+        if( asprintf( &icon_uri, "http://%s:8008/setup/icon.png", psz_addr) < 0 )
+        {
+            free( uri );
+            return;
+        }
+
+        vlc_renderer_item_t *p_renderer_item =
+            vlc_renderer_item_new( "chromecast", psz_name, uri, extra_uri,
+                                   "cc_demux", icon_uri, renderer_flags );
+        if( p_renderer_item == NULL ) {
+            free( uri );
+            free( icon_uri );
+            return;
+        }
+
+        vlc_dictionary_insert( &p_sys->services_name_to_input_item,
+            psz_name, p_renderer_item);
+        vlc_rd_add_item( p_rd, p_renderer_item );
+        vlc_renderer_item_release( p_renderer_item );
+    }
+}
+
 /*****************************************************************************
  * client_callback
  *****************************************************************************/
 static void client_callback( AvahiClient *c, AvahiClientState state,
                              void * userdata )
 {
-    services_discovery_t *p_sd = ( services_discovery_t* )userdata;
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    discovery_sys_t *p_sys = userdata;
 
     if( state == AVAHI_CLIENT_FAILURE &&
         (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) )
     {
-        msg_Err( p_sd, "avahi client disconnected" );
+        msg_Err( p_sys->parent, "avahi client disconnected" );
         avahi_threaded_poll_quit( p_sys->poll );
     }
 }
@@ -122,15 +234,14 @@ static void resolve_callback(
     AvahiLookupResultFlags flags,
     void* userdata )
 {
-    services_discovery_t *p_sd = ( services_discovery_t* )userdata;
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    discovery_sys_t *p_sys = userdata;
 
     VLC_UNUSED(interface); VLC_UNUSED(host_name);
     VLC_UNUSED(flags);
 
     if( event == AVAHI_RESOLVER_FAILURE )
     {
-        msg_Err( p_sd,
+        msg_Err( p_sys->parent,
                  "failed to resolve service '%s' of type '%s' in domain '%s'",
                  name, type, domain );
     }
@@ -142,7 +253,7 @@ static void resolve_callback(
         AvahiStringList *asl = NULL;
         input_item_t *p_input = NULL;
 
-        msg_Info( p_sd, "service '%s' of type '%s' in domain '%s' port %i",
+        msg_Info( p_sys->parent, "service '%s' of type '%s' in domain '%s' port %i",
                   name, type, domain, port );
 
         avahi_address_snprint(a, (sizeof(a)/sizeof(a[0]))-1, address);
@@ -154,10 +265,15 @@ static void resolve_callback(
             }
 
         const char *psz_protocol = NULL;
+        bool is_renderer = false;
         for( unsigned int i = 0; i < NB_PROTOCOLS; i++ )
         {
             if( !strcmp(type, protocols[i].psz_service_name) )
+            {
                 psz_protocol = protocols[i].psz_protocol;
+                is_renderer = protocols[i].b_renderer;
+                break;
+            }
         }
         if( psz_protocol == NULL )
         {
@@ -166,6 +282,15 @@ static void resolve_callback(
             return;
         }
 
+        if( txt != NULL && is_renderer )
+        {
+            const char* addr_v4v6 = psz_addr != NULL ? psz_addr : a;
+            add_renderer( psz_protocol, name, addr_v4v6, port, txt, p_sys );
+            free( psz_addr );
+            avahi_service_resolver_free( r );
+            return;
+        }
+
         if( txt != NULL )
             asl = avahi_string_list_find( txt, "path" );
         if( asl != NULL )
@@ -212,6 +337,7 @@ static void resolve_callback(
         }
         if( p_input != NULL )
         {
+            services_discovery_t *p_sd = ( services_discovery_t* )(p_sys->parent);
             vlc_dictionary_insert( &p_sys->services_name_to_input_item,
                 name, p_input );
             services_discovery_AddItem( p_sd, p_input );
@@ -238,8 +364,8 @@ static void browse_callback(
 {
     VLC_UNUSED(b);
     VLC_UNUSED(flags);
-    services_discovery_t *p_sd = ( services_discovery_t* )userdata;
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    discovery_sys_t *p_sys = userdata;
+    msg_Info( p_sys->parent, "browse_callback - event: %d, name: %s, type: %s", event, name, type);
     if( event == AVAHI_BROWSER_NEW )
     {
         if( avahi_service_resolver_new( p_sys->client, interface, protocol,
@@ -247,22 +373,32 @@ static void browse_callback(
                                         0,
                                         resolve_callback, userdata ) == NULL )
         {
-            msg_Err( p_sd, "failed to resolve service '%s': %s", name,
+            msg_Err( p_sys->parent, "failed to resolve service '%s': %s", name,
                      avahi_strerror( avahi_client_errno( p_sys->client ) ) );
         }
     }
     else if( name )
     {
         /** \todo Store the input id and search it, rather than searching the items */
-        input_item_t *p_item;
+        void *p_item;
         p_item = vlc_dictionary_value_for_key(
                         &p_sys->services_name_to_input_item,
                         name );
         if( !p_item )
-            msg_Err( p_sd, "failed to find service '%s' in playlist", name );
+            msg_Err( p_sys->parent, "failed to find service '%s' in playlist", name );
         else
         {
-            services_discovery_RemoveItem( p_sd, p_item );
+            if( p_sys->renderer )
+            {
+                vlc_renderer_discovery_t *p_rd = ( vlc_renderer_discovery_t* )(p_sys->parent);
+                vlc_rd_remove_item( p_rd, p_item );
+            }
+            else
+            {
+                //input_item_t *p_item;
+                services_discovery_t *p_sd = ( services_discovery_t* )(p_sys->parent);
+                services_discovery_RemoveItem( p_sd, p_item );
+            }
             vlc_dictionary_remove_value_for_key(
                         &p_sys->services_name_to_input_item,
                         name, NULL, NULL );
@@ -273,46 +409,41 @@ static void browse_callback(
 /*****************************************************************************
  * Open: initialize and create stuff
  *****************************************************************************/
-static int Open( vlc_object_t *p_this )
+static int OpenCommon( discovery_sys_t *p_sys )
 {
-    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
-    services_discovery_sys_t *p_sys;
     int err;
 
-    p_sd->p_sys = p_sys = calloc( 1, sizeof( services_discovery_sys_t ) );
-    if( !p_sys )
-        return VLC_ENOMEM;
-
-    p_sd->description = _("Zeroconf network services");
-
     vlc_dictionary_init( &p_sys->services_name_to_input_item, 1 );
 
     p_sys->poll = avahi_threaded_poll_new();
     if( p_sys->poll == NULL )
     {
-        msg_Err( p_sd, "failed to create Avahi threaded poll" );
+        msg_Err( p_sys->parent, "failed to create Avahi threaded poll" );
         goto error;
     }
 
     p_sys->client = avahi_client_new( avahi_threaded_poll_get(p_sys->poll),
-                                      0, client_callback, p_sd, &err );
+                                      0, client_callback, p_sys, &err );
     if( p_sys->client == NULL )
     {
-        msg_Err( p_sd, "failed to create avahi client: %s",
+        msg_Err( p_sys->parent, "failed to create avahi client: %s",
                  avahi_strerror( err ) );
         goto error;
     }
 
     for( unsigned i = 0; i < NB_PROTOCOLS; i++ )
     {
+        if ( protocols[i].b_renderer != p_sys->renderer )
+            continue;
+
         AvahiServiceBrowser *sb;
         sb = avahi_service_browser_new( p_sys->client, AVAHI_IF_UNSPEC,
                 AVAHI_PROTO_UNSPEC,
                 protocols[i].psz_service_name, NULL,
-                0, browse_callback, p_sd );
+                0, browse_callback, p_sys );
         if( sb == NULL )
         {
-            msg_Err( p_sd, "failed to create avahi service browser %s", avahi_strerror( avahi_client_errno(p_sys->client) ) );
+            msg_Err( p_sys->parent, "failed to create avahi service browser %s", avahi_strerror( avahi_client_errno(p_sys->client) ) );
             goto error;
         }
     }
@@ -333,13 +464,39 @@ error:
     return VLC_EGENERIC;
 }
 
+static int Open( vlc_object_t *p_this )
+{
+    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
+    p_sd->description = _("Zeroconf network services");
+
+    p_sd->p_sys = calloc( 1, sizeof( services_discovery_sys_t ) );
+    if( !p_sd->p_sys )
+        return VLC_ENOMEM;
+    p_sd->p_sys->s.parent = p_this;
+    p_sd->p_sys->s.renderer = false;
+
+    return OpenCommon( &p_sd->p_sys->s );
+}
+
+static int OpenRD( vlc_object_t *p_this )
+{
+    vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *)p_this;
+
+    p_rd->p_sys = calloc( 1, sizeof( vlc_renderer_discovery_sys ) );
+    if( !p_rd->p_sys )
+        return VLC_ENOMEM;
+    p_rd->p_sys->s.parent = p_this;
+    p_rd->p_sys->s.renderer = true;
+
+    msg_Info( p_this, "Opening Avahi Renderer SD");
+    return OpenCommon( &p_rd->p_sys->s );
+}
+
 /*****************************************************************************
  * Close: cleanup
  *****************************************************************************/
-static void Close( vlc_object_t *p_this )
+static void CloseCommon( discovery_sys_t *p_sys )
 {
-    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
     avahi_threaded_poll_stop( p_sys->poll );
 
     avahi_client_free( p_sys->client );
@@ -348,3 +505,18 @@ static void Close( vlc_object_t *p_this )
     vlc_dictionary_clear( &p_sys->services_name_to_input_item, NULL, NULL );
     free( p_sys );
 }
+
+static void Close( vlc_object_t *p_this )
+{
+    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
+    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    CloseCommon( &p_sys->s );
+}
+
+static void CloseRD( vlc_object_t *p_this )
+{
+    msg_Info( p_this, "Closing Avahi Renderer SD");
+    vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *)p_this;
+    vlc_renderer_discovery_sys *p_sys = p_rd->p_sys;
+    CloseCommon( &p_sys->s );
+}
-- 
2.20.1

openSUSE Build Service is sponsored by