summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2006-07-13 17:33:44 +0000
committerLennart Poettering <lennart@poettering.net>2006-07-13 17:33:44 +0000
commit76f93a07f9d683c3484ff3a71857fe30bedcfd46 (patch)
tree84c3bef20b7ed8b2998806e0db3b25c00ddd744a
parent3a816205ffde85bcf06c9ff55febc1ba69ce8de9 (diff)
* port libpulse-browse to use the native avahi API instead of the HOWL cruft
* add new function pa_browser_set_error_callback() * add doxygen docs to browser.h git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1069 fefdeb5f-60dc-0310-8127-8f9354f1896f
-rw-r--r--src/Makefile.am22
-rw-r--r--src/pulse/browser.c443
-rw-r--r--src/pulse/browser.h66
-rw-r--r--src/pulsecore/avahi-wrap.h1
4 files changed, 327 insertions, 205 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 8c8168d5..fad01e08 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -140,7 +140,7 @@ if HAVE_X11
bin_PROGRAMS += pax11publish
endif
-if HAVE_HOWL
+if HAVE_AVAHI
bin_PROGRAMS += pabrowse
endif
@@ -319,7 +319,7 @@ pulseinclude_HEADERS = \
pulse/volume.h \
pulse/xmalloc.h
-if HAVE_HOWL
+if HAVE_AVAHI
pulseinclude_HEADERS += \
pulse/browser.h
endif
@@ -338,7 +338,7 @@ lib_LTLIBRARIES = \
libpulse.la \
libpulse-simple.la
-if HAVE_HOWL
+if HAVE_AVAHI
lib_LTLIBRARIES += \
libpulse-browse.la
endif
@@ -444,9 +444,9 @@ libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la
libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO)
-libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h
-libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
-libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(HOWL_LIBS)
+libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
+libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(AVAHI_LIBS)
libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO)
libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c
@@ -864,9 +864,8 @@ modlibexec_LTLIBRARIES += \
module-solaris.la
endif
-if HAVE_HOWL
+if HAVE_AVAHI
modlibexec_LTLIBRARIES += \
- libhowl-wrap.la \
module-zeroconf-publish.la
endif
@@ -1111,12 +1110,7 @@ module_solaris_la_SOURCES = modules/module-solaris.c
module_solaris_la_LDFLAGS = -module -avoid-version
module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la
-# HOWL
-
-libhowl_wrap_la_SOURCES = modules/howl-wrap.c modules/howl-wrap.h
-libhowl_wrap_la_LDFLAGS = -avoid-version
-libhowl_wrap_la_LIBADD = $(AM_LIBADD) $(HOWL_LIBS) libpulsecore.la
-libhowl_wrap_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
+# Avahi
module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c
module_zeroconf_publish_la_LDFLAGS = -module -avoid-version
diff --git a/src/pulse/browser.c b/src/pulse/browser.c
index 60c71090..dae8e3d5 100644
--- a/src/pulse/browser.c
+++ b/src/pulse/browser.c
@@ -24,85 +24,68 @@
#endif
#include <assert.h>
-#include <howl.h>
+#include <string.h>
+
+#include <avahi-client/lookup.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/error.h>
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/avahi-wrap.h>
+
#include "browser.h"
-#define SERVICE_NAME_SINK "_pulse-sink._tcp."
-#define SERVICE_NAME_SOURCE "_pulse-source._tcp."
-#define SERVICE_NAME_SERVER "_pulse-server._tcp."
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp."
+#define SERVICE_TYPE_SOURCE "_pulse-source._tcp."
+#define SERVICE_TYPE_SERVER "_pulse-server._tcp."
struct pa_browser {
int ref;
pa_mainloop_api *mainloop;
+ AvahiPoll* avahi_poll;
pa_browse_cb_t callback;
void *userdata;
-
- sw_discovery discovery;
- pa_io_event *io_event;
-};
-
-static void io_callback(pa_mainloop_api*a, PA_GCC_UNUSED pa_io_event*e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) {
- pa_browser *b = userdata;
- assert(a && b && b->mainloop == a);
- if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) {
- pa_log(__FILE__": connection to HOWL daemon failed.");
- b->mainloop->io_free(b->io_event);
- b->io_event = NULL;
- return;
- }
-}
-
-static int type_equal(const char *a, const char *b) {
- size_t la, lb;
+ pa_browser_error_cb_t error_callback;
+ void *error_userdata;
- if (strcasecmp(a, b) == 0)
- return 1;
-
- la = strlen(a);
- lb = strlen(b);
-
- if (la > 0 && a[la-1] == '.' && la == lb+1 && strncasecmp(a, b, la-1) == 0)
- return 1;
-
- if (lb > 0 && b[lb-1] == '.' && lb == la+1 && strncasecmp(a, b, lb-1) == 0)
- return 1;
-
- return 0;
-}
+ AvahiClient *client;
+ AvahiServiceBrowser *server_browser, *sink_browser, *source_browser;
+
+};
static int map_to_opcode(const char *type, int new) {
- if (type_equal(type, SERVICE_NAME_SINK))
+ if (avahi_domain_equal(type, SERVICE_TYPE_SINK))
return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;
- else if (type_equal(type, SERVICE_NAME_SOURCE))
+ else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE))
return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE;
- else if (type_equal(type, SERVICE_NAME_SERVER))
+ else if (avahi_domain_equal(type, SERVICE_TYPE_SERVER))
return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER;
return -1;
}
-static sw_result resolve_reply(
- sw_discovery discovery,
- sw_discovery_oid oid,
- sw_uint32 interface_index,
- sw_const_string name,
- sw_const_string type,
- sw_const_string domain,
- sw_ipv4_address address,
- sw_port port,
- sw_octets text_record,
- sw_ulong text_record_len,
- sw_opaque extra) {
+static void resolve_callback(
+ AvahiServiceResolver *r,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *aa,
+ uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
- pa_browser *b = extra;
+ pa_browser *b = userdata;
pa_browse_info i;
char ip[256], a[256];
int opcode;
@@ -110,100 +93,96 @@ static sw_result resolve_reply(
uint32_t cookie;
pa_sample_spec ss;
int ss_valid = 0;
- sw_text_record_iterator iterator;
- int free_iterator = 0;
- char *c = NULL;
+ char *key = NULL, *value = NULL;
assert(b);
- sw_discovery_cancel(discovery, oid);
-
memset(&i, 0, sizeof(i));
i.name = name;
-
+
+ if (event != AVAHI_RESOLVER_FOUND)
+ goto fail;
+
if (!b->callback)
goto fail;
opcode = map_to_opcode(type, 1);
assert(opcode >= 0);
-
- snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
+
+ if (aa->proto == AVAHI_PROTO_INET)
+ snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
+ else {
+ assert(aa->proto == AVAHI_PROTO_INET6);
+ snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
+ }
i.server = a;
-
- if (text_record && text_record_len) {
- char key[SW_TEXT_RECORD_MAX_LEN];
- uint8_t val[SW_TEXT_RECORD_MAX_LEN];
- uint32_t val_len;
-
- if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) {
- pa_log_error(__FILE__": sw_text_record_string_iterator_init() failed.");
- goto fail;
- }
- free_iterator = 1;
+
+ while (txt) {
- while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
- c = pa_xstrndup((char*) val, val_len);
+ if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0)
+ break;
+
+ if (!strcmp(key, "device")) {
+ device_found = 1;
+ pa_xfree((char*) i.device);
+ i.device = value;
+ value = NULL;
+ } else if (!strcmp(key, "server-version")) {
+ pa_xfree((char*) i.server_version);
+ i.server_version = value;
+ value = NULL;
+ } else if (!strcmp(key, "user-name")) {
+ pa_xfree((char*) i.user_name);
+ i.user_name = value;
+ value = NULL;
+ } else if (!strcmp(key, "fqdn")) {
+ size_t l;
- if (!strcmp(key, "device")) {
- device_found = 1;
- pa_xfree((char*) i.device);
- i.device = c;
- c = NULL;
- } else if (!strcmp(key, "server-version")) {
- pa_xfree((char*) i.server_version);
- i.server_version = c;
- c = NULL;
- } else if (!strcmp(key, "user-name")) {
- pa_xfree((char*) i.user_name);
- i.user_name = c;
- c = NULL;
- } else if (!strcmp(key, "fqdn")) {
- size_t l;
+ pa_xfree((char*) i.fqdn);
+ i.fqdn = value;
+ value = NULL;
- pa_xfree((char*) i.fqdn);
- i.fqdn = c;
- c = NULL;
-
- l = strlen(a);
- assert(l+1 <= sizeof(a));
- strncat(a, " ", sizeof(a)-l-1);
- strncat(a, i.fqdn, sizeof(a)-l-2);
- } else if (!strcmp(key, "cookie")) {
-
- if (pa_atou(c, &cookie) < 0)
- goto fail;
-
- i.cookie = &cookie;
- } else if (!strcmp(key, "description")) {
- pa_xfree((char*) i.description);
- i.description = c;
- c = NULL;
- } else if (!strcmp(key, "channels")) {
- uint32_t ch;
-
- if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
- goto fail;
-
- ss.channels = (uint8_t) ch;
- ss_valid |= 1;
-
- } else if (!strcmp(key, "rate")) {
- if (pa_atou(c, &ss.rate) < 0)
- goto fail;
- ss_valid |= 2;
- } else if (!strcmp(key, "format")) {
+ l = strlen(a);
+ assert(l+1 <= sizeof(a));
+ strncat(a, " ", sizeof(a)-l-1);
+ strncat(a, i.fqdn, sizeof(a)-l-2);
+ } else if (!strcmp(key, "cookie")) {
+
+ if (pa_atou(value, &cookie) < 0)
+ goto fail;
+
+ i.cookie = &cookie;
+ } else if (!strcmp(key, "description")) {
+ pa_xfree((char*) i.description);
+ i.description = value;
+ value = NULL;
+ } else if (!strcmp(key, "channels")) {
+ uint32_t ch;
+
+ if (pa_atou(value, &ch) < 0 || ch <= 0 || ch > 255)
+ goto fail;
+
+ ss.channels = (uint8_t) ch;
+ ss_valid |= 1;
+
+ } else if (!strcmp(key, "rate")) {
+ if (pa_atou(value, &ss.rate) < 0)
+ goto fail;
+ ss_valid |= 2;
+ } else if (!strcmp(key, "format")) {
+
+ if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID)
+ goto fail;
+
+ ss_valid |= 4;
+ }
- if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
- goto fail;
-
- ss_valid |= 4;
- }
+ pa_xfree(key);
+ pa_xfree(value);
+ key = value = NULL;
- pa_xfree(c);
- c = NULL;
- }
-
+ txt = avahi_string_list_get_next(txt);
}
/* No device txt record was sent for a sink or source service */
@@ -212,7 +191,6 @@ static sw_result resolve_reply(
if (ss_valid == 7)
i.sample_spec = &ss;
-
b->callback(b, opcode, &i, b->userdata);
@@ -222,39 +200,72 @@ fail:
pa_xfree((void*) i.server_version);
pa_xfree((void*) i.user_name);
pa_xfree((void*) i.description);
- pa_xfree(c);
-
- if (free_iterator)
- sw_text_record_iterator_fina(iterator);
+ pa_xfree(key);
+ pa_xfree(value);
- return SW_OKAY;
+ avahi_service_resolver_free(r);
}
-static sw_result browse_reply(
- sw_discovery discovery,
- sw_discovery_oid id,
- sw_discovery_browse_status status,
- sw_uint32 interface_index,
- sw_const_string name,
- sw_const_string type,
- sw_const_string domain,
- sw_opaque extra) {
-
- pa_browser *b = extra;
+static void handle_failure(pa_browser *b) {
+ const char *e = NULL;
assert(b);
- switch (status) {
- case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
- sw_discovery_oid oid;
+ if (b->sink_browser)
+ avahi_service_browser_free(b->sink_browser);
+ if (b->source_browser)
+ avahi_service_browser_free(b->source_browser);
+ if (b->server_browser)
+ avahi_service_browser_free(b->server_browser);
- if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY)
- pa_log_error(__FILE__": sw_discovery_resolve() failed");
+ b->sink_browser = b->source_browser = b->server_browser = NULL;
+
+ if (b->client) {
+ e = avahi_strerror(avahi_client_errno(b->client));
+ avahi_client_free(b->client);
+ }
+
+ b->client = NULL;
+
+ if (b->error_callback)
+ b->error_callback(b, e, b->error_userdata);
+}
+
+static void browse_callback(
+ AvahiServiceBrowser *sb,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
+
+ pa_browser *b = userdata;
+ assert(b);
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW: {
+
+ if (!avahi_service_resolver_new(
+ b->client,
+ interface,
+ protocol,
+ name,
+ type,
+ domain,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ resolve_callback,
+ b))
+ handle_failure(b);
break;
}
- case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
+ case AVAHI_BROWSER_REMOVE: {
+
if (b->callback) {
pa_browse_info i;
int opcode;
@@ -268,63 +279,144 @@ static sw_result browse_reply(
b->callback(b, opcode, &i, b->userdata);
}
break;
+ }
+ case AVAHI_BROWSER_FAILURE: {
+ handle_failure(b);
+ break;
+ }
+
default:
;
}
+}
+
+static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) {
+ pa_browser *b = userdata;
+ assert(s);
- return SW_OKAY;
+ if (state == AVAHI_CLIENT_FAILURE)
+ handle_failure(b);
}
+static void browser_free(pa_browser *b);
+
pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
+ return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL);
+}
+
+pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) {
pa_browser *b;
- sw_discovery_oid oid;
+ int error;
+ assert(mainloop);
+
+ if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0)
+ return NULL;
+
b = pa_xnew(pa_browser, 1);
b->mainloop = mainloop;
b->ref = 1;
b->callback = NULL;
b->userdata = NULL;
+ b->error_callback = NULL;
+ b->error_userdata = NULL;
+ b->sink_browser = b->source_browser = b->server_browser = NULL;
- if (sw_discovery_init(&b->discovery) != SW_OKAY) {
- pa_log_error(__FILE__": sw_discovery_init() failed.");
- pa_xfree(b);
- return NULL;
+ b->avahi_poll = pa_avahi_poll_new(mainloop);
+
+ if (!(b->client = avahi_client_new(b->avahi_poll, 0, client_callback, b, &error))) {
+ if (error_string)
+ *error_string = avahi_strerror(error);
+ goto fail;
+ }
+
+ if ((flags & PA_BROWSE_FOR_SERVERS) &&
+ !(b->server_browser = avahi_service_browser_new(
+ b->client,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SERVER,
+ NULL,
+ 0,
+ browse_callback,
+ b))) {
+
+ if (error_string)
+ *error_string = avahi_strerror(avahi_client_errno(b->client));
+ goto fail;
}
- if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY ||
- sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY ||
- sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) {
+ if ((flags & PA_BROWSE_FOR_SINKS) &&
+ !(b->sink_browser = avahi_service_browser_new(
+ b->client,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SINK,
+ NULL,
+ 0,
+ browse_callback,
+ b))) {
+
+ if (error_string)
+ *error_string = avahi_strerror(avahi_client_errno(b->client));
+ goto fail;
+ }
- pa_log_error(__FILE__": sw_discovery_browse() failed.");
-
- sw_discovery_fina(b->discovery);
- pa_xfree(b);
- return NULL;
+ if ((flags & PA_BROWSE_FOR_SOURCES) &&
+ !(b->source_browser = avahi_service_browser_new(
+ b->client,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SOURCE,
+ NULL,
+ 0,
+ browse_callback,
+ b))) {
+
+ if (error_string)
+ *error_string = avahi_strerror(avahi_client_errno(b->client));
+ goto fail;
}
- b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
return b;
+
+fail:
+ if (b)
+ browser_free(b);
+
+ return NULL;
}
static void browser_free(pa_browser *b) {
assert(b && b->mainloop);
- if (b->io_event)
- b->mainloop->io_free(b->io_event);
+ if (b->sink_browser)
+ avahi_service_browser_free(b->sink_browser);
+ if (b->source_browser)
+ avahi_service_browser_free(b->source_browser);
+ if (b->server_browser)
+ avahi_service_browser_free(b->server_browser);
+
+ if (b->client)
+ avahi_client_free(b->client);
+
+ if (b->avahi_poll)
+ pa_avahi_poll_free(b->avahi_poll);
- sw_discovery_fina(b->discovery);
pa_xfree(b);
}
pa_browser *pa_browser_ref(pa_browser *b) {
- assert(b && b->ref >= 1);
+ assert(b);
+ assert(b->ref >= 1);
b->ref++;
return b;
}
void pa_browser_unref(pa_browser *b) {
- assert(b && b->ref >= 1);
+ assert(b);
+ assert(b->ref >= 1);
if ((-- (b->ref)) <= 0)
browser_free(b);
@@ -336,3 +428,10 @@ void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
b->callback = cb;
b->userdata = userdata;
}
+
+void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) {
+ assert(b);
+
+ b->error_callback = cb;
+ b->error_userdata = userdata;
+}
diff --git a/src/pulse/browser.h b/src/pulse/browser.h
index 2d20c6c0..fc57a4d5 100644
--- a/src/pulse/browser.h
+++ b/src/pulse/browser.h
@@ -27,42 +27,72 @@
#include <pulse/channelmap.h>
#include <pulse/cdecl.h>
+/** \file
+ * An abstract interface for Zeroconf browsing of PulseAudio servers */
+
PA_C_DECL_BEGIN
+/** An opaque Zeroconf service browser object */
typedef struct pa_browser pa_browser;
+/** Opcodes for pa_browser_cb_t callbacks */
typedef enum pa_browse_opcode {
- PA_BROWSE_NEW_SERVER = 0,
- PA_BROWSE_NEW_SINK,
- PA_BROWSE_NEW_SOURCE,
- PA_BROWSE_REMOVE_SERVER,
- PA_BROWSE_REMOVE_SINK,
- PA_BROWSE_REMOVE_SOURCE
+ PA_BROWSE_NEW_SERVER = 0, /**< New server found */
+ PA_BROWSE_NEW_SINK, /**< New sink found */
+ PA_BROWSE_NEW_SOURCE, /**< New source found */
+ PA_BROWSE_REMOVE_SERVER, /**< Server disappeared */
+ PA_BROWSE_REMOVE_SINK, /**< Sink disappeared */
+ PA_BROWSE_REMOVE_SOURCE /**< Source disappeared */
} pa_browse_opcode_t;
+typedef enum pa_browse_flags {
+ PA_BROWSE_FOR_SERVERS = 1, /**< Browse for servers */
+ PA_BROWSE_FOR_SINKS = 2, /**< Browse for sinks */
+ PA_BROWSE_FOR_SOURCES = 4 /** Browse for sources */
+} pa_browse_flags_t;
+
+/** Create a new browser object on the specified main loop */
pa_browser *pa_browser_new(pa_mainloop_api *mainloop);
+
+/** Same pa_browser_new, but pass additional flags parameter. */
+pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string);
+
+/** Increase reference counter of the specified browser object */
pa_browser *pa_browser_ref(pa_browser *z);
+
+/** Decrease reference counter of the specified browser object */
void pa_browser_unref(pa_browser *z);
+/** Information about a sink/source/server found with Zeroconf */
typedef struct pa_browse_info {
- /* Unique service name */
- const char *name; /* always available */
-
- /* Server info */
- const char *server; /* always available */
- const char *server_version, *user_name, *fqdn; /* optional */
- const uint32_t *cookie; /* optional */
-
- /* Device info */
- const char *device; /* always available when this information is of a sink/source */
- const char *description; /* optional */
- const pa_sample_spec *sample_spec; /* optional */
+ const char *name; /**< Unique service name; always available */
+
+ const char *server; /**< Server name; always available */
+ const char *server_version; /**< Server version string; optional */
+ const char *user_name; /**< User name of the server process; optional */
+ const char *fqdn; /* Server version; optional */
+ const uint32_t *cookie; /* Server cookie; optional */
+
+ const char *device; /* Device name; always available when this information is of a sink/source */
+ const char *description; /* Device description; optional */
+ const pa_sample_spec *sample_spec; /* Sample specification of the device; optional */
} pa_browse_info;
+/** Callback prototype */
typedef void (*pa_browse_cb_t)(pa_browser *z, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata);
+/** Set the callback pointer for the browser object */
void pa_browser_set_callback(pa_browser *z, pa_browse_cb_t cb, void *userdata);
+/** Callback prototype for errors */
+typedef void (*pa_browser_error_cb_t)(pa_browser *z, const char *error_string, void *userdata);
+
+/** Set a callback function that is called whenever the browser object
+ * becomes invalid due to an error. After this function has been
+ * called the browser object has become invalid and should be
+ * freed. */
+void pa_browser_set_error_callback(pa_browser *z, pa_browser_error_cb_t, void *userdata);
+
PA_C_DECL_END
#endif
diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h
index 97da11eb..d868fed4 100644
--- a/src/pulsecore/avahi-wrap.h
+++ b/src/pulsecore/avahi-wrap.h
@@ -29,5 +29,4 @@
AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *api);
void pa_avahi_poll_free(AvahiPoll *p);
-
#endif