mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-03-24 09:05:54 -04:00
modules: port rtp-session to new zeroconf helper
This commit is contained in:
parent
a1db2b8d35
commit
db713c8264
1 changed files with 154 additions and 279 deletions
|
|
@ -27,12 +27,7 @@
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <pipewire/impl.h>
|
#include <pipewire/impl.h>
|
||||||
|
|
||||||
#include <avahi-client/publish.h>
|
#include "zeroconf-utils/zeroconf.h"
|
||||||
#include <avahi-client/lookup.h>
|
|
||||||
#include <avahi-common/error.h>
|
|
||||||
#include <avahi-common/malloc.h>
|
|
||||||
|
|
||||||
#include "zeroconf-utils/avahi-poll.h"
|
|
||||||
|
|
||||||
#include <module-rtp/rtp.h>
|
#include <module-rtp/rtp.h>
|
||||||
#include <module-rtp/apple-midi.h>
|
#include <module-rtp/apple-midi.h>
|
||||||
|
|
@ -160,31 +155,21 @@ static const struct spa_dict_item module_info[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct service_info {
|
struct service_info {
|
||||||
AvahiIfIndex interface;
|
int ifindex;
|
||||||
AvahiProtocol protocol;
|
int protocol;
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *type;
|
const char *type;
|
||||||
const char *domain;
|
const char *domain;
|
||||||
const char *host_name;
|
|
||||||
AvahiAddress address;
|
|
||||||
uint16_t port;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SERVICE_INFO(...) ((struct service_info){ __VA_ARGS__ })
|
#define SERVICE_INFO(...) ((struct service_info){ __VA_ARGS__ })
|
||||||
|
|
||||||
struct service {
|
|
||||||
struct service_info info;
|
|
||||||
|
|
||||||
struct spa_list link;
|
|
||||||
struct impl *impl;
|
|
||||||
|
|
||||||
struct session *sess;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct session {
|
struct session {
|
||||||
struct impl *impl;
|
struct impl *impl;
|
||||||
struct spa_list link;
|
struct spa_list link;
|
||||||
|
|
||||||
|
struct service_info info;
|
||||||
|
|
||||||
struct sockaddr_storage ctrl_addr;
|
struct sockaddr_storage ctrl_addr;
|
||||||
socklen_t ctrl_len;
|
socklen_t ctrl_len;
|
||||||
struct sockaddr_storage data_addr;
|
struct sockaddr_storage data_addr;
|
||||||
|
|
@ -229,11 +214,8 @@ struct impl {
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
|
|
||||||
bool discover_local;
|
bool discover_local;
|
||||||
AvahiPoll *avahi_poll;
|
struct pw_zeroconf *zeroconf;
|
||||||
AvahiClient *client;
|
struct spa_hook zeroconf_listener;
|
||||||
AvahiServiceBrowser *browser;
|
|
||||||
AvahiEntryGroup *group;
|
|
||||||
struct spa_list service_list;
|
|
||||||
|
|
||||||
struct pw_properties *stream_props;
|
struct pw_properties *stream_props;
|
||||||
|
|
||||||
|
|
@ -595,6 +577,9 @@ static void free_session(struct session *sess)
|
||||||
if (sess->recv)
|
if (sess->recv)
|
||||||
rtp_stream_destroy(sess->recv);
|
rtp_stream_destroy(sess->recv);
|
||||||
free(sess->name);
|
free(sess->name);
|
||||||
|
free((char *) sess->info.name);
|
||||||
|
free((char *) sess->info.type);
|
||||||
|
free((char *) sess->info.domain);
|
||||||
free(sess);
|
free(sess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -612,7 +597,8 @@ static bool cmp_ip(const struct sockaddr_storage *sa, const struct sockaddr_stor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct session *make_session(struct impl *impl, struct pw_properties *props)
|
static struct session *make_session(struct impl *impl, struct service_info *info,
|
||||||
|
struct pw_properties *props)
|
||||||
{
|
{
|
||||||
struct session *sess;
|
struct session *sess;
|
||||||
const char *str;
|
const char *str;
|
||||||
|
|
@ -627,6 +613,11 @@ static struct session *make_session(struct impl *impl, struct pw_properties *pro
|
||||||
|
|
||||||
sess->impl = impl;
|
sess->impl = impl;
|
||||||
sess->ssrc = pw_rand32();
|
sess->ssrc = pw_rand32();
|
||||||
|
sess->info.ifindex = info->ifindex;
|
||||||
|
sess->info.protocol = info->protocol;
|
||||||
|
sess->info.name = strdup(info->name);
|
||||||
|
sess->info.type = strdup(info->type);
|
||||||
|
sess->info.domain = strdup(info->domain);
|
||||||
|
|
||||||
str = pw_properties_get(props, "sess.name");
|
str = pw_properties_get(props, "sess.name");
|
||||||
sess->name = str ? strdup(str) : strdup("RTP Session");
|
sess->name = str ? strdup(str) : strdup("RTP Session");
|
||||||
|
|
@ -669,6 +660,21 @@ error:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct session *find_session_by_info(struct impl *impl,
|
||||||
|
const struct service_info *info)
|
||||||
|
{
|
||||||
|
struct session *s;
|
||||||
|
spa_list_for_each(s, &impl->sessions, link) {
|
||||||
|
if (s->info.ifindex == info->ifindex &&
|
||||||
|
s->info.protocol == info->protocol &&
|
||||||
|
spa_streq(s->info.name, info->name) &&
|
||||||
|
spa_streq(s->info.type, info->type) &&
|
||||||
|
spa_streq(s->info.domain, info->domain))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static struct session *find_session_by_addr_name(struct impl *impl,
|
static struct session *find_session_by_addr_name(struct impl *impl,
|
||||||
const struct sockaddr_storage *sa, const char *name)
|
const struct sockaddr_storage *sa, const char *name)
|
||||||
{
|
{
|
||||||
|
|
@ -1220,8 +1226,8 @@ static void impl_destroy(struct impl *impl)
|
||||||
if (impl->data_source)
|
if (impl->data_source)
|
||||||
pw_loop_destroy_source(impl->data_loop, impl->data_source);
|
pw_loop_destroy_source(impl->data_loop, impl->data_source);
|
||||||
|
|
||||||
if (impl->client)
|
if (impl->zeroconf)
|
||||||
avahi_client_free(impl->client);
|
pw_zeroconf_destroy(impl->zeroconf);
|
||||||
|
|
||||||
if (impl->data_loop)
|
if (impl->data_loop)
|
||||||
pw_context_release_loop(impl->context, impl->data_loop);
|
pw_context_release_loop(impl->context, impl->data_loop);
|
||||||
|
|
@ -1263,20 +1269,6 @@ static const struct pw_core_events core_events = {
|
||||||
.error = on_core_error,
|
.error = on_core_error,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void free_service(struct service *s)
|
|
||||||
{
|
|
||||||
spa_list_remove(&s->link);
|
|
||||||
|
|
||||||
if (s->sess)
|
|
||||||
free_session(s->sess);
|
|
||||||
|
|
||||||
free((char *) s->info.name);
|
|
||||||
free((char *) s->info.type);
|
|
||||||
free((char *) s->info.domain);
|
|
||||||
free((char *) s->info.host_name);
|
|
||||||
free(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *get_service_name(struct impl *impl)
|
static const char *get_service_name(struct impl *impl)
|
||||||
{
|
{
|
||||||
const char *str;
|
const char *str;
|
||||||
|
|
@ -1288,21 +1280,36 @@ static const char *get_service_name(struct impl *impl)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct service *make_service(struct impl *impl, const struct service_info *info,
|
static void on_zeroconf_added(void *data, void *user, const struct spa_dict *info)
|
||||||
AvahiStringList *txt)
|
|
||||||
{
|
{
|
||||||
struct service *s = NULL;
|
struct impl *impl = data;
|
||||||
char at[AVAHI_ADDRESS_STR_MAX], if_suffix[16] = "";
|
const char *str, *service_name, *address, *hostname;
|
||||||
|
struct service_info sinfo;
|
||||||
struct session *sess;
|
struct session *sess;
|
||||||
int res, ipv;
|
int ifindex = -1, protocol = 0, res, port = 0;
|
||||||
struct pw_properties *props = NULL;
|
struct pw_properties *props = NULL;
|
||||||
const char *service_name, *str;
|
|
||||||
AvahiStringList *l;
|
|
||||||
bool compatible = true;
|
bool compatible = true;
|
||||||
|
|
||||||
|
if ((str = spa_dict_lookup(info, "zeroconf.ifindex")))
|
||||||
|
ifindex = atoi(str);
|
||||||
|
if ((str = spa_dict_lookup(info, "zeroconf.protocol")))
|
||||||
|
protocol = atoi(str);
|
||||||
|
if ((str = spa_dict_lookup(info, "zeroconf.port")))
|
||||||
|
port = atoi(str);
|
||||||
|
|
||||||
|
sinfo = SERVICE_INFO(.ifindex = ifindex,
|
||||||
|
.protocol = protocol,
|
||||||
|
.name = spa_dict_lookup(info, "zeroconf.session"),
|
||||||
|
.type = spa_dict_lookup(info, "zeroconf.service"),
|
||||||
|
.domain = spa_dict_lookup(info, "zeroconf.domain"));
|
||||||
|
|
||||||
|
sess = find_session_by_info(impl, &sinfo);
|
||||||
|
if (sess != NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
/* check for compatible session */
|
/* check for compatible session */
|
||||||
service_name = get_service_name(impl);
|
service_name = get_service_name(impl);
|
||||||
compatible = spa_streq(service_name, info->type);
|
compatible = spa_streq(service_name, sinfo.type);
|
||||||
|
|
||||||
props = pw_properties_copy(impl->stream_props);
|
props = pw_properties_copy(impl->stream_props);
|
||||||
if (props == NULL) {
|
if (props == NULL) {
|
||||||
|
|
@ -1312,53 +1319,51 @@ static struct service *make_service(struct impl *impl, const struct service_info
|
||||||
|
|
||||||
if (spa_streq(service_name, "_pipewire-audio._udp")) {
|
if (spa_streq(service_name, "_pipewire-audio._udp")) {
|
||||||
uint32_t mask = 0;
|
uint32_t mask = 0;
|
||||||
for (l = txt; l && compatible; l = l->next) {
|
const struct spa_dict_item *it;
|
||||||
|
spa_dict_for_each(it, info) {
|
||||||
const char *k = NULL;
|
const char *k = NULL;
|
||||||
char *key, *value;
|
|
||||||
|
|
||||||
if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
|
if (!compatible)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (spa_streq(key, "subtype")) {
|
if (spa_streq(it->key, "subtype")) {
|
||||||
k = "sess.media";
|
k = "sess.media";
|
||||||
mask |= 1<<0;
|
mask |= 1<<0;
|
||||||
} else if (spa_streq(key, "format")) {
|
} else if (spa_streq(it->key, "format")) {
|
||||||
k = PW_KEY_AUDIO_FORMAT;
|
k = PW_KEY_AUDIO_FORMAT;
|
||||||
mask |= 1<<1;
|
mask |= 1<<1;
|
||||||
} else if (spa_streq(key, "rate")) {
|
} else if (spa_streq(it->key, "rate")) {
|
||||||
k = PW_KEY_AUDIO_RATE;
|
k = PW_KEY_AUDIO_RATE;
|
||||||
mask |= 1<<2;
|
mask |= 1<<2;
|
||||||
} else if (spa_streq(key, "channels")) {
|
} else if (spa_streq(it->key, "channels")) {
|
||||||
k = PW_KEY_AUDIO_CHANNELS;
|
k = PW_KEY_AUDIO_CHANNELS;
|
||||||
mask |= 1<<3;
|
mask |= 1<<3;
|
||||||
} else if (spa_streq(key, "position")) {
|
} else if (spa_streq(it->key, "position")) {
|
||||||
pw_properties_set(props,
|
pw_properties_set(props,
|
||||||
SPA_KEY_AUDIO_POSITION, value);
|
SPA_KEY_AUDIO_POSITION, it->value);
|
||||||
} else if (spa_streq(key, "layout")) {
|
} else if (spa_streq(it->key, "layout")) {
|
||||||
pw_properties_set(props,
|
pw_properties_set(props,
|
||||||
SPA_KEY_AUDIO_LAYOUT, value);
|
SPA_KEY_AUDIO_LAYOUT, it->value);
|
||||||
} else if (spa_streq(key, "channelnames")) {
|
} else if (spa_streq(it->key, "channelnames")) {
|
||||||
pw_properties_set(props,
|
pw_properties_set(props,
|
||||||
PW_KEY_NODE_CHANNELNAMES, value);
|
PW_KEY_NODE_CHANNELNAMES, it->value);
|
||||||
} else if (spa_streq(key, "ts-refclk")) {
|
} else if (spa_streq(it->key, "ts-refclk")) {
|
||||||
pw_properties_set(props,
|
pw_properties_set(props,
|
||||||
"sess.ts-refclk", value);
|
"sess.ts-refclk", it->value);
|
||||||
if (spa_streq(value, impl->ts_refclk))
|
if (spa_streq(it->value, impl->ts_refclk))
|
||||||
pw_properties_set(props,
|
pw_properties_set(props,
|
||||||
"sess.ts-direct", "true");
|
"sess.ts-direct", "true");
|
||||||
} else if (spa_streq(key, "ts-offset")) {
|
} else if (spa_streq(it->key, "ts-offset")) {
|
||||||
uint32_t v;
|
uint32_t v;
|
||||||
if (spa_atou32(value, &v, 0))
|
if (spa_atou32(it->value, &v, 0))
|
||||||
pw_properties_setf(props,
|
pw_properties_setf(props,
|
||||||
"rtp.receiver-ts-offset", "%u", v);
|
"rtp.receiver-ts-offset", "%u", v);
|
||||||
}
|
}
|
||||||
if (k != NULL) {
|
if (k != NULL) {
|
||||||
str = pw_properties_get(props, k);
|
str = pw_properties_get(props, k);
|
||||||
if (str == NULL || !spa_streq(str, value))
|
if (str == NULL || !spa_streq(str, it->value))
|
||||||
compatible = false;
|
compatible = false;
|
||||||
}
|
}
|
||||||
avahi_free(key);
|
|
||||||
avahi_free(value);
|
|
||||||
}
|
}
|
||||||
str = pw_properties_get(props, "sess.media");
|
str = pw_properties_get(props, "sess.media");
|
||||||
if (spa_streq(str, "opus") && mask != 0xd)
|
if (spa_streq(str, "opus") && mask != 0xd)
|
||||||
|
|
@ -1368,281 +1373,147 @@ static struct service *make_service(struct impl *impl, const struct service_info
|
||||||
}
|
}
|
||||||
if (!compatible) {
|
if (!compatible) {
|
||||||
pw_log_info("found incompatible session IP%d:%s",
|
pw_log_info("found incompatible session IP%d:%s",
|
||||||
info->protocol == AVAHI_PROTO_INET ? 4 : 6,
|
sinfo.protocol, sinfo.name);
|
||||||
info->name);
|
|
||||||
res = 0;
|
res = 0;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = calloc(1, sizeof(*s));
|
address = spa_dict_lookup(info, "zeroconf.address");
|
||||||
if (s == NULL) {
|
hostname = spa_dict_lookup(info, "zeroconf.hostname");
|
||||||
res = -errno;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->impl = impl;
|
pw_log_info("create session: %s %s:%u %s", sinfo.name, address, port, sinfo.type);
|
||||||
spa_list_append(&impl->service_list, &s->link);
|
|
||||||
|
|
||||||
s->info.interface = info->interface;
|
pw_properties_set(props, "sess.name", sinfo.name);
|
||||||
s->info.protocol = info->protocol;
|
pw_properties_set(props, "destination.ip", address);
|
||||||
s->info.name = strdup(info->name);
|
pw_properties_setf(props, "destination.ifindex", "%u", sinfo.ifindex);
|
||||||
s->info.type = strdup(info->type);
|
pw_properties_setf(props, "destination.port", "%u", port);
|
||||||
s->info.domain = strdup(info->domain);
|
|
||||||
s->info.host_name = strdup(info->host_name);
|
|
||||||
s->info.address = info->address;
|
|
||||||
s->info.port = info->port;
|
|
||||||
|
|
||||||
avahi_address_snprint(at, sizeof(at), &s->info.address);
|
|
||||||
pw_log_info("create session: %s %s:%u %s", s->info.name, at, s->info.port, s->info.type);
|
|
||||||
|
|
||||||
if (s->info.protocol == AVAHI_PROTO_INET6 &&
|
|
||||||
s->info.address.data.ipv6.address[0] == 0xfe &&
|
|
||||||
(s->info.address.data.ipv6.address[1] & 0xc0) == 0x80)
|
|
||||||
snprintf(if_suffix, sizeof(if_suffix), "%%%d", s->info.interface);
|
|
||||||
|
|
||||||
ipv = s->info.protocol == AVAHI_PROTO_INET ? 4 : 6;
|
|
||||||
pw_properties_set(props, "sess.name", s->info.name);
|
|
||||||
pw_properties_setf(props, "destination.ip", "%s%s", at, if_suffix);
|
|
||||||
pw_properties_setf(props, "destination.ifindex", "%u", s->info.interface);
|
|
||||||
pw_properties_setf(props, "destination.port", "%u", s->info.port);
|
|
||||||
|
|
||||||
if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
|
if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
|
||||||
pw_properties_setf(props, PW_KEY_NODE_NAME, "rtp_session.%s.%s.ipv%d",
|
pw_properties_setf(props, PW_KEY_NODE_NAME, "rtp_session.%s.%s.ipv%d",
|
||||||
s->info.name, s->info.host_name, ipv);
|
sinfo.name, hostname, sinfo.protocol);
|
||||||
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, "%s (IPv%d)",
|
pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, "%s (IPv%d)",
|
||||||
s->info.name, ipv);
|
sinfo.name, sinfo.protocol);
|
||||||
if (pw_properties_get(props, PW_KEY_MEDIA_NAME) == NULL)
|
if (pw_properties_get(props, PW_KEY_MEDIA_NAME) == NULL)
|
||||||
pw_properties_setf(props, PW_KEY_MEDIA_NAME, "RTP Session with %s (IPv%d)",
|
pw_properties_setf(props, PW_KEY_MEDIA_NAME, "RTP Session with %s (IPv%d)",
|
||||||
s->info.name, ipv);
|
sinfo.name, sinfo.protocol);
|
||||||
|
|
||||||
sess = make_session(impl, props);
|
sess = make_session(impl, &sinfo, spa_steal_ptr(props));
|
||||||
props = NULL;
|
|
||||||
if (sess == NULL) {
|
if (sess == NULL) {
|
||||||
res = -errno;
|
res = -errno;
|
||||||
pw_log_error("can't create session: %m");
|
pw_log_error("can't create session: %m");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
s->sess = sess;
|
|
||||||
|
|
||||||
if ((res = pw_net_parse_address(at, s->info.port, &sess->ctrl_addr, &sess->ctrl_len)) < 0) {
|
if ((res = pw_net_parse_address(address, port, &sess->ctrl_addr, &sess->ctrl_len)) < 0) {
|
||||||
pw_log_error("invalid address %s: %s", at, spa_strerror(res));
|
pw_log_error("invalid address %s: %s", address, spa_strerror(res));
|
||||||
}
|
}
|
||||||
if ((res = pw_net_parse_address(at, s->info.port+1, &sess->data_addr, &sess->data_len)) < 0) {
|
if ((res = pw_net_parse_address(address, port+1, &sess->data_addr, &sess->data_len)) < 0) {
|
||||||
pw_log_error("invalid address %s: %s", at, spa_strerror(res));
|
pw_log_error("invalid address %s: %s", address, spa_strerror(res));
|
||||||
}
|
}
|
||||||
return s;
|
return;
|
||||||
error:
|
error:
|
||||||
pw_properties_free(props);
|
pw_properties_free(props);
|
||||||
if (s != NULL)
|
return;
|
||||||
free_service(s);
|
|
||||||
errno = -res;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct service *find_service(struct impl *impl, const struct service_info *info)
|
static void on_zeroconf_removed(void *data, void *user, const struct spa_dict *info)
|
||||||
{
|
{
|
||||||
struct service *s;
|
struct impl *impl = data;
|
||||||
spa_list_for_each(s, &impl->service_list, link) {
|
|
||||||
if (s->info.interface == info->interface &&
|
|
||||||
s->info.protocol == info->protocol &&
|
|
||||||
spa_streq(s->info.name, info->name) &&
|
|
||||||
spa_streq(s->info.type, info->type) &&
|
|
||||||
spa_streq(s->info.domain, info->domain))
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
|
|
||||||
AvahiResolverEvent event, const char *name, const char *type, const char *domain,
|
|
||||||
const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt,
|
|
||||||
AvahiLookupResultFlags flags, void *userdata)
|
|
||||||
{
|
|
||||||
struct impl *impl = userdata;
|
|
||||||
struct service_info sinfo;
|
struct service_info sinfo;
|
||||||
|
struct session *sess;
|
||||||
|
const char *str;
|
||||||
|
int ifindex = -1, protocol = 0;
|
||||||
|
|
||||||
if (event != AVAHI_RESOLVER_FOUND) {
|
if ((str = spa_dict_lookup(info, "zeroconf.ifindex")))
|
||||||
pw_log_error("Resolving of '%s' failed: %s", name,
|
ifindex = atoi(str);
|
||||||
avahi_strerror(avahi_client_errno(impl->client)));
|
if ((str = spa_dict_lookup(info, "zeroconf.protocol")))
|
||||||
goto done;
|
protocol = atoi(str);
|
||||||
}
|
|
||||||
|
|
||||||
sinfo = SERVICE_INFO(.interface = interface,
|
sinfo = SERVICE_INFO(.ifindex = ifindex,
|
||||||
.protocol = protocol,
|
.protocol = protocol,
|
||||||
.name = name,
|
.name = spa_dict_lookup(info, "zeroconf.session"),
|
||||||
.type = type,
|
.type = spa_dict_lookup(info, "zeroconf.service"),
|
||||||
.domain = domain,
|
.domain = spa_dict_lookup(info, "zeroconf.domain"));
|
||||||
.host_name = host_name,
|
|
||||||
.address = *a,
|
|
||||||
.port = port);
|
|
||||||
|
|
||||||
make_service(impl, &sinfo, txt);
|
sess = find_session_by_info(impl, &sinfo);
|
||||||
done:
|
if (sess == NULL)
|
||||||
avahi_service_resolver_free(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void browser_cb(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol,
|
|
||||||
AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
|
|
||||||
AvahiLookupResultFlags flags, void *userdata)
|
|
||||||
{
|
|
||||||
struct impl *impl = userdata;
|
|
||||||
struct service_info info;
|
|
||||||
struct service *s;
|
|
||||||
|
|
||||||
if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) && !impl->discover_local)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
info = SERVICE_INFO(.interface = interface,
|
free_session(sess);
|
||||||
.protocol = protocol,
|
|
||||||
.name = name,
|
|
||||||
.type = type,
|
|
||||||
.domain = domain);
|
|
||||||
|
|
||||||
s = find_service(impl, &info);
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case AVAHI_BROWSER_NEW:
|
|
||||||
if (s != NULL)
|
|
||||||
return;
|
|
||||||
if (!(avahi_service_resolver_new(impl->client,
|
|
||||||
interface, protocol,
|
|
||||||
name, type, domain,
|
|
||||||
AVAHI_PROTO_UNSPEC, 0,
|
|
||||||
resolver_cb, impl)))
|
|
||||||
pw_log_error("can't make service resolver: %s",
|
|
||||||
avahi_strerror(avahi_client_errno(impl->client)));
|
|
||||||
break;
|
|
||||||
case AVAHI_BROWSER_REMOVE:
|
|
||||||
if (s == NULL)
|
|
||||||
return;
|
|
||||||
free_service(s);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int make_browser(struct impl *impl)
|
static int make_browser(struct impl *impl)
|
||||||
{
|
{
|
||||||
const char *service_name;
|
const char *service_name;
|
||||||
|
int res;
|
||||||
|
|
||||||
service_name = get_service_name(impl);
|
service_name = get_service_name(impl);
|
||||||
if (service_name == NULL)
|
if (service_name == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (impl->browser == NULL) {
|
if ((res = pw_zeroconf_set_browse(impl->zeroconf, impl,
|
||||||
impl->browser = avahi_service_browser_new(impl->client,
|
&SPA_DICT_ITEMS(
|
||||||
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
|
SPA_DICT_ITEM("zeroconf.service", service_name)))) < 0) {
|
||||||
service_name, NULL, 0,
|
pw_log_error("can't make browser for %s: %s",
|
||||||
browser_cb, impl);
|
service_name, spa_strerror(res));
|
||||||
}
|
return res;
|
||||||
if (impl->browser == NULL) {
|
|
||||||
pw_log_error("can't make browser: %s",
|
|
||||||
avahi_strerror(avahi_client_errno(impl->client)));
|
|
||||||
return -EIO;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata)
|
|
||||||
{
|
|
||||||
switch (state) {
|
|
||||||
case AVAHI_ENTRY_GROUP_ESTABLISHED:
|
|
||||||
pw_log_info("Service successfully established");
|
|
||||||
break;
|
|
||||||
case AVAHI_ENTRY_GROUP_COLLISION:
|
|
||||||
pw_log_error("Service name collision");
|
|
||||||
break;
|
|
||||||
case AVAHI_ENTRY_GROUP_FAILURE:
|
|
||||||
pw_log_error("Entry group failure: %s",
|
|
||||||
avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
|
|
||||||
break;
|
|
||||||
case AVAHI_ENTRY_GROUP_UNCOMMITED:
|
|
||||||
case AVAHI_ENTRY_GROUP_REGISTERING:;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int make_announce(struct impl *impl)
|
static int make_announce(struct impl *impl)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
const char *service_name, *str;
|
const char *service_name, *str;
|
||||||
AvahiStringList *txt = NULL;
|
struct pw_properties *props;
|
||||||
|
|
||||||
|
props = pw_properties_new(NULL, NULL);
|
||||||
|
|
||||||
if ((service_name = get_service_name(impl)) == NULL)
|
if ((service_name = get_service_name(impl)) == NULL)
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
if (impl->group == NULL) {
|
|
||||||
impl->group = avahi_entry_group_new(impl->client,
|
|
||||||
entry_group_callback, impl);
|
|
||||||
}
|
|
||||||
if (impl->group == NULL) {
|
|
||||||
pw_log_error("can't make group: %s",
|
|
||||||
avahi_strerror(avahi_client_errno(impl->client)));
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
avahi_entry_group_reset(impl->group);
|
|
||||||
|
|
||||||
if (spa_streq(service_name, "_pipewire-audio._udp")) {
|
if (spa_streq(service_name, "_pipewire-audio._udp")) {
|
||||||
str = pw_properties_get(impl->props, "sess.media");
|
str = pw_properties_get(impl->props, "sess.media");
|
||||||
txt = avahi_string_list_add_pair(txt, "subtype", str);
|
pw_properties_set(props, "subtype", str);
|
||||||
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_FORMAT)) != NULL)
|
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_FORMAT)) != NULL)
|
||||||
txt = avahi_string_list_add_pair(txt, "format", str);
|
pw_properties_set(props, "format", str);
|
||||||
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_RATE)) != NULL)
|
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_RATE)) != NULL)
|
||||||
txt = avahi_string_list_add_pair(txt, "rate", str);
|
pw_properties_set(props, "rate", str);
|
||||||
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_CHANNELS)) != NULL)
|
if ((str = pw_properties_get(impl->stream_props, PW_KEY_AUDIO_CHANNELS)) != NULL)
|
||||||
txt = avahi_string_list_add_pair(txt, "channels", str);
|
pw_properties_set(props, "channels", str);
|
||||||
if ((str = pw_properties_get(impl->stream_props, SPA_KEY_AUDIO_POSITION)) != NULL)
|
if ((str = pw_properties_get(impl->stream_props, SPA_KEY_AUDIO_POSITION)) != NULL)
|
||||||
txt = avahi_string_list_add_pair(txt, "position", str);
|
pw_properties_set(props, "position", str);
|
||||||
if ((str = pw_properties_get(impl->stream_props, SPA_KEY_AUDIO_LAYOUT)) != NULL)
|
if ((str = pw_properties_get(impl->stream_props, SPA_KEY_AUDIO_LAYOUT)) != NULL)
|
||||||
txt = avahi_string_list_add_pair(txt, "layout", str);
|
pw_properties_set(props, "layout", str);
|
||||||
if ((str = pw_properties_get(impl->stream_props, PW_KEY_NODE_CHANNELNAMES)) != NULL)
|
if ((str = pw_properties_get(impl->stream_props, PW_KEY_NODE_CHANNELNAMES)) != NULL)
|
||||||
txt = avahi_string_list_add_pair(txt, "channelnames", str);
|
pw_properties_set(props, "channelnames", str);
|
||||||
if (impl->ts_refclk != NULL) {
|
if (impl->ts_refclk != NULL) {
|
||||||
txt = avahi_string_list_add_pair(txt, "ts-refclk", impl->ts_refclk);
|
pw_properties_set(props, "ts-refclk", impl->ts_refclk);
|
||||||
txt = avahi_string_list_add_printf(txt, "ts-offset=%u", impl->ts_offset);
|
pw_properties_setf(props, "ts-offset", "%u", impl->ts_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res = avahi_entry_group_add_service_strlst(impl->group,
|
|
||||||
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
|
|
||||||
(AvahiPublishFlags)0, impl->session_name,
|
|
||||||
service_name, NULL, NULL,
|
|
||||||
impl->ctrl_port, txt);
|
|
||||||
|
|
||||||
avahi_string_list_free(txt);
|
pw_properties_set(props, "zeroconf.session", impl->session_name);
|
||||||
|
pw_properties_set(props, "zeroconf.service", service_name);
|
||||||
|
pw_properties_setf(props, "zeroconf.port", "%u", impl->ctrl_port);
|
||||||
|
|
||||||
|
res = pw_zeroconf_set_announce(impl->zeroconf, impl, &props->dict);
|
||||||
|
|
||||||
|
pw_properties_free(props);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
pw_log_error("can't add service: %s",
|
pw_log_error("can't add service: %s", spa_strerror(res));
|
||||||
avahi_strerror(avahi_client_errno(impl->client)));
|
return res;
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
if ((res = avahi_entry_group_commit(impl->group)) < 0) {
|
|
||||||
pw_log_error("can't commit group: %s",
|
|
||||||
avahi_strerror(avahi_client_errno(impl->client)));
|
|
||||||
return -EIO;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata)
|
|
||||||
{
|
|
||||||
struct impl *impl = userdata;
|
|
||||||
impl->client = c;
|
|
||||||
|
|
||||||
switch (state) {
|
static const struct pw_zeroconf_events zeroconf_events = {
|
||||||
case AVAHI_CLIENT_S_REGISTERING:
|
PW_VERSION_ZEROCONF_EVENTS,
|
||||||
case AVAHI_CLIENT_S_RUNNING:
|
.added = on_zeroconf_added,
|
||||||
case AVAHI_CLIENT_S_COLLISION:
|
.removed = on_zeroconf_removed,
|
||||||
make_browser(impl);
|
};
|
||||||
make_announce(impl);
|
|
||||||
break;
|
|
||||||
case AVAHI_CLIENT_FAILURE:
|
|
||||||
case AVAHI_CLIENT_CONNECTING:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void copy_props(struct impl *impl, struct pw_properties *props, const char *key)
|
static void copy_props(struct impl *impl, struct pw_properties *props, const char *key)
|
||||||
{
|
{
|
||||||
|
|
@ -1673,7 +1544,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
args = "";
|
args = "";
|
||||||
|
|
||||||
spa_list_init(&impl->sessions);
|
spa_list_init(&impl->sessions);
|
||||||
spa_list_init(&impl->service_list);
|
|
||||||
|
|
||||||
props = pw_properties_new_string(args);
|
props = pw_properties_new_string(args);
|
||||||
if (props == NULL) {
|
if (props == NULL) {
|
||||||
|
|
@ -1685,6 +1555,8 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
|
|
||||||
impl->discover_local = pw_properties_get_bool(impl->props,
|
impl->discover_local = pw_properties_get_bool(impl->props,
|
||||||
"sess.discover-local", false);
|
"sess.discover-local", false);
|
||||||
|
pw_properties_set(impl->props, "zeroconf.discover-local",
|
||||||
|
impl->discover_local ? "true" : "false");
|
||||||
|
|
||||||
stream_props = pw_properties_new(NULL, NULL);
|
stream_props = pw_properties_new(NULL, NULL);
|
||||||
if (stream_props == NULL) {
|
if (stream_props == NULL) {
|
||||||
|
|
@ -1804,14 +1676,17 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
if ((res = setup_apple_session(impl)) < 0)
|
if ((res = setup_apple_session(impl)) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
impl->avahi_poll = pw_avahi_poll_new(impl->context);
|
impl->zeroconf = pw_zeroconf_new(impl->context, &impl->props->dict);
|
||||||
if ((impl->client = avahi_client_new(impl->avahi_poll,
|
if (impl->zeroconf == NULL) {
|
||||||
AVAHI_CLIENT_NO_FAIL,
|
res = -errno;
|
||||||
client_callback, impl,
|
pw_log_error("can't create zeroconf: %m");
|
||||||
&res)) == NULL) {
|
|
||||||
pw_log_error("can't create avahi client: %s", avahi_strerror(res));
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
pw_zeroconf_add_listener(impl->zeroconf, &impl->zeroconf_listener,
|
||||||
|
&zeroconf_events, impl);
|
||||||
|
|
||||||
|
make_browser(impl);
|
||||||
|
make_announce(impl);
|
||||||
|
|
||||||
pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
|
pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue