mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	zeroconf: use local icon for shared devices
systemd-hostnamed provides an icon for the machine it is running on.
If it is running, module-zeroconf-publish uses this icon for the
'icon-name' attribute in the Avahi properties. module-zeroconf-discover
passes this icon to module-tunnel using the module parameter
{sink/source}_properties.
This allows to display a portable, desktop or phone instead of
the generic sound card icon.
			
			
This commit is contained in:
		
							parent
							
								
									83f0a34ea6
								
							
						
					
					
						commit
						963b3ea695
					
				
					 3 changed files with 93 additions and 4 deletions
				
			
		| 
						 | 
					@ -1899,8 +1899,8 @@ module_solaris_la_LIBADD = $(MODULE_LIBADD)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c
 | 
					module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c
 | 
				
			||||||
module_zeroconf_publish_la_LDFLAGS = $(MODULE_LDFLAGS)
 | 
					module_zeroconf_publish_la_LDFLAGS = $(MODULE_LDFLAGS)
 | 
				
			||||||
module_zeroconf_publish_la_LIBADD = $(MODULE_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libprotocol-native.la
 | 
					module_zeroconf_publish_la_LIBADD = $(MODULE_LIBADD) $(AVAHI_LIBS) $(DBUS_LIBS) libavahi-wrap.la libprotocol-native.la
 | 
				
			||||||
module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
 | 
					module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) $(DBUS_CFLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c
 | 
					module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c
 | 
				
			||||||
module_zeroconf_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
 | 
					module_zeroconf_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,6 +149,7 @@ static void resolver_cb(
 | 
				
			||||||
        const char *t;
 | 
					        const char *t;
 | 
				
			||||||
        char *if_suffix = NULL;
 | 
					        char *if_suffix = NULL;
 | 
				
			||||||
        char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
 | 
					        char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
 | 
				
			||||||
 | 
					        char *properties = NULL;
 | 
				
			||||||
        pa_sample_spec ss;
 | 
					        pa_sample_spec ss;
 | 
				
			||||||
        pa_channel_map cm;
 | 
					        pa_channel_map cm;
 | 
				
			||||||
        AvahiStringList *l;
 | 
					        AvahiStringList *l;
 | 
				
			||||||
| 
						 | 
					@ -172,6 +173,8 @@ static void resolver_cb(
 | 
				
			||||||
                ss.channels = (uint8_t) atoi(value);
 | 
					                ss.channels = (uint8_t) atoi(value);
 | 
				
			||||||
            else if (pa_streq(key, "format"))
 | 
					            else if (pa_streq(key, "format"))
 | 
				
			||||||
                ss.format = pa_parse_sample_format(value);
 | 
					                ss.format = pa_parse_sample_format(value);
 | 
				
			||||||
 | 
					            else if (pa_streq(key, "icon-name"))
 | 
				
			||||||
 | 
					                properties = pa_sprintf_malloc("device.icon_name=%s", value);
 | 
				
			||||||
            else if (pa_streq(key, "channel_map")) {
 | 
					            else if (pa_streq(key, "channel_map")) {
 | 
				
			||||||
                pa_channel_map_parse(&cm, value);
 | 
					                pa_channel_map_parse(&cm, value);
 | 
				
			||||||
                channel_map_set = true;
 | 
					                channel_map_set = true;
 | 
				
			||||||
| 
						 | 
					@ -187,12 +190,14 @@ static void resolver_cb(
 | 
				
			||||||
        if (!pa_sample_spec_valid(&ss)) {
 | 
					        if (!pa_sample_spec_valid(&ss)) {
 | 
				
			||||||
            pa_log("Service '%s' contains an invalid sample specification.", name);
 | 
					            pa_log("Service '%s' contains an invalid sample specification.", name);
 | 
				
			||||||
            avahi_free(device);
 | 
					            avahi_free(device);
 | 
				
			||||||
 | 
					            pa_xfree(properties);
 | 
				
			||||||
            goto finish;
 | 
					            goto finish;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
 | 
					        if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
 | 
				
			||||||
            pa_log("Service '%s' contains an invalid channel map.", name);
 | 
					            pa_log("Service '%s' contains an invalid channel map.", name);
 | 
				
			||||||
            avahi_free(device);
 | 
					            avahi_free(device);
 | 
				
			||||||
 | 
					            pa_xfree(properties);
 | 
				
			||||||
            goto finish;
 | 
					            goto finish;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -205,6 +210,7 @@ static void resolver_cb(
 | 
				
			||||||
            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
 | 
					            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
 | 
				
			||||||
            avahi_free(device);
 | 
					            avahi_free(device);
 | 
				
			||||||
            pa_xfree(dname);
 | 
					            pa_xfree(dname);
 | 
				
			||||||
 | 
					            pa_xfree(properties);
 | 
				
			||||||
            goto finish;
 | 
					            goto finish;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -220,6 +226,7 @@ static void resolver_cb(
 | 
				
			||||||
                                 "format=%s "
 | 
					                                 "format=%s "
 | 
				
			||||||
                                 "channels=%u "
 | 
					                                 "channels=%u "
 | 
				
			||||||
                                 "rate=%u "
 | 
					                                 "rate=%u "
 | 
				
			||||||
 | 
					                                 "%s_properties=%s "
 | 
				
			||||||
                                 "%s_name=%s "
 | 
					                                 "%s_name=%s "
 | 
				
			||||||
                                 "channel_map=%s",
 | 
					                                 "channel_map=%s",
 | 
				
			||||||
                                 avahi_address_snprint(at, sizeof(at), a),
 | 
					                                 avahi_address_snprint(at, sizeof(at), a),
 | 
				
			||||||
| 
						 | 
					@ -228,6 +235,7 @@ static void resolver_cb(
 | 
				
			||||||
                                 pa_sample_format_to_string(ss.format),
 | 
					                                 pa_sample_format_to_string(ss.format),
 | 
				
			||||||
                                 ss.channels,
 | 
					                                 ss.channels,
 | 
				
			||||||
                                 ss.rate,
 | 
					                                 ss.rate,
 | 
				
			||||||
 | 
					                                 t, properties ? properties : "",
 | 
				
			||||||
                                 t, dname,
 | 
					                                 t, dname,
 | 
				
			||||||
                                 pa_channel_map_snprint(cmt, sizeof(cmt), &cm));
 | 
					                                 pa_channel_map_snprint(cmt, sizeof(cmt), &cm));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -243,6 +251,7 @@ static void resolver_cb(
 | 
				
			||||||
        pa_xfree(dname);
 | 
					        pa_xfree(dname);
 | 
				
			||||||
        pa_xfree(args);
 | 
					        pa_xfree(args);
 | 
				
			||||||
        pa_xfree(if_suffix);
 | 
					        pa_xfree(if_suffix);
 | 
				
			||||||
 | 
					        pa_xfree(properties);
 | 
				
			||||||
        avahi_free(device);
 | 
					        avahi_free(device);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,14 @@
 | 
				
			||||||
#include <pulsecore/avahi-wrap.h>
 | 
					#include <pulsecore/avahi-wrap.h>
 | 
				
			||||||
#include <pulsecore/protocol-native.h>
 | 
					#include <pulsecore/protocol-native.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_DBUS
 | 
				
			||||||
 | 
					#include <pulsecore/dbus-shared.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HOSTNAME_DBUS_INTERFACE "org.freedesktop.hostname1"
 | 
				
			||||||
 | 
					#define HOSTNAME_DBUS_PATH "/org/freedesktop/hostname1"
 | 
				
			||||||
 | 
					#define HOSTNAME_DBUS_ICON_PROPERTY "IconName"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "module-zeroconf-publish-symdef.h"
 | 
					#include "module-zeroconf-publish-symdef.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PA_MODULE_AUTHOR("Lennart Poettering");
 | 
					PA_MODULE_AUTHOR("Lennart Poettering");
 | 
				
			||||||
| 
						 | 
					@ -133,6 +141,7 @@ struct userdata {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pa_hashmap *services; /* protect with mainloop lock */
 | 
					    pa_hashmap *services; /* protect with mainloop lock */
 | 
				
			||||||
    char *service_name;
 | 
					    char *service_name;
 | 
				
			||||||
 | 
					    char *icon_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    AvahiEntryGroup *main_entry_group;
 | 
					    AvahiEntryGroup *main_entry_group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -308,8 +317,6 @@ static void publish_service(pa_mainloop_api *api PA_GCC_UNUSED, void *service) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)))
 | 
					    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)))
 | 
				
			||||||
        txt = avahi_string_list_add_pair(txt, "description", t);
 | 
					        txt = avahi_string_list_add_pair(txt, "description", t);
 | 
				
			||||||
    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_ICON_NAME)))
 | 
					 | 
				
			||||||
        txt = avahi_string_list_add_pair(txt, "icon-name", t);
 | 
					 | 
				
			||||||
    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_VENDOR_NAME)))
 | 
					    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_VENDOR_NAME)))
 | 
				
			||||||
        txt = avahi_string_list_add_pair(txt, "vendor-name", t);
 | 
					        txt = avahi_string_list_add_pair(txt, "vendor-name", t);
 | 
				
			||||||
    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
 | 
					    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
 | 
				
			||||||
| 
						 | 
					@ -319,6 +326,12 @@ static void publish_service(pa_mainloop_api *api PA_GCC_UNUSED, void *service) {
 | 
				
			||||||
    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_FORM_FACTOR)))
 | 
					    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_FORM_FACTOR)))
 | 
				
			||||||
        txt = avahi_string_list_add_pair(txt, "form-factor", t);
 | 
					        txt = avahi_string_list_add_pair(txt, "form-factor", t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (s->userdata->icon_name) {
 | 
				
			||||||
 | 
					        txt = avahi_string_list_add_pair(txt, "icon-name", s->userdata->icon_name);
 | 
				
			||||||
 | 
					    } else if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_ICON_NAME))) {
 | 
				
			||||||
 | 
					        txt = avahi_string_list_add_pair(txt, "icon-name", t);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (avahi_entry_group_add_service_strlst(
 | 
					    if (avahi_entry_group_add_service_strlst(
 | 
				
			||||||
                s->entry_group,
 | 
					                s->entry_group,
 | 
				
			||||||
                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
 | 
					                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
 | 
				
			||||||
| 
						 | 
					@ -653,6 +666,66 @@ static int avahi_process_msg(pa_msgobject *o, int code, void *data, int64_t offs
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_DBUS
 | 
				
			||||||
 | 
					static char *get_icon_name(pa_module*m) {
 | 
				
			||||||
 | 
					    const char *interface = HOSTNAME_DBUS_INTERFACE;
 | 
				
			||||||
 | 
					    const char *property = HOSTNAME_DBUS_ICON_PROPERTY;
 | 
				
			||||||
 | 
					    char *icon_name;
 | 
				
			||||||
 | 
					    pa_dbus_connection *bus;
 | 
				
			||||||
 | 
					    DBusError error;
 | 
				
			||||||
 | 
					    DBusMessageIter args;
 | 
				
			||||||
 | 
					    DBusMessage *msg = NULL;
 | 
				
			||||||
 | 
					    DBusMessage *reply = NULL;
 | 
				
			||||||
 | 
					    DBusConnection *conn = NULL;
 | 
				
			||||||
 | 
					    DBusMessageIter sub;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!(bus = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error))) {
 | 
				
			||||||
 | 
					        pa_log("Failed to get system bus connection: %s", error.message);
 | 
				
			||||||
 | 
					        goto out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    conn = pa_dbus_connection_get(bus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg = dbus_message_new_method_call(HOSTNAME_DBUS_INTERFACE,
 | 
				
			||||||
 | 
					                                       HOSTNAME_DBUS_PATH,
 | 
				
			||||||
 | 
					                                       "org.freedesktop.DBus.Properties",
 | 
				
			||||||
 | 
					                                       "Get");
 | 
				
			||||||
 | 
					    dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dbus_error_init(&error);
 | 
				
			||||||
 | 
					    if ((reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &error)) == NULL) {
 | 
				
			||||||
 | 
					        pa_log("Failed to send: %s:%s", error.name, error.message);
 | 
				
			||||||
 | 
					        dbus_error_free(&error);
 | 
				
			||||||
 | 
					        goto out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dbus_message_iter_init(reply, &args);
 | 
				
			||||||
 | 
					    if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) {
 | 
				
			||||||
 | 
					        pa_log("Incorrect reply type");
 | 
				
			||||||
 | 
					        goto out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dbus_message_iter_recurse(&args, &sub);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
 | 
				
			||||||
 | 
					        pa_log("Incorrect value type");
 | 
				
			||||||
 | 
					        goto out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dbus_message_iter_get_basic(&sub, &icon_name);
 | 
				
			||||||
 | 
					    icon_name = pa_xstrdup(icon_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
					    if (reply)
 | 
				
			||||||
 | 
					        dbus_message_unref(reply);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (msg)
 | 
				
			||||||
 | 
					        dbus_message_unref(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return icon_name;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Runs in Avahi mainloop context */
 | 
					/* Runs in Avahi mainloop context */
 | 
				
			||||||
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
 | 
					static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
 | 
				
			||||||
    struct userdata *u = userdata;
 | 
					    struct userdata *u = userdata;
 | 
				
			||||||
| 
						 | 
					@ -666,6 +739,12 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
 | 
				
			||||||
        case AVAHI_CLIENT_S_RUNNING:
 | 
					        case AVAHI_CLIENT_S_RUNNING:
 | 
				
			||||||
            /* Collect all sinks/sources, and publish them */
 | 
					            /* Collect all sinks/sources, and publish them */
 | 
				
			||||||
            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_PUBLISH_ALL, u, 0, NULL, NULL);
 | 
					            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_PUBLISH_ALL, u, 0, NULL, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_DBUS
 | 
				
			||||||
 | 
					            /* Request icon name through D-BUS */
 | 
				
			||||||
 | 
					            u->icon_name = get_icon_name(u->module);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case AVAHI_CLIENT_S_COLLISION:
 | 
					        case AVAHI_CLIENT_S_COLLISION:
 | 
				
			||||||
| 
						 | 
					@ -844,5 +923,6 @@ void pa__done(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pa_xfree(u->msg);
 | 
					    pa_xfree(u->msg);
 | 
				
			||||||
    pa_xfree(u->service_name);
 | 
					    pa_xfree(u->service_name);
 | 
				
			||||||
 | 
					    pa_xfree(u->icon_name);
 | 
				
			||||||
    pa_xfree(u);
 | 
					    pa_xfree(u);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue