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 <polyp/xmalloc.h>
27 #include <polypcore/log.h>
28 #include <polypcore/core-util.h>
32 #define SERVICE_NAME_SINK "_polypaudio-sink._tcp."
33 #define SERVICE_NAME_SOURCE "_polypaudio-source._tcp."
34 #define SERVICE_NAME_SERVER "_polypaudio-server._tcp."
38 pa_mainloop_api
*mainloop
;
40 pa_browse_cb_t callback
;
43 sw_discovery discovery
;
44 pa_io_event
*io_event
;
47 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
) {
48 pa_browser
*b
= userdata
;
49 assert(a
&& b
&& b
->mainloop
== a
);
51 if (events
!= PA_IO_EVENT_INPUT
|| sw_discovery_read_socket(b
->discovery
) != SW_OKAY
) {
52 pa_log(__FILE__
": connection to HOWL daemon failed.");
53 b
->mainloop
->io_free(b
->io_event
);
59 static int type_equal(const char *a
, const char *b
) {
62 if (strcasecmp(a
, b
) == 0)
68 if (la
> 0 && a
[la
-1] == '.' && la
== lb
+1 && strncasecmp(a
, b
, la
-1) == 0)
71 if (lb
> 0 && b
[lb
-1] == '.' && lb
== la
+1 && strncasecmp(a
, b
, lb
-1) == 0)
77 static int map_to_opcode(const char *type
, int new) {
78 if (type_equal(type
, SERVICE_NAME_SINK
))
79 return new ? PA_BROWSE_NEW_SINK
: PA_BROWSE_REMOVE_SINK
;
80 else if (type_equal(type
, SERVICE_NAME_SOURCE
))
81 return new ? PA_BROWSE_NEW_SOURCE
: PA_BROWSE_REMOVE_SOURCE
;
82 else if (type_equal(type
, SERVICE_NAME_SERVER
))
83 return new ? PA_BROWSE_NEW_SERVER
: PA_BROWSE_REMOVE_SERVER
;
88 static sw_result
resolve_reply(
89 sw_discovery discovery
,
91 sw_uint32 interface_index
,
94 sw_const_string domain
,
95 sw_ipv4_address address
,
97 sw_octets text_record
,
98 sw_ulong text_record_len
,
101 pa_browser
*b
= extra
;
103 char ip
[256], a
[256];
105 int device_found
= 0;
109 sw_text_record_iterator iterator
;
110 int free_iterator
= 0;
115 sw_discovery_cancel(discovery
, oid
);
117 memset(&i
, 0, sizeof(i
));
123 opcode
= map_to_opcode(type
, 1);
126 snprintf(a
, sizeof(a
), "tcp:%s:%u", sw_ipv4_address_name(address
, ip
, sizeof(ip
)), port
);
129 if (text_record
&& text_record_len
) {
130 char key
[SW_TEXT_RECORD_MAX_LEN
];
131 uint8_t val
[SW_TEXT_RECORD_MAX_LEN
];
134 if (sw_text_record_iterator_init(&iterator
, text_record
, text_record_len
) != SW_OKAY
) {
135 pa_log_error(__FILE__
": sw_text_record_string_iterator_init() failed.");
141 while (sw_text_record_iterator_next(iterator
, key
, val
, &val_len
) == SW_OKAY
) {
142 c
= pa_xstrndup((char*) val
, val_len
);
144 if (!strcmp(key
, "device")) {
146 pa_xfree((char*) i
.device
);
149 } else if (!strcmp(key
, "server-version")) {
150 pa_xfree((char*) i
.server_version
);
151 i
.server_version
= c
;
153 } else if (!strcmp(key
, "user-name")) {
154 pa_xfree((char*) i
.user_name
);
157 } else if (!strcmp(key
, "fqdn")) {
160 pa_xfree((char*) i
.fqdn
);
165 assert(l
+1 <= sizeof(a
));
166 strncat(a
, " ", sizeof(a
)-l
-1);
167 strncat(a
, i
.fqdn
, sizeof(a
)-l
-2);
168 } else if (!strcmp(key
, "cookie")) {
170 if (pa_atou(c
, &cookie
) < 0)
174 } else if (!strcmp(key
, "description")) {
175 pa_xfree((char*) i
.description
);
178 } else if (!strcmp(key
, "channels")) {
181 if (pa_atou(c
, &ch
) < 0 || ch
<= 0 || ch
> 255)
184 ss
.channels
= (uint8_t) ch
;
187 } else if (!strcmp(key
, "rate")) {
188 if (pa_atou(c
, &ss
.rate
) < 0)
191 } else if (!strcmp(key
, "format")) {
193 if ((ss
.format
= pa_parse_sample_format(c
)) == PA_SAMPLE_INVALID
)
205 /* No device txt record was sent for a sink or source service */
206 if (opcode
!= PA_BROWSE_NEW_SERVER
&& !device_found
)
213 b
->callback(b
, opcode
, &i
, b
->userdata
);
216 pa_xfree((void*) i
.device
);
217 pa_xfree((void*) i
.fqdn
);
218 pa_xfree((void*) i
.server_version
);
219 pa_xfree((void*) i
.user_name
);
220 pa_xfree((void*) i
.description
);
224 sw_text_record_iterator_fina(iterator
);
230 static sw_result
browse_reply(
231 sw_discovery discovery
,
233 sw_discovery_browse_status status
,
234 sw_uint32 interface_index
,
235 sw_const_string name
,
236 sw_const_string type
,
237 sw_const_string domain
,
240 pa_browser
*b
= extra
;
244 case SW_DISCOVERY_BROWSE_ADD_SERVICE
: {
245 sw_discovery_oid oid
;
247 if (sw_discovery_resolve(b
->discovery
, 0, name
, type
, domain
, resolve_reply
, b
, &oid
) != SW_OKAY
)
248 pa_log_error(__FILE__
": sw_discovery_resolve() failed");
253 case SW_DISCOVERY_BROWSE_REMOVE_SERVICE
:
258 memset(&i
, 0, sizeof(i
));
261 opcode
= map_to_opcode(type
, 0);
264 b
->callback(b
, opcode
, &i
, b
->userdata
);
275 pa_browser
*pa_browser_new(pa_mainloop_api
*mainloop
) {
277 sw_discovery_oid oid
;
279 b
= pa_xnew(pa_browser
, 1);
280 b
->mainloop
= mainloop
;
285 if (sw_discovery_init(&b
->discovery
) != SW_OKAY
) {
286 pa_log_error(__FILE__
": sw_discovery_init() failed.");
291 if (sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SERVER
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
292 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SINK
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
293 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SOURCE
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
) {
295 pa_log_error(__FILE__
": sw_discovery_browse() failed.");
297 sw_discovery_fina(b
->discovery
);
302 b
->io_event
= mainloop
->io_new(mainloop
, sw_discovery_socket(b
->discovery
), PA_IO_EVENT_INPUT
, io_callback
, b
);
306 static void browser_free(pa_browser
*b
) {
307 assert(b
&& b
->mainloop
);
310 b
->mainloop
->io_free(b
->io_event
);
312 sw_discovery_fina(b
->discovery
);
316 pa_browser
*pa_browser_ref(pa_browser
*b
) {
317 assert(b
&& b
->ref
>= 1);
322 void pa_browser_unref(pa_browser
*b
) {
323 assert(b
&& b
->ref
>= 1);
325 if ((-- (b
->ref
)) <= 0)
329 void pa_browser_set_callback(pa_browser
*b
, pa_browse_cb_t cb
, void *userdata
) {
333 b
->userdata
= userdata
;