module-raop: Add transient HomeKit pairing

This commit is contained in:
Christian Glombek 2023-07-30 00:54:56 +02:00
parent 75eb403d15
commit 45a0460722
2 changed files with 432 additions and 37 deletions

View file

@ -64,7 +64,7 @@
* #raop.domain = ""
* #raop.device = ""
* #raop.transport = "udp" | "tcp"
* #raop.encryption.type = "RSA" | "auth_setup" | "none"
* #raop.encryption.type = "RSA" | "auth_setup" | "pair_setup" | "none"
* #raop.audio.codec = "PCM" | "ALAC" | "AAC" | "AAC-ELD"
* #audio.channels = 2
* #audio.format = "S16" | "S24" | "S32"
@ -105,12 +105,14 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
static const struct spa_dict_item module_props[] = {
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
{ PW_KEY_MODULE_DESCRIPTION, "Discover remote streams" },
{ PW_KEY_MODULE_DESCRIPTION, "Discover remote speakers" },
{ PW_KEY_MODULE_USAGE, MODULE_USAGE },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
#define SERVICE_TYPE_SINK "_raop._tcp"
#define SERVICE_TYPE_RAOP "_raop._tcp"
#define SERVICE_TYPE_AP "_airplay._tcp"
struct impl {
struct pw_context *context;
@ -122,7 +124,8 @@ struct impl {
AvahiPoll *avahi_poll;
AvahiClient *client;
AvahiServiceBrowser *sink_browser;
AvahiServiceBrowser *sink_browser_raop;
AvahiServiceBrowser *sink_browser_ap;
struct spa_list tunnel_list;
};
@ -182,8 +185,10 @@ static void impl_free(struct impl *impl)
spa_list_consume(t, &impl->tunnel_list, link)
free_tunnel(t);
if (impl->sink_browser)
avahi_service_browser_free(impl->sink_browser);
if (impl->sink_browser_raop)
avahi_service_browser_free(impl->sink_browser_raop);
if (impl->sink_browser_ap)
avahi_service_browser_free(impl->sink_browser_ap);
if (impl->client)
avahi_client_free(impl->client);
if (impl->avahi_poll)
@ -215,7 +220,30 @@ static bool str_in_list(const char *haystack, const char *delimiters, const char
return false;
}
static void pw_properties_from_avahi_string(const char *key, const char *value,
static void pw_properties_from_ap_txt(const char *key, const char *value,
struct pw_properties *props)
{
if (spa_streq(key, "deviceid")) {
pw_properties_set(props, "raop.device", value);
}
else if (spa_streq(key, "features")) {
pw_properties_set(props, "raop.features", value);
}
else if (spa_streq(key, "flags")) {
pw_properties_set(props, "raop.flags", value);
}
else if (spa_streq(key, "pk")) {
pw_properties_set(props, "raop.pk", value);
}
else if (spa_streq(key, "pi")) {
pw_properties_set(props, "raop.pi", value);
}
else if (spa_streq(key, "psi")) {
pw_properties_set(props, "raop.psi", value);
}
}
static void pw_properties_from_raop_txt(const char *key, const char *value,
struct pw_properties *props)
{
if (spa_streq(key, "device")) {
@ -393,6 +421,37 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
goto done;
}
pw_log_info("mdns service type: %s", type);
if (strcmp(type, SERVICE_TYPE_AP) == 0) {
for (l = txt; l; l = l->next) {
char *key, *value;
if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
break;
pw_properties_from_ap_txt(key, value, props);
avahi_free(key);
avahi_free(value);
}
// TODO lorbus
// if !feature audio goto done
pw_properties_set(props, "raop.encryption.type", "pair_setup");
pw_properties_set(props, "raop.transport", "udp");
} else {
for (l = txt; l; l = l->next) {
char *key, *value;
if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
break;
pw_properties_from_raop_txt(key, value, props);
avahi_free(key);
avahi_free(value);
}
}
avahi_address_snprint(at, sizeof(at), a);
ipv = protocol == AVAHI_PROTO_INET ? 4 : 6;
pw_properties_setf(props, "raop.ip", "%s", at);
@ -402,17 +461,6 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
pw_properties_setf(props, "raop.hostname", "%s", host_name);
pw_properties_setf(props, "raop.domain", "%s", domain);
for (l = txt; l; l = l->next) {
char *key, *value;
if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
break;
pw_properties_from_avahi_string(key, value, props);
avahi_free(key);
avahi_free(value);
}
if ((str = pw_properties_get(impl->properties, "raop.latency.ms")) != NULL)
pw_properties_set(props, "raop.latency.ms", str);
@ -502,9 +550,13 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
case AVAHI_CLIENT_S_REGISTERING:
case AVAHI_CLIENT_S_RUNNING:
case AVAHI_CLIENT_S_COLLISION:
if (impl->sink_browser == NULL)
impl->sink_browser = make_browser(impl, SERVICE_TYPE_SINK);
if (impl->sink_browser == NULL)
if (impl->sink_browser_raop == NULL)
impl->sink_browser_raop = make_browser(impl, SERVICE_TYPE_RAOP);
if (impl->sink_browser_raop == NULL)
goto error;
if (impl->sink_browser_ap == NULL)
impl->sink_browser_ap = make_browser(impl, SERVICE_TYPE_AP);
if (impl->sink_browser_ap == NULL)
goto error;
break;
case AVAHI_CLIENT_FAILURE:
@ -513,9 +565,13 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
SPA_FALLTHROUGH;
case AVAHI_CLIENT_CONNECTING:
if (impl->sink_browser) {
avahi_service_browser_free(impl->sink_browser);
impl->sink_browser = NULL;
if (impl->sink_browser_raop) {
avahi_service_browser_free(impl->sink_browser_raop);
impl->sink_browser_raop = NULL;
}
if (impl->sink_browser_ap) {
avahi_service_browser_free(impl->sink_browser_ap);
impl->sink_browser_ap = NULL;
}
break;
default: