2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 #include <avahi-client/lookup.h>
29 #include <avahi-common/domain.h>
30 #include <avahi-common/error.h>
32 #include <pulse/xmalloc.h>
34 #include <pulsecore/log.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/avahi-wrap.h>
37 #include <pulsecore/refcnt.h>
38 #include <pulsecore/macro.h>
42 #define SERVICE_TYPE_SINK "_pulse-sink._tcp."
43 #define SERVICE_TYPE_SOURCE "_pulse-source._tcp."
44 #define SERVICE_TYPE_SERVER "_pulse-server._tcp."
49 pa_mainloop_api
*mainloop
;
50 AvahiPoll
* avahi_poll
;
52 pa_browse_cb_t callback
;
55 pa_browser_error_cb_t error_callback
;
59 AvahiServiceBrowser
*server_browser
, *sink_browser
, *source_browser
;
63 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
;
101 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
103 memset(&i
, 0, sizeof(i
));
106 if (event
!= AVAHI_RESOLVER_FOUND
)
112 opcode
= map_to_opcode(type
, 1);
113 pa_assert(opcode
>= 0);
115 if (aa
->proto
== AVAHI_PROTO_INET
)
116 pa_snprintf(a
, sizeof(a
), "tcp:%s:%u", avahi_address_snprint(ip
, sizeof(ip
), aa
), port
);
118 pa_assert(aa
->proto
== AVAHI_PROTO_INET6
);
119 pa_snprintf(a
, sizeof(a
), "tcp6:%s:%u", avahi_address_snprint(ip
, sizeof(ip
), aa
), port
);
126 if (avahi_string_list_get_pair(txt
, &key
, &value
, NULL
) < 0)
129 if (!strcmp(key
, "device")) {
131 pa_xfree((char*) i
.device
);
134 } else if (!strcmp(key
, "server-version")) {
135 pa_xfree((char*) i
.server_version
);
136 i
.server_version
= value
;
138 } else if (!strcmp(key
, "user-name")) {
139 pa_xfree((char*) i
.user_name
);
142 } else if (!strcmp(key
, "fqdn")) {
145 pa_xfree((char*) i
.fqdn
);
150 pa_assert(l
+1 <= sizeof(a
));
151 strncat(a
, " ", sizeof(a
)-l
-1);
152 strncat(a
, i
.fqdn
, sizeof(a
)-l
-2);
153 } else if (!strcmp(key
, "cookie")) {
155 if (pa_atou(value
, &cookie
) < 0)
159 } else if (!strcmp(key
, "description")) {
160 pa_xfree((char*) i
.description
);
161 i
.description
= value
;
163 } else if (!strcmp(key
, "channels")) {
166 if (pa_atou(value
, &ch
) < 0 || ch
<= 0 || ch
> 255)
169 ss
.channels
= (uint8_t) ch
;
172 } else if (!strcmp(key
, "rate")) {
173 if (pa_atou(value
, &ss
.rate
) < 0)
176 } else if (!strcmp(key
, "format")) {
178 if ((ss
.format
= pa_parse_sample_format(value
)) == PA_SAMPLE_INVALID
)
188 txt
= avahi_string_list_get_next(txt
);
191 /* No device txt record was sent for a sink or source service */
192 if (opcode
!= PA_BROWSE_NEW_SERVER
&& !device_found
)
198 b
->callback(b
, opcode
, &i
, b
->userdata
);
201 pa_xfree((void*) i
.device
);
202 pa_xfree((void*) i
.fqdn
);
203 pa_xfree((void*) i
.server_version
);
204 pa_xfree((void*) i
.user_name
);
205 pa_xfree((void*) i
.description
);
210 avahi_service_resolver_free(r
);
213 static void handle_failure(pa_browser
*b
) {
214 const char *e
= NULL
;
217 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
220 avahi_service_browser_free(b
->sink_browser
);
221 if (b
->source_browser
)
222 avahi_service_browser_free(b
->source_browser
);
223 if (b
->server_browser
)
224 avahi_service_browser_free(b
->server_browser
);
226 b
->sink_browser
= b
->source_browser
= b
->server_browser
= NULL
;
229 e
= avahi_strerror(avahi_client_errno(b
->client
));
230 avahi_client_free(b
->client
);
235 if (b
->error_callback
)
236 b
->error_callback(b
, e
, b
->error_userdata
);
239 static void browse_callback(
240 AvahiServiceBrowser
*sb
,
241 AvahiIfIndex interface
,
242 AvahiProtocol protocol
,
243 AvahiBrowserEvent event
,
247 AvahiLookupResultFlags flags
,
250 pa_browser
*b
= userdata
;
253 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
256 case AVAHI_BROWSER_NEW
: {
258 if (!avahi_service_resolver_new(
274 case AVAHI_BROWSER_REMOVE
: {
280 memset(&i
, 0, sizeof(i
));
283 opcode
= map_to_opcode(type
, 0);
284 pa_assert(opcode
>= 0);
286 b
->callback(b
, opcode
, &i
, b
->userdata
);
291 case AVAHI_BROWSER_FAILURE
: {
301 static void client_callback(AvahiClient
*s
, AvahiClientState state
, void *userdata
) {
302 pa_browser
*b
= userdata
;
306 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
308 if (state
== AVAHI_CLIENT_FAILURE
)
312 static void browser_free(pa_browser
*b
);
315 PA_WARN_REFERENCE(pa_browser_new
, "libpulse-browse is being phased out.");
317 pa_browser
*pa_browser_new(pa_mainloop_api
*mainloop
) {
318 return pa_browser_new_full(mainloop
, PA_BROWSE_FOR_SERVERS
|PA_BROWSE_FOR_SINKS
|PA_BROWSE_FOR_SOURCES
, NULL
);
321 PA_WARN_REFERENCE(pa_browser_new_full
, "libpulse-browse is being phased out.");
323 pa_browser
*pa_browser_new_full(pa_mainloop_api
*mainloop
, pa_browse_flags_t flags
, const char **error_string
) {
329 if (flags
& ~(PA_BROWSE_FOR_SERVERS
|PA_BROWSE_FOR_SINKS
|PA_BROWSE_FOR_SOURCES
) || flags
== 0)
332 b
= pa_xnew(pa_browser
, 1);
333 b
->mainloop
= mainloop
;
337 b
->error_callback
= NULL
;
338 b
->error_userdata
= NULL
;
339 b
->sink_browser
= b
->source_browser
= b
->server_browser
= NULL
;
341 b
->avahi_poll
= pa_avahi_poll_new(mainloop
);
343 if (!(b
->client
= avahi_client_new(b
->avahi_poll
, 0, client_callback
, b
, &error
))) {
345 *error_string
= avahi_strerror(error
);
349 if ((flags
& PA_BROWSE_FOR_SERVERS
) &&
350 !(b
->server_browser
= avahi_service_browser_new(
361 *error_string
= avahi_strerror(avahi_client_errno(b
->client
));
365 if ((flags
& PA_BROWSE_FOR_SINKS
) &&
366 !(b
->sink_browser
= avahi_service_browser_new(
377 *error_string
= avahi_strerror(avahi_client_errno(b
->client
));
381 if ((flags
& PA_BROWSE_FOR_SOURCES
) &&
382 !(b
->source_browser
= avahi_service_browser_new(
393 *error_string
= avahi_strerror(avahi_client_errno(b
->client
));
406 static void browser_free(pa_browser
*b
) {
408 pa_assert(b
->mainloop
);
411 avahi_service_browser_free(b
->sink_browser
);
412 if (b
->source_browser
)
413 avahi_service_browser_free(b
->source_browser
);
414 if (b
->server_browser
)
415 avahi_service_browser_free(b
->server_browser
);
418 avahi_client_free(b
->client
);
421 pa_avahi_poll_free(b
->avahi_poll
);
426 PA_WARN_REFERENCE(pa_browser_ref
, "libpulse-browse is being phased out.");
428 pa_browser
*pa_browser_ref(pa_browser
*b
) {
430 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
436 PA_WARN_REFERENCE(pa_browser_unref
, "libpulse-browse is being phased out.");
438 void pa_browser_unref(pa_browser
*b
) {
440 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
442 if (PA_REFCNT_DEC(b
) <= 0)
446 PA_WARN_REFERENCE(pa_browser_set_callback
, "libpulse-browse is being phased out.");
448 void pa_browser_set_callback(pa_browser
*b
, pa_browse_cb_t cb
, void *userdata
) {
450 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
453 b
->userdata
= userdata
;
456 PA_WARN_REFERENCE(pa_browser_set_error_callback
, "libpulse-browse is being phased out.");
458 void pa_browser_set_error_callback(pa_browser
*b
, pa_browser_error_cb_t cb
, void *userdata
) {
460 pa_assert(PA_REFCNT_VALUE(b
) >= 1);
462 b
->error_callback
= cb
;
463 b
->error_userdata
= userdata
;