void pa__done(pa_module*m) { struct userdata*u; pa_assert(m); if (!(u = m->userdata)) return; if (u->client) avahi_client_free(u->client); if (u->avahi_poll) pa_avahi_poll_free(u->avahi_poll); if (u->tunnels) { struct tunnel *t; while ((t = pa_hashmap_steal_first(u->tunnels))) { pa_module_unload_request_by_index(u->core, t->module_index, TRUE); tunnel_free(t); } pa_hashmap_free(u->tunnels, NULL, NULL); } pa_xfree(u); }
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 userdata *u = userdata; struct tunnel *t; pa_assert(u); if (flags & AVAHI_LOOKUP_RESULT_LOCAL) return; t = tunnel_new(interface, protocol, name, type, domain); if (event == AVAHI_BROWSER_NEW) { if (!pa_hashmap_get(u->tunnels, t)) if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u))) pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client))); /* We ignore the returned resolver object here, since the we don't * need to attach any special data to it, and we can still destroy * it from the callback */ } else if (event == AVAHI_BROWSER_REMOVE) { struct tunnel *t2; if ((t2 = pa_hashmap_get(u->tunnels, t))) { pa_module_unload_request_by_index(u->core, t2->module_index, TRUE); pa_hashmap_remove(u->tunnels, t2); tunnel_free(t2); } } tunnel_free(t); }
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 userdata *u = userdata; struct tunnel *tnl; char *device = NULL, *nicename, *dname, *vname, *args; char *tp = NULL, *et = NULL, *cn = NULL; char *ch = NULL, *ss = NULL, *sr = NULL; char *t = NULL; char at[AVAHI_ADDRESS_STR_MAX]; AvahiStringList *l; pa_module *m; pa_assert(u); tnl = tunnel_new(interface, protocol, name, type, domain); if (event != AVAHI_RESOLVER_FOUND) { pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client))); goto finish; } if ((nicename = strstr(name, "@"))) { ++nicename; if (strlen(nicename) > 0) { pa_log_debug("Found RAOP: %s", nicename); nicename = pa_escape(nicename, "\"'"); } else nicename = NULL; } for (l = txt; l; l = l->next) { char *key, *value; pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); pa_log_debug("Found key: '%s' with value: '%s'", key, value); if (pa_streq(key, "device")) { device = value; value = NULL; } else if (pa_streq(key, "tp")) { /* Transport protocol: * - TCP = only TCP, * - UDP = only UDP, * - TCP,UDP = both supported (UDP should be prefered) */ pa_xfree(tp); if (pa_str_in_list(value, ",", "UDP")) tp = pa_xstrdup("UDP"); else if (pa_str_in_list(value, ",", "TCP")) tp = pa_xstrdup("TCP"); else tp = pa_xstrdup(value); } else if (pa_streq(key, "et")) { /* Supported encryption types: * - 0 = none, * - 1 = RSA, * - 2 = FairPlay, * - 3 = MFiSAP, * - 4 = FairPlay SAPv2.5. */ pa_xfree(et); if (pa_str_in_list(value, ",", "1")) et = pa_xstrdup("RSA"); else et = pa_xstrdup("none"); } else if (pa_streq(key, "cn")) { /* Suported audio codecs: * - 0 = PCM, * - 1 = ALAC, * - 2 = AAC, * - 3 = AAC ELD. */ pa_xfree(cn); if (pa_str_in_list(value, ",", "1")) cn = pa_xstrdup("ALAC"); else cn = pa_xstrdup("PCM"); } else if (pa_streq(key, "md")) { /* Supported metadata types: * - 0 = text, * - 1 = artwork, * - 2 = progress. */ } else if (pa_streq(key, "pw")) { /* Requires password ? (true/false) */ } else if (pa_streq(key, "ch")) { /* Number of channels */ pa_xfree(ch); ch = pa_xstrdup(value); } else if (pa_streq(key, "ss")) { /* Sample size */ pa_xfree(ss); ss = pa_xstrdup(value); } else if (pa_streq(key, "sr")) { /* Sample rate */ pa_xfree(sr); sr = pa_xstrdup(value); } avahi_free(key); avahi_free(value); } if (device) dname = pa_sprintf_malloc("raop_output.%s.%s", host_name, device); else dname = pa_sprintf_malloc("raop_output.%s", host_name); if (!(vname = pa_namereg_make_valid_name(dname))) { pa_log("Cannot construct valid device name from '%s'.", dname); avahi_free(device); pa_xfree(dname); pa_xfree(tp); pa_xfree(et); pa_xfree(cn); pa_xfree(ch); pa_xfree(ss); pa_xfree(sr); goto finish; } avahi_free(device); pa_xfree(dname); avahi_address_snprint(at, sizeof(at), a); if (nicename) { args = pa_sprintf_malloc("server=[%s]:%u " "sink_name=%s " "sink_properties='device.description=\"%s (%s:%u)\"'", at, port, vname, nicename, at, port); pa_xfree(nicename); } else { args = pa_sprintf_malloc("server=[%s]:%u " "sink_name=%s" "sink_properties='device.description=\"%s:%u\"'", at, port, vname, at, port); } if (tp != NULL) { t = args; args = pa_sprintf_malloc("%s protocol=%s", args, tp); pa_xfree(tp); pa_xfree(t); } if (et != NULL) { t = args; args = pa_sprintf_malloc("%s encryption=%s", args, et); pa_xfree(et); pa_xfree(t); } if (cn != NULL) { t = args; args = pa_sprintf_malloc("%s codec=%s", args, cn); pa_xfree(cn); pa_xfree(t); } if (ch != NULL) { t = args; args = pa_sprintf_malloc("%s channels=%s", args, ch); pa_xfree(ch); pa_xfree(t); } if (ss != NULL) { t = args; args = pa_sprintf_malloc("%s format=%s", args, ss); pa_xfree(ss); pa_xfree(t); } if (sr != NULL) { t = args; args = pa_sprintf_malloc("%s rate=%s", args, sr); pa_xfree(sr); pa_xfree(t); } pa_log_debug("Loading module-raop-sink with arguments '%s'", args); if ((m = pa_module_load(u->core, "module-raop-sink", args))) { tnl->module_index = m->index; pa_hashmap_put(u->tunnels, tnl, tnl); tnl = NULL; } pa_xfree(vname); pa_xfree(args); finish: avahi_service_resolver_free(r); if (tnl) tunnel_free(tnl); }
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 userdata *u = userdata; struct tunnel *tnl; pa_assert(u); tnl = tunnel_new(interface, protocol, name, type, domain); if (event != AVAHI_RESOLVER_FOUND) pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client))); else { char *device = NULL, *dname, *module_name, *args; const char *t; char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_sample_spec ss; pa_channel_map cm; AvahiStringList *l; pa_bool_t channel_map_set = FALSE; pa_module *m; ss = u->core->default_sample_spec; cm = u->core->default_channel_map; for (l = txt; l; l = l->next) { char *key, *value; pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); if (pa_streq(key, "device")) { pa_xfree(device); device = value; value = NULL; } else if (pa_streq(key, "rate")) ss.rate = (uint32_t) atoi(value); else if (pa_streq(key, "channels")) ss.channels = (uint8_t) atoi(value); else if (pa_streq(key, "format")) ss.format = pa_parse_sample_format(value); else if (pa_streq(key, "channel_map")) { pa_channel_map_parse(&cm, value); channel_map_set = TRUE; } avahi_free(key); avahi_free(value); } if (!channel_map_set && cm.channels != ss.channels) pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); if (!pa_sample_spec_valid(&ss)) { pa_log("Service '%s' contains an invalid sample specification.", name); avahi_free(device); goto finish; } if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) { pa_log("Service '%s' contains an invalid channel map.", name); avahi_free(device); goto finish; } if (device) dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device); else dname = pa_sprintf_malloc("tunnel.%s", host_name); if (!pa_namereg_is_valid_name(dname)) { pa_log("Cannot construct valid device name from credentials of service '%s'.", dname); avahi_free(device); pa_xfree(dname); goto finish; } t = strstr(type, "sink") ? "sink" : "source"; module_name = pa_sprintf_malloc("module-tunnel-%s", t); args = pa_sprintf_malloc("server=[%s]:%u " "%s=%s " "format=%s " "channels=%u " "rate=%u " "%s_name=%s " "channel_map=%s", avahi_address_snprint(at, sizeof(at), a), port, t, device, pa_sample_format_to_string(ss.format), ss.channels, ss.rate, t, dname, pa_channel_map_snprint(cmt, sizeof(cmt), &cm)); pa_log_debug("Loading %s with arguments '%s'", module_name, args); if ((m = pa_module_load(u->core, module_name, args))) { tnl->module_index = m->index; pa_hashmap_put(u->tunnels, tnl, tnl); tnl = NULL; } pa_xfree(module_name); pa_xfree(dname); pa_xfree(args); avahi_free(device); } finish: avahi_service_resolver_free(r); if (tnl) tunnel_free(tnl); }
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 userdata *u = userdata; struct tunnel *tnl; pa_assert(u); tnl = tunnel_new(interface, protocol, name, type, domain); if (event != AVAHI_RESOLVER_FOUND) pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client))); else { char *device = NULL, *nicename, *dname, *vname, *args; char at[AVAHI_ADDRESS_STR_MAX]; AvahiStringList *l; pa_module *m; if ((nicename = strstr(name, "@"))) { ++nicename; if (strlen(nicename) > 0) { pa_log_debug("Found RAOP: %s", nicename); nicename = pa_escape(nicename, "\"'"); } else nicename = NULL; } for (l = txt; l; l = l->next) { char *key, *value; pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); pa_log_debug("Found key: '%s' with value: '%s'", key, value); if (pa_streq(key, "device")) { pa_xfree(device); device = value; value = NULL; } avahi_free(key); avahi_free(value); } if (device) dname = pa_sprintf_malloc("raop.%s.%s", host_name, device); else dname = pa_sprintf_malloc("raop.%s", host_name); if (!(vname = pa_namereg_make_valid_name(dname))) { pa_log("Cannot construct valid device name from '%s'.", dname); avahi_free(device); pa_xfree(dname); goto finish; } pa_xfree(dname); if (nicename) { args = pa_sprintf_malloc("server=[%s]:%u " "sink_name=%s " "sink_properties='device.description=\"%s\"'", avahi_address_snprint(at, sizeof(at), a), port, vname, nicename); pa_xfree(nicename); } else { args = pa_sprintf_malloc("server=[%s]:%u " "sink_name=%s", avahi_address_snprint(at, sizeof(at), a), port, vname); } pa_log_debug("Loading module-raop-sink with arguments '%s'", args); if ((m = pa_module_load(u->core, "module-raop-sink", args))) { tnl->module_index = m->index; pa_hashmap_put(u->tunnels, tnl, tnl); tnl = NULL; } pa_xfree(vname); pa_xfree(args); avahi_free(device); } finish: avahi_service_resolver_free(r); if (tnl) tunnel_free(tnl); }