]> code.delx.au - pulseaudio/blob - src/polyp/browser.c
* drop polylib prefix from #define
[pulseaudio] / src / polyp / browser.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
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.
10
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.
15
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
19 USA.
20 ***/
21
22 #include <assert.h>
23 #include <howl.h>
24
25 #include "browser.h"
26 #include <polypcore/xmalloc.h>
27 #include <polypcore/log.h>
28 #include <polypcore/util.h>
29
30 #define SERVICE_NAME_SINK "_polypaudio-sink._tcp."
31 #define SERVICE_NAME_SOURCE "_polypaudio-source._tcp."
32 #define SERVICE_NAME_SERVER "_polypaudio-server._tcp."
33
34 pa_browser {
35 int ref;
36 pa_mainloop_api *mainloop;
37
38 void (*callback)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata);
39 void *callback_userdata;
40
41 sw_discovery discovery;
42 pa_io_event *io_event;
43 };
44
45
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);
49
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);
53 b->io_event = NULL;
54 return;
55 }
56 }
57
58 static sw_result resolve_reply(
59 sw_discovery discovery,
60 sw_discovery_oid oid,
61 sw_uint32 interface_index,
62 sw_const_string name,
63 sw_const_string type,
64 sw_const_string domain,
65 sw_ipv4_address address,
66 sw_port port,
67 sw_octets text_record,
68 sw_ulong text_record_len,
69 sw_opaque extra) {
70
71 pa_browser *b = extra;
72 pa_browse_info i;
73 char ip[256], a[256];
74 pa_browse_opcode opcode;
75 int device_found = 0;
76 uint32_t cookie;
77 pa_typeid_t typeid;
78 pa_sample_spec ss;
79 int ss_valid = 0;
80 sw_text_record_iterator iterator;
81 int free_iterator = 0;
82 char *c = NULL;
83
84 assert(b);
85
86 sw_discovery_cancel(discovery, oid);
87
88 memset(&i, 0, sizeof(i));
89 i.name = name;
90
91 if (!b->callback)
92 goto fail;
93
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;
100 else
101 goto fail;
102
103
104 snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
105 i.server = a;
106
107 if (text_record && text_record_len) {
108 char key[SW_TEXT_RECORD_MAX_LEN];
109 uint8_t val[SW_TEXT_RECORD_MAX_LEN];
110 uint32_t val_len;
111
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");
114 goto fail;
115 }
116
117 free_iterator = 1;
118
119 while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
120 c = pa_xstrndup((char*) val, val_len);
121
122 if (!strcmp(key, "device")) {
123 device_found = 1;
124 pa_xfree((char*) i.device);
125 i.device = c;
126 c = NULL;
127 } else if (!strcmp(key, "server-version")) {
128 pa_xfree((char*) i.server_version);
129 i.server_version = c;
130 c = NULL;
131 } else if (!strcmp(key, "user-name")) {
132 pa_xfree((char*) i.user_name);
133 i.user_name = c;
134 c = NULL;
135 } else if (!strcmp(key, "fqdn")) {
136 size_t l;
137
138 pa_xfree((char*) i.fqdn);
139 i.fqdn = c;
140 c = NULL;
141
142 l = strlen(a);
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")) {
147
148 if (pa_atou(c, &cookie) < 0)
149 goto fail;
150
151 i.cookie = &cookie;
152 } else if (!strcmp(key, "description")) {
153 pa_xfree((char*) i.description);
154 i.description = c;
155 c = NULL;
156 } else if (!strcmp(key, "typeid")) {
157
158 if (pa_atou(c, &typeid) < 0)
159 goto fail;
160
161 i.typeid = &typeid;
162 } else if (!strcmp(key, "channels")) {
163 uint32_t ch;
164
165 if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
166 goto fail;
167
168 ss.channels = (uint8_t) ch;
169 ss_valid |= 1;
170
171 } else if (!strcmp(key, "rate")) {
172 if (pa_atou(c, &ss.rate) < 0)
173 goto fail;
174 ss_valid |= 2;
175 } else if (!strcmp(key, "format")) {
176
177 if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
178 goto fail;
179
180 ss_valid |= 4;
181 }
182
183 pa_xfree(c);
184 c = NULL;
185 }
186
187 }
188
189 /* No device txt record was sent for a sink or source service */
190 if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
191 goto fail;
192
193 if (ss_valid == 7)
194 i.sample_spec = &ss;
195
196
197 b->callback(b, opcode, &i, b->callback_userdata);
198
199 fail:
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);
205 pa_xfree(c);
206
207 if (free_iterator)
208 sw_text_record_iterator_fina(iterator);
209
210
211 return SW_OKAY;
212 }
213
214 static sw_result browse_reply(
215 sw_discovery discovery,
216 sw_discovery_oid id,
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,
222 sw_opaque extra) {
223
224 pa_browser *b = extra;
225 assert(b);
226
227 switch (status) {
228 case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
229 sw_discovery_oid oid;
230
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");
233
234 break;
235 }
236
237 case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
238 if (b->callback) {
239 pa_browse_info i;
240 memset(&i, 0, sizeof(i));
241 i.name = name;
242 b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata);
243 }
244 break;
245
246 default:
247 ;
248 }
249
250 return SW_OKAY;
251 }
252
253 pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
254 pa_browser *b;
255 sw_discovery_oid oid;
256
257 b = pa_xmalloc(sizeof(pa_browser));
258 b->mainloop = mainloop;
259 b->ref = 1;
260 b->callback = NULL;
261 b->callback_userdata = NULL;
262
263 if (sw_discovery_init(&b->discovery) != SW_OKAY) {
264 pa_log("sw_discovery_init() failed.\n");
265 pa_xfree(b);
266 return NULL;
267 }
268
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) {
272
273 pa_log("sw_discovery_browse() failed.\n");
274
275 sw_discovery_fina(b->discovery);
276 pa_xfree(b);
277 return NULL;
278 }
279
280 b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
281 return b;
282 }
283
284 static void browser_free(pa_browser *b) {
285 assert(b && b->mainloop);
286
287 if (b->io_event)
288 b->mainloop->io_free(b->io_event);
289
290 sw_discovery_fina(b->discovery);
291 pa_xfree(b);
292 }
293
294 pa_browser *pa_browser_ref(pa_browser *b) {
295 assert(b && b->ref >= 1);
296 b->ref++;
297 return b;
298 }
299
300 void pa_browser_unref(pa_browser *b) {
301 assert(b && b->ref >= 1);
302
303 if ((-- (b->ref)) <= 0)
304 browser_free(b);
305 }
306
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) {
308 assert(b);
309
310 b->callback = cb;
311 b->callback_userdata = userdata;
312 }