2 This file is part of PulseAudio.
4 Copyright 2009 Intel Corporation
5 Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
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.
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.
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
30 #include <pulse/xmalloc.h>
32 #include <pulsecore/sink-input.h>
33 #include <pulsecore/module.h>
34 #include <pulsecore/modargs.h>
35 #include <pulsecore/namereg.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/core-util.h>
39 #include <pulse/rtclock.h>
40 #include <pulse/timeval.h>
42 #include "module-loopback-symdef.h"
44 PA_MODULE_AUTHOR("Pierre-Louis Bossart");
45 PA_MODULE_DESCRIPTION("Loopback from source to sink");
46 PA_MODULE_VERSION(PACKAGE_VERSION);
47 PA_MODULE_LOAD_ONCE(FALSE);
49 "source=<source to connect to> "
50 "sink=<sink to connect to> "
51 "adjust_time=<how often to readjust rates in s> "
52 "latency_msec=<latency in ms> "
53 "format=<sample format> "
55 "channels=<number of channels> "
56 "channel_map=<channel map> "
57 "sink_input_name=<custom name for the sink input> "
58 "source_output_name=<custom name for the source output> "
59 "sink_input_role=<media.role for the sink input> "
60 "source_output_role=<media.role for the source output>");
62 #define DEFAULT_LATENCY_MSEC 200
64 #define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
66 #define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
72 pa_sink_input *sink_input;
73 pa_source_output *source_output;
75 pa_asyncmsgq *asyncmsgq;
76 pa_memblockq *memblockq;
78 pa_rtpoll_item *rtpoll_item_read, *rtpoll_item_write;
80 pa_time_event *time_event;
81 pa_usec_t adjust_time;
90 size_t min_memblockq_length;
94 size_t source_output_buffer;
95 pa_usec_t source_latency;
98 size_t sink_input_buffer;
99 pa_usec_t sink_latency;
101 size_t min_memblockq_length;
106 static const char* const valid_modargs[] = {
115 "source_output_name",
117 "source_output_role",
122 SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
123 SINK_INPUT_MESSAGE_REWIND,
124 SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT,
125 SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
129 SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
132 /* Called from main context */
133 static void teardown(struct userdata *u) {
135 pa_assert_ctl_context();
138 pa_sink_input_unlink(u->sink_input);
140 if (u->source_output)
141 pa_source_output_unlink(u->source_output);
144 pa_sink_input_unref(u->sink_input);
145 u->sink_input = NULL;
148 if (u->source_output) {
149 pa_source_output_unref(u->source_output);
150 u->source_output = NULL;
154 /* Called from main context */
155 static void adjust_rates(struct userdata *u) {
157 uint32_t old_rate, base_rate, new_rate;
158 pa_usec_t buffer_latency;
161 pa_assert_ctl_context();
163 pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
164 pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
167 u->latency_snapshot.sink_input_buffer +
168 u->latency_snapshot.source_output_buffer;
170 if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter)
171 buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter);
173 buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter));
175 buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec);
177 pa_log_info("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
178 (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC,
179 (double) buffer_latency / PA_USEC_PER_MSEC,
180 (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
181 ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC);
183 pa_log_info("Should buffer %zu bytes, buffered at minimum %zu bytes",
184 u->latency_snapshot.max_request*2,
185 u->latency_snapshot.min_memblockq_length);
187 fs = pa_frame_size(&u->sink_input->sample_spec);
188 old_rate = u->sink_input->sample_spec.rate;
189 base_rate = u->source_output->sample_spec.rate;
191 if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2)
192 new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
194 new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
196 pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate);
198 pa_sink_input_set_rate(u->sink_input, new_rate);
200 pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time);
203 /* Called from main context */
204 static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
205 struct userdata *u = userdata;
209 pa_assert(u->time_event == e);
214 /* Called from input thread context */
215 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
219 pa_source_output_assert_ref(o);
220 pa_source_output_assert_io_context(o);
221 pa_assert_se(u = o->userdata);
223 if (u->skip > chunk->length) {
224 u->skip -= chunk->length;
230 copy.index += u->skip;
231 copy.length -= u->skip;
237 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, chunk, NULL);
238 u->send_counter += (int64_t) chunk->length;
241 /* Called from input thread context */
242 static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
245 pa_source_output_assert_ref(o);
246 pa_source_output_assert_io_context(o);
247 pa_assert_se(u = o->userdata);
249 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
250 u->send_counter -= (int64_t) nbytes;
253 /* Called from output thread context */
254 static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
255 struct userdata *u = PA_SOURCE_OUTPUT(obj)->userdata;
259 case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT: {
262 length = pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq);
264 u->latency_snapshot.send_counter = u->send_counter;
265 u->latency_snapshot.source_output_buffer = u->source_output->thread_info.resampler ? pa_resampler_result(u->source_output->thread_info.resampler, length) : length;
266 u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source);
272 return pa_source_output_process_msg(obj, code, data, offset, chunk);
275 /* Called from output thread context */
276 static void source_output_attach_cb(pa_source_output *o) {
279 pa_source_output_assert_ref(o);
280 pa_source_output_assert_io_context(o);
281 pa_assert_se(u = o->userdata);
283 u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
284 o->source->thread_info.rtpoll,
289 /* Called from output thread context */
290 static void source_output_detach_cb(pa_source_output *o) {
293 pa_source_output_assert_ref(o);
294 pa_source_output_assert_io_context(o);
295 pa_assert_se(u = o->userdata);
297 if (u->rtpoll_item_write) {
298 pa_rtpoll_item_free(u->rtpoll_item_write);
299 u->rtpoll_item_write = NULL;
303 /* Called from output thread context */
304 static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
307 pa_source_output_assert_ref(o);
308 pa_source_output_assert_io_context(o);
309 pa_assert_se(u = o->userdata);
311 if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) {
313 u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source),
317 pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
321 /* Called from main thread */
322 static void source_output_kill_cb(pa_source_output *o) {
325 pa_source_output_assert_ref(o);
326 pa_assert_ctl_context();
327 pa_assert_se(u = o->userdata);
330 pa_module_unload_request(u->module, TRUE);
333 /* Called from main thread */
334 static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
337 pa_source_output_assert_ref(o);
338 pa_assert_ctl_context();
339 pa_assert_se(u = o->userdata);
341 return dest != u->sink_input->sink->monitor_source;
344 /* Called from main thread */
345 static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
350 pa_source_output_assert_ref(o);
351 pa_assert_ctl_context();
352 pa_assert_se(u = o->userdata);
354 p = pa_proplist_new();
355 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
357 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
358 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
360 pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p);
364 /* Called from output thread context */
365 static void update_min_memblockq_length(struct userdata *u) {
369 pa_sink_input_assert_io_context(u->sink_input);
371 length = pa_memblockq_get_length(u->memblockq);
373 if (u->min_memblockq_length == (size_t) -1 ||
374 length < u->min_memblockq_length)
375 u->min_memblockq_length = length;
378 /* Called from output thread context */
379 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
382 pa_sink_input_assert_ref(i);
383 pa_sink_input_assert_io_context(i);
384 pa_assert_se(u = i->userdata);
388 while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
392 if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
393 pa_log_info("Coud not peek into queue");
397 chunk->length = PA_MIN(chunk->length, nbytes);
398 pa_memblockq_drop(u->memblockq, chunk->length);
400 update_min_memblockq_length(u);
405 /* Called from output thread context */
406 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
409 pa_sink_input_assert_ref(i);
410 pa_sink_input_assert_io_context(i);
411 pa_assert_se(u = i->userdata);
413 pa_memblockq_rewind(u->memblockq, nbytes);
416 /* Called from output thread context */
417 static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
418 struct userdata *u = PA_SINK_INPUT(obj)->userdata;
422 case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
425 pa_sink_input_assert_io_context(u->sink_input);
427 *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec);
429 /* Fall through, the default handler will add in the extra
430 * latency added by the resampler */
434 case SINK_INPUT_MESSAGE_POST:
436 pa_sink_input_assert_io_context(u->sink_input);
438 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
439 pa_memblockq_push_align(u->memblockq, chunk);
441 pa_memblockq_flush_write(u->memblockq, TRUE);
443 update_min_memblockq_length(u);
445 /* Is this the end of an underrun? Then let's start things
448 u->sink_input->thread_info.underrun_for > 0 &&
449 pa_memblockq_is_readable(u->memblockq)) {
451 pa_log_debug("Requesting rewind due to end of underrun.");
452 pa_sink_input_request_rewind(u->sink_input,
453 (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for),
457 u->recv_counter += (int64_t) chunk->length;
461 case SINK_INPUT_MESSAGE_REWIND:
463 pa_sink_input_assert_io_context(u->sink_input);
465 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
466 pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE);
468 pa_memblockq_flush_write(u->memblockq, TRUE);
470 u->recv_counter -= offset;
472 update_min_memblockq_length(u);
476 case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
479 update_min_memblockq_length(u);
481 length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);
483 u->latency_snapshot.recv_counter = u->recv_counter;
484 u->latency_snapshot.sink_input_buffer =
485 pa_memblockq_get_length(u->memblockq) +
486 (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length);
487 u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink);
489 u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input);
491 u->latency_snapshot.min_memblockq_length = u->min_memblockq_length;
492 u->min_memblockq_length = (size_t) -1;
497 case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: {
498 /* This message is sent from the IO thread to the main
499 * thread! So don't be confused. All the user cases above
500 * are executed in thread context, but this one is not! */
502 pa_assert_ctl_context();
504 if (u->adjust_time > 0)
510 return pa_sink_input_process_msg(obj, code, data, offset, chunk);
513 /* Called from output thread context */
514 static void sink_input_attach_cb(pa_sink_input *i) {
517 pa_sink_input_assert_ref(i);
518 pa_sink_input_assert_io_context(i);
519 pa_assert_se(u = i->userdata);
521 u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
522 i->sink->thread_info.rtpoll,
526 pa_memblockq_set_prebuf(u->memblockq, pa_sink_input_get_max_request(i)*2);
527 pa_memblockq_set_maxrewind(u->memblockq, pa_sink_input_get_max_rewind(i));
529 u->min_memblockq_length = (size_t) -1;
532 /* Called from output thread context */
533 static void sink_input_detach_cb(pa_sink_input *i) {
536 pa_sink_input_assert_ref(i);
537 pa_sink_input_assert_io_context(i);
538 pa_assert_se(u = i->userdata);
540 if (u->rtpoll_item_read) {
541 pa_rtpoll_item_free(u->rtpoll_item_read);
542 u->rtpoll_item_read = NULL;
546 /* Called from output thread context */
547 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
550 pa_sink_input_assert_ref(i);
551 pa_sink_input_assert_io_context(i);
552 pa_assert_se(u = i->userdata);
554 pa_memblockq_set_maxrewind(u->memblockq, nbytes);
557 /* Called from output thread context */
558 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
561 pa_sink_input_assert_ref(i);
562 pa_sink_input_assert_io_context(i);
563 pa_assert_se(u = i->userdata);
565 pa_memblockq_set_prebuf(u->memblockq, nbytes*2);
566 pa_log_info("Max request changed");
567 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED, NULL, 0, NULL, NULL);
570 /* Called from main thread */
571 static void sink_input_kill_cb(pa_sink_input *i) {
574 pa_sink_input_assert_ref(i);
575 pa_assert_ctl_context();
576 pa_assert_se(u = i->userdata);
579 pa_module_unload_request(u->module, TRUE);
582 /* Called from main thread */
583 static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
588 pa_sink_input_assert_ref(i);
589 pa_assert_ctl_context();
590 pa_assert_se(u = i->userdata);
592 p = pa_proplist_new();
593 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
595 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
596 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
598 pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
602 /* Called from main thread */
603 static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
606 pa_sink_input_assert_ref(i);
607 pa_assert_ctl_context();
608 pa_assert_se(u = i->userdata);
610 if (!u->source_output->source->monitor_of)
613 return dest != u->source_output->source->monitor_of;
616 int pa__init(pa_module *m) {
617 pa_modargs *ma = NULL;
620 pa_sink_input_new_data sink_input_data;
622 pa_source_output_new_data source_output_data;
623 uint32_t latency_msec;
627 uint32_t adjust_time_sec;
632 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
633 pa_log("Failed to parse module arguments");
637 if (!(source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
638 pa_log("No such source.");
642 if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
643 pa_log("No such sink.");
647 ss = sink->sample_spec;
648 map = sink->channel_map;
649 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
650 pa_log("Invalid sample format specification or channel map");
654 latency_msec = DEFAULT_LATENCY_MSEC;
655 if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) {
656 pa_log("Invalid latency specification");
660 m->userdata = u = pa_xnew0(struct userdata, 1);
663 u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
665 adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
666 if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
667 pa_log("Failed to parse adjust_time value");
671 if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
672 u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC;
674 u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
676 pa_sink_input_new_data_init(&sink_input_data);
677 sink_input_data.driver = __FILE__;
678 sink_input_data.module = m;
679 sink_input_data.sink = sink;
681 if ((n = pa_modargs_get_value(ma, "sink_input_name", NULL)))
682 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, n);
684 pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback from %s",
685 pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
687 if ((n = pa_modargs_get_value(ma, "sink_input_role", NULL)))
688 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, n);
690 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
692 if ((n = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)))
693 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
695 pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
696 pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
697 sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE;
699 pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
700 pa_sink_input_new_data_done(&sink_input_data);
705 u->sink_input->parent.process_msg = sink_input_process_msg_cb;
706 u->sink_input->pop = sink_input_pop_cb;
707 u->sink_input->process_rewind = sink_input_process_rewind_cb;
708 u->sink_input->kill = sink_input_kill_cb;
709 u->sink_input->attach = sink_input_attach_cb;
710 u->sink_input->detach = sink_input_detach_cb;
711 u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
712 u->sink_input->update_max_request = sink_input_update_max_request_cb;
713 u->sink_input->may_move_to = sink_input_may_move_to_cb;
714 u->sink_input->moving = sink_input_moving_cb;
715 u->sink_input->userdata = u;
717 pa_sink_input_set_requested_latency(u->sink_input, u->latency/3);
719 pa_source_output_new_data_init(&source_output_data);
720 source_output_data.driver = __FILE__;
721 source_output_data.module = m;
722 source_output_data.source = source;
724 if ((n = pa_modargs_get_value(ma, "source_output_name", NULL)))
725 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, n);
727 pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s",
728 pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
730 if ((n = pa_modargs_get_value(ma, "source_output_role", NULL)))
731 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, n);
733 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
735 if ((n = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)))
736 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
738 pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
739 pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
741 pa_source_output_new(&u->source_output, m->core, &source_output_data);
742 pa_source_output_new_data_done(&source_output_data);
744 if (!u->source_output)
747 u->source_output->parent.process_msg = source_output_process_msg_cb;
748 u->source_output->push = source_output_push_cb;
749 u->source_output->process_rewind = source_output_process_rewind_cb;
750 u->source_output->kill = source_output_kill_cb;
751 u->source_output->attach = source_output_attach_cb;
752 u->source_output->detach = source_output_detach_cb;
753 u->source_output->state_change = source_output_state_change_cb;
754 u->source_output->may_move_to = source_output_may_move_to_cb;
755 u->source_output->moving = source_output_moving_cb;
756 u->source_output->userdata = u;
758 pa_source_output_set_requested_latency(u->source_output, u->latency/3);
760 pa_sink_input_get_silence(u->sink_input, &silence);
761 u->memblockq = pa_memblockq_new(
763 MEMBLOCKQ_MAXLENGTH, /* maxlength */
764 MEMBLOCKQ_MAXLENGTH, /* tlength */
765 pa_frame_size(&ss), /* base */
769 &silence); /* silence frame */
770 pa_memblock_unref(silence.memblock);
772 u->asyncmsgq = pa_asyncmsgq_new(0);
774 pa_sink_input_put(u->sink_input);
775 pa_source_output_put(u->source_output);
777 if (u->adjust_time > 0)
778 u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
792 void pa__done(pa_module*m) {
797 if (!(u = m->userdata))
803 pa_memblockq_free(u->memblockq);
806 pa_asyncmsgq_unref(u->asyncmsgq);
809 u->core->mainloop->time_free(u->time_event);