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
26 #include <polypcore/xmalloc.h>
27 #include <polypcore/log.h>
28 #include <polypcore/util.h>
30 #define SERVICE_NAME_SINK "_polypaudio-sink._tcp."
31 #define SERVICE_NAME_SOURCE "_polypaudio-source._tcp."
32 #define SERVICE_NAME_SERVER "_polypaudio-server._tcp."
36 pa_mainloop_api
*mainloop
;
38 void (*callback
)(pa_browser
*z
, pa_browse_opcode c
, const pa_browse_info
*i
, void *userdata
);
39 void *callback_userdata
;
41 sw_discovery discovery
;
42 pa_io_event
*io_event
;
46 static void io_callback(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags 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.\n");
52 b
->mainloop
->io_free(b
->io_event
);
58 static sw_result
resolve_reply(
59 sw_discovery discovery
,
61 sw_uint32 interface_index
,
64 sw_const_string domain
,
65 sw_ipv4_address address
,
67 sw_octets text_record
,
68 sw_ulong text_record_len
,
71 pa_browser
*b
= extra
;
74 pa_browse_opcode opcode
;
80 sw_text_record_iterator iterator
;
81 int free_iterator
= 0;
86 sw_discovery_cancel(discovery
, oid
);
88 memset(&i
, 0, sizeof(i
));
94 if (!strcmp(type
, SERVICE_NAME_SINK
))
95 opcode
= PA_BROWSE_NEW_SINK
;
96 else if (!strcmp(type
, SERVICE_NAME_SOURCE
))
97 opcode
= PA_BROWSE_NEW_SOURCE
;
98 else if (!strcmp(type
, SERVICE_NAME_SERVER
))
99 opcode
= PA_BROWSE_NEW_SERVER
;
104 snprintf(a
, sizeof(a
), "tcp:%s:%u", sw_ipv4_address_name(address
, ip
, sizeof(ip
)), port
);
107 if (text_record
&& text_record_len
) {
108 char key
[SW_TEXT_RECORD_MAX_LEN
];
109 uint8_t val
[SW_TEXT_RECORD_MAX_LEN
];
112 if (sw_text_record_iterator_init(&iterator
, text_record
, text_record_len
) != SW_OKAY
) {
113 pa_log("sw_text_record_string_iterator_init() failed.\n");
119 while (sw_text_record_iterator_next(iterator
, key
, val
, &val_len
) == SW_OKAY
) {
120 c
= pa_xstrndup((char*) val
, val_len
);
122 if (!strcmp(key
, "device")) {
124 pa_xfree((char*) i
.device
);
127 } else if (!strcmp(key
, "server-version")) {
128 pa_xfree((char*) i
.server_version
);
129 i
.server_version
= c
;
131 } else if (!strcmp(key
, "user-name")) {
132 pa_xfree((char*) i
.user_name
);
135 } else if (!strcmp(key
, "fqdn")) {
138 pa_xfree((char*) i
.fqdn
);
143 assert(l
+1 <= sizeof(a
));
144 strncat(a
, " ", sizeof(a
)-l
-1);
145 strncat(a
, i
.fqdn
, sizeof(a
)-l
-2);
146 } else if (!strcmp(key
, "cookie")) {
148 if (pa_atou(c
, &cookie
) < 0)
152 } else if (!strcmp(key
, "description")) {
153 pa_xfree((char*) i
.description
);
156 } else if (!strcmp(key
, "typeid")) {
158 if (pa_atou(c
, &typeid) < 0)
162 } else if (!strcmp(key
, "channels")) {
165 if (pa_atou(c
, &ch
) < 0 || ch
<= 0 || ch
> 255)
168 ss
.channels
= (uint8_t) ch
;
171 } else if (!strcmp(key
, "rate")) {
172 if (pa_atou(c
, &ss
.rate
) < 0)
175 } else if (!strcmp(key
, "format")) {
177 if ((ss
.format
= pa_parse_sample_format(c
)) == PA_SAMPLE_INVALID
)
189 /* No device txt record was sent for a sink or source service */
190 if (opcode
!= PA_BROWSE_NEW_SERVER
&& !device_found
)
197 b
->callback(b
, opcode
, &i
, b
->callback_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
);
208 sw_text_record_iterator_fina(iterator
);
214 static sw_result
browse_reply(
215 sw_discovery discovery
,
217 sw_discovery_browse_status status
,
218 sw_uint32 interface_index
,
219 sw_const_string name
,
220 sw_const_string type
,
221 sw_const_string domain
,
224 pa_browser
*b
= extra
;
228 case SW_DISCOVERY_BROWSE_ADD_SERVICE
: {
229 sw_discovery_oid oid
;
231 if (sw_discovery_resolve(b
->discovery
, 0, name
, type
, domain
, resolve_reply
, b
, &oid
) != SW_OKAY
)
232 pa_log("sw_discovery_resolve() failed\n");
237 case SW_DISCOVERY_BROWSE_REMOVE_SERVICE
:
240 memset(&i
, 0, sizeof(i
));
242 b
->callback(b
, PA_BROWSE_REMOVE
, &i
, b
->callback_userdata
);
253 pa_browser
*pa_browser_new(pa_mainloop_api
*mainloop
) {
255 sw_discovery_oid oid
;
257 b
= pa_xmalloc(sizeof(pa_browser
));
258 b
->mainloop
= mainloop
;
261 b
->callback_userdata
= NULL
;
263 if (sw_discovery_init(&b
->discovery
) != SW_OKAY
) {
264 pa_log("sw_discovery_init() failed.\n");
269 if (sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SERVER
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
270 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SINK
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
271 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SOURCE
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
) {
273 pa_log("sw_discovery_browse() failed.\n");
275 sw_discovery_fina(b
->discovery
);
280 b
->io_event
= mainloop
->io_new(mainloop
, sw_discovery_socket(b
->discovery
), PA_IO_EVENT_INPUT
, io_callback
, b
);
284 static void browser_free(pa_browser
*b
) {
285 assert(b
&& b
->mainloop
);
288 b
->mainloop
->io_free(b
->io_event
);
290 sw_discovery_fina(b
->discovery
);
294 pa_browser
*pa_browser_ref(pa_browser
*b
) {
295 assert(b
&& b
->ref
>= 1);
300 void pa_browser_unref(pa_browser
*b
) {
301 assert(b
&& b
->ref
>= 1);
303 if ((-- (b
->ref
)) <= 0)
307 void pa_browser_set_callback(pa_browser
*b
, void (*cb
)(pa_browser
*z
, pa_browse_opcode c
, const pa_browse_info
*i
, void* userdata
), void *userdata
) {
311 b
->callback_userdata
= userdata
;