raop: Deduplicate sink creation

Currently, RAOP sinks referencing the same remote ip and port may be created multiple times:
One each for IPv4 and IPv6, times the number of network interfaces used for mDNS discovery.

A recent change added `(IPv4)`and `(IPv6)`identifiers to the sinks' pretty names, however that
is misleading, as often times the service advertised through an mDNSv6 record is actually an
IPv4 service (i.e. the IP reference contained in the IPv6 record may be an IPv4 address).

With this change, sink creation is skipped if a sink with the same advertised name already exists.
This commit is contained in:
Christian Glombek 2023-04-29 20:44:00 +02:00 committed by Wim Taymans
parent 0e831c52d8
commit 0bb0b524c7
2 changed files with 31 additions and 40 deletions

View file

@ -125,12 +125,10 @@ struct impl {
}; };
struct tunnel_info { struct tunnel_info {
AvahiIfIndex interface;
AvahiProtocol protocol;
const char *name; const char *name;
const char *host_name; const char *host_name;
const char *type; const char *ip;
const char *domain; const char *port;
}; };
#define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ }) #define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ })
@ -152,12 +150,10 @@ static struct tunnel *make_tunnel(struct impl *impl, const struct tunnel_info *i
if (t == NULL) if (t == NULL)
return NULL; return NULL;
t->info.interface = info->interface;
t->info.protocol = info->protocol;
t->info.name = strdup(info->name); t->info.name = strdup(info->name);
t->info.host_name = strdup(info->host_name); t->info.host_name = strdup(info->host_name);
t->info.type = strdup(info->type); t->info.ip = strdup(info->ip);
t->info.domain = strdup(info->domain); t->info.port = strdup(info->port);
spa_list_append(&impl->tunnel_list, &t->link); spa_list_append(&impl->tunnel_list, &t->link);
return t; return t;
@ -167,11 +163,7 @@ static struct tunnel *find_tunnel(struct impl *impl, const struct tunnel_info *i
{ {
struct tunnel *t; struct tunnel *t;
spa_list_for_each(t, &impl->tunnel_list, link) { spa_list_for_each(t, &impl->tunnel_list, link) {
if (t->info.interface == info->interface && if (spa_streq(t->info.name, info->name))
t->info.protocol == info->protocol &&
spa_streq(t->info.name, info->name) &&
spa_streq(t->info.type, info->type) &&
spa_streq(t->info.domain, info->domain))
return t; return t;
} }
return NULL; return NULL;
@ -298,8 +290,8 @@ static void submodule_destroy(void *data)
free((char *) t->info.name); free((char *) t->info.name);
free((char *) t->info.host_name); free((char *) t->info.host_name);
free((char *) t->info.type); free((char *) t->info.ip);
free((char *) t->info.domain); free((char *) t->info.port);
free(t); free(t);
} }
@ -385,23 +377,18 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
{ {
struct impl *impl = userdata; struct impl *impl = userdata;
struct tunnel_info tinfo; struct tunnel_info tinfo;
const char *str; const char *str, *port_str;
AvahiStringList *l; AvahiStringList *l;
struct pw_properties *props = NULL; struct pw_properties *props = NULL;
char at[AVAHI_ADDRESS_STR_MAX]; char at[AVAHI_ADDRESS_STR_MAX];
int ipv;
if (event != AVAHI_RESOLVER_FOUND) { if (event != AVAHI_RESOLVER_FOUND) {
pw_log_error("Resolving of '%s' failed: %s", name, pw_log_error("Resolving of '%s' failed: %s", name,
avahi_strerror(avahi_client_errno(impl->client))); avahi_strerror(avahi_client_errno(impl->client)));
goto done; goto done;
} }
tinfo = TUNNEL_INFO(.interface = interface,
.protocol = protocol, avahi_address_snprint(at, sizeof(at), a);
.host_name = host_name,
.name = name,
.type = type,
.domain = domain);
props = pw_properties_new(NULL, NULL); props = pw_properties_new(NULL, NULL);
if (props == NULL) { if (props == NULL) {
@ -409,11 +396,7 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
goto done; goto done;
} }
avahi_address_snprint(at, sizeof(at), a);
ipv = protocol == AVAHI_PROTO_INET ? 4 : 6;
pw_properties_setf(props, "raop.ip", "%s", at); pw_properties_setf(props, "raop.ip", "%s", at);
pw_properties_setf(props, "raop.ip.version", "%d", ipv);
pw_properties_setf(props, "raop.port", "%u", port); pw_properties_setf(props, "raop.port", "%u", port);
pw_properties_setf(props, "raop.name", "%s", name); pw_properties_setf(props, "raop.name", "%s", name);
pw_properties_setf(props, "raop.hostname", "%s", host_name); pw_properties_setf(props, "raop.hostname", "%s", host_name);
@ -430,6 +413,13 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
avahi_free(value); avahi_free(value);
} }
port_str = pw_properties_get(props, "raop.port");
tinfo = TUNNEL_INFO(.name = name,
.host_name = host_name,
.ip = at,
.port = port_str);
if ((str = pw_properties_get(impl->properties, "stream.rules")) == NULL) if ((str = pw_properties_get(impl->properties, "stream.rules")) == NULL)
str = DEFAULT_CREATE_RULES; str = DEFAULT_CREATE_RULES;
if (str != NULL) { if (str != NULL) {
@ -462,18 +452,16 @@ static void browser_cb(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProt
if (flags & AVAHI_LOOKUP_RESULT_LOCAL) if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
return; return;
info = TUNNEL_INFO(.interface = interface, info = TUNNEL_INFO(.name = name);
.protocol = protocol,
.name = name,
.type = type,
.domain = domain);
t = find_tunnel(impl, &info); t = find_tunnel(impl, &info);
switch (event) { switch (event) {
case AVAHI_BROWSER_NEW: case AVAHI_BROWSER_NEW:
if (t != NULL) if (t != NULL) {
pw_log_debug("found duplicate mdns entry - skipping tunnel creation");
return; return;
}
if (!(avahi_service_resolver_new(impl->client, if (!(avahi_service_resolver_new(impl->client,
interface, protocol, interface, protocol,
name, type, domain, name, type, domain,

View file

@ -1861,7 +1861,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
struct pw_context *context = pw_impl_module_get_context(module); struct pw_context *context = pw_impl_module_get_context(module);
struct pw_properties *props = NULL; struct pw_properties *props = NULL;
struct impl *impl; struct impl *impl;
const char *str, *name, *hostname, *ipv; const char *str, *name, *hostname, *ip, *port;
int res; int res;
PW_LOG_TOPIC_INIT(mod_topic); PW_LOG_TOPIC_INIT(mod_topic);
@ -1902,6 +1902,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->context = context; impl->context = context;
impl->loop = pw_context_get_main_loop(context); impl->loop = pw_context_get_main_loop(context);
if ((ip = pw_properties_get(props, "raop.ip")) == NULL)
goto error;
if ((port = pw_properties_get(props, "raop.port")) == NULL)
goto error;
if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
@ -1916,17 +1921,15 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (strlen(str) > 0) if (strlen(str) > 0)
name = str; name = str;
} }
if ((ipv = pw_properties_get(props, "raop.ip.version")) == NULL)
ipv = "4";
if ((hostname = pw_properties_get(props, "raop.hostname")) == NULL) if ((hostname = pw_properties_get(props, "raop.hostname")) == NULL)
hostname = name; hostname = name;
if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
pw_properties_setf(props, PW_KEY_NODE_NAME, "raop_sink.%s.%s.%s",
hostname, ip, port);
if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL) if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION,
"%s (IPv%s)", name, ipv); "%s", name);
if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
pw_properties_setf(props, PW_KEY_NODE_NAME, "raop_sink.%s.ipv%s",
hostname, ipv);
if (pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL) if (pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL)
pw_properties_set(props, PW_KEY_NODE_LATENCY, "352/44100"); pw_properties_set(props, PW_KEY_NODE_LATENCY, "352/44100");