]> code.delx.au - pulseaudio/blob - src/modules/module-virtual-source.c
Remove unnecessary #includes
[pulseaudio] / src / modules / module-virtual-source.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2010 Intel Corporation
5 Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28
29 #include <pulse/xmalloc.h>
30 #include <pulse/i18n.h>
31
32 #include <pulsecore/macro.h>
33 #include <pulsecore/namereg.h>
34 #include <pulsecore/sink.h>
35 #include <pulsecore/module.h>
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/modargs.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/rtpoll.h>
40 #include <pulsecore/sample-util.h>
41 #include <pulsecore/ltdl-helper.h>
42
43 #include "module-virtual-source-symdef.h"
44
45 PA_MODULE_AUTHOR("Pierre-Louis Bossart");
46 PA_MODULE_DESCRIPTION("Virtual source");
47 PA_MODULE_VERSION(PACKAGE_VERSION);
48 PA_MODULE_LOAD_ONCE(FALSE);
49 PA_MODULE_USAGE(
50 _("source_name=<name for the source> "
51 "source_properties=<properties for the source> "
52 "master=<name of source to filter> "
53 "uplink_sink=<name> (optional)"
54 "format=<sample format> "
55 "rate=<sample rate> "
56 "channels=<number of channels> "
57 "channel_map=<channel map> "
58 "use_volume_sharing=<yes or no> "
59 "force_flat_volume=<yes or no> "
60 ));
61
62 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
63 #define BLOCK_USEC 1000 /* FIXME */
64
65 struct userdata {
66 pa_module *module;
67
68 /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
69 /* pa_bool_t autoloaded; */
70
71 pa_source *source;
72 pa_source_output *source_output;
73
74 pa_memblockq *memblockq;
75
76 pa_bool_t auto_desc;
77 unsigned channels;
78
79 /* optional fields for uplink sink */
80 pa_sink *sink;
81 pa_usec_t block_usec;
82 pa_memblockq *sink_memblockq;
83
84 };
85
86 static const char* const valid_modargs[] = {
87 "source_name",
88 "source_properties",
89 "master",
90 "uplink_sink",
91 "format",
92 "rate",
93 "channels",
94 "channel_map",
95 "use_volume_sharing",
96 "force_flat_volume",
97 NULL
98 };
99
100 /* Called from I/O thread context */
101 static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
102
103 switch (code) {
104
105 case PA_SINK_MESSAGE_GET_LATENCY:
106
107 /* there's no real latency here */
108 *((pa_usec_t*) data) = 0;
109
110 return 0;
111 }
112
113 return pa_sink_process_msg(o, code, data, offset, chunk);
114 }
115
116 /* Called from main context */
117 static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
118 struct userdata *u;
119
120 pa_sink_assert_ref(s);
121 pa_assert_se(u = s->userdata);
122
123 if (!PA_SINK_IS_LINKED(state)) {
124 return 0;
125 }
126
127 if (state == PA_SINK_RUNNING) {
128 /* need to wake-up source if it was suspended */
129 pa_source_suspend(u->source, FALSE, PA_SUSPEND_ALL);
130
131 /* FIXME: if there's no client connected, the source will suspend
132 and playback will be stuck. You'd want to prevent the source from
133 sleeping when the uplink sink is active; even if the audio is
134 discarded at least the app isn't stuck */
135
136 } else {
137 /* nothing to do, if the sink becomes idle or suspended let
138 module-suspend-idle handle the sources later */
139 }
140
141 return 0;
142 }
143
144 static void sink_update_requested_latency_cb(pa_sink *s) {
145 struct userdata *u;
146
147 pa_sink_assert_ref(s);
148 pa_assert_se(u = s->userdata);
149
150 /* FIXME: there's no latency support */
151
152 }
153
154
155 /* Called from I/O thread context */
156 static void sink_request_rewind_cb(pa_sink *s) {
157 struct userdata *u;
158
159 pa_sink_assert_ref(s);
160 pa_assert_se(u = s->userdata);
161
162 /* Do nothing */
163 pa_sink_process_rewind(u->sink, 0);
164
165 }
166
167 /* Called from I/O thread context */
168 static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
169 struct userdata *u = PA_SOURCE(o)->userdata;
170
171 switch (code) {
172
173 case PA_SOURCE_MESSAGE_GET_LATENCY:
174
175 /* The source is _put() before the source output is, so let's
176 * make sure we don't access it in that time. Also, the
177 * source output is first shut down, the source second. */
178 if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
179 !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
180 *((pa_usec_t*) data) = 0;
181 return 0;
182 }
183
184 *((pa_usec_t*) data) =
185
186 /* Get the latency of the master source */
187 pa_source_get_latency_within_thread(u->source_output->source) +
188
189 /* Add the latency internal to our source output on top */
190 /* FIXME, no idea what I am doing here */
191 pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec);
192
193 return 0;
194 }
195
196 return pa_source_process_msg(o, code, data, offset, chunk);
197 }
198
199 /* Called from main context */
200 static int source_set_state_cb(pa_source *s, pa_source_state_t state) {
201 struct userdata *u;
202
203 pa_source_assert_ref(s);
204 pa_assert_se(u = s->userdata);
205
206 if (!PA_SOURCE_IS_LINKED(state) ||
207 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
208 return 0;
209
210 pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED);
211 return 0;
212 }
213
214 /* Called from I/O thread context */
215 static void source_update_requested_latency_cb(pa_source *s) {
216 struct userdata *u;
217
218 pa_source_assert_ref(s);
219 pa_assert_se(u = s->userdata);
220
221 if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
222 !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state))
223 return;
224
225 /* Just hand this one over to the master source */
226 pa_source_output_set_requested_latency_within_thread(
227 u->source_output,
228 pa_source_get_requested_latency_within_thread(s));
229 }
230
231 /* Called from main context */
232 static void source_set_volume_cb(pa_source *s) {
233 struct userdata *u;
234
235 pa_source_assert_ref(s);
236 pa_assert_se(u = s->userdata);
237
238 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
239 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
240 return;
241
242 pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, TRUE);
243 }
244
245 /* Called from main context */
246 static void source_set_mute_cb(pa_source *s) {
247 struct userdata *u;
248
249 pa_source_assert_ref(s);
250 pa_assert_se(u = s->userdata);
251
252 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
253 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
254 return;
255
256 pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
257 }
258
259 /* Called from input thread context */
260 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
261 struct userdata *u;
262
263 pa_source_output_assert_ref(o);
264 pa_source_output_assert_io_context(o);
265 pa_assert_se(u = o->userdata);
266
267 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
268 pa_log("push when no link?");
269 return;
270 }
271
272 /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */
273
274 /* if uplink sink exists, pull data from there; simplify by using
275 same length as chunk provided by source */
276 if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
277 pa_memchunk tchunk;
278 size_t nbytes = chunk->length;
279 pa_mix_info streams[2];
280 pa_memchunk target_chunk;
281 void *target;
282 int ch;
283
284 /* Hmm, process any rewind request that might be queued up */
285 pa_sink_process_rewind(u->sink, 0);
286
287 /* get data from the sink */
288 while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) {
289 pa_memchunk nchunk;
290
291 /* make sure we get nbytes from the sink with render_full,
292 otherwise we cannot mix with the uplink */
293 pa_sink_render_full(u->sink, nbytes, &nchunk);
294 pa_memblockq_push(u->sink_memblockq, &nchunk);
295 pa_memblock_unref(nchunk.memblock);
296 }
297 pa_assert(tchunk.length == chunk->length);
298
299 /* move the read pointer for sink memblockq */
300 pa_memblockq_drop(u->sink_memblockq, tchunk.length);
301
302 /* allocate target chunk */
303 /* this could probably be done in-place, but having chunk as both
304 the input and output creates issues with reference counts */
305 target_chunk.index = 0;
306 target_chunk.length = chunk->length;
307 pa_assert(target_chunk.length == chunk->length);
308
309 target_chunk.memblock = pa_memblock_new(o->source->core->mempool,
310 target_chunk.length);
311 pa_assert( target_chunk.memblock );
312
313 /* get target pointer */
314 target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock)
315 + target_chunk.index);
316
317 /* set-up mixing structure
318 volume was taken care of in sink and source already */
319 streams[0].chunk = *chunk;
320 for(ch=0;ch<o->sample_spec.channels;ch++)
321 streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
322 streams[0].volume.channels = o->sample_spec.channels;
323
324 streams[1].chunk = tchunk;
325 for(ch=0;ch<o->sample_spec.channels;ch++)
326 streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
327 streams[1].volume.channels = o->sample_spec.channels;
328
329 /* do mixing */
330 pa_mix(streams, /* 2 streams to be mixed */
331 2,
332 target, /* put result in target chunk */
333 chunk->length, /* same length as input */
334 (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */
335 NULL, /* no volume information */
336 FALSE); /* no mute */
337
338 pa_memblock_release(target_chunk.memblock);
339 pa_memblock_unref(tchunk.memblock); /* clean-up */
340
341 /* forward the data to the virtual source */
342 pa_source_post(u->source, &target_chunk);
343
344 pa_memblock_unref(target_chunk.memblock); /* clean-up */
345
346 } else {
347 /* forward the data to the virtual source */
348 pa_source_post(u->source, chunk);
349 }
350
351
352 }
353
354 /* Called from input thread context */
355 static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
356 struct userdata *u;
357
358 pa_source_output_assert_ref(o);
359 pa_source_output_assert_io_context(o);
360 pa_assert_se(u = o->userdata);
361
362 /* FIXME, no idea what I am doing here */
363 #if 0
364 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
365 u->send_counter -= (int64_t) nbytes;
366 #endif
367 }
368
369 /* Called from output thread context */
370 static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
371
372 /* FIXME, nothing to do here ? */
373
374 return pa_source_output_process_msg(obj, code, data, offset, chunk);
375 }
376
377 /* Called from output thread context */
378 static void source_output_attach_cb(pa_source_output *o) {
379 struct userdata *u;
380
381 pa_source_output_assert_ref(o);
382 pa_source_output_assert_io_context(o);
383 pa_assert_se(u = o->userdata);
384
385 pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll);
386 pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
387 pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
388 pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
389
390 pa_source_attach_within_thread(u->source);
391 }
392
393 /* Called from output thread context */
394 static void source_output_detach_cb(pa_source_output *o) {
395 struct userdata *u;
396
397 pa_source_output_assert_ref(o);
398 pa_source_output_assert_io_context(o);
399 pa_assert_se(u = o->userdata);
400
401 pa_source_detach_within_thread(u->source);
402 pa_source_set_rtpoll(u->source, NULL);
403 }
404
405 /* Called from output thread context */
406 static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
407 struct userdata *u;
408
409 pa_source_output_assert_ref(o);
410 pa_source_output_assert_io_context(o);
411 pa_assert_se(u = o->userdata);
412
413 /* FIXME */
414 #if 0
415 if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) {
416
417 u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source),
418 u->latency),
419 &o->sample_spec);
420
421 pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
422 }
423 #endif
424 }
425
426 /* Called from main thread */
427 static void source_output_kill_cb(pa_source_output *o) {
428 struct userdata *u;
429
430 pa_source_output_assert_ref(o);
431 pa_assert_ctl_context();
432 pa_assert_se(u = o->userdata);
433
434 /* The order here matters! We first kill the source output, followed
435 * by the source. That means the source callbacks must be protected
436 * against an unconnected source output! */
437 pa_source_output_unlink(u->source_output);
438 pa_source_unlink(u->source);
439
440 pa_source_output_unref(u->source_output);
441 u->source_output = NULL;
442
443 pa_source_unref(u->source);
444 u->source = NULL;
445
446 pa_module_unload_request(u->module, TRUE);
447 }
448
449 /* Called from main thread */
450 static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
451 struct userdata *u;
452
453 pa_source_output_assert_ref(o);
454 pa_assert_ctl_context();
455 pa_assert_se(u = o->userdata);
456
457 /* FIXME */
458 //return dest != u->source_input->source->monitor_source;
459
460 return TRUE;
461 }
462
463 /* Called from main thread */
464 static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
465 struct userdata *u;
466
467 pa_source_output_assert_ref(o);
468 pa_assert_ctl_context();
469 pa_assert_se(u = o->userdata);
470
471 if (dest) {
472 pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
473 pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
474 } else
475 pa_source_set_asyncmsgq(u->source, NULL);
476
477 if (u->auto_desc && dest) {
478 const char *z;
479 pa_proplist *pl;
480
481 pl = pa_proplist_new();
482 z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
483 pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s",
484 pa_proplist_gets(u->source->proplist, "device.vsource.name"), z ? z : dest->name);
485
486 pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl);
487 pa_proplist_free(pl);
488 }
489 }
490
491
492 int pa__init(pa_module*m) {
493 struct userdata *u;
494 pa_sample_spec ss;
495 pa_channel_map map;
496 pa_modargs *ma;
497 pa_source *master=NULL;
498 pa_source_output_new_data source_output_data;
499 pa_source_new_data source_data;
500 pa_bool_t use_volume_sharing = FALSE;
501 pa_bool_t force_flat_volume = FALSE;
502
503 /* optional for uplink_sink */
504 pa_sink_new_data sink_data;
505 size_t nbytes;
506
507 pa_assert(m);
508
509 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
510 pa_log("Failed to parse module arguments.");
511 goto fail;
512 }
513
514 if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SOURCE))) {
515 pa_log("Master source not found");
516 goto fail;
517 }
518
519 pa_assert(master);
520
521 ss = master->sample_spec;
522 ss.format = PA_SAMPLE_FLOAT32;
523 map = master->channel_map;
524 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
525 pa_log("Invalid sample format specification or channel map");
526 goto fail;
527 }
528
529 if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
530 pa_log("use_volume_sharing= expects a boolean argument");
531 goto fail;
532 }
533
534 if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
535 pa_log("force_flat_volume= expects a boolean argument");
536 goto fail;
537 }
538
539 if (use_volume_sharing && force_flat_volume) {
540 pa_log("Flat volume can't be forced when using volume sharing.");
541 goto fail;
542 }
543
544 u = pa_xnew0(struct userdata, 1);
545 if (!u) {
546 pa_log("Failed to alloc userdata");
547 goto fail;
548 }
549 u->module = m;
550 m->userdata = u;
551 u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
552 if (!u->memblockq) {
553 pa_log("Failed to create source memblockq.");
554 goto fail;
555 }
556 u->channels = ss.channels;
557
558 /* Create source */
559 pa_source_new_data_init(&source_data);
560 source_data.driver = __FILE__;
561 source_data.module = m;
562 if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
563 source_data.name = pa_sprintf_malloc("%s.vsource", master->name);
564 pa_source_new_data_set_sample_spec(&source_data, &ss);
565 pa_source_new_data_set_channel_map(&source_data, &map);
566 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
567 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
568 pa_proplist_sets(source_data.proplist, "device.vsource.name", source_data.name);
569
570 if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
571 pa_log("Invalid properties");
572 pa_source_new_data_done(&source_data);
573 goto fail;
574 }
575
576 if ((u->auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
577 const char *z;
578
579 z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
580 pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", source_data.name, z ? z : master->name);
581 }
582
583 u->source = pa_source_new(m->core, &source_data, (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY))
584 | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)
585 | (force_flat_volume ? PA_SOURCE_FLAT_VOLUME : 0));
586
587 pa_source_new_data_done(&source_data);
588
589 if (!u->source) {
590 pa_log("Failed to create source.");
591 goto fail;
592 }
593
594 u->source->parent.process_msg = source_process_msg_cb;
595 u->source->set_state = source_set_state_cb;
596 u->source->update_requested_latency = source_update_requested_latency_cb;
597 u->source->set_volume = use_volume_sharing ? NULL : source_set_volume_cb;
598 u->source->set_mute = source_set_mute_cb;
599 u->source->userdata = u;
600
601 pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
602
603 /* Create source output */
604 pa_source_output_new_data_init(&source_output_data);
605 source_output_data.driver = __FILE__;
606 source_output_data.module = m;
607 pa_source_output_new_data_set_source(&source_output_data, master, FALSE);
608 source_output_data.destination_source = u->source;
609 /* FIXME
610 source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */
611
612 pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Source Stream of %s", pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION));
613 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
614 pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
615 pa_source_output_new_data_set_channel_map(&source_output_data, &map);
616
617 pa_source_output_new(&u->source_output, m->core, &source_output_data);
618 pa_source_output_new_data_done(&source_output_data);
619
620 if (!u->source_output)
621 goto fail;
622
623 u->source_output->parent.process_msg = source_output_process_msg_cb;
624 u->source_output->push = source_output_push_cb;
625 u->source_output->process_rewind = source_output_process_rewind_cb;
626 u->source_output->kill = source_output_kill_cb;
627 u->source_output->attach = source_output_attach_cb;
628 u->source_output->detach = source_output_detach_cb;
629 u->source_output->state_change = source_output_state_change_cb;
630 u->source_output->may_move_to = source_output_may_move_to_cb;
631 u->source_output->moving = source_output_moving_cb;
632 u->source_output->userdata = u;
633
634 u->source->output_from_master = u->source_output;
635
636 pa_source_put(u->source);
637 pa_source_output_put(u->source_output);
638
639 /* Create optional uplink sink */
640 pa_sink_new_data_init(&sink_data);
641 sink_data.driver = __FILE__;
642 sink_data.module = m;
643 if ((sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "uplink_sink", NULL)))) {
644 pa_sink_new_data_set_sample_spec(&sink_data, &ss);
645 pa_sink_new_data_set_channel_map(&sink_data, &map);
646 pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
647 pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "uplink sink");
648 pa_proplist_sets(sink_data.proplist, "device.uplink_sink.name", sink_data.name);
649
650 if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
651 const char *z;
652
653 z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
654 pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Uplink Sink %s on %s", sink_data.name, z ? z : master->name);
655 }
656
657 u->sink_memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
658 if (!u->sink_memblockq) {
659 pa_log("Failed to create sink memblockq.");
660 goto fail;
661 }
662
663 u->sink = pa_sink_new(m->core, &sink_data, 0); /* FIXME, sink has no capabilities */
664 pa_sink_new_data_done(&sink_data);
665
666 if (!u->sink) {
667 pa_log("Failed to create sink.");
668 goto fail;
669 }
670
671 u->sink->parent.process_msg = sink_process_msg_cb;
672 u->sink->update_requested_latency = sink_update_requested_latency_cb;
673 u->sink->request_rewind = sink_request_rewind_cb;
674 u->sink->set_state = sink_set_state_cb;
675 u->sink->userdata = u;
676
677 pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
678
679 /* FIXME: no idea what I am doing here */
680 u->block_usec = BLOCK_USEC;
681 nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
682 pa_sink_set_max_rewind(u->sink, nbytes);
683 pa_sink_set_max_request(u->sink, nbytes);
684
685 pa_sink_put(u->sink);
686 } else {
687 /* optional uplink sink not enabled */
688 u->sink = NULL;
689 }
690
691 pa_modargs_free(ma);
692
693 return 0;
694
695 fail:
696 if (ma)
697 pa_modargs_free(ma);
698
699 pa__done(m);
700
701 return -1;
702 }
703
704 int pa__get_n_used(pa_module *m) {
705 struct userdata *u;
706
707 pa_assert(m);
708 pa_assert_se(u = m->userdata);
709
710 return pa_source_linked_by(u->source);
711 }
712
713 void pa__done(pa_module*m) {
714 struct userdata *u;
715
716 pa_assert(m);
717
718 if (!(u = m->userdata))
719 return;
720
721 /* See comments in source_output_kill_cb() above regarding
722 * destruction order! */
723
724 if (u->source_output)
725 pa_source_output_unlink(u->source_output);
726
727 if (u->source)
728 pa_source_unlink(u->source);
729
730 if (u->source_output)
731 pa_source_output_unref(u->source_output);
732
733 if (u->source)
734 pa_source_unref(u->source);
735
736 if (u->sink)
737 pa_sink_unref(u->sink);
738
739 if (u->memblockq)
740 pa_memblockq_free(u->memblockq);
741
742 if (u->sink_memblockq)
743 pa_memblockq_free(u->sink_memblockq);
744
745 pa_xfree(u);
746 }