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 "polyplib-browser.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 struct pa_mainloop_api
*mainloop
;
38 void (*callback
)(struct pa_browser
*z
, enum pa_browse_opcode c
, const struct pa_browse_info
*i
, void *userdata
);
39 void *callback_userdata
;
41 sw_discovery discovery
;
42 struct pa_io_event
*io_event
;
46 static void io_callback(struct pa_mainloop_api
*a
, struct pa_io_event
*e
, int fd
, enum pa_io_event_flags events
, void *userdata
) {
47 struct 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
);
59 static sw_result
resolve_reply(
60 sw_discovery discovery
,
62 sw_uint32 interface_index
,
65 sw_const_string domain
,
66 sw_ipv4_address address
,
68 sw_octets text_record
,
69 sw_ulong text_record_len
,
72 struct pa_browser
*b
= extra
;
73 struct pa_browse_info i
;
75 enum pa_browse_opcode opcode
;
79 struct pa_sample_spec ss
;
81 sw_text_record_iterator iterator
;
82 int free_iterator
= 0;
87 sw_discovery_cancel(discovery
, oid
);
89 memset(&i
, 0, sizeof(i
));
95 if (!strcmp(type
, SERVICE_NAME_SINK
))
96 opcode
= PA_BROWSE_NEW_SINK
;
97 else if (!strcmp(type
, SERVICE_NAME_SOURCE
))
98 opcode
= PA_BROWSE_NEW_SOURCE
;
99 else if (!strcmp(type
, SERVICE_NAME_SERVER
))
100 opcode
= PA_BROWSE_NEW_SERVER
;
105 snprintf(a
, sizeof(a
), "tcp:%s:%u", sw_ipv4_address_name(address
, ip
, sizeof(ip
)), port
);
108 if (text_record
&& text_record_len
) {
109 char key
[SW_TEXT_RECORD_MAX_LEN
];
110 uint8_t val
[SW_TEXT_RECORD_MAX_LEN
];
113 if (sw_text_record_iterator_init(&iterator
, text_record
, text_record_len
) != SW_OKAY
) {
114 pa_log("sw_text_record_string_iterator_init() failed.\n");
120 while (sw_text_record_iterator_next(iterator
, key
, val
, &val_len
) == SW_OKAY
) {
121 c
= pa_xstrndup((char*) val
, val_len
);
123 if (!strcmp(key
, "device")) {
125 pa_xfree((char*) i
.device
);
128 } else if (!strcmp(key
, "server-version")) {
129 pa_xfree((char*) i
.server_version
);
130 i
.server_version
= c
;
132 } else if (!strcmp(key
, "user-name")) {
133 pa_xfree((char*) i
.user_name
);
136 } else if (!strcmp(key
, "fqdn")) {
137 pa_xfree((char*) i
.fqdn
);
140 } else if (!strcmp(key
, "cookie")) {
142 if (pa_atou(c
, &cookie
) < 0)
146 } else if (!strcmp(key
, "description")) {
147 pa_xfree((char*) i
.description
);
150 } else if (!strcmp(key
, "typeid")) {
152 if (pa_atou(c
, &typeid) < 0)
156 } else if (!strcmp(key
, "channels")) {
159 if (pa_atou(c
, &ch
) < 0 || ch
<= 0 || ch
> 255)
162 ss
.channels
= (uint8_t) ch
;
165 } else if (!strcmp(key
, "rate")) {
166 if (pa_atou(c
, &ss
.rate
) < 0)
169 } else if (!strcmp(key
, "format")) {
171 if ((ss
.format
= pa_parse_sample_format(c
)) == PA_SAMPLE_INVALID
)
183 /* No device txt record was sent for a sink or source service */
184 if (opcode
!= PA_BROWSE_NEW_SERVER
&& !device_found
)
191 b
->callback(b
, opcode
, &i
, b
->callback_userdata
);
194 pa_xfree((void*) i
.device
);
195 pa_xfree((void*) i
.fqdn
);
196 pa_xfree((void*) i
.server_version
);
197 pa_xfree((void*) i
.user_name
);
198 pa_xfree((void*) i
.description
);
202 sw_text_record_iterator_fina(iterator
);
208 static sw_result
browse_reply(
209 sw_discovery discovery
,
211 sw_discovery_browse_status status
,
212 sw_uint32 interface_index
,
213 sw_const_string name
,
214 sw_const_string type
,
215 sw_const_string domain
,
218 struct pa_browser
*b
= extra
;
222 case SW_DISCOVERY_BROWSE_ADD_SERVICE
: {
223 sw_discovery_oid oid
;
224 fprintf(stderr
, "debug: new service: %s\n", name
);
226 if (sw_discovery_resolve(b
->discovery
, 0, name
, type
, domain
, resolve_reply
, b
, &oid
) != SW_OKAY
)
227 pa_log("sw_discovery_resolve() failed\n");
232 case SW_DISCOVERY_BROWSE_REMOVE_SERVICE
:
234 struct pa_browse_info i
;
235 memset(&i
, 0, sizeof(i
));
237 b
->callback(b
, PA_BROWSE_REMOVE
, &i
, b
->callback_userdata
);
248 struct pa_browser
*pa_browser_new(struct pa_mainloop_api
*mainloop
) {
249 struct pa_browser
*b
;
250 sw_discovery_oid oid
;
252 b
= pa_xmalloc(sizeof(struct pa_browser
));
253 b
->mainloop
= mainloop
;
256 b
->callback_userdata
= NULL
;
258 if (sw_discovery_init(&b
->discovery
) != SW_OKAY
) {
259 pa_log("sw_discovery_init() failed.\n");
264 if (sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SERVER
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
265 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SINK
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
||
266 sw_discovery_browse(b
->discovery
, 0, SERVICE_NAME_SOURCE
, NULL
, browse_reply
, b
, &oid
) != SW_OKAY
) {
268 pa_log("sw_discovery_browse() failed.\n");
270 sw_discovery_fina(b
->discovery
);
275 b
->io_event
= mainloop
->io_new(mainloop
, sw_discovery_socket(b
->discovery
), PA_IO_EVENT_INPUT
, io_callback
, b
);
279 static void browser_free(struct pa_browser
*b
) {
280 assert(b
&& b
->mainloop
);
283 b
->mainloop
->io_free(b
->io_event
);
285 sw_discovery_fina(b
->discovery
);
289 struct pa_browser
*pa_browser_ref(struct pa_browser
*b
) {
290 assert(b
&& b
->ref
>= 1);
295 void pa_browser_unref(struct pa_browser
*b
) {
296 assert(b
&& b
->ref
>= 1);
298 if ((-- (b
->ref
)) <= 0)
302 void pa_browser_set_callback(struct pa_browser
*b
, void (*cb
)(struct pa_browser
*z
, enum pa_browse_opcode c
, const struct pa_browse_info
*i
, void* userdata
), void *userdata
) {
306 b
->callback_userdata
= userdata
;