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) {
368 pa_source_output_assert_ref(o);
369 pa_assert_ctl_context();
370 pa_assert_se(u = o->userdata);
372 p = pa_proplist_new();
373 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
375 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
376 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
378 pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p);
382 /* Called from output thread context */
383 static void update_min_memblockq_length(struct userdata *u) {
387 pa_sink_input_assert_io_context(u->sink_input);
389 length = pa_memblockq_get_length(u->memblockq);
391 if (u->min_memblockq_length == (size_t) -1 ||
392 length < u->min_memblockq_length)
393 u->min_memblockq_length = length;
396 /* Called from output thread context */
397 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
400 pa_sink_input_assert_ref(i);
401 pa_sink_input_assert_io_context(i);
402 pa_assert_se(u = i->userdata);
406 while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
410 if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
411 pa_log_info("Could not peek into queue");
415 chunk->length = PA_MIN(chunk->length, nbytes);
416 pa_memblockq_drop(u->memblockq, chunk->length);
418 update_min_memblockq_length(u);
423 /* Called from output thread context */
424 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
427 pa_sink_input_assert_ref(i);
428 pa_sink_input_assert_io_context(i);
429 pa_assert_se(u = i->userdata);
431 pa_memblockq_rewind(u->memblockq, nbytes);
434 /* Called from output thread context */
435 static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
436 struct userdata *u = PA_SINK_INPUT(obj)->userdata;
440 case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
443 pa_sink_input_assert_io_context(u->sink_input);
445 *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec);
447 /* Fall through, the default handler will add in the extra
448 * latency added by the resampler */
452 case SINK_INPUT_MESSAGE_POST:
454 pa_sink_input_assert_io_context(u->sink_input);
456 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
457 pa_memblockq_push_align(u->memblockq, chunk);
459 pa_memblockq_flush_write(u->memblockq, TRUE);
461 update_min_memblockq_length(u);
463 /* Is this the end of an underrun? Then let's start things
466 u->sink_input->thread_info.underrun_for > 0 &&
467 pa_memblockq_is_readable(u->memblockq)) {
469 pa_log_debug("Requesting rewind due to end of underrun.");
470 pa_sink_input_request_rewind(u->sink_input,
471 (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for),
475 u->recv_counter += (int64_t) chunk->length;
479 case SINK_INPUT_MESSAGE_REWIND:
481 pa_sink_input_assert_io_context(u->sink_input);
483 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
484 pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE);
486 pa_memblockq_flush_write(u->memblockq, TRUE);
488 u->recv_counter -= offset;
490 update_min_memblockq_length(u);
494 case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
497 update_min_memblockq_length(u);
499 length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);
501 u->latency_snapshot.recv_counter = u->recv_counter;
502 u->latency_snapshot.sink_input_buffer =
503 pa_memblockq_get_length(u->memblockq) +
504 (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length);
505 u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink);
507 u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input);
509 u->latency_snapshot.min_memblockq_length = u->min_memblockq_length;
510 u->min_memblockq_length = (size_t) -1;
515 case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: {
516 /* This message is sent from the IO thread to the main
517 * thread! So don't be confused. All the user cases above
518 * are executed in thread context, but this one is not! */
520 pa_assert_ctl_context();
522 if (u->adjust_time > 0)
528 return pa_sink_input_process_msg(obj, code, data, offset, chunk);
531 /* Called from output thread context */
532 static void sink_input_attach_cb(pa_sink_input *i) {
535 pa_sink_input_assert_ref(i);
536 pa_sink_input_assert_io_context(i);
537 pa_assert_se(u = i->userdata);
539 u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
540 i->sink->thread_info.rtpoll,
544 pa_memblockq_set_prebuf(u->memblockq, pa_sink_input_get_max_request(i)*2);
545 pa_memblockq_set_maxrewind(u->memblockq, pa_sink_input_get_max_rewind(i));
547 u->min_memblockq_length = (size_t) -1;
550 /* Called from output thread context */
551 static void sink_input_detach_cb(pa_sink_input *i) {
554 pa_sink_input_assert_ref(i);
555 pa_sink_input_assert_io_context(i);
556 pa_assert_se(u = i->userdata);
558 if (u->rtpoll_item_read) {
559 pa_rtpoll_item_free(u->rtpoll_item_read);
560 u->rtpoll_item_read = NULL;
564 /* Called from output thread context */
565 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
568 pa_sink_input_assert_ref(i);
569 pa_sink_input_assert_io_context(i);
570 pa_assert_se(u = i->userdata);
572 pa_memblockq_set_maxrewind(u->memblockq, nbytes);
575 /* Called from output thread context */
576 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
579 pa_sink_input_assert_ref(i);
580 pa_sink_input_assert_io_context(i);
581 pa_assert_se(u = i->userdata);
583 pa_memblockq_set_prebuf(u->memblockq, nbytes*2);
584 pa_log_info("Max request changed");
585 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED, NULL, 0, NULL, NULL);
588 /* Called from main thread */
589 static void sink_input_kill_cb(pa_sink_input *i) {
592 pa_sink_input_assert_ref(i);
593 pa_assert_ctl_context();
594 pa_assert_se(u = i->userdata);
597 pa_module_unload_request(u->module, TRUE);
600 /* Called from main thread */
601 static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
609 pa_sink_input_assert_ref(i);
610 pa_assert_ctl_context();
611 pa_assert_se(u = i->userdata);
613 p = pa_proplist_new();
614 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
616 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
617 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
619 pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
623 /* Called from main thread */
624 static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
627 pa_sink_input_assert_ref(i);
628 pa_assert_ctl_context();
629 pa_assert_se(u = i->userdata);
631 if (!u->source_output->source->monitor_of)
634 return dest != u->source_output->source->monitor_of;
637 int pa__init(pa_module *m) {
638 pa_modargs *ma = NULL;
641 pa_sink_input_new_data sink_input_data;
642 pa_bool_t sink_dont_move;
644 pa_source_output_new_data source_output_data;
645 pa_bool_t source_dont_move;
646 uint32_t latency_msec;
650 uint32_t adjust_time_sec;
652 pa_bool_t remix = TRUE;
656 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
657 pa_log("Failed to parse module arguments");
661 if (!(source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
662 pa_log("No such source.");
666 if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
667 pa_log("No such sink.");
671 if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
672 pa_log("Invalid boolean remix parameter");
676 ss = sink->sample_spec;
677 map = sink->channel_map;
678 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
679 pa_log("Invalid sample format specification or channel map");
683 latency_msec = DEFAULT_LATENCY_MSEC;
684 if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) {
685 pa_log("Invalid latency specification");
689 m->userdata = u = pa_xnew0(struct userdata, 1);
692 u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
694 adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
695 if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
696 pa_log("Failed to parse adjust_time value");
700 if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
701 u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC;
703 u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
705 pa_sink_input_new_data_init(&sink_input_data);
706 sink_input_data.driver = __FILE__;
707 sink_input_data.module = m;
708 pa_sink_input_new_data_set_sink(&sink_input_data, sink, FALSE);
710 if (pa_modargs_get_proplist(ma, "sink_input_properties", sink_input_data.proplist, PA_UPDATE_REPLACE) < 0) {
711 pa_log("Failed to parse the sink_input_properties value.");
712 pa_sink_input_new_data_done(&sink_input_data);
716 if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_NAME))
717 pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback from %s",
718 pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
720 if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_ROLE))
721 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
723 if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME)
724 && (n = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)))
725 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
727 pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
728 pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
729 sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX);
731 sink_dont_move = FALSE;
732 if (pa_modargs_get_value_boolean(ma, "sink_dont_move", &sink_dont_move) < 0) {
733 pa_log("sink_dont_move= expects a boolean argument.");
738 sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE;
740 pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
741 pa_sink_input_new_data_done(&sink_input_data);
746 u->sink_input->parent.process_msg = sink_input_process_msg_cb;
747 u->sink_input->pop = sink_input_pop_cb;
748 u->sink_input->process_rewind = sink_input_process_rewind_cb;
749 u->sink_input->kill = sink_input_kill_cb;
750 u->sink_input->attach = sink_input_attach_cb;
751 u->sink_input->detach = sink_input_detach_cb;
752 u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
753 u->sink_input->update_max_request = sink_input_update_max_request_cb;
754 u->sink_input->may_move_to = sink_input_may_move_to_cb;
755 u->sink_input->moving = sink_input_moving_cb;
756 u->sink_input->userdata = u;
758 pa_sink_input_set_requested_latency(u->sink_input, u->latency/3);
760 pa_source_output_new_data_init(&source_output_data);
761 source_output_data.driver = __FILE__;
762 source_output_data.module = m;
763 pa_source_output_new_data_set_source(&source_output_data, source, FALSE);
765 if (pa_modargs_get_proplist(ma, "source_output_properties", source_output_data.proplist, PA_UPDATE_REPLACE) < 0) {
766 pa_log("Failed to parse the source_output_properties value.");
767 pa_source_output_new_data_done(&source_output_data);
771 if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_NAME))
772 pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s",
773 pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
775 if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_ROLE))
776 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
778 if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME)
779 && (n = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)))
780 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
782 pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
783 pa_source_output_new_data_set_channel_map(&source_output_data, &map);
784 source_output_data.flags = (remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX);
786 source_dont_move = FALSE;
787 if (pa_modargs_get_value_boolean(ma, "source_dont_move", &source_dont_move) < 0) {
788 pa_log("source_dont_move= expects a boolean argument.");
792 if (source_dont_move)
793 source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE;
795 pa_source_output_new(&u->source_output, m->core, &source_output_data);
796 pa_source_output_new_data_done(&source_output_data);
798 if (!u->source_output)
801 u->source_output->parent.process_msg = source_output_process_msg_cb;
802 u->source_output->push = source_output_push_cb;
803 u->source_output->process_rewind = source_output_process_rewind_cb;
804 u->source_output->kill = source_output_kill_cb;
805 u->source_output->attach = source_output_attach_cb;
806 u->source_output->detach = source_output_detach_cb;
807 u->source_output->state_change = source_output_state_change_cb;
808 u->source_output->may_move_to = source_output_may_move_to_cb;
809 u->source_output->moving = source_output_moving_cb;
810 u->source_output->userdata = u;
812 pa_source_output_set_requested_latency(u->source_output, u->latency/3);
814 pa_sink_input_get_silence(u->sink_input, &silence);
815 u->memblockq = pa_memblockq_new(
816 "module-loopback memblockq",
818 MEMBLOCKQ_MAXLENGTH, /* maxlength */
819 MEMBLOCKQ_MAXLENGTH, /* tlength */
820 &ss, /* sample_spec */
824 &silence); /* silence frame */
825 pa_memblock_unref(silence.memblock);
827 u->asyncmsgq = pa_asyncmsgq_new(0);
829 pa_sink_input_put(u->sink_input);
830 pa_source_output_put(u->source_output);
832 if (u->adjust_time > 0)
833 u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
847 void pa__done(pa_module*m) {
852 if (!(u = m->userdata))
858 pa_memblockq_free(u->memblockq);
861 pa_asyncmsgq_unref(u->asyncmsgq);
864 u->core->mainloop->time_free(u->time_event);