4 This file is part of polypaudio.
6 polypaudio 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 of the
9 License, or (at your option) any later version.
11 polypaudio 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 polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
25 #include <polypcore/xmalloc.h>
26 #include <polypcore/log.h>
27 #include <polypcore/util.h>
31 #define SERVICE_NAME_SINK "_polypaudio-sink._tcp."
32 #define SERVICE_NAME_SOURCE "_polypaudio-source._tcp."
33 #define SERVICE_NAME_SERVER "_polypaudio-server._tcp."
37 pa_mainloop_api
*mainloop
;
39 pa_browse_cb_t callback
;
42 sw_discovery discovery
;
43 pa_io_event
*io_event
;
46 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
) {
47 pa_browser
*b
= userdata
;
48 assert(a
&& b
&& b
->mainloop
== a
);
50 if (events
!= PA_IO_EVENT_INPUT
|| sw_discovery_read_socket(b
->discovery
) != SW_OKAY
) {
51 pa_log(__FILE__
": connection to HOWL daemon failed.");
52 b
->mainloop
->io_free(b
->io_event
);
58 static int type_equal(const char *a
, const char *b
) {
61 if (strcasecmp(a
, b
) == 0)
67 if (la
> 0 && a
[la
-1] == '.' && la
== lb
+1 && strncasecmp(a
, b
, la
-1) == 0)
70 if (lb
> 0 && b
[lb
-1] == '.' && lb
== la
+1 && strncasecmp(a
, b
, lb
-1) == 0)
76 static int map_to_opcode(const char *type
, int new) {
77 if (type_equal(type
, SERVICE_NAME_SINK
))
78 return new ? PA_BROWSE_NEW_SINK
: PA_BROWSE_REMOVE_SINK
;
79 else if (type_equal(type
, SERVICE_NAME_SOURCE
))
80 return new ? PA_BROWSE_NEW_SOURCE
: PA_BROWSE_REMOVE_SOURCE
;
81 else if (type_equal(type
, SERVICE_NAME_SERVER
))
82 return new ? PA_BROWSE_NEW_SERVER
: PA_BROWSE_REMOVE_SERVER
;
87 static sw_result
resolve_reply(
88 sw_discovery discovery
,
90 sw_uint32 interface_index
,
93 sw_const_string domain
,
94 sw_ipv4_address address
,
96 sw_octets text_record
,
97 sw_ulong text_record_len
,
100 pa_browser
*b
= extra
;
102 char ip
[256], a
[256];
104 int device_found
= 0;
108 sw_text_record_iterator iterator
;
109 int free_iterator
= 0;
114 sw_discovery_cancel(discovery
, oid
);
116 memset(&i
, 0, sizeof(i
));
122 opcode
= map_to_opcode(type
, 1);
125 snprintf(a
, sizeof(a
), "tcp:%s:%u", sw_ipv4_address_name(address
, ip
, sizeof(ip
)), port
);
128 if (text_record
&& text_record_len
) {
129 char key
[SW_TEXT_RECORD_MAX_LEN
];
130 uint8_t val
[SW_TEXT_RECORD_MAX_LEN
];
133 if (sw_text_record_iterator_init(&iterator
, text_record
, text_record_len
) != SW_OKAY
) {
134 pa_log_error(__FILE__
": sw_text_record_string_iterator_init() failed.");
140 while (sw_text_record_iterator_next(iterator
, key
, val
, &val_len
) == SW_OKAY
) {
141 c
= pa_xstrndup((char*) val
, val_len
);
143 if (!strcmp(key
, "device")) {
145 pa_xfree((char*) i
.device
);
148 } else if (!strcmp(key
, "server-version")) {
149 pa_xfree((char*) i
.server_version
);
150 i
.server_version
= c
;
152 } else if (!strcmp(key
, "user-name")) {
153 pa_xfree((char*) i
.user_name
);
156 } else if (!strcmp(key
, "fqdn")) {
159 pa_xfree((char*) i
.fqdn
);
164 assert(l
+1 <= sizeof(a
));
165 strncat(a
, " ", sizeof(a
)-l
-1);
166 strncat(a
, i
.fqdn
, sizeof(a
)-l
-2);
167 } else if (!strcmp(key
, "cookie")) {
169 if (pa_atou(c
, &cookie
) < 0)
173 } else if (!strcmp(key
, "description")) {
174 pa_xfree((char*) i
.description
);
177 } else if (!strcmp(key
, "channels")) {
180 if (pa_atou(c
, &ch
) < 0 || ch
<= 0 || ch
> 255)
183 ss
.channels
= (uint8_t) ch
;
186 } else if (!strcmp(key
, "rate")) {
187 if (pa_atou(c
, &ss
.rate
) < 0)
190 } else if (!strcmp(key
, "format")) {
192 if ((ss
.format
= pa_parse_sample_format(c
)) == PA_SAMPLE_INVALID
)
204 /* No device txt record was sent for a sink or source service */
205 if (opcode
!= PA_BROWSE_NEW_SERVER
&& !device_found
)
212 b
->callback(b
, opcode
, &i
, b
->userdata
);
215 pa_xfree((void*) i
.device
);
216 pa_xfree((void*) i
.fqdn
);
217 pa_xfree((void*) i
.server_version
);
218 pa_xfree((void*) i
.user_name
);
219 pa_xfree((void*) i
.description
);
223 sw_text_record_iterator_fina(iterator
);
229 static sw_result
browse_reply(
230 sw_discovery discovery
,
232 sw_discovery_browse_status status
,
233 sw_uint32 interface_index
,
234 sw_const_string name
,
235 sw_const_string type
,
236 sw_const_string domain
,
239 pa_browser
*b
= extra
;
243 case SW_DISCOVERY_BROWSE_ADD_SERVICE
: {
244 sw_discovery_oid oid
;
246 if (sw_discovery_resolve(b
->discovery
, 0, name
, type
, domain
, resolve_reply
, b
, &oid
) != SW_OKAY
)
247 pa_log_error(__FILE__
": sw_discovery_resolve() failed");
252 case SW_DISCOVERY_BROWSE_REMOVE_SERVICE
:
257 memset(&i
, 0, sizeof(i
));
260 opcode
= map_to_opcode(type
, 0);
263 b
->callback(b
, opcode
, &i
, b
->userdata
);
274 pa_browser
*pa_browser_new(pa_mainloop_api
*mainloop
) {
276 sw_discovery_oid oid
;
278 b
= pa_xnew(pa_browser
, 1);
279 b
->mainloop
= mainloop
;
284 if (sw_discovery_init(&b
->discovery
) != SW_OKAY
) {
285 pa_log_error(__FILE__
": sw_discovery_init() failed.");
290 if (sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SERVER
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
291 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SINK
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
292 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SOURCE
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
) {
294 pa_log_error(__FILE__
": sw_discovery_browse() failed.");
296 sw_discovery_fina(b
->discovery
);
301 b
->io_event
= mainloop
->io_new(mainloop
, sw_discovery_socket(b
->discovery
), PA_IO_EVENT_INPUT
, io_callback
, b
);
305 static void browser_free(pa_browser
*b
) {
306 assert(b
&& b
->mainloop
);
309 b
->mainloop
->io_free(b
->io_event
);
311 sw_discovery_fina(b
->discovery
);
315 pa_browser
*pa_browser_ref(pa_browser
*b
) {
316 assert(b
&& b
->ref
>= 1);
321 void pa_browser_unref(pa_browser
*b
) {
322 assert(b
&& b
->ref
>= 1);
324 if ((-- (b
->ref
)) <= 0)
328 void pa_browser_set_callback(pa_browser
*b
, pa_browse_cb_t cb
, void *userdata
) {
332 b
->userdata
= userdata
;