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
29 #include <pulse/xmalloc.h>
31 #include <pulsecore/sink-input.h>
32 #include <pulsecore/module.h>
33 #include <pulsecore/modargs.h>
34 #include <pulsecore/namereg.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/core-util.h>
38 #include <pulse/rtclock.h>
39 #include <pulse/timeval.h>
41 #include "module-loopback-symdef.h"
43 PA_MODULE_AUTHOR("Pierre-Louis Bossart");
44 PA_MODULE_DESCRIPTION("Loopback from source to sink");
45 PA_MODULE_VERSION(PACKAGE_VERSION);
46 PA_MODULE_LOAD_ONCE(FALSE);
48 "source=<source to connect to> "
49 "sink=<sink to connect to> "
50 "adjust_time=<how often to readjust rates in s> "
51 "latency_msec=<latency in ms> "
52 "format=<sample format> "
54 "channels=<number of channels> "
55 "channel_map=<channel map> "
56 "sink_input_properties=<proplist> "
57 "source_output_properties=<proplist> "
58 "source_dont_move=<boolean> "
59 "sink_dont_move=<boolean> "
60 "remix=<remix channels?> ");
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 "sink_input_properties",
116 "source_output_properties",
124 SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
125 SINK_INPUT_MESSAGE_REWIND,
126 SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT,
127 SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
131 SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
134 /* Called from main context */
135 static void teardown(struct userdata *u) {
137 pa_assert_ctl_context();
139 pa_asyncmsgq_flush(u->asyncmsgq, 0);
141 pa_sink_input_unlink(u->sink_input);
143 if (u->source_output)
144 pa_source_output_unlink(u->source_output);
147 pa_sink_input_unref(u->sink_input);
148 u->sink_input = NULL;
151 if (u->source_output) {
152 pa_source_output_unref(u->source_output);
153 u->source_output = NULL;
157 /* Called from main context */
158 static void adjust_rates(struct userdata *u) {
160 uint32_t old_rate, base_rate, new_rate;
161 pa_usec_t buffer_latency;
164 pa_assert_ctl_context();
166 pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
167 pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
170 u->latency_snapshot.sink_input_buffer +
171 u->latency_snapshot.source_output_buffer;
173 if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter)
174 buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter);
176 buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter));
178 buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec);
180 pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
181 (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC,
182 (double) buffer_latency / PA_USEC_PER_MSEC,
183 (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
184 ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC);
186 pa_log_debug("Should buffer %zu bytes, buffered at minimum %zu bytes",
187 u->latency_snapshot.max_request*2,
188 u->latency_snapshot.min_memblockq_length);
190 fs = pa_frame_size(&u->sink_input->sample_spec);
191 old_rate = u->sink_input->sample_spec.rate;
192 base_rate = u->source_output->sample_spec.rate;
194 if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2)
195 new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
197 new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
199 if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) {
200 pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate, new_rate);
201 new_rate = base_rate;
203 if (base_rate < new_rate + 20 && new_rate < base_rate + 20)
204 new_rate = base_rate;
205 /* Do the adjustment in small steps; 2‰ can be considered inaudible */
206 if (new_rate < (uint32_t) (old_rate*0.998) || new_rate > (uint32_t) (old_rate*1.002)) {
207 pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate, old_rate);
208 new_rate = PA_CLAMP(new_rate, (uint32_t) (old_rate*0.998), (uint32_t) (old_rate*1.002));
212 pa_sink_input_set_rate(u->sink_input, new_rate);
213 pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u->sink_input->sink->name, (unsigned long) new_rate);
215 pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time);
218 /* Called from main context */
219 static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
220 struct userdata *u = userdata;
224 pa_assert(u->time_event == e);
229 /* Called from input thread context */
230 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
234 pa_source_output_assert_ref(o);
235 pa_source_output_assert_io_context(o);
236 pa_assert_se(u = o->userdata);
238 if (u->skip > chunk->length) {
239 u->skip -= chunk->length;
245 copy.index += u->skip;
246 copy.length -= u->skip;
252 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, chunk, NULL);
253 u->send_counter += (int64_t) chunk->length;
256 /* Called from input thread context */
257 static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
260 pa_source_output_assert_ref(o);
261 pa_source_output_assert_io_context(o);
262 pa_assert_se(u = o->userdata);
264 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
265 u->send_counter -= (int64_t) nbytes;
268 /* Called from output thread context */
269 static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
270 struct userdata *u = PA_SOURCE_OUTPUT(obj)->userdata;
274 case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT: {
277 length = pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq);
279 u->latency_snapshot.send_counter = u->send_counter;
280 u->latency_snapshot.source_output_buffer = u->source_output->thread_info.resampler ? pa_resampler_result(u->source_output->thread_info.resampler, length) : length;
281 u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source);
287 return pa_source_output_process_msg(obj, code, data, offset, chunk);
290 /* Called from output thread context */
291 static void source_output_attach_cb(pa_source_output *o) {
294 pa_source_output_assert_ref(o);
295 pa_source_output_assert_io_context(o);
296 pa_assert_se(u = o->userdata);
298 u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
299 o->source->thread_info.rtpoll,
304 /* Called from output thread context */
305 static void source_output_detach_cb(pa_source_output *o) {
308 pa_source_output_assert_ref(o);
309 pa_source_output_assert_io_context(o);
310 pa_assert_se(u = o->userdata);
312 if (u->rtpoll_item_write) {
313 pa_rtpoll_item_free(u->rtpoll_item_write);
314 u->rtpoll_item_write = NULL;
318 /* Called from output thread context */
319 static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
322 pa_source_output_assert_ref(o);
323 pa_source_output_assert_io_context(o);
324 pa_assert_se(u = o->userdata);
326 if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) {
328 u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source),
332 pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
336 /* Called from main thread */
337 static void source_output_kill_cb(pa_source_output *o) {
340 pa_source_output_assert_ref(o);
341 pa_assert_ctl_context();
342 pa_assert_se(u = o->userdata);
345 pa_module_unload_request(u->module, TRUE);
348 /* Called from main thread */
349 static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
352 pa_source_output_assert_ref(o);
353 pa_assert_ctl_context();
354 pa_assert_se(u = o->userdata);
356 return dest != u->sink_input->sink->monitor_source;
359 /* Called from main thread */
360 static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
367 pa_source_output_assert_ref(o);
368 pa_assert_ctl_context();
369 pa_assert_se(u = o->userdata);
371 p = pa_proplist_new();
372 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
374 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
375 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
377 pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p);
381 /* Called from output thread context */
382 static void update_min_memblockq_length(struct userdata *u) {
386 pa_sink_input_assert_io_context(u->sink_input);
388 length = pa_memblockq_get_length(u->memblockq);
390 if (u->min_memblockq_length == (size_t) -1 ||
391 length < u->min_memblockq_length)
392 u->min_memblockq_length = length;
395 /* Called from output thread context */
396 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
399 pa_sink_input_assert_ref(i);
400 pa_sink_input_assert_io_context(i);
401 pa_assert_se(u = i->userdata);
405 while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
409 if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
410 pa_log_info("Could not peek into queue");
414 chunk->length = PA_MIN(chunk->length, nbytes);
415 pa_memblockq_drop(u->memblockq, chunk->length);
417 update_min_memblockq_length(u);
422 /* Called from output thread context */
423 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
426 pa_sink_input_assert_ref(i);
427 pa_sink_input_assert_io_context(i);
428 pa_assert_se(u = i->userdata);
430 pa_memblockq_rewind(u->memblockq, nbytes);
433 /* Called from output thread context */
434 static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
435 struct userdata *u = PA_SINK_INPUT(obj)->userdata;
439 case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
442 pa_sink_input_assert_io_context(u->sink_input);
444 *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec);
446 /* Fall through, the default handler will add in the extra
447 * latency added by the resampler */
451 case SINK_INPUT_MESSAGE_POST:
453 pa_sink_input_assert_io_context(u->sink_input);
455 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
456 pa_memblockq_push_align(u->memblockq, chunk);
458 pa_memblockq_flush_write(u->memblockq, TRUE);
460 update_min_memblockq_length(u);
462 /* Is this the end of an underrun? Then let's start things
465 u->sink_input->thread_info.underrun_for > 0 &&
466 pa_memblockq_is_readable(u->memblockq)) {
468 pa_log_debug("Requesting rewind due to end of underrun.");
469 pa_sink_input_request_rewind(u->sink_input,
470 (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for),
474 u->recv_counter += (int64_t) chunk->length;
478 case SINK_INPUT_MESSAGE_REWIND:
480 pa_sink_input_assert_io_context(u->sink_input);
482 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
483 pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE);
485 pa_memblockq_flush_write(u->memblockq, TRUE);
487 u->recv_counter -= offset;
489 update_min_memblockq_length(u);
493 case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
496 update_min_memblockq_length(u);
498 length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);
500 u->latency_snapshot.recv_counter = u->recv_counter;
501 u->latency_snapshot.sink_input_buffer =
502 pa_memblockq_get_length(u->memblockq) +
503 (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length);
504 u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink);
506 u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input);
508 u->latency_snapshot.min_memblockq_length = u->min_memblockq_length;
509 u->min_memblockq_length = (size_t) -1;
514 case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: {
515 /* This message is sent from the IO thread to the main
516 * thread! So don't be confused. All the user cases above
517 * are executed in thread context, but this one is not! */
519 pa_assert_ctl_context();
521 if (u->adjust_time > 0)
527 return pa_sink_input_process_msg(obj, code, data, offset, chunk);
530 /* Called from output thread context */
531 static void sink_input_attach_cb(pa_sink_input *i) {
534 pa_sink_input_assert_ref(i);
535 pa_sink_input_assert_io_context(i);
536 pa_assert_se(u = i->userdata);
538 u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
539 i->sink->thread_info.rtpoll,
543 pa_memblockq_set_prebuf(u->memblockq, pa_sink_input_get_max_request(i)*2);
544 pa_memblockq_set_maxrewind(u->memblockq, pa_sink_input_get_max_rewind(i));
546 u->min_memblockq_length = (size_t) -1;
549 /* Called from output thread context */
550 static void sink_input_detach_cb(pa_sink_input *i) {
553 pa_sink_input_assert_ref(i);
554 pa_sink_input_assert_io_context(i);
555 pa_assert_se(u = i->userdata);
557 if (u->rtpoll_item_read) {
558 pa_rtpoll_item_free(u->rtpoll_item_read);
559 u->rtpoll_item_read = NULL;
563 /* Called from output thread context */
564 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
567 pa_sink_input_assert_ref(i);
568 pa_sink_input_assert_io_context(i);
569 pa_assert_se(u = i->userdata);
571 pa_memblockq_set_maxrewind(u->memblockq, nbytes);
574 /* Called from output thread context */
575 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
578 pa_sink_input_assert_ref(i);
579 pa_sink_input_assert_io_context(i);
580 pa_assert_se(u = i->userdata);
582 pa_memblockq_set_prebuf(u->memblockq, nbytes*2);
583 pa_log_info("Max request changed");
584 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED, NULL, 0, NULL, NULL);
587 /* Called from main thread */
588 static void sink_input_kill_cb(pa_sink_input *i) {
591 pa_sink_input_assert_ref(i);
592 pa_assert_ctl_context();
593 pa_assert_se(u = i->userdata);
596 pa_module_unload_request(u->module, TRUE);
599 /* Called from main thread */
600 static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
605 pa_sink_input_assert_ref(i);
606 pa_assert_ctl_context();
607 pa_assert_se(u = i->userdata);
609 p = pa_proplist_new();
610 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
612 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
613 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
615 pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
619 /* Called from main thread */
620 static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
623 pa_sink_input_assert_ref(i);
624 pa_assert_ctl_context();
625 pa_assert_se(u = i->userdata);
627 if (!u->source_output->source->monitor_of)
630 return dest != u->source_output->source->monitor_of;
633 int pa__init(pa_module *m) {
634 pa_modargs *ma = NULL;
637 pa_sink_input_new_data sink_input_data;
638 pa_bool_t sink_dont_move;
640 pa_source_output_new_data source_output_data;
641 pa_bool_t source_dont_move;
642 uint32_t latency_msec;
646 uint32_t adjust_time_sec;
648 pa_bool_t remix = TRUE;
652 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
653 pa_log("Failed to parse module arguments");
657 if (!(source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
658 pa_log("No such source.");
662 if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
663 pa_log("No such sink.");
667 if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
668 pa_log("Invalid boolean remix parameter");
672 ss = sink->sample_spec;
673 map = sink->channel_map;
674 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
675 pa_log("Invalid sample format specification or channel map");
679 latency_msec = DEFAULT_LATENCY_MSEC;
680 if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) {
681 pa_log("Invalid latency specification");
685 m->userdata = u = pa_xnew0(struct userdata, 1);
688 u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
690 adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
691 if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
692 pa_log("Failed to parse adjust_time value");
696 if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
697 u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC;
699 u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
701 pa_sink_input_new_data_init(&sink_input_data);
702 sink_input_data.driver = __FILE__;
703 sink_input_data.module = m;
704 pa_sink_input_new_data_set_sink(&sink_input_data, sink, FALSE);
706 if (pa_modargs_get_proplist(ma, "sink_input_properties", sink_input_data.proplist, PA_UPDATE_REPLACE) < 0) {
707 pa_log("Failed to parse the sink_input_properties value.");
708 pa_sink_input_new_data_done(&sink_input_data);
712 if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_NAME))
713 pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback from %s",
714 pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
716 if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_ROLE))
717 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
719 if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME)
720 && (n = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)))
721 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
723 pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
724 pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
725 sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX);
727 sink_dont_move = FALSE;
728 if (pa_modargs_get_value_boolean(ma, "sink_dont_move", &sink_dont_move) < 0) {
729 pa_log("sink_dont_move= expects a boolean argument.");
734 sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE;
736 pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
737 pa_sink_input_new_data_done(&sink_input_data);
742 u->sink_input->parent.process_msg = sink_input_process_msg_cb;
743 u->sink_input->pop = sink_input_pop_cb;
744 u->sink_input->process_rewind = sink_input_process_rewind_cb;
745 u->sink_input->kill = sink_input_kill_cb;
746 u->sink_input->attach = sink_input_attach_cb;
747 u->sink_input->detach = sink_input_detach_cb;
748 u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
749 u->sink_input->update_max_request = sink_input_update_max_request_cb;
750 u->sink_input->may_move_to = sink_input_may_move_to_cb;
751 u->sink_input->moving = sink_input_moving_cb;
752 u->sink_input->userdata = u;
754 pa_sink_input_set_requested_latency(u->sink_input, u->latency/3);
756 pa_source_output_new_data_init(&source_output_data);
757 source_output_data.driver = __FILE__;
758 source_output_data.module = m;
759 pa_source_output_new_data_set_source(&source_output_data, source, FALSE);
761 if (pa_modargs_get_proplist(ma, "source_output_properties", source_output_data.proplist, PA_UPDATE_REPLACE) < 0) {
762 pa_log("Failed to parse the source_output_properties value.");
763 pa_source_output_new_data_done(&source_output_data);
767 if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_NAME))
768 pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s",
769 pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
771 if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_ROLE))
772 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
774 if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME)
775 && (n = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)))
776 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
778 pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
779 pa_source_output_new_data_set_channel_map(&source_output_data, &map);
780 source_output_data.flags = (remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX);
782 source_dont_move = FALSE;
783 if (pa_modargs_get_value_boolean(ma, "source_dont_move", &source_dont_move) < 0) {
784 pa_log("source_dont_move= expects a boolean argument.");
788 if (source_dont_move)
789 source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE;
791 pa_source_output_new(&u->source_output, m->core, &source_output_data);
792 pa_source_output_new_data_done(&source_output_data);
794 if (!u->source_output)
797 u->source_output->parent.process_msg = source_output_process_msg_cb;
798 u->source_output->push = source_output_push_cb;
799 u->source_output->process_rewind = source_output_process_rewind_cb;
800 u->source_output->kill = source_output_kill_cb;
801 u->source_output->attach = source_output_attach_cb;
802 u->source_output->detach = source_output_detach_cb;
803 u->source_output->state_change = source_output_state_change_cb;
804 u->source_output->may_move_to = source_output_may_move_to_cb;
805 u->source_output->moving = source_output_moving_cb;
806 u->source_output->userdata = u;
808 pa_source_output_set_requested_latency(u->source_output, u->latency/3);
810 pa_sink_input_get_silence(u->sink_input, &silence);
811 u->memblockq = pa_memblockq_new(
812 "module-loopback memblockq",
814 MEMBLOCKQ_MAXLENGTH, /* maxlength */
815 MEMBLOCKQ_MAXLENGTH, /* tlength */
816 &ss, /* sample_spec */
820 &silence); /* silence frame */
821 pa_memblock_unref(silence.memblock);
823 u->asyncmsgq = pa_asyncmsgq_new(0);
825 pa_sink_input_put(u->sink_input);
826 pa_source_output_put(u->source_output);
828 if (u->adjust_time > 0)
829 u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
843 void pa__done(pa_module*m) {
848 if (!(u = m->userdata))
854 pa_memblockq_free(u->memblockq);
857 pa_asyncmsgq_unref(u->asyncmsgq);
860 u->core->mainloop->time_free(u->time_event);