]> code.delx.au - pulseaudio/blob - src/modules/module-tunnel.c
Update module-tunnel to the new protocol.
[pulseaudio] / src / modules / module-tunnel.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 published
8 by the Free Software Foundation; either version 2 of the License,
9 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 License
17 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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 #include <polyp/version.h>
35
36 #include <polypcore/module.h>
37 #include <polypcore/util.h>
38 #include <polypcore/modargs.h>
39 #include <polypcore/log.h>
40 #include <polypcore/core-subscribe.h>
41 #include <polypcore/xmalloc.h>
42 #include <polypcore/sink-input.h>
43 #include <polypcore/pdispatch.h>
44 #include <polypcore/pstream.h>
45 #include <polypcore/pstream-util.h>
46 #include <polypcore/authkey.h>
47 #include <polypcore/socket-client.h>
48 #include <polypcore/socket-util.h>
49 #include <polypcore/authkey-prop.h>
50
51 #ifdef TUNNEL_SINK
52 #include "module-tunnel-sink-symdef.h"
53 PA_MODULE_DESCRIPTION("Tunnel module for sinks")
54 PA_MODULE_USAGE("server=<address> sink=<remote sink name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> sink_name=<name for the local sink>")
55 #else
56 #include "module-tunnel-source-symdef.h"
57 PA_MODULE_DESCRIPTION("Tunnel module for sources")
58 PA_MODULE_USAGE("server=<address> source=<remote source name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> source_name=<name for the local source>")
59 #endif
60
61 PA_MODULE_AUTHOR("Lennart Poettering")
62 PA_MODULE_VERSION(PACKAGE_VERSION)
63
64 #define DEFAULT_SINK_NAME "tunnel"
65 #define DEFAULT_SOURCE_NAME "tunnel"
66
67 #define DEFAULT_TLENGTH (44100*2*2/10) //(10240*8)
68 #define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2)
69 #define DEFAULT_MINREQ 512
70 #define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ)
71 #define DEFAULT_FRAGSIZE 1024
72
73 #define DEFAULT_TIMEOUT 5
74
75 #define LATENCY_INTERVAL 10
76
77 static const char* const valid_modargs[] = {
78 "server",
79 "cookie",
80 "format",
81 "channels",
82 "rate",
83 #ifdef TUNNEL_SINK
84 "sink_name",
85 "sink",
86 #else
87 "source_name",
88 "source",
89 #endif
90 NULL,
91 };
92
93 static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
94
95 #ifdef TUNNEL_SINK
96 static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
97 #endif
98
99 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
100 #ifdef TUNNEL_SINK
101 [PA_COMMAND_REQUEST] = command_request,
102 #endif
103 [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
104 [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed
105 };
106
107 struct userdata {
108 pa_socket_client *client;
109 pa_pstream *pstream;
110 pa_pdispatch *pdispatch;
111
112 char *server_name;
113 #ifdef TUNNEL_SINK
114 char *sink_name;
115 pa_sink *sink;
116 uint32_t requested_bytes;
117 #else
118 char *source_name;
119 pa_source *source;
120 #endif
121
122 pa_module *module;
123 pa_core *core;
124
125 uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
126
127 uint32_t version;
128 uint32_t ctag;
129 uint32_t device_index;
130 uint32_t channel;
131
132 pa_usec_t host_latency;
133
134 pa_time_event *time_event;
135
136 int auth_cookie_in_property;
137 };
138
139 static void close_stuff(struct userdata *u) {
140 assert(u);
141
142 if (u->pstream) {
143 pa_pstream_close(u->pstream);
144 pa_pstream_unref(u->pstream);
145 u->pstream = NULL;
146 }
147
148 if (u->pdispatch) {
149 pa_pdispatch_unref(u->pdispatch);
150 u->pdispatch = NULL;
151 }
152
153 if (u->client) {
154 pa_socket_client_unref(u->client);
155 u->client = NULL;
156 }
157
158 #ifdef TUNNEL_SINK
159 if (u->sink) {
160 pa_sink_disconnect(u->sink);
161 pa_sink_unref(u->sink);
162 u->sink = NULL;
163 }
164 #else
165 if (u->source) {
166 pa_source_disconnect(u->source);
167 pa_source_unref(u->source);
168 u->source = NULL;
169 }
170 #endif
171
172 if (u->time_event) {
173 u->core->mainloop->time_free(u->time_event);
174 u->time_event = NULL;
175 }
176 }
177
178 static void die(struct userdata *u) {
179 assert(u);
180 close_stuff(u);
181 pa_module_unload_request(u->module);
182 }
183
184 static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
185 struct userdata *u = userdata;
186 assert(pd && t && u && u->pdispatch == pd);
187
188 pa_log(__FILE__": stream killed");
189 die(u);
190 }
191
192 #ifdef TUNNEL_SINK
193 static void send_prebuf_request(struct userdata *u) {
194 pa_tagstruct *t;
195
196 t = pa_tagstruct_new(NULL, 0);
197 pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM);
198 pa_tagstruct_putu32(t, u->ctag++);
199 pa_tagstruct_putu32(t, u->channel);
200 pa_pstream_send_tagstruct(u->pstream, t);
201 }
202
203 static void send_bytes(struct userdata *u) {
204 assert(u);
205
206 if (!u->pstream)
207 return;
208
209 while (u->requested_bytes > 0) {
210 pa_memchunk chunk;
211 if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) {
212
213
214 if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF)
215 send_prebuf_request(u);
216
217 return;
218 }
219
220 pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, &chunk);
221 pa_memblock_unref(chunk.memblock);
222
223 if (chunk.length > u->requested_bytes)
224 u->requested_bytes = 0;
225 else
226 u->requested_bytes -= chunk.length;
227 }
228 }
229
230 static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
231 struct userdata *u = userdata;
232 uint32_t bytes, channel;
233 assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd);
234
235 if (pa_tagstruct_getu32(t, &channel) < 0 ||
236 pa_tagstruct_getu32(t, &bytes) < 0 ||
237 !pa_tagstruct_eof(t)) {
238 pa_log(__FILE__": invalid protocol reply");
239 die(u);
240 return;
241 }
242
243 if (channel != u->channel) {
244 pa_log(__FILE__": recieved data for invalid channel");
245 die(u);
246 return;
247 }
248
249 u->requested_bytes += bytes;
250 send_bytes(u);
251 }
252
253 #endif
254
255 static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
256 struct userdata *u = userdata;
257 pa_usec_t buffer_usec, sink_usec, source_usec, transport_usec;
258 int playing;
259 uint32_t queue_length;
260 int64_t write_index, read_index;
261 struct timeval local, remote, now;
262 assert(pd && u);
263
264 if (command != PA_COMMAND_REPLY) {
265 if (command == PA_COMMAND_ERROR)
266 pa_log(__FILE__": failed to get latency.");
267 else
268 pa_log(__FILE__": protocol error.");
269 die(u);
270 return;
271 }
272
273 if (pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
274 pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
275 pa_tagstruct_get_usec(t, &source_usec) < 0 ||
276 pa_tagstruct_get_boolean(t, &playing) < 0 ||
277 pa_tagstruct_getu32(t, &queue_length) < 0 ||
278 pa_tagstruct_get_timeval(t, &local) < 0 ||
279 pa_tagstruct_get_timeval(t, &remote) < 0 ||
280 pa_tagstruct_gets64(t, &write_index) < 0 ||
281 pa_tagstruct_gets64(t, &read_index) < 0 ||
282 !pa_tagstruct_eof(t)) {
283 pa_log(__FILE__": invalid reply.");
284 die(u);
285 return;
286 }
287
288 pa_gettimeofday(&now);
289
290 if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
291 /* local and remote seem to have synchronized clocks */
292 #ifdef TUNNEL_SINK
293 transport_usec = pa_timeval_diff(&remote, &local);
294 #else
295 transport_usec = pa_timeval_diff(&now, &remote);
296 #endif
297 } else
298 transport_usec = pa_timeval_diff(&now, &local)/2;
299
300 #ifdef TUNNEL_SINK
301 u->host_latency = sink_usec + transport_usec;
302 #else
303 u->host_latency = source_usec + transport_usec;
304 if (u->host_latency > sink_usec)
305 u->host_latency -= sink_usec;
306 else
307 u->host_latency = 0;
308 #endif
309
310 /* pa_log(__FILE__": estimated host latency: %0.0f usec", (double) u->host_latency); */
311 }
312
313 static void request_latency(struct userdata *u) {
314 pa_tagstruct *t;
315 struct timeval now;
316 uint32_t tag;
317 assert(u);
318
319 t = pa_tagstruct_new(NULL, 0);
320 #ifdef TUNNEL_SINK
321 pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
322 #else
323 pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
324 #endif
325 pa_tagstruct_putu32(t, tag = u->ctag++);
326 pa_tagstruct_putu32(t, u->channel);
327
328 pa_gettimeofday(&now);
329 pa_tagstruct_put_timeval(t, &now);
330
331 pa_pstream_send_tagstruct(u->pstream, t);
332 pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u);
333 }
334
335 static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
336 struct userdata *u = userdata;
337 assert(pd && u && u->pdispatch == pd);
338
339 if (command != PA_COMMAND_REPLY) {
340 if (command == PA_COMMAND_ERROR)
341 pa_log(__FILE__": failed to create stream.");
342 else
343 pa_log(__FILE__": protocol error.");
344 die(u);
345 return;
346 }
347
348 if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
349 pa_tagstruct_getu32(t, &u->device_index) < 0 ||
350 #ifdef TUNNEL_SINK
351 pa_tagstruct_getu32(t, &u->requested_bytes) < 0 ||
352 #endif
353 !pa_tagstruct_eof(t)) {
354 pa_log(__FILE__": invalid reply.");
355 die(u);
356 return;
357 }
358
359 request_latency(u);
360 #ifdef TUNNEL_SINK
361 send_bytes(u);
362 #endif
363 }
364
365 static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
366 struct userdata *u = userdata;
367 pa_tagstruct *reply;
368 char name[256], un[128], hn[128];
369 #ifdef TUNNEL_SINK
370 pa_cvolume volume;
371 #endif
372 assert(pd && u && u->pdispatch == pd);
373
374 if (command != PA_COMMAND_REPLY ||
375 pa_tagstruct_getu32(t, &u->version) < 0 ||
376 !pa_tagstruct_eof(t)) {
377 if (command == PA_COMMAND_ERROR)
378 pa_log(__FILE__": failed to authenticate");
379 else
380 pa_log(__FILE__": protocol error.");
381 die(u);
382 return;
383 }
384
385 /* Minimum supported protocol version */
386 if (u->version < 8) {
387 pa_log(__FILE__": incompatible protocol version");
388 die(u);
389 return;
390 }
391
392 #ifdef TUNNEL_SINK
393 snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', sink '%s'",
394 pa_get_host_name(hn, sizeof(hn)),
395 pa_get_user_name(un, sizeof(un)),
396 u->sink->name);
397 #else
398 snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', source '%s'",
399 pa_get_host_name(hn, sizeof(hn)),
400 pa_get_user_name(un, sizeof(un)),
401 u->source->name);
402 #endif
403
404 reply = pa_tagstruct_new(NULL, 0);
405 pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
406 pa_tagstruct_putu32(reply, tag = u->ctag++);
407 pa_tagstruct_puts(reply, name);
408 pa_pstream_send_tagstruct(u->pstream, reply);
409 /* We ignore the server's reply here */
410
411 reply = pa_tagstruct_new(NULL, 0);
412 #ifdef TUNNEL_SINK
413 pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
414 pa_tagstruct_putu32(reply, tag = u->ctag++);
415 pa_tagstruct_puts(reply, name);
416 pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
417 pa_tagstruct_put_channel_map(reply, &u->sink->channel_map);
418 pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
419 pa_tagstruct_puts(reply, u->sink_name);
420 pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
421 pa_tagstruct_put_boolean(reply, 0);
422 pa_tagstruct_putu32(reply, DEFAULT_TLENGTH);
423 pa_tagstruct_putu32(reply, DEFAULT_PREBUF);
424 pa_tagstruct_putu32(reply, DEFAULT_MINREQ);
425 pa_tagstruct_putu32(reply, 0);
426 pa_cvolume_reset(&volume, u->sink->sample_spec.channels);
427 pa_tagstruct_put_cvolume(reply, &volume);
428 #else
429 pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
430 pa_tagstruct_putu32(reply, tag = u->ctag++);
431 pa_tagstruct_puts(reply, name);
432 pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
433 pa_tagstruct_put_channel_map(reply, &u->source->channel_map);
434 pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
435 pa_tagstruct_puts(reply, u->source_name);
436 pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
437 pa_tagstruct_put_boolean(reply, 0);
438 pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE);
439 #endif
440
441 pa_pstream_send_tagstruct(u->pstream, reply);
442 pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u);
443 }
444
445 static void pstream_die_callback(pa_pstream *p, void *userdata) {
446 struct userdata *u = userdata;
447 assert(p && u);
448
449 pa_log(__FILE__": stream died.");
450 die(u);
451 }
452
453
454 static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const void*creds, void *userdata) {
455 struct userdata *u = userdata;
456 assert(p && packet && u);
457
458 if (pa_pdispatch_run(u->pdispatch, packet, creds, u) < 0) {
459 pa_log(__FILE__": invalid packet");
460 die(u);
461 }
462 }
463
464 #ifndef TUNNEL_SINK
465 static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
466 struct userdata *u = userdata;
467 assert(p && chunk && u);
468
469 if (channel != u->channel) {
470 pa_log(__FILE__": recieved memory block on bad channel.");
471 die(u);
472 return;
473 }
474
475 pa_source_post(u->source, chunk);
476 }
477 #endif
478
479 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
480 struct userdata *u = userdata;
481 pa_tagstruct *t;
482 uint32_t tag;
483 assert(sc && u && u->client == sc);
484
485 pa_socket_client_unref(u->client);
486 u->client = NULL;
487
488 if (!io) {
489 pa_log(__FILE__": connection failed.");
490 pa_module_unload_request(u->module);
491 return;
492 }
493
494 u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->memblock_stat);
495 u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX);
496
497 pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
498 pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u);
499 #ifndef TUNNEL_SINK
500 pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u);
501 #endif
502
503 t = pa_tagstruct_new(NULL, 0);
504 pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
505 pa_tagstruct_putu32(t, tag = u->ctag++);
506 pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
507 pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie));
508 pa_pstream_send_tagstruct(u->pstream, t);
509 pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u);
510
511 }
512
513 #ifdef TUNNEL_SINK
514 static void sink_notify(pa_sink*sink) {
515 struct userdata *u;
516 assert(sink && sink->userdata);
517 u = sink->userdata;
518
519 send_bytes(u);
520 }
521
522 static pa_usec_t sink_get_latency(pa_sink *sink) {
523 struct userdata *u;
524 uint32_t l;
525 pa_usec_t usec = 0;
526 assert(sink && sink->userdata);
527 u = sink->userdata;
528
529 l = DEFAULT_TLENGTH;
530
531 if (l > u->requested_bytes) {
532 l -= u->requested_bytes;
533 usec += pa_bytes_to_usec(l, &u->sink->sample_spec);
534 }
535
536 usec += u->host_latency;
537
538 return usec;
539 }
540 #else
541 static pa_usec_t source_get_latency(pa_source *source) {
542 struct userdata *u;
543 assert(source && source->userdata);
544 u = source->userdata;
545
546 return u->host_latency;
547 }
548 #endif
549
550 static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
551 struct userdata *u = userdata;
552 struct timeval ntv;
553 assert(m && e && u);
554
555 request_latency(u);
556
557 pa_gettimeofday(&ntv);
558 ntv.tv_sec += LATENCY_INTERVAL;
559 m->time_restart(e, &ntv);
560 }
561
562 static int load_key(struct userdata *u, const char*fn) {
563 assert(u);
564
565 u->auth_cookie_in_property = 0;
566
567 if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
568 pa_log_debug(__FILE__": using already loaded auth cookie.");
569 pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
570 u->auth_cookie_in_property = 1;
571 return 0;
572 }
573
574 if (!fn)
575 fn = PA_NATIVE_COOKIE_FILE;
576
577 if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
578 return -1;
579
580 pa_log_debug(__FILE__": loading cookie from disk.");
581
582 if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
583 u->auth_cookie_in_property = 1;
584
585 return 0;
586 }
587
588 int pa__init(pa_core *c, pa_module*m) {
589 pa_modargs *ma = NULL;
590 struct userdata *u = NULL;
591 pa_sample_spec ss;
592 struct timeval ntv;
593 assert(c && m);
594
595 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
596 pa_log(__FILE__": failed to parse module arguments");
597 goto fail;
598 }
599
600 u = pa_xmalloc(sizeof(struct userdata));
601 m->userdata = u;
602 u->module = m;
603 u->core = c;
604 u->client = NULL;
605 u->pdispatch = NULL;
606 u->pstream = NULL;
607 u->server_name = NULL;
608 #ifdef TUNNEL_SINK
609 u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
610 u->sink = NULL;
611 u->requested_bytes = 0;
612 #else
613 u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
614 u->source = NULL;
615 #endif
616 u->ctag = 1;
617 u->device_index = u->channel = PA_INVALID_INDEX;
618 u->host_latency = 0;
619 u->auth_cookie_in_property = 0;
620 u->time_event = NULL;
621
622 if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
623 goto fail;
624
625 if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
626 pa_log(__FILE__": no server specified.");
627 goto fail;
628 }
629
630 ss = c->default_sample_spec;
631 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
632 pa_log(__FILE__": invalid sample format specification");
633 goto fail;
634 }
635
636 if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
637 pa_log(__FILE__": failed to connect to server '%s'", u->server_name);
638 goto fail;
639 }
640
641 if (!u->client)
642 goto fail;
643
644 pa_socket_client_set_callback(u->client, on_connection, u);
645
646 #ifdef TUNNEL_SINK
647 if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
648 pa_log(__FILE__": failed to create sink.");
649 goto fail;
650 }
651
652 u->sink->notify = sink_notify;
653 u->sink->get_latency = sink_get_latency;
654 u->sink->userdata = u;
655 u->sink->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->sink_name ? u->sink_name : "", u->sink_name ? "@" : "", u->server_name);
656
657 pa_sink_set_owner(u->sink, m);
658 #else
659 if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) {
660 pa_log(__FILE__": failed to create source.");
661 goto fail;
662 }
663
664 u->source->get_latency = source_get_latency;
665 u->source->userdata = u;
666 u->source->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->source_name ? u->source_name : "", u->source_name ? "@" : "", u->server_name);
667
668 pa_source_set_owner(u->source, m);
669 #endif
670
671 pa_gettimeofday(&ntv);
672 ntv.tv_sec += LATENCY_INTERVAL;
673 u->time_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, u);
674
675 pa_modargs_free(ma);
676
677 return 0;
678
679 fail:
680 pa__done(c, m);
681
682 if (ma)
683 pa_modargs_free(ma);
684 return -1;
685 }
686
687 void pa__done(pa_core *c, pa_module*m) {
688 struct userdata* u;
689 assert(c && m);
690
691 if (!(u = m->userdata))
692 return;
693
694 close_stuff(u);
695
696 if (u->auth_cookie_in_property)
697 pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
698
699 #ifdef TUNNEL_SINK
700 pa_xfree(u->sink_name);
701 #else
702 pa_xfree(u->source_name);
703 #endif
704 pa_xfree(u->server_name);
705
706 pa_xfree(u);
707 }
708
709