diff options
Diffstat (limited to 'polyp')
-rw-r--r-- | polyp/alsa-util.c | 9 | ||||
-rw-r--r-- | polyp/howl-wrap.c | 116 | ||||
-rw-r--r-- | polyp/howl-wrap.h | 37 | ||||
-rw-r--r-- | polyp/module-match.c | 2 | ||||
-rw-r--r-- | polyp/module-zeroconf-publish.c | 415 |
5 files changed, 573 insertions, 6 deletions
diff --git a/polyp/alsa-util.c b/polyp/alsa-util.c index b6b9ac11..2894c9e8 100644 --- a/polyp/alsa-util.c +++ b/polyp/alsa-util.c @@ -29,6 +29,7 @@ #include "alsa-util.h" #include "sample.h" #include "xmalloc.h" +#include "log.h" /* Set the hardware parameters of the given ALSA device. Returns the * selected fragment settings in *period and *period_size */ @@ -61,14 +62,12 @@ int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const struct pa_sample_spec *ss if (snd_pcm_prepare(pcm_handle) < 0) goto finish; - if (snd_pcm_hw_params_current(pcm_handle, hwparams) < 0) - goto finish; - if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0 || snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL) < 0) goto finish; - - assert(buffer_size > 0 && *period_size > 0); + + assert(buffer_size > 0); + assert(*period_size > 0); *periods = buffer_size / *period_size; assert(*periods > 0); diff --git a/polyp/howl-wrap.c b/polyp/howl-wrap.c new file mode 100644 index 00000000..af050930 --- /dev/null +++ b/polyp/howl-wrap.c @@ -0,0 +1,116 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <assert.h> + +#include "howl-wrap.h" +#include "log.h" +#include "xmalloc.h" +#include "props.h" + +#define HOWL_PROPERTY "howl" + +struct pa_howl_wrapper { + struct pa_core *core; + int ref; + + struct pa_io_event *io_event; + sw_discovery discovery; + +}; + +static void howl_io_event(struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) { + struct pa_howl_wrapper *w = userdata; + assert(m && e && fd >= 0 && w && w->ref >= 1); + + if (f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) + goto fail; + + if (sw_discovery_read_socket(w->discovery) != SW_OKAY) + goto fail; + + return; + +fail: + pa_log(__FILE__": howl connection died.\n"); + w->core->mainloop->io_free(w->io_event); + w->io_event = NULL; +} + +static struct pa_howl_wrapper* howl_wrapper_new(struct pa_core *c) { + struct pa_howl_wrapper *h; + sw_discovery session; + assert(c); + + if (sw_discovery_init(&session) != SW_OKAY) { + pa_log("sw_discovery_init() failed.\n"); + return NULL; + } + + h = pa_xmalloc(sizeof(struct pa_howl_wrapper)); + h->core = c; + h->ref = 1; + h->discovery = session; + + h->io_event = c->mainloop->io_new(c->mainloop, sw_discovery_socket(session), PA_IO_EVENT_INPUT, howl_io_event, h); + + return h; +} + +static void howl_wrapper_free(struct pa_howl_wrapper *h) { + assert(h); + + sw_discovery_fina(h->discovery); + + if (h->io_event) + h->core->mainloop->io_free(h->io_event); + + pa_xfree(h); +} + +struct pa_howl_wrapper* pa_howl_wrapper_get(struct pa_core *c) { + struct pa_howl_wrapper *h; + assert(c); + + if ((h = pa_property_get(c, HOWL_PROPERTY))) + return pa_howl_wrapper_ref(h); + + return howl_wrapper_new(c); +} + +struct pa_howl_wrapper* pa_howl_wrapper_ref(struct pa_howl_wrapper *h) { + assert(h && h->ref >= 1); + h->ref++; + return h; +} + +void pa_howl_wrapper_unref(struct pa_howl_wrapper *h) { + assert(h && h->ref >= 1); + if (!(--h->ref)) + howl_wrapper_free(h); +} + +sw_discovery pa_howl_wrapper_get_discovery(struct pa_howl_wrapper *h) { + assert(h && h->ref >= 1); + + return h->discovery; +} + diff --git a/polyp/howl-wrap.h b/polyp/howl-wrap.h new file mode 100644 index 00000000..feb54556 --- /dev/null +++ b/polyp/howl-wrap.h @@ -0,0 +1,37 @@ +#ifndef foohowlwrapperhfoo +#define foohowlwrapperhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <howl.h> + +#include "core.h" + +struct pa_howl_wrapper; + +struct pa_howl_wrapper* pa_howl_wrapper_get(struct pa_core *c); +struct pa_howl_wrapper* pa_howl_wrapper_ref(struct pa_howl_wrapper *h); +void pa_howl_wrapper_unref(struct pa_howl_wrapper *h); + +sw_discovery pa_howl_wrapper_get_discovery(struct pa_howl_wrapper *h); + +#endif diff --git a/polyp/module-match.c b/polyp/module-match.c index 7fdc5f3e..9d969b31 100644 --- a/polyp/module-match.c +++ b/polyp/module-match.c @@ -171,7 +171,7 @@ static void callback(struct pa_core *c, enum pa_subscription_event_type t, uint3 for (r = u->rules; r; r = r->next) { if (!regexec(&r->regex, si->name, 0, NULL, 0)) { - pa_log(__FILE__": changing volume of sink input '%s' to 0x%03x\n", si->name, r->volume); + pa_log_debug(__FILE__": changing volume of sink input '%s' to 0x%03x\n", si->name, r->volume); pa_sink_input_set_volume(si, r->volume); } } diff --git a/polyp/module-zeroconf-publish.c b/polyp/module-zeroconf-publish.c new file mode 100644 index 00000000..6eee143b --- /dev/null +++ b/polyp/module-zeroconf-publish.c @@ -0,0 +1,415 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "module-zeroconf-publish-symdef.h" +#include "howl-wrap.h" +#include "xmalloc.h" +#include "autoload.h" +#include "sink.h" +#include "source.h" +#include "native-common.h" +#include "util.h" +#include "log.h" +#include "subscribe.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher") +PA_MODULE_VERSION(PACKAGE_VERSION) + +#define SERVICE_NAME_SINK "_polypaudio-sink._tcp" +#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp" +#define SERVICE_NAME_SERVER "_polypaudio-server._tcp" + +struct service { + sw_discovery_oid oid; + char *name; + int published; /* 0 -> not yet registered, 1 -> registered with data from real device, 2 -> registered with data from autoload device */ + + struct { + int valid; + enum pa_namereg_type type; + uint32_t index; + } loaded; + + struct { + int valid; + enum pa_namereg_type type; + uint32_t index; + } autoload; +}; + +struct userdata { + struct pa_core *core; + struct pa_howl_wrapper *howl_wrapper; + struct pa_hashmap *services; + struct pa_subscription *subscription; +}; + +static sw_result publish_reply(sw_discovery discovery, sw_discovery_publish_status status, sw_discovery_oid oid, sw_opaque extra) { + return SW_OKAY; +} + +static void get_service_sample_spec(struct userdata *u, struct service *s, struct pa_sample_spec *ret_ss) { + assert(u && s && s->loaded.valid && ret_ss); + + if (s->loaded.type == PA_NAMEREG_SINK) { + struct pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index); + assert(sink); + *ret_ss = sink->sample_spec; + } else if (s->loaded.type == PA_NAMEREG_SOURCE) { + struct pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index); + assert(source); + *ret_ss = source->sample_spec; + } else + assert(0); +} + +static int publish_service(struct userdata *u, struct service *s) { + assert(u && s); + char t[256]; + char hn[256]; + int r = -1; + sw_text_record txt; + int free_txt = 0; + + if ((s->published == 1 && s->loaded.valid) || + (s->published == 2 && s->autoload.valid && !s->loaded.valid)) + return 0; + + if (s->published) { + sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid); + s->published = 0; + } + + snprintf(t, sizeof(t), "%s@%s", s->name, pa_get_host_name(hn, sizeof(hn))); + + if (sw_text_record_init(&txt) != SW_OKAY) { + pa_log(__FILE__": sw_text_record_init() failed\n"); + goto finish; + } + free_txt = 1; + + sw_text_record_add_key_and_string_value(txt, "device", s->name); + + if (s->loaded.valid) { + char z[64]; + struct pa_sample_spec ss; + + get_service_sample_spec(u, s, &ss); + + snprintf(z, sizeof(z), "%u", ss.rate); + sw_text_record_add_key_and_string_value(txt, "rate", z); + snprintf(z, sizeof(z), "%u", ss.channels); + sw_text_record_add_key_and_string_value(txt, "channels", z); + sw_text_record_add_key_and_string_value(txt, "format", pa_sample_format_to_string(ss.format)); + + if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, + s->loaded.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, + NULL, NULL, PA_NATIVE_DEFAULT_PORT, sw_text_record_bytes(txt), sw_text_record_len(txt), + publish_reply, s, &s->oid) != SW_OKAY) { + pa_log(__FILE__": failed to register sink on zeroconf.\n"); + goto finish; + } + + s->published = 1; + } else if (s->autoload.valid) { + + if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, + s->autoload.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, + NULL, NULL, PA_NATIVE_DEFAULT_PORT, sw_text_record_bytes(txt), sw_text_record_len(txt), + publish_reply, s, &s->oid) != SW_OKAY) { + pa_log(__FILE__": failed to register sink on zeroconf.\n"); + goto finish; + } + + s->published = 2; + } + + r = 0; + +finish: + + if (!s->published) { + /* Remove this service */ + pa_hashmap_remove(u->services, s->name); + pa_xfree(s->name); + pa_xfree(s); + } + + if (free_txt) + sw_text_record_fina(txt); + + return r; +} + +struct service *get_service(struct userdata *u, const char *name) { + struct service *s; + + if ((s = pa_hashmap_get(u->services, name))) + return s; + + s = pa_xmalloc(sizeof(struct service)); + s->published = 0; + s->name = pa_xstrdup(name); + s->loaded.valid = s->autoload.valid = 0; + + pa_hashmap_put(u->services, s->name, s); + + return s; +} + +static int publish_sink(struct userdata *u, struct pa_sink *s) { + struct service *svc; + assert(u && s); + + svc = get_service(u, s->name); + if (svc->loaded.valid) + return 0; + + svc->loaded.valid = 1; + svc->loaded.type = PA_NAMEREG_SINK; + svc->loaded.index = s->index; + + return publish_service(u, svc); +} + +static int publish_source(struct userdata *u, struct pa_source *s) { + struct service *svc; + assert(u && s); + + svc = get_service(u, s->name); + if (svc->loaded.valid) + return 0; + + svc->loaded.valid = 1; + svc->loaded.type = PA_NAMEREG_SOURCE; + svc->loaded.index = s->index; + + return publish_service(u, svc); +} + +static int publish_autoload(struct userdata *u, struct pa_autoload_entry *s) { + struct service *svc; + assert(u && s); + + svc = get_service(u, s->name); + if (svc->autoload.valid) + return 0; + + svc->autoload.valid = 1; + svc->autoload.type = s->type; + svc->autoload.index = s->index; + + return publish_service(u, svc); +} + +static int remove_sink(struct userdata *u, struct pa_sink *s) { + struct service *svc; + assert(u && s); + + if (!(svc = pa_hashmap_get(u->services, s->name))) + return 0; + + if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK) + return 0; + + svc->loaded.valid = 0; + return publish_service(u, svc); +} + +static int remove_source(struct userdata *u, struct pa_source *s) { + struct service *svc; + assert(u && s); + + if (!(svc = pa_hashmap_get(u->services, s->name))) + return 0; + + if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE) + return 0; + + svc->loaded.valid = 0; + return publish_service(u, svc); +} + +static int remove_autoload(struct userdata *u, struct pa_autoload_entry *s) { + struct service *svc; + assert(u && s); + + if (!(svc = pa_hashmap_get(u->services, s->name))) + return 0; + + if (!svc->autoload.valid || svc->autoload.type != s->type) + return 0; + + svc->autoload.valid = 0; + return publish_service(u, svc); +} + +static void subscribe_callback(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) { + struct userdata *u = userdata; + assert(u && c); + + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SINK: { + struct pa_sink *sink; + + pa_log("subscribe: %x\n", t); + + + + + if ((sink = pa_idxset_get_by_index(c->sinks, index))) { + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_log("add\n"); + if (publish_sink(u, sink) < 0) + goto fail; + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_log("remove\n"); + + + if (remove_sink(u, sink) < 0) + goto fail; + } + } + + break; + } + + case PA_SUBSCRIPTION_EVENT_SOURCE: { + struct pa_source *source; + + if ((source = pa_idxset_get_by_index(c->sources, index))) { + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + if (publish_source(u, source) < 0) + goto fail; + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + if (remove_source(u, source) < 0) + goto fail; + } + } + + break; + } + + case PA_SUBSCRIPTION_EVENT_AUTOLOAD: { + struct pa_autoload_entry *autoload; + + if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, index))) { + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + if (publish_autoload(u, autoload) < 0) + goto fail; + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + if (remove_autoload(u, autoload) < 0) + goto fail; + } + } + + break; + } + } + + return; + +fail: + if (u->subscription) { + pa_subscription_free(u->subscription); + u->subscription = NULL; + } +} + +int pa__init(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + uint32_t index; + struct pa_sink *sink; + struct pa_source *source; + struct pa_autoload_entry *autoload; + + m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + + if (!(u->howl_wrapper = pa_howl_wrapper_get(c))) + goto fail; + + u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + u->subscription = pa_subscription_new(c, + PA_SUBSCRIPTION_MASK_SINK| + PA_SUBSCRIPTION_MASK_SOURCE| + PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u); + + for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) + if (publish_sink(u, sink) < 0) + goto fail; + + for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) + if (publish_source(u, source) < 0) + goto fail; + + if (c->autoload_idxset) + for (autoload = pa_idxset_first(c->autoload_idxset, &index); autoload; autoload = pa_idxset_next(c->autoload_idxset, &index)) + if (publish_autoload(u, autoload) < 0) + goto fail; + + return 0; + +fail: + pa__done(c, m); + return -1; +} + +static void service_free(void *p, void *userdata) { + struct service *s = p; + struct userdata *u = userdata; + assert(s && u); + sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid); + pa_xfree(s->name); + pa_xfree(s); +} + +void pa__done(struct pa_core *c, struct pa_module*m) { + struct userdata*u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->services) + pa_hashmap_free(u->services, service_free, u); + + if (u->subscription) + pa_subscription_free(u->subscription); + + if (u->howl_wrapper) + pa_howl_wrapper_unref(u->howl_wrapper); + + pa_xfree(u); +} + |