4 This file is part of PulseAudio.
6 Copyright 2004-2006 Lennart Poettering
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
31 #include <avahi-client/lookup.h>
32 #include <avahi-common/domain.h>
33 #include <avahi-common/error.h>
35 #include <pulse/xmalloc.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/core-util.h>
40 #include <pulsecore/avahi-wrap.h>
44 #define SERVICE_TYPE_SINK "_pulse-sink._tcp."
45 #define SERVICE_TYPE_SOURCE "_pulse-source._tcp."
46 #define SERVICE_TYPE_SERVER "_pulse-server._tcp."
50 pa_mainloop_api *mainloop;
51 AvahiPoll* avahi_poll;
53 pa_browse_cb_t callback;
56 pa_browser_error_cb_t error_callback;
60 AvahiServiceBrowser *server_browser, *sink_browser, *source_browser;
64 static int map_to_opcode(const char *type, int new) {
65 if (avahi_domain_equal(type, SERVICE_TYPE_SINK))
66 return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;
67 else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE))
68 return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE;
69 else if (avahi_domain_equal(type, SERVICE_TYPE_SERVER))
70 return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER;
75 static void resolve_callback(
76 AvahiServiceResolver *r,
77 AvahiIfIndex interface,
78 AvahiProtocol protocol,
79 AvahiResolverEvent event,
83 const char *host_name,
84 const AvahiAddress *aa,
87 AvahiLookupResultFlags flags,
90 pa_browser *b = userdata;
98 char *key = NULL, *value = NULL;
102 memset(&i, 0, sizeof(i));
105 if (event != AVAHI_RESOLVER_FOUND)
111 opcode = map_to_opcode(type, 1);
114 if (aa->proto == AVAHI_PROTO_INET)
115 snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
117 assert(aa->proto == AVAHI_PROTO_INET6);
118 snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
125 if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0)
128 if (!strcmp(key, "device")) {
130 pa_xfree((char*) i.device);
133 } else if (!strcmp(key, "server-version")) {
134 pa_xfree((char*) i.server_version);
135 i.server_version = value;
137 } else if (!strcmp(key, "user-name")) {
138 pa_xfree((char*) i.user_name);
141 } else if (!strcmp(key, "fqdn")) {
144 pa_xfree((char*) i.fqdn);
149 assert(l+1 <= sizeof(a));
150 strncat(a, " ", sizeof(a)-l-1);
151 strncat(a, i.fqdn, sizeof(a)-l-2);
152 } else if (!strcmp(key, "cookie")) {
154 if (pa_atou(value, &cookie) < 0)
158 } else if (!strcmp(key, "description")) {
159 pa_xfree((char*) i.description);
160 i.description = value;
162 } else if (!strcmp(key, "channels")) {
165 if (pa_atou(value, &ch) < 0 || ch <= 0 || ch > 255)
168 ss.channels = (uint8_t) ch;
171 } else if (!strcmp(key, "rate")) {
172 if (pa_atou(value, &ss.rate) < 0)
175 } else if (!strcmp(key, "format")) {
177 if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID)
187 txt = avahi_string_list_get_next(txt);
190 /* No device txt record was sent for a sink or source service */
191 if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
197 b->callback(b, opcode, &i, b->userdata);
200 pa_xfree((void*) i.device);
201 pa_xfree((void*) i.fqdn);
202 pa_xfree((void*) i.server_version);
203 pa_xfree((void*) i.user_name);
204 pa_xfree((void*) i.description);
209 avahi_service_resolver_free(r);
212 static void handle_failure(pa_browser *b) {
213 const char *e = NULL;
217 avahi_service_browser_free(b->sink_browser);
218 if (b->source_browser)
219 avahi_service_browser_free(b->source_browser);
220 if (b->server_browser)
221 avahi_service_browser_free(b->server_browser);
223 b->sink_browser = b->source_browser = b->server_browser = NULL;
226 e = avahi_strerror(avahi_client_errno(b->client));
227 avahi_client_free(b->client);
232 if (b->error_callback)
233 b->error_callback(b, e, b->error_userdata);
236 static void browse_callback(
237 AvahiServiceBrowser *sb,
238 AvahiIfIndex interface,
239 AvahiProtocol protocol,
240 AvahiBrowserEvent event,
244 AvahiLookupResultFlags flags,
247 pa_browser *b = userdata;
251 case AVAHI_BROWSER_NEW: {
253 if (!avahi_service_resolver_new(
269 case AVAHI_BROWSER_REMOVE: {
275 memset(&i, 0, sizeof(i));
278 opcode = map_to_opcode(type, 0);
281 b->callback(b, opcode, &i, b->userdata);
286 case AVAHI_BROWSER_FAILURE: {
296 static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) {
297 pa_browser *b = userdata;
300 if (state == AVAHI_CLIENT_FAILURE)
304 static void browser_free(pa_browser *b);
306 pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
307 return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL);
310 pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) {
316 if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0)
319 b = pa_xnew(pa_browser, 1);
320 b->mainloop = mainloop;
324 b->error_callback = NULL;
325 b->error_userdata = NULL;
326 b->sink_browser = b->source_browser = b->server_browser = NULL;
328 b->avahi_poll = pa_avahi_poll_new(mainloop);
330 if (!(b->client = avahi_client_new(b->avahi_poll, 0, client_callback, b, &error))) {
332 *error_string = avahi_strerror(error);
336 if ((flags & PA_BROWSE_FOR_SERVERS) &&
337 !(b->server_browser = avahi_service_browser_new(
348 *error_string = avahi_strerror(avahi_client_errno(b->client));
352 if ((flags & PA_BROWSE_FOR_SINKS) &&
353 !(b->sink_browser = avahi_service_browser_new(
364 *error_string = avahi_strerror(avahi_client_errno(b->client));
368 if ((flags & PA_BROWSE_FOR_SOURCES) &&
369 !(b->source_browser = avahi_service_browser_new(
380 *error_string = avahi_strerror(avahi_client_errno(b->client));
393 static void browser_free(pa_browser *b) {
394 assert(b && b->mainloop);
397 avahi_service_browser_free(b->sink_browser);
398 if (b->source_browser)
399 avahi_service_browser_free(b->source_browser);
400 if (b->server_browser)
401 avahi_service_browser_free(b->server_browser);
404 avahi_client_free(b->client);
407 pa_avahi_poll_free(b->avahi_poll);
412 pa_browser *pa_browser_ref(pa_browser *b) {
419 void pa_browser_unref(pa_browser *b) {
423 if ((-- (b->ref)) <= 0)
427 void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
431 b->userdata = userdata;
434 void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) {
437 b->error_callback = cb;
438 b->error_userdata = userdata;