]> code.delx.au - pulseaudio/blob - polyp/polyplib-browser.c
* fix include file names in installed header files
[pulseaudio] / polyp / polyplib-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 "polyplib-browser.h"
26 #include "xmalloc.h"
27 #include "log.h"
28 #include "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 struct pa_browser {
35 int ref;
36 struct pa_mainloop_api *mainloop;
37
38 void (*callback)(struct pa_browser *z, enum pa_browse_opcode c, const struct pa_browse_info *i, void *userdata);
39 void *callback_userdata;
40
41 sw_discovery discovery;
42 struct pa_io_event *io_event;
43 };
44
45
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);
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
59 static sw_result resolve_reply(
60 sw_discovery discovery,
61 sw_discovery_oid oid,
62 sw_uint32 interface_index,
63 sw_const_string name,
64 sw_const_string type,
65 sw_const_string domain,
66 sw_ipv4_address address,
67 sw_port port,
68 sw_octets text_record,
69 sw_ulong text_record_len,
70 sw_opaque extra) {
71
72 struct pa_browser *b = extra;
73 struct pa_browse_info i;
74 char ip[256], a[256];
75 enum pa_browse_opcode opcode;
76 int device_found = 0;
77 uint32_t cookie;
78 pa_typeid_t typeid;
79 struct pa_sample_spec ss;
80 int ss_valid = 0;
81 sw_text_record_iterator iterator;
82 int free_iterator = 0;
83 char *c = NULL;
84
85 assert(b);
86
87 sw_discovery_cancel(discovery, oid);
88
89 memset(&i, 0, sizeof(i));
90 i.name = name;
91
92 if (!b->callback)
93 goto fail;
94
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;
101 else
102 goto fail;
103
104
105 snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
106 i.server = a;
107
108 if (text_record && text_record_len) {
109 char key[SW_TEXT_RECORD_MAX_LEN];
110 uint8_t val[SW_TEXT_RECORD_MAX_LEN];
111 uint32_t val_len;
112
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");
115 goto fail;
116 }
117
118 free_iterator = 1;
119
120 while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
121 c = pa_xstrndup((char*) val, val_len);
122
123 if (!strcmp(key, "device")) {
124 device_found = 1;
125 pa_xfree((char*) i.device);
126 i.device = c;
127 c = NULL;
128 } else if (!strcmp(key, "server-version")) {
129 pa_xfree((char*) i.server_version);
130 i.server_version = c;
131 c = NULL;
132 } else if (!strcmp(key, "user-name")) {
133 pa_xfree((char*) i.user_name);
134 i.user_name = c;
135 c = NULL;
136 } else if (!strcmp(key, "fqdn")) {
137 pa_xfree((char*) i.fqdn);
138 i.fqdn = c;
139 c = NULL;
140 } else if (!strcmp(key, "cookie")) {
141
142 if (pa_atou(c, &cookie) < 0)
143 goto fail;
144
145 i.cookie = &cookie;
146 } else if (!strcmp(key, "description")) {
147 pa_xfree((char*) i.description);
148 i.description = c;
149 c = NULL;
150 } else if (!strcmp(key, "typeid")) {
151
152 if (pa_atou(c, &typeid) < 0)
153 goto fail;
154
155 i.typeid = &typeid;
156 } else if (!strcmp(key, "channels")) {
157 uint32_t ch;
158
159 if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
160 goto fail;
161
162 ss.channels = (uint8_t) ch;
163 ss_valid |= 1;
164
165 } else if (!strcmp(key, "rate")) {
166 if (pa_atou(c, &ss.rate) < 0)
167 goto fail;
168 ss_valid |= 2;
169 } else if (!strcmp(key, "format")) {
170
171 if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
172 goto fail;
173
174 ss_valid |= 4;
175 }
176
177 pa_xfree(c);
178 c = NULL;
179 }
180
181 }
182
183 /* No device txt record was sent for a sink or source service */
184 if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
185 goto fail;
186
187 if (ss_valid == 7)
188 i.sample_spec = &ss;
189
190
191 b->callback(b, opcode, &i, b->callback_userdata);
192
193 fail:
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);
199 pa_xfree(c);
200
201 if (free_iterator)
202 sw_text_record_iterator_fina(iterator);
203
204
205 return SW_OKAY;
206 }
207
208 static sw_result browse_reply(
209 sw_discovery discovery,
210 sw_discovery_oid id,
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,
216 sw_opaque extra) {
217
218 struct pa_browser *b = extra;
219 assert(b);
220
221 switch (status) {
222 case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
223 sw_discovery_oid oid;
224 fprintf(stderr, "debug: new service: %s\n", name);
225
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");
228
229 break;
230 }
231
232 case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
233 if (b->callback) {
234 struct pa_browse_info i;
235 memset(&i, 0, sizeof(i));
236 i.name = name;
237 b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata);
238 }
239 break;
240
241 default:
242 ;
243 }
244
245 return SW_OKAY;
246 }
247
248 struct pa_browser *pa_browser_new(struct pa_mainloop_api *mainloop) {
249 struct pa_browser *b;
250 sw_discovery_oid oid;
251
252 b = pa_xmalloc(sizeof(struct pa_browser));
253 b->mainloop = mainloop;
254 b->ref = 1;
255 b->callback = NULL;
256 b->callback_userdata = NULL;
257
258 if (sw_discovery_init(&b->discovery) != SW_OKAY) {
259 pa_log("sw_discovery_init() failed.\n");
260 pa_xfree(b);
261 return NULL;
262 }
263
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) {
267
268 pa_log("sw_discovery_browse() failed.\n");
269
270 sw_discovery_fina(b->discovery);
271 pa_xfree(b);
272 return NULL;
273 }
274
275 b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
276 return b;
277 }
278
279 static void browser_free(struct pa_browser *b) {
280 assert(b && b->mainloop);
281
282 if (b->io_event)
283 b->mainloop->io_free(b->io_event);
284
285 sw_discovery_fina(b->discovery);
286 pa_xfree(b);
287 }
288
289 struct pa_browser *pa_browser_ref(struct pa_browser *b) {
290 assert(b && b->ref >= 1);
291 b->ref++;
292 return b;
293 }
294
295 void pa_browser_unref(struct pa_browser *b) {
296 assert(b && b->ref >= 1);
297
298 if ((-- (b->ref)) <= 0)
299 browser_free(b);
300 }
301
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) {
303 assert(b);
304
305 b->callback = cb;
306 b->callback_userdata = userdata;
307 }